|
Java example source code file (FieldExpression.java)
The FieldExpression.java Java example source code
/*
* Copyright (c) 1994, 2013, 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.*;
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 FieldExpression extends UnaryExpression {
Identifier id;
MemberDefinition field;
Expression implementation;
// The class from which the field is select ed.
ClassDefinition clazz;
// For an expression of the form '<class>.super', then
// this is <class>, else null.
private ClassDefinition superBase;
/**
* constructor
*/
public FieldExpression(long where, Expression right, Identifier id) {
super(FIELD, where, Type.tError, right);
this.id = id;
}
public FieldExpression(long where, Expression right, MemberDefinition field) {
super(FIELD, where, field.getType(), right);
this.id = field.getName();
this.field = field;
}
public Expression getImplementation() {
if (implementation != null)
return implementation;
return this;
}
/**
* Return true if the field is being selected from
* a qualified 'super'.
*/
private boolean isQualSuper() {
return superBase != null;
}
/**
* Convert an '.' expression to a qualified identifier
*/
static public Identifier toIdentifier(Expression e) {
StringBuffer buf = new StringBuffer();
while (e.op == FIELD) {
FieldExpression fe = (FieldExpression)e;
if (fe.id == idThis || fe.id == idClass) {
return null;
}
buf.insert(0, fe.id);
buf.insert(0, '.');
e = fe.right;
}
if (e.op != IDENT) {
return null;
}
buf.insert(0, ((IdentifierExpression)e).id);
return Identifier.lookup(buf.toString());
}
/**
* Convert a qualified name into a type.
* Performs a careful check of each inner-class component,
* including the JLS 6.6.1 access checks that were omitted
* in 'FieldExpression.toType'.
* <p>
* This code is similar to 'checkCommon', which could be cleaned
* up a bit long the lines we have done here.
*/
/*-------------------------------------------------------*
Type toQualifiedType(Environment env, Context ctx) {
ClassDefinition ctxClass = ctx.field.getClassDefinition();
Type rty = right.toQualifiedType(env, ctx);
if (rty == Type.tPackage) {
// Is this field expression a non-inner type?
Identifier nm = toIdentifier(this);
if ((nm != null) && env.classExists(nm)) {
Type t = Type.tClass(nm);
if (env.resolve(where, ctxClass, t)) {
return t;
} else {
return null;
}
}
// Not a type. Must be a package prefix.
return Type.tPackage;
}
if (rty == null) {
// An error was already reported, so quit.
return null;
}
// Check inner-class qualification while unwinding from recursion.
try {
ClassDefinition rightClass = env.getClassDefinition(rty);
// Local variables, which cannot be inner classes,
// are ignored here, and thus will not hide inner
// classes. Is this correct?
MemberDefinition field = rightClass.getInnerClass(env, id);
if (field == null) {
env.error(where, "inner.class.expected", id, rightClass);
return Type.tError;
}
ClassDefinition innerClass = field.getInnerClass();
Type t = innerClass.getType();
if (!ctxClass.canAccess(env, field)) {
env.error(where, "no.type.access", id, rightClass, ctxClass);
return t;
}
if (field.isProtected()
&& !ctxClass.protectedAccess(env, field, rty)) {
env.error(where, "invalid.protected.type.use", id, ctxClass, rty);
return t;
}
// These were omitted earlier in calls to 'toType', but I can't
// see any reason for that. I think it was an oversight. See
// 'checkCommon' and 'checkInnerClass'.
innerClass.noteUsedBy(ctxClass, where, env);
ctxClass.addDependency(field.getClassDeclaration());
return t;
} catch (ClassNotFound e) {
env.error(where, "class.not.found", e.name, ctx.field);
}
// Class not found.
return null;
}
*-------------------------------------------------------*/
/**
* Convert an '.' expression to a type
*/
// This is a rewrite to treat qualified names in a
// context in which a type name is expected in the
// same way that they are handled for an ambiguous
// or expression-expected context in 'checkCommon'
// below. The new code is cleaner and allows better
// localization of errors. Unfortunately, most
// qualified names appearing in types are actually
// handled by 'Environment.resolve'. There isn't
// much point, then, in breaking out 'toType' as a
// special case until the other cases can be cleaned
// up as well. For the time being, we will leave this
// code disabled, thus reducing the testing requirements.
/*-------------------------------------------------------*
Type toType(Environment env, Context ctx) {
Type t = toQualifiedType(env, ctx);
if (t == null) {
return Type.tError;
}
if (t == Type.tPackage) {
FieldExpression.reportFailedPackagePrefix(env, right, true);
return Type.tError;
}
return t;
}
*-------------------------------------------------------*/
Type toType(Environment env, Context ctx) {
Identifier id = toIdentifier(this);
if (id == null) {
env.error(where, "invalid.type.expr");
return Type.tError;
}
Type t = Type.tClass(ctx.resolveName(env, id));
if (env.resolve(where, ctx.field.getClassDefinition(), t)) {
return t;
}
return Type.tError;
}
/**
* Check if the present name is part of a scoping prefix.
*/
public Vset checkAmbigName(Environment env, Context ctx,
Vset vset, Hashtable exp,
UnaryExpression loc) {
if (id == idThis || id == idClass) {
loc = null; // this cannot be a type or package
}
return checkCommon(env, ctx, vset, exp, loc, false);
}
/**
* Check the expression
*/
public Vset checkValue(Environment env, Context ctx,
Vset vset, Hashtable exp) {
vset = checkCommon(env, ctx, vset, exp, null, false);
if (id == idSuper && type != Type.tError) {
// "super" is not allowed in this context.
// It must always qualify another name.
env.error(where, "undef.var.super", idSuper);
}
return vset;
}
/**
* If 'checkAmbiguousName' returns 'Package.tPackage', then it was
* unable to resolve any prefix of the qualified name. This method
* attempts to diagnose the problem.
*/
static void reportFailedPackagePrefix(Environment env, Expression right) {
reportFailedPackagePrefix(env, right, false);
}
static void reportFailedPackagePrefix(Environment env,
Expression right,
boolean mustBeType) {
// Find the leftmost component, and put the blame on it.
Expression idp = right;
while (idp instanceof UnaryExpression)
idp = ((UnaryExpression)idp).right;
IdentifierExpression ie = (IdentifierExpression)idp;
// It may be that 'ie' refers to an ambiguous class. Check this
// with a call to env.resolve(). Part of solution for 4059855.
try {
env.resolve(ie.id);
} catch (AmbiguousClass e) {
env.error(right.where, "ambig.class", e.name1, e.name2);
return;
} catch (ClassNotFound e) {
}
if (idp == right) {
if (mustBeType) {
env.error(ie.where, "undef.class", ie.id);
} else {
env.error(ie.where, "undef.var.or.class", ie.id);
}
} else {
if (mustBeType) {
env.error(ie.where, "undef.class.or.package", ie.id);
} else {
env.error(ie.where, "undef.var.class.or.package", ie.id);
}
}
}
/**
* Rewrite accesses to private fields of another class.
*/
private Expression
implementFieldAccess(Environment env, Context ctx, Expression base, boolean isLHS) {
ClassDefinition abase = accessBase(env, ctx);
if (abase != null) {
// If the field is final and its initializer is a constant expression,
// then just rewrite to the constant expression. This is not just an
// optimization, but is required for correctness. If an expression is
// rewritten to use an access method, then its status as a constant
// expression is lost. This was the cause of bug 4098737. Note that
// a call to 'getValue(env)' below would not be correct, as it attempts
// to simplify the initial value expression, which must not occur until
// after the checking phase, for example, after definite assignment checks.
if (field.isFinal()) {
Expression e = (Expression)field.getValue();
// Must not be LHS here. Test as a precaution,
// as we may not be careful to avoid this when
// compiling an erroneous program.
if ((e != null) && e.isConstant() && !isLHS) {
return e.copyInline(ctx);
}
}
//System.out.println("Finding access method for " + field);
MemberDefinition af = abase.getAccessMember(env, ctx, field, isQualSuper());
//System.out.println("Using access method " + af);
if (!isLHS) {
//System.out.println("Reading " + field +
// " via access method " + af);
// If referencing the value of the field, then replace
// with a call to the access method. If assigning to
// the field, a call to the update method will be
// generated later. It is important that
// 'implementation' not be set to non-null if the
// expression is a valid assignment target.
// (See 'checkLHS'.)
if (field.isStatic()) {
Expression args[] = { };
Expression call =
new MethodExpression(where, null, af, args);
return new CommaExpression(where, base, call);
} else {
Expression args[] = { base };
return new MethodExpression(where, null, af, args);
}
}
}
return null;
}
/**
* Determine if an access method is required, and, if so, return
* the class in which it should appear, else return null.
*/
private ClassDefinition accessBase(Environment env, Context ctx) {
if (field.isPrivate()) {
ClassDefinition cdef = field.getClassDefinition();
ClassDefinition ctxClass = ctx.field.getClassDefinition();
if (cdef == ctxClass){
// If access from same class as field, then no access
// method is needed.
return null;
}
// An access method is needed in the class containing the field.
return cdef;
} else if (field.isProtected()) {
if (superBase == null) {
// If access is not via qualified super, then it is either
// OK without an access method, or it is an illegal access
// for which an error message should have been issued.
// Legal accesses include unqualified 'super.foo'.
return null;
}
ClassDefinition cdef = field.getClassDefinition();
ClassDefinition ctxClass = ctx.field.getClassDefinition();
if (cdef.inSamePackage(ctxClass)) {
// Access to protected member in same package always allowed.
return null;
}
// Access via qualified super.
// An access method is needed in the qualifying class, an
// immediate subclass of the class containing the selected
// field. NOTE: The fact that the returned class is 'superBase'
// carries the additional bit of information (that a special
// superclass access method is being created) which is provided
// to 'getAccessMember' via its 'isSuper' argument.
return superBase;
} else {
// No access method needed.
return null;
}
}
/**
* Determine if a type is accessible from a given class.
*/
static boolean isTypeAccessible(long where,
Environment env,
Type t,
ClassDefinition c) {
switch (t.getTypeCode()) {
case TC_CLASS:
try {
Identifier nm = t.getClassName();
// Why not just use 'Environment.getClassDeclaration' here?
// But 'Environment.getClassDeclation' has special treatment
// for local classes that is probably necessary. This code
// was adapted from 'Environment.resolve'.
ClassDefinition def = env.getClassDefinition(t);
return c.canAccess(env, def.getClassDeclaration());
} catch (ClassNotFound e) {} // Ignore -- reported elsewhere.
return true;
case TC_ARRAY:
return isTypeAccessible(where, env, t.getElementType(), c);
default:
return true;
}
}
/**
* Common code for checkValue and checkAmbigName
*/
private Vset checkCommon(Environment env, Context ctx,
Vset vset, Hashtable exp,
UnaryExpression loc, boolean isLHS) {
// Handle class literal, e.g., 'x.class'.
if (id == idClass) {
// In 'x.class', 'x' must be a type name, possibly qualified.
Type t = right.toType(env, ctx);
if (!t.isType(TC_CLASS) && !t.isType(TC_ARRAY)) {
if (t.isType(TC_ERROR)) {
type = Type.tClassDesc;
return vset;
}
String wrc = null;
switch (t.getTypeCode()) {
case TC_VOID: wrc = "Void"; break;
case TC_BOOLEAN: wrc = "Boolean"; break;
case TC_BYTE: wrc = "Byte"; break;
case TC_CHAR: wrc = "Character"; break;
case TC_SHORT: wrc = "Short"; break;
case TC_INT: wrc = "Integer"; break;
case TC_FLOAT: wrc = "Float"; break;
case TC_LONG: wrc = "Long"; break;
case TC_DOUBLE: wrc = "Double"; break;
default:
env.error(right.where, "invalid.type.expr");
return vset;
}
Identifier wid = Identifier.lookup(idJavaLang+"."+wrc);
Expression wcls = new TypeExpression(where, Type.tClass(wid));
implementation = new FieldExpression(where, wcls, idTYPE);
vset = implementation.checkValue(env, ctx, vset, exp);
type = implementation.type; // java.lang.Class
return vset;
}
// Check for the bogus type `array of void'
if (t.isVoidArray()) {
type = Type.tClassDesc;
env.error(right.where, "void.array");
return vset;
}
// it is a class or array
long fwhere = ctx.field.getWhere();
ClassDefinition fcls = ctx.field.getClassDefinition();
MemberDefinition lookup = fcls.getClassLiteralLookup(fwhere);
String sig = t.getTypeSignature();
String className;
if (t.isType(TC_CLASS)) {
// sig is like "Lfoo/bar;", name is like "foo.bar".
// We assume SIG_CLASS and SIG_ENDCLASS are 1 char each.
className = sig.substring(1, sig.length()-1)
.replace(SIGC_PACKAGE, '.');
} else {
// sig is like "[Lfoo/bar;" or "[I";
// name is like "[Lfoo.bar" or (again) "[I".
className = sig.replace(SIGC_PACKAGE, '.');
}
if (fcls.isInterface()) {
// The immediately-enclosing type is an interface.
// The class literal can only appear in an initialization
// expression, so don't bother caching it. (This could
// lose if many initializations use the same class literal,
// but saves time and code space otherwise.)
implementation =
makeClassLiteralInlineRef(env, ctx, lookup, className);
} else {
// Cache the call to the helper, as it may be executed
// many times (e.g., if the class literal is inside a loop).
ClassDefinition inClass = lookup.getClassDefinition();
MemberDefinition cfld =
getClassLiteralCache(env, ctx, className, inClass);
implementation =
makeClassLiteralCacheRef(env, ctx, lookup, cfld, className);
}
vset = implementation.checkValue(env, ctx, vset, exp);
type = implementation.type; // java.lang.Class
return vset;
}
// Arrive here if not a class literal.
if (field != null) {
// The field as been pre-set, e.g., as the result of transforming
// an 'IdentifierExpression'. Most error-checking has already been
// performed at this point.
// QUERY: Why don't we further unify checking of identifier
// expressions and field expressions that denote instance and
// class variables?
implementation = implementFieldAccess(env, ctx, right, isLHS);
return (right == null) ?
vset : right.checkAmbigName(env, ctx, vset, exp, this);
}
// Does the qualifier have a meaning of its own?
vset = right.checkAmbigName(env, ctx, vset, exp, this);
if (right.type == Type.tPackage) {
// Are we out of options?
if (loc == null) {
FieldExpression.reportFailedPackagePrefix(env, right);
return vset;
}
// ASSERT(loc.right == this)
// Nope. Is this field expression a type?
Identifier nm = toIdentifier(this);
if ((nm != null) && env.classExists(nm)) {
loc.right = new TypeExpression(where, Type.tClass(nm));
// Check access. (Cf. IdentifierExpression.toResolvedType.)
ClassDefinition ctxClass = ctx.field.getClassDefinition();
env.resolve(where, ctxClass, loc.right.type);
return vset;
}
// Let the caller make sense of it, then.
type = Type.tPackage;
return vset;
}
// Good; we have a well-defined qualifier type.
ClassDefinition ctxClass = ctx.field.getClassDefinition();
boolean staticRef = (right instanceof TypeExpression);
try {
// Handle array 'length' field, e.g., 'x.length'.
if (!right.type.isType(TC_CLASS)) {
if (right.type.isType(TC_ARRAY) && id.equals(idLength)) {
// Verify that the type of the base expression is accessible.
// Required by JLS 6.6.1. Fixes 4094658.
if (!FieldExpression.isTypeAccessible(where, env, right.type, ctxClass)) {
ClassDeclaration cdecl = ctxClass.getClassDeclaration();
if (staticRef) {
env.error(where, "no.type.access",
id, right.type.toString(), cdecl);
} else {
env.error(where, "cant.access.member.type",
id, right.type.toString(), cdecl);
}
}
type = Type.tInt;
implementation = new LengthExpression(where, right);
return vset;
}
if (!right.type.isType(TC_ERROR)) {
env.error(where, "invalid.field.reference", id, right.type);
}
return vset;
}
// At this point, we know that 'right.type' is a class type.
// Note that '<expr>.super(...)' and '
Other Java examples (source code examples)Here is a short list of links related to this Java FieldExpression.java source code file: |
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.