/* * 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.api.tree.TypeTree.Cardinality; import org.visage.tools.code.FunctionType; import org.visage.tools.code.VisageClassSymbol; 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.tree.*; import com.sun.tools.mjavac.code.Flags; import com.sun.tools.mjavac.code.Kinds; import com.sun.tools.mjavac.code.Scope; import com.sun.tools.mjavac.code.Symbol; import com.sun.tools.mjavac.code.Symbol.*; import com.sun.tools.mjavac.code.Type; import com.sun.tools.mjavac.code.Type.ClassType; import static com.sun.tools.mjavac.code.TypeTags.*; import com.sun.tools.mjavac.util.Context; import com.sun.tools.mjavac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.mjavac.util.List; import com.sun.tools.mjavac.util.Name; import com.sun.tools.mjavac.util.Options; import javax.tools.JavaFileObject; /** * Shared support for the pre-translation passes. Not a pass itself. * * @author Maurizio Cimadamore * @author Robert Field */ public class VisagePreTranslationSupport { private final VisageTreeMaker visagemake; private final VisageDefs defs; private final Name.Table names; private final VisageCheck chk; private final VisageTypes types; private final VisageSymtab syms; private final VisageOptimizationStatistics optStat; private final boolean debugNames; private int tmpCount = 0; protected static final Context.Key<VisagePreTranslationSupport> preTranslation = new Context.Key<VisagePreTranslationSupport>(); public static VisagePreTranslationSupport instance(Context context) { VisagePreTranslationSupport instance = context.get(preTranslation); if (instance == null) { instance = new VisagePreTranslationSupport(context); } return instance; } private VisagePreTranslationSupport(Context context) { context.put(preTranslation, this); visagemake = VisageTreeMaker.instance(context); defs = VisageDefs.instance(context); names = Name.Table.instance(context); chk = VisageCheck.instance(context); types = VisageTypes.instance(context); syms = (VisageSymtab)VisageSymtab.instance(context); optStat = VisageOptimizationStatistics.instance(context); String opt = Options.instance(context).get("debugNames"); debugNames = opt != null && !opt.startsWith("n"); } // Just adds a counter. prefix is expected to include "$" public Name syntheticName(String prefix) { return names.fromString(prefix + tmpCount++); } public VisageExpression defaultValue(Type type) { VisageExpression res; if (types.isSequence(type)) { res = visagemake.EmptySequence(); } else { switch (type.tag) { case FLOAT: res = visagemake.Literal(0F); break; case DOUBLE: res = visagemake.Literal(0.0); break; case CHAR: res = visagemake.Literal((char) 0); break; case BYTE: res = visagemake.Literal((byte) 0); break; case SHORT: res = visagemake.Literal((short) 0); break; case INT: res = visagemake.Literal((int) 0); break; case LONG: res = visagemake.Literal(0L); break; case BOOLEAN: res = visagemake.Literal(false); break; default: res = visagemake.Literal(BOT, null); } } res.type = type; return res; } public Scope getEnclosingScope(Symbol s) { if (s.owner.kind == Kinds.TYP) { return ((ClassSymbol)s.owner).members(); } else if (s.owner.kind == Kinds.PCK) { return ((PackageSymbol)s.owner).members(); } else return null; } public JavaFileObject sourceFile(Symbol owner) { for (Symbol currOwner = owner; currOwner != null; currOwner = currOwner.owner) { if (currOwner instanceof ClassSymbol) { JavaFileObject src = ((ClassSymbol)currOwner).sourcefile; if (src != null) { return src; } } } return null; } public VisageClassSymbol makeClassSymbol(Name name, Symbol owner) { VisageClassSymbol classSym = new VisageClassSymbol(Flags.SYNTHETIC, name, owner); classSym.flatname = chk.localClassName(classSym); chk.compiled.put(classSym.flatname, classSym); // we may be able to get away without any scope stuff // s.enter(sym); // Fill out class fields. classSym.completer = null; if (classSym.owner instanceof MethodSymbol && (classSym.owner.flags() & VisageFlags.BOUND) != 0L) { classSym.flags_field |= VisageFlags.VISAGE_BOUND_FUNCTION_CLASS; } classSym.sourcefile = sourceFile(owner); classSym.members_field = new Scope(classSym); ClassType ct = (ClassType) classSym.type; // We are seeing a local or inner class. // Set outer_field of this class to closest enclosing class // which contains this class in a non-static context // (its "enclosing instance class"), provided such a class exists. Symbol owner1 = owner.enclClass(); if (owner1.kind == Kinds.TYP) { ct.setEnclosingType(owner1.type); } ct.supertype_field = syms.visage_BaseType; return classSym; } public MethodSymbol makeDummyMethodSymbol(Symbol owner) { return makeDummyMethodSymbol(owner, names.empty); } public MethodSymbol makeDummyMethodSymbol(Symbol owner, Name name) { return new MethodSymbol(Flags.BLOCK, name, null, owner.enclClass()); } VisageType makeTypeTree(Type type) { Type elemType = types.elementTypeOrType(type); VisageExpression typeExpr = visagemake.Type(elemType).setType(elemType); VisageTreeInfo.setSymbol(typeExpr, elemType.tsym); return (VisageType)visagemake.TypeClass(typeExpr, types.isSequence(type) ? Cardinality.ANY : Cardinality.SINGLETON, (ClassSymbol)type.tsym).setType(type); } VisageVar BoundLocalVar(DiagnosticPosition diagPos, Type type, Name name, VisageExpression boundExpr, Symbol owner) { return Var(diagPos, VisageFlags.IS_DEF, type, name, VisageBindStatus.UNIDIBIND, boundExpr, owner); } VisageVar LocalVar(DiagnosticPosition diagPos, Type type, Name name, VisageExpression expr, Symbol owner) { return Var(diagPos,0L, type, name, VisageBindStatus.UNBOUND, expr, owner); } private static final String idChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private String suffixGen() { final int dig = idChars.length(); int i = ++tmpCount; StringBuffer sb = new StringBuffer(); while (i > 0) { int md = i % dig; char ch = idChars.charAt(md); sb.append(ch); i -= md; i = i / dig; } return sb.toString(); } VisageVar SynthVar(DiagnosticPosition diagPos, VisageVarSymbol vsymParent, String id, VisageExpression initExpr, VisageBindStatus bindStatus, Type type, boolean inScriptLevel, Symbol owner) { optStat.recordSynthVar(id); String ns = "_$" + suffixGen(); if (debugNames) { ns = (vsymParent==null? "" : vsymParent.toString() + "$") + id + ns; } Name name = names.fromString(ns); long flags = VisageFlags.SCRIPT_PRIVATE | Flags.SYNTHETIC | (inScriptLevel ? Flags.STATIC | VisageFlags.SCRIPT_LEVEL_SYNTH_STATIC : 0L); VisageVar var = Var(diagPos, flags, types.normalize(type), name, bindStatus, initExpr, owner); owner.members().enter(var.sym); return var; } VisageVar Var(DiagnosticPosition diagPos, long flags, Type type, Name name, VisageBindStatus bindStatus, VisageExpression expr, Symbol owner) { VisageVarSymbol vsym = new VisageVarSymbol( types, names, flags, name, type, owner); VisageVar var = visagemake.at(diagPos).Var( name, makeTypeTree(vsym.type), visagemake.at(diagPos).Modifiers(flags), expr, bindStatus, null, null); var.type = vsym.type; var.sym = vsym; return var; } VisageExpression makeCastIfNeeded(VisageExpression tree, Type type) { if (type == Type.noType || type == null || type.isErroneous() || type == syms.voidType || tree.type == syms.voidType || tree.type == syms.unreachableType || type == syms.unreachableType) return tree; else { tree = makeNumericBoxConversionIfNeeded(tree, type); return !types.isSameType(tree.type, type) && (!types.isSubtypeUnchecked(tree.type, type) || (tree.type.isPrimitive() && type.isPrimitive() || (types.isSameType(tree.type, syms.visage_EmptySequenceType) && types.isSequence(type)))) ? makeCast(tree, type) : tree; } } /** * It is necessary to add an extra cast if either source type or target type * is a boxed Java type - this is required because we might want to go from * java.Lang.Long to int and vice-versa */ private boolean needNumericBoxConversion(VisageExpression tree, Type type) { boolean sourceIsPrimitive = tree.type.isPrimitive(); boolean targetIsPrimitive = type.isPrimitive(); Type unboxedSource = types.unboxedType(tree.type); Type unboxedTarget = types.unboxedType(type); return (sourceIsPrimitive && !targetIsPrimitive && unboxedTarget != Type.noType && !types.isSameType(unboxedTarget, tree.type)) || (targetIsPrimitive && !sourceIsPrimitive && unboxedSource != Type.noType && !types.isSameType(unboxedSource, type)) || (!sourceIsPrimitive && !targetIsPrimitive && unboxedTarget != Type.noType && unboxedSource!= Type.noType && !types.isSameType(type, tree.type)); } private VisageExpression makeNumericBoxConversionIfNeeded(VisageExpression tree, Type type) { if (needNumericBoxConversion(tree, type)) { //either tree.type or type is primitive! if (tree.type.isPrimitive() && !type.isPrimitive()) { return makeCast(tree, types.unboxedType(type)); } else if (type.isPrimitive() && !tree.type.isPrimitive()) { return makeCast(tree, types.unboxedType(tree.type)); } else { //both are boxed types return makeCast(makeCast(tree, types.unboxedType(tree.type)), types.unboxedType(type)); } } else { return tree; } } private VisageExpression makeCast(VisageExpression tree, Type type) { VisageExpression typeTree = makeTypeTree(type); VisageExpression expr = visagemake.at(tree.pos).TypeCast(typeTree, tree); expr.type = type; return expr; } void liftTypes(final VisageClassDeclaration cdecl, final Type newEncl, final Symbol newOwner) { class NestedClassTypeLifter extends VisageTreeScanner { @Override public void visitClassDeclaration(VisageClassDeclaration that) { super.visitClassDeclaration(that); if (that.sym != newEncl.tsym && (that.type.getEnclosingType() == Type.noType || that.type.getEnclosingType().tsym == newEncl.getEnclosingType().tsym)) { Scope oldScope = getEnclosingScope(that.sym); if (oldScope != null) oldScope.remove(that.sym); ((ClassType)that.type).setEnclosingType(newEncl); that.sym.owner = newOwner; newEncl.tsym.members().enter(that.sym); } } } new NestedClassTypeLifter().scan(cdecl); } Symbol makeSyntheticBuiltinsMethod(Name name) { return new MethodSymbol( Flags.PUBLIC | Flags.STATIC | VisageFlags.FUNC_IS_BUILTINS_SYNTH, name, new Type.MethodType( List.of(syms.visage_ObjectType, syms.intType), syms.booleanType, List.<Type>nil(), syms.methodClass), syms.visage_AutoImportRuntimeType.tsym); } Symbol makeSyntheticPointerMake() { return new MethodSymbol( Flags.PUBLIC | Flags.STATIC | VisageFlags.FUNC_POINTER_MAKE, defs.Pointer_make.methodName, new Type.MethodType( List.of(syms.visage_ObjectType, syms.intType, types.erasure(syms.classType)), syms.visage_PointerType, List.<Type>nil(), syms.methodClass), syms.visage_PointerType.tsym); } Name makeUniqueVarNameIn(Name name, Symbol owner) { while (owner.members().lookup(name).sym != null) { name = name.append('$', name); } return name; } boolean isNullable(VisageExpression expr) { if (!types.isNullable(expr.type)) { return false; } while (true) { switch (expr.getVisageTag()) { case OBJECT_LITERAL: return false; case PARENS: expr = ((VisageParens)expr).getExpression(); break; case BLOCK_EXPRESSION: expr = ((VisageBlock)expr).getValue(); break; case CONDEXPR: { VisageIfExpression ife = (VisageIfExpression)expr; return isNullable(ife.getTrueExpression()) || isNullable(ife.getFalseExpression()); } default: return true; } } } //TODO: unify with hasSideEffects in TranslationSupport boolean hasSideEffectsInBind(VisageExpression expr) { class SideEffectScanner extends VisageTreeScanner { boolean hse = false; private void markSideEffects() { hse = true; } @Override public void visitAssign(VisageAssign tree) { // In case we add back assignment markSideEffects(); } @Override public void visitInstanciate(VisageInstanciate tree) { markSideEffects(); } @Override public void visitFunctionInvocation(VisageFunctionInvocation tree) { markSideEffects(); } } SideEffectScanner scanner = new SideEffectScanner(); scanner.scan(expr); return scanner.hse; } boolean isImmutable(List<VisageExpression> trees) { for (VisageExpression item : trees) { if (!isImmutable(item)) { return false; } } return true; } boolean isImmutable(VisageExpression tree) { //TODO: add for-loop, sequence indexed, string expression switch (tree.getVisageTag()) { case IDENT: { VisageIdent id = (VisageIdent) tree; return isImmutable(id.sym, id.getName()); } case SELECT: { VisageSelect sel = (VisageSelect) tree; return (sel.sym.isStatic() || isImmutable(sel.getExpression())) && isImmutable(sel.sym, sel.getIdentifier()); } case LITERAL: case TIME_LITERAL: case SEQUENCE_EMPTY: return true; case PARENS: return isImmutable(((VisageParens)tree).getExpression()); case BLOCK_EXPRESSION: { VisageBlock be = (VisageBlock) tree; for (VisageExpression stmt : be.getStmts()) { //TODO: OPT probably many false positive cases if (stmt instanceof VisageVar) { VisageVar var = (VisageVar) stmt; if (!isImmutable(var.getInitializer())) { return false; } } else { return false; } } return isImmutable(be.getValue()); } case FOR_EXPRESSION: { VisageForExpression fe = (VisageForExpression) tree; for (VisageForExpressionInClause clause : fe.getForExpressionInClauses()) { if (!isImmutable(clause.getSequenceExpression())) { return false; } if (clause.getWhereExpression() != null && !isImmutable(clause.getWhereExpression())) { return false; } } return isImmutable(fe.getBodyExpression()); } case APPLY: { VisageFunctionInvocation finv = (VisageFunctionInvocation) tree; VisageExpression meth = finv.meth; Symbol refSym = VisageTreeInfo.symbol(meth); return isImmutable(meth) && // method being called won't change isImmutable(finv.getArguments()) && // arguments won't change !(meth.type instanceof FunctionType) && // not a function value call -- over-cautious (refSym instanceof MethodSymbol) && // call to a method, protects the next check -- over-cautious (refSym.flags() & VisageFlags.BOUND) == 0; // and isn't a call to a bound function } case CONDEXPR: { VisageIfExpression ife = (VisageIfExpression) tree; return isImmutable(ife.getCondition()) && isImmutable(ife.getTrueExpression()) && isImmutable(ife.getFalseExpression()); } case SEQUENCE_RANGE: { VisageSequenceRange rng = (VisageSequenceRange) tree; return isImmutable(rng.getLower()) && isImmutable(rng.getUpper()) && (rng.getStepOrNull()==null || isImmutable(rng.getStepOrNull())); } case SEQUENCE_EXPLICIT: { VisageSequenceExplicit se = (VisageSequenceExplicit) tree; for (VisageExpression item : se.getItems()) { if (!isImmutable(item)) { return false; } } return true; } case TYPECAST: { VisageTypeCast tc = (VisageTypeCast) tree; return isImmutable(tc.getExpression()); } default: if (tree instanceof VisageUnary) { if (tree.getVisageTag().isIncDec()) { return false; } else { return isImmutable(((VisageUnary) tree).getExpression()); } } else if (tree instanceof VisageBinary) { VisageBinary b = (VisageBinary) tree; return isImmutable(b.lhs) && isImmutable(b.rhs); } else { return false; } } } private boolean isImmutable(Symbol sym, Name name) { if (sym.kind != Kinds.VAR) { return true; } VisageVarSymbol vsym = (VisageVarSymbol) sym; Symbol owner = sym.owner; // Note: cannot depend on vsym.canChange() until after the Variable Usage Analysis faze is complete // (so to be correct we don't check this right now) return name == names._this || name == names._super || (owner instanceof VisageClassSymbol && name == visagemake.ScriptAccessSymbol(owner).name); } }