/*
* Copyright 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 com.sun.tools.mjavac.code.*;
import com.sun.tools.mjavac.code.Scope.Entry;
import com.sun.tools.mjavac.code.Symbol.ClassSymbol;
import com.sun.tools.mjavac.code.Symbol.MethodSymbol;
import com.sun.tools.mjavac.code.Symbol.VarSymbol;
import com.sun.tools.mjavac.code.Type.*;
import com.sun.tools.mjavac.tree.JCTree;
import com.sun.tools.mjavac.tree.JCTree.*;
import com.sun.tools.mjavac.util.*;
import com.sun.tools.mjavac.util.JCDiagnostic.DiagnosticPosition;
import org.visage.tools.code.VisageClassSymbol;
import org.visage.tools.code.VisageFlags;
import org.visage.tools.code.VisageSymtab;
import org.visage.tools.code.VisageTypeRepresentation;
import org.visage.tools.code.VisageVarSymbol;
import org.visage.tools.comp.VisageAnalyzeClass.*;
import org.visage.tools.comp.VisageAbstractTranslation.*;
import org.visage.tools.comp.VisageAbstractTranslation.ExpressionResult.*;
import static org.visage.tools.comp.VisageDefs.*;
import org.visage.tools.tree.*;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
/**
* Build the representation(s) of a Visage class. Includes class initialization, attribute and function proxies.
* With support for mixins.
*
* @author Robert Field
* @author Lubo Litchev
* @author Per Bothner
* @author Zhiqun Chen
* @author Jim Laskey
*/
public class VisageInitializationBuilder extends VisageTranslationSupport {
protected static final Context.Key<VisageInitializationBuilder> visageInitializationBuilderKey =
new Context.Key<VisageInitializationBuilder>();
private final VisageToJava toJava;
private final VisageClassReader reader;
private final VisageOptimizationStatistics optStat;
private final DependencyGraphWriter depGraphWriter;
private final boolean annoBindees;
public static class LiteralInitVarMap {
private int count = 1;
public Map<VisageVarSymbol, Integer> varMap = new LinkedHashMap<VisageVarSymbol, Integer>();
public ListBuffer<VisageVarSymbol> varList = ListBuffer.lb();
public int addVar(VisageVarSymbol sym) {
Integer value = varMap.get(sym);
if (value == null) {
value = new Integer(count++);
varMap.put(sym, value);
varList.append(sym);
}
return value.intValue();
}
public int size() {
return varMap.size();
}
}
public static class LiteralInitClassMap {
public Map<ClassSymbol, LiteralInitVarMap> classMap = new LinkedHashMap<ClassSymbol, LiteralInitVarMap>();
public LiteralInitVarMap getVarMap(ClassSymbol sym) {
LiteralInitVarMap map = classMap.get(sym);
if (map == null) {
map = new LiteralInitVarMap();
classMap.put(sym, map);
}
return map;
}
public int size() {
return classMap.size();
}
}
public static VisageInitializationBuilder instance(Context context) {
VisageInitializationBuilder instance = context.get(visageInitializationBuilderKey);
if (instance == null)
instance = new VisageInitializationBuilder(context);
return instance;
}
protected VisageInitializationBuilder(Context context) {
super(context);
context.put(visageInitializationBuilderKey, this);
toJava = VisageToJava.instance(context);
reader = (VisageClassReader) VisageClassReader.instance(context);
optStat = VisageOptimizationStatistics.instance(context);
depGraphWriter = DependencyGraphWriter.instance(context);
annoBindees = options.get("annobindees") != null;
}
/**
* Hold the result of analyzing the class.
* */
static class VisageClassModel {
final Name interfaceName;
final List<JCExpression> interfaces;
final List<JCTree> iDefinitions;
final List<JCTree> additionalClassMembers;
final List<JCExpression> additionalImports;
final Type superType;
final ClassSymbol superClassSym;
final List<ClassSymbol> superClasses;
final List<ClassSymbol> immediateMixins;
final List<ClassSymbol> allMixins;
VisageClassModel(
Name interfaceName,
List<JCExpression> interfaces,
List<JCTree> iDefinitions,
List<JCTree> addedClassMembers,
List<JCExpression> additionalImports,
Type superType,
ClassSymbol superClassSym,
List<ClassSymbol> superClasses,
List<ClassSymbol> immediateMixins,
List<ClassSymbol> allMixins) {
this.interfaceName = interfaceName;
this.interfaces = interfaces;
this.iDefinitions = iDefinitions;
this.additionalClassMembers = addedClassMembers;
this.additionalImports = additionalImports;
this.superType = superType;
this.superClassSym = superClassSym;
this.superClasses = superClasses;
this.immediateMixins = immediateMixins;
this.allMixins = allMixins;
}
}
/**
* Analyze the class.
*
* Determine what methods will be needed to access attributes.
* Determine what methods will be needed to proxy to the static implementations of functions.
* Determine what other misc fields and methods will be needed.
* Create the corresponding interface.
*
* Return all this as a VisageClassModel for use in translation.
* */
VisageClassModel createVisageClassModel(VisageClassDeclaration cDecl,
List<TranslatedVarInfo> translatedAttrInfo,
List<TranslatedOverrideClassVarInfo> translatedOverrideAttrInfo,
List<TranslatedFuncInfo> translatedFuncInfo,
LiteralInitClassMap initClassMap,
ListBuffer<JCStatement> translatedInitBlocks, ListBuffer<JCStatement> translatedPostInitBlocks) {
DiagnosticPosition diagPos = cDecl.pos();
Type superType = types.supertype(cDecl.type);
ClassSymbol outerTypeSym = outerTypeSymbol(cDecl.sym); // null unless inner class with outer reference
boolean isLibrary = toJava.getAttrEnv().toplevel.isLibrary;
boolean isRunnable = toJava.getAttrEnv().toplevel.isRunnable;
VisageAnalyzeClass analysis = new VisageAnalyzeClass(this, diagPos,
cDecl.sym, translatedAttrInfo, translatedOverrideAttrInfo, translatedFuncInfo,
names, types, defs, syms, reader);
List<VarInfo> classVarInfos = analysis.classVarInfos();
List<VarInfo> scriptVarInfos = analysis.scriptVarInfos();
List<FuncInfo> classFuncInfos = analysis.classFuncInfos();
List<FuncInfo> scriptFuncInfos = analysis.scriptFuncInfos();
boolean hasStatics = !scriptVarInfos.isEmpty() || !cDecl.invokeCases(true).isEmpty();
int classVarCount = analysis.getClassVarCount();
int scriptVarCount = analysis.getScriptVarCount();
List<MethodSymbol> needDispatch = analysis.needDispatch();
ClassSymbol visageSuperClassSym = analysis.getVisageSuperClassSym();
List<ClassSymbol> superClasses = analysis.getSuperClasses();
List<ClassSymbol> immediateMixinClasses = analysis.getImmediateMixins();
List<ClassSymbol> allMixinClasses = analysis.getAllMixins();
boolean isMixinClass = cDecl.isMixinClass();
boolean isScriptClass = cDecl.isScriptClass();
boolean isAnonClass = isAnonClass(analysis.getCurrentClassSymbol());
boolean needsGetMap = isAnonClass && cDecl.getObjInitSyms() != null;
boolean hasVisageSuper = visageSuperClassSym != null;
// Have to populate the var map for anon classes.
LiteralInitVarMap varMap = null;
if (needsGetMap) {
varMap = initClassMap.getVarMap(analysis.getCurrentClassSymbol());
populateAnonInitVarMap(cDecl, varMap);
}
ListBuffer<JCTree> cDefinitions = ListBuffer.lb(); // additional class members needed
ListBuffer<JCTree> iDefinitions = ListBuffer.lb();
JavaCodeMaker javaCodeMaker = new JavaCodeMaker(analysis, cDefinitions);
if (!isMixinClass) {
javaCodeMaker.makeAttributeNumbers(classVarInfos, classVarCount);
javaCodeMaker.makeAttributeFlags(classVarInfos);
javaCodeMaker.makeAttributeFields(classVarInfos);
javaCodeMaker.makeAttributeAccessorMethods(classVarInfos);
javaCodeMaker.makeVarNumMethods();
JCStatement initMap = needsGetMap ? javaCodeMaker.makeInitVarMapInit(varMap) : null;
if (outerTypeSym == null) {
javaCodeMaker.makeJavaEntryConstructor();
} else {
javaCodeMaker.makeOuterAccessorField(outerTypeSym);
javaCodeMaker.makeOuterAccessorMethod(outerTypeSym);
}
javaCodeMaker.makeFunctionProxyMethods(needDispatch);
javaCodeMaker.makeVisageEntryConstructor(classVarInfos, outerTypeSym);
javaCodeMaker.makeInitMethod(defs.userInit_VisageObjectMethodName, translatedInitBlocks, immediateMixinClasses);
javaCodeMaker.makeInitMethod(defs.postInit_VisageObjectMethodName, translatedPostInitBlocks, immediateMixinClasses);
javaCodeMaker.gatherFunctions(classFuncInfos);
if (isScriptClass) {
javaCodeMaker.makeInitClassMaps(initClassMap);
javaCodeMaker.gatherFunctions(scriptFuncInfos);
if (hasStatics) {
ListBuffer<JCTree> sDefinitions = ListBuffer.lb();
// script-level into class X
javaCodeMaker.makeAttributeFields(scriptVarInfos);
javaCodeMaker.makeAttributeAccessorMethods(scriptVarInfos);
// script-level into class X.X$Script
javaCodeMaker.setContext(true, sDefinitions);
javaCodeMaker.makeAttributeNumbers(scriptVarInfos, scriptVarCount);
javaCodeMaker.makeAttributeFlags(scriptVarInfos);
javaCodeMaker.makeVarNumMethods();
javaCodeMaker.makeVisageEntryConstructor(scriptVarInfos, null);
javaCodeMaker.makeScriptLevelAccess(cDecl.sym, true);
javaCodeMaker.setContext(false, cDefinitions);
// script-level into class X
javaCodeMaker.makeScriptLevelAccess(cDecl.sym, false);
javaCodeMaker.makeInitStaticAttributesBlock(cDecl.sym, true, isLibrary, scriptVarInfos, initMap);
javaCodeMaker.makeScript(sDefinitions.toList());
}
} else {
javaCodeMaker.makeInitStaticAttributesBlock(cDecl.sym, false, false, null, initMap);
}
if (!hasVisageSuper) {
// Has a non-Visage super, so we can't use VisageBase, therefore we need
// to clone the necessary vars and methods.
// This code must be after all methods have been added to cDefinitions,
// A set of methods to exclude from cloning.
HashSet<String> excludes = new HashSet<String>();
// Exclude any methods generated by the init builder.
for (JCTree member : cDefinitions) {
if (member.getTag() == JCTree.METHODDEF) {
JCMethodDecl jcmeth = (JCMethodDecl)member;
excludes.add(jcMethodDeclStr(jcmeth));
}
}
// Clone what is needed from VisageBase/VisageObject.
javaCodeMaker.cloneVisageBase(excludes);
}
} else {
// Mixin class
javaCodeMaker.makeAttributeFlags(classVarInfos);
javaCodeMaker.makeAttributeFields(classVarInfos);
javaCodeMaker.makeAttributeAccessorMethods(classVarInfos);
javaCodeMaker.makeVarNumMethods();
if (isScriptClass) {
javaCodeMaker.makeInitClassMaps(initClassMap);
javaCodeMaker.gatherFunctions(scriptFuncInfos);
if (hasStatics) {
ListBuffer<JCTree> sDefinitions = ListBuffer.lb();
// script-level into class X
javaCodeMaker.makeAttributeFields(scriptVarInfos);
javaCodeMaker.setContext(true, cDefinitions);
javaCodeMaker.makeAttributeAccessorMethods(scriptVarInfos);
javaCodeMaker.setContext(false, cDefinitions);
// script-level into class X.X$Script
javaCodeMaker.setContext(true, sDefinitions);
javaCodeMaker.makeAttributeNumbers(scriptVarInfos, scriptVarCount);
javaCodeMaker.makeAttributeFlags(scriptVarInfos);
javaCodeMaker.makeVarNumMethods();
javaCodeMaker.makeVisageEntryConstructor(scriptVarInfos, null);
javaCodeMaker.makeScriptLevelAccess(cDecl.sym, true);
javaCodeMaker.setContext(false, cDefinitions);
// script-level into class X
javaCodeMaker.makeScriptLevelAccess(cDecl.sym, false);
javaCodeMaker.makeInitStaticAttributesBlock(cDecl.sym, true, isLibrary, scriptVarInfos, null);
javaCodeMaker.makeScript(sDefinitions.toList());
}
} else {
javaCodeMaker.makeInitStaticAttributesBlock(cDecl.sym, false, false, null, null);
}
javaCodeMaker.makeInitMethod(defs.userInit_VisageObjectMethodName, translatedInitBlocks, immediateMixinClasses);
javaCodeMaker.makeInitMethod(defs.postInit_VisageObjectMethodName, translatedPostInitBlocks, immediateMixinClasses);
javaCodeMaker.gatherFunctions(classFuncInfos);
javaCodeMaker.setContext(false, iDefinitions);
javaCodeMaker.makeMemberVariableAccessorInterfaceMethods(classVarInfos);
javaCodeMaker.makeMixinDCNT$(analysis.getCurrentClassSymbol(), false);
javaCodeMaker.makeMixinFCNT$(analysis.getCurrentClassSymbol(), false);
javaCodeMaker.makeFunctionInterfaceMethods();
javaCodeMaker.makeOuterAccessorInterfaceMembers();
javaCodeMaker.setContext(false, cDefinitions);
}
Name interfaceName = isMixinClass ? interfaceName(cDecl) : null;
return new VisageClassModel(
interfaceName,
makeImplementingInterfaces(diagPos, cDecl, immediateMixinClasses),
iDefinitions.toList(),
cDefinitions.toList(),
makeAdditionalImports(diagPos, cDecl, immediateMixinClasses),
superType,
visageSuperClassSym,
superClasses,
immediateMixinClasses,
allMixinClasses);
}
//
// Build a string that can be compared against MethodSymbol.toString()
//
private static String jcMethodDeclStr(JCMethodDecl meth) {
String str = meth.name.toString() + "(";
boolean needsComma = false;
boolean varArgs = (meth.mods.flags & Flags.VARARGS) != 0;
for (JCVariableDecl varDecl : meth.getParameters()) {
if (needsComma) str += ",";
str += varDecl.vartype.toString();
needsComma = true;
}
if (varArgs && str.endsWith("[]")) {
str = str.substring(0, str.length() - 2) + "...";
}
str += ")";
return str;
}
private List<JCExpression> makeImplementingInterfaces(DiagnosticPosition diagPos,
VisageClassDeclaration cDecl,
List<ClassSymbol> baseInterfaces) {
ListBuffer<JCExpression> implementing = ListBuffer.lb();
if (cDecl.isMixinClass()) {
implementing.append(makeIdentifier(diagPos, cObject));
implementing.append(makeIdentifier(diagPos, cMixin));
} else {
implementing.append(makeIdentifier(diagPos, cObject));
}
for (VisageExpression intf : cDecl.getImplementing()) {
implementing.append(makeType(diagPos, intf.type, false));
}
for (ClassSymbol baseClass : baseInterfaces) {
implementing.append(makeType(diagPos, baseClass.type, true));
}
return implementing.toList();
}
private List<JCExpression> makeAdditionalImports(DiagnosticPosition diagPos, VisageClassDeclaration cDecl, List<ClassSymbol> baseInterfaces) {
// Add import statements for all the base classes and basClass $Mixin(s).
// There might be references to them when the methods/attributes are rolled up.
ListBuffer<JCExpression> additionalImports = new ListBuffer<JCExpression>();
for (ClassSymbol baseClass : baseInterfaces) {
if (baseClass.type != null && baseClass.type.tsym != null &&
baseClass.type.tsym.packge() != cDecl.sym.packge() && // Work around javac bug (CR 6695838)
baseClass.type.tsym.packge() != syms.unnamedPackage) { // Work around javac bug. the visitImport of Attr
// is casting to JCFieldAcces, but if you have imported an
// JCIdent only a ClassCastException is thrown.
additionalImports.append(makeType( diagPos,baseClass.type, false));
additionalImports.append(makeType( diagPos,baseClass.type, true));
}
}
return additionalImports.toList();
}
// Add the methods and field for accessing the outer members. Also add a constructor with an extra parameter
// to handle the instantiation of the classes that access outer members
@SuppressWarnings("element-type-mismatch")
private ClassSymbol outerTypeSymbol(Symbol csym) {
if (csym != null && toJava.getHasOuters().containsKey(csym)) {
Symbol typeOwner = csym.owner;
while (typeOwner != null && typeOwner.kind != Kinds.TYP) {
typeOwner = typeOwner.owner;
}
if (typeOwner != null) {
// Only return an interface class if it's a mixin.
return (ClassSymbol)typeOwner.type.tsym;
}
}
return null;
}
// Add the vars referenced in the object literal init.
private void populateAnonInitVarMap(VisageClassDeclaration cDecl, LiteralInitVarMap varMap) {
List<VisageVarSymbol> objInitSyms = cDecl.getObjInitSyms();
for (VisageVarSymbol varSym : objInitSyms) {
varMap.addVar(varSym);
}
}
protected String getSyntheticPrefix() {
return "ivisage$";
}
//-----------------------------------------------------------------------------------------------------------------------------
//
// This class is used to simplify the construction of java code in the
// initialization builder.
//
class JavaCodeMaker extends JavaTreeBuilder {
// The current class analysis/
private final VisageAnalyzeClass analysis;
private ListBuffer<JCTree> definitions;
private Name scriptName;
private ClassSymbol scriptClassSymbol;
private final boolean isBoundFuncClass;
// Accessor body types.
static final int BODY_NONE = 0;
static final int BODY_NORMAL = 1;
static final int BODY_MIXIN = 2;
JavaCodeMaker(VisageAnalyzeClass analysis, ListBuffer<JCTree> definitions) {
super(null, analysis.getCurrentClassDecl(), false);
this.analysis = analysis;
this.definitions = definitions;
this.scriptClassSymbol = visagemake.ScriptSymbol(getCurrentClassSymbol());
this.scriptName = this.scriptClassSymbol.name;
this.isBoundFuncClass = (getCurrentOwner().flags() & VisageFlags.VISAGE_BOUND_FUNCTION_CLASS) != 0L;
}
//
// Method for changing the current definition list.
//
public void setContext(boolean isScript, ListBuffer<JCTree> definitions) {
setIsScript(isScript);
this.definitions = definitions;
}
//
// This method returns the current owner symbol.
//
ClassSymbol getCurrentOwner() {
return isScript() ? scriptClassSymbol : analysis.getCurrentClassSymbol();
}
//
// Methods for adding a new definitions.
//
private void addDefinition(JCTree member) {
if (member != null) {
definitions.append(member);
}
}
private void addDefinitions(List<JCTree> members) {
if (members != null) {
definitions.appendList(members);
}
}
//
// Methods for managing the current diagnostic position.
//
private void setDiagPos(VarInfo ai) { setDiagPos(ai.pos()); }
private void resetDiagPos() { setDiagPos(analysis.getCurrentClassPos()); }
//
// Returns the current class symbol.
//
public ClassSymbol getCurrentClassSymbol() {
return analysis.getCurrentClassSymbol();
}
//
// Argument ids
//
JCIdent varNumArg() {
return makeMethodArg(defs.varNum_ArgName, syms.intType);
}
JCIdent depNumArg() {
return makeMethodArg(defs.depNum_ArgName, syms.intType);
}
JCIdent updateInstanceArg() {
return makeMethodArg(defs.updateInstance_ArgName, syms.visage_ObjectType);
}
JCIdent objArg() {
return makeMethodArg(defs.obj_ArgName, syms.objectType);
}
JCIdent numberArg() {
return makeMethodArg(defs.number_ArgName, syms.intType);
}
JCIdent argsArg() {
return makeMethodArg(defs.args_ArgName, syms.visage_ObjectArray);
}
JCIdent argsFixedArg(int argNum) {
Name argName = argNum == 0 ? defs.arg1_ArgName : defs.arg2_ArgName;
return makeMethodArg(argName, syms.objectType);
}
JCIdent clearBitsArg() {
return makeMethodArg(defs.clearBits_ArgName, syms.intType);
}
JCIdent setBitsArg() {
return makeMethodArg(defs.setBits_ArgName, syms.intType);
}
//
// Returns true if the sym is the current class symbol.
//
public boolean isCurrentClassSymbol(Symbol sym) {
return analysis.isCurrentClassSymbol(sym);
}
//
// Return raw flags for current class.
//
public long rawFlags() {
return (isMixinClass() && !isScript()) ? (Flags.STATIC | Flags.PUBLIC) : Flags.PUBLIC;
}
//
// Create a method symbol.
//
public MethodSymbol makeMethodSymbol(long flags, Type returnType, Name methodName, List<Type> argTypes) {
return makeMethodSymbol(flags, returnType, methodName, getCurrentOwner(), argTypes);
}
//
// Create a var symbol.
//
public VisageVarSymbol makeVarSymbol(long flags, Type type, Name varName) {
return new VisageVarSymbol(types, names, flags, varName, type, getCurrentOwner());
}
//
// This method creates a member field field.
//
private JCVariableDecl makeField(long flags, Type varType, Name name, JCExpression varInit) {
VisageVarSymbol varSym = makeVarSymbol(flags, varType, name);
return Var(flags, varType, name, varInit, varSym);
}
//
//
// This method generates a simple java integer field then adds to the buffer.
//
private JCVariableDecl addSimpleIntVariable(long flags, Name name, int value) {
return makeField(flags, syms.intType, name, Int(value));
}
//
// This method generates a java field for a varInfo.
//
private JCVariableDecl makeVariableField(VarInfo varInfo, JCModifiers mods, Type varType, Name name, JCExpression varInit) {
// Get the var symbol.
VisageVarSymbol varSym = varInfo.getSymbol();
// Construct the variable itself.
JCVariableDecl var = Var(mods, makeType(varType), name, varInit, varSym);
// Update the statistics.
optStat.recordClassVar(varSym);
optStat.recordConcreteField();
return var;
}
//
// Determine if a var can be initialized simply.
//
public boolean useSimpleInit(VarInfo varInfo) {
if (!varInfo.useAccessors() && varInfo instanceof TranslatedVarInfo) {
VisageVar var = ((TranslatedVarInfo)varInfo).visageVar();
return var.isLiteralInit();
}
return false;
}
//
// Generate a simple init expression for the var.
//
public JCExpression getSimpleInit(VarInfo varInfo) {
if (useSimpleInit(varInfo)) {
VisageVar var = ((TranslatedVarInfo)varInfo).visageVar();
if (var.getInitializer().type.tag == TypeTags.BOT) {
return DefaultValue(var.type);
}
else {
return toJava.translateToExpression(var.getInitializer(), varInfo.getRealType());
}
}
return null;
}
//
// Generate a string containing bindee information used in the bindees annotation.
//
private String makeAnnoBindeesString(VarInfo varInfo) {
String annoBindeesString = "";
if (annoBindees) {
Set<String> bindeesSet = new HashSet<String>();
for (VarSymbol sym : varInfo.boundBindees()) {
bindeesSet.add(attributeValueName(sym).toString());
}
for (DependentPair pair : varInfo.boundBoundSelects()) {
bindeesSet.add(attributeValueName(pair.instanceSym) + "." + attributeValueName(pair.referencedSym));
}
for (String bindee : bindeesSet) {
if (annoBindeesString.length() != 0) annoBindeesString += ",";
annoBindeesString += bindee;
}
}
return annoBindeesString;
}
//
// Build the location and value field for each attribute.
//
public void makeAttributeFields(List<? extends VarInfo> attrInfos) {
for (VarInfo ai : attrInfos) {
// Only process attributes declared in this class (includes mixins.)
if (ai.needsCloning() && !ai.isOverride()) {
// Set the current diagnostic position.
setDiagPos(ai);
// Grab the variable symbol.
VisageVarSymbol varSym = ai.getSymbol();
long flags = attributeFieldAccessFlags(ai);
if (ai.isStatic()) flags |= Flags.STATIC;
if (ai.isDefault()) flags |= VisageFlags.DEFAULT;
JCModifiers mods = m().Modifiers(flags);
// Apply annotations, if current class then add source annotations.
List<JCAnnotation> annotations;
JCAnnotation annoSource = m().Annotation(
makeIdentifier(diagPos, VisageSymtab.sourceNameAnnotationClassNameString),
List.<JCExpression>of(String(varSym.name.toString())));
String annoBindeesString = makeAnnoBindeesString(ai);
if (annoBindeesString.length() != 0) {
JCAnnotation annoBindees = m().Annotation(
makeIdentifier(diagPos, VisageSymtab.bindeesAnnotationClassNameString),
List.<JCExpression>of(String(annoBindeesString)));
annotations = List.<JCAnnotation>of(annoSource, annoBindees);
} else {
annotations = List.<JCAnnotation>of(annoSource);
}
if (! isCurrentClassSymbol(varSym.owner))
annotations = annotations.prepend(make.Annotation(makeIdentifier(diagPos, VisageSymtab.inheritedAnnotationClassNameString), List.<JCExpression>nil()));
mods = addAccessAnnotationModifiers(null, varSym.flags(), mods, annotations);
// Construct the value field
boolean simple = useSimpleInit(ai);
JCExpression init = simple ? getSimpleInit(ai) :
isValueType(ai.getRealType()) ? defaultValue(ai) :
null;
// We only want a breakpoint for simple initializers - if
// we're just setting the default-value we're presumably
// doing the real initialization else - i.e. applyDefaulst$.
// Furthermore, setting a breakpoint in <clinit> can
// cause problems when loading the class, and it has
// marginal usefulness.
setDiagPos(simple && ! ai.isStatic() ? ai.pos() : null);
addDefinition(makeVariableField(ai, mods, ai.getRealType(), attributeValueName(varSym), init));
}
}
}
//
// Return a receiver$, scriptLevelAccess$() or null depending on the context.
//
private JCExpression getReceiver(VarInfo varInfo) {
return getReceiver(varInfo.getSymbol());
}
private JCExpression getReceiverOrThis(VarInfo varInfo) {
return getReceiverOrThis(varInfo.getSymbol());
}
//
// This method gathers all the translated functions in funcInfos.
//
public void gatherFunctions(List<FuncInfo> funcInfos) {
for (FuncInfo func : funcInfos) {
if (func instanceof TranslatedFuncInfo) {
addDefinitions(((TranslatedFuncInfo)func).jcFunction());
}
}
}
/* In an ideal world this is what we would like.
//
// Returns access flags appropriate for an attribute's field.
//
private long attributeFieldAccessFlags(VarInfo varInfo) {
long flags = Flags.PUBLIC;
if (!varInfo.isMixinVar()) {
flags = varInfo.isPublicAccess() ? Flags.PUBLIC : // User specified
varInfo.isProtectedAccess() ? Flags.PROTECTED : // User specified
varInfo.isStatic() ? Flags.PUBLIC : // Can't change access in subclass
varInfo.hasScriptOnlyAccess() &&
!varInfo.isExternallySeen() ? Flags.PRIVATE : // Internal var
varInfo.useAccessors() ? Flags.PROTECTED : // Subclasses need access for overrides
varInfo.isExternallySeen() ? 0 : // Package private
Flags.PRIVATE;
}
return flags;
}
//
// Returns access flags appropriate for an attribute's method.
//
private long attributeMethodAccessFlags(VarInfo varInfo) {
long flags = Flags.PUBLIC;
// TODO - Handle public read/init properly.
if (!varInfo.isMixinVar()) {
flags = varInfo.isPublicAccess() ||
varInfo.isPublicReadAccess() ||
varInfo.isPublicInitAccess() ? Flags.PUBLIC : // User specified
varInfo.isProtectedAccess() ? Flags.PROTECTED : // User specified
varInfo.isStatic() ? Flags.PUBLIC : // Can't change access in subclass
varInfo.hasScriptOnlyAccess() &&
!varInfo.isExternallySeen() ? Flags.PRIVATE : // Internal vars
varInfo.useAccessors() ? Flags.PUBLIC : // Generally visible
0; // Package private
}
return flags;
}
however this is what we need */
//
// Returns access flags appropriate for an attribute's field.
//
private long attributeFieldAccessFlags(VarInfo varInfo) {
long flags = Flags.PUBLIC;
if (!varInfo.isMixinVar()) {
flags = varInfo.isPublicAccess() ? Flags.PUBLIC : // User specified
varInfo.isProtectedAccess() ? Flags.PUBLIC : // User specified
varInfo.isStatic() ? Flags.PUBLIC : // Can't change access in subclass
varInfo.hasScriptOnlyAccess() &&
!varInfo.isExternallySeen() ? Flags.PRIVATE : // Internal var
varInfo.useAccessors() ? Flags.PUBLIC : // Subclasses need access for overrides
varInfo.isExternallySeen() ? Flags.PUBLIC : // Package private
Flags.PRIVATE;
}
return flags;
}
//
// Returns access flags appropriate for an attribute's method.
//
private long attributeMethodAccessFlags(VarInfo varInfo) {
long flags = Flags.PUBLIC;
// TODO - Handle public read/init properly.
if (!varInfo.isMixinVar()) {
flags = varInfo.isPublicAccess() ||
varInfo.isPublicReadAccess() ||
varInfo.isPublicInitAccess() ? Flags.PUBLIC : // User specified
varInfo.isProtectedAccess() ? Flags.PUBLIC : // User specified
varInfo.isStatic() ? Flags.PUBLIC : // Can't change access in subclass
varInfo.hasScriptOnlyAccess() &&
!varInfo.isExternallySeen() ? Flags.PRIVATE : // Internal vars
varInfo.useAccessors() ? Flags.PUBLIC : // Generally visible
Flags.PUBLIC; // Package private
}
return flags;
}
// This method generates code for setting a non-sequence var.
public List<JCStatement> makeSetAttributeCode(VarInfo varInfo, Name newValueName) {
ListBuffer<JCStatement> stmts = ListBuffer.lb();
VisageVarSymbol varSym = varInfo.getSymbol();
VisageVarSymbol proxyVarSym = varInfo.proxyVarSym();
Type type = varInfo.getRealType();
boolean needsInvalidate = needInvalidateAccessorMethod(varInfo);
boolean needsOnReplace = needOnReplaceAccessorMethod(varInfo);
// Set read only bit (trapdoor.)
if (varInfo.isReadOnly()) {
stmts.append(FlagChangeStmt(proxyVarSym, null, defs.varFlagIS_READONLY));
}
if (needsInvalidate || needsOnReplace) {
// T varOldValue$ = $var;
stmts.append(Var(Flags.FINAL, type, defs.varOldValue_LocalVarName, Get(proxyVarSym)));
// short varFlags$ = VFLG$var;
stmts.append(Var(Flags.FINAL, syms.intType, defs.varFlags_LocalVarName, GetFlags(proxyVarSym)));
// Set the state valid and mark defaults as applied
stmts.append(FlagChangeStmt(proxyVarSym, null, defs.varFlagINIT_INITIALIZED_DEFAULT));
ListBuffer<JCStatement> changedStmts = ListBuffer.lb();
if (needsInvalidate) {
changedStmts.append(CallBeInvalidate(varSym));
}
changedStmts.append(SetStmt(proxyVarSym, id(newValueName)));
if (needsInvalidate) {
changedStmts.append(CallBeTrigger(varSym));
}
if (needsOnReplace) {
changedStmts.append(CallStmt(attributeOnReplaceName(varSym), id(defs.varOldValue_LocalVarName), id(newValueName)));
}
// varOldValue$ != varNewValue$
// or !varOldValue$.equals(varNewValue$) test for Object value types
JCExpression valueChangedTest = isValueType(type) ?
NOT(Call(defs.Checks_equals, id(defs.varOldValue_LocalVarName), id(newValueName)))
: NE(id(defs.varOldValue_LocalVarName), id(newValueName));
// Default-Not_applied
JCExpression defaultAppliedTest = FlagTest(defs.varFlags_LocalVarName, defs.varFlagINITIALIZED_STATE_BIT, null);
stmts.append(
OptIf (OR(valueChangedTest, defaultAppliedTest), Block(changedStmts), null));
} else {
// Set the state valid and mark defaults as applied
stmts.append(FlagChangeStmt(proxyVarSym, null, defs.varFlagINIT_INITIALIZED_DEFAULT));
// var = varNewValue$
stmts.append(SetStmt(proxyVarSym, id(newValueName)));
}
if (needsInvalidate) {
// Set the state valid and mark defaults as applied, but don't cancel an invalidation in progress
stmts.append(
FlagChangeStmt(proxyVarSym, defs.varFlagSTATE_MASK, defs.varFlagSTATE_VALID));
}
return stmts.toList();
}
//
// This class is designed to reduce the repetitiveness of constructing methods.
//
public class MethodBuilder {
// Name of method to generate.
protected Name methodName;
// Method return type.
protected Type returnType;
// True if the return type is void.
protected boolean isVoidReturnType;
// True if we're to stop the build.
protected boolean stopBuild = false;
// True if needs a receiver arg.
protected boolean needsReceiver = isMixinClass() && !isScript();
// True if body is required.
protected int bodyType = BODY_NORMAL;
// Cached method symbol.
MethodSymbol methodSymbol = null;
// Grab the super class.
ClassSymbol superClassSym = analysis.getVisageSuperClassSym();
// Grab the mixin classes.
public List<ClassSymbol> immediateMixinClasses = analysis.getImmediateMixins();
// Stack of nested statements.
protected Stack<ListBuffer<JCStatement>> stmtsStack = new Stack<ListBuffer<JCStatement>>();
// Current set of statements.
protected ListBuffer<JCStatement> stmts = ListBuffer.lb();
void buildIf(boolean condition) {
stopBuild = !condition;
}
// List of parameter types.
ListBuffer<Type> paramTypes = ListBuffer.lb();
// List of parameter names.
ListBuffer<Name> paramNames = ListBuffer.lb();
MethodBuilder(Name methodName, Type returnType) {
this.methodName = methodName;
this.returnType = returnType;
this.isVoidReturnType = returnType == syms.voidType;
}
// This method saves the current list of statements and starts a new one.
public void beginBlock() {
stmtsStack.push(stmts);
stmts = ListBuffer.lb();
}
// This method restores the previous list of statements and returns the current
// list of statements in a block.
public JCBlock endBlock() {
return Block(endBlockAsList());
}
// This method restores the previous list of statements and returns the current
// list of statements.
public List<JCStatement> endBlockAsList() {
assert !stmtsStack.empty() : "MethodBuilder: mismatched blocks";
List<JCStatement> result = stmts.toList();
stmts = stmtsStack.pop();
return result;
}
// This method restores the previous list of statements and returns the current
// list buffer of statements.
public ListBuffer<JCStatement> endBlockAsBuffer() {
assert !stmtsStack.empty() : "MethodBuilder: mismatched blocks";
ListBuffer<JCStatement> result = stmts;
stmts = stmtsStack.pop();
return result;
}
// This method adds a new statement to the current lists of statements.
public void addStmt(JCStatement stmt) {
if (stmt != null) {
stmts.append(stmt);
}
}
// This method adds a new statement to the front of the current lists of statements.
public void prependStmt(JCStatement stmt) {
if (stmt != null) {
stmts.prepend(stmt);
}
}
// This method adds several statements to the current lists of statements.
public void addStmts(List<JCStatement> list) {
stmts.appendList(list);
}
public void addStmts(ListBuffer<JCStatement> list) {
stmts.appendList(list.toList());
}
public void addStmts(JCStatement... stmts) {
for (JCStatement stmt : stmts)
addStmt(stmt); // handle nulls this way
}
// This method adds a new parameter type and name to the current method.
public void addParam(Type type, Name name) {
paramTypes.append(type);
paramNames.append(name);
}
// This method adds a new parameter type and name to the current method.
public void addParam(JCIdent arg) {
addParam(arg.type, arg.name);
}
// This method returns all the parameters for the current method as a
// list of JCVariableDecl.
protected List<JCVariableDecl> paramList() {
Iterator<Type> typeIter = paramTypes.iterator();
Iterator<Name> nameIter = paramNames.iterator();
ListBuffer<JCVariableDecl> params = ListBuffer.lb();
if (needsReceiver) {
params.append(ReceiverParam(getCurrentClassDecl()));
}
while (typeIter.hasNext() && nameIter.hasNext()) {
params.append(makeParam(typeIter.next(), nameIter.next()));
}
return params.toList();
}
protected JCVariableDecl makeParam(Type varType, Name varName) {
return Param(varType, varName);
}
// This method returns all the parameters for the current method as a
// list of JCExpression.
protected List<JCExpression> argList() {
ListBuffer<JCExpression> args = ListBuffer.lb();
for (Name name : paramNames) {
args.append(id(name));
}
return args.toList();
}
// This method returns the rawFlags for the method.
protected long rawFlags() {
return JavaCodeMaker.this.rawFlags();
}
// This method generates a method symbol for the current method.
protected MethodSymbol methodSymbol() {
if (methodSymbol == null) {
ListBuffer<Type> argtypes = ListBuffer.lb();
if (needsReceiver) {
argtypes.append(getCurrentOwner().type);
}
for (Type type : paramTypes) {
argtypes.append(type);
}
methodSymbol = makeMethodSymbol(flags().flags, returnType, methodName, argtypes.toList());
}
return methodSymbol;
}
// This method generates a call statement to the mixin symbol.
public JCStatement callMixinStmt(ClassSymbol mixin) {
List<JCExpression> mixinArgs = List.<JCExpression>of(getReceiverOrThis()).appendList(argList());
JCExpression selector = makeType(mixin.type, false);
if (isVoidReturnType) {
return CallStmt(selector, methodName, mixinArgs);
} else {
return Return(Call(selector, methodName, mixinArgs));
}
}
// This method generates a call to the mixin symbol.
public void callMixin(ClassSymbol mixin) {
addStmt(callMixinStmt(mixin));
}
// This method generates all the calls for immediate mixins.
public void callMixins() {
for (ClassSymbol mixin : immediateMixinClasses) {
callMixin(mixin);
}
}
// This method generates the call statement for the super class.
public JCStatement callSuperStmt() {
if (superClassSym != null && !isMixinClass()) {
List<JCExpression> superArgs = argList();
if (isVoidReturnType) {
return CallStmt(id(names._super), methodName, superArgs);
} else {
return Return(Call(id(names._super), methodName, superArgs));
}
}
return null;
}
// This method generates the call for the super class.
public void callSuper() {
if (superClassSym != null && !isMixinClass()) {
addStmt(callSuperStmt());
}
}
// Return the method flags.
public JCModifiers flags() {
return m().Modifiers(rawFlags());
}
// Driver method to construct the current method.
public void build() {
// Initialize for method.
initialize();
// Generate the code.
if (!stopBuild) {
switch (bodyType) {
case BODY_NONE:
break;
case BODY_NORMAL:
generate();
break;
case BODY_MIXIN:
generateMixin();
break;
}
}
// Produce no method if generate indicates stopBuild.
if (!stopBuild) {
// Record method.
optStat.recordProxyMethod();
// Construct method.
addDefinition(Method(flags(),
returnType,
methodName,
paramList(),
bodyType != BODY_NONE ? stmts.toList() : null,
methodSymbol()));
}
}
// This method generates the statements for the method.
public void generate() {
// Emit method body.
body();
}
// This method generates the statements for a mixin proxy.
public void generateMixin() {
}
// This method contains any code to initialize the builder.
public void initialize() {
}
// This method generates the body of the method.
public void body() {
statements();
}
// This method generates specialized code for the body.
public void statements() {
}
}
//
// This class is designed to generate a method whose body is a static
// utility.
//
public class StaticMethodBuilder extends MethodBuilder {
StaticMethodBuilder(Name methodName, Type returnType) {
super(methodName, returnType);
}
// Return the method flags.
@Override
public JCModifiers flags() {
return m().Modifiers(Flags.STATIC | Flags.PUBLIC);
}
}
//
// This class is designed to generate a method whose body is a var
// accessor.
//
public class VarAccessorMethodBuilder extends MethodBuilder {
// Current var info.
protected VarInfo varInfo;
// Symbol used on the method.
protected VisageVarSymbol varSym;
// Symbol used when accessing the variable.
protected VisageVarSymbol proxyVarSym;
// Is a sequence type.
protected boolean isSequence;
// Real type of the var.
protected Type type;
// Element type of the var.
protected Type elementType;
VarAccessorMethodBuilder(Name methodName, Type returnType, VarInfo varInfo, int bodyType) {
super(methodName, returnType);
this.varInfo = varInfo;
this.bodyType = bodyType;
this.varSym = varInfo.getSymbol();
this.proxyVarSym = varInfo.proxyVarSym();
this.isSequence = varInfo.isSequence();
this.type = varInfo.getRealType();
this.elementType = isSequence ? varInfo.getElementType() : null;
this.needsReceiver = isMixinClass() && bodyType == BODY_NORMAL && !varInfo.isStatic();
}
// Return the raw Method flags.
@Override
public long rawFlags() {
long flags = attributeMethodAccessFlags(varInfo);
if (bodyType == BODY_NONE) {
flags |= Flags.ABSTRACT;
} else if (isMixinClass() || varInfo.isStatic()) {
flags |= Flags.STATIC;
}
return flags;
}
// This method generates the statements for a mixin proxy.
@Override
public void generateMixin() {
callMixin((ClassSymbol)varSym.owner);
}
}
//
// This class is designed to generate a method whose body is switched
// on var offsets.
//
public class VarCaseMethodBuilder extends MethodBuilder {
// List of attributes to scan.
protected List<VarInfo> attrInfos;
// Total count of attributes.
protected int varCount;
// True if overrides should be included.
protected boolean allowOverides;
// Current attribute.
protected VarInfo varInfo;
// Symbol used on the method.
protected VisageVarSymbol varSym;
// Symbol used when accessing the variable.
protected VisageVarSymbol proxyVarSym;
// Is a sequence type.
protected boolean isSequence;
// Real type of the var.
protected Type type;
// Element type of the var.
protected Type elementType;
VarCaseMethodBuilder(Name methodName, Type returnType, List<VarInfo> attrInfos, int varCount) {
super(methodName, returnType);
this.attrInfos = attrInfos;
this.varCount = varCount;
allowOverides = false;
addParam(varNumArg());
}
// Specialized body the handles a case per var.
@Override
public void body() {
// Prepare to accumulate cases.
ListBuffer<JCCase> cases = ListBuffer.lb();
// Prepare to accumulate ifs.
JCStatement ifStmt = null;
// Iterate thru each var.
for (VarInfo ai : attrInfos) {
clearDiagPos();
// Constrain the var.
if (ai.needsCloning() && !ai.isBareSynth()) {
// Prepare for the var.
this.varInfo = ai;
this.varSym = ai.getSymbol();
this.proxyVarSym = ai.proxyVarSym();
this.isSequence = ai.isSequence();
this.type = ai.getRealType();
this.elementType = isSequence ? ai.getElementType() : null;
// Construct the case.
beginBlock();
statements();
List<JCStatement> caseStmts = endBlockAsList();
if (!caseStmts.isEmpty()) {
if (isMixinClass()) {
// Test to see if it's the correct var.
ifStmt = OptIf(EQ(Offset(ai.getSymbol()), varNumArg()), Block(caseStmts), ifStmt);
} else if (ai.hasEnumeration()) {
// case tag number
JCExpression tag = Int(analysis.isFirstTier() ? ai.getEnumeration() :
(ai.getEnumeration() - varCount));
// Add the case, something like:
// case i: statement;
cases.append(m().Case(tag, caseStmts));
} else if (allowOverides && varInfo.isOverride()) {
// Test to see if it's the correct var.
ifStmt = OptIf(EQ(Offset(ai.getSymbol()), varNumArg()), Block(caseStmts), ifStmt);
}
}
}
}
// Add statement if there were some cases.
if (cases.nonEmpty()) {
// Add if as default case.
if (ifStmt != null) {
cases.append(m().Case(null, List.<JCStatement>of(ifStmt)));
}
// varNum - VCNT$
JCExpression tagExpr = analysis.isFirstTier() ? varNumArg() : MINUS(varNumArg(), id(defs.count_VisageObjectFieldName));
// Construct and add: switch(varNum - VCNT$) { ... }
addStmt(m().Switch(tagExpr, cases.toList()));
} else {
// No switch just rest.
addStmt(ifStmt);
}
if (stmts.nonEmpty()) {
// Call the super version.
callSuper();
} else {
// Control build.
buildIf(false);
}
}
}
//
// This method returns the default statement for a given var.
//
public List<JCStatement> getDefaultInitStatements(VarInfo varInfo) {
VisageVarSymbol varSym = varInfo.getSymbol();
VisageVarSymbol proxyVarSym = varInfo.proxyVarSym();
boolean hasOnReplace = varInfo.onReplaceAsInline() != null;
boolean isOverride = varInfo.isOverride();
boolean isBound = varInfo.hasBoundDefinition();
ListBuffer<JCStatement> stmts = ListBuffer.lb();
if (isBound) {
if (varSym.isVisageMember()) {
if (!varInfo.isSynthetic()) {
stmts.appendList(Stmts(
CallInvalidate(varSym),
CallTrigger(varSym),
If (NOT(FlagTest(proxyVarSym, BITOR(id(defs.varFlagIS_EAGER), id(defs.varFlagFORWARD_ACCESS)), null)),
Block(
Stmt(Getter(varSym))
)
)
));
}
} else {
// skip non-visage members for now
}
} else {
JCExpression init = varInfo.getDefaultInitExpression();
if (init != null) {
if (proxyVarSym.useAccessors()) {
if (proxyVarSym.isSequence()) {
stmts.append(CallStmt(defs.Sequences_set, getReceiverOrThis(), Offset(proxyVarSym), init));
} else if (varInfo.useSetters()) {
stmts.append(SetterStmt(proxyVarSym, init));
} else {
// Nest to allow duplicate varNewValue.
ListBuffer<JCStatement> inlineSetterStmts = ListBuffer.lb();
inlineSetterStmts.append(Var(varInfo.getRealType(), defs.varNewValue_ArgName, init));
inlineSetterStmts.appendList(makeSetAttributeCode(varInfo, defs.varNewValue_ArgName));
stmts.append(Block(inlineSetterStmts));
}
} else {
if (proxyVarSym.isSequence()) {
init = Call(defs.Sequences_incrementSharing, init);
}
stmts.append(SetStmt(proxyVarSym, init));
}
} else {
if (hasOnReplace && !isOverride) {
stmts.append(FlagChangeStmt(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_INITIALIZED));
stmts.append(CallStmt(attributeOnReplaceName(proxyVarSym), Get(proxyVarSym), Get(proxyVarSym)));
} else if (!varInfo.useAccessors()) {
stmts.append(FlagChangeStmt(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_INITIALIZED));
}
}
}
return stmts.toList();
}
//
// This method returns the default statements for a given sequence var.
//
private List<JCStatement> getSeqDefaultInitStatement(VarInfo varInfo) {
VisageVarSymbol varSym = varInfo.getSymbol();
VisageVarSymbol proxyVarSym = varInfo.proxyVarSym();
boolean hasOnReplace = varInfo.onReplaceAsInline() != null;
boolean isOverride = varInfo.isOverride();
boolean isBound = varInfo.hasBoundDefinition();
ListBuffer<JCStatement> stmts = ListBuffer.lb();
if (varInfo.getDefaultInitExpression() != null) {
stmts.appendList(getDefaultInitStatements(varInfo));
}
if (isBound) {
if (hasOnReplace) {
stmts.append(CallStmt(attributeSizeName(varSym)));
} else {
stmts.append(
If (NOT(FlagTest(proxyVarSym, BITOR(id(defs.varFlagIS_EAGER), id(defs.varFlagFORWARD_ACCESS)), null)),
Block(
CallStmt(attributeSizeName(varSym))
),
/*else*/
Block(
FlagChangeStmt(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_INITIALIZED)
)
)
);
}
} else if (varInfo.getDefaultInitExpression() == null) {
if (hasOnReplace && !isOverride) {
stmts.append(FlagChangeStmt(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_INITIALIZED));
stmts.append(CallSeqInvalidate(varSym, Int(0), Int(0), Int(0)));
stmts.append(CallSeqTriggerInitial(varSym, Int(0)));
if (needOnReplaceAccessorMethod(varInfo)) {
stmts.append(
// If it didn't get initialized to default along the way, send the on-replace (because it would be blocked)
If (FlagTest(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_INITIALIZED),
CallStmt(attributeOnReplaceName(proxyVarSym), Int(0), Int(0), Int(0))
));
}
} else if (varInfo.useAccessors()) {
stmts.append(CallStmt(defs.Sequences_replaceSlice, getReceiverOrThis(), Offset(varSym), Get(varSym), Int(0), Int(0)));
} else {
stmts.append(FlagChangeStmt(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_INITIALIZED));
}
}
return stmts.toList();
}
//
// Determine if this override needs an invalidate method
// Must be in sync with makeInvalidateAccessorMethod
//
private boolean needOverrideInvalidateAccessorMethod(VarInfo varInfo) {
if (varInfo.isMixinVar() ||
varInfo.onReplace() != null ||
varInfo.onInvalidate() != null ||
!varInfo.boundInvalidatees().isEmpty()) {
// based on makeInvalidateAccessorMethod
return true;
} else if (varInfo.hasBoundDefinition()) {
return false;
} else {
if (varInfo instanceof TranslatedVarInfoBase) {
return ((TranslatedVarInfoBase) varInfo).boundBinders().size() != 0;
} else {
return false;
}
}
}
//
// Determine if this var needs an invalidate method
// Must be in sync with makeInvalidateAccessorMethod
//
private boolean needInvalidateAccessorMethod(VarInfo varInfo) {
return (varInfo.isOverride() && needOverrideInvalidateAccessorMethod(varInfo)) ||
varInfo.generateSequenceAccessors() ||
!isLeaf(varInfo) ||
varInfo.hasDependents() || varInfo.isDependent() ||
!varInfo.boundInvalidatees().isEmpty() ||
varInfo.isMixinVar() ||
(varInfo.isStatic() && !varInfo.getSymbol().hasScriptOnlyAccess()) ||
varInfo.onInvalidate() != null;
}
//
// Determine if this var needs to call switchDependence in onReplace
//
private boolean needSwitchDependence(VarInfo varInfo) {
for (VarInfo dependent : varInfo.boundBinders()) {
for (DependentPair depPair : dependent.boundBoundSelects()) {
// static variables and sequences are handled diffently
if (depPair.instanceSym != varInfo.sym ||
depPair.referencedSym.isStatic() ||
types.isSequence(depPair.referencedSym.type)) {
continue;
}
return true;
}
}
return false;
}
//
// Determine if this var needs an on replace method.
//
private boolean needOnReplaceAccessorMethod(VarInfo varInfo) {
return needSwitchDependence(varInfo) ||
varInfo.onReplace() != null ||
varInfo.isMixinVar() ||
!(!varInfo.isOverride() && isLeaf(varInfo));
}
//
// Returns true if the var can not be overridden.
//
private boolean isLeaf(VarInfo varInfo) {
VisageVarSymbol varSym = (VisageVarSymbol)varInfo.getSymbol();
long flags = varSym.flags();
return isAnonClass() ||
varSym.isDef() ||
varSym.hasScriptOnlyAccess() && (flags & VisageFlags.OVERRIDE) == 0;
}
//-----------------------------------------------------------------------------------------------------------------------------
//
// Sequence var accessors.
//
//
// This method constructs the getter method for a sequence attribute.
//
private void makeSeqGetterAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeGetterName(varInfo.getSymbol()),
varInfo.getRealType(),
varInfo, bodyType) {
@Override
public void statements() {
if (isBoundFuncClass && varInfo.isParameter()) {
// Prepare to accumulate body of if.
beginBlock();
/*
* if "foo" is the variable name, then we generate
*
* var = (cast)$$boundInstance$foo.get($$boundVarNum$foo);
*
*/
JCExpression get$call = Call(
id(boundFunctionObjectParamName(varSym.name)),
defs.get_VisageObjectMethodName,
id(boundFunctionVarNumParamName(varSym.name)));
JCExpression castGet = typeCast(varInfo.getRealType(), syms.objectType, get$call);
addStmt(SetStmt(varSym, castGet));
// Release cycle lock.
addStmt(FlagChangeStmt(proxyVarSym, null, defs.varFlagINIT_INITIALIZED_DEFAULT));
// Is it invalid?
JCExpression condition = FlagTest(proxyVarSym, defs.varFlagIS_BOUND_INVALID, defs.varFlagIS_BOUND_INVALID);
// if (invalid) { set$var(init/bound expression); }
addStmt(OptIf(condition, endBlock(), null));
} else {
// Begin if block.
beginBlock();
// seq$ = new SequenceRef(<<typeinfo T>>, this, VOFF$seq);
List<JCExpression> args = List.<JCExpression>of(
TypeInfo(diagPos, elementType),
getReceiverOrThis(proxyVarSym),
Offset(varSym));
JCExpression newExpr = m().NewClass(null, null, makeType(types.erasure(syms.visage_SequenceRefType)), args, null);
// If (seq$ == null && isBound) { seq$ = new SequenceRef(<<typeinfo T>>, this, VOFF$seq); }
addStmt(
OptIf (AND(
EQ(Get(proxyVarSym), defaultValue(varInfo)),
FlagTest(proxyVarSym, defs.varFlagIS_BOUND, defs.varFlagIS_BOUND)),
Block(
// Be sure the sequence is initialized before returning the SequenceRef -- call the size accessor to initialize
CallStmt(attributeSizeName(varSym)),
// If the size method didn't set the sequence value, make it a SequenceRef
OptIf( EQ(Get(proxyVarSym), defaultValue(varInfo)),
SetStmt(proxyVarSym, newExpr)
)
)
));
}
// Construct and add: return $var;
addStmt(Return(Get(proxyVarSym)));
}
};
vamb.build();
}
//
// This method constructs the get element method for a sequence attribute.
//
private void makeSeqGetElementAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeGetElementName(varInfo.getSymbol()),
varInfo.getElementType(),
varInfo, bodyType) {
@Override
public void initialize() {
addParam(posArg());
}
// Construct and add: return $var.get(pos$);
private JCStatement accessorGet() {
VisageTypeRepresentation typeRep = types.typeRep(varInfo.getElementType());
Name getMethodName = defs.typedGet_SequenceMethodName[typeRep.ordinal()];
return Return(Call(Get(proxyVarSym), getMethodName, posArg()));
}
@Override
public void statements() {
if (varInfo.hasBoundDefinition()) {
if (isBoundFuncClass && varInfo.isParameter()) {
JCExpression apply = Call(
id(boundFunctionObjectParamName(varSym.name)),
defs.getElement_VisageObjectMethodName,
id(boundFunctionVarNumParamName(varSym.name)),
posArg());
addStmt(Return(castFromObject(apply, varInfo.getElementType())));
} else if (varInfo.isInitWithBoundFuncResult()) {
/**
* If this var "foo" is initialized with bound function result var, then
* we want to get element from the Pointer. We translate as:
*
* public static int elem$foo(final int pos$) {
* final Pointer ivisage$0tmp = get$$$bound$result$foo();
* return ivisage$0tmp != null ? (Integer)ivisage$0tmp.get(pos$) : 0;
* }
*/
JCVariableDecl tmpPtrVar = TmpVar("tmp", syms.visage_PointerType, Getter(varInfo.boundFuncResultInitSym()));
addStmt(tmpPtrVar);
JCExpression ptrNonNullCond = NEnull(id(tmpPtrVar));
JCExpression apply = Call(
id(tmpPtrVar),
defs.get_PointerMethodName,
posArg());
addStmt(Return(If(ptrNonNullCond,
castFromObject(apply, varInfo.getElementType()),
makeDefaultValue(varInfo.pos(), varInfo.getElementType()))));
} else if (varInfo.isSynthetic()) {
addStmt(varInfo.boundElementGetter());
} else {
addStmt(
If (FlagTest(proxyVarSym, defs.varFlagIS_BOUND, defs.varFlagIS_BOUND),
If (FlagTest(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_PENDING),
Block(
FlagChangeStmt(proxyVarSym, null, defs.varFlagFORWARD_ACCESS),
Return (DefaultValue(this.elementType))
),
/*else (active)*/
varInfo.boundElementGetter()
),
/*else (not bound)*/
accessorGet()
)
);
}
} else if (varInfo.useAccessors()) {
addStmt(accessorGet());
}
}
};
vamb.build();
}
//
// This method constructs the getter method for a sequence attribute.
//
private void makeSeqGetSizeAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeSizeName(varInfo.getSymbol()),
syms.intType,
varInfo, bodyType) {
// Size from sequence
private JCStatement accessorSize() {
return Return(Call(Get(proxyVarSym), defs.size_SequenceMethodName));
}
@Override
public void statements() {
if (varInfo.hasBoundDefinition()) {
if (isBoundFuncClass && varInfo.isParameter()) {
JCExpression apply = Call(
id(boundFunctionObjectParamName(varSym.name)),
defs.size_VisageObjectMethodName,
id(boundFunctionVarNumParamName(varSym.name)));
addStmt(Return(apply));
} else if (varInfo.isInitWithBoundFuncResult()) {
/**
* If this var "foo" is initialized with bound function result var, then
* we want to get sequence size from the Pointer. We translate as:
*
* public static int size$foo() {
* Pointer oldPtr = $$$bound$result$$foo;
* Pointer newPtr = get$$$bound$result$$foo();
* Pointer.switchDependence(oldPtr, newPtr, receiver);
*
* <make-it-valid>
* if (newPtr != null) {
* return (Integer)ivisage$0tmp.size();
* } else {
* return 0;
* }
* }
*/
Name ptrVarName = attributeValueName(varInfo.boundFuncResultInitSym());
// declare a temp variable of type Pointer to store old value of Pointer field
JCVariableDecl oldPtrVar = TmpVar("old", syms.visage_PointerType, id(ptrVarName));
addStmt(oldPtrVar);
JCVariableDecl newPtrVar = TmpVar("new", syms.visage_PointerType, Getter(varInfo.boundFuncResultInitSym()));
addStmt(newPtrVar);
// Add the receiver of the current Var symbol as dependency to the Pointer, so that
// we will get notification whenever the result of the bound function evaluation changes.
addStmt(CallStmt(defs.Pointer_switchDependence,
id(oldPtrVar),
id(newPtrVar),
getReceiverOrThis(varSym),
DepNum(getReceiver(varSym), null, varInfo.boundFuncResultInitSym())));
JCStatement setValid = FlagChangeStmt(proxyVarSym, defs.varFlagINIT_STATE_MASK, defs.varFlagVALID_DEFAULT_APPLIED);
addStmt(setValid);
JCExpression apply = Call(
Getter(varInfo.boundFuncResultInitSym()),
defs.size_PointerMethodName);
addStmt(OptIf(NEnull(id(newPtrVar)),
Block(setValid, Return(apply)),
Return(Int(0))
)
);
} else if (varInfo.isSynthetic()) {
addStmt(varInfo.boundSizeGetter());
} else {
addStmt(
If (FlagTest(proxyVarSym, defs.varFlagIS_BOUND, defs.varFlagIS_BOUND),
If (FlagTest(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_PENDING),
Block(
FlagChangeStmt(proxyVarSym, null, defs.varFlagFORWARD_ACCESS),
Return (Int(0))
),
/*else (active)*/
varInfo.boundSizeGetter()
),
/*else (not bound)*/
accessorSize()
)
);
}
} else if (varInfo.useAccessors()) {
// Construct and add: return $var.size();
addStmt(accessorSize());
}
}
};
vamb.build();
}
//
// This method constructs the invalidate method for a sequence attribute.
//
private void makeSeqInvalidateAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeInvalidateName(varInfo.getSymbol()),
syms.voidType,
varInfo, bodyType) {
@Override
public void initialize() {
addParam(startPosArg());
addParam(endPosArg());
addParam(newLengthArg());
addParam(phaseArg());
}
@Override
public void statements() {
// Handle invalidators if present.
List<BindeeInvalidator> invalidatees = varInfo.boundInvalidatees();
boolean hasInvalidators = !invalidatees.isEmpty();
beginBlock();
if (hasInvalidators) {
// Insert invalidators.
for (BindeeInvalidator invalidator : invalidatees) {
addStmt(invalidator.invalidator);
}
}
boolean override = varInfo.isOverride();
boolean mixin = !isMixinClass() && varInfo instanceof MixinClassVarInfo;
// Call super.
if (override || varInfo instanceof SuperClassVarInfo) {
callSuper();
} else if (mixin) {
// Mixin.invalidate$var(this, phase$);
callMixin((ClassSymbol)varSym.owner);
override = true;
}
for (VarInfo otherVar : varInfo.boundBinders()) {
if (depGraphWriter != null) {
depGraphWriter.writeDependency(otherVar.sym, varSym);
}
// invalidate$var(phase$);
if (!otherVar.generateSequenceAccessors()) {
addStmt(CallStmt(attributeInvalidateName(otherVar.getSymbol()), phaseArg()));
} else {
addStmt(CallStmt(attributeInvalidateName(otherVar.getSymbol()),
startPosArg(),
endPosArg(),
newLengthArg(),
phaseArg()));
}
}
if (!override) {
// notifyDependents(VOFF$var, phase$);
addStmt(CallStmt(getReceiver(varInfo), defs.notifyDependents_VisageObjectMethodName, Offset(proxyVarSym),
startPosArg(), endPosArg(), newLengthArg(),
phaseArg()));
}
if (varInfo.onInvalidate() != null) {
// if (invalidate-phase) { call-on-invalidate }
addStmt(OptIf(AND(
IsInvalidatePhase(),
FlagTest(proxyVarSym, defs.varFlagINIT_INITIALIZED_DEFAULT, defs.varFlagINIT_INITIALIZED_DEFAULT)
), varInfo.onInvalidateAsInline()));
}
if (!override && varInfo.sym.useReplaceTrigger()) {
// if (trigger-phase and real-trigger) { call-on-replace }
addStmt(
OptIf(
AND(AND(
IsTriggerPhase(),
GE(startPosArg(), Int(0))),
FlagTest(proxyVarSym, defs.varFlagINIT_INITIALIZED_DEFAULT, defs.varFlagINIT_INITIALIZED_DEFAULT)
),
CallStmt(attributeOnReplaceName(proxyVarSym),
startPosArg(),
endPosArg(),
newLengthArg())
)
);
}
//TODO: no test needed if non-bound and not overriddable
addStmt(
OptIf (FlagTest(proxyVarSym, defs.varFlagINIT_INITIALIZED, defs.varFlagINIT_INITIALIZED),
endBlock()
)
);
}
};
vamb.build();
}
//
// This method constructs the onreplace$ method for a sequence attribute.
//
private void makeSeqOnReplaceAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeOnReplaceName(varInfo.getSymbol()),
syms.voidType,
varInfo, bodyType) {
Name oldValueName;
Name newValueName;
Name firstIndexName;
Name lastIndexName;
Name newElementsName;
Name newLengthName;
@Override
public void initialize() {
VisageOnReplace onReplace = varInfo.onReplace();
newValueName = defs.varNewValue_ArgName;
firstIndexName = paramStartPosName(onReplace);
lastIndexName = paramEndPosName(onReplace);
newLengthName = paramNewElementsLengthName(onReplace);
addParam(syms.intType, firstIndexName);
addParam(syms.intType, lastIndexName);
addParam(syms.intType, newLengthName);
buildIf(varSym.useReplaceTrigger());
}
@Override
public void statements() {
// Call super first.
if (varInfo.isOverride()) {
callSuper();
}
// Mixin onreplace$
if (!isMixinClass() && varInfo instanceof MixinClassVarInfo) {
callMixin((ClassSymbol)varSym.owner);
}
// Fetch the on replace statement or null.
VisageOnReplace onReplace = varInfo.onReplace();
if (onReplace != null) {
VisageVar lastIndex = varInfo.onReplace().getLastIndex();
VisageVar newElements = varInfo.onReplace().getNewElements();
if (lastIndex != null && varInfo.onReplace().getEndKind() == VisageSequenceSlice.END_INCLUSIVE) {
addStmt(Var(syms.intType, lastIndex.name,
MINUS(endPosArg(), Int(1))));
}
VisageVarSymbol savedVarSym = onReplace.getSaveVar() != null ? onReplace.getSaveVar().sym : null;
// The idea of the following is to implement:
// var x : T[] = ... on replace oldV[i..j] = newV { something };
// as if it were:
// var x$save$ : T[];
// var x : T[] = ... on replace [i..j] = newV {
// def oldV = Sequences.copy(x$save$);
// x$save$[i..j] = newV;
// something
// };
if (savedVarSym != null) {
// FIXME Some performance tweaking makes sense:
// - If the oldValue is only used for indexing or sizeof, then we
// can extract the value of the "gap" of the saved-dalue ArraySequence,
// as in the 1.2 compiler.
// - The getNewElements call should be combined with the replaceSlice.
addStmt(Var(type, onReplace.getOldValue().getName(),
Call(defs.Sequences_incrementSharing, Get(savedVarSym))));
}
if (newElements != null
&& (newElements.sym.flags() & VisageFlags.VARUSE_OPT_TRIGGER) == 0) {
JCExpression seq = Getter(varSym);
JCExpression init = Call(defs.Sequences_getNewElements, seq, id(firstIndexName), id(newLengthName));
addStmt(Var(newElements.type, newElements.name, init));
}
// Insert the trigger.
JCStatement triggerBody = varInfo.onReplaceAsInline();
if (savedVarSym != null) {
JCStatement decr = CallStmt(Get(savedVarSym), names.fromString("decrementSharing"));
JCStatement update =
SetStmt(savedVarSym,
Call(defs.Sequences_replaceSlice,
Get(savedVarSym),
Call(defs.Sequences_getNewElements, Getter(varSym), id(firstIndexName), id(newLengthName)),
id(firstIndexName),
endPosArg()
));
triggerBody = m().Try(Block(triggerBody), List.<JCCatch>nil(), Block(decr, update));
}
addStmt(triggerBody);
}
}
};
vamb.build();
}
//-----------------------------------------------------------------------------------------------------------------------------
//
// Normal var accessors.
//
//
// This method constructs the getter method for the specified attribute.
//
private void makeGetterAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeGetterName(varInfo.getSymbol()),
varInfo.getRealType(),
varInfo, bodyType) {
boolean needsInvalidate = needInvalidateAccessorMethod(varInfo);
boolean needsOnReplace = needOnReplaceAccessorMethod(varInfo);
@Override
public void statements() {
clearDiagPos();
if (varInfo.isBareSynth()) {
// short varFlags$ = VFLG$var;
addStmt(Var(Flags.FINAL, syms.intType, defs.varFlags_LocalVarName, GetFlags(proxyVarSym)));
// for a bare-synthethic, just return bound-expression
JCExpression returnVal = varInfo.boundInit();
if (varInfo.isInitWithBoundFuncResult()) {
JCVariableDecl newPtrVar = TmpVar("new", syms.visage_PointerType, returnVal);
addStmt(newPtrVar);
returnVal = If(NEnull(id(newPtrVar)),
castFromObject(Call(
id(newPtrVar),
defs.get_PointerMethodName), varSym.type),
defaultValue(varInfo));
}
addStmt(
TryWithErrorHandler(varInfo,
varInfo.boundPreface(),
Return(returnVal),
Return(defaultValue(varInfo))));
} else {
if (isBoundFuncClass && varInfo.isParameter()) {
// Wrapping if (!init pending)
beginBlock();
// Prepare to accumulate body of if.
beginBlock();
// short varFlags$ = VFLG$var;
addStmt(Var(Flags.FINAL, syms.intType, defs.varFlags_LocalVarName, GetFlags(proxyVarSym)));
// Lock cycles.
addStmt(FlagChangeStmt(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_PENDING));
/*
* if "foo" is the variable name, then we generate
*
* set$var((cast)$$boundInstance$foo.get($$boundVarNum$foo));
*
*/
JCExpression get$call = Call(
id(boundFunctionObjectParamName(varSym.name)),
defs.get_VisageObjectMethodName,
id(boundFunctionVarNumParamName(varSym.name)));
JCExpression castGet = typeCast(varInfo.getRealType(), syms.objectType, get$call);
// T varNewValue$ = cast value
addStmt(Var(Flags.FINAL, type, defs.varNewValue_ArgName, castGet));
// Set the var.
addStmts(makeBoundGetAttributeCode());
// Is it invalid?
JCExpression condition = FlagTest(proxyVarSym, defs.varFlagIS_BOUND_INVALID, defs.varFlagIS_BOUND_INVALID);
// if (invalid) { set$var(init/bound expression); }
addStmt(OptIf(condition,
endBlock(),
null));
// if (!init pending)
JCExpression initPendingExpr = NOT(FlagTest(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_PENDING));
addStmt(OptIf(initPendingExpr,
endBlock(),
null));
} else if (varInfo.hasBoundDefinition()) {
// Prepare to accumulate body of if.
beginBlock();
// Set to new value. Bogus assert, it seems an local var can be bound have no init.
// assert varInfo.boundInit() != null : "Oops! No boundInit. varInfo = " + varInfo + ", preface = " + varInfo.boundPreface();
// short varFlags$ = VFLG$var;
addStmt(Var(Flags.FINAL, syms.intType, defs.varFlags_LocalVarName, GetFlags(proxyVarSym)));
// Lock cycles.
addStmt(FlagChangeStmt(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_PENDING));
// set$var(init/bound expression)
JCExpression initValue = varInfo.boundInit();
if (initValue == null) {
initValue = defaultValue(varInfo);
}
if (varInfo.isInitWithBoundFuncResult()) {
addStmts(varInfo.boundPreface());
/**
* For a field named "foo" that is initialized from the bound function
* result Pointer, we generate the following:
*
* Pointer oldPtr = $$$bound$result$$foo;
* Pointer newPtr = get$$$bound$result$$foo();
* Pointer.switchDependence(oldPtr, newPtr, receiver);
*
* if (newPtr != null) {
* set$foo((ExpectedType)newPtr.get());
* } else {
* set$foo(<default-value>);
* }
*/
Name ptrVarName = attributeValueName(varInfo.boundFuncResultInitSym());
// declare a temp variable of type Pointer to store old value of Pointer field
JCVariableDecl oldPtrVar = TmpVar("old", syms.visage_PointerType, id(ptrVarName));
addStmt(oldPtrVar);
JCVariableDecl newPtrVar = TmpVar("new", syms.visage_PointerType, initValue);
addStmt(newPtrVar);
// Add the receiver of the current Var symbol as dependency to the Pointer, so that
// we will get notification whenever the result of the bound function evaluation changes.
addStmt(CallStmt(defs.Pointer_switchDependence,
id(oldPtrVar),
id(newPtrVar),
getReceiverOrThis(varSym),
DepNum(getReceiver(varSym), null, varInfo.boundFuncResultInitSym())));
// We have a Pointer - we need to call Pointer.get() and cast the result.
initValue = castFromObject(Call(id(newPtrVar), defs.get_PointerMethodName), varSym.type);
initValue = If(NEnull(id(newPtrVar)), initValue, defaultValue(varInfo));
// T varNewValue$ = default value
addStmt(Var(Flags.FINAL, type, defs.varNewValue_ArgName, initValue));
// Set the var.
addStmts(makeBoundGetAttributeCode());
} else {
// T varNewValue$
addStmt(Var(0, type, defs.varNewValue_ArgName, null));
addStmt(
TryWithErrorHandler(varInfo,
varInfo.boundPreface(),
Stmt(m().Assign(id(defs.varNewValue_ArgName), initValue)),
/*on exception*/
Stmt(m().Assign(id(defs.varNewValue_ArgName), defaultValue(varInfo)))));
// Set the var.
addStmts(makeBoundGetAttributeCode());
}
// if (pending) { mark forward access } else if (bound and invalid) { set$var(init/bound expression); }
addStmt(
OptIf (FlagTest(proxyVarSym, defs.varFlagINIT_MASK, defs.varFlagINIT_PENDING),
Block(
FlagChangeStmt(proxyVarSym, null, defs.varFlagFORWARD_ACCESS)
),
/*else (active)*/
OptIf (FlagTest(proxyVarSym, defs.varFlagIS_BOUND_INVALID, defs.varFlagIS_BOUND_INVALID),
endBlock()
)
)
);
} else if (varInfo.onInvalidate() != null) {
// unbound with on-invalidate -- reset validity
addStmt(FlagChangeStmt(proxyVarSym, defs.varFlagSTATE_MASK, defs.varFlagSTATE_VALID));
}
// Construct and add: return $var;
addStmt(Return(Get(proxyVarSym)));
}
}
// This method generates code for setting a non-sequence var.
public List<JCStatement> makeBoundGetAttributeCode() {
Name newValueName = defs.varNewValue_ArgName;
Name oldValueName = defs.varOldValue_LocalVarName;
ListBuffer<JCStatement> bgstmts = ListBuffer.lb();
JCStatement finish;
if (needsOnReplace) {
// varOldValue$ != varNewValue$
// or !varOldValue$.equals(varNewValue$) test for Object value types
JCExpression valueChangedTest = isValueType(type)
? NOT(Call(defs.Checks_equals, id(oldValueName), id(newValueName)))
: NE(id(oldValueName), id(newValueName));
// Default-Not_applied
JCExpression defaultAppliedTest = FlagTest(defs.varFlags_LocalVarName, defs.varFlagINITIALIZED_STATE_BIT, null);
finish =
Block(
//Debug("BGet-ApplyOnr "+proxyVarSym, id(newValueName)),
Var(Flags.FINAL, type, oldValueName, Get(proxyVarSym)),
FlagChangeStmt(proxyVarSym, defs.varFlagSTATE_MASK, defs.varFlagVALID_DEFAULT_APPLIED),
If (OR(valueChangedTest, defaultAppliedTest),
Block(
//Debug("Onr "+proxyVarSym, id(newValueName)),
SetStmt(proxyVarSym, id(newValueName)),
CallStmt(attributeOnReplaceName(varSym), id(oldValueName), id(newValueName))
)
)
);
} else {
finish =
Block(
//Debug("BGet-Apply "+proxyVarSym, id(newValueName)),
FlagChangeStmt(proxyVarSym, defs.varFlagSTATE_MASK, defs.varFlagVALID_DEFAULT_APPLIED),
SetStmt(proxyVarSym, id(newValueName))
);
}
bgstmts.append(finish);
return bgstmts.toList();
}
// generates try {preface, action} catch(RuntimeException re) { ErrorHandler.bindException(re); <onCatchStat> }
JCStatement TryWithErrorHandler(VarInfo varInfo, List<JCStatement> preface, JCStatement action, JCStatement onCatchStat) {
boolean isSafe = varInfo.hasSafeInitializer();
if (isSafe) {
// No exceptions can be thrown, just in-line it
return
Block(
preface,
action
);
}
JCVariableDecl tmpVar = TmpVar(syms.runtimeExceptionType, null);
JCStatement callErrorHandler = CallStmt(defs.ErrorHandler_bindException, id(tmpVar));
// The Javac backend expects a line-number of the catch-clause;
// otherwise it sometimes emits "line 0".
// But we need and want no line-number on the other boiler-plate.
clearDiagPos();
JCBlock cblock = Block(callErrorHandler, onCatchStat);
setDiagPos(varInfo);
JCCatch tcatch = m().Catch(tmpVar, cblock);
clearDiagPos();
return
Try(
Block(preface, action),
tcatch
);
}
};
clearDiagPos();
vamb.build();
}
private JCExpression validBindeesTest(VarInfo varInfo) {
Set<VisageVarSymbol> unique = new LinkedHashSet<VisageVarSymbol>();
for (VisageVarSymbol vsym : varInfo.boundBindees()) {
if (!vsym.isSpecial() && !vsym.isSequence()) {
unique.add(vsym);
}
}
if (unique.size() == 0) {
return null;
} else {
JCExpression test = null;
for (VisageVarSymbol vsym : unique) {
JCExpression t = FlagTest(vsym, defs.varFlagSTATE_TRIGGERED, defs.varFlagINVALID_STATE_BIT);
if (test == null) {
test = t;
} else {
test = OR(test, t);
}
}
return test;
}
}
//
// This method constructs the setter method for the specified attribute.
//
private void makeSetterAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeSetterName(varInfo.getSymbol()),
varInfo.getRealType(),
varInfo, bodyType) {
@Override
public void initialize() {
addParam(type, defs.varNewValue_ArgName);
}
@Override
public void statements() {
boolean isLeaf = isLeaf(varInfo);
if (isLeaf) {
if (varInfo.isReadOnly()) {
addStmt(CallStmt(getReceiver(varSym), defs.restrictSet_VisageObjectMethodName, GetFlags(varSym)));
}
} else {
// Restrict setting.
beginBlock();
addStmt(CallStmt(getReceiver(varSym), defs.restrictSet_VisageObjectMethodName, GetFlags(varSym)));
JCExpression ifReadonlyTest = FlagTest(varSym, defs.varFlagIS_READONLY, null);
// if (isReadonly$(VOFF$var)) { restrictSet$(VOFF$var); }
addStmt(OptIf(NOT(ifReadonlyTest),
endBlock()));
}
if (varInfo.hasBoundDefinition() && varInfo.hasBiDiBoundDefinition()) {
// Begin bidi block.
beginBlock();
// Preface to setter.
addStmts(varInfo.boundInvSetterPreface());
// Test to see if bound.
JCExpression ifBoundTest = FlagTest(varSym, defs.varFlagIS_BOUND, defs.varFlagIS_BOUND);
// if (isBound$(VOFF$var)) { set$other(inv bound expression); }
addStmt(OptIf(ifBoundTest,
endBlock()));
}
// Set the var.
addStmts(makeSetAttributeCode(varInfo, defs.varNewValue_ArgName));
// return $var;
addStmt(Return(Get(proxyVarSym)));
}
};
vamb.build();
}
//
// This method constructs the invalidate method for the specified attribute.
//
private void makeInvalidateAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeInvalidateName(varInfo.getSymbol()),
syms.voidType,
varInfo, bodyType) {
private void abortIfInvalidComponents() {
JCExpression vbt = validBindeesTest(varInfo);
if (vbt != null) {
addStmt(
If (AND(IsTriggerPhase(), vbt),
// Abort
Block(
// Some component is invalid -- wait for it to come around triggered
//Debug("Inv-Abort "+proxyVarSym),
Return (null)
)
)
);
}
}
@Override
public void initialize() {
addParam(phaseArg());
}
@Override
public void statements() {
// Handle invalidators if present.
List<BindeeInvalidator> invalidatees = varInfo.boundInvalidatees();
boolean hasInvalidators = !invalidatees.isEmpty();
JCVariableDecl varState = Var(syms.intType,
defs.varState_LocalVarName,
BITAND(GetFlags(proxyVarSym), id(defs.varFlagSTATE_MASK)));
JCVariableDecl wasValidVar = Var(syms.booleanType,
defs.wasInvalid_LocalVarName,
EQ(BITAND(id(varState), phaseArg()), id(varState)));
addStmt(varState);
addStmt(wasValidVar);
if (hasInvalidators) {
//Abort if invalid
abortIfInvalidComponents();
// Insert invalidators.
for (BindeeInvalidator invalidator : invalidatees) {
addStmt(invalidator.invalidator);
}
//TODO: not generating the rest of invalidation is only going to work if all things with invalidators are shredded
// note the assymetry with sequence invalidators, which are not all shredded
return;
}
// Prepare to accumulate if statements.
beginBlock();
//Abort if invalid
abortIfInvalidComponents();
//addStmt(Debug("InvalidateGO "+proxyVarSym, phaseArg()));
boolean mixin = !isMixinClass() && varInfo instanceof MixinClassVarInfo;
boolean notifyDependents = false;
if (varInfo.isOverride() || varInfo instanceof SuperClassVarInfo) {
// Call super first.
callSuper();
} else if (mixin) {
callMixin((ClassSymbol)varSym.owner);
} else {
// Set the phase state part of the flag to the next state part of the phase transition
addStmt(SetNextVarFlagsStateFromPhaseTransition(proxyVarSym));
notifyDependents = !isLeaf(varInfo) || varInfo.hasDependents();
}
// Strip phase down to the non-BE form before propagating
addStmt(ClearBeFromPhaseTransition());
if (notifyDependents) {
// notifyDependents(VOFF$var, phase$);
addStmt(CallStmt(getReceiver(varInfo), defs.notifyDependents_VisageObjectMethodName, Offset(proxyVarSym), phaseArg()));
}
for (VarInfo otherVar : varInfo.boundBinders()) {
if (!otherVar.getSymbol().isVisageMember()) {
// force a set on non-visage members for now
addStmt(Stmt(Setter(otherVar.getSymbol(), Getter(proxyVarSym))));
} else if (!otherVar.generateSequenceAccessors()) {
// invalidate$var(phase$);
if (depGraphWriter != null) {
depGraphWriter.writeDependency(otherVar.sym, varSym);
}
addStmt(CallStmt(attributeInvalidateName(otherVar.getSymbol()), phaseArg()));
} else if (isBoundFunctionResult(varSym)) {
// This is bound function result variable. And the dependent is a sequence
// So, make sure we call the right sequence invalidate method with correct
// computed old and new sizes.
if (depGraphWriter != null) {
depGraphWriter.writeDependency(otherVar.sym, varSym);
}
JCVariableDecl oldPtrVar = TmpVar("old", syms.visage_PointerType, Get(varSym));
JCVariableDecl newPtrVar = TmpVar("new", syms.visage_PointerType, Getter(varSym));
JCVariableDecl oldSizeVar = TmpVar("oldSize", syms.intType,
If (NEnull(id(oldPtrVar)),
Call(id(oldPtrVar), defs.size_PointerMethodName),
Int(0)
));
JCVariableDecl newSizeVar = TmpVar("newSize", syms.intType,
If (NEnull(id(newPtrVar)),
Call(id(newPtrVar), defs.size_PointerMethodName),
Int(0)
));
Symbol otherSym = otherVar.getSymbol();
addStmt(
If(IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(otherSym)
),
/*Else (Trigger phase)*/
Block(
oldPtrVar,
newPtrVar,
oldSizeVar,
newSizeVar,
CallSeqTrigger(otherSym, Int(0), id(oldSizeVar), id(newSizeVar))
)
)
);
}
}
// Graph back to inverse.
if (depGraphWriter != null && varInfo.hasBoundDefinition() && varInfo.hasBiDiBoundDefinition()) {
for (VisageVarSymbol bindeeSym : varInfo.boundBindees()) {
depGraphWriter.writeDependency(bindeeSym, varSym);
break; // Only need the first entry (rest are duplicates)
}
}
if (varInfo.onInvalidate() != null) {
// if (phase$ == PHASE$INVALIDATE) { inline invalidation }
addStmt(OptIf(IsInvalidatePhase(), varInfo.onInvalidateAsInline()));
}
if (varInfo.onReplace() != null) {
// Begin the get$ block.
beginBlock();
// Call the get$var to force evaluation.
if (varInfo.onReplace() != null) {
addStmt(
If (FlagTest(proxyVarSym, defs.varFlagIS_EAGER, defs.varFlagIS_EAGER),
Block(
Stmt(Getter(proxyVarSym))
)
));
}
// if (phase$ == PHASE$TRIGGER) { get$var(); }
addStmt(OptIf(IsTriggerPhase(),
endBlock()));
}
// Wrap up main block.
JCBlock mainBlock = endBlock();
// Necessary to call mixin parent in else in case the var is a bare synth.
JCStatement mixinBlock = null;
if (mixin) {
beginBlock();
callMixin((ClassSymbol)varSym.owner);
mixinBlock = endBlock();
} else {
//mixinBlock = Block(Debug("InvalidateFail "+proxyVarSym, phaseArg()), Debug("..InvalidateFail "+proxyVarSym, id(varState)));
}
// if (!isValidValue$(VOFF$var)) { ... invalidate code ... }
addStmt(If(id(wasValidVar),
mainBlock, mixinBlock));
}
// phase non-FINAL
@Override
protected JCVariableDecl makeParam(Type varType, Name varName) {
return Var(Flags.PARAMETER, varType, varName, null);
}
};
clearDiagPos();
vamb.build();
}
//
// This method constructs the onreplace$ method for the specified attribute.
//
private void makeOnReplaceAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeOnReplaceName(varInfo.getSymbol()),
syms.voidType,
varInfo, bodyType) {
Name oldValueName;
Name newValueName;
@Override
public void initialize() {
VisageOnReplace onReplace = varInfo.onReplace();
oldValueName = paramOldValueName(onReplace);
newValueName = paramNewValueName(onReplace);
addParam(type, oldValueName);
addParam(type, newValueName);
buildIf(needOnReplaceAccessorMethod(varInfo) && (!varInfo.isOverride() || onReplace != null));
}
@Override
public void statements() {
// Call super first.
if (varInfo.isOverride()) {
callSuper();
}
// Mixin onreplace$
if (!isMixinClass() && varInfo instanceof MixinClassVarInfo) {
callMixin((ClassSymbol)varSym.owner);
}
generateSwitchDependences();
// Fetch the on replace statement or null.
JCStatement onReplace = varInfo.onReplaceAsInline();
// Need to capture init state if has trigger.
if (onReplace != null) {
// Insert the trigger.
addStmt(onReplace);
}
}
// This method generates VisageBase.switchDependence$ calls for all the
// bound select expressions that use the current var as the selector.
private void generateSwitchDependences() {
for (VarInfo dependent : varInfo.boundBinders()) {
JCExpression rcvr = getReceiverOrThis(varInfo.sym);
for (DependentPair depPair : dependent.boundBoundSelects()) {
// static variables and sequences are handled diffently
if (depPair.instanceSym != varInfo.sym ||
depPair.referencedSym.isStatic() ||
types.isSequence(depPair.referencedSym.type)) {
continue;
}
if (isMixinVar(depPair.referencedSym)) {
JCExpression oldOffset = If(EQnull(id(oldValueName)),
Int(0),
Offset(id(oldValueName), depPair.referencedSym));
JCExpression newOffset = If(EQnull(id(newValueName)),
Int(0),
Offset(id(newValueName), depPair.referencedSym));
addStmt(CallStmt(defs.VisageBase_switchDependence,
rcvr,
id(oldValueName), oldOffset,
id(newValueName), newOffset,
DepNum(getReceiver(depPair.instanceSym), depPair.instanceSym, depPair.referencedSym)));
} else {
JCVariableDecl offsetVar = TmpVar(syms.intType, Offset(depPair.referencedSym));
addStmt(offsetVar);
addStmt(CallStmt(defs.VisageBase_switchDependence,
rcvr,
id(oldValueName), id(offsetVar),
id(newValueName), id(offsetVar),
DepNum(getReceiver(depPair.instanceSym), depPair.instanceSym, depPair.referencedSym)));
}
}
}
}
};
vamb.build();
}
//-----------------------------------------------------------------------------------------------------------------------------
//
// Mixin var accessors.
//
//
// This method constructs a getMixin$ method.
//
private void makeGetMixinAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeGetMixinName(varInfo.getSymbol()),
varInfo.getRealType(),
varInfo, bodyType) {
@Override
public void statements() {
// Construct and add: return $var;
addStmt(Return(id(attributeValueName(proxyVarSym))));
}
};
vamb.build();
}
//
// This method constructs a getVOFF$ method.
//
private void makeGetVOFFAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeGetVOFFName(varInfo.getSymbol()),
syms.intType,
varInfo, bodyType) {
@Override
public void statements() {
addStmt(Return(id(attributeOffsetName(proxyVarSym))));
}
};
vamb.build();
}
//
// This method constructs a setMixin$ method.
//
private void makeSetMixinAccessorMethod(VarInfo varInfo, int bodyType) {
VarAccessorMethodBuilder vamb = new VarAccessorMethodBuilder(attributeSetMixinName(varInfo.getSymbol()),
varInfo.getRealType(),
varInfo, bodyType) {
@Override
public void initialize() {
addParam(type, defs.varNewValue_ArgName);
}
@Override
public void statements() {
// Construct and add: return $var;
addStmt(Return(m().Assign(id(attributeValueName(proxyVarSym)), id(defs.varNewValue_ArgName))));
}
};
vamb.build();
}
//-----------------------------------------------------------------------------------------------------------------------------
//
// This method constructs the accessor methods for an attribute.
//
public void makeAnAttributeAccessorMethods(VarInfo ai, int bodyType) {
if (!ai.useAccessors()) {
if (ai.useGetters() && !ai.isOverride()) {
makeGetterAccessorMethod(ai, bodyType);
}
} else {
if (!(ai instanceof MixinClassVarInfo)) {
if (ai.generateSequenceAccessors()) {
if (!ai.isOverride()) {
makeSeqGetterAccessorMethod(ai, bodyType);
makeSeqGetElementAccessorMethod(ai, bodyType);
makeSeqGetSizeAccessorMethod(ai, bodyType);
makeSeqInvalidateAccessorMethod(ai, bodyType);
makeSeqOnReplaceAccessorMethod(ai, bodyType);
} else if (bodyType != BODY_NONE) {
if (ai.hasInitializer()) {
// We only need to worry about computational methods
// The getter and be are generic.
makeSeqGetElementAccessorMethod(ai, bodyType);
makeSeqGetSizeAccessorMethod(ai, bodyType);
}
if (needOverrideInvalidateAccessorMethod(ai)) {
makeSeqInvalidateAccessorMethod(ai, bodyType);
}
if (ai.onReplace() != null || ai.isMixinVar()) {
makeSeqOnReplaceAccessorMethod(ai, bodyType);
}
}
} else {
if (!ai.isOverride()) {
makeGetterAccessorMethod(ai, bodyType);
if (ai.useSetters()) {
makeSetterAccessorMethod(ai, bodyType);
}
if (needInvalidateAccessorMethod(ai)) {
makeInvalidateAccessorMethod(ai, bodyType);
}
makeOnReplaceAccessorMethod(ai, bodyType);
} else if (bodyType != BODY_NONE) {
if (ai.hasInitializer()) {
// Bound or not, we need getter & setter on override since we
// may be switching between bound and non-bound or visa versa
makeGetterAccessorMethod(ai, bodyType);
makeSetterAccessorMethod(ai, bodyType);
}
if (needOverrideInvalidateAccessorMethod(ai)) {
makeInvalidateAccessorMethod(ai, bodyType);
}
if (ai.onReplace() != null || ai.isMixinVar()) {
makeOnReplaceAccessorMethod(ai, bodyType);
}
}
}
} else {
// Mixins in a normal class.
if (ai.needsCloning()) {
boolean hasInit = ai.hasInitializer() || ai.hasBoundDefinition();
bodyType = hasInit ? BODY_NORMAL : BODY_MIXIN;
if (ai.generateSequenceAccessors()) {
makeSeqGetterAccessorMethod(ai, BODY_MIXIN);
makeSeqGetElementAccessorMethod(ai, bodyType);
makeSeqGetSizeAccessorMethod(ai, bodyType);
makeSeqInvalidateAccessorMethod(ai, BODY_NORMAL);
makeSeqOnReplaceAccessorMethod(ai, BODY_NORMAL);
} else {
makeGetterAccessorMethod(ai, bodyType);
makeSetterAccessorMethod(ai, bodyType);
makeInvalidateAccessorMethod(ai, BODY_NORMAL);
makeOnReplaceAccessorMethod(ai, BODY_NORMAL);
}
}
}
}
}
//
// This method constructs mixin interfaces for the specified var.
//
public void makeAttributeMixinInterfaces(VarInfo ai, int bodyType) {
makeGetMixinAccessorMethod(ai, bodyType);
makeGetVOFFAccessorMethod(ai, bodyType);
makeSetMixinAccessorMethod(ai, bodyType);
}
//
// This method constructs the accessor methods for each attribute.
//
public void makeAttributeAccessorMethods(List<VarInfo> attrInfos) {
for (VarInfo ai : attrInfos) {
// Only create accessors for declared and proxied vars.
if (ai.needsCloning()) {
makeAnAttributeAccessorMethods(ai, BODY_NORMAL);
} else {
// If a super has binders we need to emit an overriding invalidate$.
if (ai.boundBinders().size() != 0) {
if (ai.generateSequenceAccessors())
makeSeqInvalidateAccessorMethod(ai, BODY_NORMAL);
else
makeInvalidateAccessorMethod(ai, BODY_NORMAL);
}
}
if (ai.needsMixinInterface()) {
makeAttributeMixinInterfaces(ai, BODY_NORMAL);
}
}
}
//
// This method constructs the abstract interfaces for the accessors in
// a mixin class.
//
public void makeMemberVariableAccessorInterfaceMethods(List<VarInfo> attrInfos) {
// Only for vars within the class.
for (VarInfo ai : attrInfos) {
if (ai.needsCloning()) {
makeAnAttributeAccessorMethods(ai, BODY_NONE);
if (isMixinClass()) {
makeAttributeMixinInterfaces(ai, BODY_NONE);
}
}
}
}
// Returns true if VCNT$ is needed.
public boolean needsVCNT$() {
boolean hasVars = (isScript() ? analysis.getScriptVarCount() : analysis.getClassVarCount()) != 0;
boolean hasJavaSuperClass = analysis.getVisageSuperClassSym() == null;
boolean hasMixins = !isScript() && !isMixinClass() && !analysis.getImmediateMixins().isEmpty();
return hasVars || hasJavaSuperClass || hasMixins || isMixinClass();
}
// Returns true if DCNT$ is needed.
public boolean needsDCNT$() {
HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> updateMap =
isScript() ? analysis.getScriptUpdateMap() : analysis.getClassUpdateMap();
List<VarInfo> varInfos = isScript() ? analysis.scriptVarInfos() : analysis.classVarInfos();
HashMap<Name, Integer> depMap = getDepMap(varInfos, updateMap);
boolean hasDeps = !getDepMap(varInfos, updateMap).isEmpty();
boolean hasJavaSuperClass = analysis.getVisageSuperClassSym() == null;
boolean hasMixins = !isScript() && !isMixinClass() && !analysis.getImmediateMixins().isEmpty();
return hasDeps || hasJavaSuperClass || hasMixins || isMixinClass();
}
// Returns true if FCNT$ is needed.
public boolean needsFCNT$() {
List<JCTree> invokeCases = getCurrentClassDecl().invokeCases(isScript());
boolean hasFuncs = !invokeCases.isEmpty();
boolean hasJavaSuperClass = analysis.getVisageSuperClassSym() == null;
boolean hasMixins = !isScript() && !isMixinClass() && !analysis.getImmediateMixins().isEmpty();
return hasFuncs || hasJavaSuperClass || hasMixins || isMixinClass();
}
//
// This method generates an enumeration for each of the class's instance attributes.
//
public void makeAttributeNumbers(List<VarInfo> attrInfos, int varCount) {
if (!needsVCNT$()) return;
// Construct a static count variable (VCNT$), -1 indicates count has not been initialized.
int initCount = analysis.isFirstTier() ? varCount : -1;
addDefinition(addSimpleIntVariable(Flags.STATIC | Flags.PRIVATE, defs.count_VisageObjectFieldName, initCount));
// Construct a static count accessor method (VCNT$)
makeVCNT$(attrInfos, varCount);
// Construct a virtual count accessor method (count$)
makecount$(varCount);
// Accumulate variable numbering.
for (VarInfo ai : attrInfos) {
// Only variables actually declared.
if (ai.hasEnumeration()) {
// Construct offset var.
Name name = attributeOffsetName(ai.getSymbol());
JCExpression init = analysis.isFirstTier() ? Int(ai.getEnumeration()) : null;
long flags = analysis.isFirstTier() && isLeaf(ai) ? (Flags.FINAL | Flags.STATIC | Flags.PUBLIC) :
(Flags.STATIC | Flags.PUBLIC);
// Construct and add: public static int VOFF$name = n;
addDefinition(makeField(flags, syms.intType, name, init));
}
}
}
//
// This method generates a flags field for each of the class's instance attributes.
//
public void makeAttributeFlags(List<VarInfo> attrInfos) {
// Define attribute flags.
for (VarInfo ai : attrInfos) {
// Only variables actually declared.
if (ai.hasEnumeration()) {
// Construct flags var.
Name name = attributeFlagsName(ai.getSymbol());
// Determine access flags.
long flags = attributeFieldAccessFlags(ai);
if (isScript() || isMixinClass()) flags |= Flags.STATIC;
boolean zero = false;
JCExpression init = null;
if (!ai.isOverride()) {
if (ai instanceof MixinClassVarInfo) {
init = updateVarBits(ai, Select(makeType(ai.getSymbol().owner.type, false), name));
if (init == null) {
// TODO - fix when overridden twice.
init = Select(makeType(ai.getSymbol().owner.type, false), name);
}
} else {
init = initialVarBits(ai);
zero = init == null;
}
} else if (isMixinClass()) {
// TODO - fix when overridden twice.
init = updateVarBits(ai, Select(makeType(ai.getSymbol().owner.type, false), name));
} else {
// done in init.
}
if (zero || init != null) {
// Construct and add: public static short VFLGS$name = n;
addDefinition(makeField(flags, syms.shortType, name, init));
}
}
}
}
//
// The method constructs the VCNT$ method for the current class.
//
public void makeVCNT$(final List<VarInfo> attrInfos, final int varCount) {
StaticMethodBuilder smb = new StaticMethodBuilder(defs.count_VisageObjectFieldName, syms.intType) {
@Override
public void statements() {
if (analysis.isFirstTier()) {
addStmt(Return(Int(varCount)));
} else {
// Start if block.
beginBlock();
// VCNT$ = super.VCNT$() + n or VCNT$ = n;
JCExpression setVCNT$Expr = superClassSym == null ? Int(varCount) :
PLUS(Call(makeType(superClassSym.type), defs.count_VisageObjectFieldName),
Int(varCount));
Name countName = names.fromString("$count");
// final int $count = VCNT$ = super.VCNT$() + n;
addStmt(makeField(Flags.FINAL, syms.intType, countName, m().Assign(id(defs.count_VisageObjectFieldName), setVCNT$Expr)));
for (VarInfo ai : attrInfos) {
// Only variables actually declared.
if (ai.hasEnumeration()) {
// Offset var name.
Name name = attributeOffsetName(ai.getSymbol());
// VCNT$ - n + i;
JCExpression setVOFF$Expr = PLUS(id(countName), Int(ai.getEnumeration() - varCount));
// VOFF$var = VCNT$ - n + i;
addStmt(Stmt(m().Assign(id(name), setVOFF$Expr)));
}
}
// VCNT$ == -1
JCExpression condition = EQ(id(defs.count_VisageObjectFieldName), Int(-1));
// if (VCNT$ == -1) { ...
addStmt(OptIf(condition,
endBlock()));
// return VCNT$;
addStmt(Return(id(defs.count_VisageObjectFieldName)));
}
}
};
clearDiagPos();
smb.build();
}
//
// The method constructs the count$ method for the current class.
//
public void makecount$(final int varCount) {
MethodBuilder smb = new MethodBuilder(defs.count_VisageObjectMethodName, syms.intType) {
@Override
public void statements() {
if (analysis.isFirstTier()) {
// Construct and add: return n;
addStmt(Return(Int(varCount)));
} else {
// Construct and add: return VCNT$();
addStmt(Return(Call(defs.count_VisageObjectFieldName)));
}
}
};
clearDiagPos();
smb.build();
}
//
// Clones a field declared in VisageBase as an non-static field. It also creates
// VisageObject accessor method.
//
private void cloneVisageBaseVar(VisageVarSymbol var, HashSet<String> excludes) {
// Var name as a string.
String str = var.name.toString();
String upperStr = str.substring(0, 1).toUpperCase() + str.substring(1);
// Var modifier flags.
long flags = var.flags();
// If it's an excluded name or a static then skip it.
if (excludes.contains(str) ||
(flags & (Flags.SYNTHETIC | Flags.STATIC)) != 0) {
return;
}
// Var Visage type.
Type type = var.asType();
// Clone the var.
addDefinition(Var(flags, type, names.fromString(str), null, var));
// Construct the getter.
ListBuffer<JCStatement> stmts = ListBuffer.lb();
boolean isBoolean = var.getTypeRepresentation() == VisageTypeRepresentation.TYPE_REPRESENTATION_BOOLEAN;
Name name = names.fromString((isBoolean ? "is" : "get") + upperStr);
stmts.append(Return(id(var)));
// public int getVar { return Var; }
MethodSymbol getMethSym = makeMethodSymbol(flags, type, name, List.<Type>nil());
JCMethodDecl getMeth = Method(flags, type, name, List.<JCVariableDecl>nil(), stmts.toList(), getMethSym);
// Add to definitions.
addDefinition(getMeth);
// Add to the exclusion set.
excludes.add(jcMethodDeclStr(getMeth));
// Construct the setter.
stmts = ListBuffer.lb();
name = names.fromString("set" + upperStr);
Name argName = names.fromString("value");
JCVariableDecl arg = Param(type, argName);
stmts.append(m().Exec(m().Assign(id(var), id(argName))));
// public void setVar(final int value) { Var = value; }
MethodSymbol setMethSym = makeMethodSymbol(flags, syms.voidType, name, List.<Type>of(type));
JCMethodDecl setMeth = Method(flags, syms.voidType, name, List.<JCVariableDecl>of(arg), stmts.toList(), setMethSym);
// Add to definitions.
addDefinition(setMeth);
// Add to the exclusion set.
excludes.add(jcMethodDeclStr(setMeth));
}
//
// Clones a method declared as an VisageObject interface to call the static
// equivalent in VisageBase.
//
private void cloneVisageBaseMethod(MethodSymbol method, HashSet<String> excludes) {
// Method modifier flags.
long flags = method.flags();
// If it's an excluded name or a static then skip it.
if (excludes.contains(method.toString()) ||
(flags & (Flags.SYNTHETIC | Flags.STATIC)) != 0) {
return;
}
// List of arguments to new method.
ListBuffer<JCVariableDecl> args = ListBuffer.lb();
// List of arguments to call supporting VisageBase method.
ListBuffer<JCExpression> callArgs = ListBuffer.lb();
// Add this to to the call.
callArgs.append(id(names._this));
// Add arguments to both lists.
for (VarSymbol argSym : method.getParameters()) {
args.append(Param(argSym.asType(), argSym.name));
callArgs.append(id(argSym));
}
// Buffer for statements.
ListBuffer<JCStatement> stmts = ListBuffer.lb();
// Method return type.
Type returnType = method.getReturnType();
// Basic call to supporting VisageBase method.
JCExpression visageBaseCall = Call(makeType(syms.visage_BaseType), method.name, callArgs);
// Exec or return based on return type.
if (returnType == syms.voidType) {
stmts.append(Stmt(visageBaseCall));
} else {
stmts.append(Return(visageBaseCall));
}
// public type meth$(t0 arg0, ...) { return VisageBase.meth$(this, arg0, ...); }
addDefinition(Method(Flags.PUBLIC, returnType, method.name, args.toList(), stmts.toList(), method));
}
//
// This method clones the contents of VisageBase and VisageObject when inheriting
// from a java class.
//
public void cloneVisageBase(HashSet<String> excludes) {
// Retrieve VisageBase and VisageObject.
ClassSymbol visageBaseSym = (ClassSymbol)syms.visage_BaseType.tsym;
ClassSymbol visageObjectSym = (ClassSymbol)syms.visage_ObjectType.tsym;
Entry e;
// Clone the vars in VisageBase.
for (e = visageBaseSym.members().elems; e != null && e.sym != null; e = e.sibling) {
if (e.sym instanceof VarSymbol) {
cloneVisageBaseVar((VisageVarSymbol)e.sym, excludes);
}
}
// Clone the interfaces in VisageObject.
for (e = visageObjectSym.members().elems; e != null && e.sym != null; e = e.sibling) {
if (e.sym instanceof MethodSymbol) {
cloneVisageBaseMethod((MethodSymbol)e.sym, excludes);
}
}
}
//-----------------------------------------------------------------------------------------------------------------------------
//
// VarNum method generation.
//
//
// This method coordinates the generation of instance level varnum methods.
//
public void makeVarNumMethods() {
final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> updateMap =
isScript() ? analysis.getScriptUpdateMap() : analysis.getClassUpdateMap();
final List<VarInfo> varInfos = isScript() ? analysis.scriptVarInfos() : analysis.classVarInfos();
final int varCount = isScript() ? analysis.getScriptVarCount() : analysis.getClassVarCount();
final List<JCTree> invokeCases = getCurrentClassDecl().invokeCases(isScript());
HashMap<Name, Integer> depMap = getDepMap(varInfos, updateMap);
final boolean useMixins = !isScript() && !isMixinClass();
List<ClassSymbol> mixinClasses = useMixins ? analysis.getImmediateMixins() : null;
boolean useConstants = analysis.isFirstTierNoMixins() || isMixinClass();
makeApplyDefaultsMethod(varInfos, varCount);
makeInvokeMethod(useConstants, invokeCases, mixinClasses);
makeInitVarsMethod(varInfos, updateMap);
makeDependencyNumbers(useConstants, depMap, mixinClasses);
makeFunctionNumbers(useConstants, invokeCases, mixinClasses);
if (useMixins) {
makeNeededMixinDCNT$(mixinClasses);
makeNeededMixinFCNT$(mixinClasses);
}
makeUpdateMethod(useConstants, varInfos, updateMap, depMap, mixinClasses);
if ((isScript() || !isMixinClass()) && varCount > 0) {
makeGetMethod(varInfos, varCount);
makeGetElementMethod(varInfos, varCount);
makeGetAsMethods(varInfos, varCount);
makeSizeMethod(varInfos, varCount);
makeSetMethod(varInfos, varCount);
makeSeqMethod(varInfos, varCount);
makeInvalidateMethod(varInfos, varCount);
makeVarChangeBitsMethod(varInfos, varCount);
}
}
//
// This method constructs the current class's applyDefaults$ method.
//
public void makeApplyDefaultsMethod(final List<VarInfo> attrInfos, final int count) {
MethodBuilder vcmb = new VarCaseMethodBuilder(defs.applyDefaults_VisageObjectMethodName, syms.voidType,
attrInfos, count) {
@Override
public void initialize() {
allowOverides = true;
}
@Override
public void statements() {
// Constrain the var.
if (varInfo.needsCloning() &&
!varInfo.isBareSynth() &&
!useSimpleInit(varInfo) &&
(!varInfo.isOverride() || varInfo.hasInitializer() || varInfo instanceof MixinClassVarInfo)) {
if (varInfo instanceof MixinClassVarInfo && !varInfo.hasInitializer()) {
// Call the appropriate mixin owner.
callMixin((ClassSymbol)varInfo.getSymbol().owner);
} else {
setDiagPos(varInfo.pos());
// Get body of applyDefaults$.
if (varInfo.generateSequenceAccessors()) {
addStmts(getSeqDefaultInitStatement(varInfo));
} else {
addStmts(getDefaultInitStatements(varInfo));
}
clearDiagPos();
}
if (!stmts.isEmpty()) {
addStmt(Return(null));
}
}
}
// Specialized body that wraps the case body.
@Override
public void body() {
// Start inner block.
beginBlock();
// Fill in body.
super.body();
// if (init ready)
JCExpression ifExpr = FlagTest(varNumArg(), defs.varFlagINIT_WITH_AWAIT_MASK, defs.varFlagINIT_READY);
// if (init ready) { body }
addStmt(OptIf(ifExpr, endBlock()));
}
};
vcmb.build();
}
//
// This method generates an count for the class's function values.
//
public void makeFunctionNumbers(final boolean useConstants, List<JCTree> invokeCases, List<ClassSymbol> mixinClasses) {
if (!needsFCNT$()) return;
// Construct a static count variable (FCNT$), -1 indicates function count has not been initialized.
int initCount = useConstants ? 0 : -1;
addDefinition(addSimpleIntVariable(Flags.STATIC | Flags.PRIVATE, defs.funcCount_VisageObjectFieldName, initCount));
// Mixin class base numbering.
if (mixinClasses != null) {
for (ClassSymbol classSym : mixinClasses) {
// Construct and add: public static int DEP$name;
addDefinition(makeField(Flags.STATIC | Flags.PUBLIC, syms.intType, classFCNT$Name(classSym), null));
}
}
// Construct a static count accessor method (FCNT$)
makeFCNT$(useConstants, invokeCases, mixinClasses);
}
//
// The method constructs the FCNT$ method for the current class.
//
public void makeFCNT$(final boolean useConstants, final List<JCTree> invokeCases, final List<ClassSymbol> mixinClasses) {
StaticMethodBuilder smb = new StaticMethodBuilder(defs.funcCount_VisageObjectFieldName, syms.intType) {
@Override
public void initialize() {
needsReceiver = false;
}
@Override
public void statements() {
// Number of function values in this class.
int funcCount = invokeCases.size();
if (useConstants) {
addStmt(Return(Int(funcCount)));
} else {
// Start if block.
beginBlock();
// Check if super is required.
boolean isFirstTier = analysis.isFirstTier() || superClassSym == null;
// Base for first function number.
JCExpression countExpr = isFirstTier ? Int(0) : Call(makeType(superClassSym.type), defs.funcCount_VisageObjectFieldName);
// Create base numbers for mixins
if (mixinClasses != null && !mixinClasses.isEmpty()) {
for (ClassSymbol classSym : mixinClasses) {
Name mixinName = classFCNT$Name(classSym);
addStmt(Stmt(m().Assign(id(mixinName), countExpr)));
countExpr = PLUS(id(mixinName),
Call(makeType(classSym.type, false), defs.funcCount_VisageObjectFieldName));
}
// last mixin count
} else {
// super class count
countExpr = isFirstTier ? Int(0) : Call(makeType(superClassSym.type), defs.funcCount_VisageObjectFieldName);
}
// Set this classes count.
Name countName = names.fromString("$count");
addStmt(makeField(Flags.FINAL, syms.intType, countName, m().Assign(id(defs.funcCount_VisageObjectFieldName), countExpr)));
// FCNT$ == -1
JCExpression condition = EQ(id(defs.funcCount_VisageObjectFieldName), Int(-1));
// if (FCNT$ == -1) { ...
addStmt(OptIf(condition,
endBlock()));
// return FCNT$ + funcCount;
addStmt(Return(PLUS(id(defs.funcCount_VisageObjectFieldName), Int(funcCount))));
}
}
};
smb.build();
}
//
// This method constructs an interface for a mixin's FCNT$.
//
public void makeMixinFCNT$(final ClassSymbol classSym, final boolean needsBody) {
MethodBuilder mb = new MethodBuilder(classFCNT$Name(classSym), syms.intType) {
@Override
public void initialize() {
bodyType = needsBody ? BODY_NORMAL : BODY_NONE;
needsReceiver = false;
}
@Override
public long rawFlags() {
return needsBody ? Flags.PUBLIC : (Flags.PUBLIC | Flags.ABSTRACT);
}
@Override
public void statements() {
addStmt(Return(id(classFCNT$Name(classSym))));
}
};
mb.build();
}
//
// This method generates all the mixin FCNT$ for the current class.
//
public void makeNeededMixinFCNT$(List<ClassSymbol> mixinClasses) {
for (ClassSymbol classSym : mixinClasses) {
makeMixinFCNT$(classSym, true);
}
}
//
// This method constructs the invoke method.
//
public void makeInvokeMethod(final boolean useConstants, final List<JCTree> invokeCases, final List<ClassSymbol> mixinClasses) {
MethodBuilder vcmb = new MethodBuilder(defs.invoke_VisageObjectMethodName, syms.objectType) {
@Override
public void initialize() {
addParam(numberArg());
addParam(argsFixedArg(0));
addParam(argsFixedArg(1));
addParam(argsArg());
}
@Override
public void statements() {
// Function number count.
int funcCount = invokeCases.size();
// Prepare to accumulate cases.
ListBuffer<JCCase> cases = ListBuffer.lb();
// Case number.
int tag = 0;
// Iterate thru each invoke case.
for (JCTree invoke : invokeCases) {
cases.append(m().Case(Int(tag), Stmts((JCBlock)invoke,
m().Break(null))));
tag++;
}
// Start default block.
beginBlock();
// Add mixins to the default chain.
if (mixinClasses != null) {
for (ClassSymbol classSym : mixinClasses) {
// Begin if block.
beginBlock();
// Call mixin update.
callMixin(classSym);
// if (depNum$ >= FCNT$mixn)
prependStmt(If(GE(numberArg(), id(classFCNT$Name(classSym))),
endBlock(),
null));
}
}
// Call the super version.
if (cases.nonEmpty()) {
// Add in super call.
callSuper();
}
// Default statements.
List<JCStatement> defaults = endBlockAsList();
if (cases.nonEmpty()) {
if (!defaults.isEmpty()) {
cases.append(m().Case(null, defaults));
}
JCExpression tagExpr = isMixinClass() && !isScript() ? MINUS(numberArg(), Call(classFCNT$Name(getCurrentOwner()))) :
useConstants ? numberArg() :
MINUS(numberArg(), id(defs.funcCount_VisageObjectFieldName));
// Construct and add: switch(FCNT$ + number) { ... }
addStmt(m().Switch(tagExpr, cases.toList()));
// Default returns null (for void);
addStmt(Return(Null()));
} else if (!defaults.isEmpty()) {
addStmts(defaults);
if (isMixinClass() || superClassSym == null) {
addStmt(Return(Null()));
} else {
callSuper();
}
} else {
buildIf(false);
}
}
};
vcmb.build();
}
//
// This method simplifies the bitor-ing of several flags.
//
private JCExpression bitOrFlags(JCExpression initial, Name... flags) {
JCExpression expr = initial;
for (Name flag : flags) {
if (expr == null) {
expr = id(flag);
} else {
expr = BITOR(expr, id(flag));
}
}
return expr;
}
//
// Return the initialize settings of a vars flags.
//
private JCExpression initialVarBits(VarInfo ai) {
boolean isBound = ai.hasBoundDefinition();
boolean isSynthetic = ai.isSynthetic();
boolean isReadonly = ai.isReadOnly();
boolean isEager = ai.onReplace() != null;
Name initialState = defs.varFlagSTATE_VALID;
JCExpression setBits = null;
if (useSimpleInit(ai)) {
setBits = bitOrFlags(setBits, defs.varFlagINIT_INITIALIZED_DEFAULT);
} else {
if (isSynthetic) {
setBits = bitOrFlags(setBits, defs.varFlagINIT_READY);
if (isBound) {
initialState = defs.varFlagSTATE_TRIGGERED;
}
} else if (ai.hasVarInit()) {
setBits = bitOrFlags(setBits, defs.varFlagINIT_AWAIT_VARINIT);
}
if (isBound) {
setBits = bitOrFlags(setBits, defs.varFlagIS_BOUND);
}
}
setBits = bitOrFlags(setBits, initialState);
if (ai.generateSequenceAccessors() && !isBound && (ai.hasInitializer() || (ai.isDirectOwner() && !ai.isOverride()))) {
// Non-bound sequences are immediately live
setBits = bitOrFlags(setBits, defs.varFlagSEQUENCE_LIVE);
}
// Read only is normally marked after the default is set (set once).
if (isReadonly && (isBound ||
(!ai.hasInitializer() && !(ai instanceof MixinClassVarInfo)) ||
!ai.useAccessors())) {
setBits = bitOrFlags(setBits, defs.varFlagIS_READONLY);
}
if (isEager) {
setBits = bitOrFlags(setBits, defs.varFlagIS_EAGER);
}
return setBits;
}
//
// This method generates code to update overridden var flags.
//
public JCExpression updateVarBits(VarInfo ai, JCExpression oldFlags) {
JCExpression setBits = initialVarBits(ai);
JCExpression clearBits = oldFlags;
if (ai.hasBoundDefinition() || ai.hasInitializer()) {
clearBits = BITAND(oldFlags, id(defs.varFlagIS_EAGER));
}
if (setBits != null) {
setBits = BITOR(clearBits, setBits);
} else if (clearBits != oldFlags) {
setBits = clearBits;
}
return setBits == null ? null : flagCast(setBits);
}
//
// This method sets up the initial var state.
//
private void makeInitVarsMethod(final List<VarInfo> attrInfos,
final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> updateMap) {
MethodBuilder mb = new MethodBuilder(defs.initVars_VisageObjectMethodName, syms.voidType) {
@Override
public void statements() {
// Begin collecting statements.
beginBlock();
// Include mixins.
callMixins();
// Add "this" and "script access" dependencies.
for (VisageVarSymbol instanceVar : updateMap.keySet()) {
HashMap<VisageVarSymbol, HashSet<VarInfo>> instanceMap = updateMap.get(instanceVar);
for (VisageVarSymbol referenceVar : instanceMap.keySet()) {
if (instanceVar.isSpecial()) {
// Dependent on var referenced via "this"
addFixedDependent(instanceVar, referenceVar);
} else if (referenceVar.isStatic()) {
// Dependent on a script-level var, reference via the script-level var
VisageClassSymbol classSym = (VisageClassSymbol) referenceVar.owner;
VisageVarSymbol scriptAccess = visagemake.ScriptAccessSymbol(classSym);
addFixedDependent(scriptAccess, referenceVar);
}
}
}
if (isBoundFuncClass) {
/*
* For each bound function param (VisageObject+varNum pair), at the
* end of object creation register "this" as a dependent by
* calling addDependent$ method:
*
* boundFuncObjParam1.addDependent$(boundFunctionVarNumParam1, this);
* boundFuncObjParam2.addDependent$(boundFunctionVarNumParam2, this);
* ....
*/
for (VarInfo vi : attrInfos) {
if (vi.isParameter()) {
// call VisageObject.addDependent$(int varNum, VisageObject dep)
Symbol varSym = vi.getSymbol();
addStmt(CallStmt(
id(boundFunctionObjectParamName(varSym.name)),
defs.VisageBase_addDependent.methodName,
id(boundFunctionVarNumParamName(varSym.name)),
getReceiverOrThis(),
DepNum(null, null, varSym)));
}
}
}
ListBuffer<JCStatement> inits = endBlockAsBuffer();
// Emit super vars first
callSuper();
// Mixins and current class next.
addStmts(inits);
// Emit method only if there was anything beyond the super call.
buildIf(!inits.isEmpty());
}
private void addFixedDependent(VisageVarSymbol instanceVar, VisageVarSymbol referenceVar) {
addStmt(CallStmt(defs.VisageBase_addDependent,
Get(instanceVar),
Offset(Get(instanceVar), referenceVar),
getReceiverOrThis(),
DepNum(null, instanceVar, referenceVar)));
}
};
mb.build();
}
//
// This method generates a map for the class's dependency numbers.
//
public HashMap<Name, Integer> getDepMap(List<VarInfo> attrInfos,
final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> updateMap) {
// Set up dependency map.
HashMap<Name, Integer> depMap = new HashMap<Name, Integer>();
int depCount = 0;
for (VarInfo vi : attrInfos) {
if (vi.isInitWithBoundFuncResult()) {
VisageVarSymbol varSym = vi.getSymbol();
Symbol initSym = vi.boundFuncResultInitSym();
Name depName = depName(null, initSym);
if (!depMap.containsKey(depName)) {
depMap.put(depName, new Integer(depCount));
depCount++;
}
}
}
if (isBoundFuncClass) {
MethodSymbol msym = (MethodSymbol) getCurrentClassSymbol().owner;
List<VarSymbol> params = msym.params();
for (VarSymbol mParam : params) {
Scope.Entry e = getCurrentClassSymbol().members().lookup(mParam.name);
if (e.sym.kind == Kinds.VAR) {
VisageVarSymbol param = (VisageVarSymbol) e.sym;
Name depName = depName(null, param);
if (!depMap.containsKey(depName)) {
depMap.put(depName, new Integer(depCount));
depCount++;
}
}
}
}
for (VisageVarSymbol instanceVar : updateMap.keySet()) {
HashMap<VisageVarSymbol, HashSet<VarInfo>> instanceMap = updateMap.get(instanceVar);
for (VisageVarSymbol referenceVar : instanceMap.keySet()) {
Name depName = depName(instanceVar, referenceVar);
if (!depMap.containsKey(depName)) {
depMap.put(depName, new Integer(depCount));
depCount++;
}
}
}
return depMap;
}
//
// This method generates an enumeration for each of the class's dependencies.
//
public void makeDependencyNumbers(final boolean useConstants, final HashMap<Name, Integer> depMap, List<ClassSymbol> mixinClasses) {
if (!needsDCNT$()) return;
// Construct a static count variable (DCNT$), -1 indicates dep count has not been initialized.
int initCount = useConstants ? depMap.size() : -1;
addDefinition(addSimpleIntVariable(Flags.STATIC | Flags.PRIVATE, defs.depCount_VisageObjectFieldName, initCount));
// Mixin class base numbering.
if (mixinClasses != null) {
for (ClassSymbol classSym : mixinClasses) {
// Construct and add: public static int DEP$name;
addDefinition(makeField(Flags.STATIC | Flags.PUBLIC, syms.intType, classDCNT$Name(classSym), null));
}
}
// Accumulate dependency numbering.
for (Name depName : depMap.keySet()) {
int num = depMap.get(depName).intValue();
JCExpression init = useConstants ? Int(num) : null;
long flags = useConstants ? (Flags.FINAL | Flags.STATIC | Flags.PUBLIC) :
(Flags.STATIC | Flags.PUBLIC);
// Construct and add: public static int DEP$name = n;
addDefinition(makeField(flags, syms.intType, depName, init));
}
// Construct a static count accessor method (DCNT$)
makeDCNT$(useConstants, depMap, mixinClasses);
}
//
// The method constructs the DCNT$ method for the current class.
//
public void makeDCNT$(final boolean useConstants, final HashMap<Name, Integer> depMap, final List<ClassSymbol> mixinClasses) {
StaticMethodBuilder smb = new StaticMethodBuilder(defs.depCount_VisageObjectFieldName, syms.intType) {
@Override
public void initialize() {
needsReceiver = false;
}
@Override
public void statements() {
// Number fo dependencies in this class.
int depCount = depMap.size();
if (useConstants) {
addStmt(Return(Int(depCount)));
} else {
// Start if block.
beginBlock();
// Check if super is required.
boolean isFirstTier = analysis.isFirstTier() || superClassSym == null;
// Base for first dependency number.
JCExpression countExpr = isFirstTier ? Int(0) : Call(makeType(superClassSym.type), defs.depCount_VisageObjectFieldName);
// Create base counts for mixins
if (mixinClasses != null && !mixinClasses.isEmpty()) {
for (ClassSymbol classSym : mixinClasses) {
Name mixinName = classDCNT$Name(classSym);
addStmt(Stmt(m().Assign(id(mixinName), countExpr)));
countExpr = PLUS(id(mixinName),
Call(makeType(classSym.type, false), defs.depCount_VisageObjectFieldName));
}
// last mixin + depCount
countExpr = PLUS(countExpr, Int(depCount));
} else {
// super class + depCount
countExpr = isFirstTier ? Int(depCount) :
PLUS(Call(makeType(superClassSym.type), defs.depCount_VisageObjectFieldName),
Int(depCount));
}
// Set this classes count.
Name countName = names.fromString("$count");
addStmt(makeField(Flags.FINAL, syms.intType, countName, m().Assign(id(defs.depCount_VisageObjectFieldName), countExpr)));
// Accumulate dependency numbering.
for (Name depName : depMap.keySet()) {
int num = depMap.get(depName).intValue();
// DCNT$ - n + i;
JCExpression setDEP$Expr = PLUS(id(countName), Int(num - depCount));
// DEP$ = DCNT$ - n + i;
addStmt(Stmt(m().Assign(id(depName), setDEP$Expr)));
}
// DCNT$ == -1
JCExpression condition = EQ(id(defs.depCount_VisageObjectFieldName), Int(-1));
// if (DCNT$ == -1) { ...
addStmt(OptIf(condition,
endBlock()));
// return DCNT$;
addStmt(Return(id(defs.depCount_VisageObjectFieldName)));
}
}
};
smb.build();
}
//
// This method constructs an interface for a mixin's DCNT$.
//
public void makeMixinDCNT$(final ClassSymbol classSym, final boolean needsBody) {
MethodBuilder mb = new MethodBuilder(classDCNT$Name(classSym), syms.intType) {
@Override
public void initialize() {
bodyType = needsBody ? BODY_NORMAL : BODY_NONE;
needsReceiver = false;
}
@Override
public long rawFlags() {
return needsBody ? Flags.PUBLIC : (Flags.PUBLIC | Flags.ABSTRACT);
}
@Override
public void statements() {
addStmt(Return(id(classDCNT$Name(classSym))));
}
};
mb.build();
}
//
// This method generates all the mixin DCNT$ for the current class.
//
public void makeNeededMixinDCNT$(List<ClassSymbol> mixinClasses) {
for (ClassSymbol classSym : mixinClasses) {
makeMixinDCNT$(classSym, true);
}
}
//
// This method constructs the current class's update$ method.
//
public void makeUpdateMethod(final boolean useConstants,
final List<VarInfo> varInfos,
final HashMap<VisageVarSymbol, HashMap<VisageVarSymbol, HashSet<VarInfo>>> updateMap,
final HashMap<Name, Integer> depMap,
final List<ClassSymbol> mixinClasses) {
MethodBuilder mb = new MethodBuilder(defs.update_VisageObjectMethodName, syms.booleanType) {
// Number fo dependencies in this class.
int depCount = depMap.size();
// Condition expression for each dependent.
JCExpression[] depCondExpr;
// Statement lists for each dependent.
ListBuffer<JCStatement>[] depStats;
@Override
public void initialize() {
addParam(updateInstanceArg());
addParam(depNumArg());
addParam(startPosArg());
addParam(endPosArg());
addParam(newLengthArg());
addParam(phaseArg());
// Construct buffers for each dependency.
depCondExpr = new JCExpression[depCount];
depStats = new ListBuffer[depCount];
for (int i = 0; i < depCount; i++) {
depStats[i] = ListBuffer.lb();
}
}
// This method adds a clause to the list of statements for a dependency.
public void addDepClause(Symbol selector, Symbol sym, JCExpression objCond, JCStatement invalStmt) {
Name depName = depName(selector, sym);
Integer enumeration = depMap.get(depName);
assert enumeration != null : "Missing dependency.";
int i = enumeration.intValue();
depCondExpr[i] = objCond;
depStats[i].append(invalStmt);
}
// This method constructs an invalidate statement for the given var.
JCStatement invalidate(boolean isSequence, VisageVarSymbol vsym) {
if (isSequence) {
// Sequence: update$ is only used on select, so, for sequences, we can just pass through
return CallStmt(attributeInvalidateName(vsym),
startPosArg(),
endPosArg(),
newLengthArg(),
phaseArg());
} else {
// Non-sequence
return CallStmt(attributeInvalidateName(vsym), phaseArg());
}
}
// This method adds clauses for bound function results.
public void addBoundFuncResultClauses() {
//
// If the current class has bound function call expressions in bind call sites,
// then we would have introduced Pointer synthetic instance vars to store
// bound call result. We need to check if the update$ call is from the Pointer
// value change. If so, invalidate appropriate bound function result cache var.
// Note that the bound function call may not yet have been called - only after
// first call, the Pointer synthetic var has non-null value. So we need to check
// check null Pointer value.
//
for (VarInfo vi : varInfos) {
JCStatement ifReferenceStmt = null;
VisageVarSymbol varSym = vi.getSymbol();
if (vi.isInitWithBoundFuncResult() && varSym.isVisageMember()) {
Symbol initSym = vi.boundFuncResultInitSym();
Name ptrVarName = attributeValueName(initSym);
//
// For each "foo" field that stores result of a bound function call expression,
// we generate pointer dependency update check as follows:
//
// if (instance$ == $$$bound$result$foo.getVisageObject()) {
// invalidate$foo(phase$);
// }
//
// instance$ == ptrVar.getVisageObject()
JCExpression objCond = EQ(updateInstanceArg(), Call(id(ptrVarName), defs.getVisageObject_PointerMethodName));
// invalidate$foo(phase$);
JCStatement invalStat = invalidate(types.isSequence(varSym.type), varSym);
// Add statement to dependency.
addDepClause(null, initSym, objCond, invalStat);
}
}
}
// This method adds clauses for bound function parameters.
public void addBoundFuncParamClauses() {
//
// For bound functions, we generate a local class. If the current
// class is such a class, we need to invalidate the synthetic
// bound function param instance fields from the input VisageObject
// and varNum pairs.
//
if (isBoundFuncClass) {
MethodSymbol msym = (MethodSymbol) getCurrentClassSymbol().owner;
List<VarSymbol> params = msym.params();
//
// For each bound function param "foo", we generate
//
// if (varNum$ == $$boundVarNum$foo && instance$ == $$boundInstance$foo) {
// invalidate$local_klass2$foo(phase$);
// // or sequence version of invalidate..
// }
//
for (VarSymbol mParam : params) {
Scope.Entry e = getCurrentClassSymbol().members().lookup(mParam.name);
VisageVarSymbol param = (VisageVarSymbol) e.sym;
if (e.sym.kind == Kinds.VAR && param.isVisageMember()) {
// instance$ == $$boundInstance$foo
JCExpression objCond = EQ(updateInstanceArg(), id(boundFunctionObjectParamName(param.name)));
// invalidate$local_klass2$foo(phase$);
JCStatement invalStat = invalidate(types.isSequence(param.type), param);
// Add statement to dependency.
addDepClause(null, param, objCond, invalStat);
}
}
}
}
// This method adds clauses for inter-instance dependencies.
public void addInstanceClauses() {
// Loop for instance symbol.
for (VisageVarSymbol instanceVar : updateMap.keySet()) {
VisageVarSymbol scriptAccess = null;
HashMap<VisageVarSymbol, HashSet<VarInfo>> instanceMap = updateMap.get(instanceVar);
// Loop for reference symbol.
JCStatement ifReferenceStmt = null;
for (VisageVarSymbol referenceVar : instanceMap.keySet()) {
HashSet<VarInfo> referenceSet = instanceMap.get(referenceVar);
// Loop for local vars.
for (VarInfo varInfo : referenceSet) {
if (depGraphWriter != null) {
depGraphWriter.writeInterObjectDependency(instanceVar, referenceVar);
}
if (referenceVar.isStatic() && !instanceVar.isSpecial()) {
VisageClassSymbol classSym = (VisageClassSymbol)referenceVar.owner;
scriptAccess = visagemake.ScriptAccessSymbol(classSym);
}
// instance == selector
JCExpression objCond = EQ(updateInstanceArg(), Get(scriptAccess != null ? scriptAccess : instanceVar));
JCStatement invalStat;
if (varInfo.getSymbol().isVisageMember()) {
// invalidate$var(phase$);
invalStat = invalidate(varInfo.generateSequenceAccessors(), varInfo.proxyVarSym());
} else {
// todo - do something useful here
invalStat = Stmt(False());
}
// Add statement to dependency.
addDepClause(instanceVar, referenceVar, objCond, invalStat);
}
}
}
}
@Override
public void statements() {
// Gather clauses for invalidation.
addBoundFuncResultClauses();
addBoundFuncParamClauses();
addInstanceClauses();
// Construct the switch.
ListBuffer<JCCase> cases = ListBuffer.lb();
for (Name depName : depMap.keySet()) {
Integer enumeration = depMap.get(depName);
int i = enumeration.intValue();
JCStatement caseBody = If(depCondExpr[i],
Block(depStats[i].toList(),
Return(True())),
// else
null);
JCExpression tag = Int(useConstants ? i : i - depCount);
cases.append(m().Case(tag, List.<JCStatement>of(caseBody, m().Break(null))));
}
// Prepare to accumulate default.
beginBlock();
// Add mixins to the default chain.
if (mixinClasses != null) {
for (ClassSymbol classSym : mixinClasses) {
// Begin if block.
beginBlock();
// Call mixin update.
callMixin(classSym);
// if (depNum$ >= DCNT$mixn)
prependStmt(If(GE(depNumArg(), id(classDCNT$Name(classSym))),
endBlock(),
null));
}
}
if (cases.nonEmpty()) {
// Add in super call.
callSuper();
}
// Default statements.
List<JCStatement> defaults = endBlockAsList();
if (cases.nonEmpty()) {
if (!defaults.isEmpty()) {
cases.append(m().Case(null, defaults));
}
JCExpression tagExpr = isMixinClass() && !isScript() ? MINUS(depNumArg(), Call(classDCNT$Name(getCurrentOwner()))) :
useConstants ? depNumArg() :
MINUS(depNumArg(), id(defs.depCount_VisageObjectFieldName));
// Construct and add: switch(depNum - DCNT$) { ... }
addStmt(m().Switch(tagExpr, cases.toList()));
addStmt(Return(False()));
} else if (!defaults.isEmpty()) {
addStmts(defaults);
if (isMixinClass() || superClassSym == null) {
addStmt(Return(False()));
} else {
callSuper();
}
} else {
buildIf(false);
}
}
};
mb.build();
}
//
// This method constructs the current class's get$ method.
//
public void makeGetMethod(List<VarInfo> attrInfos, int varCount) {
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.get_VisageObjectMethodName, syms.objectType,
attrInfos, varCount) {
@Override
public void statements() {
if (!varInfo.isOverride()) {
clearDiagPos();
// get$var()
JCExpression getterExp = Getter(varSym);
// return get$var()
addStmt(Return(getterExp));
}
}
};
vcmb.build();
}
//
// This method constructs the current class's elem$(varnum, pos) method.
//
public void makeGetElementMethod(List<VarInfo> attrInfos, int varCount) {
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.getElement_VisageObjectMethodName, syms.objectType,
attrInfos, varCount) {
@Override
public void initialize() {
addParam(posArg());
}
@Override
public void statements() {
if (varInfo.useAccessors() && !varInfo.isOverride()) {
if (varInfo.generateSequenceAccessors()) {
// return elem$var(pos$)
addStmt(Return(Call(attributeGetElementName(varSym), posArg())));
}
}
}
};
vcmb.build();
}
//
// This method constructs the current class's getAsXXX$ methods.
//
public void makeGetAsMethods(List<VarInfo> attrInfos, final int varCount) {
int typeRepCnt = VisageTypeRepresentation.values().length;
ListBuffer<VarInfo>[] seqVars = new ListBuffer[typeRepCnt];
boolean hasSequences = false;
// Iterate thru each var.
for (VarInfo varInfo : attrInfos) {
// Constrain the var.
if (varInfo.needsEnumeration() &&
!varInfo.isBareSynth() &&
varInfo.useAccessors() &&
varInfo.generateSequenceAccessors()) {
// Element type of sequence.
Type elemType = varInfo.getElementType();
// Type representation of sequence element type.
VisageTypeRepresentation typeRep = types.typeRep(elemType);
// Only care about primitive types.
if (typeRep.isPrimitive()) {
// Ordinal is used as index.
int ordinal = typeRep.ordinal();
// If this type of sequence has not been encountered before.
if (seqVars[ordinal] == null) {
// Init the list buffer for this type.
seqVars[ordinal] = ListBuffer.lb();
// Worth the effort to iterate thru the list later.
hasSequences = true;
}
// Add var to list to include in method generation.
seqVars[ordinal].append(varInfo);
}
}
}
// Worth the effort.
if (hasSequences) {
// For each element type.
for (VisageTypeRepresentation typeRep : VisageTypeRepresentation.values()) {
// Ordinal is used as index.
int ordinal = typeRep.ordinal();
// Only include method if a sequence of that type exists.
if (seqVars[ordinal] != null) {
// All vars of that element type.
List<VarInfo> seqVarInfos = seqVars[ordinal].toList();
// Use the first as a sample to get the type.
final Type elemType = seqVarInfos.get(0).getElementType();
// Can use case method with a subset of vars.
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.getAs_VisageObjectMethodName[ordinal], elemType,
seqVarInfos, varCount) {
@Override
public void initialize() {
addParam(posArg());
}
@Override
public void statements() {
// The vars have already been filtered.
addStmt(Return(Call(attributeGetElementName(varSym), posArg())));
}
};
// Build the method.
vcmb.build();
}
}
}
}
//
// This method constructs the current class's size$(varnum) method.
//
public void makeSizeMethod(List<VarInfo> attrInfos, int varCount) {
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.size_VisageObjectMethodName, syms.intType,
attrInfos, varCount) {
@Override
public void statements() {
if (varInfo.useAccessors() && !varInfo.isOverride()) {
if (varInfo.generateSequenceAccessors()) {
// return size$var()
addStmt(Return(Call(attributeSizeName(varSym))));
}
}
}
};
vcmb.build();
}
//
// This method constructs the current class's set$ method.
//
public void makeSetMethod(List<VarInfo> attrInfos, int varCount) {
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.set_AttributeMethodPrefixName, syms.voidType,
attrInfos, varCount) {
@Override
public void initialize() {
addParam(objArg());
}
@Override
public void statements() {
if (varInfo.useAccessors() && !varInfo.isDef() && !varInfo.isOverride() && !varInfo.isBareSynth()) {
// (type)object$
JCExpression objCast = typeCast(varInfo.getRealType(), syms.objectType, objArg());
if (varInfo.generateSequenceAccessors()) {
addStmt(CallStmt(defs.Sequences_set, id(names._this), Offset(varSym), objCast));
} else {
// set$var((type)object$)
addStmt(SetterStmt(varSym, objCast));
}
// return
addStmt(Return(null));
}
}
};
vcmb.build();
}
//
// This method constructs the current class's seq$ method.
//
public void makeSeqMethod(List<VarInfo> attrInfos, int varCount) {
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.seq_AttributeMethodPrefixName, syms.voidType,
attrInfos, varCount) {
@Override
public void initialize() {
addParam(objArg());
}
@Override
public void statements() {
if (varInfo.useAccessors() && !varInfo.isOverride() && !varInfo.isBareSynth() && varInfo.generateSequenceAccessors()) {
// (type)object$
JCExpression objCast = typeCast(varInfo.getRealType(), syms.objectType, objArg());
// $var = value
addStmt(SetStmt(proxyVarSym, objCast));
// return
addStmt(Return(null));
}
}
};
vcmb.build();
}
//
// This method constructs the current class's invalidate$(varnum, ...) method.
//
public void makeInvalidateMethod(List<VarInfo> attrInfos, int varCount) {
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.invalidate_VisageObjectMethodName, syms.voidType,
attrInfos, varCount) {
@Override
public void initialize() {
addParam(startPosArg());
addParam(endPosArg());
addParam(newLengthArg());
addParam(phaseArg());
}
@Override
public void statements() {
if (varInfo.useAccessors() && !varInfo.isOverride() && needInvalidateAccessorMethod(varInfo)) {
if (varInfo.generateSequenceAccessors()) {
addStmt(CallStmt(attributeInvalidateName(varSym),
startPosArg(), endPosArg(), newLengthArg(), phaseArg()));
} else {
addStmt(CallStmt(attributeInvalidateName(varSym), phaseArg()));
}
addStmt(Return(null));
}
}
};
vcmb.build();
}
//
// This method constructs the current class's varChangeBits$(varnum, ...) method.
//
public void makeVarChangeBitsMethod(List<VarInfo> attrInfos, int varCount) {
VarCaseMethodBuilder vcmb = new VarCaseMethodBuilder(defs.varFlagActionChange, syms.intType,
attrInfos, varCount) {
@Override
public void initialize() {
addParam(clearBitsArg());
addParam(setBitsArg());
}
@Override
public void statements() {
if (varInfo.needsCloning()) {
JCExpression clearBits = BITAND(VarFlags(varSym), BITNOT(clearBitsArg()));
JCExpression setBits = BITOR(clearBits, setBitsArg());
JCExpression assignBits = m().Assign(VarFlags(varSym), flagCast(setBits));
addStmt(Return(assignBits));
}
}
};
vcmb.build();
}
//
// This method constructs the initializer for a var map.
//
public JCExpression makeInitVarMapExpression(ClassSymbol cSym, LiteralInitVarMap varMap) {
// Build up the argument list for the call.
ListBuffer<JCExpression> args = ListBuffer.lb();
// X.VCNT$()
args.append(Call(makeType(cSym.type), defs.count_VisageObjectFieldName));
// For each var declared in order (to make the switch tags align to the vars.)
for (VisageVarSymbol vSym : varMap.varList.toList()) {
// ..., X.VOFF$x, ...
args.append(Select(makeType(cSym.type), attributeOffsetName(vSym)));
}
// VisageBase.makeInitMap$(X.VCNT$(), X.VOFF$a, ...)
return Call(defs.VisageBase_makeInitMap, args);
}
//
// This method constructs a single var map declaration.
//
private JCVariableDecl makeInitVarMapDecl(ClassSymbol cSym, LiteralInitVarMap varMap) {
// Fetch name of map.
Name mapName = varMapName(cSym);
// static short[] Map$X;
return makeField(Flags.STATIC, syms.visage_ShortArray, mapName, null);
}
//
// This method constructs a single var map initial value.
//
public JCStatement makeInitVarMapInit(LiteralInitVarMap varMap) {
// Get current class symbol.
ClassSymbol cSym = getCurrentClassSymbol();
// Fetch name of map.
Name mapName = varMapName(cSym);
// Map$X = VisageBase.makeInitMap$(X.VCNT$(), X.VOFF$a, ...);
return Stmt(m().Assign(id(mapName), makeInitVarMapExpression(cSym, varMap)));
}
//
// This method constructs declarations for var maps used by literal initializers.
//
public void makeInitClassMaps(LiteralInitClassMap initClassMap) {
// For each class initialized in the current class.
for (ClassSymbol cSym : initClassMap.classMap.keySet()) {
// Get the var map for the referencing class.
LiteralInitVarMap varMap = initClassMap.classMap.get(cSym);
// Add to var.
addDefinition(makeInitVarMapDecl(cSym, varMap));
// Fetch name of map.
Name mapName = varMapName(cSym);
// Prepare to accumulate statements.
ListBuffer<JCStatement> stmts = ListBuffer.lb();
if (isAnonClass(cSym)) {
// Construct and add: return MAP$X;
stmts.append(Return(id(mapName)));
} else {
// MAP$X == null
JCExpression condition = EQnull(id(mapName));
// MAP$X = VisageBase.makeInitMap$(X.VCNT$, X.VOFF$a, ...)
JCExpression assignExpr = m().Assign(id(mapName), makeInitVarMapExpression(cSym, varMap));
// Construct and add: return MAP$X == null ? (MAP$X = VisageBase.makeInitMap$(X.VCNT$, X.VOFF$a, ...)) : MAP$X;
stmts.append(
Return(
If (condition,
assignExpr,
id(mapName))));
}
// Construct the method symbol.
MethodSymbol methSym = makeMethodSymbol(Flags.PUBLIC | Flags.STATIC,
syms.visage_ShortArray,
varGetMapName(cSym),
List.<Type>nil());
// Construct lazy accessor method.
JCMethodDecl method = Method(Flags.PUBLIC | Flags.STATIC,
syms.visage_ShortArray,
varGetMapName(cSym),
List.<JCVariableDecl>nil(),
stmts.toList(),
methSym);
// Add method.
addDefinition(method);
}
}
private JCExpression defaultValue(VarInfo varInfo) {
return makeDefaultValue(varInfo.pos(), varInfo.getSymbol());
}
//
// This method constructs a super call with appropriate arguments.
//
private JCStatement makeSuperCall(ClassSymbol cSym, Name name, JCExpression... args) {
ListBuffer<JCExpression> buffer = ListBuffer.lb();
for (JCExpression arg : args) {
buffer.append(arg);
}
return makeSuperCall(cSym, name, buffer.toList());
}
private JCStatement makeSuperCall(ClassSymbol cSym, Name name, List<JCExpression> args) {
// If this is from a mixin class then we need to use receiver$ otherwise this.
boolean fromMixinClass = isMixinClass();
// If this is to a mixin class then we need to use receiver$ otherwise this.
boolean toMixinClass = VisageAnalyzeClass.isMixinClass(cSym);
// If this class doesn't have a visage super then punt to VisageBase.
boolean toVisageBase = cSym == null;
// Add in the receiver if necessary.
if (toMixinClass || toVisageBase) {
// Determine the receiver name.
Name receiver = fromMixinClass ? defs.receiverName : names._this;
args.prepend(id(receiver));
}
// Determine the selector.
JCExpression selector;
if (toMixinClass) {
selector = makeType(cSym.type, false);
} else if (toVisageBase) {
selector = makeType(syms.visage_BaseType, false);
} else {
selector = id(names._super);
}
// Construct the call.
return CallStmt(selector, name, args);
}
//
// Construct the static block for setting defaults
//
public void makeInitStaticAttributesBlock(ClassSymbol sym, boolean isScriptLevel, boolean isLibrary, List<VarInfo> attrInfo, JCStatement initMap) {
// Buffer for init statements.
ListBuffer<JCStatement> stmts = ListBuffer.lb();
// Initialize the var map for anon class.
if (initMap != null) {
stmts.append(initMap);
}
Symbol scriptLevelAccessSym = visagemake.ScriptAccessSymbol(sym);
if (isScriptLevel) {
stmts.append(Stmt(m().Assign(id(scriptLevelAccessSym),
m().NewClass(null, null, id(scriptName), List.<JCExpression>of(False()), null))));
stmts.append(CallStmt(id(scriptLevelAccessSym), defs.initialize_VisageObjectMethodName, False()));
}
if (isLibrary) {
stmts.append(CallStmt(id(scriptLevelAccessSym), defs.applyDefaults_VisageObjectMethodName));
}
addDefinition(m().Block(Flags.STATIC, stmts.toList()));
}
//
// This method generates the code for a userInit or postInit method.
public void makeInitMethod(Name methName, ListBuffer<JCStatement> translatedInitBlocks, List<ClassSymbol> immediateMixinClasses) {
ClassSymbol superClassSym = analysis.getVisageSuperClassSym();
// Only create method if necessary (rely on VisageBase.)
if (translatedInitBlocks.nonEmpty() || immediateMixinClasses.nonEmpty() || isMixinClass()) {
List<JCVariableDecl> receiverVarDeclList;
MethodSymbol methSym;
ListBuffer<JCStatement> stmts = ListBuffer.lb();
// Mixin super calls will be handled when inserted into real classes.
if (!isMixinClass()) {
receiverVarDeclList = List.<JCVariableDecl>nil();
if (superClassSym != null) {
stmts.append(CallStmt(id(names._super), methName));
}
for (ClassSymbol mixinClassSym : immediateMixinClasses) {
JCExpression selector = makeType(mixinClassSym.type, false);
stmts.append(CallStmt(selector, methName, m().TypeCast(makeType(mixinClassSym), id(names._this))));
}
methSym = makeMethodSymbol(rawFlags(), syms.voidType, methName, List.<Type>nil());
} else {
receiverVarDeclList = List.<JCVariableDecl>of(ReceiverParam(getCurrentClassDecl()));
methSym = makeMethodSymbol(rawFlags(), syms.voidType, methName, List.<Type>of(getCurrentClassSymbol().type));
}
stmts.appendList(translatedInitBlocks);
addDefinition(Method(rawFlags(),
syms.voidType,
methName,
receiverVarDeclList,
stmts.toList(),
methSym));
}
}
private void makeConstructor(List<JCVariableDecl> params, List<Type> types, List<JCStatement> cStats) {
addDefinition(Method(Flags.PUBLIC,
syms.voidType,
names.init,
params,
cStats,
makeMethodSymbol(Flags.PUBLIC, syms.voidType, names.init, types)));
}
//
// Make a constructor to be called by Java code.
// Simply pass up to super, unless this is the last Visage class, in which case add object initialization
//
public void makeJavaEntryConstructor() {
// public Foo() {
// this(false);
// initialize$(true);
// }
makeConstructor(List.<JCVariableDecl>nil(), List.<Type>nil(),
Stmts(
CallStmt(names._this, False()),
CallStmt(defs.initialize_VisageObjectMethodName, isScript() || isAnonClass() ? False() : True())
)
);
}
//
// Make a constructor to be called by Visage code.
//
public void makeVisageEntryConstructor(List<VarInfo> varInfos, ClassSymbol outerTypeSym) {
ListBuffer<JCStatement> stmts = ListBuffer.lb();
Name dummyParamName = names.fromString("dummy");
// call the Visage version of the constructor in the superclass
// public Foo(boolean dummy) {
// super(dummy);
// }
if (analysis.getVisageSuperClassSym() != null || isScript()) {
Symbol outerSuper = outerTypeSymbol(types.supertype(getCurrentClassDecl().type).tsym);
if (outerSuper == null) {
stmts.append(CallStmt(names._super, id(dummyParamName)));
}
else {
stmts.append(CallStmt(names._super, resolveThis(outerSuper, false), id(dummyParamName)));
}
}
if (!analysis.isFirstTierNoMixins()) {
if (needsVCNT$()) {
stmts.append(CallStmt(defs.count_VisageObjectFieldName));
}
if (needsDCNT$()) {
stmts.append(CallStmt(defs.depCount_VisageObjectFieldName));
}
if (needsFCNT$()) {
stmts.append(CallStmt(defs.funcCount_VisageObjectFieldName));
}
}
// Update any local flag changes.
for (VarInfo ai : varInfos) {
if (ai.needsCloning() && ai.isOverride() && ai.getSymbol().isVisageMember()) {
Name flagName = attributeFlagsName(ai.proxyVarSym());
JCExpression update = updateVarBits(ai, id(flagName));
if (update != null) {
stmts.append(Stmt(m().Assign(id(flagName), update)));
}
}
}
// Construct the parameters
ListBuffer<JCVariableDecl> params = ListBuffer.lb();
ListBuffer<Type> types = ListBuffer.lb();
if (outerTypeSym != null) {
// add a parameter and a statement to constructor for the outer instance reference
params.append(Param(outerTypeSym.type, defs.outerAccessor_VisageObjectFieldName) );
types.append(outerTypeSym.type);
JCExpression cSelect = Select(id(names._this), defs.outerAccessor_VisageObjectFieldName);
stmts.append(Stmt(m().Assign(cSelect, id(defs.outerAccessor_VisageObjectFieldName))));
}
params.append(Param(syms.booleanType, dummyParamName));
types.append(syms.booleanType);
makeConstructor(params.toList(), types.toList(), stmts.toList());
}
//
// Make the field for accessing the outer members
//
public void makeOuterAccessorField(ClassSymbol outerTypeSym) {
// Create the field to store the outer instance reference
addDefinition(makeField(Flags.PUBLIC, outerTypeSym.type, defs.outerAccessor_VisageObjectFieldName, null));
}
//
// Make the method for accessing the outer members
//
public void makeOuterAccessorMethod(ClassSymbol outerTypeSym) {
ListBuffer<JCStatement> stmts = ListBuffer.lb();
VisageVarSymbol vs = new VisageVarSymbol(types, names,Flags.PUBLIC, defs.outerAccessor_VisageObjectFieldName, outerTypeSym.type, getCurrentClassSymbol());
stmts.append(Return(id(vs)));
MethodSymbol methSym = makeMethodSymbol(Flags.PUBLIC, outerTypeSym.type, defs.outerAccessor_MethodName, List.<Type>nil());
addDefinition(Method(Flags.PUBLIC, outerTypeSym.type, defs.outerAccessor_MethodName, List.<JCVariableDecl>nil(), stmts.toList(), methSym));
}
//
// Test to see if a name is a var accessor function.
//
private boolean isVarAccessor(Name name) {
return name.startsWith(defs.get_AttributeMethodPrefixName) ||
name.startsWith(defs.set_AttributeMethodPrefixName) ||
name.startsWith(defs.seq_AttributeMethodPrefixName) ||
name.startsWith(defs.invalidate_VisageObjectMethodName) ||
name.startsWith(defs.onReplaceAttributeMethodPrefixName) ||
name.startsWith(defs.getElement_VisageObjectMethodName) ||
name.startsWith(defs.size_VisageObjectMethodName) ||
name.startsWith(defs.applyDefaults_VisageObjectMethodName) ||
name.startsWith(defs.count_VisageObjectMethodName) ||
name.startsWith(defs.getFlags_VisageObjectMethodName) ||
name.startsWith(defs.setFlags_VisageObjectMethodName);
}
//
// Make a method from a MethodSymbol and an optional method body.
// Make a bound version if "isBound" is set.
//
private void appendMethodClones(final MethodSymbol methSym, final int bodyType) {
final boolean isBound = (methSym.flags() & VisageFlags.BOUND) != 0;
final boolean isStatic = methSym.isStatic();
final Name functionName = functionName(methSym, false, isBound);
List<VarSymbol> parameters = methSym.getParameters();
ListBuffer<JCStatement> stmts = null;
ListBuffer<JCVariableDecl> params = ListBuffer.lb();
ListBuffer<JCExpression> args = ListBuffer.lb();
ListBuffer<Type> argTypes = ListBuffer.lb();
boolean isProxy = isStatic &&
!parameters.isEmpty() &&
parameters.get(0).type == methSym.owner.type &&
isVarAccessor(methSym.name);
if (!isStatic || isProxy) {
args.append(id(names._this));
}
boolean skipFirst = isProxy;
for (VarSymbol vsym : parameters) {
if (!skipFirst) {
args.append(id(vsym.name));
params.append(Param(vsym.type, vsym.name));
argTypes.append(vsym.type);
}
skipFirst = false;
}
if (bodyType != BODY_NONE) {
stmts = ListBuffer.lb();
Name callName = functionName(methSym, !isStatic, isBound);
JCExpression receiver = makeType(methSym.owner.type, false);
if (methSym.getReturnType() == syms.voidType) {
stmts.append(CallStmt(receiver, callName, args));
} else {
stmts.append(Return(Call(receiver, callName, args)));
}
}
long flags = bodyType != BODY_NONE ? Flags.PUBLIC : (Flags.PUBLIC | Flags.ABSTRACT);
JCModifiers mods = m().Modifiers(flags);
if (isCurrentClassSymbol(methSym.owner))
mods = addAccessAnnotationModifiers(diagPos, methSym.flags(), mods);
else
mods = addInheritedAnnotationModifiers(diagPos, methSym.flags(), mods);
Type returnType = isBound? syms.visage_PointerType : methSym.getReturnType();
addDefinition(Method(
mods,
returnType,
functionName,
params.toList(),
bodyType != BODY_NONE ? stmts.toList() : null,
makeMethodSymbol(mods.flags, returnType, functionName, methSym.owner, argTypes.toList())));
if (bodyType != BODY_NONE) {
optStat.recordProxyMethod();
}
}
//
// Add proxies which redirect to the static implementation for every concrete method
//
public void makeFunctionProxyMethods(List<MethodSymbol> needDispatch) {
for (MethodSymbol sym : needDispatch) {
appendMethodClones(sym, BODY_NORMAL);
}
}
//
// Add interface declarations for declared methods.
//
public void makeFunctionInterfaceMethods() {
for (VisageTree def : getCurrentClassDecl().getMembers()) {
if (def.getVisageTag() == VisageTag.FUNCTION_DEF) {
VisageFunctionDefinition func = (VisageFunctionDefinition) def;
MethodSymbol sym = func.sym;
if ((sym.flags() & (Flags.SYNTHETIC | Flags.STATIC | Flags.PRIVATE)) == 0) {
appendMethodClones(sym, BODY_NONE);
}
}
}
}
//
// This method constructs a script class.
//
public void makeScript(List<JCTree> definitions) {
long flags = Flags.PUBLIC | Flags.STATIC;
JCModifiers classMods = m().Modifiers(flags);
classMods = addAccessAnnotationModifiers(diagPos, flags, classMods);
JCClassDecl script = m().ClassDef(
classMods,
scriptName,
List.<JCTypeParameter>nil(),
makeType(syms.visage_BaseType),
List.of(makeType(syms.visage_ObjectType)),
definitions);
script.sym = scriptClassSymbol;
membersToSymbol(script);
addDefinition(script);
}
//
// Methods for accessing the outer members.
//
public void makeOuterAccessorInterfaceMembers() {
ClassSymbol cSym = getCurrentClassSymbol();
if (cSym != null && toJava.getHasOuters().containsKey(cSym)) {
Symbol typeOwner = cSym.owner;
while (typeOwner != null && typeOwner.kind != Kinds.TYP) {
typeOwner = typeOwner.owner;
}
if (typeOwner != null) {
ClassSymbol returnSym = reader.enterClass(names.fromString(typeOwner.type.toString() + mixinClassSuffix));
JCMethodDecl accessorMethod = Method(
Flags.PUBLIC,
returnSym.type,
defs.outerAccessor_MethodName,
List.<JCVariableDecl>nil(),
null,
makeMethodSymbol(Flags.PUBLIC, returnSym.type, defs.outerAccessor_MethodName, List.<Type>nil()));
addDefinition(accessorMethod);
optStat.recordProxyMethod();
}
}
}
//
// Add definitions to class to access the script-level sole instance.
//
public void makeScriptLevelAccess(ClassSymbol sym, boolean scriptLevel) {
if (!scriptLevel) {
Symbol scriptLevelAccessSym = visagemake.ScriptAccessSymbol(sym);
addDefinition(makeField(scriptLevelAccessSym.flags_field & ~Flags.FINAL, scriptLevelAccessSym.type, scriptLevelAccessSym.name, null));
}
}
}
}