| /* |
| * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.tools.tree; |
| |
| import sun.tools.java.*; |
| import sun.tools.asm.Assembler; |
| import sun.tools.asm.LocalVariable; |
| import java.io.PrintStream; |
| import java.util.Hashtable; |
| |
| /** |
| * WARNING: The contents of this source file are not part of any |
| * supported API. Code that depends on them does so at its own risk: |
| * they are subject to change or removal without notice. |
| */ |
| public |
| class IdentifierExpression extends Expression { |
| Identifier id; |
| MemberDefinition field; |
| Expression implementation; |
| |
| /** |
| * Constructor |
| */ |
| public IdentifierExpression(long where, Identifier id) { |
| super(IDENT, where, Type.tError); |
| this.id = id; |
| } |
| public IdentifierExpression(IdentifierToken id) { |
| this(id.getWhere(), id.getName()); |
| } |
| public IdentifierExpression(long where, MemberDefinition field) { |
| super(IDENT, where, field.getType()); |
| this.id = field.getName(); |
| this.field = field; |
| } |
| |
| public Expression getImplementation() { |
| if (implementation != null) |
| return implementation; |
| return this; |
| } |
| |
| /** |
| * Check if the expression is equal to a value |
| */ |
| public boolean equals(Identifier id) { |
| return this.id.equals(id); |
| } |
| |
| |
| /** |
| * Assign a value to this identifier. [It must already be "bound"] |
| */ |
| private Vset assign(Environment env, Context ctx, Vset vset) { |
| if (field.isLocal()) { |
| LocalMember local = (LocalMember)field; |
| if (local.scopeNumber < ctx.frameNumber) { |
| env.error(where, "assign.to.uplevel", id); |
| } |
| if (local.isFinal()) { |
| // allow definite single assignment of blank finals |
| if (!local.isBlankFinal()) { |
| env.error(where, "assign.to.final", id); |
| } else if (!vset.testVarUnassigned(local.number)) { |
| env.error(where, "assign.to.blank.final", id); |
| } |
| } |
| vset.addVar(local.number); |
| local.writecount++; |
| } else if (field.isFinal()) { |
| vset = FieldExpression.checkFinalAssign(env, ctx, vset, |
| where, field); |
| } |
| return vset; |
| } |
| |
| /** |
| * Get the value of this identifier. [ It must already be "bound"] |
| */ |
| private Vset get(Environment env, Context ctx, Vset vset) { |
| if (field.isLocal()) { |
| LocalMember local = (LocalMember)field; |
| if (local.scopeNumber < ctx.frameNumber && !local.isFinal()) { |
| env.error(where, "invalid.uplevel", id); |
| } |
| if (!vset.testVar(local.number)) { |
| env.error(where, "var.not.initialized", id); |
| vset.addVar(local.number); |
| } |
| local.readcount++; |
| } else { |
| if (!field.isStatic()) { |
| if (!vset.testVar(ctx.getThisNumber())) { |
| env.error(where, "access.inst.before.super", id); |
| implementation = null; |
| } |
| } |
| if (field.isBlankFinal()) { |
| int number = ctx.getFieldNumber(field); |
| if (number >= 0 && !vset.testVar(number)) { |
| env.error(where, "var.not.initialized", id); |
| } |
| } |
| } |
| return vset; |
| } |
| |
| /** |
| * Bind to a field |
| */ |
| boolean bind(Environment env, Context ctx) { |
| try { |
| field = ctx.getField(env, id); |
| if (field == null) { |
| for (ClassDefinition cdef = ctx.field.getClassDefinition(); |
| cdef != null; cdef = cdef.getOuterClass()) { |
| if (cdef.findAnyMethod(env, id) != null) { |
| env.error(where, "invalid.var", id, |
| ctx.field.getClassDeclaration()); |
| return false; |
| } |
| } |
| env.error(where, "undef.var", id); |
| return false; |
| } |
| |
| type = field.getType(); |
| |
| // Check access permission |
| if (!ctx.field.getClassDefinition().canAccess(env, field)) { |
| env.error(where, "no.field.access", |
| id, field.getClassDeclaration(), |
| ctx.field.getClassDeclaration()); |
| return false; |
| } |
| |
| // Find out how to access this variable. |
| if (field.isLocal()) { |
| LocalMember local = (LocalMember)field; |
| if (local.scopeNumber < ctx.frameNumber) { |
| // get a "val$x" copy via the current object |
| implementation = ctx.makeReference(env, local); |
| } |
| } else { |
| MemberDefinition f = field; |
| |
| if (f.reportDeprecated(env)) { |
| env.error(where, "warn.field.is.deprecated", |
| id, f.getClassDefinition()); |
| } |
| |
| ClassDefinition fclass = f.getClassDefinition(); |
| if (fclass != ctx.field.getClassDefinition()) { |
| // Maybe an inherited field hides an apparent variable. |
| MemberDefinition f2 = ctx.getApparentField(env, id); |
| if (f2 != null && f2 != f) { |
| ClassDefinition c = ctx.findScope(env, fclass); |
| if (c == null) c = f.getClassDefinition(); |
| if (f2.isLocal()) { |
| env.error(where, "inherited.hides.local", |
| id, c.getClassDeclaration()); |
| } else { |
| env.error(where, "inherited.hides.field", |
| id, c.getClassDeclaration(), |
| f2.getClassDeclaration()); |
| } |
| } |
| } |
| |
| // Rewrite as a FieldExpression. |
| // Access methods for private fields, if needed, will be added |
| // during subsequent processing of the FieldExpression. See |
| // method 'FieldExpression.checkCommon'. This division of labor |
| // is somewhat awkward, as most further processing of a |
| // FieldExpression during the checking phase is suppressed when |
| // the referenced field is pre-set as it is here. |
| |
| if (f.isStatic()) { |
| Expression base = new TypeExpression(where, |
| f.getClassDeclaration().getType()); |
| implementation = new FieldExpression(where, null, f); |
| } else { |
| Expression base = ctx.findOuterLink(env, where, f); |
| if (base != null) { |
| implementation = new FieldExpression(where, base, f); |
| } |
| } |
| } |
| |
| // Check forward reference |
| if (!ctx.canReach(env, field)) { |
| env.error(where, "forward.ref", |
| id, field.getClassDeclaration()); |
| return false; |
| } |
| return true; |
| } catch (ClassNotFound e) { |
| env.error(where, "class.not.found", e.name, ctx.field); |
| } catch (AmbiguousMember e) { |
| env.error(where, "ambig.field", id, |
| e.field1.getClassDeclaration(), |
| e.field2.getClassDeclaration()); |
| } |
| return false; |
| } |
| |
| /** |
| * Check expression |
| */ |
| public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable exp) { |
| if (field != null) { |
| // An internally pre-set field, such as an argument copying |
| // an uplevel value. Do not re-check it. |
| return vset; |
| } |
| if (bind(env, ctx)) { |
| vset = get(env, ctx, vset); |
| ctx.field.getClassDefinition().addDependency(field.getClassDeclaration()); |
| if (implementation != null) |
| vset = implementation.checkValue(env, ctx, vset, exp); |
| } |
| return vset; |
| } |
| |
| /** |
| * Check the expression if it appears on the LHS of an assignment |
| */ |
| public Vset checkLHS(Environment env, Context ctx, |
| Vset vset, Hashtable exp) { |
| if (!bind(env, ctx)) |
| return vset; |
| vset = assign(env, ctx, vset); |
| if (implementation != null) |
| vset = implementation.checkValue(env, ctx, vset, exp); |
| return vset; |
| } |
| |
| /** |
| * Check the expression if it appears on the LHS of an op= expression |
| */ |
| public Vset checkAssignOp(Environment env, Context ctx, |
| Vset vset, Hashtable exp, Expression outside) { |
| if (!bind(env, ctx)) |
| return vset; |
| vset = assign(env, ctx, get(env, ctx, vset)); |
| if (implementation != null) |
| vset = implementation.checkValue(env, ctx, vset, exp); |
| return vset; |
| } |
| |
| /** |
| * Return an accessor if one is needed for assignments to this expression. |
| */ |
| public FieldUpdater getAssigner(Environment env, Context ctx) { |
| if (implementation != null) |
| return implementation.getAssigner(env, ctx); |
| return null; |
| } |
| |
| /** |
| * Return an updater if one is needed for assignments to this expression. |
| */ |
| public FieldUpdater getUpdater(Environment env, Context ctx) { |
| if (implementation != null) |
| return implementation.getUpdater(env, ctx); |
| return null; |
| } |
| |
| /** |
| * Check if the present name is part of a scoping prefix. |
| */ |
| public Vset checkAmbigName(Environment env, Context ctx, Vset vset, Hashtable exp, |
| UnaryExpression loc) { |
| try { |
| if (ctx.getField(env, id) != null) { |
| // if this is a local field, there's nothing more to do. |
| return checkValue(env, ctx, vset, exp); |
| } |
| } catch (ClassNotFound ee) { |
| } catch (AmbiguousMember ee) { |
| } |
| // Can this be interpreted as a type? |
| ClassDefinition c = toResolvedType(env, ctx, true); |
| // Is it a real type?? |
| if (c != null) { |
| loc.right = new TypeExpression(where, c.getType()); |
| return vset; |
| } |
| // We hope it is a package prefix. Let the caller decide. |
| type = Type.tPackage; |
| return vset; |
| } |
| |
| /** |
| * Convert an identifier to a known type, or null. |
| */ |
| private ClassDefinition toResolvedType(Environment env, Context ctx, |
| boolean pkgOK) { |
| Identifier rid = ctx.resolveName(env, id); |
| Type t = Type.tClass(rid); |
| if (pkgOK && !env.classExists(t)) { |
| return null; |
| } |
| if (env.resolve(where, ctx.field.getClassDefinition(), t)) { |
| try { |
| ClassDefinition c = env.getClassDefinition(t); |
| |
| // Maybe an inherited class hides an apparent class. |
| if (c.isMember()) { |
| ClassDefinition sc = ctx.findScope(env, c.getOuterClass()); |
| if (sc != c.getOuterClass()) { |
| Identifier rid2 = ctx.getApparentClassName(env, id); |
| if (!rid2.equals(idNull) && !rid2.equals(rid)) { |
| env.error(where, "inherited.hides.type", |
| id, sc.getClassDeclaration()); |
| } |
| } |
| } |
| |
| if (!c.getLocalName().equals(id.getFlatName().getName())) { |
| env.error(where, "illegal.mangled.name", id, c); |
| } |
| |
| return c; |
| } catch (ClassNotFound ee) { |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Convert an identifier to a type. |
| * If one is not known, use the current package as a qualifier. |
| */ |
| Type toType(Environment env, Context ctx) { |
| ClassDefinition c = toResolvedType(env, ctx, false); |
| if (c != null) { |
| return c.getType(); |
| } |
| return Type.tError; |
| } |
| |
| /** |
| * Convert an expresion to a type in a context where a qualified |
| * type name is expected, e.g., in the prefix of a qualified type |
| * name. We do not necessarily know where the package prefix ends, |
| * so we operate similarly to 'checkAmbiguousName'. This is the |
| * base case -- the first component of the qualified name. |
| */ |
| /*-------------------------------------------------------* |
| Type toQualifiedType(Environment env, Context ctx) { |
| // We do not look for non-type fields. Is this correct? |
| ClassDefinition c = toResolvedType(env, ctx, true); |
| // Is it a real type? |
| if (c != null) { |
| return c.getType(); |
| } |
| // We hope it is a package prefix. Let the caller decide. |
| return Type.tPackage; |
| } |
| *-------------------------------------------------------*/ |
| |
| /** |
| * Check if constant: Will it inline away? |
| */ |
| public boolean isConstant() { |
| if (implementation != null) |
| return implementation.isConstant(); |
| if (field != null) { |
| return field.isConstant(); |
| } |
| return false; |
| } |
| |
| /** |
| * Inline |
| */ |
| public Expression inline(Environment env, Context ctx) { |
| return null; |
| } |
| public Expression inlineValue(Environment env, Context ctx) { |
| if (implementation != null) |
| return implementation.inlineValue(env, ctx); |
| if (field == null) { |
| return this; |
| } |
| try { |
| if (field.isLocal()) { |
| if (field.isInlineable(env, false)) { |
| Expression e = (Expression)field.getValue(env); |
| return (e == null) ? this : e.inlineValue(env, ctx); |
| } |
| return this; |
| } |
| return this; |
| } catch (ClassNotFound e) { |
| throw new CompilerError(e); |
| } |
| } |
| public Expression inlineLHS(Environment env, Context ctx) { |
| if (implementation != null) |
| return implementation.inlineLHS(env, ctx); |
| return this; |
| } |
| |
| public Expression copyInline(Context ctx) { |
| if (implementation != null) |
| return implementation.copyInline(ctx); |
| IdentifierExpression e = |
| (IdentifierExpression)super.copyInline(ctx); |
| if (field != null && field.isLocal()) { |
| e.field = ((LocalMember)field).getCurrentInlineCopy(ctx); |
| } |
| return e; |
| } |
| |
| public int costInline(int thresh, Environment env, Context ctx) { |
| if (implementation != null) |
| return implementation.costInline(thresh, env, ctx); |
| return super.costInline(thresh, env, ctx); |
| } |
| |
| /** |
| * Code local vars (object fields have been inlined away) |
| */ |
| int codeLValue(Environment env, Context ctx, Assembler asm) { |
| return 0; |
| } |
| void codeLoad(Environment env, Context ctx, Assembler asm) { |
| asm.add(where, opc_iload + type.getTypeCodeOffset(), |
| new Integer(((LocalMember)field).number)); |
| } |
| void codeStore(Environment env, Context ctx, Assembler asm) { |
| LocalMember local = (LocalMember)field; |
| asm.add(where, opc_istore + type.getTypeCodeOffset(), |
| new LocalVariable(local, local.number)); |
| } |
| public void codeValue(Environment env, Context ctx, Assembler asm) { |
| codeLValue(env, ctx, asm); |
| codeLoad(env, ctx, asm); |
| } |
| |
| /** |
| * Print |
| */ |
| public void print(PrintStream out) { |
| out.print(id + "#" + ((field != null) ? field.hashCode() : 0)); |
| if (implementation != null) { |
| out.print("/IMPL="); |
| implementation.print(out); |
| } |
| } |
| } |