thisc = prevc.getOuterClass(); if (prevc == conCls) { // Must pick up "this$C" from the constructor argument, // not from "this.this$C", since the latter may not be // initialized properly. (This way is cheaper too.) Identifier nm = outerMember.getName(); IdentifierExpression arg = new IdentifierExpression(where, nm); arg.bind(env, this); thise = arg; } else { thise = new FieldExpression(where, thise, outerMember); } } if (thise != null) { // mark crossed scopes // ????? //ensureAvailable(root); return thise; } if (f == null) { // say something like: undefined variable A.this Identifier nm = reqc.getName().getFlatName().getName(); env.error(where, "undef.var", Identifier.lookup(nm,idThis)); } else if (f.isConstructor()) { env.error(where, "no.outer.arg", reqc, f.getClassDefinition()); } else { env.error(where, "no.static.field.access", f, field); } // avoid floodgating: Expression e = new ThisExpression(where, this); e.type = reqc.getType(); return e; } /** * Is there a "this" of type reqc in scope? */ public static boolean outerLinkExists(Environment env, ClassDefinition reqc, ClassDefinition thisc) { while (!match(env, thisc, reqc)) { if (thisc.isTopLevel()) { return false; } thisc = thisc.getOuterClass(); } return true; } /** * From which enclosing class do members of this type come? */ public ClassDefinition findScope(Environment env, ClassDefinition reqc) { ClassDefinition thisc = field.getClassDefinition(); while (thisc != null && !match(env, thisc, reqc)) { thisc = thisc.getOuterClass(); } return thisc; } /** * Resolve a type name from within a local scope. * @see Environment#resolveName */ Identifier resolveName(Environment env, Identifier name) { // This logic is pretty much exactly parallel to that of // Environment.resolveName(). if (name.isQualified()) { // Try to resolve the first identifier component, // because inner class names take precedence over // package prefixes. (Cf. Environment.resolveName.) Identifier rhead = resolveName(env, name.getHead()); if (rhead.hasAmbigPrefix()) { // The first identifier component refers to an // ambiguous class. Limp on. We throw away the // rest of the classname as it is irrelevant. // (part of solution for 4059855). return rhead; } if (!env.classExists(rhead)) { return env.resolvePackageQualifiedName(name); } try { return env.getClassDefinition(rhead). resolveInnerClass(env, name.getTail()); } catch (ClassNotFound ee) { // return partially-resolved name someone else can fail on return Identifier.lookupInner(rhead, name.getTail()); } } // Look for an unqualified name in enclosing scopes. try { MemberDefinition f = getClassCommon(env, name, false); if (f != null) { return f.getInnerClass().getName(); } } catch (ClassNotFound ee) { // a missing superclass, or something catastrophic } // look in imports, etc. return env.resolveName(name); } /** * Return the name of a lexically apparent type, * skipping inherited members, and ignoring * the current pacakge and imports. * This is used for error checking. */ public Identifier getApparentClassName(Environment env, Identifier name) { if (name.isQualified()) { // Try to resolve the first identifier component, // because inner class names take precedence over // package prefixes. (Cf. Environment.resolveName.) Identifier rhead = getApparentClassName(env, name.getHead()); return (rhead == null) ? idNull : Identifier.lookup(rhead, name.getTail()); } // Look for an unqualified name in enclosing scopes. try { MemberDefinition f = getClassCommon(env, name, true); if (f != null) { return f.getInnerClass().getName(); } } catch (ClassNotFound ee) { // a missing superclass, or something catastrophic } // the enclosing class name is the only apparent package member: Identifier topnm = field.getClassDefinition().getTopClass().getName(); if (topnm.getName().equals(name)) { return topnm; } return idNull; } /** * Raise an error if a blank final was definitely unassigned * on entry to a loop, but has possibly been assigned on the * back-branch. If this is the case, the loop may be assigning * it multiple times. */ public void checkBackBranch(Environment env, Statement loop, Vset vsEntry, Vset vsBack) { for (LocalMember f = locals ; f != null ; f = f.prev) { if (f.isBlankFinal() && vsEntry.testVarUnassigned(f.number) && !vsBack.testVarUnassigned(f.number)) { env.error(loop.where, "assign.to.blank.final.in.loop", f.getName()); } } } /** * Check if a field can reach another field (only considers * forward references, not the access modifiers). */ public boolean canReach(Environment env, MemberDefinition f) { return field.canReach(env, f); } /** * Get the context that corresponds to a label, return null if * not found. */ public Context getLabelContext(Identifier lbl) { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if ((ctx.node != null) && (ctx.node instanceof Statement)) { if (((Statement)(ctx.node)).hasLabel(lbl)) return ctx; } } return null; } /** * Get the destination context of a break */ public Context getBreakContext(Identifier lbl) { if (lbl != null) { return getLabelContext(lbl); } for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case SWITCH: case FOR: case DO: case WHILE: return ctx; } } } return null; } /** * Get the destination context of a continue */ public Context getContinueContext(Identifier lbl) { if (lbl != null) { return getLabelContext(lbl); } for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case FOR: case DO: case WHILE: return ctx; } } } return null; } /** * Get the destination context of a return (the method body) */ public CheckContext getReturnContext() { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { // The METHOD node is set up by Statement.checkMethod(). if (ctx.node != null && ctx.node.op == METHOD) { return (CheckContext)ctx; } } return null; } /** * Get the context of the innermost surrounding try-block. * Consider only try-blocks contained within the same method. * (There could be others when searching from within a method * of a local class, but they are irrelevant to our purpose.) * This is used for recording DA/DU information preceding * all abnormal transfers of control: break, continue, return, * and throw. */ public CheckContext getTryExitContext() { for (Context ctx = this; ctx != null && ctx.node != null && ctx.node.op != METHOD; ctx = ctx.prev) { if (ctx.node.op == TRY) { return (CheckContext)ctx; } } return null; } /** * Get the nearest inlined context */ Context getInlineContext() { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case INLINEMETHOD: case INLINENEWINSTANCE: return ctx; } } } return null; } /** * Get the context of a field that is being inlined */ Context getInlineMemberContext(MemberDefinition field) { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case INLINEMETHOD: if (((InlineMethodExpression)ctx.node).field.equals(field)) { return ctx; } break; case INLINENEWINSTANCE: if (((InlineNewInstanceExpression)ctx.node).field.equals(field)) { return ctx; } } } } return null; } /** * Remove variables from the vset set that are no longer part of * this context. */ public final Vset removeAdditionalVars(Vset vset) { return vset.removeAdditionalVars(varNumber); } public final int getVarNumber() { return varNumber; } /** * Return the number of the innermost current instance reference. */ public int getThisNumber() { LocalMember thisf = getLocalField(idThis); if (thisf != null && thisf.getClassDefinition() == field.getClassDefinition()) { return thisf.number; } // this is a variable; there is no "this" (should not happen) return varNumber; } /** * Return the field containing the present context. */ public final MemberDefinition getField() { return field; } /** * Extend an environment with the given context. * The resulting environment behaves the same as * the given one, except that resolveName() takes * into account local class names in this context. */ public static Environment newEnvironment(Environment env, Context ctx) { return new ContextEnvironment(env, ctx); }} finalclass ContextEnvironment extends Environment { Context ctx; Environment innerEnv; ContextEnvironment(Environment env, Context ctx) { super(env, env.getSource()); this.ctx = ctx; this.innerEnv = env; } public Identifier resolveName(Identifier name) { return ctx.resolveName(innerEnv, name); }}Lines 501–861 of 861. Previous lines
thisc = prevc.getOuterClass(); if (prevc == conCls) { // Must pick up "this$C" from the constructor argument, // not from "this.this$C", since the latter may not be // initialized properly. (This way is cheaper too.) Identifier nm = outerMember.getName(); IdentifierExpression arg = new IdentifierExpression(where, nm); arg.bind(env, this); thise = arg; } else { thise = new FieldExpression(where, thise, outerMember); } } if (thise != null) { // mark crossed scopes // ????? //ensureAvailable(root); return thise; } if (f == null) { // say something like: undefined variable A.this Identifier nm = reqc.getName().getFlatName().getName(); env.error(where, "undef.var", Identifier.lookup(nm,idThis)); } else if (f.isConstructor()) { env.error(where, "no.outer.arg", reqc, f.getClassDefinition()); } else { env.error(where, "no.static.field.access", f, field); } // avoid floodgating: Expression e = new ThisExpression(where, this); e.type = reqc.getType(); return e; } /** * Is there a "this" of type reqc in scope? */ public static boolean outerLinkExists(Environment env, ClassDefinition reqc, ClassDefinition thisc) { while (!match(env, thisc, reqc)) { if (thisc.isTopLevel()) { return false; } thisc = thisc.getOuterClass(); } return true; } /** * From which enclosing class do members of this type come? */ public ClassDefinition findScope(Environment env, ClassDefinition reqc) { ClassDefinition thisc = field.getClassDefinition(); while (thisc != null && !match(env, thisc, reqc)) { thisc = thisc.getOuterClass(); } return thisc; } /** * Resolve a type name from within a local scope. * @see Environment#resolveName */ Identifier resolveName(Environment env, Identifier name) { // This logic is pretty much exactly parallel to that of // Environment.resolveName(). if (name.isQualified()) { // Try to resolve the first identifier component, // because inner class names take precedence over // package prefixes. (Cf. Environment.resolveName.) Identifier rhead = resolveName(env, name.getHead()); if (rhead.hasAmbigPrefix()) { // The first identifier component refers to an // ambiguous class. Limp on. We throw away the // rest of the classname as it is irrelevant. // (part of solution for 4059855). return rhead; } if (!env.classExists(rhead)) { return env.resolvePackageQualifiedName(name); } try { return env.getClassDefinition(rhead). resolveInnerClass(env, name.getTail()); } catch (ClassNotFound ee) { // return partially-resolved name someone else can fail on return Identifier.lookupInner(rhead, name.getTail()); } } // Look for an unqualified name in enclosing scopes. try { MemberDefinition f = getClassCommon(env, name, false); if (f != null) { return f.getInnerClass().getName(); } } catch (ClassNotFound ee) { // a missing superclass, or something catastrophic } // look in imports, etc. return env.resolveName(name); } /** * Return the name of a lexically apparent type, * skipping inherited members, and ignoring * the current pacakge and imports. * This is used for error checking. */ public Identifier getApparentClassName(Environment env, Identifier name) { if (name.isQualified()) { // Try to resolve the first identifier component, // because inner class names take precedence over // package prefixes. (Cf. Environment.resolveName.) Identifier rhead = getApparentClassName(env, name.getHead()); return (rhead == null) ? idNull : Identifier.lookup(rhead, name.getTail()); } // Look for an unqualified name in enclosing scopes. try { MemberDefinition f = getClassCommon(env, name, true); if (f != null) { return f.getInnerClass().getName(); } } catch (ClassNotFound ee) { // a missing superclass, or something catastrophic } // the enclosing class name is the only apparent package member: Identifier topnm = field.getClassDefinition().getTopClass().getName(); if (topnm.getName().equals(name)) { return topnm; } return idNull; } /** * Raise an error if a blank final was definitely unassigned * on entry to a loop, but has possibly been assigned on the * back-branch. If this is the case, the loop may be assigning * it multiple times. */ public void checkBackBranch(Environment env, Statement loop, Vset vsEntry, Vset vsBack) { for (LocalMember f = locals ; f != null ; f = f.prev) { if (f.isBlankFinal() && vsEntry.testVarUnassigned(f.number) && !vsBack.testVarUnassigned(f.number)) { env.error(loop.where, "assign.to.blank.final.in.loop", f.getName()); } } } /** * Check if a field can reach another field (only considers * forward references, not the access modifiers). */ public boolean canReach(Environment env, MemberDefinition f) { return field.canReach(env, f); } /** * Get the context that corresponds to a label, return null if * not found. */ public Context getLabelContext(Identifier lbl) { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if ((ctx.node != null) && (ctx.node instanceof Statement)) { if (((Statement)(ctx.node)).hasLabel(lbl)) return ctx; } } return null; } /** * Get the destination context of a break */ public Context getBreakContext(Identifier lbl) { if (lbl != null) { return getLabelContext(lbl); } for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case SWITCH: case FOR: case DO: case WHILE: return ctx; } } } return null; } /** * Get the destination context of a continue */ public Context getContinueContext(Identifier lbl) { if (lbl != null) { return getLabelContext(lbl); } for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case FOR: case DO: case WHILE: return ctx; } } } return null; } /** * Get the destination context of a return (the method body) */ public CheckContext getReturnContext() { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { // The METHOD node is set up by Statement.checkMethod(). if (ctx.node != null && ctx.node.op == METHOD) { return (CheckContext)ctx; } } return null; } /** * Get the context of the innermost surrounding try-block. * Consider only try-blocks contained within the same method. * (There could be others when searching from within a method * of a local class, but they are irrelevant to our purpose.) * This is used for recording DA/DU information preceding * all abnormal transfers of control: break, continue, return, * and throw. */ public CheckContext getTryExitContext() { for (Context ctx = this; ctx != null && ctx.node != null && ctx.node.op != METHOD; ctx = ctx.prev) { if (ctx.node.op == TRY) { return (CheckContext)ctx; } } return null; } /** * Get the nearest inlined context */ Context getInlineContext() { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case INLINEMETHOD: case INLINENEWINSTANCE: return ctx; } } } return null; } /** * Get the context of a field that is being inlined */ Context getInlineMemberContext(MemberDefinition field) { for (Context ctx = this ; ctx != null ; ctx = ctx.prev) { if (ctx.node != null) { switch (ctx.node.op) { case INLINEMETHOD: if (((InlineMethodExpression)ctx.node).field.equals(field)) { return ctx; } break; case INLINENEWINSTANCE: if (((InlineNewInstanceExpression)ctx.node).field.equals(field)) { return ctx; } } } } return null; } /** * Remove variables from the vset set that are no longer part of * this context. */ public final Vset removeAdditionalVars(Vset vset) { return vset.removeAdditionalVars(varNumber); } public final int getVarNumber() { return varNumber; } /** * Return the number of the innermost current instance reference. */ public int getThisNumber() { LocalMember thisf = getLocalField(idThis); if (thisf != null && thisf.getClassDefinition() == field.getClassDefinition()) { return thisf.number; } // this is a variable; there is no "this" (should not happen) return varNumber; } /** * Return the field containing the present context. */ public final MemberDefinition getField() { return field; } /** * Extend an environment with the given context. * The resulting environment behaves the same as * the given one, except that resolveName() takes * into account local class names in this context. */ public static Environment newEnvironment(Environment env, Context ctx) { return new ContextEnvironment(env, ctx); } } final class ContextEnvironment extends Environment { Context ctx; Environment innerEnv; ContextEnvironment(Environment env, Context ctx) { super(env, env.getSource()); this.ctx = ctx; this.innerEnv = env; } public Identifier resolveName(Identifier name) { return ctx.resolveName(innerEnv, name); } }