com.tangosol.dev.compiler.java.NameExpression Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of coherence Show documentation
Show all versions of coherence Show documentation
Oracle Coherence Community Edition
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.dev.compiler.java;
import com.tangosol.dev.assembler.CodeAttribute;
import com.tangosol.dev.compiler.CompilerException;
import com.tangosol.dev.compiler.Context;
import com.tangosol.dev.compiler.PackageInfo;
import com.tangosol.dev.compiler.TypeInfo;
import com.tangosol.dev.compiler.FieldInfo;
import com.tangosol.dev.component.DataType;
import com.tangosol.util.ErrorList;
import java.util.Set;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
/**
* Implements a simple or qualified name expression. This class is basically
* the "ambiguous name" referred to in the Java Language Specification.
*
* @version 1.00, 10/05/98
* @author Cameron Purdy
*/
public class NameExpression extends Expression implements TokenConstants
{
// ----- construction ---------------------------------------------------
/**
* Construct a NameExpression.
*
* @param block the containing block
* @param tokName the simple name
*/
public NameExpression(Block block, Token tokName)
{
super(block, tokName);
}
// ----- code generation ------------------------------------------------
/**
* Perform semantic checks, parse tree re-organization, name binding,
* and optimizations.
*
* @param ctx the compiler context
* @param setUVars the set of potentially unassigned variables
* @param setFVars the set of potentially assigned final variables
* @param mapThrown the set of potentially thrown checked exceptions
* @param errlist the error list
*
* @return the resulting language element (typically this)
*
* @exception CompilerException thrown if an error occurs that should
* stop the compilation process
*/
protected Element precompile(Context ctx, DualSet setUVars, DualSet setFVars, Map mapThrown, ErrorList errlist)
throws CompilerException
{
Expression expr = this;
boolean fQualified = isQualified();
Block block = getBlock();
Token tokName = getStartToken();
String sName = tokName.getText();
if (fQualified)
{
// JLS 6.5.2 Reclassification of Contextually Ambiguous Names
// If the AmbiguousName is a qualified name, consisting of a
// name, a ".", and an Identifier, then the name to the left
// of the "." is first reclassified, for it is itself an
// AmbiguousName. There is then a choice:
// 1) If the name to the left of the "." is reclassified as
// a PackageName, then there is a further choice:
// 1) If there is a package whose name is the name to
// the left of the "." and that package contains a
// declaration of a type whose name is the same as the
// Identifier, then this AmbiguousName is reclassified
// as a TypeName.
// 2) Otherwise, this AmbiguousName is reclassified as a
// PackageName. A later step determines whether or not
// a package of that name actually exists.
// 2) If the name to the left of the "." is reclassified as a
// TypeName, then this AmbiguousName is reclassified as an
// ExpressionName.
// 3) If the name to the left of the "." is reclassified as an
// ExpressionName, then this AmbiguousName is reclassified
// as an ExpressionName.
//
// Translation:
// 1) Process each portion of the name, left to right.
// 2) As soon as some portion of the name is known to be a
// field or variable, then all subsequent portions of the
// name are considered to be field expressions.
// 3) Otherwise, if the name resolves to a class type, then
// all subsequent portions of the name are considered to
// be field expressions.
// 4) Otherwise, if the name resolves to a component type,
// and subsequent portions exist, then it cannot be
// determined yet whether those portions of the name
// thus far processed represent a type or a package.
List listNames = this.listNames;
int cNames = listNames.size();
int iName = 0;
// check if the very first part of the name is a variable or
// field, which means that the rest are field expressions
Variable var;
FieldInfo field;
if ((var = block.getVariable(sName)) != null)
{
VariableExpression exprVar = new VariableExpression(block, tokName);
exprVar.setAssignee(fAssignee);
expr = exprVar;
// next portion of the name is a field accessor
iName = 1;
}
else if ((field = ctx.getMethodInfo().getTypeInfo().getFieldInfo(sName)) != null
&& !field.isViaAccessor())
{
// a field accessor (implied "this" or this class name);
Token tokThis = new Token(Token.TOK_THIS,
tokName.getLine(), tokName.getOffset(), 0);
Token tokDot = new Token(Token.TOK_DOT,
tokName.getLine(), tokName.getOffset(), 0);
expr = new FieldAccessExpression(tokDot,
new ThisExpression(block, tokThis), tokName);
// next portion of the name is a field accessor
iName = 1;
}
else
{
// the first n portions of the name identify a type; the type
// extent (n) is determined when:
// 1) the type name identifies a class type
// 2) the next portion is a field
// an error occurs when:
// 1) all portions have been processed and a type has not been
// identified (i.e. the name refers to a package)
// verify that the first portion of the name is a type or a
// package; otherwise assume the first portion was supposed
// to be a variable name
PackageInfo pkg = null;
TypeInfo type = null;
// build a list of names that represents just the type
// portion(s) of the name
List listType = new ArrayList();
// check if the name imports a type
DataType dt = ctx.getImport(sName);
if (dt != null)
{
// name is imported; look up the type info
type = ctx.getTypeInfo(dt);
if (type == null)
{
// the type was imported but cannot be found
tokName.logError(ERROR, IMPORT_NOT_FOUND, new String[]
{dt.isComponent() ? dt.getComponentName()
: dt.getClassName()}, errlist);
}
}
// check:
// 1) if the name is a type in this package
// 2) if the name is a type in the root package (i.e. the
// simple name is fully qualified)
// 3) if the name is a package name
else if ((type = ctx.getMethodInfo().getTypeInfo().getPackageInfo()
.getTypeInfo (sName)) == null
&& (type = ctx.getTypeInfo (sName)) == null
&& (pkg = ctx.getPackageInfo(sName)) == null)
{
// assume it was supposed to be a variable name
logError(ERROR, VAR_NOT_FOUND, new String[] {sName}, errlist);
}
// if the name found either (or both) a type or a package,
// then determine at which point (if any) the name refers
// to a field
if (pkg != null || type != null)
{
if (type != null)
{
// check if the type is also a package
pkg = type.getPackageInfo().getPackageInfo(sName);
}
while (true)
{
// add the name portion as a part of the type
listType.add(tokName);
// advance to next portion of the name
++iName;
tokName = (Token) listNames.get(iName);
sName = tokName.getText();
// check if name is a field
if (type != null && (field = type.getFieldInfo(sName)) != null
&& !field.isViaAccessor())
{
// store portion(s) of name that is type as the
// new name (the rest of the name becomes field
// access expressions)
this.listNames = listType;
expr = new TypeExpression(this);
break;
}
// check if name is still a package and/or a type
if (pkg == null)
{
// the current name cannot specify a type
// because the previous name didn't specify
// a package
type = null;
}
else
{
type = pkg.getTypeInfo (sName);
pkg = pkg.getPackageInfo(sName);
}
if (pkg == null && type == null)
{
// the portion of the name processed so far
// is neither a package nor a type so the
// name can be neither a type nor a field
break;
}
if (iName == cNames - 1)
{
if (type == null)
{
// out of name portions and the name is not a
// type (strangely enough, it is a package)
break;
}
else
{
// the entire name expression is a type
iName = cNames;
expr = new TypeExpression(this);
break;
}
}
}
if (expr == this)
{
// assemble name of type or package that was verified
// to exist
StringBuffer sb = new StringBuffer();
int c = listType.size();
for (int i = 0; i < c; ++i)
{
if (i > 0)
{
sb.append('.');
}
sb.append(((Token) listType.get(i)).getText());
}
String sTypeOrPkg = sb.toString();
if (ctx.getTypeInfo(sTypeOrPkg) != null)
{
logError(ERROR, FIELD_NOT_FOUND, new String[]
{sName, sTypeOrPkg}, errlist);
}
else
{
logError(ERROR, TYPE_NOT_FOUND, new String[]
{sName, sTypeOrPkg}, errlist);
}
}
}
}
if (expr != this)
{
// convert the remainder of the name to field expressions
for (; iName < cNames; ++iName)
{
// the field name
tokName = (Token) listNames.get(iName);
// fake the dot (.field)
Token tokDot = new Token(Token.TOK_DOT,
tokName.getLine(), tokName.getOffset(), 0);
expr = new FieldAccessExpression(tokDot, expr, tokName);
}
}
}
else
{
// JLS 6.5.2 Reclassification of Contextually Ambiguous Names
// If the AmbiguousName is a simple name, consisting of a single
// Identifier:
// 1) If the Identifier appears within the scope (6.3) of a
// local variable declaration (14.3) or parameter
// declaration (8.4.1, 8.6.1, 14.18) with that name, then
// the AmbiguousName is reclassified as an ExpressionName.
// 2) Otherwise, consider the class or interface C within whose
// declaration the Identifier occurs. If C has one or more
// fields with that name, which may be either declared
// within it or inherited, then the AmbiguousName is
// reclassified as an ExpressionName.
// 3) Otherwise, if a type of that name is declared in the
// compilation unit (7.3) containing the Identifier, either
// by a single-type-import declaration (7.5.1) or by a
// class or interface type declaration (7.6), then the
// AmbiguousName is reclassified as a TypeName.
// 4) Otherwise, if a type of that name is declared in another
// compilation unit (7.3) of the package (7.1) of the
// compilation unit containing the Identifier, then the
// AmbiguousName is reclassified as a TypeName.
// 5) Otherwise, if a type of that name is declared by exactly
// one type-import-on-demand declaration (7.5.2) of the
// compilation unit containing the Identifier, then the
// AmbiguousName is reclassified as a TypeName.
// 6) Otherwise, if a type of that name is declared by more
// than one type-import-on-demand declaration of the
// compilation unit containing the Identifier, then a
// compile-time error results.
// 7) Otherwise, the AmbiguousName is reclassified as a
// PackageName. A later step determines whether or not a
// package of that name actually exists.
//
// Note: Rule 5 is not applicable since type-import-on-demand
// declarations are not supported.
// Note: Rule 6 is not applicable for the same reason.
// Note: Rule 7 is not applicable because there are no
// constructs within which a package name would be legal.
// In other words, a NameExpression, by its context, must
// always resolve to a type or a variable/value.
// check if the name is a variable
Variable var;
FieldInfo field;
if ((var = block.getVariable(sName)) != null)
{
VariableExpression exprVar = new VariableExpression(block, tokName);
exprVar.setAssignee(fAssignee);
expr = exprVar;
}
// check if the name is a field
else if ((field = ctx.getMethodInfo().getTypeInfo().getFieldInfo(sName)) != null
&& !field.isViaAccessor())
{
// a field accessor (implied "this" or this class name);
// replace with a field access expression against an
// implied this
Token tokThis = new Token(Token.TOK_THIS,
tokName.getLine(), tokName.getOffset(), 0);
Token tokDot = new Token(Token.TOK_DOT,
tokName.getLine(), tokName.getOffset(), 0);
expr = new FieldAccessExpression(tokDot,
new ThisExpression(block, tokThis), tokName);
}
// check if the name imports a type
else if (ctx.getImport(sName) != null)
{
expr = new TypeExpression(this);
}
// check if the name is a type in this package
else if (ctx.getMethodInfo().getTypeInfo().getPackageInfo().getTypeInfo(sName) != null)
{
expr = new TypeExpression(this);
}
// check if the name is a type in the root package (i.e. the
// simple name is fully qualified)
else if (ctx.getTypeInfo(sName) != null)
{
expr = new TypeExpression(this);
}
// assume it was supposed to be a variable name
else
{
logError(ERROR, VAR_NOT_FOUND, new String[] {sName}, errlist);
}
}
if (expr != this)
{
expr = (Expression) expr.precompile(ctx, setUVars, setFVars, mapThrown, errlist);
}
return expr;
}
/**
* Perform final optimizations and code generation.
*
* @param ctx the compiler context
* @param code the assembler code attribute to compile to
* @param fReached true if this language element is reached (JLS 14.19)
* @param errlist the error list to log errors to
*
* @return true if the element can complete normally (JLS 14.1)
*
* @exception CompilerException thrown if an error occurs that should
* stop the compilation process
*/
protected boolean compile(Context ctx, CodeAttribute code, boolean fReached, ErrorList errlist)
throws CompilerException
{
throw new IllegalStateException();
}
// ----- Expression methods ---------------------------------------------
/**
* Check that the expression is assignable (a "variable"). This call
* may occur before pre-compilation.
*
* @param errlist the error list to log errors to
*
* @return true if the expression is a variable
*
* @exception CompilerException thrown if an error occurs that should
* stop the compilation process
*/
protected boolean checkAssignable(ErrorList errlist)
throws CompilerException
{
setAssignee(true);
return true;
}
// ----- accessors ------------------------------------------------------
/**
* Get the name.
*
* @return the name
*/
public String getName()
{
String sName;
if (isQualified())
{
StringBuffer sb = new StringBuffer();
List listNames = this.listNames;
int c = listNames.size();
for (int i = 0; i < c; ++i)
{
if (i > 0)
{
sb.append('.');
}
sb.append(((Token) listNames.get(i)).getText());
}
sName = sb.toString();
}
else
{
sName = getStartToken().getText();
}
return sName;
}
/**
* Determine how many parts are in the name expression. One part is
* in a simple name expression; more than one part is in a qualified
* name expression.
*
* @return the number of parts in the name expression
*/
public int getTokenCount()
{
return listNames == null ? 1 : listNames.size();
}
/**
* Look up the i-th name in the name expression.
*
* @param i zero-based index
*
* @return the token holding the i-th portion of the name expression
*/
public Token getToken(int i)
{
return listNames == null ? getStartToken() : (Token) listNames.get(i);
}
/**
* Determine if the name is qualified.
*
* @return true if the name is qualified
*/
public boolean isQualified()
{
List listNames = this.listNames;
return listNames != null && listNames.size() > 1;
}
/**
* Add a name to the name expression. For example, adding "b" to "a"
* results in "a.b".
*
* @param tokName the name token to add
*/
protected void addName(Token tokName)
{
List listNames = this.listNames;
if (listNames == null)
{
this.listNames = listNames = new ArrayList();
listNames.add(getStartToken());
}
// update end token
setEndToken(tokName);
// add to list
listNames.add(tokName);
}
/**
* Remove a name from the name expression. For example, removing from
* "a.b" returns "b" and results in "a".
*
* @return the token for the last simple part of the name
*/
protected Token removeName()
{
List listNames = this.listNames;
if (listNames != null)
{
int iName = listNames.size() - 1;
// update end token (if any tokens will remain)
if (iName >= 1)
{
setEndToken((Token) listNames.get(iName-1));
}
// remove from list and return
return (Token) listNames.remove(iName);
}
else
{
return getStartToken();
}
}
/**
* Determine if this variable expression is an assignee. In other words,
* is this expression being used on the left-hand-side of an assignment.
*
* @return true if this variable expression is being assigned to
*/
public boolean isAssignee()
{
return fAssignee;
}
/**
* Specify that this variable expression is an assignee.
*
* @param fAssignee if this variable expression is being assigned to
*/
protected void setAssignee(boolean fAssignee)
{
this.fAssignee = fAssignee;
}
// ----- Object methods -------------------------------------------------
/**
* Format the element information as a string.
*
* @return a human-readable description of the element
*/
public String toString()
{
return super.toString() + " " + getName();
}
// ----- data members ---------------------------------------------------
/**
* The class name.
*/
private static final String CLASS = "NameExpression";
/**
* Qualified names are stored as an array of tokens.
*/
private List listNames;
/**
* If this name expression is used as a "left-hand-side" of an assignment
* operation.
*/
private boolean fAssignee;
}