/*
* Copyright 2008-2009 Sun Microsystems, Inc. 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.
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package org.visage.tools.comp;
import org.visage.api.VisageBindStatus;
import com.sun.tools.mjavac.code.Flags;
import com.sun.tools.mjavac.code.Kinds;
import com.sun.tools.mjavac.code.Scope.Entry;
import com.sun.tools.mjavac.code.Symbol;
import com.sun.tools.mjavac.code.Symbol.ClassSymbol;
import com.sun.tools.mjavac.code.Symbol.MethodSymbol;
import com.sun.tools.mjavac.code.Type;
import com.sun.tools.mjavac.code.Type.*;
import com.sun.tools.mjavac.tree.JCTree;
import com.sun.tools.mjavac.tree.JCTree.JCExpression;
import com.sun.tools.mjavac.tree.JCTree.JCStatement;
import com.sun.tools.mjavac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.mjavac.util.List;
import com.sun.tools.mjavac.util.ListBuffer;
import com.sun.tools.mjavac.util.Name;
import org.visage.tools.code.VisageFlags;
import org.visage.tools.code.VisageTypes;
import org.visage.tools.code.VisageSymtab;
import org.visage.tools.code.VisageVarSymbol;
import org.visage.tools.comp.VisageAbstractTranslation.*;
import org.visage.tools.tree.*;
import com.sun.tools.mjavac.code.Symbol.VarSymbol;
import static com.sun.tools.mjavac.code.Flags.*;
import java.util.*;
/**
* This class is used by VisageInitializationBuilder to determine which inherited
* attributes and methods have effect in the current visage class.
* @author Robert Field
* @author Jim Laskey
*/
class VisageAnalyzeClass {
// Invoking translator.
private final VisageInitializationBuilder initBuilder;
// Position in the current visage class source.
private final DiagnosticPosition diagPos;
// Current class decl.
private final VisageClassDeclaration currentClassDecl;
// Current class symbol.
private final ClassSymbol currentClassSym;
// Null or symbol for the immediate super class (if a visage class.)
private ClassSymbol superClassSym;
// Resulting list of all superclasses in top down order.
private ListBuffer<ClassSymbol> superClasses = ListBuffer.lb();
// Resulting list of immediate mixin classes in left to right order.
private ListBuffer<ClassSymbol> immediateMixins = ListBuffer.lb();
// Resulting list of all mixin classes in top down order.
private ListBuffer<ClassSymbol> allMixins = ListBuffer.lb();
// Number of vars in the current class (includes mixins.)
private int classVarCount;
// Number of vars in the current class (includes mixins.)
private int scriptVarCount;
// Resulting list of class vars.
private final ListBuffer<VarInfo> classVarInfos = ListBuffer.lb();
// Resulting list of script vars.
private final ListBuffer<VarInfo> scriptVarInfos = ListBuffer.lb();
// Resulting list of class vars.
private final ListBuffer<FuncInfo> classFuncInfos = ListBuffer.lb();
// Resulting list of script vars.
private final ListBuffer<FuncInfo> scriptFuncInfos = ListBuffer.lb();
// List of all attributes. Used to track overridden and mixin attributes.
private final Map<Name, VarInfo> visitedAttributes = new HashMap<Name, VarInfo>();
// Map of all bind selects used to construct the class update$ method.
private final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> classUpdateMap = new LinkedHashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>>();
// Map of all bind selects used to construct the script update$ method.
private final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> scriptUpdateMap = new LinkedHashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>>();
// Resulting list of relevant methods. A map is used to so that only the last occurrence is kept.
private final Map<String, FuncInfo> needDispatchMethods = new LinkedHashMap<String, FuncInfo>();
// List of all methods. Used to track whether a mixin method should be included.
private final Map<String, FuncInfo> visitedMethods = new HashMap<String, FuncInfo>();
// List of classes encountered. Used to prevent duplication.
private final Set<Symbol> addedBaseClasses = new HashSet<Symbol>();
// List of vars (attributes) found in the current visage class (supplied by VisageInitializationBuilder.)
private final List<TranslatedVarInfo> translatedAttrInfo;
// List of overriding vars (attributes) found in the current visage class (supplied by VisageInitializationBuilder.)
private final List<TranslatedOverrideClassVarInfo> translatedOverrideAttrInfo;
// List of functions (methods) found in the current visage class (supplied by VisageInitializationBuilder.)
private final List<TranslatedFuncInfo> translatedFuncInfo;
// Global names table (supplied by VisageInitializationBuilder.)
private final Name.Table names;
// Global types table (supplied by VisageInitializationBuilder.)
private final VisageTypes types;
// Global defs table (supplied by VisageInitializationBuilder.)
private final VisageDefs defs;
// Global syms table (supplied by VisageInitializationBuilder.)
private final VisageSymtab syms;
// Class reader used to fetch superclass .class files (supplied by VisageInitializationBuilder.)
private final VisageClassReader reader;
//
// This class supers all classes used to hold var information. Consumed by
// VisageInitializationBuilder.
//
static abstract class VarInfo {
// Position of the var declaration or current visage class if read from superclass.
private final DiagnosticPosition diagPos;
// Var symbol (unique to symbol.)
protected final VisageVarSymbol sym;
// Name of the var (is it the same as sym.name?)
private final Name name;
// Null or code for initializing the var.
protected final JCExpression initExpr;
// The class local enumeration value for this var.
private int enumeration = -1;
// Inversion of boundBindees.
private HashSet<VarInfo> binders = new LinkedHashSet<VarInfo>();
// Inversion of invalidators.
private ListBuffer<BindeeInvalidator> boundInvalidatees = ListBuffer.lb();;
// True if the var needs to generate mixin interfaces (getMixin$, setMixin$ and getVOFF$)
private boolean needsMixinInterface = false;
private VarInfo(DiagnosticPosition diagPos, Name name, VisageVarSymbol attrSym, JCExpression initExpr) {
this.diagPos = diagPos;
this.name = name;
this.sym = attrSym;
this.initExpr = initExpr;
}
// Return the var symbol.
public VisageVarSymbol getSymbol() { return sym; }
// Return the var position.
public DiagnosticPosition pos() { return diagPos; }
// Return type information from type translation.
public Type getRealType() { return sym.type; }
public Type getElementType() { return sym.getElementType(); }
public boolean useAccessors() { return sym.useAccessors(); }
public boolean useGetters() { return sym.useGetters(); }
public boolean useSetters() { return sym.useSetters() || needsMixinInterface(); }
// Return var name.
public Name getName() { return name; }
// Return var name as string.
public String getNameString() { return name.toString(); }
// Return modifier flags from the symbol.
public long getFlags() { return sym.flags(); }
// Return true if the var is a bare bones synthesize var (bind temp.)
public boolean isBareSynth() {
return (getFlags() & VisageFlags.VARMARK_BARE_SYNTH) != 0;
}
// Returns true if the var is read only.
public boolean isReadOnly() {
return isDef() || (hasBoundDefinition() && !hasBiDiBoundDefinition());
}
// Returns true if the var needs to generate mixin interfaces (getMixin$, setMixin$ and getVOFF$)
public boolean needsMixinInterface() {
return needsMixinInterface;
}
// Indicate if the var needs a mixin interface.
public void setNeedsMixinInterface(boolean needs) {
needsMixinInterface = needs;
}
// Return true if the var/override has an initializing expression
public boolean hasInitializer() { return false; }
// true if this variable is initialized with a "safe" expression -
// so that we don't need a try..catch error handler wrapper in it's
// getter method.
public boolean hasSafeInitializer() { return false; }
// Return true if the var has a VarInit
public boolean hasVarInit() { return false; }
// is this initialzed with a bound function result var?
public boolean isInitWithBoundFuncResult() { return false; }
// Is this variable is initialized by another synthetic variable
// of Pointer type and that var stores result from a bound function call?
// If so, return the symbol of the synthetic variable.
public Symbol boundFuncResultInitSym() { return null; }
// Return true if the var has a bound definition.
public boolean hasBoundDefinition() { return false; }
// Return true if the var has a bidirectional bind.
public boolean hasBiDiBoundDefinition() { return false; }
// Return true if the var is an inline bind.
public boolean isInlinedBind() { return hasBoundDefinition(); }
// Generally means that the var needs to be included in the current method.
public boolean needsCloning() { return false; }
// Return true if the var is an override.
public boolean isOverride() { return false; }
// A proxy var serves several roles, but generally means that the proxy's
// qualified name should be used in place of the current var's qualified name.
public VarInfo proxyVar() { return null; }
public boolean hasProxyVar() { return proxyVar() != null; }
// An override is non-null when a mixin var is overridden in the mixee.
public VarInfo overrideVar() { return null; }
public boolean hasOverrideVar() { return overrideVar() != null; }
// Convenience method to return the current symbol to be used for qualified name.
public VisageVarSymbol proxyVarSym() { return hasProxyVar() ? proxyVar().sym : sym; }
// Predicate for static var test.
public boolean isStatic() { return sym.isStatic(); }
// Predicate for synthetic var test.
public boolean isSynthetic() { return (getFlags() & Flags.SYNTHETIC) != 0L; }
// Predicate for default var test.
public boolean isDefault() { return (getFlags() & VisageFlags.DEFAULT) != 0L; }
// Predicate for protexted var test.
public boolean isProtectedAccess() { return (getFlags() & Flags.PROTECTED) != 0L; }
// Predicate for public var test.
public boolean isPublicAccess() { return (getFlags() & Flags.PUBLIC) != 0L; }
// Predicate for public read var test.
public boolean isPublicReadAccess() { return (getFlags() & VisageFlags.PUBLIC_READ) != 0L; }
// Predicate for public init var test.
public boolean isPublicInitAccess() { return (getFlags() & VisageFlags.PUBLIC_INIT) != 0L; }
// Predicate for is externally seen test.
public boolean isExternallySeen() { return sym.isExternallySeen(); }
// Predicate for script private var test.
public boolean hasScriptOnlyAccess() { return sym.hasScriptOnlyAccess(); }
// Predicate for def (constant) var.
public boolean isDef() { return sym.isDef(); }
// Predicate for parameter var.
public boolean isParameter() { return sym.isParameter(); }
// Predicate for self-reference in init.
public boolean hasSelfReference() { return sym.hasSelfReference(); }
// Predicate whether the var came from a mixin.
public boolean isMixinVar() { return isMixinClass(sym.owner); }
// Predicate whether the var came from the current visage class.
public boolean isDirectOwner() { return false; }
// Predicate to test for sequence.
public boolean isSequence() {
return sym.isSequence();
}
// Returns null or the code for var initialization.
public JCExpression getDefaultInitExpression() { return initExpr; }
// Class local enumeration accessors.
public int getEnumeration() { return enumeration; }
public void setEnumeration(int enumeration) { this.enumeration = enumeration; }
public boolean hasEnumeration() { return enumeration != -1; }
public boolean needsEnumeration() {
return !isOverride() &&
needsCloning() &&
sym.needsEnumeration();
}
// null or visage tree for the var's 'on replace'.
public VisageOnReplace onReplace() { return null; }
// null or Java tree for var's on-replace for use in a setter.
public JCStatement onReplaceAsInline() { return null; }
// null or Java tree for var's on-replace for use in change listeber.
public JCStatement onReplaceAsListenerInstanciation() { return null; }
// null or visage tree for the var's 'on invalidate'.
public VisageOnReplace onInvalidate() { return null; }
// null or Java tree for var's on-invalidate for use in var$invalidate method.
public JCStatement onInvalidateAsInline() { return null; }
// Is there a getter expression of bound variable
public boolean hasBoundInit() { return false; }
// Null or Java code for getter expression of bound variable
public JCExpression boundInit() { return null; }
// Empty or Java preface code for getter of bound variable
public List<JCStatement> boundPreface() { return List.<JCStatement>nil(); }
// Empty or Java preface code for setting of bound with inverse variable
public List<JCStatement> boundInvSetterPreface() { return List.<JCStatement>nil(); }
// Empty or variable symbols on which this variable depends
public List<VisageVarSymbol> boundBindees() { return List.<VisageVarSymbol>nil(); }
// Bound variable symbols on which this variable is used.
public HashSet<VarInfo> boundBinders() { return binders; }
// Bound sequences that need to be invalidated when invalidated.
public List<BindeeInvalidator> boundInvalidatees() { return boundInvalidatees.toList(); }
// Empty or bound select pairs.
public List<DependentPair> boundBoundSelects() { return List.<DependentPair>nil(); }
// Predicate for generating sequence style accessors -- if bound, must be virtual
public boolean generateSequenceAccessors() { return isSequence() &&
(hasBoundDefinition() == isBoundVirtualSequence()); }
// Predicate for bound sequence represented as virtual sequence
public boolean isBoundVirtualSequence() { return false; }
// Null or Java code for getting the element of a bound sequence
public JCStatement boundElementGetter() { return null; }
// Null or Java code for getting the size of a bound sequence
public JCStatement boundSizeGetter() { return null; }
// Null or Java code for invalidation of a bound sequence
public List<BindeeInvalidator> boundInvalidators() { return List.<BindeeInvalidator>nil(); }
// Return true if the var has dependents.
public boolean hasDependents() {
return (getFlags() & (VisageFlags.VARUSE_BIND_ACCESS | VisageFlags.VARUSE_VARREF)) != 0 || !boundBinders().isEmpty();
}
// Return true if the var is dependent.
public boolean isDependent() {
return (getFlags() & (VisageFlags.VARUSE_BOUND_INIT | VisageFlags.VARUSE_VARREF)) != 0 || hasBiDiBoundDefinition() ||
!boundBindees().isEmpty() || !boundBoundSelects().isEmpty();
}
@Override
public String toString() { return getNameString(); }
// Useful diagnostic tool.
public void printInfo() {
printInfo(true);
}
public void printInfo(boolean detail) {
System.err.println(" " + getEnumeration() + ". " +
getSymbol() +
", type=" + getSymbol().type +
", owner=" + getSymbol().owner +
(isStatic() ? ", static" : "") +
(isDefault() ? ", default" : "") +
(isSynthetic() ? ", synthetic" : "") +
(useAccessors() ? ", useAccessors" : "") +
(needsCloning() ? ", clone" : "") +
(isDef() ? ", isDef" : "") +
(!boundBindees().isEmpty() ? ", intra binds" : "") +
(!boundBoundSelects().isEmpty() ? ", inter binds" : "") +
(binders != null ? ", binders" : "") +
(!boundInvalidatees.isEmpty() ? ", invalidators" : "") +
(getDefaultInitExpression() != null ? ", init" : "") +
(isBareSynth() ? ", bare" : "") +
(needsMixinInterface() ? ", needsMixinInterface" : "") +
", class=" + getClass().getSimpleName());
if (detail) {
long flags = getFlags();
System.err.println(" " +
(((flags & VisageFlags.VARUSE_HAS_REPLACE_TRIGGER) != 0) ? ", VARUSE_HAS_REPLACE_TRIGGER" : "") +
(((flags & VisageFlags.VARUSE_BOUND_INIT) != 0) ? ", VARUSE_BOUND_INIT" : "") +
(((flags & VisageFlags.VARUSE_ASSIGNED_TO) != 0) ? ", VARUSE_ASSIGNED_TO" : "") +
(((flags & VisageFlags.VARUSE_OBJ_LIT_INIT) != 0) ? ", VARUSE_OBJ_LIT_INIT" : "") +
(((flags & VisageFlags.VARUSE_FORWARD_REFERENCE) != 0) ? ", VARUSE_FORWARD_REFERENCE" : "") +
(((flags & VisageFlags.VARUSE_SELF_REFERENCE) != 0) ? ", VARUSE_SELF_REFERENCE" : "") +
(((flags & VisageFlags.VARUSE_OPT_TRIGGER) != 0) ? ", VARUSE_OPT_TRIGGER" : "") +
(((flags & VisageFlags.VARUSE_TMP_IN_INIT_EXPR) != 0) ? ", VARUSE_TMP_IN_INIT_EXPR" : "") +
(((flags & VisageFlags.VARUSE_NEED_ACCESSOR) != 0) ? ", VARUSE_NEED_ACCESSOR" : "") +
(((flags & VisageFlags.VARUSE_NON_LITERAL) != 0) ? ", VARUSE_NON_LITERAL" : "") +
(((flags & VisageFlags.VARUSE_BIND_ACCESS) != 0) ? ", VARUSE_BIND_ACCESS" : "") +
(((flags & VisageFlags.VARUSE_VARREF) != 0) ? ", VARUSE_VARREF" : "") +
(((flags & VisageFlags.VARUSE_SPECIAL) != 0) ? ", VARUSE_SPECIAL" : "") +
(getSymbol().isExternallySeen() ? ", EXTERNALLY SEEN" : ""));
if (!boundBoundSelects().isEmpty()) {
for (DependentPair pair : boundBoundSelects()) {
System.err.println(" select=" + pair.instanceSym + " " + pair.referencedSym);
}
}
if (!boundBindees().isEmpty()) {
for (VisageVarSymbol bindeeSym : boundBindees()) {
System.err.println(" bindee=" + bindeeSym);
}
}
if (hasProxyVar()) {
System.err.print(" proxy=");
proxyVar().printInfo(false);
}
if (hasOverrideVar()) {
System.err.print(" override=");
overrideVar().printInfo(false);
}
if (boundElementGetter() != null) {
System.err.print(" element getter=");
System.err.println(boundElementGetter());
}
if (boundSizeGetter() != null) {
System.err.print(" size getter=");
System.err.println(boundSizeGetter());
}
if (boundInvalidators().size() != 0) {
System.err.println(" invalidators=");
for (BindeeInvalidator bi : boundInvalidators()) {
System.err.println(" " + bi.bindee + " " + bi.invalidator);
}
}
}
}
}
//
// This base class is used for vars declared in the current visage class..
//
static abstract class TranslatedVarInfoBase extends VarInfo {
// Null or visage code for the var's on replace.
private final VisageOnReplace onReplace;
// Null or visage code for the var's on invalidate.
private final VisageOnReplace onInvalidate;
// The bind status for the var/override
private final VisageBindStatus bindStatus;
// The does this var have an initializing expression
private final boolean hasInitializer;
// Null or java code for the var's on replace inlined in setter.
private final JCStatement onReplaceAsInline;
// Null or java code for the var's on invalidate inlined in var$invalidate method.
private final JCStatement onInvalidateAsInline;
// Result of bind translation
private final ExpressionResult bindOrNull;
TranslatedVarInfoBase(DiagnosticPosition diagPos, Name name, VisageVarSymbol attrSym, VisageBindStatus bindStatus, boolean hasInitializer,
JCExpression initExpr, ExpressionResult bindOrNull,
VisageOnReplace onReplace, JCStatement onReplaceAsInline,
VisageOnReplace onInvalidate, JCStatement onInvalidateAsInline) {
super(diagPos, name, attrSym, initExpr);
this.hasInitializer = hasInitializer;
this.bindStatus = bindStatus;
this.bindOrNull = bindOrNull;
this.onReplace = onReplace;
this.onReplaceAsInline = onReplaceAsInline;
this.onInvalidate = onInvalidate;
this.onInvalidateAsInline = onInvalidateAsInline;
}
// Return true if the var/override has an initializing expression
@Override
public boolean hasInitializer() { return hasInitializer; }
// Return true if the var has a bound definition.
@Override
public boolean hasBoundDefinition() { return bindStatus.isBound(); }
// Return true if the var has a bidirectional bind.
@Override
public boolean hasBiDiBoundDefinition() { return bindStatus.isBidiBind(); }
// Is there a getter expression of bound variable
@Override
public boolean hasBoundInit() { return bindOrNull==null? false : bindOrNull.hasExpr(); }
// Null or Java code for getter expression of bound variable
@Override
public JCExpression boundInit() { return bindOrNull==null? null : bindOrNull.expr(); }
// Null or Java preface code for getter of bound variable
@Override
public List<JCStatement> boundPreface() { return bindOrNull==null? List.<JCStatement>nil() : bindOrNull.statements(); }
// Empty or Java preface code for setting of bound with inverse variable
@Override
public List<JCStatement> boundInvSetterPreface() { return bindOrNull==null? List.<JCStatement>nil() : bindOrNull.setterPreface(); }
// Variable symbols on which this variable depends
@Override
public List<VisageVarSymbol> boundBindees() { return bindOrNull==null? List.<VisageVarSymbol>nil() : bindOrNull.bindees(); }
// Empty or bound select pairs.
@Override
public List<DependentPair> boundBoundSelects() { return bindOrNull == null? List.<DependentPair>nil() : bindOrNull.interClass(); }
// Return true if this is a bound sequence represented as virtual sequence
@Override
public boolean isBoundVirtualSequence() { return bindOrNull==null? false : bindOrNull.isBoundVirtualSequence(); }
// Null or Java code for getting the element of a bound sequence
@Override
public JCStatement boundElementGetter() { return !generateSequenceAccessors() || bindOrNull == null ? null : bindOrNull.getElementMethodBody(); }
// Null or Java code for getting the size of a bound sequence
@Override
public JCStatement boundSizeGetter() { return !generateSequenceAccessors() || bindOrNull == null ? null : bindOrNull.getSizeMethodBody(); }
// Null or Java code for invalidation of a bound sequence
@Override
public List<BindeeInvalidator> boundInvalidators() { return bindOrNull == null ? List.<BindeeInvalidator>nil() : bindOrNull.invalidators(); }
// Possible visage code for the var's 'on replace'.
@Override
public VisageOnReplace onReplace() { return onReplace; }
// Possible java code for the var's 'on replace' in setter.
@Override
public JCStatement onReplaceAsInline() { return onReplaceAsInline; }
// Possible visage code for the var's 'on invalidate'.
@Override
public VisageOnReplace onInvalidate() { return onInvalidate; }
// Possible java code for the var's 'on invalidate' in var$invalidate method.
@Override
public JCStatement onInvalidateAsInline() { return onInvalidateAsInline; }
// This var is in the current visage class so it has to be cloned into the java class.
@Override
public boolean needsCloning() { return true; }
// Has to be the current visage class.
@Override
public boolean isDirectOwner() { return true; }
}
//
// This class is used for basic vars declared in the current visage class.
//
static class TranslatedVarInfo extends TranslatedVarInfoBase {
// Tree for the visage var.
private final VisageVar var;
private final Symbol boundFuncResultInitSym;
TranslatedVarInfo(VisageVar var,
JCExpression initExpr, Symbol boundFuncResultInitSym,
ExpressionResult bindOrNull,
VisageOnReplace onReplace, JCStatement onReplaceAsInline,
VisageOnReplace onInvalidate, JCStatement onInvalidateAsInline) {
super(var.pos(), var.sym.name, var.sym, var.getBindStatus(), var.getInitializer()!=null,
initExpr, bindOrNull,
onReplace, onReplaceAsInline, onInvalidate, onInvalidateAsInline);
this.var = var;
this.boundFuncResultInitSym = boundFuncResultInitSym;
}
// Returns the tree for the visage var.
public VisageVar visageVar() { return var; }
@Override
public boolean hasSafeInitializer() {
// If unexaminable code can be executed (function/method call or init block) or
// a division can happen then it is not exception safe.
if (hasInitializer()) {
class ExceptionThrowingScanner extends VisageTreeScanner {
boolean safe = true;
private void markCanThrowException() {
safe = false;
}
@Override
public void visitBinary(VisageBinary tree) {
switch (tree.getVisageTag()) {
case DIV:
case MOD:
markCanThrowException();
break;
default:
super.visitBinary(tree);
break;
}
}
@Override
public void visitAssignop(VisageAssignOp tree) {
switch (tree.getVisageTag()) {
case DIV_ASG:
markCanThrowException();
break;
default:
super.visitAssignop(tree);
break;
}
}
@Override
public void visitInstanciate(VisageInstanciate tree) {
markCanThrowException();
}
@Override
public void visitFunctionInvocation(VisageFunctionInvocation tree) {
markCanThrowException();
}
}
ExceptionThrowingScanner scanner = new ExceptionThrowingScanner();
scanner.scan(var.getInitializer());
return scanner.safe;
} else {
return false;
}
}
// Return true if the var has a VarInit
@Override
public boolean hasVarInit() { return var.getVarInit() != null; }
@Override
public boolean isInitWithBoundFuncResult() {
return boundFuncResultInitSym != null;
}
@Override
public Symbol boundFuncResultInitSym() {
return boundFuncResultInitSym;
}
}
//
// This class represents a var override declared in the current visage class.
//
static class TranslatedOverrideClassVarInfo extends TranslatedVarInfoBase {
// Reference to the var information the override overshadows.
private VarInfo proxyVar;
private final Symbol boundFuncResultInitSym;
TranslatedOverrideClassVarInfo(VisageOverrideClassVar override,
JCExpression initExpr, Symbol boundFuncResultInitSym,
ExpressionResult bindOrNull,
VisageOnReplace onReplace, JCStatement onReplaceAsInline,
VisageOnReplace onInvalidate, JCStatement onInvalidateAsInline) {
super(override.pos(), override.sym.name, override.sym, override.getBindStatus(), override.getInitializer() != null,
initExpr, bindOrNull,
onReplace, onReplaceAsInline, onInvalidate, onInvalidateAsInline);
this.boundFuncResultInitSym = boundFuncResultInitSym;
}
// Return true if the var is an override.
@Override
public boolean isOverride() { return true; }
// Returns the var information the override overshadows.
@Override
public VarInfo proxyVar() { return proxyVar; }
// Setter for the proxy var information.
public void setProxyVar(VarInfo proxyVar) { this.proxyVar = proxyVar; }
// Returns true is this var overrides a mixin.
public boolean overridesMixin() {
return proxyVar != null && proxyVar instanceof MixinClassVarInfo;
}
@Override
public boolean isInitWithBoundFuncResult() {
return boundFuncResultInitSym != null;
}
@Override
public Symbol boundFuncResultInitSym() {
return boundFuncResultInitSym;
}
}
//
// This class represents a var that is declared in a superclass. This var may be
// declared in the same compile unit or read in from a .class file.
//
static class SuperClassVarInfo extends VarInfo {
SuperClassVarInfo(DiagnosticPosition diagPos, VisageVarSymbol var) {
super(diagPos, var.name, var, null);
}
// Superclass vars are never cloned.
@Override
public boolean needsCloning() { return false; }
}
//
// This class represents a var that is declared in a mixin superclass. This var may be
// declared in the same compile unit or read in from a .class file.
//
static class MixinClassVarInfo extends VarInfo {
// Accessors for mixin var.
ListBuffer<FuncInfo> accessors;
// Override from mixee.
private VarInfo overrideVar;
MixinClassVarInfo(DiagnosticPosition diagPos, VisageVarSymbol var) {
super(diagPos, var.name, var, null);
this.accessors = ListBuffer.lb();
this.overrideVar = null;
}
// Returns true if the var needs to generate mixin interfaces (getMixin$, setMixin$ and getVOFF$)
@Override
public boolean needsMixinInterface() {
return true;
}
// Return true if the var/override has an initializing expression
@Override
public boolean hasInitializer() {
return hasOverrideVar() && overrideVar().hasInitializer();
}
// true if this variable is initialized with a "safe" expression -
// so that we don't need a try..catch error handler wrapper in it's
// getter method.
@Override
public boolean hasSafeInitializer() {
return hasOverrideVar() && overrideVar().hasSafeInitializer();
}
// Return true if the var has a VarInit
@Override
public boolean hasVarInit() {
return hasOverrideVar() && overrideVar().hasVarInit();
}
// is this initialzed with a bound function result var?
@Override
public boolean isInitWithBoundFuncResult() {
return hasOverrideVar() && overrideVar().isInitWithBoundFuncResult();
}
// Is this variable is initialized by another synthetic variable
// of Pointer type and that var stores result from a bound function call?
// If so, return the symbol of the synthetic variable.
@Override
public Symbol boundFuncResultInitSym() {
return hasOverrideVar() ? overrideVar().boundFuncResultInitSym() : super.boundFuncResultInitSym();
}
// Returns null or the code for var initialization.
@Override
public JCExpression getDefaultInitExpression() {
if (hasOverrideVar() && overrideVar().initExpr != null) {
return overrideVar().initExpr;
}
return initExpr;
}
// Return true if the var has a bound definition.
@Override
public boolean hasBoundDefinition() {
return hasOverrideVar() && overrideVar().hasBoundDefinition();
}
// Return true if the var has a bidirectional bind.
@Override
public boolean hasBiDiBoundDefinition() {
return hasOverrideVar() && overrideVar().hasBiDiBoundDefinition();
}
// Is there a getter expression of bound variable
@Override
public boolean hasBoundInit() {
return hasOverrideVar() ? overrideVar().hasBoundInit() : super.hasBoundInit();
}
// Null or Java code for getter expression of bound variable
@Override
public JCExpression boundInit() {
return hasOverrideVar() ? overrideVar().boundInit() : super.boundInit();
}
// Empty or Java preface code for getter of bound variable
@Override
public List<JCStatement> boundPreface() {
return hasOverrideVar() ? overrideVar().boundPreface() : super.boundPreface();
}
// Empty or Java preface code for setting of bound with inverse variable
@Override
public List<JCStatement> boundInvSetterPreface() {
return hasOverrideVar() ? overrideVar().boundInvSetterPreface() : super.boundInvSetterPreface();
}
// Variable symbols on which this variable depends
@Override
public List<VisageVarSymbol> boundBindees() {
ListBuffer<VisageVarSymbol> bindees = ListBuffer.lb();
bindees.appendList(super.boundBindees());
if (hasOverrideVar()) {
bindees.appendList(overrideVar().boundBindees());
}
return bindees.toList();
}
// Bound variable symbols on which this variable is used.
@Override
public HashSet<VarInfo> boundBinders() {
return hasOverrideVar() ? overrideVar().boundBinders() : super.boundBinders();
}
// Bound sequences that need to be invalidated when invalidated.
@Override
public List<BindeeInvalidator> boundInvalidatees() {
return hasOverrideVar() ? overrideVar().boundInvalidatees() : super.boundInvalidatees();
}
// Empty or bound select pairs.
@Override
public List<DependentPair> boundBoundSelects() {
return hasOverrideVar() ? overrideVar().boundBoundSelects() : super.boundBoundSelects();
}
// Return true if this is a bound sequence represented as virtual sequence
@Override
public boolean isBoundVirtualSequence() {
return hasOverrideVar() ? overrideVar().isBoundVirtualSequence() : super.isBoundVirtualSequence();
}
// Null or Java code for getting the element of a bound sequence
@Override
public JCStatement boundElementGetter() {
return hasOverrideVar() ? overrideVar().boundElementGetter() : super.boundElementGetter();
}
// Null or Java code for getting the size of a bound sequence
@Override
public JCStatement boundSizeGetter() {
return hasOverrideVar() ? overrideVar().boundSizeGetter() : super.boundSizeGetter();
}
// Null or Java code for invalidation of a bound sequence
@Override
public List<BindeeInvalidator> boundInvalidators() {
return hasOverrideVar() ? overrideVar().boundInvalidators() : super.boundInvalidators();
}
// Possible visage code for the var's 'on replace'.
@Override
public VisageOnReplace onReplace() {
return hasOverrideVar() ? overrideVar().onReplace() : super.onReplace();
}
// Possible java code for the var's 'on replace' in setter.
@Override
public JCStatement onReplaceAsInline() {
return hasOverrideVar() ? overrideVar().onReplaceAsInline() : super.onReplaceAsInline();
}
// Possible visage code for the var's 'on invalidate'.
@Override
public VisageOnReplace onInvalidate() {
return hasOverrideVar() ? overrideVar().onInvalidate() : super.onInvalidate();
}
// Possible java code for the var's 'on invalidate' in var$invalidate method.
@Override
public JCStatement onInvalidateAsInline() {
return hasOverrideVar() ? overrideVar().onInvalidateAsInline() : super.onInvalidateAsInline();
}
// Mixin vars are always cloned.
@Override
public boolean needsCloning() { return true; }
// Fetch the override.
@Override
public VarInfo overrideVar() { return overrideVar; }
// Fetch the override.
public void setOverride(VarInfo override) { overrideVar = override; }
// Add an accessor function.
public void addAccessor(FuncInfo accessor) {
accessors.append(accessor);
}
// Add an accessor function.
public List<FuncInfo> getAccessors() {
return accessors.toList();
}
}
//
// Set up the analysis.
//
VisageAnalyzeClass(
VisageInitializationBuilder initBuilder,
DiagnosticPosition diagPos,
ClassSymbol currentClassSym,
List<TranslatedVarInfo> translatedAttrInfo,
List<TranslatedOverrideClassVarInfo> translatedOverrideAttrInfo,
List<TranslatedFuncInfo> translatedFuncInfo,
Name.Table names,
VisageTypes types,
VisageDefs defs,
VisageSymtab syms,
VisageClassReader reader) {
this.initBuilder = initBuilder;
this.names = names;
this.types = types;
this.defs = defs;
this.syms = syms;
this.reader = reader;
this.diagPos = diagPos;
this.currentClassDecl = types.getVisageClass(currentClassSym);
this.currentClassSym = currentClassSym;
this.translatedAttrInfo = translatedAttrInfo;
this.translatedOverrideAttrInfo = translatedOverrideAttrInfo;
this.translatedFuncInfo = translatedFuncInfo;
this.classVarCount = 0;
this.scriptVarCount = 0;
// Start by analyzing the current class.
analyzeCurrentClass();
// Assign var enumeration and binders.
for (VarInfo ai : classVarInfos) {
if (ai.needsEnumeration()) {
ai.setEnumeration(classVarCount++);
}
addBinders(ai);
}
for (VarInfo ai : scriptVarInfos) {
if (ai.needsEnumeration()) {
ai.setEnumeration(scriptVarCount++);
}
addBinders(ai);
}
if (initBuilder.options.get("dumpvarinfo") != null) {
printAnalysis(false);
}
}
private void addInterClassBinder(VarInfo varInfo, VisageVarSymbol instanceSymbol, VisageVarSymbol referenceSymbol) {
// Get the correct update map.
HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> updateMap =
varInfo.isStatic() ? scriptUpdateMap : classUpdateMap;
// Get instance level map.
HashMap<VisageVarSymbol, HashSet<VarInfo>> instanceMap = updateMap.get(instanceSymbol);
// Add new entry if not found.
if (instanceMap == null) {
instanceMap = new LinkedHashMap<VisageVarSymbol, HashSet<VarInfo>>();
updateMap.put(instanceSymbol, instanceMap);
}
// Get reference level map.
HashSet<VarInfo> referenceSet = instanceMap.get(referenceSymbol);
// Add new entry if not found.
if (referenceSet == null) {
referenceSet = new LinkedHashSet<VarInfo>();
instanceMap.put(referenceSymbol, referenceSet);
}
// Add symbol to set.
referenceSet.add(varInfo);
}
private void addBinders(VarInfo ai) {
// Add any bindees to binders.
for (VisageVarSymbol bindeeSym : ai.boundBindees()) {
// Find the varInfo
VarInfo bindee = visitedAttributes.get(initBuilder.attributeValueName(bindeeSym));
if (bindee != null) {
bindee.binders.add((VarInfo)ai);
}
}
// Add any bind select pairs to update map.
for (DependentPair pair : ai.boundBoundSelects()) {
addInterClassBinder(ai, pair.instanceSym, (VisageVarSymbol)pair.referencedSym);
}
// If the ai has invalidators.
for (BindeeInvalidator invalidator: ai.boundInvalidators()) {
// Find the varInfo
VarInfo bindee = visitedAttributes.get(initBuilder.attributeValueName(invalidator.bindee));
if (bindee != null && invalidator.invalidator != null) {
bindee.boundInvalidatees.append(invalidator);
}
}
}
//
// This class supers all classes used to hold function information. Consumed by
// VisageInitializationBuilder.
//
static abstract class FuncInfo {
// Position of the function declaration or current visage class if read from superclass.
private final DiagnosticPosition diagPos;
// Function symbol (unique to symbol.)
private final MethodSymbol funcSym;
FuncInfo(DiagnosticPosition diagPos, MethodSymbol funcSym) {
this.diagPos = diagPos;
this.funcSym = funcSym;
}
// Return the function position.
public DiagnosticPosition pos() { return diagPos; }
// Return the function symbol.
public MethodSymbol getSymbol() { return funcSym; }
// Return modifier flags from the symbol.
public long getFlags() { return funcSym.flags(); }
// Predicate for static func test.
public boolean isStatic() { return (getFlags() & Flags.STATIC) != 0; }
// Useful diagnostic tool.
public void printInfo() {
System.err.println(" " + getSymbol() +
(isStatic() ? ", static" : ""));
}
}
//
// This class is used for basic functions declared in the current visage class.
//
static class TranslatedFuncInfo extends FuncInfo {
// Visage definition of the function.
private final VisageFunctionDefinition visageFuncDef;
// Java translation of the function.
private final List<JCTree> jcFuncDef;
TranslatedFuncInfo(VisageFunctionDefinition visageFuncDef, List<JCTree> jcFuncDef) {
super(visageFuncDef, visageFuncDef.sym);
this.visageFuncDef = visageFuncDef;
this.jcFuncDef = jcFuncDef;
}
// Return the visage definition of the function.
public VisageFunctionDefinition visageFunction() { return visageFuncDef; }
// Return the java translation of the function.
public List<JCTree> jcFunction() { return jcFuncDef; }
}
//
// This class represents a function that is declared in a superclass.
// This function may be declared in the same compile unit or read in from a .class file.
//
static class SuperClassFuncInfo extends FuncInfo {
SuperClassFuncInfo(MethodSymbol funcSym) {
super(null, funcSym);
}
}
//
// This class represents a function that is declared in a mixin superclass. This function may be
// This function may be declared in the same compile unit or read in from a .class file.
//
static class MixinFuncInfo extends FuncInfo {
MixinFuncInfo(MethodSymbol funcSym) {
super(null, funcSym);
}
}
//
// Returns the current class position.
//
public DiagnosticPosition getCurrentClassPos() { return diagPos; }
//
// Returns the current class decl.
//
public VisageClassDeclaration getCurrentClassDecl() { return currentClassDecl; }
//
// Returns the current class symbol.
//
public ClassSymbol getCurrentClassSymbol() { return currentClassSym; }
//
// Returns true if specified symbol is the current class symbol.
//
public boolean isCurrentClassSymbol(Symbol sym) { return sym == currentClassSym; }
//
// Returns true if specified class is the VisageBase class.
//
public boolean isVisageBase(Symbol sym) { return sym == syms.visage_BaseType.tsym; }
//
// Returns true if specified class is the VisageObject class.
//
public boolean isVisageObject(Symbol sym) { return sym == syms.visage_ObjectType.tsym; }
//
// Returns true if specified class is either the VisageBase or the VisageObject class.
//
public boolean isRootClass(Symbol sym) { return isVisageBase(sym) || isVisageObject(sym); }
//
// Returns true if the current class inherits directly from VisageBase.
//
public boolean isFirstTier() { return superClassSym != null && isVisageBase(superClassSym); }
//
// Returns true if the current class inherits directly from VisageBase and has no mixins.
//
public boolean isFirstTierNoMixins() { return isFirstTier() && allMixins.isEmpty(); }
//
// Returns the var count for the current class.
//
public int getClassVarCount() { return classVarCount; }
//
// Returns the var count for the current script.
//
public int getScriptVarCount() { return scriptVarCount; }
//
// Returns the translatedAttrInfo for the current class.
//
public List<TranslatedVarInfo> getTranslatedAttrInfo() { return translatedAttrInfo; }
//
// Returns the translatedOverrideAttrInfo for the current class.
//
public List<TranslatedOverrideClassVarInfo> getTranslatedOverrideAttrInfo() {
return translatedOverrideAttrInfo;
}
//
// Returns the translatedFuncInfo for the current class.
//
public List<TranslatedFuncInfo> getTranslatedFuncVarInfo() {
return translatedFuncInfo;
}
//
// Returns the resulting list of class vars.
//
public List<VarInfo> classVarInfos() {
return classVarInfos.toList();
}
//
// Returns the resulting list of script vars.
//
public List<VarInfo> scriptVarInfos() {
return scriptVarInfos.toList();
}
//
// Returns the resulting list of class funcs.
//
public List<FuncInfo> classFuncInfos() {
return classFuncInfos.toList();
}
//
// Returns the resulting list of script func.
//
public List<FuncInfo> scriptFuncInfos() {
return scriptFuncInfos.toList();
}
//
// Returns the map used to construct the class update$ method.
//
public final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> getClassUpdateMap() {
return classUpdateMap;
}
//
// Returns the map used to construct the script update$ method.
//
public final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> getScriptUpdateMap() {
return scriptUpdateMap;
}
//
// Returns the resulting list of methods needing $impl dispatching.
//
public List<MethodSymbol> needDispatch() {
ListBuffer<MethodSymbol> meths = ListBuffer.lb();
// Never add dispatch methods to mixins.
if (!isMixinClass()) {
for (FuncInfo fi : needDispatchMethods.values()) {
meths.append(fi.getSymbol());
}
}
return meths.toList();
}
//
// Returns true if the type is a valid class worthy of analysis.
//
private boolean isValidClass(Type type) {
return type != null && type.tsym != null && type.tsym.kind == Kinds.TYP;
}
//
// Returns true if the class (or current class) is a mixin.
//
public boolean isMixinClass() {
return isMixinClass(currentClassSym);
}
public static boolean isMixinClass(Symbol cSym) {
return (cSym.flags() & VisageFlags.MIXIN) != 0;
}
//
// Returns true if the class (or current class) is FINAL.
//
public boolean isFinal() {
return isFinal(currentClassSym);
}
public static boolean isFinal(Symbol cSym) {
return (cSym.flags() & Flags.FINAL) != 0;
}
//
// Returns true if the class is a Interface.
//
public boolean isInterface(Symbol cSym) {
return (cSym.flags() & Flags.INTERFACE) != 0;
}
//
// Returns null or the superclass symbol if it is a visage class.
//
public ClassSymbol getVisageSuperClassSym() { return superClassSym; }
//
// Returns resulting list of all superclasses in top down order.
//
public List<ClassSymbol> getSuperClasses() { return superClasses.toList(); }
//
// Returns resulting list of immediate mixin classes in left to right order.
//
public List<ClassSymbol> getImmediateMixins() { return immediateMixins.toList(); }
//
// Returns resulting list of all mixin classes in top down order.
//
public List<ClassSymbol> getAllMixins() { return allMixins.toList(); }
//
// Add a var to the proper vars list.
//
public void addVarToList(VarInfo varInfo) {
if (varInfo.isStatic()) {
scriptVarInfos.append(varInfo);
} else {
classVarInfos.append(varInfo);
}
}
//
// Check to see if a mixin has an override.
//
public void checkMixinOverride(MixinClassVarInfo varInfo) {
for (TranslatedOverrideClassVarInfo tai : translatedOverrideAttrInfo) {
if (tai.getSymbol() == varInfo.getSymbol()) {
varInfo.setOverride(tai);
tai.setProxyVar(varInfo);
break;
}
}
}
//
// This method analyzes the current visage class.
//
private void analyzeCurrentClass() {
// Make sure we don't recursively redo this class.
addedBaseClasses.add(currentClassSym);
// Process up the super class chain first.
Type superType = currentClassSym.getSuperclass();
// Make sure the super is a valid java class (is this always true?)
if (isValidClass(superType)) {
// Analyze the super class, but note that we don't want to clone
// anything in the super chain.
analyzeClass(superType.tsym, true, false);
}
// Track the current vars to the instance attribute results.
for (TranslatedVarInfo tai : translatedAttrInfo) {
// Track the var for overrides and mixin duplication.
visitedAttributes.put(initBuilder.attributeValueName(tai.getSymbol()), tai);
}
// Map the current methods so they are filtered out of the results.
for (TranslatedFuncInfo func : translatedFuncInfo) {
visitedMethods.put(methodSignature(func.getSymbol()), func);
}
// Lastly, insert any mixin vars and methods from the interfaces.
for (VisageExpression supertype : currentClassDecl.getSupertypes()) {
// This will technically only analyze mixin classes.
// We also want to clone all mixin vars amnd methods.
analyzeClass(supertype.type.tsym, true, true);
}
// Track the override vars to the instance attribute results.
for (TranslatedOverrideClassVarInfo tai : translatedOverrideAttrInfo) {
// Find the overridden var.
VarInfo oldVarInfo = visitedAttributes.get(initBuilder.attributeValueName(tai.getSymbol()));
// Test because it's possible to find the override before the var.
if (oldVarInfo != null && !(oldVarInfo instanceof MixinClassVarInfo)) {
// Proxy to the overridden var.
tai.setProxyVar(oldVarInfo);
tai.setNeedsMixinInterface(tai.needsMixinInterface() || oldVarInfo.needsMixinInterface());
oldVarInfo.setNeedsMixinInterface(false);
}
// Track the var for overrides and mixin duplication.
visitedAttributes.put(initBuilder.attributeValueName(tai.getSymbol()), tai);
}
// Add the current vars to the var results.
// VSGC-3043 - This needs to be done after mixins.
for (TranslatedVarInfo tai : translatedAttrInfo) {
addVarToList(tai);
}
// Add the override vars to the var results.
// VSGC-3043 - This needs to be done after mixins.
for (TranslatedOverrideClassVarInfo tai : translatedOverrideAttrInfo) {
if (!tai.overridesMixin()) {
addVarToList(tai);
}
}
// Add the functions to the function results.
// VSGC-3043 - This needs to be done after mixins.
for (TranslatedFuncInfo tfi : translatedFuncInfo) {
if (tfi.isStatic()) {
scriptFuncInfos.append(tfi);
} else {
classFuncInfos.append(tfi);
}
}
}
private void analyzeClass(Symbol sym, boolean isImmediateSuper, boolean needsCloning) {
// Ignore pure java interfaces, classes we've visited before and non-visage classes.
if (!isInterface(sym) && !addedBaseClasses.contains(sym) && types.isVisageClass(sym)) {
// Get the current class symbol and add it to the visited map.
ClassSymbol cSym = (ClassSymbol)sym;
addedBaseClasses.add(cSym);
// Only continue cloning up the hierarchy if this is a mixin class.
boolean isMixinClass = isMixinClass(cSym);
needsCloning = needsCloning && isMixinClass;
// Process up the super class chain first.
Type superType = cSym.getSuperclass();
if (isValidClass(superType)) {
// Analyze the super class, but note that we don't want to clone
// anything in the super chain.
analyzeClass(superType.tsym, false, false);
}
// Class loaded from .class file.
if (cSym.members() != null) {
// Scope information is held in reverse order of declaration.
ListBuffer<Symbol> reversed = ListBuffer.lb();
for (Entry e = cSym.members().elems; e != null && e.sym != null; e = e.sibling) {
reversed.prepend(e.sym);
}
// Track mixin var accessors.
HashMap<Name, MixinClassVarInfo> mixinVarMap = new LinkedHashMap<Name, MixinClassVarInfo>();
// Scan attribute members.
for (Symbol varMem : reversed) {
if (varMem instanceof VisageVarSymbol) {
// Attribute member.
VisageVarSymbol var = (VisageVarSymbol)varMem;
// Filter out methods generated by the compiler.
if (isRootClass(cSym) || !filterVars(var)) {
processAttribute(var, cSym, needsCloning, mixinVarMap);
}
}
}
// Scan attribute/method members.
for (Symbol methMem : reversed) {
if (methMem.kind == Kinds.MTH) {
// Method member.
MethodSymbol meth = (MethodSymbol)methMem;
// Workaround for VSGC-3040 - Compile failure building
// runtime/visage-ui-controls/visage/scene/control/Button.visage
//if (!needsCloning) continue;
// Filter out methods generated by the compiler.
if (isRootClass(cSym) || !filterMethods(meth)) {
processMethod(meth, needsCloning, cSym, mixinVarMap);
}
}
}
}
// Now analyze interfaces.
for (Type supertype : cSym.getInterfaces()) {
ClassSymbol iSym = (ClassSymbol) supertype.tsym;
analyzeClass(iSym, isImmediateSuper && isMixinClass, needsCloning);
}
// Record the superclass in top down order.
recordClass(cSym, isImmediateSuper);
}
}
//
// Predicate method indicates if the method should be include in processing.
// Should filter out unrelated methods generated by the compiler.
//
private boolean filterMethods(MethodSymbol meth) {
Name name = meth.name;
// if this is a main method in an Visage class then it is synthetic, ignore it
if (name == defs.main_MethodName) {
if (meth.type instanceof MethodType) {
MethodType mt = (MethodType)meth.type;
List<Type> paramTypes = mt.getParameterTypes();
if (paramTypes.size() == 1 && paramTypes.head instanceof ArrayType) {
Type elemType = ((ArrayType) paramTypes.head).getComponentType();
if (elemType.tsym.name == syms.stringType.tsym.name) {
return true;
}
}
}
}
// ignore GETMAPxxx methods
if (name.toString().startsWith(defs.varGetMapString)) {
return true;
}
return name == names.init || name == names.clinit ||
name == defs.internalRunFunctionName ||
name == defs.applyDefaults_VisageObjectMethodName ||
name == defs.count_VisageObjectMethodName ||
name == defs.get_VisageObjectMethodName ||
name == defs.set_VisageObjectMethodName ||
name == defs.invalidate_VisageObjectMethodName ||
name == defs.notifyDependents_VisageObjectMethodName ||
name == defs.getElement_VisageObjectMethodName ||
name == defs.size_VisageObjectMethodName ||
name == defs.update_VisageObjectMethodName ||
name == defs.complete_VisageObjectMethodName ||
name == defs.initialize_VisageObjectMethodName ||
name == defs.userInit_VisageObjectMethodName ||
name == defs.postInit_VisageObjectMethodName ||
name == defs.initVars_VisageObjectMethodName ||
name == defs.invoke_VisageObjectMethodName;
}
//
// Predicate method indicates if the var should be include in processing.
// Should filter out unrelated methods generated by the compiler.
//
private boolean filterVars(VisageVarSymbol var) {
Name name = var.name;
String nameString = name.toString();
return nameString.startsWith(VisageDefs.varMap_VisageObjectFieldPrefix) ||
nameString.startsWith(VisageDefs.count_VisageObjectFieldString) ||
nameString.startsWith(VisageDefs.offset_AttributeFieldPrefix) ||
nameString.startsWith(VisageDefs.flags_AttributeFieldPrefix) ||
nameString.startsWith(VisageDefs.varFlags_VisageObjectFieldPrefix);
}
//
// Record the superclasses and mixins in top down order.
//
private void recordClass(ClassSymbol cSym, boolean isImmediateSuper) {
// Make a distinction between superclasses and mixins.
if (isMixinClass(cSym)) {
// Record immediate mixin classes in left to right order.
if (isImmediateSuper) {
immediateMixins.append(cSym);
}
// Record all mixin classes in top down order.
allMixins.append(cSym);
} else {
// Record the immediate superclass.
if (isImmediateSuper) {
superClassSym = cSym;
}
// Record all superclasses in top down order.
superClasses.append(cSym);
}
}
//
// This method strips the var name out of an accessor method.
//
private Name getAccessorVarName(Name name) {
for (Name prefix : defs.accessorPrefixes) {
if (name.startsWith(prefix)) {
return name.subName(prefix.length() - 1, name.length());
}
}
return null;
}
//
// This method determines if a method should be added to the list of methods
// needing dispatch. This method is only called for inherited methods.
//
private void processMethod(MethodSymbol sym, boolean needsCloning, ClassSymbol cSym, HashMap<Name, MixinClassVarInfo> mixinVarMap) {
long flags = sym.flags();
// Only look at real instance methods.
if ((flags & (Flags.ABSTRACT | Flags.SYNTHETIC)) == 0) {
// Generate a name/signature string for uniqueness.
String nameSig = methodSignature(sym);
// Look to see if we've seen a method like this before.
FuncInfo oldMethod = visitedMethods.get(nameSig);
// See if the current method is a mixin.
boolean newIsMixin = isMixinClass(sym.owner);
// See if the previous methods is a mixin.
boolean oldIsMixin = oldMethod != null && isMixinClass(oldMethod.getSymbol().owner);
// Create new info.
FuncInfo newMethod = newIsMixin ? new MixinFuncInfo(sym) : new SuperClassFuncInfo(sym);
// Are we are still cloning this far up the hierarchy?
if (needsCloning && (sym.flags() & Flags.PRIVATE) == 0) {
// If the method didn't occur before or is a real method overshadowing a prior mixin.
if ((oldMethod == null || (oldIsMixin && !newIsMixin)) && sym.owner == cSym) {
Name varName = getAccessorVarName(sym.name);
if (varName != null) {
// Associate the accessor with the var.
MixinClassVarInfo varInfo = mixinVarMap.get(varName);
if (varInfo != null) {
varInfo.addAccessor(newMethod);
}
} else {
// Add to the methods needing $impl dispatch.
needDispatchMethods.put(nameSig, newMethod);
}
}
}
// Map the fact we've seen this name/signature.
visitedMethods.put(nameSig, newMethod);
}
}
//
// This method determines if the var needs to be handled in the current class.
// This method is only called for inherited attributes.
//
private void processAttribute(VisageVarSymbol var, ClassSymbol cSym, boolean needsCloning, HashMap<Name, MixinClassVarInfo> mixinVarMap) {
boolean isStatic = (var.flags() & Flags.STATIC) != 0;
// If the var is in a class and not a static (ie., an instance attribute.)
if (var.isMember() && !isStatic) {
// See if we've seen this var before.
VarInfo oldVarInfo = visitedAttributes.get(initBuilder.attributeValueName(var));
// If we've seen this class before, it must be the same symbol and type,
// otherwise in doesn't conflict.
if (oldVarInfo != null &&
(!oldVarInfo.getSymbol().name.equals(var.name) ||
!types.isSameType(oldVarInfo.getSymbol().type, var.type))) {
oldVarInfo = null;
}
// Is the var in a mixin class and needs cloning.
boolean newIsMixin = isMixinClass(var.owner);
if (newIsMixin && needsCloning) {
// Only process the mixin var if we've not seen it before.
if ((oldVarInfo == null || oldVarInfo instanceof TranslatedOverrideClassVarInfo) && (var.flags() & Flags.PRIVATE) == 0) {
// Construct a new mixin VarInfo.
MixinClassVarInfo newVarInfo = new MixinClassVarInfo(diagPos, var);
// Check for overriding var.
checkMixinOverride(newVarInfo);
// Add var to map.
Name varName = initBuilder.attributeValueName(var);
mixinVarMap.put(varName, newVarInfo);
// Don't add mixin vars to mixin classes.
if (!isMixinClass()) {
// Add the new mixin VarInfo to the result list.
addVarToList(newVarInfo);
}
// Map the fact we've seen this var.
visitedAttributes.put(initBuilder.attributeValueName(var), newVarInfo);
} else if (oldVarInfo != null) {
// Still needs the interface.
oldVarInfo.setNeedsMixinInterface(true);
}
} else {
// Construct a new superclass VarInfo.
SuperClassVarInfo newVarInfo = new SuperClassVarInfo(diagPos, var);
// Add the new superclass VarInfo to the result list.
classVarInfos.append(newVarInfo);
// Map the fact we've seen this var.
visitedAttributes.put(initBuilder.attributeValueName(var), newVarInfo);
}
}
}
//
// This method constructs a name/signature string for the supplied method
// symbol.
//
private String methodSignature(MethodSymbol meth) {
StringBuilder nameSigBld = new StringBuilder();
nameSigBld.append(meth.name.toString());
nameSigBld.append(":");
nameSigBld.append(meth.getReturnType().tsym.flatName());
nameSigBld.append(":");
for (VarSymbol param : meth.getParameters()) {
nameSigBld.append(param.type.tsym.flatName());
nameSigBld.append(":");
}
return nameSigBld.toString();
}
//
// This method can be used to dump the state of a VarInfo or subclass.
// The before flag cam be used to dump the VarInfo supplied by the
// VisageInitializationBuilder.
//
private void printAnalysis(boolean before) {
System.err.println("Analyzed : " + currentClassSym);
if (before) {
System.err.println(" translatedAttrInfo");
for (TranslatedVarInfo ai : translatedAttrInfo) ai.printInfo();
System.err.println(" translatedOverrideAttrInfo");
for (TranslatedOverrideClassVarInfo ai : translatedOverrideAttrInfo) ai.printInfo();
}
System.err.println(" superClass");
System.err.println(" " + superClassSym);
System.err.println(" superClasses");
for (ClassSymbol cs : superClasses) System.err.println(" " + cs);
System.err.println(" immediate mixins");
for (ClassSymbol cs : immediateMixins) System.err.println(" " + cs);
System.err.println(" all mixins");
for (ClassSymbol cs : allMixins) System.err.println(" " + cs);
System.err.println(" classVarInfos");
for (VarInfo ai : classVarInfos) ai.printInfo();
System.err.println(" scriptVarInfos");
for (VarInfo ai : scriptVarInfos) ai.printInfo();
System.err.println(" classFuncInfos");
for (FuncInfo fi : classFuncInfos) fi.printInfo();
System.err.println(" scriptFuncInfos");
for (FuncInfo fi : scriptFuncInfos) fi.printInfo();
System.err.println(" needDispatchMethods");
for (MethodSymbol m : needDispatch()) {
System.err.println(" " + m + ", owner=" + m.owner);
}
System.err.println();
System.err.println();
}
}