/*
* 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 java.util.HashMap;
import java.util.Map;
import com.sun.tools.mjavac.code.*;
import com.sun.tools.mjavac.code.Type.MethodType;
import com.sun.tools.mjavac.code.Symbol;
import com.sun.tools.mjavac.code.Symbol.*;
import com.sun.tools.mjavac.tree.JCTree;
import com.sun.tools.mjavac.tree.JCTree.*;
import com.sun.tools.mjavac.util.Context;
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.JCDiagnostic.DiagnosticPosition;
import org.visage.tools.code.VisageVarSymbol;
import org.visage.tools.comp.VisageAbstractTranslation.Translator;
import org.visage.tools.comp.VisageAnalyzeClass.*;
import static org.visage.tools.comp.VisageDefs.*;
import org.visage.tools.comp.VisageInitializationBuilder.*;
import org.visage.tools.tree.*;
import static org.visage.tools.comp.VisageAbstractTranslation.Yield.*;
/**
* Translate Visage ASTs into Java ASTs
*
* @author Robert Field
* @author Per Bothner
* @author Lubo Litchev
*/
public class VisageToJava extends VisageAbstractTranslation {
protected static final Context.Key<VisageToJava> visageToJavaKey =
new Context.Key<VisageToJava>();
/*
* modules imported by context
*/
private final VisageInitializationBuilder initBuilder;
private final VisageTranslateBind translateBind;
private final VisageTranslateInvBind translateInvBind;
/*
* Buffers holding definitions waiting to be prepended to the current list of definitions.
* At class or top-level these are the same.
* Within a method (or block) prependToStatements is split off.
* They need to be different because anonymous classes need to be declared in the scope of the method,
* but interfaces can't be declared here.
*/
private ListBuffer<JCStatement> prependToDefinitions = null;
private ListBuffer<JCStatement> prependToStatements = null;
private ListBuffer<JCExpression> additionalImports = null;
// Stack used to track literal symbols for the current class.
private Map<Symbol, Name> substitutionMap = new HashMap<Symbol, Name>();
// Stack used to track literal symbols for the current class.
private LiteralInitClassMap literalInitClassMap = null;
/** Class symbols for classes that need a reference to the outer class. */
private final Map<ClassSymbol, ClassSymbol> hasOuters = new HashMap<ClassSymbol, ClassSymbol>();
private VisageEnv<VisageAttrContext> attrEnv;
ReceiverContext inInstanceContext = ReceiverContext.Oops;
private DependencyGraphWriter depGraphWriter;
/*
* Sole instance creation
*/
public static VisageToJava instance(Context context) {
VisageToJava instance = context.get(visageToJavaKey);
if (instance == null)
instance = new VisageToJava(context);
return instance;
}
protected VisageToJava(Context context) {
super(context, null);
context.put(visageToJavaKey, this);
this.initBuilder = VisageInitializationBuilder.instance(context);
this.translateBind = VisageTranslateBind.instance(context);
this.translateInvBind = VisageTranslateInvBind.instance(context);
this.depGraphWriter = DependencyGraphWriter.instance(context);
}
/**
* Entry point
*/
public void toJava(VisageEnv<VisageAttrContext> attrEnv) {
this.setAttrEnv(attrEnv);
attrEnv.translatedToplevel = (JCCompilationUnit)((SpecialResult)translateToSpecialResult(attrEnv.toplevel)).tree();
attrEnv.translatedToplevel.endPositions = attrEnv.toplevel.endPositions;
}
/**
* For special cases where the expression may not be fully attributed.
* Specifically: package and import names.
* Do a dumb simple conversion.
*/
private JCExpression straightConvert(VisageExpression tree) {
if (tree == null) {
return null;
}
DiagnosticPosition diagPos = tree.pos();
switch (tree.getVisageTag()) {
case IDENT: {
VisageIdent id = (VisageIdent) tree;
return make.at(diagPos).Ident(id.getName());
}
case SELECT: {
VisageSelect sel = (VisageSelect) tree;
return make.at(diagPos).Select(
straightConvert(sel.getExpression()),
sel.getIdentifier());
}
default:
throw new RuntimeException("bad conversion");
}
}
private boolean substitute(DiagnosticPosition diagPos, final Symbol sym) {
final Name name = getSubstitutionMap().get(sym);
if (name == null) {
return false;
} else {
result = new ExpressionTranslator(diagPos) {
protected ExpressionResult doit() {
return toResult(id(name), sym.type);
}
}.doit();
return true;
}
}
private void setSubstitution(VisageTree target, Symbol sym) {
if (target instanceof VisageInstanciate) {
// Set up to substitute a reference to the this var within its definition
((VisageInstanciate) target).varDefinedByThis = sym;
}
}
/**
* @return the attrEnv
*/
@Override
protected VisageEnv<VisageAttrContext> getAttrEnv() {
return attrEnv;
}
@Override
protected ReceiverContext receiverContext() {
return inInstanceContext;
}
@Override
protected void setReceiverContext(ReceiverContext rc) {
inInstanceContext = rc;
}
@Override
protected VisageToJava toJava() {
return this;
}
/**
* @param attrEnv the attrEnv to set
*/
public void setAttrEnv(VisageEnv<VisageAttrContext> attrEnv) {
this.attrEnv = attrEnv;
}
/**
* @return the substitutionMap
*/
@Override
Map<Symbol, Name> getSubstitutionMap() {
return substitutionMap;
}
/**
* Class symbols for classes that need a reference to the outer class.
*/
@Override
Map<ClassSymbol, ClassSymbol> getHasOuters() {
return hasOuters;
}
/**
* @return the literalInitClassMap
*/
@Override
LiteralInitClassMap getLiteralInitClassMap() {
return literalInitClassMap;
}
/**
* @param literalInitClassMap the literalInitClassMap to set
*/
private void setLiteralInitClassMap(LiteralInitClassMap literalInitClassMap) {
this.literalInitClassMap = literalInitClassMap;
}
/**
* Make a version of the on-replace to be used in inline in a setter.
*/
private JCStatement translateTriggerAsInline(VisageVarSymbol vsym, VisageOnReplace onReplace) {
if (onReplace == null) return null;
boolean isSequence = vsym.isSequence();
if (isSequence) {
OnReplaceInfo info = new OnReplaceInfo();
info.onReplace = onReplace;
info.vsym = vsym;
info.outer = onReplaceInfo;
if (onReplace.getNewElements() != null)
info.newElementsSym = onReplace.getNewElements().sym;
onReplaceInfo = info;
}
JCStatement ret = translateToStatement(onReplace.getBody(), syms.voidType);
if (isSequence)
onReplaceInfo = onReplaceInfo.outer;
return ret;
}
void scriptBegin() {
}
private class ClassDeclarationTranslator extends Translator {
private final VisageClassDeclaration tree;
private final boolean isMixinClass;
private final ListBuffer<JCTree> translatedDefs = ListBuffer.lb();
ClassDeclarationTranslator(VisageClassDeclaration tree) {
super(tree.pos());
this.tree = tree;
isMixinClass = tree.isMixinClass();
}
protected StatementsResult doit() {
final ListBuffer<JCStatement> translatedInitBlocks = ListBuffer.lb();
final ListBuffer<JCStatement> translatedPostInitBlocks = ListBuffer.lb();
ListBuffer<TranslatedVarInfo> attrInfo = ListBuffer.lb();
ListBuffer<TranslatedOverrideClassVarInfo> overrideInfo = ListBuffer.lb();
ListBuffer<TranslatedFuncInfo> funcInfo = ListBuffer.lb();
ReceiverContext prevReceiverContext = receiverContext();
// translate all the definitions that make up the class.
// collect any prepended definitions, and prepend then to the tranlations
ListBuffer<JCStatement> prevPrependToDefinitions = prependToDefinitions;
ListBuffer<JCStatement> prevPrependToStatements = prependToStatements;
prependToStatements = prependToDefinitions = ListBuffer.lb();
{
for (VisageTree def : tree.getMembers()) {
switch (def.getVisageTag()) {
case INIT_DEF: {
setContext(false);
translateAndAppendStaticBlock(((VisageInitDefinition) def).getBody(), translatedInitBlocks);
inInstanceContext = ReceiverContext.Oops;
break;
}
case POSTINIT_DEF: {
setContext(false);
translateAndAppendStaticBlock(((VisagePostInitDefinition) def).getBody(), translatedPostInitBlocks);
inInstanceContext = ReceiverContext.Oops;
break;
}
case VAR_DEF: {
VisageVar attrDef = (VisageVar) def;
setContext(attrDef.isStatic());
VisageExpression initializer = attrDef.getInitializer();
boolean initWithBoundFuncResult =
(initializer instanceof VisageIdent) &&
isBoundFunctionResult(((VisageIdent)initializer).sym);
ExpressionResult bindResult = translateBind(attrDef);
TranslatedVarInfo ai = new TranslatedVarInfo(
attrDef,
translateVarInit(attrDef),
initWithBoundFuncResult? ((VisageIdent)initializer).sym : null,
bindResult,
attrDef.getOnReplace(),
translateTriggerAsInline(attrDef.sym, attrDef.getOnReplace()),
attrDef.getOnInvalidate(),
translateTriggerAsInline(attrDef.sym, attrDef.getOnInvalidate()));
attrInfo.append(ai);
break;
}
case OVERRIDE_ATTRIBUTE_DEF: {
VisageOverrideClassVar override = (VisageOverrideClassVar) def;
setContext(override.isStatic());
VisageExpression initializer = override.getInitializer();
boolean initWithBoundFuncResult =
(initializer instanceof VisageIdent) &&
isBoundFunctionResult(((VisageIdent)initializer).sym);
ExpressionResult bindResult = translateBind(override);
TranslatedOverrideClassVarInfo ai = new TranslatedOverrideClassVarInfo(
override,
translateVarInit(override),
initWithBoundFuncResult? ((VisageIdent)initializer).sym : null,
bindResult,
override.getOnReplace(),
translateTriggerAsInline(override.sym, override.getOnReplace()),
override.getOnInvalidate(),
translateTriggerAsInline(override.sym, override.getOnInvalidate()));
overrideInfo.append(ai);
break;
}
case FUNCTION_DEF: {
VisageFunctionDefinition funcDef = (VisageFunctionDefinition) def;
setContext(funcDef.isStatic());
funcInfo.append(new TranslatedFuncInfo(funcDef, translateToSpecialResult(funcDef).trees()));
break;
}
case CLASS_DEF: {
// Handle other classes.
inInstanceContext = ReceiverContext.Oops;
translatedDefs.appendList(translateToStatementsResult((VisageClassDeclaration)def, syms.voidType).trees());
break;
}
default: {
assert false : "Unexpected class member: " + def;
inInstanceContext = ReceiverContext.Oops;
translatedDefs.appendList(translateToSpecialResult(def).trees());
break;
}
}
}
}
inInstanceContext = ReceiverContext.Oops;
// the translated defs have prepends in front
for (JCTree prepend : prependToDefinitions) {
translatedDefs.prepend(prepend);
}
inInstanceContext = ReceiverContext.Oops;
prependToDefinitions = prevPrependToDefinitions;
prependToStatements = prevPrependToStatements;
// WARNING: translate can't be called directly or indirectly after this point in the method, or the prepends won't be included
VisageClassModel model = initBuilder.createVisageClassModel(tree,
attrInfo.toList(),
overrideInfo.toList(),
funcInfo.toList(),
getLiteralInitClassMap(),
translatedInitBlocks,
translatedPostInitBlocks);
additionalImports.appendList(model.additionalImports);
translatedDefs.appendList(model.additionalClassMembers);
// build the list of implemented interfaces
List<JCExpression> implementing = model.interfaces;
// include the interface only once
if (!tree.hasBeenTranslated) {
if (isMixinClass) {
JCModifiers mods = m().Modifiers(Flags.PUBLIC | Flags.INTERFACE);
mods = addAccessAnnotationModifiers(diagPos, tree.mods.flags, mods);
JCClassDecl cInterface = m().ClassDef(mods,
model.interfaceName, List.<JCTypeParameter>nil(), null,
implementing, model.iDefinitions);
cInterface.sym = makeClassSymbol(mods.flags, cInterface.name, tree.sym.owner);
membersToSymbol(cInterface);
prependToDefinitions.append(cInterface); // prepend to the enclosing class or top-level
}
tree.hasBeenTranslated = true;
}
// Class must be visible for reflection.
long flags = tree.mods.flags & (Flags.FINAL | Flags.ABSTRACT | Flags.SYNTHETIC);
if ((flags & Flags.SYNTHETIC) == 0) {
flags |= Flags.PUBLIC;
}
if (tree.sym.owner.kind == Kinds.TYP) {
flags |= Flags.STATIC;
}
// VSGC-2831 - Mixins should be abstract.
if (tree.sym.kind == Kinds.TYP && isMixinClass) {
flags |= Flags.ABSTRACT;
}
JCModifiers classMods = make.at(diagPos).Modifiers(flags);
classMods = addAccessAnnotationModifiers(diagPos, tree.mods.flags, classMods);
// make the Java class corresponding to this Visage class, and return it
JCClassDecl res = m().ClassDef(
classMods,
tree.getName(),
List.<JCTypeParameter>nil(), // type parameters
model.superType == null ? null : makeType(model.superType, false),
implementing,
translatedDefs.toList());
res.sym = tree.sym;
res.type = tree.type;
membersToSymbol(res);
setReceiverContext(prevReceiverContext);
return new StatementsResult(res);
}
private void setContext(boolean isStatic) {
setReceiverContext( isStatic ?
ReceiverContext.ScriptAsStatic
: isMixinClass ?
ReceiverContext.InstanceAsStatic
: ReceiverContext.InstanceAsInstance );
}
private ExpressionResult translateBind(VisageAbstractVar var) {
return var.isBidiBind() ?
translateInvBind.translate(var.getInitializer(), var.type, var.sym) :
var.isBound() ?
translateBind.translateBoundExpression(var.getInitializer(), var.sym) :
null;
}
private JCExpression translateVarInit(VisageAbstractVar var) {
if (var.getInitializer()==null || var.isBound()) {
// no init, or init handled by bind or VisageVarInit
return null;
}
Name instanceName = (var.isStatic() || !isMixinClass) ? null : defs.receiverName;
return translateInitExpression(
var.pos(),
var.getInitializer(),
var.sym,
instanceName
).expr();
}
private void translateAndAppendStaticBlock(VisageBlock block, ListBuffer<JCStatement> translatedBlocks) {
JCStatement stmt = translateToStatement(block, syms.voidType);
if (stmt != null) {
translatedBlocks.append(stmt);
}
}
}
private JCExpression translateNonBoundInit(DiagnosticPosition diagPos,
VisageExpression init,
VisageVarSymbol vsym) {
// normal init -- unbound
if (init == null) {
// no initializing expression determine a default value from the type
return makeDefaultValue(diagPos, vsym);
} else {
// do a vanilla translation of the expression
Type resultType = vsym.type;
JCExpression trans = translateToExpression(init, resultType);
return convertNullability(diagPos, trans, init, resultType);
}
}
private ExpressionResult translateDefinitionalAssignmentToSetExpression(final DiagnosticPosition diagPos,
final VisageExpression init,
final VisageVarSymbol vsym,
final Name instanceName) {
class DefinitionalAssignmentTranslator extends ExpressionTranslator {
DefinitionalAssignmentTranslator(DiagnosticPosition diagPos) { super(diagPos); }
protected ExpressionResult doit() {
assert !vsym.isParameter() : "Parameters are not initialized";
setSubstitution(init, vsym);
final JCExpression nonNullInit = translateNonBoundInit(diagPos, init, vsym);
final boolean isLocal = !vsym.isMember();
assert !isLocal || instanceName == null;
JCExpression res;
if (vsym.useAccessors()) {
if (vsym.isMember() && vsym.isSequence()) {
JCExpression tc =
instanceName == null ? getReceiverOrThis(vsym) : id(instanceName);
res = Call(defs.Sequences_set, tc, Offset(vsym), nonNullInit);
} else {
JCExpression tc = instanceName == null ? null : id(instanceName);
res = Setter(tc, vsym, nonNullInit);
}
} else {
res = nonNullInit;
if (vsym.isSequence())
res = Call(defs.Sequences_incrementSharing, res);
res = Set(vsym, res);
}
return toResult(nonNullInit, vsym.type);
}
}
return new DefinitionalAssignmentTranslator(diagPos).doit();
}
private ExpressionResult translateInitExpression(final DiagnosticPosition diagPos,
final VisageExpression init,
final VisageVarSymbol vsym,
final Name instanceName) {
class InitTranslator extends ExpressionTranslator {
InitTranslator(DiagnosticPosition diagPos) { super(diagPos); }
protected ExpressionResult doit() {
assert !vsym.isParameter() : "Parameters are not initialized";
setSubstitution(init, vsym);
final JCExpression nonNullInit = translateNonBoundInit(diagPos, init, vsym);
final boolean isLocal = !vsym.isMember();
assert !isLocal || instanceName == null;
return toResult(nonNullInit, vsym.type);
}
}
return new InitTranslator(diagPos).doit();
}
/**
* Translate a local variable
*/
private class VarTranslator extends ExpressionTranslator {
final VisageVar tree;
final VisageVarSymbol vsym;
final long modFlags;
VarTranslator(VisageVar tree) {
super(tree.pos());
this.tree = tree;
VisageModifiers mods = tree.getModifiers();
vsym = tree.getSymbol();
assert !vsym.isMember() : "attributes are processed in the class and should never come here: " + tree.name;
assert !vsym.isParameter() : "we should not see parameters here" + tree.name;
modFlags = (mods.flags & ~Flags.FINAL) | (vsym.isMutatedWithinScript() ? 0L : Flags.FINAL);
}
protected AbstractStatementsResult doit() {
optStat.recordLocalVar(vsym, tree.getBindStatus().isBound(), false);
if (vsym.hasForwardReference()) {
// create a blank variable declaration and move the declaration to the beginning of the block
JCExpression init = makeDefaultValue(null, vsym);
prependToStatements.prepend(Var(modFlags, tree.type, tree.name, init));
return translateDefinitionalAssignmentToSetExpression(diagPos, tree.getInitializer(), vsym, null);
} else {
// Translate in-place
JCExpression init = translateNonBoundInit(diagPos, tree.getInitializer(), vsym);
if (vsym.isSequence())
init = Call(defs.Sequences_incrementSharing, init);
JCStatement var = Var(modFlags, tree.type, tree.name, init);
return new StatementsResult(var);
}
}
}
boolean isInnerFunction(MethodSymbol sym) {
return sym.owner != null && sym.owner.kind != Kinds.TYP && (sym.flags() & Flags.SYNTHETIC) == 0;
}
private class BlockExpressionTranslator extends ExpressionTranslator {
private final VisageExpression value;
private final List<VisageExpression> statements;
BlockExpressionTranslator(VisageBlock tree) {
super(tree.pos());
this.value = tree.value;
this.statements = tree.getStmts();
}
protected AbstractStatementsResult doit() {
ListBuffer<JCStatement> prevPrependToStatements = prependToStatements;
try {
prependToStatements = ListBuffer.lb();
for (VisageExpression expr : statements) {
JCStatement stmt = translateToStatement(expr, syms.voidType);
if (stmt != null) {
addPreface(stmt);
}
}
if (yield() == ToExpression) {
// make into block expression
//TODO: this may be unneeded, or even wrong
VisageExpression rawValue = (value.getVisageTag() == VisageTag.RETURN)?
((VisageReturn) value).getExpression()
: value;
JCExpression tvalue = translateExpr(rawValue, targetType); // must be before prepend
List<JCStatement> localDefs = prependToStatements.appendList(statements()).toList();
return new ExpressionResult(
diagPos,
List.<JCStatement>nil(), //TODO: statements rolled into expression (below) is this needed?
localDefs.size() == 0 ? tvalue : BlockExpression(localDefs, tvalue),
bindees(),
invalidators(),
interClass(),
setterPreface(),
targetType);
} else {
// make into block
if (value != null) {
if (value.getVisageTag() == VisageTag.VAR_SCRIPT_INIT && targetType != syms.voidType) {
translateStmt(value, syms.voidType);
addPreface(Stmt(Get(((VisageVarInit) value).getSymbol()), targetType));
} else {
translateStmt(value, targetType);
}
}
List<JCStatement> localDefs = prependToStatements.appendList(statements()).toList();
return new StatementsResult(localDefs.size() == 1 ? localDefs.head : Block(localDefs));
}
} finally {
prependToStatements = prevPrependToStatements;
}
}
}
class SequenceActionTranslator extends AssignTranslator {
final RuntimeMethod meth;
/**
*
* @param diagPos
* @param ref Variable being referenced (different from LHS if indexed -- where it is sequence or array)
* @param indexOrNull The index into the variable reference. Or null if not indexed.
* @param rhs The expression acting on ref or null
*/
SequenceActionTranslator(DiagnosticPosition diagPos, VisageExpression ref, RuntimeMethod meth, VisageExpression indexOrNull, VisageExpression rhs) {
this(diagPos, ref, meth, indexOrNull, syms.voidType, rhs);
}
SequenceActionTranslator(DiagnosticPosition diagPos, VisageExpression ref, RuntimeMethod meth, VisageExpression indexOrNull, Type fullType, VisageExpression rhs) {
super(diagPos, ref, indexOrNull, fullType, rhs);
this.meth = meth;
}
SequenceActionTranslator(DiagnosticPosition diagPos, VisageExpression ref, RuntimeMethod meth, VisageExpression indexOrNull) {
this(diagPos, ref, meth, indexOrNull, null);
}
@Override
JCExpression fullExpression(JCExpression tToCheck) {
return sequencesOp(meth, tToCheck);
}
@Override
JCExpression sequencesOp(RuntimeMethod meth, JCExpression tToCheck) {
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
VisageVarSymbol vsym = (VisageVarSymbol) refSym;
boolean useAccessor = vsym.useAccessors();
JCExpression tRHS = buildRHS(rhsTranslated);
JCVariableDecl newResult = null;
if (targetType != syms.voidType) {
newResult = TmpVar(rhsType(), tRHS);
tRHS = id(newResult);
}
if (! useAccessor) {
// Non-accessor-using variable sequence -- roughly:
// lhs = sequenceAction(lhs, rhs);
args.append(Getter(copyOfTranslatedToCheck(tToCheck), vsym));
} else {
// Instance variable sequence -- roughly:
// sequenceAction(instance, varNum, rhs);
args.append(instance(tToCheck));
args.append(Offset(copyOfTranslatedToCheck(tToCheck), vsym));
}
if (tRHS != null) {
args.append(tRHS);
}
JCExpression tIndex = translateIndex();
if (tIndex != null) {
args.append(tIndex);
JCExpression tIndex2 = translateIndex2();
if (tIndex2 != null) {
args.append(tIndex2);
}
}
JCExpression res = Call(meth, args);
if (! useAccessor) {
res = Setter(tToCheck, vsym, res);
}
if (newResult != null) {
res = BlockExpression(newResult, Stmt(res), id(newResult));
}
return res;
}
JCExpression translateIndex2() {
return null;
}
/**
* If we are operating on an array or sequence, convert to the sequence type.
* Otherwise, to the element type.
*/
@Override
protected Type rhsType() {
if (types.isArray(rhs.type) || types.isSequence(rhs.type))
return ref.type;
else
return types.elementType(ref.type);
}
}
class SequenceSliceActionTranslator extends SequenceActionTranslator {
private final VisageSequenceSlice slice;
SequenceSliceActionTranslator(VisageSequenceSlice slice, RuntimeMethod meth, Type fullType, VisageExpression rhs) {
super(slice.pos(), slice.getSequence(), meth, slice.getFirstIndex(), fullType, rhs);
this.slice = slice;
}
@Override
JCExpression translateIndex2() {
return makeSliceEndPos(slice);
}
}
class SequenceInsertTranslator extends SequenceActionTranslator {
VisageSequenceInsert tree;
public SequenceInsertTranslator(VisageSequenceInsert tree) {
super(
tree.pos(),
tree.getSequence(),
(tree.getPosition() == null)? defs.Sequences_insert : defs.Sequences_insertBefore,
tree.getPosition(),
tree.getElement());
this.tree = tree;
}
@Override
JCExpression translateIndex() {
if (indexOrNull == null) {
return null;
}
JCExpression position = translateExpr(indexOrNull, syms.intType);
if (tree.shouldInsertAfter()) {
position = PLUS(position, Int(1));
}
return position;
}
}
private class TryTranslator extends ExpressionTranslator {
private final VisageTry tree;
TryTranslator(VisageTry tree) {
super(tree.pos());
this.tree = tree;
}
protected StatementsResult doit() {
ListBuffer<JCCatch> tCatchers = ListBuffer.lb();
for (List<VisageCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) {
VisageCatch cat = l.head;
JCVariableDecl param = convertParam(cat.param);
JCBlock tCatBody = translateToBlock(cat.body, syms.voidType);
tCatchers.append(m().Catch(param, tCatBody));
}
JCBlock body = translateToBlock(tree.body, syms.voidType);
JCBlock finalizer = translateToBlock(tree.finalizer, syms.voidType);
return new StatementsResult(m().Try(body, tCatchers.toList(), finalizer));
}
}
private class WhileTranslator extends ExpressionTranslator {
private final VisageWhileLoop tree;
WhileTranslator(VisageWhileLoop tree) {
super(tree);
this.tree = tree;
}
protected StatementsResult doit() {
JCExpression cond = translateToExpression(tree.cond, syms.booleanType);
JCStatement body = translateToStatement(tree.body, syms.voidType);
return toStatementResult(m().WhileLoop(cond, body));
}
}
class ScriptTranslator extends Translator {
final VisageScript tree;
ScriptTranslator(VisageScript tree) {
super(tree.pos());
this.tree = tree;
}
SpecialResult doit() {
// add to the hasOuters set the class symbols for classes that need a reference to the outer class
fillClassesWithOuters(tree);
ListBuffer<JCTree> translatedDefinitions = ListBuffer.lb();
ListBuffer<JCTree> imports = ListBuffer.lb();
additionalImports = ListBuffer.lb();
prependToStatements = prependToDefinitions = ListBuffer.lb();
for (VisageTree def : tree.defs) {
switch (def.getVisageTag()) {
case IMPORT:
// ignore import
break;
case CLASS_DEF:
translatedDefinitions.appendList(translateToStatementsResult((VisageClassDeclaration) def, syms.voidType).trees());
break;
default:
assert false : "something wierd in the script: " + def;
break;
}
}
// order is imports, any prepends, then the translated non-imports
for (JCTree prepend : prependToDefinitions) {
translatedDefinitions.prepend(prepend);
}
for (JCTree prepend : imports) {
translatedDefinitions.prepend(prepend);
}
for (JCExpression prepend : additionalImports) {
translatedDefinitions.append(m().Import(prepend, false));
}
prependToStatements = prependToDefinitions = null; // shouldn't be used again until the next top level
JCExpression pid = straightConvert(tree.pid);
JCCompilationUnit translated = m().TopLevel(List.<JCAnnotation>nil(), pid, translatedDefinitions.toList());
translated.sourcefile = tree.sourcefile;
// System.err.println("<translated src="+tree.sourcefile+">"); System.err.println(translated); System.err.println("</translated>");
translated.docComments = null;
translated.lineMap = tree.lineMap;
translated.flags = tree.flags;
return new SpecialResult(translated);
}
}
class InvalidateTranslator extends ExpressionTranslator {
private final VisageExpression varRef;
private final Symbol vsym;
private JCVariableDecl invalVar = null;
private JCVariableDecl newLenVar = null;
InvalidateTranslator(VisageInvalidate tree) {
super(tree.pos());
this.varRef = tree.getVariable();
this.vsym = VisageTreeInfo.symbol(varRef);
}
private JCExpression receiver() {
return invalVar == null ? null : id(invalVar);
}
private void callInvalidate(Name phase) {
ListBuffer<JCExpression> args = ListBuffer.lb();
if (types.isSequence(vsym.type)) {
addPreface(newLenVar);
args.append(Int(0));
args.append(MINUS(id(newLenVar), Int(1)));
args.append(id(newLenVar));
}
args.append(id(phase));
addPreface(CallStmt(receiver(), attributeInvalidateName(vsym), args.toList()));
}
protected AbstractStatementsResult doit() {
if (varRef.getVisageTag() == VisageTag.SELECT) {
VisageSelect sel = (VisageSelect) varRef;
JCExpression selected = translateToExpression(sel.selected, sel.selected.type);
if (selected != null) {
invalVar = TmpVar("inval", sel.selected.type, selected);
addPreface(invalVar);
}
}
if (types.isSequence(vsym.type)) {
newLenVar = TmpVar("newLen", syms.intType, Call(receiver(), attributeSizeName(vsym)));
addPreface(newLenVar);
}
callInvalidate(defs.phaseTransitionCASCADE_INVALIDATE);
callInvalidate(defs.phaseTransitionCASCADE_TRIGGER);
return toStatementResult();
}
}
/***********************************************************************
*
* Utilities
*
*/
protected String getSyntheticPrefix() {
return "visage$";
}
private void fillClassesWithOuters(VisageScript tree) {
class FillClassesWithOuters extends VisageTreeScanner {
VisageClassDeclaration currentClass;
@Override
public void visitClassDeclaration(VisageClassDeclaration tree) {
VisageClassDeclaration prevClass = currentClass;
try {
currentClass = tree;
super.visitClassDeclaration(tree);
}
finally {
currentClass = prevClass;
}
}
@Override
public void visitIdent(VisageIdent tree) {
super.visitIdent(tree);
if (currentClass != null && tree.sym.kind != Kinds.TYP) {
addOutersForOuterAccess(tree.sym, currentClass.sym);
}
}
@Override
public void visitSelect(VisageSelect tree) {
super.visitSelect(tree);
Symbol sym = expressionSymbol(tree.selected);
if (currentClass != null && sym != null && sym.kind == Kinds.TYP) {
addOutersForOuterAccess(tree.sym, currentClass.sym);
}
}
@Override // Need this because VisageTreeScanner is not visiting the args of the VisageInstanciate tree. Starting to visit them generate tons of errors.
public void visitInstanciate(VisageInstanciate tree) {
super.visitInstanciate(tree);
super.scan(tree.getArgs());
}
private void addOutersForOuterAccess(Symbol sym, Symbol currentClass) {
if (sym != null && (sym.kind == Kinds.VAR || sym.kind == Kinds.MTH)
&& !sym.isStatic() && sym.owner.kind == Kinds.TYP && currentClass != null) {
Type ctype = currentClass.type;
boolean foundOwner = false;
while (ctype != Type.noType &&
types.isMixin(ctype.tsym) == types.isMixin(currentClass)) {
if (ctype.tsym.isSubClass(sym.owner, types)) {
foundOwner = true;
break;
}
ctype = ctype.getEnclosingType();
}
if (!foundOwner) {
Symbol csym = null;
while (currentClass != null) {
if (currentClass.isSubClass(sym.owner, types)) {
getHasOuters().put((ClassSymbol)csym, (ClassSymbol)currentClass);
break;
}
csym = currentClass;
currentClass = currentClass.owner.enclClass();
}
}
}
}
}
new FillClassesWithOuters().scan(tree);
}
/***********************************************************************
*
* Visitors (alphabetical order)
*
* Overrides to add contructs allowed in non-bound contexts.
*/
@Override
public void visitAssign(final VisageAssign tree) {
if (types.isSequence(tree.lhs.type)) {
if (tree.lhs.getVisageTag() == VisageTag.SEQUENCE_SLICE) {
result = new SequenceSliceActionTranslator((VisageSequenceSlice) tree.lhs, defs.Sequences_replaceSlice, tree.type, tree.rhs).doit();
} else {
result = new SequenceActionTranslator(tree.pos(), tree.lhs, defs.Sequences_set, null, tree.type, tree.rhs) {
@Override
protected Type rhsType() {
return tree.type;
}
}.doit();
}
} else {
result = new AssignTranslator(tree.pos(), tree.lhs, tree.rhs) {
@Override
JCExpression defaultFullExpression(JCExpression lhsTranslated, JCExpression rhsTranslated) {
return m().Assign(lhsTranslated, rhsTranslated);
}
}.doit();
}
}
@Override
public void visitAssignop(final VisageAssignOp tree) {
badVisitor("Assignop should have been lowered");
}
public void visitBlockExpression(VisageBlock tree) {
result = new BlockExpressionTranslator(tree).doit();
}
@Override
public void visitBreak(VisageBreak tree) {
result = new StatementsResult(make.at(tree.pos).Break(tree.label));
}
@Override
public void visitClassDeclaration(VisageClassDeclaration tree) {
VisageClassDeclaration prevClass = currentClass();
setCurrentClass(tree);
if (tree.isScriptClass()) {
scriptBegin();
}
try {
if (tree.isScriptClass()) {
setLiteralInitClassMap(new LiteralInitClassMap());
}
result = new ClassDeclarationTranslator(tree).doit();
} finally {
setCurrentClass(prevClass);
}
}
@Override
public void visitContinue(VisageContinue tree) {
result = new StatementsResult(make.at(tree.pos).Continue(tree.label));
}
@Override
public void visitFunctionDefinition(VisageFunctionDefinition tree) {
VisageFunctionDefinition prevFunction = currentFunction();
try {
setCurrentFunction(tree);
result = new FunctionTranslator(tree, false).doit();
}
finally {
setCurrentFunction(prevFunction);
}
}
@Override
public void visitFunctionValue(VisageFunctionValue tree) {
VisageFunctionDefinition def = tree.definition;
result = new FunctionValueTranslator(make.Ident(defs.lambda_MethodName), def, tree.pos(), (MethodType) def.type, tree.type).doit();
}
public void visitIdent(VisageIdent tree) {
result = new IdentTranslator(tree).doit();
}
@Override
public void visitInvalidate(final VisageInvalidate tree) {
result = new InvalidateTranslator(tree).doit();
}
@Override
public void visitParens(VisageParens tree) {
if (yield() == ToExpression) {
result = translateToExpressionResult(tree.expr, targetType);
} else {
result = translateToStatementsResult(tree.expr, targetType);
}
}
@Override
public void visitReturn(VisageReturn tree) {
VisageExpression exp = tree.getExpression();
if (exp == null) {
result = new StatementsResult(make.at(tree).Return(null));
} else {
result = translateToStatementsResult(exp, exp.type);
}
}
@Override
public void visitScript(VisageScript tree) {
if (depGraphWriter != null) {
depGraphWriter.start(tree);
}
try {
result = new ScriptTranslator(tree).doit();
} finally {
if (depGraphWriter != null) {
depGraphWriter.end();
}
}
}
public void visitSelect(VisageSelect tree) {
result = new SelectTranslator(tree).doit();
}
@Override
public void visitSequenceDelete(VisageSequenceDelete tree) {
DiagnosticPosition diagPos = tree.pos();
VisageExpression seq = tree.getSequence();
SequenceActionTranslator trans;
if (tree.getElement() != null) {
trans = new SequenceActionTranslator(diagPos, seq, defs.Sequences_deleteValue, null, tree.getElement());
} else {
switch (seq.getVisageTag()) {
case SEQUENCE_INDEXED:
VisageSequenceIndexed si = (VisageSequenceIndexed) seq;
trans = new SequenceActionTranslator(diagPos, si.getSequence(), defs.Sequences_deleteIndexed, si.getIndex());
break;
case SEQUENCE_SLICE:
final VisageSequenceSlice ss = (VisageSequenceSlice) seq;
trans = new SequenceSliceActionTranslator((VisageSequenceSlice) seq, defs.Sequences_deleteSlice, syms.voidType, null);
break;
default:
if (types.isSequence(seq.type)) {
trans = new SequenceActionTranslator(diagPos, seq, defs.Sequences_deleteAll, null);
} else {
TODO("delete non-sequence");
trans = null; //shut-up
}
}
}
result = trans.doit();
}
@Override
public void visitSequenceInsert(VisageSequenceInsert tree) {
result = new SequenceInsertTranslator(tree).doit();
}
@Override
public void visitSkip(VisageSkip tree) {
result = new StatementsResult(make.at(tree.pos).Skip());
}
@Override
public void visitThrow(VisageThrow tree) {
JCTree expr = translateToExpression(tree.expr, tree.type);
result = new StatementsResult(make.at(tree.pos).Throw(expr));
}
@Override
public void visitTry(VisageTry tree) {
result = new TryTranslator(tree).doit();
}
@Override
public void visitVar(VisageVar tree) {
result = new VarTranslator(tree).doit();
}
@Override
public void visitWhileLoop(VisageWhileLoop tree) {
result = new WhileTranslator(tree).doit();
}
}