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