static String classModifierString(int mods) { String s = ""; for (int i = 0; i < classModifierBits.length; i++) { if ((mods & classModifierBits[i]) != 0) { s = s + " " + classModifierNames[i]; mods &= ~classModifierBits[i]; } } if (mods != 0) { s = s + " ILLEGAL:" + Integer.toHexString(mods); } return s; } /** * Find or create an access method for a private member, * or return null if this is not possible. */ public MemberDefinition getAccessMember(Environment env, Context ctx, MemberDefinition field, boolean isSuper) { return getAccessMember(env, ctx, field, false, isSuper); } public MemberDefinition getUpdateMember(Environment env, Context ctx, MemberDefinition field, boolean isSuper) { if (!field.isVariable()) { throw new CompilerError("method"); } return getAccessMember(env, ctx, field, true, isSuper); } private MemberDefinition getAccessMember(Environment env, Context ctx, MemberDefinition field, boolean isUpdate, boolean isSuper) { // The 'isSuper' argument is really only meaningful when the // target member is a method, in which case an 'invokespecial' // is needed. For fields, 'getfield' and 'putfield' instructions // are generated in either case, and 'isSuper' currently plays // no essential role. Nonetheless, we maintain the distinction // consistently for the time being. boolean isStatic = field.isStatic(); boolean isMethod = field.isMethod(); // Find pre-existing access method. // In the case of a field access method, we only look for the getter. // A getter is always created whenever a setter is. // QUERY: Why doesn't the 'MemberDefinition' object for the field // itself just have fields for its getter and setter? MemberDefinition af; for (af = getFirstMember(); af != null; af = af.getNextMember()) { if (af.getAccessMethodTarget() == field) { if (isMethod && af.isSuperAccessMethod() == isSuper) { break; } // Distinguish the getter and the setter by the number of // arguments. int nargs = af.getType().getArgumentTypes().length; // This was (nargs == (isStatic ? 0 : 1) + (isUpdate ? 1 : 0)) // in order to find a setter as well as a getter. This caused // allocation of multiple getters. if (nargs == (isStatic ? 0 : 1)) { break; } } } if (af != null) { if (!isUpdate) { return af; } else { MemberDefinition uf = af.getAccessUpdateMember(); if (uf != null) { return uf; } } } else if (isUpdate) { // must find or create the getter before creating the setter af = getAccessMember(env, ctx, field, false, isSuper); } // If we arrive here, we are creating a new access member. Identifier anm; Type dummyType = null; if (field.isConstructor()) { // For a constructor, we use the same name as for all // constructors ("<init>"), but add a distinguishing // argument of an otherwise unused "dummy" type. anm = idInit; // Get the dummy class, creating it if necessary. SourceClass outerMostClass = (SourceClass)getTopClass(); dummyType = outerMostClass.dummyArgumentType; if (dummyType == null) { // Create dummy class. IdentifierToken sup = new IdentifierToken(0, idJavaLangObject); IdentifierToken interfaces[] = {}; IdentifierToken t = new IdentifierToken(0, idNull); int mod = M_ANONYMOUS | M_STATIC | M_SYNTHETIC; // If an interface has a public inner class, the dummy class for // the constructor must always be accessible. Fix for 4221648. if (outerMostClass.isInterface()) { mod |= M_PUBLIC; } ClassDefinition dummyClass = toplevelEnv.makeClassDefinition(toplevelEnv, 0, t, null, mod, sup, interfaces, outerMostClass); // Check the class. // It is likely that a full check is not really necessary, // but it is essential that the class be marked as parsed. dummyClass.getClassDeclaration().setDefinition(dummyClass, CS_PARSED); Expression argsX[] = {}; Type argTypesX[] = {}; try { ClassDefinition supcls = toplevelEnv.getClassDefinition(idJavaLangObject); dummyClass.checkLocalClass(toplevelEnv, null, new Vset(), supcls, argsX, argTypesX); } catch (ClassNotFound ee) {}; // Get class type. dummyType = dummyClass.getType(); outerMostClass.dummyArgumentType = dummyType; } } else { // Otherwise, we use the name "access$N", for the // smallest value of N >= 0 yielding an unused name. for (int i = 0; ; i++) { anm = Identifier.lookup(prefixAccess + i); if (getFirstMatch(anm) == null) { break; } } } Type argTypes[]; Type t = field.getType(); if (isStatic) { if (!isMethod) { if (!isUpdate) { Type at[] = { }; argTypes = at; t = Type.tMethod(t); // nullary getter } else { Type at[] = { t }; argTypes = at; t = Type.tMethod(Type.tVoid, argTypes); // unary setter } } else { // Since constructors are never static, we don't // have to worry about a dummy argument here. argTypes = t.getArgumentTypes(); } } else { // All access methods for non-static members get an explicit // 'this' pointer as an extra argument, as the access methods // themselves must be static. EXCEPTION: Access methods for // constructors are non-static. Type classType = this.getType(); if (!isMethod) { if (!isUpdate) { Type at[] = { classType }; argTypes = at; t = Type.tMethod(t, argTypes); // nullary getter } else { Type at[] = { classType, t }; argTypes = at; t = Type.tMethod(Type.tVoid, argTypes); // unary setter } } else { // Target is a method, possibly a constructor. Type at[] = t.getArgumentTypes(); int nargs = at.length; if (field.isConstructor()) { // Access method is a constructor. // Requires a dummy argument. MemberDefinition outerThisArg = ((SourceMember)field).getOuterThisArg(); if (outerThisArg != null) { // Outer instance link must be the first argument. // The following is a sanity check that will catch // most cases in which in this requirement is violated. if (at[0] != outerThisArg.getType()) { throw new CompilerError("misplaced outer this"); } // Strip outer 'this' argument. // It will be added back when the access method is checked. argTypes = new Type[nargs]; argTypes[0] = dummyType; for (int i = 1; i < nargs; i++) { argTypes[i] = at[i]; } } else { // There is no outer instance. argTypes = new Type[nargs+1]; argTypes[0] = dummyType; for (int i = 0; i < nargs; i++) { argTypes[i+1] = at[i]; } } } else { // Access method is static. // Requires an explicit 'this' argument. argTypes = new Type[nargs+1]; argTypes[0] = classType; for (int i = 0; i < nargs; i++) { argTypes[i+1] = at[i]; } } t = Type.tMethod(t.getReturnType(), argTypes); } } int nlen = argTypes.length; long where = field.getWhere(); IdentifierToken names[] = new IdentifierToken[nlen]; for (int i = 0; i < nlen; i++) { names[i] = new IdentifierToken(where, Identifier.lookup("$"+i)); } Expression access = null; Expression thisArg = null; Expression args[] = null; if (isStatic) { args = new Expression[nlen]; for (int i = 0 ; i < nlen ; i++) { args[i] = new IdentifierExpression(names[i]); } } else { if (field.isConstructor()) { // Constructor access method is non-static, so // 'this' works normally. thisArg = new ThisExpression(where); // Remove dummy argument, as it is not // passed to the target method. args = new Expression[nlen-1]; for (int i = 1 ; i < nlen ; i++) { args[i-1] = new IdentifierExpression(names[i]); } } else { // Non-constructor access method is static, so // we use the first argument as 'this'. thisArg = new IdentifierExpression(names[0]); // Remove first argument. args = new Expression[nlen-1]; for (int i = 1 ; i < nlen ; i++) { args[i-1] = new IdentifierExpression(names[i]); } } access = thisArg; } if (!isMethod) { access = new FieldExpression(where, access, field); if (isUpdate) { access = new AssignExpression(where, access, args[0]); } } else { // If true, 'isSuper' forces a non-virtual call. access = new MethodExpression(where, access, field, args, isSuper); } Statement code; if (t.getReturnType().isType(TC_VOID)) { code = new ExpressionStatement(where, access); } else { code = new ReturnStatement(where, access); } Statement body[] = { code }; code = new CompoundStatement(where, body); // Access methods are now static (constructors excepted), and no longer final. // This change was mandated by the interaction of the access method // naming conventions and the restriction against overriding final // methods. int mod = M_SYNTHETIC; if (!field.isConstructor()) { mod |= M_STATIC; } // Create the synthetic method within the class in which the referenced // private member appears. The 'env' argument to 'makeMemberDefinition' // is suspect because it represents the environment at the point at // which a reference takes place, while it should represent the // environment in which the definition of the synthetic method appears. // We get away with this because 'env' is used only to access globals // such as 'Environment.error', and also as an argument to // 'resolveTypeStructure', which immediately discards it using // 'setupEnv'. Apparently, the current definition of 'setupEnv' // represents a design change that has not been thoroughly propagated. // An access method is declared with same list of exceptions as its // target. As the exceptions are simply listed by name, the correctness // of this approach requires that the access method be checked // (name-resolved) in the same context as its target method This // should always be the case. SourceMember newf = (SourceMember) env.makeMemberDefinition(env, where, this, null, mod, t, anm, names, field.getExceptionIds(), code); // Just to be safe, copy over the name-resolved exceptions from the // target so that the context in which the access method is checked // doesn't matter. newf.setExceptions(field.getExceptions(env)); newf.setAccessMethodTarget(field); if (isUpdate) { af.setAccessUpdateMember(newf); } newf.setIsSuperAccessMethod(isSuper); // The call to 'check' is not needed, as the access method will be // checked by the containing class after it is added. This is the // idiom followed in the implementation of class literals. (See // 'FieldExpression.java'.) In any case, the context is wrong in the // call below. The access method must be checked in the context in // which it is declared, i.e., the class containing the referenced // private member, not the (inner) class in which the original member // reference occurs. // // try { // newf.check(env, ctx, new Vset()); // } catch (ClassNotFound ee) { // env.error(where, "class.not.found", ee.name, this); // } // The comment above is inaccurate. While it is often the case // that the containing class will check the access method, this is // by no means guaranteed. In fact, an access method may be added // after the checking of its class is complete. In this case, however, // the context in which the class was checked will have been saved in // the class definition object (by the fix for 4095716), allowing us // to check the field now, and in the correct context. // This fixes bug 4098093. Context checkContext = newf.getClassDefinition().getClassContext(); if (checkContext != null) { //System.out.println("checking late addition: " + this); try { newf.check(env, checkContext, new Vset()); } catch (ClassNotFound ee) { env.error(where, "class.not.found", ee.name, this); } } //System.out.println("[Access member '" + // newf + "' created for field '" + // field +"' in class '" + this + "']"); return newf; } /** * Find an inner class of 'this', chosen arbitrarily. * Result is always an actual class, never an interface. * Returns null if none found. */ SourceClass findLookupContext() { // Look for an immediate inner class. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { SourceClass ic = (SourceClass)f.getInnerClass(); if (!ic.isInterface()) { return ic; } } } // Look for a class nested within an immediate inner interface. // At this point, we have given up on finding a minimally-nested // class (which would require a breadth-first traversal). It doesn't // really matter which inner class we find. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { SourceClass lc = ((SourceClass)f.getInnerClass()).findLookupContext(); if (lc != null) { return lc; } } } // No inner classes. return null; } private MemberDefinition lookup = null; /** * Get helper method for class literal lookup. */ public MemberDefinition getClassLiteralLookup(long fwhere) { // If we have already created a lookup method, reuse it. if (lookup != null) { return lookup; } // If the current class is a nested class, make sure we put the // lookup method in the outermost class. Set 'lookup' for the // intervening inner classes so we won't have to do the search // again. if (outerClass != null) { lookup = outerClass.getClassLiteralLookup(fwhere); return lookup; } // If we arrive here, there was no existing 'class$' method. ClassDefinition c = this; boolean needNewClass = false; if (isInterface()) { // The top-level type is an interface. Try to find an existing // inner class in which to create the helper method. Any will do. c = findLookupContext(); if (c == null) { // The interface has no inner classes. Create an anonymous // inner class to hold the helper method, as an interface must // not have any methods. The tests above for prior creation // of a 'class$' method assure that only one such class is // allocated for each outermost class containing a class // literal embedded somewhere within. Part of fix for 4055017. needNewClass = true; IdentifierToken sup = new IdentifierToken(fwhere, idJavaLangObject); IdentifierToken interfaces[] = {}; IdentifierToken t = new IdentifierToken(fwhere, idNull); int mod = M_PUBLIC | M_ANONYMOUS | M_STATIC | M_SYNTHETIC; c = (SourceClass) toplevelEnv.makeClassDefinition(toplevelEnv, fwhere, t, null, mod, sup, interfaces, this); } } // The name of the class-getter stub is "class$" Identifier idDClass = Identifier.lookup(prefixClass); Type strarg[] = { Type.tString }; // Some sanity checks of questionable value. // // This check became useless after matchMethod() was modified // to not return synthetic methods. // //try { // lookup = c.matchMethod(toplevelEnv, c, idDClass, strarg); //} catch (ClassNotFound ee) { // throw new CompilerError("unexpected missing class"); //} catch (AmbiguousMember ee) { // throw new CompilerError("synthetic name clash"); //} //if (lookup != null && lookup.getClassDefinition() == c) { // // Error if method found was not inherited. // throw new CompilerError("unexpected duplicate"); //} // Some sanity checks of questionable value. /* // The helper function looks like this. * // It simply maps a checked exception to an unchecked one. * static Class class$(String class$) { * try { return Class.forName(class$); } * catch (ClassNotFoundException forName) { * throw new NoClassDefFoundError(forName.getMessage()); * } * } */ long w = c.getWhere(); IdentifierToken arg = new IdentifierToken(w, idDClass); Expression e = new IdentifierExpression(arg); Expression a1[] = { e }; Identifier idForName = Identifier.lookup("forName"); e = new MethodExpression(w, new TypeExpression(w, Type.tClassDesc), idForName, a1); Statement body = new ReturnStatement(w, e); // map the exceptions Identifier idClassNotFound = Identifier.lookup("java.lang.ClassNotFoundException"); Identifier idNoClassDefFound = Identifier.lookup("java.lang.NoClassDefFoundError"); Type ctyp = Type.tClass(idClassNotFound); Type exptyp = Type.tClass(idNoClassDefFound); Identifier idGetMessage = Identifier.lookup("getMessage"); e = new IdentifierExpression(w, idForName); e = new MethodExpression(w, e, idGetMessage, new Expression[0]); Expression a2[] = { e }; e = new NewInstanceExpression(w, new TypeExpression(w, exptyp), a2); Statement handler = new CatchStatement(w, new TypeExpression(w, ctyp), new IdentifierToken(idForName),Lines 1501–2000 of 2674. Previous lines Load more
static String classModifierString(int mods) { String s = ""; for (int i = 0; i < classModifierBits.length; i++) { if ((mods & classModifierBits[i]) != 0) { s = s + " " + classModifierNames[i]; mods &= ~classModifierBits[i]; } } if (mods != 0) { s = s + " ILLEGAL:" + Integer.toHexString(mods); } return s; } /** * Find or create an access method for a private member, * or return null if this is not possible. */ public MemberDefinition getAccessMember(Environment env, Context ctx, MemberDefinition field, boolean isSuper) { return getAccessMember(env, ctx, field, false, isSuper); } public MemberDefinition getUpdateMember(Environment env, Context ctx, MemberDefinition field, boolean isSuper) { if (!field.isVariable()) { throw new CompilerError("method"); } return getAccessMember(env, ctx, field, true, isSuper); } private MemberDefinition getAccessMember(Environment env, Context ctx, MemberDefinition field, boolean isUpdate, boolean isSuper) { // The 'isSuper' argument is really only meaningful when the // target member is a method, in which case an 'invokespecial' // is needed. For fields, 'getfield' and 'putfield' instructions // are generated in either case, and 'isSuper' currently plays // no essential role. Nonetheless, we maintain the distinction // consistently for the time being. boolean isStatic = field.isStatic(); boolean isMethod = field.isMethod(); // Find pre-existing access method. // In the case of a field access method, we only look for the getter. // A getter is always created whenever a setter is. // QUERY: Why doesn't the 'MemberDefinition' object for the field // itself just have fields for its getter and setter? MemberDefinition af; for (af = getFirstMember(); af != null; af = af.getNextMember()) { if (af.getAccessMethodTarget() == field) { if (isMethod && af.isSuperAccessMethod() == isSuper) { break; } // Distinguish the getter and the setter by the number of // arguments. int nargs = af.getType().getArgumentTypes().length; // This was (nargs == (isStatic ? 0 : 1) + (isUpdate ? 1 : 0)) // in order to find a setter as well as a getter. This caused // allocation of multiple getters. if (nargs == (isStatic ? 0 : 1)) { break; } } } if (af != null) { if (!isUpdate) { return af; } else { MemberDefinition uf = af.getAccessUpdateMember(); if (uf != null) { return uf; } } } else if (isUpdate) { // must find or create the getter before creating the setter af = getAccessMember(env, ctx, field, false, isSuper); } // If we arrive here, we are creating a new access member. Identifier anm; Type dummyType = null; if (field.isConstructor()) { // For a constructor, we use the same name as for all // constructors ("<init>"), but add a distinguishing // argument of an otherwise unused "dummy" type. anm = idInit; // Get the dummy class, creating it if necessary. SourceClass outerMostClass = (SourceClass)getTopClass(); dummyType = outerMostClass.dummyArgumentType; if (dummyType == null) { // Create dummy class. IdentifierToken sup = new IdentifierToken(0, idJavaLangObject); IdentifierToken interfaces[] = {}; IdentifierToken t = new IdentifierToken(0, idNull); int mod = M_ANONYMOUS | M_STATIC | M_SYNTHETIC; // If an interface has a public inner class, the dummy class for // the constructor must always be accessible. Fix for 4221648. if (outerMostClass.isInterface()) { mod |= M_PUBLIC; } ClassDefinition dummyClass = toplevelEnv.makeClassDefinition(toplevelEnv, 0, t, null, mod, sup, interfaces, outerMostClass); // Check the class. // It is likely that a full check is not really necessary, // but it is essential that the class be marked as parsed. dummyClass.getClassDeclaration().setDefinition(dummyClass, CS_PARSED); Expression argsX[] = {}; Type argTypesX[] = {}; try { ClassDefinition supcls = toplevelEnv.getClassDefinition(idJavaLangObject); dummyClass.checkLocalClass(toplevelEnv, null, new Vset(), supcls, argsX, argTypesX); } catch (ClassNotFound ee) {}; // Get class type. dummyType = dummyClass.getType(); outerMostClass.dummyArgumentType = dummyType; } } else { // Otherwise, we use the name "access$N", for the // smallest value of N >= 0 yielding an unused name. for (int i = 0; ; i++) { anm = Identifier.lookup(prefixAccess + i); if (getFirstMatch(anm) == null) { break; } } } Type argTypes[]; Type t = field.getType(); if (isStatic) { if (!isMethod) { if (!isUpdate) { Type at[] = { }; argTypes = at; t = Type.tMethod(t); // nullary getter } else { Type at[] = { t }; argTypes = at; t = Type.tMethod(Type.tVoid, argTypes); // unary setter } } else { // Since constructors are never static, we don't // have to worry about a dummy argument here. argTypes = t.getArgumentTypes(); } } else { // All access methods for non-static members get an explicit // 'this' pointer as an extra argument, as the access methods // themselves must be static. EXCEPTION: Access methods for // constructors are non-static. Type classType = this.getType(); if (!isMethod) { if (!isUpdate) { Type at[] = { classType }; argTypes = at; t = Type.tMethod(t, argTypes); // nullary getter } else { Type at[] = { classType, t }; argTypes = at; t = Type.tMethod(Type.tVoid, argTypes); // unary setter } } else { // Target is a method, possibly a constructor. Type at[] = t.getArgumentTypes(); int nargs = at.length; if (field.isConstructor()) { // Access method is a constructor. // Requires a dummy argument. MemberDefinition outerThisArg = ((SourceMember)field).getOuterThisArg(); if (outerThisArg != null) { // Outer instance link must be the first argument. // The following is a sanity check that will catch // most cases in which in this requirement is violated. if (at[0] != outerThisArg.getType()) { throw new CompilerError("misplaced outer this"); } // Strip outer 'this' argument. // It will be added back when the access method is checked. argTypes = new Type[nargs]; argTypes[0] = dummyType; for (int i = 1; i < nargs; i++) { argTypes[i] = at[i]; } } else { // There is no outer instance. argTypes = new Type[nargs+1]; argTypes[0] = dummyType; for (int i = 0; i < nargs; i++) { argTypes[i+1] = at[i]; } } } else { // Access method is static. // Requires an explicit 'this' argument. argTypes = new Type[nargs+1]; argTypes[0] = classType; for (int i = 0; i < nargs; i++) { argTypes[i+1] = at[i]; } } t = Type.tMethod(t.getReturnType(), argTypes); } } int nlen = argTypes.length; long where = field.getWhere(); IdentifierToken names[] = new IdentifierToken[nlen]; for (int i = 0; i < nlen; i++) { names[i] = new IdentifierToken(where, Identifier.lookup("$"+i)); } Expression access = null; Expression thisArg = null; Expression args[] = null; if (isStatic) { args = new Expression[nlen]; for (int i = 0 ; i < nlen ; i++) { args[i] = new IdentifierExpression(names[i]); } } else { if (field.isConstructor()) { // Constructor access method is non-static, so // 'this' works normally. thisArg = new ThisExpression(where); // Remove dummy argument, as it is not // passed to the target method. args = new Expression[nlen-1]; for (int i = 1 ; i < nlen ; i++) { args[i-1] = new IdentifierExpression(names[i]); } } else { // Non-constructor access method is static, so // we use the first argument as 'this'. thisArg = new IdentifierExpression(names[0]); // Remove first argument. args = new Expression[nlen-1]; for (int i = 1 ; i < nlen ; i++) { args[i-1] = new IdentifierExpression(names[i]); } } access = thisArg; } if (!isMethod) { access = new FieldExpression(where, access, field); if (isUpdate) { access = new AssignExpression(where, access, args[0]); } } else { // If true, 'isSuper' forces a non-virtual call. access = new MethodExpression(where, access, field, args, isSuper); } Statement code; if (t.getReturnType().isType(TC_VOID)) { code = new ExpressionStatement(where, access); } else { code = new ReturnStatement(where, access); } Statement body[] = { code }; code = new CompoundStatement(where, body); // Access methods are now static (constructors excepted), and no longer final. // This change was mandated by the interaction of the access method // naming conventions and the restriction against overriding final // methods. int mod = M_SYNTHETIC; if (!field.isConstructor()) { mod |= M_STATIC; } // Create the synthetic method within the class in which the referenced // private member appears. The 'env' argument to 'makeMemberDefinition' // is suspect because it represents the environment at the point at // which a reference takes place, while it should represent the // environment in which the definition of the synthetic method appears. // We get away with this because 'env' is used only to access globals // such as 'Environment.error', and also as an argument to // 'resolveTypeStructure', which immediately discards it using // 'setupEnv'. Apparently, the current definition of 'setupEnv' // represents a design change that has not been thoroughly propagated. // An access method is declared with same list of exceptions as its // target. As the exceptions are simply listed by name, the correctness // of this approach requires that the access method be checked // (name-resolved) in the same context as its target method This // should always be the case. SourceMember newf = (SourceMember) env.makeMemberDefinition(env, where, this, null, mod, t, anm, names, field.getExceptionIds(), code); // Just to be safe, copy over the name-resolved exceptions from the // target so that the context in which the access method is checked // doesn't matter. newf.setExceptions(field.getExceptions(env)); newf.setAccessMethodTarget(field); if (isUpdate) { af.setAccessUpdateMember(newf); } newf.setIsSuperAccessMethod(isSuper); // The call to 'check' is not needed, as the access method will be // checked by the containing class after it is added. This is the // idiom followed in the implementation of class literals. (See // 'FieldExpression.java'.) In any case, the context is wrong in the // call below. The access method must be checked in the context in // which it is declared, i.e., the class containing the referenced // private member, not the (inner) class in which the original member // reference occurs. // // try { // newf.check(env, ctx, new Vset()); // } catch (ClassNotFound ee) { // env.error(where, "class.not.found", ee.name, this); // } // The comment above is inaccurate. While it is often the case // that the containing class will check the access method, this is // by no means guaranteed. In fact, an access method may be added // after the checking of its class is complete. In this case, however, // the context in which the class was checked will have been saved in // the class definition object (by the fix for 4095716), allowing us // to check the field now, and in the correct context. // This fixes bug 4098093. Context checkContext = newf.getClassDefinition().getClassContext(); if (checkContext != null) { //System.out.println("checking late addition: " + this); try { newf.check(env, checkContext, new Vset()); } catch (ClassNotFound ee) { env.error(where, "class.not.found", ee.name, this); } } //System.out.println("[Access member '" + // newf + "' created for field '" + // field +"' in class '" + this + "']"); return newf; } /** * Find an inner class of 'this', chosen arbitrarily. * Result is always an actual class, never an interface. * Returns null if none found. */ SourceClass findLookupContext() { // Look for an immediate inner class. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { SourceClass ic = (SourceClass)f.getInnerClass(); if (!ic.isInterface()) { return ic; } } } // Look for a class nested within an immediate inner interface. // At this point, we have given up on finding a minimally-nested // class (which would require a breadth-first traversal). It doesn't // really matter which inner class we find. for (MemberDefinition f = getFirstMember(); f != null; f = f.getNextMember()) { if (f.isInnerClass()) { SourceClass lc = ((SourceClass)f.getInnerClass()).findLookupContext(); if (lc != null) { return lc; } } } // No inner classes. return null; } private MemberDefinition lookup = null; /** * Get helper method for class literal lookup. */ public MemberDefinition getClassLiteralLookup(long fwhere) { // If we have already created a lookup method, reuse it. if (lookup != null) { return lookup; } // If the current class is a nested class, make sure we put the // lookup method in the outermost class. Set 'lookup' for the // intervening inner classes so we won't have to do the search // again. if (outerClass != null) { lookup = outerClass.getClassLiteralLookup(fwhere); return lookup; } // If we arrive here, there was no existing 'class$' method. ClassDefinition c = this; boolean needNewClass = false; if (isInterface()) { // The top-level type is an interface. Try to find an existing // inner class in which to create the helper method. Any will do. c = findLookupContext(); if (c == null) { // The interface has no inner classes. Create an anonymous // inner class to hold the helper method, as an interface must // not have any methods. The tests above for prior creation // of a 'class$' method assure that only one such class is // allocated for each outermost class containing a class // literal embedded somewhere within. Part of fix for 4055017. needNewClass = true; IdentifierToken sup = new IdentifierToken(fwhere, idJavaLangObject); IdentifierToken interfaces[] = {}; IdentifierToken t = new IdentifierToken(fwhere, idNull); int mod = M_PUBLIC | M_ANONYMOUS | M_STATIC | M_SYNTHETIC; c = (SourceClass) toplevelEnv.makeClassDefinition(toplevelEnv, fwhere, t, null, mod, sup, interfaces, this); } } // The name of the class-getter stub is "class$" Identifier idDClass = Identifier.lookup(prefixClass); Type strarg[] = { Type.tString }; // Some sanity checks of questionable value. // // This check became useless after matchMethod() was modified // to not return synthetic methods. // //try { // lookup = c.matchMethod(toplevelEnv, c, idDClass, strarg); //} catch (ClassNotFound ee) { // throw new CompilerError("unexpected missing class"); //} catch (AmbiguousMember ee) { // throw new CompilerError("synthetic name clash"); //} //if (lookup != null && lookup.getClassDefinition() == c) { // // Error if method found was not inherited. // throw new CompilerError("unexpected duplicate"); //} // Some sanity checks of questionable value. /* // The helper function looks like this. * // It simply maps a checked exception to an unchecked one. * static Class class$(String class$) { * try { return Class.forName(class$); } * catch (ClassNotFoundException forName) { * throw new NoClassDefFoundError(forName.getMessage()); * } * } */ long w = c.getWhere(); IdentifierToken arg = new IdentifierToken(w, idDClass); Expression e = new IdentifierExpression(arg); Expression a1[] = { e }; Identifier idForName = Identifier.lookup("forName"); e = new MethodExpression(w, new TypeExpression(w, Type.tClassDesc), idForName, a1); Statement body = new ReturnStatement(w, e); // map the exceptions Identifier idClassNotFound = Identifier.lookup("java.lang.ClassNotFoundException"); Identifier idNoClassDefFound = Identifier.lookup("java.lang.NoClassDefFoundError"); Type ctyp = Type.tClass(idClassNotFound); Type exptyp = Type.tClass(idNoClassDefFound); Identifier idGetMessage = Identifier.lookup("getMessage"); e = new IdentifierExpression(w, idForName); e = new MethodExpression(w, e, idGetMessage, new Expression[0]); Expression a2[] = { e }; e = new NewInstanceExpression(w, new TypeExpression(w, exptyp), a2); Statement handler = new CatchStatement(w, new TypeExpression(w, ctyp), new IdentifierToken(idForName),