This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
Java: Class accessibility rules
- To: java-patches at gcc dot gnu dot org, gcc-patches at gcc dot gnu dot org
- Subject: Java: Class accessibility rules
- From: Bryce McKinlay <bryce at albatross dot co dot nz>
- Date: Mon, 05 Feb 2001 16:11:37 +1300
This patch implements accessibility rules for nested types according
to section 6.6.1/6.6.2 of the spec. Previously, the rule for top-level
classes was used in some cases where it shouldn't be, and although
there was a specific function being used for checking inner class
access, the rules it used were not correct. This resulted in several
situations in which legal code got rejected. For example:
http://gcc.gnu.org/ml/java/2001-01/msg00559.html
And I suspect should also nail down some cases in which illegal code
was accepted. I have also fixed an unrelated problem where access to a
non-public top-level class in the empty package was erroneously
permitted.
Unfortunately check_inner_class_access doesn't seem to work well with
class files produced by "javac". It appears that the bytecode parser
does not set the CLASS_PROTECTED flag (for example) in some cases
where it should. However, as far as I can tell, this only means that
illegal accesses might be accepted - not that legal code would get
rejected. Class files produced by "gcj -C" seem to work ok.
Alex, could you run this through your regression tests?
regards
[ bryce ]
2001-02-05 Bryce McKinlay <bryce@albatross.co.nz>
* parse.y (parser_check_super_interface): Don't call
check_pkg_class_access for an inner interface.
(parser_check_super): Don't call check_pkg_class_access for inner
class.
(do_resolve_class): Simplify enclosing type loop. Don't call
check_pkg_class_access if CL and DECL are not set.
(find_in_imports_on_demand): Set DECL if class_type needed to be
loaded. Don't call check_pkg_class_access for an inner class.
(check_inner_class_access): Rewritten to implement member access
rules as per spec 6.6.1.
(check_pkg_class_access): Handle the empty package correctly.
(in_same_package): New function. Determine if two classes are in the
same package.
Index: parse.y
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/parse.y,v
retrieving revision 1.250
diff -u -r1.250 parse.y
--- parse.y 2001/02/04 22:44:03 1.250
+++ parse.y 2001/02/05 02:23:53
@@ -127,6 +127,7 @@
static tree patch_method_invocation PARAMS ((tree, tree, tree,
int *, tree *));
static int breakdown_qualified PARAMS ((tree *, tree *, tree));
+static int in_same_package PARAMS ((tree, tree));
static tree resolve_and_layout PARAMS ((tree, tree));
static tree qualify_and_find PARAMS ((tree, tree, tree));
static tree resolve_no_layout PARAMS ((tree, tree));
@@ -4895,8 +4896,10 @@
return 1;
}
- /* Check scope: same package OK, other package: OK if public */
- if (check_pkg_class_access (DECL_NAME (super_decl), lookup_cl (this_decl)))
+ /* Check top-level interface access. Inner classes are subject to member
+ access rules (6.6.1). */
+ if (! INNER_CLASS_P (super_type)
+ && check_pkg_class_access (DECL_NAME (super_decl), lookup_cl (this_decl)))
return 1;
SOURCE_FRONTEND_DEBUG (("Completing interface %s with %s",
@@ -4932,8 +4935,10 @@
return 1;
}
- /* Check scope: same package OK, other package: OK if public */
- if (check_pkg_class_access (DECL_NAME (super_decl), wfl))
+ /* Check top-level class scope. Inner classes are subject to member access
+ rules (6.6.1). */
+ if (! INNER_CLASS_P (super_type)
+ && (check_pkg_class_access (DECL_NAME (super_decl), wfl)))
return 1;
SOURCE_FRONTEND_DEBUG (("Completing class %s with %s",
@@ -5513,7 +5518,6 @@
being loaded from class file. FIXME. */
while (enclosing)
{
- tree name;
tree intermediate;
if ((new_class_decl = find_as_inner_class (enclosing, class_type, cl)))
@@ -5532,19 +5536,12 @@
/* Now go to the upper classes, bail out if necessary. */
enclosing = CLASSTYPE_SUPER (TREE_TYPE (enclosing));
if (!enclosing || enclosing == object_type_node)
- break;
-
- if (TREE_CODE (enclosing) == RECORD_TYPE)
- {
- enclosing = TYPE_NAME (enclosing);
- continue;
- }
+ break;
- if (TREE_CODE (enclosing) == IDENTIFIER_NODE)
- BUILD_PTR_FROM_NAME (name, enclosing);
+ if (TREE_CODE (enclosing) == POINTER_TYPE)
+ enclosing = do_resolve_class (NULL, enclosing, NULL, NULL);
else
- name = enclosing;
- enclosing = do_resolve_class (NULL, name, NULL, NULL);
+ enclosing = TYPE_NAME (enclosing);
}
/* 1- Check for the type in single imports. This will change
@@ -5597,10 +5594,19 @@
/* 5- Check an other compilation unit that bears the name of type */
load_class (TYPE_NAME (class_type), 0);
- if (check_pkg_class_access (TYPE_NAME (class_type),
- (cl ? cl : lookup_cl (decl))))
- return NULL_TREE;
-
+
+ if (!cl)
+ cl = lookup_cl (decl);
+
+ /* If we don't have a value for CL, then we're being called recursively.
+ We can't check package access just yet, but it will be taken care of
+ by the caller. */
+ if (cl)
+ {
+ if (check_pkg_class_access (TYPE_NAME (class_type), cl))
+ return NULL_TREE;
+ }
+
/* 6- Last call for a resolution */
return IDENTIFIER_CLASS_VALUE (TYPE_NAME (class_type));
}
@@ -6668,9 +6674,16 @@
loaded and not seen in source yet, the load */
if (!decl || (!CLASS_LOADED_P (TREE_TYPE (decl))
&& !CLASS_FROM_SOURCE_P (TREE_TYPE (decl))))
- load_class (node_to_use, 0);
+ {
+ load_class (node_to_use, 0);
+ decl = IDENTIFIER_CLASS_VALUE (TYPE_NAME (class_type));
+ }
lineno = saved_lineno;
- return check_pkg_class_access (TYPE_NAME (class_type), cl);
+ if (! INNER_CLASS_P (TREE_TYPE (decl)))
+ return check_pkg_class_access (TYPE_NAME (class_type), cl);
+ else
+ /* 6.6.1: Inner classes are subject to member access rules. */
+ return 0;
}
else
return (seen_once < 0 ? 0 : seen_once); /* It's ok not to have found */
@@ -6764,11 +6777,15 @@
return get_identifier (subname);
}
+/* Check accessibility of inner classes according to member access rules.
+ DECL is the inner class, ENCLOSING_DECL is the class from which the
+ access is being attempted. */
+
static void
check_inner_class_access (decl, enclosing_decl, cl)
tree decl, enclosing_decl, cl;
{
- int access = 0;
+ const char *access;
/* We don't issue an error message when CL is null. CL can be null
as a result of processing a JDEP crafted by source_start_java_method
@@ -6777,31 +6794,70 @@
DECL can also be NULL in case of earlier errors. */
if (!decl || !cl)
return;
+
+ tree enclosing_decl_type = TREE_TYPE (enclosing_decl);
- /* We grant access to private and protected inner classes if the
- location from where we're trying to access DECL is an enclosing
- context for DECL or if both have a common enclosing context. */
if (CLASS_PRIVATE (decl))
- access = 1;
- if (CLASS_PROTECTED (decl))
- access = 2;
- if (!access)
- return;
+ {
+ /* Access is permitted only within the body of the top-level
+ class in which DECL is declared. */
+ tree top_level = decl;
+ while (DECL_CONTEXT (top_level))
+ top_level = DECL_CONTEXT (top_level);
+ while (DECL_CONTEXT (enclosing_decl))
+ enclosing_decl = DECL_CONTEXT (enclosing_decl);
+ if (top_level == enclosing_decl)
+ return;
+ access = "private";
+ }
+ else if (CLASS_PROTECTED (decl))
+ {
+ tree decl_context;
+ /* Access is permitted from within the same package... */
+ if (in_same_package (decl, enclosing_decl))
+ return;
- if (common_enclosing_context_p (TREE_TYPE (enclosing_decl),
- TREE_TYPE (decl))
- || enclosing_context_p (TREE_TYPE (enclosing_decl),
- TREE_TYPE (decl)))
+ /* ... or from within the body of a subtype of the context in which
+ DECL is declared. */
+ decl_context = DECL_CONTEXT (decl);
+ while (enclosing_decl)
+ {
+ if (CLASS_INTERFACE (decl))
+ {
+ if (interface_of_p (TREE_TYPE (decl_context),
+ enclosing_decl_type))
+ return;
+ }
+ else
+ {
+ /* Eww. The order of the arguments is different!! */
+ if (inherits_from_p (enclosing_decl_type,
+ TREE_TYPE (decl_context)))
+ return;
+ }
+ enclosing_decl = DECL_CONTEXT (enclosing_decl);
+ }
+ access = "protected";
+ }
+ else if (! CLASS_PUBLIC (decl))
+ {
+ /* Access is permitted only from within the same package as DECL. */
+ if (in_same_package (decl, enclosing_decl))
+ return;
+ access = "non-public";
+ }
+ else
+ /* Class is public. */
return;
- parse_error_context (cl, "Can't access %s nested %s %s. Only public classes and interfaces in other packages can be accessed",
- (access == 1 ? "private" : "protected"),
+ parse_error_context (cl, "Nested %s %s is %s; cannot be accessed from here",
(CLASS_INTERFACE (decl) ? "interface" : "class"),
- lang_printable_name (decl, 0));
+ lang_printable_name (decl, 0), access);
}
-/* Check that CLASS_NAME refers to a PUBLIC class. Return 0 if no
- access violations were found, 1 otherwise. */
+/* Accessibility check for top-level classes. If CLASS_NAME is in a foreign
+ package, it must be PUBLIC. Return 0 if no access violations were found,
+ 1 otherwise. */
static int
check_pkg_class_access (class_name, cl)
@@ -6810,7 +6866,7 @@
{
tree type;
- if (!QUALIFIED_P (class_name) || !IDENTIFIER_CLASS_VALUE (class_name))
+ if (!IDENTIFIER_CLASS_VALUE (class_name))
return 0;
if (!(type = TREE_TYPE (IDENTIFIER_CLASS_VALUE (class_name))))
@@ -6822,7 +6878,11 @@
allowed. */
tree l, r;
breakdown_qualified (&l, &r, class_name);
+ if (!QUALIFIED_P (class_name) && !ctxp->package)
+ /* Both in the empty package. */
+ return 0;
if (l == ctxp->package)
+ /* Both in the same package. */
return 0;
parse_error_context
@@ -10916,6 +10976,35 @@
*left = get_identifier (base);
return 0;
+}
+
+/* Return TRUE if two classes are from the same package. */
+
+static int
+in_same_package (name1, name2)
+ tree name1, name2;
+{
+ tree tmp;
+ tree pkg1;
+ tree pkg2;
+
+ if (TREE_CODE (name1) == TYPE_DECL)
+ name1 = DECL_NAME (name1);
+ if (TREE_CODE (name2) == TYPE_DECL)
+ name2 = DECL_NAME (name2);
+
+ if (QUALIFIED_P (name1) != QUALIFIED_P (name2))
+ /* One in empty package. */
+ return 0;
+
+ if (QUALIFIED_P (name1) == 0 && QUALIFIED_P (name2) == 0)
+ /* Both in empty package. */
+ return 1;
+
+ breakdown_qualified (&pkg1, &tmp, name1);
+ breakdown_qualified (&pkg2, &tmp, name2);
+
+ return (pkg1 == pkg2);
}
/* Patch tree nodes in a function body. When a BLOCK is found, push