/* * 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 org.visage.api.VisageBindStatus; import org.visage.tools.code.VisageFlags; import org.visage.tools.code.VisageSymtab; import org.visage.tools.code.VisageTypes; import org.visage.tools.code.VisageVarSymbol; import org.visage.tools.comp.VisageTranslationSupport.NotYetImplementedException; import org.visage.tools.tree.*; import com.sun.tools.mjavac.code.Flags; import com.sun.tools.mjavac.code.Kinds; import com.sun.tools.mjavac.code.Symbol; import com.sun.tools.mjavac.code.Symbol.ClassSymbol; import com.sun.tools.mjavac.code.Symbol.MethodSymbol; import com.sun.tools.mjavac.code.Symbol.VarSymbol; import com.sun.tools.mjavac.code.Type; import com.sun.tools.mjavac.code.Type.MethodType; import com.sun.tools.mjavac.code.TypeTags; import com.sun.tools.mjavac.jvm.ClassReader; import com.sun.tools.mjavac.util.List; import com.sun.tools.mjavac.util.ListBuffer; import com.sun.tools.mjavac.util.Name; import com.sun.tools.mjavac.util.Context; import com.sun.tools.mjavac.util.JCDiagnostic.DiagnosticPosition; import java.util.HashMap; import java.util.Set; import java.util.HashSet; import java.util.Map; /** * Decompose bind expressions into easily translated expressions * * @author Robert Field */ public class VisageDecompose implements VisageVisitor { protected static final Context.Key<VisageDecompose> decomposeKey = new Context.Key<VisageDecompose>(); private VisageTree result; private VisageBindStatus bindStatus = VisageBindStatus.UNBOUND; private ListBuffer<VisageTree> lbVar; private Set<String> synthNames; private Symbol varOwner = null; private VisageVarSymbol currentVarSymbol; private Symbol currentClass = null; private boolean inScriptLevel = true; private VisageVarInit varInitContext = null; private boolean allowDebinding = false; // Map of shreded (Ident) selectors in bound select expressions. // Used in shred optimization. We use two maps - one for instance level // expressions and one for script level expressions. private Map<Symbol, VisageExpression> shrededSelectors; private Map<Symbol, VisageExpression> scriptShrededSelectors; protected final VisageTreeMaker visagemake; protected final VisagePreTranslationSupport preTrans; protected final VisageDefs defs; protected final Name.Table names; protected final VisageResolve rs; protected final VisageSymtab syms; protected final VisageTypes types; protected final ClassReader reader; protected final VisageOptimizationStatistics optStat; public static VisageDecompose instance(Context context) { VisageDecompose instance = context.get(decomposeKey); if (instance == null) instance = new VisageDecompose(context); return instance; } VisageDecompose(Context context) { context.put(decomposeKey, this); visagemake = VisageTreeMaker.instance(context); preTrans = VisagePreTranslationSupport.instance(context); names = Name.Table.instance(context); types = VisageTypes.instance(context); syms = (VisageSymtab)VisageSymtab.instance(context); rs = VisageResolve.instance(context); defs = VisageDefs.instance(context); reader = ClassReader.instance(context); optStat = VisageOptimizationStatistics.instance(context); } /** * External access: overwrite the top-level tree with the translated tree */ public void decompose(VisageEnv<VisageAttrContext> attrEnv) { bindStatus = VisageBindStatus.UNBOUND; lbVar = null; synthNames = new HashSet<String>(); attrEnv.toplevel = inlineShreddedVarInits(decompose(attrEnv.toplevel)); synthNames = null; lbVar = null; } void TODO(String msg) { throw new NotYetImplementedException("Not yet implemented: " + msg); } @SuppressWarnings("unchecked") private <T extends VisageTree> T decompose(T tree) { if (tree == null) return null; boolean ib = bindStatus != VisageBindStatus.UNBOUND; if (ib) optStat.recordDecomposeEnter(tree.getClass()); tree.accept(this); if (ib) optStat.recordDecomposeExit(); result.type = tree.type; return (T)result; } private <T extends VisageTree> List<T> decompose(List<T> trees) { if (trees == null) return null; ListBuffer<T> lb = new ListBuffer<T>(); for (T tree: trees) lb.append(decompose(tree)); return lb.toList(); } private boolean requiresShred(VisageExpression tree) { if (tree==null) { return false; } switch (tree.getVisageTag()) { case APPLY: case OBJECT_LITERAL: case SEQUENCE_EXPLICIT: case SEQUENCE_RANGE: case FOR_EXPRESSION: return true; case CONDEXPR: return types.isSequence(tree.type); } return false; } private VisageExpression decomposeComponent(VisageExpression tree) { if (requiresShred(tree)) return shred(tree); else return decompose(tree); } private List<VisageExpression> decomposeComponents(List<VisageExpression> trees) { if (trees == null) return null; ListBuffer<VisageExpression> lb = ListBuffer.lb(); for (VisageExpression tree: trees) lb.append(decomposeComponent(tree)); return lb.toList(); } private <T extends VisageTree> T inlineShreddedVarInits(T tree) { new VisageTreeScanner() { private void unwindVarInit(VisageVarInit vi, ListBuffer<VisageExpression> elb) { for (VisageVarInit cvi : vi.getShreddedVarInits()) { unwindVarInit(cvi, elb); } elb.append(vi); } private List<VisageExpression> process(VisageExpression expr) { scan(expr); if (expr instanceof VisageVarInit) { ListBuffer<VisageExpression> elb = ListBuffer.lb(); unwindVarInit((VisageVarInit) expr, elb); return elb.toList(); } else { return List.of(expr); } } @Override public void visitBlockExpression(VisageBlock tree) { ListBuffer<VisageExpression> elb = ListBuffer.lb(); for (VisageExpression expr : tree.stats) { elb.appendList(process(expr)); } if (tree.value != null) { List<VisageExpression> val = process(tree.value); for (int i = 0; i < (val.length() - 1); ++i) { elb.append(val.get(i)); } } tree.stats = elb.toList(); } }.scan(tree); return tree; } private VisageVar makeVar(DiagnosticPosition diagPos, String label, VisageExpression initExpr, VisageBindStatus bindStatus, Type type) { VisageVar var = preTrans.SynthVar(diagPos, currentVarSymbol, label, initExpr, bindStatus, type, inScriptLevel, varOwner); lbVar.append(var); return var; } private VisageVar makeVar(DiagnosticPosition diagPos, Name vName, VisageExpression pose, VisageBindStatus bindStatus, Type type) { optStat.recordSynthVar("synth"); long flags = VisageFlags.SCRIPT_PRIVATE | Flags.SYNTHETIC | (inScriptLevel ? Flags.STATIC | VisageFlags.SCRIPT_LEVEL_SYNTH_STATIC : 0L); VisageVar var = preTrans.Var(diagPos, flags, types.normalize(type), vName, bindStatus, pose, varOwner); varOwner.members().enter(var.sym); lbVar.append(var); return var; } private VisageVar shredVar(String label, VisageExpression pose, Type type) { return shredVar(label, pose, type, VisageBindStatus.UNIDIBIND); } private VisageVar shredVar(String label, VisageExpression pose, Type type, VisageBindStatus bindStatus) { optStat.recordShreds(); Name tmpName = tempName(label); // If this shred var initialized with a call to a bound function? VisageVar ptrVar = makeTempBoundResultName(tmpName, pose); if (ptrVar != null) { return makeVar(pose.pos(), tmpName, id(ptrVar), bindStatus, type); } else { return makeVar(pose.pos(), label, pose, bindStatus, type); } } private VisageIdent id(VisageVar v) { VisageIdent id = visagemake.at(v.pos).Ident(v.getName()); id.sym = v.sym; id.type = v.type; return id; } /** * If we are in a bound expression, break this expression out into a separate synthetic bound variable. */ private VisageExpression shred(VisageExpression tree, Type contextType) { if (tree == null) { return null; } if (bindStatus.isBound()) { VisageVarInit prevVarInitContext = varInitContext; VisageVarInit ourVarInit = null; VisageBindStatus prevBindStatus = bindStatus; if (false & allowDebinding && preTrans.isImmutable(tree)) { bindStatus = VisageBindStatus.UNBOUND; if (prevVarInitContext != null) { ourVarInit = visagemake.VarInit(null); varInitContext = ourVarInit; } } VisageExpression pose = decompose(tree); Type varType = tree.type; if (tree.type == syms.botType && contextType != null) { // If the tree type is bottom, try to use contextType varType = contextType; } VisageVar v = shredVar("", pose, varType, bindStatus); if (ourVarInit != null) { ourVarInit.resetVar(v); prevVarInitContext.addShreddedVarInit(ourVarInit); } varInitContext = prevVarInitContext; VisageExpression shred = id(v); bindStatus = prevBindStatus; return shred; } else { return decompose(tree); } } private VisageExpression shred(VisageExpression tree) { return shred(tree, null); } private VisageExpression shredUnlessIdent(VisageExpression tree) { if (tree instanceof VisageIdent) { return decompose(tree); } return shred(tree); } private List<VisageExpression> shred(List<VisageExpression> trees, List<Type> paramTypes) { if (trees == null) return null; ListBuffer<VisageExpression> lb = new ListBuffer<VisageExpression>(); Type paramType = paramTypes != null? paramTypes.head : null; for (VisageExpression tree: trees) { if (false/*disable-VSGC-4079*/ && tree != null && preTrans.isImmutable(tree)) { lb.append(tree); } else { lb.append(shred(tree, paramType)); } if (paramTypes != null) { paramTypes = paramTypes.tail; paramType = paramTypes.head; } } return lb.toList(); } private Name tempName(String label) { String name = currentVarSymbol != null ? currentVarSymbol.toString() : ""; name += "$" + label + "$"; if (synthNames.contains(name)) { for (int i = 0; true; i++) { String numbered = name + i; if (!synthNames.contains(numbered)) { name = numbered; break; } } } // name += defs.internalNameMarker; synthNames.add(name); return names.fromString(name); } private Name tempBoundResultName(Name name) { return names.fromString(VisageDefs.boundFunctionResult + name); } //TODO: clean-up this whole mess private boolean isBoundFunctionResult(VisageExpression initExpr) { if (initExpr instanceof VisageFunctionInvocation) { Symbol meth = VisageTreeInfo.symbol(((VisageFunctionInvocation)initExpr).meth); return meth != null && (meth.flags() & VisageFlags.BOUND) != 0L; } else { return false; } } private VisageVar makeTempBoundResultName(Name varName, VisageExpression initExpr) { VisageVar ptrVar = null; if (isBoundFunctionResult(initExpr)) { Name tmpBoundResName = tempBoundResultName(varName); /* * Introduce a Pointer synthetic variable which will be used to cache * bound function's return value. The name of the sythetic Pointer * variable is derived from the given varName. */ ptrVar = makeVar(initExpr.pos(), tmpBoundResName, initExpr, VisageBindStatus.UNIDIBIND, syms.visage_PointerType); ptrVar.sym.flags_field |= Flags.SYNTHETIC | VisageFlags.VARUSE_BIND_ACCESS; } return ptrVar; } private <T extends VisageTree> List<T> decomposeContainer(List<T> trees) { if (trees == null) return null; ListBuffer<T> lb = new ListBuffer<T>(); for (T tree: trees) lb.append(decompose(tree)); return lb.toList(); } public void visitScript(VisageScript tree) { bindStatus = VisageBindStatus.UNBOUND; tree.defs = decomposeContainer(tree.defs); result = tree; } public void visitImport(VisageImport tree) { result = tree; } public void visitSkip(VisageSkip tree) { result = tree; } public void visitWhileLoop(VisageWhileLoop tree) { VisageExpression cond = decompose(tree.cond); VisageExpression body = decompose(tree.body); result = visagemake.at(tree.pos).WhileLoop(cond, body); } public void visitTry(VisageTry tree) { VisageBlock body = decompose(tree.body); List<VisageCatch> catchers = decompose(tree.catchers); VisageBlock finalizer = decompose(tree.finalizer); result = visagemake.at(tree.pos).Try(body, catchers, finalizer); } public void visitCatch(VisageCatch tree) { VisageVar param = decompose(tree.param); VisageBlock body = decompose(tree.body); result = visagemake.at(tree.pos).Catch(param, body); } public void visitIfExpression(VisageIfExpression tree) { VisageExpression cond = decomposeComponent(tree.cond); VisageExpression truepart = decomposeComponent(tree.truepart); VisageExpression falsepart = decomposeComponent(tree.falsepart); VisageIfExpression res = visagemake.at(tree.pos).Conditional(cond, truepart, falsepart); if (bindStatus.isBound() && types.isSequence(tree.type)) { res.boundCondVar = synthVar(defs.condNamePrefix(), cond, cond.type, false); res.boundThenVar = synthVar(defs.thenNamePrefix(), truepart, truepart.type, false); res.boundElseVar = synthVar(defs.elseNamePrefix(), falsepart, falsepart.type, false); // Add a size field to hold the previous size on condition switch VisageVar v = makeSizeVar(tree.pos(), VisageDefs.UNDEFINED_MARKER_INT); res.boundSizeVar = v; } result = res; } public void visitBreak(VisageBreak tree) { if (tree.nonLocalBreak) { // A non-local break gets turned into an exception VisageIdent nonLocalExceptionClass = visagemake.Ident(names.fromString(VisageDefs.cNonLocalBreakException)); nonLocalExceptionClass.sym = syms.visage_NonLocalBreakExceptionType.tsym; nonLocalExceptionClass.type = syms.visage_NonLocalBreakExceptionType; VisageInstanciate expInst = visagemake.InstanciateNew(nonLocalExceptionClass, List.<VisageExpression>nil()); expInst.sym = (ClassSymbol)syms.visage_NonLocalBreakExceptionType.tsym; expInst.type = syms.visage_NonLocalBreakExceptionType; result = visagemake.Throw(expInst).setType(syms.unreachableType); } else { result = tree; } } public void visitContinue(VisageContinue tree) { if (tree.nonLocalContinue) { // A non-local continue gets turned into an exception VisageIdent nonLocalExceptionClass = visagemake.Ident(names.fromString(VisageDefs.cNonLocalContinueException)); nonLocalExceptionClass.sym = syms.visage_NonLocalContinueExceptionType.tsym; nonLocalExceptionClass.type = syms.visage_NonLocalContinueExceptionType; VisageInstanciate expInst = visagemake.InstanciateNew(nonLocalExceptionClass, List.<VisageExpression>nil()); expInst.sym = (ClassSymbol)syms.visage_NonLocalContinueExceptionType.tsym; expInst.type = syms.visage_NonLocalContinueExceptionType; result = visagemake.Throw(expInst).setType(syms.unreachableType); } else { result = tree; } } public void visitReturn(VisageReturn tree) { tree.expr = decompose(tree.expr); if (tree.nonLocalReturn) { // A non-local return gets turned into an exception VisageIdent nonLocalExceptionClass = visagemake.Ident(names.fromString(VisageDefs.cNonLocalReturnException)); nonLocalExceptionClass.sym = syms.visage_NonLocalReturnExceptionType.tsym; nonLocalExceptionClass.type = syms.visage_NonLocalReturnExceptionType; List<VisageExpression> valueArg = tree.expr==null? List.<VisageExpression>nil() : List.of(tree.expr); VisageInstanciate expInst = visagemake.InstanciateNew( nonLocalExceptionClass, valueArg); expInst.sym = (ClassSymbol)syms.visage_NonLocalReturnExceptionType.tsym; expInst.type = syms.visage_NonLocalReturnExceptionType; result = visagemake.Throw(expInst); } else { result = tree; } } public void visitThrow(VisageThrow tree) { result = tree; } public void visitFunctionInvocation(VisageFunctionInvocation tree) { VisageExpression fn = decompose(tree.meth); Symbol msym = VisageTreeInfo.symbol(tree.meth); /* * Do *not* shred select expression if it is passed to intrinsic function * Pointer.make(Object). Shred only the "selected" portion of it. If * we shred the whole select expr, then a temporary shred variable will * be used to create Pointer. That temporary is a bound variable and so * Pointer.set() on that would throw assign-to-bind-variable exception. */ List<VisageExpression> args; if (types.isSyntheticPointerFunction(msym)) { VisageVarRef varRef = (VisageVarRef)tree.args.head; if (varRef.getReceiver() != null) { varRef.setReceiver(shred(varRef.getReceiver())); } args = tree.args; } else { List<Type> paramTypes = tree.meth.type.getParameterTypes(); Symbol sym = VisageTreeInfo.symbolFor(tree.meth); if (sym instanceof MethodSymbol && ((MethodSymbol)sym).isVarArgs()) { Type varargType = paramTypes.reverse().head; paramTypes = paramTypes.reverse().tail.reverse(); //remove last formal while (paramTypes.size() < tree.args.size()) { paramTypes = paramTypes.append(types.elemtype(varargType)); } } args = shred(tree.args, paramTypes); } VisageExpression res = visagemake.at(tree.pos).Apply(tree.typeargs, fn, args); res.type = tree.type; if (bindStatus.isBound() && types.isSequence(tree.type) && !isBoundFunctionResult(tree)) { VisageVar v = shredVar(defs.functionResultNamePrefix(), res, tree.type); VisageVar sz = makeSizeVar(v.pos(), VisageDefs.UNDEFINED_MARKER_INT); res = visagemake.IdentSequenceProxy(v.name, v.sym, sz.sym); } result = res; } public void visitParens(VisageParens tree) { VisageExpression expr = decomposeComponent(tree.expr); result = visagemake.at(tree.pos).Parens(expr); } public void visitAssign(VisageAssign tree) { VisageExpression lhs = decompose(tree.lhs); VisageExpression rhs = decompose(tree.rhs); result = visagemake.at(tree.pos).Assign(lhs, rhs); } public void visitAssignop(VisageAssignOp tree) { VisageExpression lhs = decompose(tree.lhs); VisageExpression rhs = decompose(tree.rhs); VisageTag tag = tree.getVisageTag(); VisageAssignOp res = visagemake.at(tree.pos).Assignop(tag, lhs, rhs); res.operator = tree.operator; result = res; } public void visitUnary(VisageUnary tree) { VisageTag tag = tree.getVisageTag(); VisageExpression arg = tag == VisageTag.REVERSE || tag == VisageTag.SIZEOF ? shredUnlessIdent(tree.arg) : decomposeComponent(tree.arg); VisageUnary res = visagemake.at(tree.pos).Unary(tag, arg); res.operator = tree.operator; result = res; } public void visitBinary(VisageBinary tree) { VisageTag tag = tree.getVisageTag(); boolean cutOff = tag==VisageTag.AND || tag==VisageTag.OR; VisageExpression lhs = decomposeComponent(tree.lhs); VisageExpression rhs = cutOff? shredUnlessIdent(tree.rhs) : // If cut-off operation, preface code must be evaluated separately decomposeComponent(tree.rhs); VisageBinary res = visagemake.at(tree.pos).Binary(tag, lhs, rhs); res.operator = tree.operator; result = res; } public void visitTypeCast(VisageTypeCast tree) { boolean isBoundSequence = bindStatus.isBound() && types.isSequence(tree.type); boolean isCastingArray = types.isArray(tree.expr.type); VisageTree clazz = decompose(tree.clazz); VisageExpression expr = isBoundSequence? isCastingArray? shred(tree.expr) : // can't smash invalidation logic of user var shredUnlessIdent(tree.expr) : decomposeComponent(tree.expr); VisageTypeCast res = visagemake.at(tree.pos).TypeCast(clazz, expr); if (isBoundSequence && isCastingArray) { // Add a size field to hold the previous size of nativearray VisageVar v = makeSizeVar(tree.pos(), 0); res.boundArraySizeSym = v.sym; } result = res; } public void visitInstanceOf(VisageInstanceOf tree) { VisageExpression expr = decomposeComponent(tree.expr); VisageTree clazz = decompose(tree.clazz); result = visagemake.at(tree.pos).TypeTest(expr, clazz); } public void visitSelect(VisageSelect tree) { DiagnosticPosition diagPos = tree.pos(); Symbol sym = tree.sym; Symbol selectSym = VisageTreeInfo.symbolFor(tree.selected); if (selectSym != null && ((selectSym.kind == Kinds.TYP && sym.kind != Kinds.MTH) || selectSym.name == names._this)) { // Select is just via "this" -- make it a simple Ident //TODO: move this to lower VisageIdent res = visagemake.at(diagPos).Ident(sym.name); res.sym = sym; result = res; } else { VisageExpression selected; if ((selectSym != null && (selectSym.kind == Kinds.TYP || selectSym.name == names._super || selectSym.name == names._class))) { // Referenced is static, or qualified super access // then selected is a class reference selected = decompose(tree.selected); } else { VisageBindStatus oldBindStatus = bindStatus; if (bindStatus == VisageBindStatus.BIDIBIND) bindStatus = VisageBindStatus.UNIDIBIND; /** * Avoding shreding as an optimization: if the select expression's selected part * is a VisageIdent and that identifier is an instance var of current class, then we * don't have to shred it. * * Example: * * class Person { * var name : String; * var age: Integer; * } * * class Test { * var p : Person; * var name = bind p.name; // instance var "p" in bind-select * var age = bind p.age; // same instance var "p" in bind-select * } * * In this case we can avoid shreding and generating two synthetic variables for * bind select expressions p.name, p.age. * * Special cases: * * (1) sequences are always shreded * (2) non-variable access (eg. select expression selects method) * * TODO: for some reason this optimization does not work if the same selected part is * used by a unidirectional and bidirectional bind expressions. For now, filtering out * bidirectional cases. We need to revisit that mystery. Also. I've to oldBindStatus * because bindStatus has been set to UNIDIBIND in the previous statement. */ if (oldBindStatus == VisageBindStatus.UNIDIBIND && tree.selected instanceof VisageIdent && !types.isSequence(tree.type) && sym instanceof VarSymbol) { if (selectSym.owner == currentClass && !(selectSym.isStatic() ^ inScriptLevel)) { selected = tree.selected; } else { Map<Symbol, VisageExpression> shredMap = inScriptLevel? scriptShrededSelectors : shrededSelectors; if (shredMap.containsKey(selectSym)) { selected = shredMap.get(selectSym); } else { selected = shred(tree.selected); shredMap.put(selectSym, selected); } } } else { selected = shred(tree.selected); } bindStatus = oldBindStatus; } VisageSelect res = visagemake.at(diagPos).Select(selected, sym.name, tree.nullCheck); res.sym = sym; if (bindStatus.isBound() && types.isSequence(tree.type)) { // Add a size field to hold the previous size on selector switch VisageVar v = makeSizeVar(diagPos, 0); res.boundSize = v; } result = res; } } public void visitIdent(VisageIdent tree) { VisageIdent res = visagemake.at(tree.pos).Ident(tree.getName()); res.sym = tree.sym; result = res; } public void visitLiteral(VisageLiteral tree) { result = tree; } public void visitModifiers(VisageModifiers tree) { result = tree; } public void visitErroneous(VisageErroneous tree) { result = tree; } public void visitClassDeclaration(VisageClassDeclaration tree) { VisageBindStatus prevBindStatus = bindStatus; bindStatus = VisageBindStatus.UNBOUND; Symbol prevVarOwner = varOwner; Symbol prevClass = currentClass; Map<Symbol, VisageExpression> prevShredExprs = shrededSelectors; Map<Symbol, VisageExpression> prevScriptShredExprs = scriptShrededSelectors; shrededSelectors = new HashMap<Symbol, VisageExpression>(); scriptShrededSelectors = new HashMap<Symbol, VisageExpression>(); currentClass = varOwner = tree.sym; ListBuffer<VisageTree> prevLbVar = lbVar; lbVar = ListBuffer.<VisageTree>lb(); for (VisageTree mem : tree.getMembers()) { lbVar.append(decompose(mem)); } tree.setMembers(lbVar.toList()); lbVar = prevLbVar; varOwner = prevVarOwner; currentClass = prevClass; shrededSelectors = prevShredExprs; scriptShrededSelectors = prevScriptShredExprs; result = tree; bindStatus = prevBindStatus; } public void visitFunctionDefinition(VisageFunctionDefinition tree) { boolean wasInScriptLevel = inScriptLevel; // Bound functions are handled by local variable bind facility. // The return value is transformed already in VisageLocalToClass. // So, we are not changing bind state "inBind". VisageBindStatus prevBindStatus = bindStatus; bindStatus = VisageBindStatus.UNBOUND; inScriptLevel = tree.isStatic(); Symbol prevVarOwner = varOwner; varOwner = null; VisageModifiers mods = tree.mods; Name name = tree.getName(); VisageType restype = tree.getVisageReturnType(); List<VisageVar> params = decompose(tree.getParams()); VisageBlock bodyExpression = decompose(tree.getBodyExpression()); VisageFunctionDefinition res = visagemake.at(tree.pos).FunctionDefinition(mods, name, restype, params, bodyExpression); res.sym = tree.sym; result = res; bindStatus = prevBindStatus; inScriptLevel = wasInScriptLevel; varOwner = prevVarOwner; } public void visitInitDefinition(VisageInitDefinition tree) { boolean wasInScriptLevel = inScriptLevel; inScriptLevel = tree.sym.isStatic(); VisageBlock body = decompose(tree.body); VisageInitDefinition res = visagemake.at(tree.pos).InitDefinition(body); res.sym = tree.sym; result = res; inScriptLevel = wasInScriptLevel; } public void visitPostInitDefinition(VisagePostInitDefinition tree) { boolean wasInScriptLevel = inScriptLevel; inScriptLevel = tree.sym.isStatic(); VisageBlock body = decompose(tree.body); VisagePostInitDefinition res = visagemake.at(tree.pos).PostInitDefinition(body); res.sym = tree.sym; result = res; inScriptLevel = wasInScriptLevel; } public void visitStringExpression(VisageStringExpression tree) { List<VisageExpression> parts = decomposeComponents(tree.parts); result = visagemake.at(tree.pos).StringExpression(parts, tree.translationKey); } public void visitInstanciate(VisageInstanciate tree) { VisageExpression klassExpr = tree.getIdentifier(); List<VisageObjectLiteralPart> dparts = decompose(tree.getParts()); VisageClassDeclaration dcdel = decompose(tree.getClassBody()); List<VisageExpression> dargs = decomposeComponents(tree.getArgs()); VisageInstanciate res = visagemake.at(tree.pos).Instanciate(tree.getVisageKind(), klassExpr, dcdel, dargs, dparts, tree.getLocalvars()); res.sym = tree.sym; res.constructor = tree.constructor; res.varDefinedByThis = tree.varDefinedByThis; long anonTestFlags = Flags.SYNTHETIC | Flags.FINAL; if (dcdel != null && (dcdel.sym.flags_field & anonTestFlags) == anonTestFlags) { ListBuffer<VisageVarSymbol> objInitSyms = ListBuffer.lb(); for (VisageObjectLiteralPart olp : dparts) { objInitSyms.append((VisageVarSymbol)olp.sym); } if (objInitSyms.size() > 1) { dcdel.setObjInitSyms(objInitSyms.toList()); } } result = res; } public void visitObjectLiteralPart(VisageObjectLiteralPart tree) { VisageVarSymbol prevVarSymbol = currentVarSymbol; currentVarSymbol = (VisageVarSymbol)tree.sym; if (tree.isExplicitlyBound()) throw new AssertionError("bound parts should have been converted to overrides"); VisageExpression expr = shred(tree.getExpression(), tree.sym.type); VisageObjectLiteralPart res = visagemake.at(tree.pos).ObjectLiteralPart(tree.name, expr, tree.getExplicitBindStatus()); res.markBound(bindStatus); res.sym = tree.sym; currentVarSymbol = prevVarSymbol; result = res; } public void visitTypeAny(VisageTypeAny tree) { result = tree; } public void visitTypeClass(VisageTypeClass tree) { result = tree; } public void visitTypeFunctional(VisageTypeFunctional tree) { result = tree; } public void visitTypeArray(VisageTypeArray tree) { result = tree; } public void visitTypeUnknown(VisageTypeUnknown tree) { result = tree; } public void visitVarInit(VisageVarInit tree) { // Handled in visitVar result = tree; } public void visitVarRef(VisageVarRef tree) { result = tree; } public void visitVar(VisageVar tree) { boolean wasInScriptLevel = inScriptLevel; inScriptLevel = tree.isStatic(); VisageVarSymbol prevVarSymbol = currentVarSymbol; currentVarSymbol = tree.sym; VisageBindStatus prevBindStatus = bindStatus; // for on-replace, decompose as unbound bindStatus = VisageBindStatus.UNBOUND; VisageOnReplace onReplace = decompose(tree.getOnReplace()); VisageOnReplace onInvalidate = decompose(tree.getOnInvalidate()); // bound if was bind context or is bound variable bindStatus = tree.isBound()? tree.getBindStatus() : prevBindStatus; VisageVarInit vsi = tree.getVarInit(); VisageVarInit prevVarInitContext = varInitContext; boolean prevAllowDebinding = allowDebinding; varInitContext = vsi; allowDebinding = !tree.sym.hasForwardReference(); // No debinding for forward referenced var VisageExpression initExpr = decompose(tree.getInitializer()); // Is this a bound var and initialized with a Pointer result // from a bound function call? If so, we need to create Pointer // synthetic var here. VisageVar ptrVar = bindStatus.isBound()? makeTempBoundResultName(tree.name, initExpr) : null; VisageVar res = visagemake.at(tree.pos).Var( tree.name, tree.getVisageType(), tree.getModifiers(), (ptrVar != null)? id(ptrVar) : initExpr, tree.getBindStatus(), onReplace, onInvalidate); res.sym = tree.sym; res.type = tree.type; if (vsi != null) { // update the var in the var-init vsi.resetVar(res); } allowDebinding = prevAllowDebinding; varInitContext = prevVarInitContext; bindStatus = prevBindStatus; inScriptLevel = wasInScriptLevel; currentVarSymbol = prevVarSymbol; result = res; } public void visitOnReplace(VisageOnReplace tree) { VisageVar oldValue = tree.getOldValue(); VisageVar firstIndex = tree.getFirstIndex(); VisageVar lastIndex = tree.getLastIndex(); VisageVar newElements = tree.getNewElements(); VisageVar saveVar = null; if (oldValue != null && types.isSequence(oldValue.type)) { VisageVarSymbol sym = oldValue.sym; // FIXME OPTIMIZATION: // if sym.isUsedInSizeof() && ! sym.isUsedOutsideSizeof()) // then we can save just the old size in the save-var. // We also have to translate 'sizeof oldVar' to the saved size. if (sym.isUsedInSizeof() || sym.isUsedOutsideSizeof()) saveVar = makeSaveVar(tree.pos(), oldValue.type); } VisageBlock body = decompose(tree.getBody()); result = visagemake.at(tree.pos).OnReplace(oldValue, firstIndex, lastIndex, tree.getEndKind(), newElements, saveVar, body); } /** * Block-expressions * * For bound sequence block-expressions, get the initialization right by * The block vars have already been made into VarInits. * Making block value into a synthetic var, and add a VarInit for it * to the block vars */ public void visitBlockExpression(VisageBlock tree) { List<VisageExpression> stats; VisageExpression value; if (bindStatus.isBound() && types.isSequence(tree.type)) { for (VisageExpression stat : tree.stats) { if (!(stat instanceof VisageVarInit)) { throw new AssertionError("the statements in a bound block should already be just VarInit"); } } VisageVar v = shredVar(defs.valueNamePrefix(), decompose(tree.value), tree.type); VisageVarInit vi = visagemake.at(tree.value.pos()).VarInit(v); vi.type = tree.type; stats = tree.stats.append(vi); VisageIdent val = id(v); val.sym = v.sym; val.type = tree.type; value = val; } else { stats = decomposeContainer(tree.stats); value = decompose(tree.value); } VisageBlock res = visagemake.at(tree.pos()).Block(tree.flags, stats, value); res.endpos = tree.endpos; result = res; } public void visitFunctionValue(VisageFunctionValue tree) { VisageBindStatus prevBindStatus = bindStatus; bindStatus = VisageBindStatus.UNBOUND; tree.bodyExpression = decompose(tree.bodyExpression); result = tree; bindStatus = prevBindStatus; } public void visitSequenceEmpty(VisageSequenceEmpty tree) { result = tree; } private VisageVar synthVar(String label, VisageExpression tree, Type type) { return synthVar(label, tree, type, true); } private VisageVar synthVar(String label, VisageExpression tree, Type type, boolean decompose) { if (tree == null) { return null; } VisageExpression expr = decompose ? decompose(tree) : tree; visagemake.at(tree.pos()); // set position if (!types.isSameType(tree.type, type)) { // cast to desired type VisageIdent tp = (VisageIdent) visagemake.Type(type); tp.sym = type.tsym; expr = visagemake.TypeCast(tp, expr); } VisageVar v = shredVar(label, expr, type); v.sym.flags_field |= VisageFlags.VARMARK_BARE_SYNTH; return v; } private VisageVar makeSizeVar(DiagnosticPosition diagPos, int initial) { return makeIntVar(diagPos, defs.sizeNamePrefix(), initial); } private VisageVar makeIntVar(DiagnosticPosition diagPos, String label, int initial) { VisageExpression initialSize = visagemake.at(diagPos).Literal(initial); initialSize.type = syms.intType; VisageVar v = makeVar(diagPos, label, initialSize, VisageBindStatus.UNBOUND, syms.intType); return v; } private VisageVar makeSaveVar(DiagnosticPosition diagPos, Type type) { VisageVar v = makeVar(diagPos, defs.saveNamePrefix(), null, VisageBindStatus.UNBOUND, type); v.sym.flags_field |= VisageFlags.VARMARK_BARE_SYNTH; return v; } /** * Add synthetic variables, and attach them to the reconstituted range. * def range = bind [rb .. re step st] * adds: * * def lower = bind rb; // marked BARE_SYNTH * def upper = bind re; // marked BARE_SYNTH * def step = bind st; // marked BARE_SYNTH * def size = bind -99; */ public void visitSequenceRange(VisageSequenceRange tree) { VisageExpression lower; VisageExpression upper; VisageExpression stepOrNull; if (bindStatus.isBound()) { Type elemType = types.elementType(tree.type); lower = synthVar(defs.lowerNamePrefix(), tree.getLower(), elemType); upper = synthVar(defs.upperNamePrefix(), tree.getUpper(), elemType); stepOrNull = synthVar(defs.stepNamePrefix(), tree.getStepOrNull(), elemType); } else { lower = decomposeComponent(tree.getLower()); upper = decomposeComponent(tree.getUpper()); stepOrNull = decomposeComponent(tree.getStepOrNull()); } VisageSequenceRange res = visagemake.at(tree.pos).RangeSequence(lower, upper, stepOrNull, tree.isExclusive()); res.type = tree.type; if (bindStatus.isBound()) { // now add a size var res.boundSizeVar = makeSizeVar(tree.pos(), VisageDefs.UNDEFINED_MARKER_INT); } result = res; } public void visitSequenceExplicit(VisageSequenceExplicit tree) { DiagnosticPosition diagPos = tree.pos(); VisageSequenceExplicit res; if (bindStatus.isBound()) { // bound should not use items - non-null for pretty-printing res = visagemake.at(diagPos).ExplicitSequence(List.<VisageExpression>nil()); boolean hasNullable = false; int n = 0; ListBuffer<VisageVarSymbol> vb = ListBuffer.lb(); ListBuffer<VisageVarSymbol> vblen = ListBuffer.lb(); for (VisageExpression item : tree.getItems()) { vb.append(synthVar(defs.itemNamePrefix()+n, item, item.type).sym); VisageVarSymbol lenSym = null; if (preTrans.isNullable(item)) { lenSym = makeIntVar(item.pos(), defs.lengthNamePrefix()+n, 0).sym; hasNullable = true; } vblen.append(lenSym); ++n; } res.boundItemsSyms = vb.toList(); res.boundItemLengthSyms = vblen.toList(); // now add synth vars if (tree.getItems().length() > 1 || types.isArrayOrSequenceType(res.boundItemsSyms.get(0).type)) { res.boundLowestInvalidPartSym = makeIntVar(diagPos, defs.lowNamePrefix(), VisageDefs.UNDEFINED_MARKER_INT).sym; res.boundHighestInvalidPartSym = makeIntVar(diagPos, defs.highNamePrefix(), VisageDefs.UNDEFINED_MARKER_INT).sym; res.boundPendingTriggersSym = makeIntVar(diagPos, defs.pendingNamePrefix(), 0).sym; if (hasNullable) { res.boundSizeSym = makeSizeVar(diagPos, VisageDefs.UNDEFINED_MARKER_INT).sym; res.boundDeltaSym = makeIntVar(diagPos, defs.deltaNamePrefix(), 0).sym; res.boundChangeStartPosSym = makeIntVar(diagPos, defs.cngStartNamePrefix(), 0).sym; res.boundChangeEndPosSym = makeIntVar(diagPos, defs.cngEndNamePrefix(), 0).sym; } } VisageExpression falseLit = visagemake.Literal(TypeTags.BOOLEAN, 0); falseLit.type = syms.booleanType; res.boundIgnoreInvalidationsSym = makeVar(diagPos, defs.ignoreNamePrefix(), falseLit, VisageBindStatus.UNBOUND, syms.booleanType).sym; } else { List<VisageExpression> items = decomposeComponents(tree.getItems()); res = visagemake.at(diagPos).ExplicitSequence(items); } res.type = tree.type; result = res; } public void visitSequenceIndexed(VisageSequenceIndexed tree) { VisageExpression sequence = null; if (bindStatus.isBound()) { sequence = shredUnlessIdent(tree.getSequence()); } else { sequence = decomposeComponent(tree.getSequence()); } VisageExpression index = decomposeComponent(tree.getIndex()); result = visagemake.at(tree.pos).SequenceIndexed(sequence, index); } public void visitSequenceSlice(VisageSequenceSlice tree) { VisageExpression sequence = shred(tree.getSequence()); VisageExpression firstIndex = shred(tree.getFirstIndex()); VisageExpression lastIndex = shred(tree.getLastIndex()); result = visagemake.at(tree.pos).SequenceSlice(sequence, firstIndex, lastIndex, tree.getEndKind()); } public void visitSequenceInsert(VisageSequenceInsert tree) { VisageExpression sequence = decompose(tree.getSequence()); VisageExpression element = decompose(tree.getElement()); VisageExpression position = decompose(tree.getPosition()); result = visagemake.at(tree.pos).SequenceInsert(sequence, element, position, tree.shouldInsertAfter()); } public void visitSequenceDelete(VisageSequenceDelete tree) { VisageExpression sequence = decompose(tree.getSequence()); VisageExpression element = decompose(tree.getElement()); result = visagemake.at(tree.pos).SequenceDelete(sequence, element); } public void visitInvalidate(VisageInvalidate tree) { VisageExpression variable = decompose(tree.getVariable()); result = visagemake.at(tree.pos).Invalidate(variable); } public void visitForExpression(VisageForExpression tree) { if (bindStatus.isBound()) { VisageForExpressionInClause clause = tree.inClauses.head; clause.seqExpr = shred(clause.seqExpr, null); // clause.whereExpr = decompose(clause.whereExpr); // Create the BoundForHelper variable: Type inductionType = types.boxedTypeOrType(clause.inductionVarSym.type); VisageBlock body = (VisageBlock) tree.getBodyExpression(); Type helperType = types.applySimpleGenericType( types.isSequence(body.type)? syms.visage_BoundForOverSequenceType : (preTrans.isNullable(body) || clause.hasWhereExpression())? syms.visage_BoundForOverNullableSingletonType : syms.visage_BoundForOverSingletonType, types.boxedElementType(tree.type), inductionType); VisageExpression init = visagemake.Literal(TypeTags.BOT, null); init.type = helperType; Name helperName = preTrans.makeUniqueVarNameIn(names.fromString(defs.helperDollarNamePrefix()+currentVarSymbol.name), varOwner); VisageVar helper = makeVar(tree, helperName, init, VisageBindStatus.UNBOUND, helperType); //helper.sym.flags_field |= VisageFlags.VARMARK_BARE_SYNTH; clause.boundHelper = helper; // Fix up the class VisageClassDeclaration cdecl = (VisageClassDeclaration) decompose(body.stats.head); body.stats.head = cdecl; // Patch the type of the doit function patchDoitFunction(cdecl); // Patch the type of the anon{}.doit() call body.value.type = cdecl.type; //TODO: probably need to go deeper // Add VisageForPart as implemented interface -- VisageForPart<T> Type intfc = types.applySimpleGenericType(types.erasure(syms.visage_ForPartInterfaceType), inductionType); cdecl.setDifferentiatedExtendingImplementingMixing( List.<VisageExpression>nil(), List.<VisageExpression>of(visagemake.Type(intfc)), // implement interface List.<VisageExpression>nil()); result = visagemake.at(tree.pos).ForExpression(List.of(clause), body); } else { List<VisageForExpressionInClause> inClauses = decompose(tree.inClauses); VisageExpression bodyExpr = decompose(tree.bodyExpr); result = visagemake.at(tree.pos).ForExpression(inClauses, bodyExpr); } } private void patchDoitFunction(VisageClassDeclaration cdecl) { Type ctype = cdecl.type; for (VisageTree mem : cdecl.getMembers()) { if (mem.getVisageTag() == VisageTag.FUNCTION_DEF) { VisageFunctionDefinition func = (VisageFunctionDefinition) mem; if ((func.sym.flags() & VisageFlags.FUNC_SYNTH_LOCAL_DOIT) != 0L) { // Change the value to be "this" VisageBlock body = func.getBodyExpression(); body.value = visagemake.This(ctype); body.type = ctype; // Adjust function to return class type final MethodType funcType = new MethodType( List.<Type>nil(), // arg types ctype, // return type List.<Type>nil(), // Throws type syms.methodClass); // TypeSymbol func.sym.type = funcType; func.type = funcType; } } } } public void visitForExpressionInClause(VisageForExpressionInClause tree) { tree.seqExpr = decompose(tree.seqExpr); tree.setWhereExpr(decompose(tree.getWhereExpression())); result = tree; } public void visitIndexof(VisageIndexof tree) { result = tree.clause.indexVarSym == null ? tree : visagemake.Ident(tree.clause.indexVarSym); } public void visitTimeLiteral(VisageTimeLiteral tree) { result = tree; } public void visitLengthLiteral(VisageLengthLiteral tree) { result = tree; } public void visitAngleLiteral(VisageAngleLiteral tree) { result = tree; } public void visitColorLiteral(VisageColorLiteral tree) { result = tree; } public void visitOverrideClassVar(VisageOverrideClassVar tree) { boolean wasInScriptLevel = inScriptLevel; inScriptLevel = tree.isStatic(); VisageBindStatus prevBindStatus = bindStatus; VisageVarSymbol prevVarSymbol = currentVarSymbol; currentVarSymbol = tree.sym; // on-replace is always unbound bindStatus = VisageBindStatus.UNBOUND; VisageOnReplace onReplace = decompose(tree.getOnReplace()); VisageOnReplace onInvalidate = decompose(tree.getOnInvalidate()); // bound if was bind context or is bound variable bindStatus = tree.isBound()? tree.getBindStatus() : prevBindStatus; VisageExpression initializer = shredUnlessIdent(tree.getInitializer()); VisageOverrideClassVar res = visagemake.at(tree.pos).OverrideClassVar(tree.getName(), tree.getVisageType(), tree.getModifiers(), tree.getId(), initializer, tree.getBindStatus(), onReplace, onInvalidate); res.sym = tree.sym; bindStatus = prevBindStatus; currentVarSymbol = prevVarSymbol; inScriptLevel = wasInScriptLevel; result = res; } public void visitInterpolateValue(VisageInterpolateValue tree) { VisageBindStatus prevBindStatus = bindStatus; bindStatus = VisageBindStatus.UNBOUND; VisageExpression attr = decompose(tree.attribute); VisageExpression funcValue = decompose(tree.funcValue); VisageExpression interpolation = decompose(tree.interpolation); // Note: funcValue takes the place of value VisageInterpolateValue res = visagemake.at(tree.pos).InterpolateValue(attr, funcValue, interpolation); res.sym = tree.sym; result = res; bindStatus = prevBindStatus; } public void visitKeyFrameLiteral(VisageKeyFrameLiteral tree) { VisageExpression start = decompose(tree.start); List<VisageExpression> values = decomposeComponents(tree.values); VisageExpression trigger = decompose(tree.trigger); result = visagemake.at(tree.pos).KeyFrameLiteral(start, values, trigger); } }