/*
* 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.SequenceSliceTree;
import org.visage.api.tree.Tree.VisageKind;
import org.visage.tools.code.VisageSymtab;
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.TypeTags;
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.FunctionType;
import org.visage.tools.code.VisageFlags;
import org.visage.tools.code.VisageTypeRepresentation;
import org.visage.tools.code.VisageClassSymbol;
import org.visage.tools.code.VisageVarSymbol;
import org.visage.tools.comp.VisageDefs.RuntimeMethod;
import org.visage.tools.comp.VisageInitializationBuilder.LiteralInitClassMap;
import org.visage.tools.comp.VisageInitializationBuilder.LiteralInitVarMap;
import org.visage.tools.tree.*;
import com.sun.tools.mjavac.code.Type.MethodType;
import com.sun.tools.mjavac.jvm.Target;
import com.sun.tools.mjavac.tree.JCTree.JCFieldAccess;
import com.sun.tools.mjavac.tree.TreeInfo;
import com.sun.tools.mjavac.tree.TreeTranslator;
import java.util.Map;
import visage.lang.AngleUnit;
import visage.lang.LengthUnit;
import javax.lang.model.type.TypeKind;
import static org.visage.tools.comp.VisageAbstractTranslation.Yield.*;
/**
* Common translation mechanism
*
* @author Robert Field
*/
public abstract class VisageAbstractTranslation
extends VisageTranslationSupport
implements VisageVisitor {
/*
* the result of translating a tree by a visit method
*/
Result result;
final VisageOptimizationStatistics optStat;
final Target target;
Type targetType;
Yield yieldKind;
private VisageToJava toJava; //TODO: this should go away
protected VisageAbstractTranslation(Context context, VisageToJava toJava) {
super(context);
this.optStat = VisageOptimizationStatistics.instance(context);
this.toJava = toJava;
this.target = Target.instance(context);
}
/********** translation state tracking types and methods **********/
protected enum ReceiverContext {
// In a script function or script var init, implemented as a static method
ScriptAsStatic,
// In an instance function or instance var init, implemented as static
InstanceAsStatic,
// In an instance function or instance var init, implemented as an instance method
InstanceAsInstance,
// Should not see code in this state
Oops
}
enum Yield {
ToExpression,
ToStatement
}
Yield yield() {
return yieldKind;
}
VisageClassDeclaration currentClass() {
return getAttrEnv().enclClass;
}
VisageFunctionDefinition currentFunction() {
return getAttrEnv().enclFunction;
}
void setCurrentClass(VisageClassDeclaration tree) {
getAttrEnv().enclClass = tree;
}
void setCurrentFunction(VisageFunctionDefinition tree) {
getAttrEnv().enclFunction = tree;
}
protected VisageEnv<VisageAttrContext> getAttrEnv() {
return toJava.getAttrEnv();
}
protected ReceiverContext receiverContext() {
return toJava.receiverContext();
}
protected void setReceiverContext(ReceiverContext rc) {
toJava.setReceiverContext(rc);
}
protected VisageToJava toJava() {
return toJava;
}
/********** Utility routines **********/
/**
* @return the substitutionMap
*/
Map<Symbol, Name> getSubstitutionMap() {
return toJava.getSubstitutionMap();
}
/**
* Class symbols for classes that need a reference to the outer class.
*/
Map<ClassSymbol, ClassSymbol> getHasOuters() {
return toJava.getHasOuters();
}
/**
* @return the literalInitClassMap
*/
LiteralInitClassMap getLiteralInitClassMap() {
return toJava.getLiteralInitClassMap();
}
/** Box up a single primitive expression. */
JCExpression makeBox(DiagnosticPosition diagPos, JCExpression translatedExpr, Type primitiveType) {
make.at(translatedExpr.pos());
Type boxedType = types.boxedTypeOrType(primitiveType);
JCExpression box;
if (target.boxWithConstructors()) {
Symbol ctor = lookupConstructor(translatedExpr.pos(),
boxedType,
List.<Type>nil().prepend(primitiveType));
box = make.Create(ctor, List.of(translatedExpr));
} else {
Symbol valueOfSym = lookupMethod(translatedExpr.pos(),
names.valueOf,
boxedType,
List.<Type>nil().prepend(primitiveType));
// JCExpression meth =makeIdentifier(valueOfSym.owner.type.toString() + "." + valueOfSym.name.toString());
JCExpression meth = make.Select(makeType(diagPos, valueOfSym.owner.type), valueOfSym.name);
TreeInfo.setSymbol(meth, valueOfSym);
meth.type = valueOfSym.type;
box = make.App(meth, List.of(translatedExpr));
}
return box;
}
/** Look up a method in a given scope.
*/
private MethodSymbol lookupMethod(DiagnosticPosition pos, Name name, Type qual, List<Type> args) {
return rs.resolveInternalMethod(pos, getAttrEnv(), qual, name, args, null);
}
//where
/** Look up a constructor.
*/
private MethodSymbol lookupConstructor(DiagnosticPosition pos, Type qual, List<Type> args) {
return rs.resolveInternalConstructor(pos, getAttrEnv(), qual, args, null);
}
ExpressionResult convertTranslated(ExpressionResult res, DiagnosticPosition diagPos, Type targettedType) {
return (targettedType == null || targettedType == syms.voidType) ?
res
: new ExpressionResult(
diagPos,
res.statements(),
new TypeConversionTranslator(diagPos, res.expr(), res.resultType, targettedType).doitExpr(),
res.bindees(),
res.invalidators(),
res.interClass,
res.setterPreface(),
targettedType);
}
JCExpression convertTranslated(JCExpression translated, DiagnosticPosition diagPos,
Type sourceType, Type targettedType) {
return (targettedType == null || targettedType == syms.voidType) ?
translated
: new TypeConversionTranslator(diagPos, translated, sourceType, targettedType).doitExpr();
}
/**
* Special handling for Strings, Durations, Lengths, Angles, and Colors. If a value
* assigned to one of these is null, the default value for the type must be substituted.
* inExpr is the input expression. outType is the desired result type.
* expr is the result value to use in the normal case.
* This doesn't handle the case var ss: String = if (true) null else "Hi there, sailor"
* But it does handle nulls coming in from Java method returns, and variables.
*/
protected JCExpression convertNullability(final DiagnosticPosition diagPos, final JCExpression expr,
final VisageExpression inExpr, final Type outType) {
class ConvertNullabilityTranslator extends Translator {
ConvertNullabilityTranslator(DiagnosticPosition diagPos) { super(diagPos); }
Result doit() {
throw new IllegalArgumentException();
}
JCExpression doitExpr() {
if (outType != syms.stringType && outType != syms.visage_DurationType
&& outType != syms.visage_LengthType && outType != syms.visage_AngleType
&& outType != syms.visage_ColorType) {
return expr;
}
final Type inType = inExpr.type;
if (inType == syms.botType || inExpr.getVisageKind() == VisageKind.NULL_LITERAL) {
return DefaultValue(outType);
}
if (!types.isSameType(inType, outType) || isValueFromJava(inExpr)) {
JCVariableDecl daVar = TmpVar(outType, expr);
JCExpression toTest = id(daVar.name);
JCExpression cond = NEnull(toTest);
JCExpression ret =
If (cond,
id(daVar.name),
DefaultValue(outType));
return BlockExpression(daVar, ret);
}
return expr;
}
}
return new ConvertNullabilityTranslator(diagPos).doitExpr();
}
/********** Result types **********/
public static abstract class Result {
final DiagnosticPosition diagPos;
Result(DiagnosticPosition diagPos) {
this.diagPos = diagPos;
}
abstract List<JCTree> trees();
}
public static abstract class AbstractStatementsResult extends Result {
private final List<JCStatement> stmts;
AbstractStatementsResult(DiagnosticPosition diagPos, List<JCStatement> stmts) {
super(diagPos);
this.stmts = stmts;
}
public List<JCStatement> statements() {
return stmts;
}
List<JCTree> trees() {
ListBuffer<JCTree> ts = ListBuffer.lb();
for (JCTree t : stmts) {
ts.append(t);
}
return ts.toList();
}
}
public static class StatementsResult extends AbstractStatementsResult {
StatementsResult(DiagnosticPosition diagPos, List<JCStatement> stmts) {
super(diagPos, stmts);
}
StatementsResult(DiagnosticPosition diagPos, ListBuffer<JCStatement> buf) {
super(diagPos, buf.toList());
}
StatementsResult(JCStatement stmt) {
super(stmt.pos(), List.of(stmt));
}
}
public static class DependentPair {
public final VisageVarSymbol instanceSym;
public final Symbol referencedSym;
DependentPair(VisageVarSymbol instanceSym, Symbol referencedSym) {
this.instanceSym = instanceSym;
this.referencedSym = referencedSym;
}
}
/**
* A variable (represented as a Symbol) on which the current variable depends
* and the, optional, invalidation code to be placed in the variable's
* invalidation method to corrected invalidate the current variable.
*/
public static class BindeeInvalidator {
// Variable symbols on which the current variable depends
public final VisageVarSymbol bindee;
// Invalidation code to be placed in bindee.
// Optional. If null, use default invalidation of the current variable.
public final JCStatement invalidator;
BindeeInvalidator(VisageVarSymbol bindee, JCStatement invalidator) {
this.bindee = bindee;
this.invalidator = invalidator;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BindeeInvalidator)) {
return false;
}
BindeeInvalidator other = (BindeeInvalidator)obj;
return other.bindee == bindee && other.invalidator == invalidator;
}
@Override
public int hashCode() {
return bindee.hashCode() + (invalidator == null ? 0 : invalidator.hashCode());
}
@Override
public String toString() {
return "BindeeInvalidator " + bindee.name + " / " + invalidator;
}
}
public static class ExpressionResult extends AbstractStatementsResult {
private final JCExpression value;
private final List<VisageVarSymbol> bindees;
private final List<BindeeInvalidator> invalidators;
private final List<DependentPair> interClass;
private final List<JCStatement> setterPreface;
private final Type resultType;
ExpressionResult(DiagnosticPosition diagPos, List<JCStatement> stmts, JCExpression value,
List<VisageVarSymbol> bindees, List<BindeeInvalidator> invalidators, List<DependentPair> interClass,
List<JCStatement> setterPreface, Type resultType) {
super(diagPos, stmts);
this.value = value;
this.bindees = bindees;
this.invalidators = invalidators;
this.interClass = interClass;
this.setterPreface = setterPreface;
this.resultType = resultType;
}
public JCExpression expr() {
return value;
}
public boolean hasExpr() {
return value != null;
}
// Invalidators for this variable
public List<BindeeInvalidator> invalidators() {
return invalidators;
}
public List<VisageVarSymbol> bindees() {
return bindees;
}
public List<DependentPair> interClass() {
return interClass;
}
public List<JCStatement> setterPreface() {
return setterPreface;
}
public Type resultType() {
return resultType;
}
public boolean isBoundVirtualSequence() {
return false;
}
public JCStatement getElementMethodBody() {
TODO("sequence element getter for: " + value);
return null;
}
public JCStatement getSizeMethodBody() {
TODO("sequence size getter for: " + value);
return null;
}
@Override
List<JCTree> trees() {
List<JCTree> ts = super.trees();
return value==null? ts : ts.append(value);
}
}
/**
* Bound sequence get element / size method body pair as Result
*/
public static class BoundSequenceResult extends ExpressionResult {
private final JCStatement getElement;
private final JCStatement getSize;
BoundSequenceResult(List<VisageVarSymbol> bindees, List<BindeeInvalidator> invalidators, List<DependentPair> interClass, JCStatement getElement, JCStatement getSize) {
this(null, null, bindees, invalidators, interClass, getElement, getSize, null);
}
BoundSequenceResult(
List<JCStatement> stmts, JCExpression value,
List<VisageVarSymbol> bindees, List<BindeeInvalidator> invalidators, List<DependentPair> interClass,
JCStatement getElement, JCStatement getSize, Type resultType) {
super(getElement.pos(), stmts, value, bindees, invalidators, interClass, null, resultType);
this.getElement = getElement;
this.getSize = getSize;
}
@Override
public boolean isBoundVirtualSequence() {
return true;
}
// Java code for getting the element of a bound sequence
@Override
public JCStatement getElementMethodBody() {
return getElement;
}
// Java code for getting the size of a bound sequence
@Override
public JCStatement getSizeMethodBody() {
return getSize;
}
@Override
public String toString() {
return "SequenceElementSizeResult- get element: " + getElement.getClass() + " = " + getElement + ", size: " + getSize.getClass() + " = " + getSize;
}
@Override
List<JCTree> trees() {
return List.<JCTree>of(getElement, getSize);
}
}
public static class SpecialResult extends Result {
private final JCTree tree;
SpecialResult(JCTree tree) {
super(tree.pos());
this.tree = tree;
}
JCTree tree() {
return tree;
}
@Override
public String toString() {
return "SpecialResult-" + tree.getClass() + " = " + tree;
}
List<JCTree> trees() {
return tree==null? List.<JCTree>nil() : List.of(tree);
}
}
/********** translation support **********/
private void translateCore(VisageTree expr, Type targettedType, Yield yield) {
VisageTree prevWhere = getAttrEnv().where;
Yield prevYield = yield();
Type prevTargetType = targetType;
getAttrEnv().where = expr;
yieldKind = yield;
targetType = targettedType;
expr.accept(this);
yieldKind = prevYield;
targetType = prevTargetType;
getAttrEnv().where = prevWhere;
}
ExpressionResult translateToExpressionResult(VisageExpression expr, Type targettedType) {
if (expr == null) {
return null;
} else {
translateCore(expr, targettedType, ToExpression);
ExpressionResult ret = (ExpressionResult)this.result;
this.result = null;
return ret.hasExpr()?
convertTranslated(ret, expr.pos(), targettedType)
: ret;
}
}
StatementsResult translateToStatementsResult(VisageExpression expr, Type targettedType) {
if (expr == null) {
return null;
} else {
translateCore(expr, targettedType, ToStatement);
Result ret = this.result;
this.result = null;
if (ret instanceof StatementsResult) {
return (StatementsResult) ret; // already converted
} else if (ret instanceof ExpressionResult) {
return new StatementsResult(expr.pos(), asStatements((ExpressionResult) ret, targettedType));
} else {
throw new RuntimeException(ret.toString());
}
}
}
class JCConverter extends JavaTreeBuilder {
private final AbstractStatementsResult res;
private final Type type;
JCConverter(AbstractStatementsResult res, Type type) {
super(res.diagPos, currentClass(), receiverContext() == ReceiverContext.ScriptAsStatic);
this.res = res;
this.type = type;
}
List<JCStatement> asStatements() {
int typeTag = type.tag; // Blow up if we are passed null as the type of statements
List<JCStatement> stmts = res.statements();
if (res instanceof ExpressionResult) {
ExpressionResult eres = (ExpressionResult) res;
JCExpression expr = eres.expr();
if (expr != null) {
stmts = stmts.append(Stmt(convertedExpression(eres), type));
}
}
return stmts;
}
JCStatement asStatement() {
List<JCStatement> stmts = asStatements();
if (stmts.length() == 1) {
return stmts.head;
} else {
return asBlock();
}
}
JCBlock asBlock() {
return Block(asStatements());
}
JCExpression asExpression() {
if (res instanceof ExpressionResult) {
ExpressionResult er = (ExpressionResult) res;
if (er.statements().nonEmpty()) {
BlockExprJCBlockExpression bexpr = new BlockExprJCBlockExpression(0L, er.statements(), convertedExpression(er));
bexpr.pos = er.expr().pos;
return bexpr;
} else {
return convertedExpression(er);
}
} else {
throw new IllegalArgumentException("must be ExpressionResult -- was: " + res);
}
}
private JCExpression convertedExpression(ExpressionResult eres) {
return convertTranslated(eres.expr(), diagPos, eres.resultType, type);
}
}
JCBlock asBlock(AbstractStatementsResult res, Type targettedType) {
return new JCConverter(res, targettedType).asBlock();
}
JCStatement asStatement(AbstractStatementsResult res, Type targettedType) {
return new JCConverter(res, targettedType).asStatement();
}
List<JCStatement> asStatements(AbstractStatementsResult res, Type targettedType) {
return new JCConverter(res, targettedType).asStatements();
}
JCExpression asExpression(AbstractStatementsResult res, Type targettedType) {
return new JCConverter(res, targettedType).asExpression();
}
JCExpression translateToExpression(VisageExpression expr, Type targettedType) {
return asExpression(translateToExpressionResult(expr, targettedType), targettedType);
}
JCStatement translateToStatement(VisageExpression expr, Type targettedType) {
return asStatement(translateToStatementsResult(expr, targettedType), targettedType);
}
JCBlock translateToBlock(VisageExpression expr, Type targettedType) {
if (expr == null) {
return null;
} else {
return asBlock(translateToStatementsResult(expr, targettedType), targettedType);
}
}
JCTree translateFunction(VisageFunctionDefinition tree, boolean maintainContext) {
return new FunctionTranslator(tree, maintainContext).doit().tree();
}
/** Translate a single tree.
*/
SpecialResult translateToSpecialResult(VisageTree tree) {
SpecialResult ret;
VisageTree prevWhere = getAttrEnv().where;
getAttrEnv().where = tree;
tree.accept(this);
getAttrEnv().where = prevWhere;
ret = (SpecialResult) this.result;
this.result = null;
return ret;
}
static class OnReplaceInfo {
public OnReplaceInfo outer;
VisageVarSymbol vsym;
public VisageOnReplace onReplace;
Symbol newElementsSym;
Type arraySequenceType;
Type seqWithExtendsType;
}
OnReplaceInfo onReplaceInfo;
OnReplaceInfo findOnReplaceInfo(Symbol sym) {
OnReplaceInfo info = onReplaceInfo;
while (info != null && sym != info.newElementsSym)
info = info.outer;
return info;
}
/**** utility methods ******/
private UseSequenceBuilder useSequenceBuilder(DiagnosticPosition diagPos, Type elemType, final int initLength, final boolean nonLocal) {
return new UseSequenceBuilder(diagPos, elemType, null) {
@Override
JCStatement addElement(VisageExpression exprToAdd) {
JCExpression expr = translateToExpression(exprToAdd, targettedType(exprToAdd));
JCVariableDecl varDef = TmpVar(targettedType(exprToAdd), expr);
return nonLocal ?
Block(varDef, makeAdd(id(varDef))) :
makeAdd(expr);
}
@Override
List<JCExpression> makeConstructorArgs() {
ListBuffer<JCExpression> lb = ListBuffer.lb();
if (initLength != -1) {
lb.append(make.at(diagPos).Literal(Integer.valueOf(initLength)));
}
if (addTypeInfoArg)
lb.append(TypeInfo(diagPos, elemType));
return lb.toList();
}
@Override
JCExpression makeToSequence() {
return makeBuilderVarAccess();
}
};
}
UseSequenceBuilder useSequenceBuilder(DiagnosticPosition diagPos, Type elemType, boolean nonLocal) {
return useSequenceBuilder(diagPos, elemType, -1, nonLocal);
}
abstract class UseSequenceBuilder extends JavaTreeBuilder {
final Type elemType;
private final String seqBuilder;
boolean addTypeInfoArg = true;
// Sequence builder temp var name "sb"
private final Name sbName = getSyntheticName("sb");
private UseSequenceBuilder(DiagnosticPosition diagPos, Type elemType, String seqBuilder) {
super(diagPos, currentClass(), receiverContext() == ReceiverContext.ScriptAsStatic);
this.elemType = elemType;
this.seqBuilder = seqBuilder;
}
Type targettedType(VisageExpression exprToAdd) {
Type exprType = exprToAdd.type;
if (types.isArray(exprType) || types.isSequence(exprType)) {
return types.sequenceType(elemType);
} else {
Type unboxed = types.unboxedType(elemType);
return (unboxed.tag != TypeTags.NONE) ? unboxed : elemType;
}
}
JCStatement makeBuilderVar() {
String localSeqBuilder = this.seqBuilder;
boolean primitive = false;
if (localSeqBuilder == null) {
if (elemType.isPrimitive()) {
primitive = true;
addTypeInfoArg = false;
VisageTypeRepresentation typeRep = types.typeRep(elemType);
localSeqBuilder = "org.visage.runtime.sequence." + VisageDefs.getTypePrefix(typeRep.ordinal()) + "ArraySequence"; //TODO: put in defs
}
else
localSeqBuilder = VisageDefs.cObjectArraySequence;
}
JCExpression builderTypeExpr = QualifiedTree(localSeqBuilder);
JCExpression builderClassExpr = QualifiedTree(localSeqBuilder);
if (! primitive) {
builderTypeExpr = m().TypeApply(builderTypeExpr,
List.of(makeType(elemType)));
// class name -- SequenceBuilder<elemType>
builderClassExpr = m().TypeApply(builderClassExpr,
List.<JCExpression>of(makeType(elemType)));
}
// Build "sb" initializing expression -- new SequenceBuilder<T>(clazz)
JCExpression newExpr = m().NewClass(
null, // enclosing
List.<JCExpression>nil(), // type args
builderClassExpr, // class name -- SequenceBuilder<elemType>
makeConstructorArgs(), // args
null // empty body
);
// Build the sequence builder variable
return Var(0L, builderTypeExpr, sbName, newExpr);
}
JCIdent makeBuilderVarAccess() {
return id(sbName);
}
abstract JCStatement addElement(VisageExpression expr);
abstract List<JCExpression> makeConstructorArgs();
JCStatement makeAdd(JCExpression expr) {
return CallStmt(makeBuilderVarAccess(), names.fromString("add"), expr);
}
abstract JCExpression makeToSequence();
}
/****************************** Translators ******************************/
abstract class Translator extends JavaTreeBuilder {
Translator(DiagnosticPosition diagPos) {
super(diagPos, currentClass(), receiverContext() == ReceiverContext.ScriptAsStatic);
optStat.recordTranslator(this.getClass());
}
abstract Result doit();
VisageTreeMaker visagem() {
return visagemake.at(diagPos);
}
JCVariableDecl convertParam(VisageVar param) {
return Param(param.type, param.name);
}
}
abstract class ExpressionTranslator extends Translator {
private final ListBuffer<JCStatement> stmts = ListBuffer.lb();
private final ListBuffer<BindeeInvalidator> invalidators = ListBuffer.lb();
private final ListBuffer<VisageVarSymbol> bindees = ListBuffer.lb();
private final ListBuffer<DependentPair> interClass = ListBuffer.lb();
private final ListBuffer<JCStatement> setterPreface = ListBuffer.lb();
ExpressionTranslator(DiagnosticPosition diagPos) {
super(diagPos);
}
JCExpression mergeResults(ExpressionResult res) {
addPreface(res.statements());
addBindees(res.bindees());
addInterClassBindees(res.interClass());
addInvalidators(res.invalidators());
return res.expr();
}
JCExpression mergeResultsToBlockExpression(ExpressionResult res) {
addBindees(res.bindees());
addInterClassBindees(res.interClass());
if (!res.statements().isEmpty()) {
return BlockExpression(res.statements(), res.expr());
} else {
return res.expr();
}
}
JCExpression translateExpr(VisageExpression expr, Type type) {
ExpressionResult result = translateToExpressionResult(expr, type);
return mergeResults(result);
}
JCExpression translateExprToBlockExpression(VisageExpression expr, Type type) {
ExpressionResult result = translateToExpressionResult(expr, type);
return mergeResultsToBlockExpression(result);
}
List<JCExpression> translateExprs(List<VisageExpression> list) {
ListBuffer<JCExpression> trans = ListBuffer.lb();
for (List<VisageExpression> l = list; l.nonEmpty(); l = l.tail) {
JCExpression res = translateExpr(l.head, null);
if (res != null) {
trans.append(res);
}
}
return trans.toList();
}
void translateStmt(VisageExpression expr, Type targettedType) {
//TODO: My guess is that statements will need to preserve bindee info for block-expressions
StatementsResult res = translateToStatementsResult(expr, targettedType);
addPreface(res.statements());
}
void addPreface(JCStatement stmt) {
stmts.append(stmt);
}
void addPreface(List<JCStatement> list) {
stmts.appendList(list);
}
void addInvalidator(VisageVarSymbol sym, JCStatement invStmt) {
addInvalidator(new BindeeInvalidator(sym, invStmt));
}
void addInvalidators(List<BindeeInvalidator> bis) {
for (BindeeInvalidator bi : bis)
addInvalidator(bi);
}
void addInvalidator(BindeeInvalidator bi) {
if (!invalidators.contains(bi)) {
invalidators.append(bi);
}
}
void addBindee(VisageVarSymbol sym) {
if (types.isVisageClass(sym.owner)) {
bindees.append(sym);
}
}
void addBindees(List<VisageVarSymbol> syms) {
bindees.appendList(syms);
}
void addInterClassBindee(VisageVarSymbol instanceSym, Symbol referencedSym) {
if (types.isVisageClass(instanceSym.owner) && types.isVisageClass(referencedSym.owner)) {
interClass.append(new DependentPair( instanceSym, referencedSym));
}
}
void addInterClassBindees(List<DependentPair> pairs) {
for (DependentPair pair : pairs) {
interClass.append(pair);
}
}
void addSetterPreface(JCStatement stmt) {
setterPreface.append(stmt);
}
ExpressionResult toResult(JCExpression translated, Type resultType) {
return new ExpressionResult(diagPos, statements(), translated, bindees(), invalidators(), interClass(), setterPreface(), resultType);
}
StatementsResult toStatementResult(JCExpression translated, Type resultType, Type targettedType) {
return toStatementResult(
(targettedType == null || targettedType == syms.voidType) ?
Stmt(translated)
: Return(
convertTranslated(translated, diagPos, resultType, targettedType)));
}
StatementsResult toStatementResult(JCStatement translated) {
assert invalidators.length() == 0;
return new StatementsResult(diagPos, stmts.append(translated));
}
StatementsResult toStatementResult() {
assert invalidators.length() == 0;
return new StatementsResult(diagPos, stmts);
}
List<JCStatement> statements() {
return stmts.toList();
}
List<VisageVarSymbol> bindees() {
return bindees.toList();
}
List<BindeeInvalidator> invalidators() {
return invalidators.toList();
}
List<DependentPair> interClass() {
return interClass.toList();
}
List<JCStatement> setterPreface() {
return setterPreface.toList();
}
@Override
abstract AbstractStatementsResult doit();
JCExpression staticReference(Symbol sym) {
Symbol owner = sym.owner;
Symbol encl = currentClass().sym;
if (encl.name.endsWith(defs.scriptClassSuffixName) && owner == encl.owner) {
return null;
} else {
//TODO: see init builder getStaticContext() for a better implementation
Type classType = types.erasure(owner.type);
return makeType(classType, false);
}
}
JCExpression reference(Symbol sym) {
if (sym.isStatic()) {
return Select(staticReference(sym), sym.name);
} else if (sym.isLocal()) {
return id(sym);
} else {
return Select(getReceiver(sym), sym.name);
}
}
JCExpression translateSizeof(VisageExpression expr, JCExpression transExpr) {
if (expr instanceof VisageIdent) {
VisageIdent varId = (VisageIdent) expr;
OnReplaceInfo info = findOnReplaceInfo(varId.sym);
if (info != null) {
return id(paramNewElementsLengthName(info.onReplace));
}
}
return Call(defs.Sequences_size, transExpr);
}
}
class LiteralTranslator extends ExpressionTranslator {
protected final VisageLiteral tree;
LiteralTranslator(VisageLiteral tree) {
super(tree.pos());
this.tree = tree;
}
@Override
protected ExpressionResult doit() {
if (tree.typetag == TypeTags.BOT && types.isSequence(tree.type)) {
throw new AssertionError("Should have been converted");
}
// Just translate to literal value
return toResult(m().Literal(tree.typetag, tree.value), tree.type);
}
}
class StringExpressionTranslator extends ExpressionTranslator {
private final VisageStringExpression tree;
StringExpressionTranslator(VisageStringExpression tree) {
super(tree.pos());
this.tree = tree;
}
protected ExpressionResult doit() {
StringBuffer sb = new StringBuffer();
List<VisageExpression> parts = tree.getParts();
ListBuffer<JCExpression> values = new ListBuffer<JCExpression>();
VisageLiteral lit = (VisageLiteral) (parts.head); // "...{
sb.append((String) lit.value);
parts = parts.tail;
boolean containsDateTimeFormat = false;
while (parts.nonEmpty()) {
lit = (VisageLiteral) (parts.head); // optional format (or null)
String format = (String) lit.value;
if ((!containsDateTimeFormat) && format.length() > 0
&& VisageDefs.DATETIME_FORMAT_PATTERN.matcher(format).find()) {
containsDateTimeFormat = true;
}
parts = parts.tail;
VisageExpression exp = parts.head;
JCExpression texp;
if (exp != null && types.isSameType(exp.type, syms.visage_DurationType)) {
texp = Call(translateExpr(exp, syms.visage_DurationType), defs.toMillis_DurationMethodName);
texp = typeCast(syms.visage_LongType, syms.visage_DoubleType, texp);
sb.append(format.length() == 0 ? "%dms" : format);
} else {
texp = translateExpr(exp, null);
sb.append(format.length() == 0 ? "%s" : format);
}
values.append(texp);
parts = parts.tail;
lit = (VisageLiteral) (parts.head); // }...{ or }..."
String part = (String)lit.value;
sb.append(part.replace("%", "%%")); // escape percent signs
parts = parts.tail;
}
values.prepend(String(sb.toString()));
RuntimeMethod formatMethod;
if (tree.translationKey != null) {
formatMethod = defs.StringLocalization_getLocalizedString;
if (tree.translationKey.length() == 0) {
values.prepend(Null());
} else {
values.prepend(String(tree.translationKey));
}
String resourceName =
currentClass().sym.flatname.toString().replace('.', '/').replaceAll("\\$.*", "");
values.prepend(String(resourceName));
} else if (containsDateTimeFormat) {
formatMethod = defs.VisageFormatter_sprintf;
} else {
formatMethod = defs.String_format;
}
return toResult(
Call(formatMethod, values),
syms.stringType);
}
}
abstract class MemberReferenceTranslator extends ExpressionTranslator {
protected MemberReferenceTranslator(DiagnosticPosition diagPos) {
super(diagPos);
}
JCExpression convertVariableReference(JCExpression varRef, Symbol sym) {
JCExpression expr = varRef;
if (sym instanceof VarSymbol) {
final VisageVarSymbol vsym = (VisageVarSymbol) sym;
boolean isVisageMemberVar = vsym.isVisageMember();
if (isVisageMemberVar) {
// this is a reference to a Visage class variable, use getter
JCExpression instance;
// find referenced instance, null for current
switch (expr.getTag()) {
case JCTree.IDENT:
// if we are in a mixin class reference variables through the receiver
instance = currentClass().isMixinClass()?
id(defs.receiverName)
: null;
break;
case JCTree.SELECT:
instance = ((JCFieldAccess) varRef).getExpression();
break;
default:
throw new AssertionError();
}
expr = makeAccess(instance, vsym);
}
}
return expr;
}
JCExpression makeAccess(JCExpression instance, VisageVarSymbol vsym) {
return Getter(instance, vsym);
}
}
abstract class NullCheckTranslator extends MemberReferenceTranslator {
protected final Symbol refSym; //
protected final Type fullType; // Type, before conversion, of expression
protected final Type resultType; // Type of final generated expression
protected boolean staticReference; // Is this a static reference
NullCheckTranslator(DiagnosticPosition diagPos, Symbol sym, Type fullType) {
super(diagPos);
this.refSym = sym;
this.fullType = fullType;
this.resultType = targetType==null? fullType : targetType; // use targetType, if any
this.staticReference = refSym != null && refSym.isStatic();
}
abstract VisageExpression getToCheck();
abstract JCExpression fullExpression(JCExpression mungedToCheckTranslated);
boolean needNullCheck() {
return getToCheck() != null && !staticReference && !getToCheck().type.isPrimitive() && possiblyNull(getToCheck());
}
boolean canChange() {
return getToCheck() != null && !getToCheck().type.isPrimitive() && possiblyNull(getToCheck());
}
protected JCExpression preserveSideEffects(Type type, VisageExpression expr, JCExpression trans) {
if (needNullCheck() && expr!=null && hasSideEffects(expr)) {
// if there is going to be a null check (which thus could keep expr
// from being evaluated), and expr has side-effects, then eval
// it first and put it in a temp var.
return addTempVar(type, trans);
} else {
// no side-effects, just pass-through
return trans;
}
}
protected JCExpression addTempVar(Type varType, JCExpression trans) {
if (varType == syms.voidType) {
// If void is passed in then the temp will be ignored.
return trans;
} else {
JCVariableDecl tmpVar = TmpVar("pse", types.normalize(varType), trans);
addPreface(tmpVar);
return id(tmpVar);
}
}
/**
* Translate the 'toCheck' of 'toCheck.name'.
* Override to specialize the translation.
* Note: 'toCheck' may or may not be in a LHS but in either
* event the selector is a value expression
*/
JCExpression translateToCheck(VisageExpression expr) {
if (staticReference) {
return staticReference(refSym);
} else if (expr == null) {
if (refSym != null && refSym.owner.kind == Kinds.TYP) {
// it is a non-static attribute or function class member
// reference it through the receiver
return getReceiver(refSym);
}
return null;
}
Symbol selectorSym = expressionSymbol(expr);
// If this is OuterClass.memberName or MixinClass.memberName, then
// we want to create expression to get the proper receiver.
if (selectorSym != null && selectorSym.kind == Kinds.TYP) {
return getReceiver(refSym);
}
Type exprType = expr.type;
// translate normally, preserving side-effects if need be
JCExpression tExpr = preserveSideEffects(exprType, expr, translateExpr(expr, exprType));
// if expr is primitve, box it
// expr.type is null for package symbols.
if (exprType != null && exprType.isPrimitive()) {
return makeBox(diagPos, tExpr, exprType);
}
return tExpr;
}
@Override
protected AbstractStatementsResult doit() {
JCExpression tToCheck = translateToCheck(getToCheck());
JCExpression full = fullExpression(tToCheck);
full = convertTranslated(full, diagPos, fullType, resultType);
if (yield() == ToStatement) {
// a statement is the desired result of the translation
return toStatementResult(wrapInNullCheckStatement(full, tToCheck, resultType, fullType));
} else {
// an expression is the desired result of the translation, convert it to a conditional expression
// if it would dereference null, then the full expression instead yields the default value
return toResult(wrapInNullCheckExpression(full, tToCheck, resultType, fullType), resultType);
}
}
protected JCExpression copyOfTranslatedToCheck(JCExpression tToCheck) {
// Make an expression to use in null test.
// If translated toCheck is an identifier (tmp var or not), just make a new identifier.
// Otherwise, retranslate.
return (tToCheck instanceof JCIdent) ? id(((JCIdent)tToCheck).name) : translateToCheck(getToCheck());
}
private JCExpression makeNullCheckCondition(JCExpression tToCheck) {
return NEnull(copyOfTranslatedToCheck(tToCheck));
}
private JCExpression makeDefault(Type theResultType, Type theFullType) {
JCExpression defaultValue = DefaultValue(theFullType);
// Return default value for Pointer type to be null
return defaultValue.type == syms.botType ?
DefaultValue(theResultType) :
(types.isSameType(theResultType, syms.visage_PointerType)) ?
Null() :
convertTranslated(defaultValue, diagPos, theFullType, theResultType);
}
protected JCExpression wrapInNullCheckExpression(JCExpression full, JCExpression tToCheck, Type theResultType, Type theFullType) {
if (needNullCheck()) {
// Do a null check
// we have a testable guard for null, test before the invoke (boxed conversions don't need a test)
// an expression is the desired result of the translation, convert it to a conditional expression
// if it would dereference null, then the full expression instead yields the default value
return
If (makeNullCheckCondition(tToCheck),
full,
makeDefault(theResultType, theFullType));
} else {
return full;
}
}
protected JCStatement wrapInNullCheckStatement(JCExpression full, JCExpression tToCheck, Type theResultType, Type theFullType) {
if (needNullCheck()) {
// Do a null check
// we have a testable guard for null, test before the invoke (boxed conversions don't need a test)
// a statement is the desired result of the translation, return the If-statement
JCStatement nullAction = null;
if (theResultType != null && theResultType != syms.voidType) {
nullAction = Stmt(makeDefault(theResultType, theFullType), theResultType);
}
return If(makeNullCheckCondition(tToCheck),
Stmt(full, theResultType),
nullAction);
} else {
return Stmt(full, theResultType);
}
}
private boolean possiblyNull(VisageExpression expr) {
if (expr == null) {
return true;
}
switch (expr.getVisageTag()) {
case ASSIGN:
return possiblyNull(((VisageAssign)expr).getExpression());
case APPLY:
return true;
case BLOCK_EXPRESSION:
return possiblyNull(((VisageBlock)expr).getValue());
case IDENT: {
if (((VisageIdent)expr).sym instanceof VarSymbol) {
Symbol sym = ((VisageIdent)expr).sym;
return sym.name != names._this && sym.name != names._super;
} else {
return false;
}
}
case CONDEXPR:
return possiblyNull(((VisageIfExpression)expr).getTrueExpression()) || possiblyNull(((VisageIfExpression)expr).getFalseExpression());
case LITERAL:
return expr.getVisageKind() == VisageKind.NULL_LITERAL;
case PARENS:
return possiblyNull(((VisageParens)expr).getExpression());
case SELECT:
return ((VisageSelect)expr).sym instanceof VarSymbol;
case SEQUENCE_INDEXED:
return true;
case TYPECAST:
return possiblyNull(((VisageTypeCast)expr).getExpression());
case VAR_DEF:
return possiblyNull(((VisageVar)expr).getInitializer());
default:
return false;
}
}
}
class SelectTranslator extends NullCheckTranslator {
protected final VisageSelect tree;
protected final Name name;
protected SelectTranslator(VisageSelect tree) {
super(tree.pos(), tree.sym, tree.type);
this.tree = tree;
this.name = tree.getIdentifier();
}
@Override
boolean needNullCheck() {
return !tree.nullCheck && super.needNullCheck();
}
@Override
VisageExpression getToCheck() {
return tree.getExpression();
}
@Override
JCExpression fullExpression(JCExpression tToCheck) {
JCExpression translated = Select(tToCheck, name);
return convertVariableReference(translated, refSym);
}
}
class SelectElementTranslator extends NullCheckTranslator {
private final VisageSelect tree;
private final Name name;
private final JCExpression tIndex;
private SelectElementTranslator(VisageSelect tree, JCExpression tIndex) {
super(tree.pos(), tree.sym, types.elementType(tree.type));
this.tree = tree;
this.name = tree.getIdentifier();
this.tIndex = tIndex;
}
@Override
boolean needNullCheck() {
return tree.nullCheck && super.needNullCheck();
}
@Override
VisageExpression getToCheck() {
return tree.getExpression();
}
@Override
JCExpression fullExpression(JCExpression tToCheck) {
JCExpression translated = Select(tToCheck, name);
return convertVariableReference(translated, refSym);
}
@Override
JCExpression makeAccess(JCExpression instance, VisageVarSymbol vsym) {
return Call(instance, attributeGetElementName(vsym), tIndex);
}
}
class IdentElementTranslator extends IdentTranslator {
private final JCExpression tIndex;
IdentElementTranslator(VisageIdent tree, JCExpression tIndex) {
super(tree);
this.tIndex = tIndex;
}
@Override
JCExpression makeAccess(JCExpression instance, VisageVarSymbol vsym) {
return Call(instance, attributeGetElementName(vsym), tIndex);
}
}
class FunctionCallTranslator extends NullCheckTranslator {
// Function determination
protected final VisageExpression meth;
protected final VisageExpression selector;
protected final boolean thisCall;
protected final boolean superCall;
protected final MethodSymbol msym;
protected final Symbol funcSym;
protected final Symbol selectorSym;
protected final boolean renameToThis;
protected final boolean renameToSuper;
protected final boolean superToStatic;
protected final boolean useInvoke;
protected final boolean callBound;
protected final boolean magicPointerMakeFunction;
protected final boolean selectorNullCheck;
// Call info
protected final List<VisageExpression> typeargs;
protected final List<VisageExpression> args;
// Null Checking control
protected final boolean knownNonNull;
// are we in the middle of translating argument expressions?
private boolean inTranslateArgs;
// preface statements generated when translating argument expressions
private ListBuffer<JCStatement> argumentPreface;
FunctionCallTranslator(final VisageFunctionInvocation tree) {
// If this is an invoke (later "useInvoke") then the named meth is not the refSym
// since it will be wrapped with a ".invoke()"
super(
tree.pos(),
(tree.meth.type instanceof FunctionType)? null : expressionSymbol(tree.meth),
tree.type);
// Function determination
meth = tree.meth;
VisageSelect fieldAccess = meth.getVisageTag() == VisageTag.SELECT ? (VisageSelect) meth : null;
selector = fieldAccess != null ? fieldAccess.getExpression() : null;
selectorNullCheck = fieldAccess != null ? fieldAccess.nullCheck : false;
msym = (refSym instanceof MethodSymbol) ? (MethodSymbol) refSym : null;
funcSym = expressionSymbol(tree.meth); //either MethodSymbol or VarSymbol
Name selectorIdName = (selector != null && selector.getVisageTag() == VisageTag.IDENT) ? ((VisageIdent) selector).getName() : null;
thisCall = selectorIdName == names._this;
superCall = selectorIdName == names._super;
ClassSymbol csym = currentClass().sym;
useInvoke = meth.type instanceof FunctionType;
selectorSym = selector != null? expressionSymbol(selector) : null;
boolean namedSuperCall = isNamedSuperCall();
boolean isMixinSuper = namedSuperCall && (selectorSym.flags_field & VisageFlags.MIXIN) != 0;
boolean canRename = namedSuperCall && !isMixinSuper;
renameToThis = canRename && selectorSym == csym;
renameToSuper = canRename && selectorSym != csym;
superToStatic = (superCall || namedSuperCall) && isMixinSuper;
callBound = msym != null && !useInvoke &&
((msym.flags() & VisageFlags.BOUND) != 0);
magicPointerMakeFunction = types.isSyntheticPointerFunction(msym);
// Call info
this.typeargs = tree.getTypeArguments();
this.args = tree.getArguments();
// Null Checking control
boolean selectorImmutable =
msym == null ||
msym.isStatic() ||
selector == null ||
selector.type.isPrimitive() ||
namedSuperCall ||
superCall ||
thisCall;
knownNonNull = selectorImmutable && !useInvoke;
}
@Override
void addPreface(JCStatement stat) {
if (inTranslateArgs) {
// translating args
if (argumentPreface == null) {
argumentPreface = new ListBuffer<JCStatement>();
}
// put it in argument preface statements
argumentPreface.append(stat);
} else {
super.addPreface(stat);
}
}
@Override
JCExpression translateToCheck(VisageExpression expr) {
JCExpression newExpr = null;
if (renameToSuper || superCall) {
newExpr = resolveSuper(funcSym.owner);
} else if (renameToThis || thisCall) {
newExpr = getReceiver(funcSym);
} else if (superToStatic) {
newExpr = staticReference(funcSym);
}
if (newExpr != null) {
return !useInvoke ? newExpr :
Select(newExpr, attributeValueName(funcSym));
}
else {
return super.translateToCheck(expr);
}
}
private boolean isNamedSuperCall() {
if (msym == null || msym.isStatic() ||
!(selectorSym instanceof ClassSymbol))
return false;
else {
Type currentType = currentClass().type;
while (currentType != Type.noType) {
// selector type is same as some enclosing type, so not a super call
if (types.isSameType(currentType, selectorSym.type)) {
return false;
}
if (types.isSubtype(currentType, selectorSym.type)) {
return true;
}
currentType = currentType.getEnclosingType();
}
return false;
}
}
JCExpression fullExpression(JCExpression mungedToCheckTranslated) {
JCExpression tMeth = Select(mungedToCheckTranslated, methodName());
JCMethodInvocation app = m().Apply(translateExprs(typeargs), tMeth, determineArgs());
JCExpression full = null;
if (callBound) {
// Call to bound functions in bind context is handled elsewhere.
/*
* call Pointer.get() on the return value of bound function to get
* computed value of bound function. We need to cast to the right
* type as Pointer.get() returns Object type value.
*/
full = castFromObject(Call(app, defs.get_PointerMethodName), resultType);
} else {
full = app;
if (useInvoke) {
if (resultType != syms.voidType) {
full = typeCast(resultType, syms.objectType, full);
}
}
}
return full;
}
@Override
protected JCStatement wrapInNullCheckStatement(JCExpression full, JCExpression tToCheck, Type theResultType, Type theFullType) {
JCStatement resStmt = super.wrapInNullCheckStatement(full, tToCheck, resultType, fullType);
if (argumentPreface != null) {
argumentPreface.append(resStmt);
resStmt = m().Block(0L, argumentPreface.toList());
}
return resStmt;
}
@Override
protected JCExpression wrapInNullCheckExpression(JCExpression full, JCExpression tToCheck, Type theResultType, Type theFullType) {
full = super.wrapInNullCheckExpression(full, tToCheck, resultType, fullType);
if (argumentPreface != null) {
full = new BlockExprJCBlockExpression(0L, argumentPreface.toList(), full);
}
return full;
}
Name methodName() {
return useInvoke? defs.invoke_VisageObjectMethodName : functionName(msym, superToStatic, callBound);
}
@Override
VisageExpression getToCheck() {
return useInvoke? meth : selector;
}
@Override
boolean needNullCheck() {
return !selectorNullCheck && !knownNonNull && super.needNullCheck();
}
// make it final and set "inTranslateArgs" flag
final List<JCExpression> determineArgs() {
try {
inTranslateArgs = true;
return determineArgsImpl();
} finally {
inTranslateArgs = false;
}
}
/**
* Compute the translated arguments.
*/
List<JCExpression> determineArgsImpl() {
final List<Type> formals = meth.type.getParameterTypes();
final boolean usesVarArgs = args != null && msym != null &&
(msym.flags() & Flags.VARARGS) != 0 &&
(formals.size() != args.size() ||
types.isConvertible(args.last().type,
types.elemtype(formals.last())));
ListBuffer<JCExpression> targs = ListBuffer.lb();
// if this is a super.foo(x) call, "super" will be translated to referenced class,
// so we add a receiver arg to make a direct call to the implementing method MyClass.foo(receiver$, x)
if (superToStatic) {
targs.append(id(defs.receiverName));
}
if (callBound) {
// This section handles argument expression for bound function calls
// from non-bind call site.
/*
* For each source bound function argument expression, we create a wrapper
* variable of type VisageConstant and pass it along with VisageConstant.VOFF$value
* as the arguments to the bound function.
*/
Type formal = null;
List<Type> t = formals;
for (VisageExpression arg : args) {
formal = t.head;
t = t.tail;
// pass VisageConstant wrapper for argument expression
targs.append(Call(defs.VisageConstant_make, translateExpr(arg, formal)));
// pass VisageConstant.VOFF$value as offset value
targs.append(Select(makeType(syms.visage_ConstantType), defs.varOFF$valueName));
}
} else {
boolean handlingVarargs = false;
Type formal = null;
List<Type> t = formals;
ListBuffer<JCExpression> rargs = null;
int argNum = 0;
for (List<VisageExpression> l = args; l.nonEmpty(); l = l.tail) {
VisageExpression arg = l.head;
if (!handlingVarargs) {
formal = t.head;
t = t.tail;
if (usesVarArgs && t.isEmpty()) {
formal = types.elemtype(formal);
handlingVarargs = true;
}
}
JCExpression argExpr = translateArg(arg, formal);
// Proper cast to Object.
if (useInvoke) {
argExpr = typeCast(types.boxedTypeOrType(formal), formal, argExpr);
}
if (! useInvoke || argNum < 2)
targs.append(argExpr);
else {
if (rargs == null)
rargs = ListBuffer.lb();
rargs.append(argExpr);
}
argNum++;
}
if (useInvoke) {
for (; argNum < 2; argNum++)
targs.append(Null()); // arg1, arg2
if (argNum <= 2)
targs.append(Null());
else
targs.append(m().NewArray(makeType(syms.objectType), List.of(Int(argNum-2)), rargs.toList()));
}
if (magicPointerMakeFunction) {
// Pointer.make has just two arguments (inst, varNum) -- we need to
// add an extra argument - so that the Pointer.make(Type, VisageObject, int) is called.
VisageVarRef varRef = (VisageVarRef)args.head;
JCExpression varType = makeKeyValueTargetType(varRef.getVarSymbol().type);
targs.prepend(varType);
}
}
return targs.toList();
}
JCExpression translateArg(VisageExpression arg, Type formal) {
return preserveSideEffects(formal, arg, translateExpr(arg, formal));
}
}
class TimeLiteralTranslator extends ExpressionTranslator {
VisageExpression value;
TimeLiteralTranslator(VisageTimeLiteral tree) {
super(tree.pos());
this.value = tree.value;
}
protected ExpressionResult doit() {
return toResult(
Call(defs.Duration_valueOf, translateExpr(value, syms.doubleType)),
syms.visage_DurationType);
}
}
class LengthLiteralTranslator extends ExpressionTranslator {
VisageExpression value;
LengthUnit units;
LengthLiteralTranslator(VisageLengthLiteral tree) {
super(tree.pos());
this.value = tree.value;
this.units = tree.units;
}
protected ExpressionResult doit() {
JCExpression unitSelect = Select(makeType(syms.visage_LengthUnitType), names.fromString(units.name()));
return toResult(
Call(defs.Length_valueOf, translateExpr(value, syms.doubleType), unitSelect),
syms.visage_LengthType);
}
}
class AngleLiteralTranslator extends ExpressionTranslator {
VisageExpression value;
AngleUnit units;
AngleLiteralTranslator(VisageAngleLiteral tree) {
super(tree.pos());
this.value = tree.value;
this.units = tree.units;
}
protected ExpressionResult doit() {
JCExpression unitSelect = Select(makeType(syms.visage_AngleUnitType), names.fromString(units.name()));
return toResult(
Call(defs.Angle_valueOf, translateExpr(value, syms.doubleType), unitSelect),
syms.visage_AngleType);
}
}
class ColorLiteralTranslator extends ExpressionTranslator {
VisageExpression value;
ColorLiteralTranslator(VisageColorLiteral tree) {
super(tree.pos());
this.value = tree.value;
}
protected ExpressionResult doit() {
return toResult(
Call(defs.Color_valueOf, translateExpr(value, syms.intType), m().Literal(TypeTags.BOOLEAN, 1)),
syms.visage_ColorType);
}
}
class FunctionTranslator extends Translator {
final VisageFunctionDefinition tree;
final boolean maintainContext;
final MethodType mtype;
final MethodSymbol sym;
final Symbol owner;
final Name name;
final boolean isBound;
final boolean isRunMethod;
final boolean isAbstract;
final boolean isStatic;
final boolean isSynthetic;
final boolean isInstanceFunction;
final boolean isInstanceFunctionAsStaticMethod;
final boolean isMixinClass;
FunctionTranslator(VisageFunctionDefinition tree, boolean maintainContext) {
super(tree.pos());
this.tree = tree;
this.maintainContext = maintainContext;
this.mtype = (MethodType) tree.type;
this.sym = (MethodSymbol) tree.sym;
this.owner = sym.owner;
this.name = tree.name;
this.isBound = (sym.flags() & VisageFlags.BOUND) != 0;
this.isRunMethod = syms.isRunMethod(tree.sym);
this.isMixinClass = currentClass().isMixinClass();
long originalFlags = tree.mods.flags;
this.isAbstract = (originalFlags & Flags.ABSTRACT) != 0L;
this.isSynthetic = (originalFlags & Flags.SYNTHETIC) != 0L;
this.isStatic = (originalFlags & Flags.STATIC) != 0L;
this.isInstanceFunction = !isAbstract && !isStatic && !isSynthetic;
this.isInstanceFunctionAsStaticMethod = isInstanceFunction && isMixinClass;
}
private JCBlock makeRunMethodBody(VisageBlock bexpr) {
final VisageExpression value = bexpr.value;
JCBlock block;
if (value == null || value.type == syms.voidType) {
// the block has no value: translate as simple statement and add a null return
block = translateToBlock(bexpr, syms.voidType);
clearDiagPos();
block.stats = block.stats.append(Return(Null()));
} else {
// block has a value, return it
block = translateToBlock(bexpr, value.type);
final Type valueType = value.type;
if (valueType != null && valueType.isPrimitive()) {
// box up any primitives returns so they return Object -- the return type of the run method
new TreeTranslator() {
@Override
public void visitReturn(JCReturn tree) {
tree.expr = makeBox(tree.expr.pos(), tree.expr, valueType);
result = tree;
}
// do not descend into inner classes
@Override
public void visitClassDef(JCClassDecl tree) {
result = tree;
}
}.translate(block);
}
}
return block;
}
private long methodFlags() {
long methodFlags = tree.mods.flags;
methodFlags &= ~Flags.PROTECTED;
methodFlags &= ~Flags.SYNTHETIC;
methodFlags |= Flags.PUBLIC;
if (isInstanceFunctionAsStaticMethod) {
methodFlags |= Flags.STATIC;
}
return methodFlags;
}
private List<JCVariableDecl> methodParameters() {
ListBuffer<JCVariableDecl> params = ListBuffer.lb();
if (isInstanceFunctionAsStaticMethod) {
// if we are converting a standard instance function (to a static method), the first parameter becomes a reference to the receiver
params.prepend(ReceiverParam(currentClass()));
}
if (isBound) {
for (VisageVar visageVar : tree.getParams()) {
params.append(Param(syms.visage_ObjectType,
boundFunctionObjectParamName(visageVar.name)));
params.append(Param(syms.visage_IntegerType,
boundFunctionVarNumParamName(visageVar.name)));
}
} else {
for (VisageVar visageVar : tree.getParams()) {
params.append(convertParam(visageVar));
}
}
return params.toList();
}
private JCBlock methodBody() {
// construct the body of the translated function
VisageBlock bexpr = tree.getBodyExpression();
JCBlock body;
if (bexpr == null) {
body = null; // null if no block expression
} else if (isRunMethod) {
// it is a module level run method, do special translation
body = makeRunMethodBody(bexpr);
} else {
// the "normal" case
ListBuffer<JCStatement> stmts = ListBuffer.lb();
if (! isBound) {
for (VisageVar visageVar : tree.getParams()) {
if (types.isSequence(visageVar.sym.type)) {
setDiagPos(visageVar);
stmts.append(CallStmt(id(visageVar.getName()), defs.incrementSharing_SequenceMethodName));
}
}
} // else FIXME: what should we do for bound function sequence params?
setDiagPos(bexpr);
stmts.appendList(translateToStatementsResult(bexpr, isBound? syms.visage_PointerType : mtype.getReturnType()).statements());
body = Block(stmts);
body.endpos = bexpr.endpos;
}
if (isInstanceFunction && !isMixinClass) {
//TODO: unfortunately, some generated code still expects a receiver$ to always be present.
// In the instance as instance case, there is no receiver param, so allow generated code
// to function by adding: var receiver = this;
//TODO: this should go away
clearDiagPos();
body.stats = body.stats.prepend( m().VarDef(
m().Modifiers(Flags.FINAL),
defs.receiverName,
id(interfaceName(currentClass())),
id(names._this)));
}
return body;
}
private JCMethodDecl makeMethod(long flags, JCBlock body, List<JCVariableDecl> params) {
JCMethodDecl meth = m().MethodDef(
addAccessAnnotationModifiers(diagPos, tree.mods.flags, m().Modifiers(flags)),
functionName(sym, isInstanceFunctionAsStaticMethod, isBound),
makeReturnTypeTree(diagPos, sym, isBound),
m().TypeParams(mtype.getTypeArguments()),
params,
m().Types(mtype.getThrownTypes()), // makeThrows(diagPos), //
body,
null);
meth.sym = sym;
meth.type = tree.type;
if (isBound) {
meth.mods.annotations = meth.mods.annotations.append(methodSignature());
}
return meth;
}
private JCAnnotation methodSignature() {
JCAnnotation sig = make.Annotation(
makeIdentifier(diagPos, VisageSymtab.signatureAnnotationClassNameString),
List.<JCExpression>of(makeLit(diagPos, syms.stringType, types.toSignature(sym.type))));
return sig;
}
protected SpecialResult doit() {
VisageTree prevWhere = getAttrEnv().where;
Yield prevYield = yield();
Type prevTargetType = targetType;
getAttrEnv().where = tree;
yieldKind = ToStatement;
targetType = null;
ReceiverContext prevContext = receiverContext();
if (!maintainContext) {
setReceiverContext(isStatic ?
ReceiverContext.ScriptAsStatic :
isInstanceFunctionAsStaticMethod ?
ReceiverContext.InstanceAsStatic :
ReceiverContext.InstanceAsInstance);
}
try {
return new SpecialResult(makeMethod(methodFlags(), methodBody(), methodParameters()));
} finally {
setReceiverContext(prevContext);
yieldKind = prevYield;
targetType = prevTargetType;
getAttrEnv().where = prevWhere;
}
}
}
class IdentTranslator extends MemberReferenceTranslator {
protected final VisageIdent tree;
protected final Symbol sym;
IdentTranslator(VisageIdent tree) {
super(tree.pos());
this.tree = tree;
this.sym = tree.sym;
}
protected ExpressionResult doit() {
return toResult(doitExpr(), tree.type);
}
protected JCExpression doitExpr() {
if (tree.getName() == names._this) {
// in the static implementation method, "this" becomes "receiver$"
return getReceiverOrThis(sym);
} else if (tree.getName() == names._super) {
if (types.isMixin(tree.type.tsym)) {
// "super" becomes just the class where the static implementation method is defined
// the rest of the implementation is in visitFunctionInvocation
return id(tree.type.tsym.name);
} else {
// Just use super.
return resolveSuper(tree.sym.owner);
}
}
int kind = sym.kind;
if (kind == Kinds.TYP) {
// This is a class name, replace it with the full name (no generics)
return makeType(types.erasure(sym.type), false);
}
// if this is an instance reference to an attribute or function, it needs to go the the "receiver$" arg,
// and possible outer access methods
JCExpression convert;
if (sym.isStatic()) {
// make class-based direct static reference: Foo.x
convert = reference(sym);
} else {
if ((kind == Kinds.VAR || kind == Kinds.MTH) &&
sym.owner.kind == Kinds.TYP) {
// it is a non-static attribute or function class member
// reference it through the receiver
convert = Select(getReceiver(sym),tree.getName());
} else {
convert = id(tree.getName());
}
}
return convertVariableReference(convert, sym);
}
}
/**
* Bound identifier reference (non-sequence)
*/
class BoundIdentTranslator extends IdentTranslator {
BoundIdentTranslator(VisageIdent tree) {
super(tree);
}
protected void addIdentInterClassBindee(VisageVarSymbol vsym) {
if (vsym.owner.kind == Kinds.TYP && !vsym.isSpecial()) {
if (vsym.isStatic()) {
// Script var
VisageClassSymbol classSym = (VisageClassSymbol) vsym.owner;
VisageVarSymbol scriptAccess = visagemake.ScriptAccessSymbol(classSym);
addInterClassBindee(scriptAccess, vsym);
} else {
// Outer class reference through "this"
addInterClassBindee(visagemake.ThisSymbol(vsym.owner.type), vsym);
}
}
}
@Override
protected ExpressionResult doit() {
if (sym instanceof VisageVarSymbol) {
VisageVarSymbol vsym = (VisageVarSymbol) sym;
boolean isScriptContext = receiverContext() == ReceiverContext.ScriptAsStatic;
if ((isScriptContext == sym.isStatic()) && currentClass().sym.isSubClass(sym.owner, types)) {
// The var is in our class (or a superclass)
addBindee(vsym);
} else {
// Possible script or outer class reference
addIdentInterClassBindee(vsym);
}
}
return super.doit();
}
}
/**
* Bound member select (non-sequence)
*/
class BoundSelectTranslator extends SelectTranslator {
protected final VisageVarSymbol selectResSym;
BoundSelectTranslator(VisageSelect tree, VisageVarSymbol selectResSym) {
super(tree);
this.selectResSym = selectResSym;
}
@Override
protected ExpressionResult doit() {
VisageExpression selectorExpr = tree.getExpression();
if (selectorExpr instanceof VisageIdent) {
VisageIdent selector = (VisageIdent) selectorExpr;
if (selector.sym instanceof VisageVarSymbol) {
VisageVarSymbol selectorSym = (VisageVarSymbol)selector.sym;
if (selectorSym.isSpecial()) {
addInterClassBindee(selectorSym, refSym);
} else if (canChange()) {
if (tree.sym instanceof VisageVarSymbol) {
// cases that need a null check are the same as cases that have changing dependencies
addBindee(selectorSym);
addInterClassBindee(selectorSym, refSym);
}
}
}
}
return (ExpressionResult) super.doit();
}
}
class BoundBlockExpressionTranslator extends ExpressionTranslator {
private final VisageExpression value;
private final List<VisageExpression> statements;
BoundBlockExpressionTranslator(VisageBlock tree) {
super(tree.pos());
this.value = tree.value;
this.statements = tree.getStmts();
}
protected ExpressionResult doit() {
for (VisageExpression expr : statements) {
translateStmt(expr, syms.voidType);
}
JCExpression tvalue = translateExpr(value, targetType);
return toResult(tvalue, targetType);
}
}
/**
* Translator for assignment, and other mutating operations
*/
abstract class AssignTranslator extends NullCheckTranslator {
protected final VisageExpression ref;
protected final VisageExpression indexOrNull;
protected final VisageExpression rhs;
protected final VisageExpression selector;
protected final JCExpression rhsTranslated;
protected final boolean useAccessors;
protected final boolean selectorNullCheck;
/**
*
* @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 fullType The type of the resultant expression
* @param rhs The expression acting on ref
*/
AssignTranslator(final DiagnosticPosition diagPos, final VisageExpression ref, final VisageExpression indexOrNull, Type fullType, final VisageExpression rhs) {
super(diagPos, expressionSymbol(ref), fullType);
this.ref = ref;
this.indexOrNull = indexOrNull;
this.rhs = rhs;
this.selector = (ref instanceof VisageSelect) ? ((VisageSelect) ref).getExpression() : null;
this.selectorNullCheck = (ref instanceof VisageSelect) ? ((VisageSelect) ref).nullCheck : false;
if (rhs != null) {
JCExpression translated = convertNullability(diagPos, translateExpr(rhs, rhsType()), rhs, rhsType());
this.rhsTranslated = preserveSideEffects(fullType, rhs, translated);
} else {
this.rhsTranslated = null;
}
this.useAccessors = (refSym!=null && refSym.kind==Kinds.VAR)?
((VisageVarSymbol)refSym).useAccessors()
: false;
}
/**
* Constructor for assignment forms: =, ++, +=, etc
* @param diagPos
* @param lhs
* @param rhs
*/
AssignTranslator(final DiagnosticPosition diagPos, final VisageExpression lhs, final VisageExpression rhs) {
this(
diagPos,
lhs.getVisageTag() == VisageTag.SEQUENCE_INDEXED? ((VisageSequenceIndexed)lhs).getSequence() : lhs,
lhs.getVisageTag() == VisageTag.SEQUENCE_INDEXED? ((VisageSequenceIndexed)lhs).getIndex() : null,
lhs.type,
rhs);
}
JCExpression buildRHS(JCExpression rhsTranslated) {
return rhsTranslated;
}
JCExpression defaultFullExpression(JCExpression lhsTranslated, JCExpression rhsTranslated) {
throw new AssertionError("should not reach here");
}
@Override
VisageExpression getToCheck() {
return selector;
}
@Override
boolean needNullCheck() {
return !selectorNullCheck && selector != null && super.needNullCheck();
}
JCExpression translateIndex() {
return indexOrNull==null? null : translateExpr(indexOrNull, syms.intType);
}
// Figure out the instance containing the variable
JCExpression instance(JCExpression tToCheck) {
if (staticReference) {
return getReceiver(refSym);
} else if (tToCheck == null) {
return id(names._this);
} else {
return tToCheck;
}
}
JCExpression sequencesOp(RuntimeMethod meth, JCExpression tToCheck) {
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
VisageVarSymbol vsym = (VisageVarSymbol) refSym;
if (! vsym.useAccessors()) {
// In this case make a block expression - roughly:
// { Foo tmp = rhs;
// lhs = sequenceAction(lhs, tmp);
// tmp;
// }
args.append(Getter(tToCheck, vsym));
JCVariableDecl tv;
// The special case for JCLiteral is to avoid a bug in Gen - it
// optimizes away initializing a variable that is constant,
// but somehow gets confused when later trying to load the variable.
// Probably something to do with BlockExpressions confusing it.
if (rhsTranslated instanceof JCLiteral || targetType == syms.voidType) {
tv = null;
args.append(rhsTranslated);
}
else {
tv = TmpVar(rhsType(), buildRHS(rhsTranslated));
args.append(id(tv));
}
JCExpression tIndex = translateIndex();
if (tIndex != null) {
args.append(tIndex);
}
JCExpression assign = Setter(tToCheck, vsym, Call(meth, args));
if (targetType == syms.voidType)
return assign;
if (rhsTranslated instanceof JCLiteral)
return BlockExpression(Stmt(assign), translateExpr(rhs, rhsType()));
return BlockExpression(
tv,
Stmt(assign),
id(tv));
} else {
// Instance variable sequence -- roughly:
// sequenceAction(instance, varNum, rhs);
args.append(instance(tToCheck));
args.append(Offset(copyOfTranslatedToCheck(tToCheck), vsym));
args.append(buildRHS(rhsTranslated));
JCExpression tIndex = translateIndex();
if (tIndex != null) {
args.append(tIndex);
}
return Call(meth, args);
}
}
JCExpression makeSliceEndPos(VisageSequenceSlice tree) {
JCExpression endPos;
if (tree.getLastIndex() == null) {
endPos = Call(
translateExpr(tree.getSequence(), null),
defs.size_SequenceMethodName);
if (tree.getEndKind() == SequenceSliceTree.END_EXCLUSIVE) {
endPos = MINUS(endPos, Int(1));
}
} else {
endPos = translateExpr(tree.getLastIndex(), syms.intType);
if (tree.getEndKind() == SequenceSliceTree.END_INCLUSIVE) {
endPos = PLUS(endPos, Int(1));
}
}
return endPos;
}
@Override
JCExpression fullExpression(JCExpression tToCheck) {
if (indexOrNull != null) {
if (ref.type.tag == TypeTags.ARRAY) {
// set of an array element -- s[i]=8, set the array element
JCExpression tArray = translateExpr(ref, ref.type);
return m().Assign(m().Indexed(tArray, translateIndex()), buildRHS(rhsTranslated));
} else {
// set of a sequence element -- s[i]=8, call the sequence set method
return sequencesOp(defs.Sequences_set, tToCheck);
}
} else {
if (useAccessors) {
return postProcessExpression(buildSetter(tToCheck, buildRHS(rhsTranslated)));
} else if (refSym instanceof VarSymbol && ((VisageVarSymbol)refSym).isVisageMember()) {
if (((VisageVarSymbol)refSym).useGetters()) {
return Setter(tToCheck, refSym, rhsTranslated);
}
else {
JCExpression lhsTranslated = selector != null ?
Select(tToCheck, attributeValueName(refSym)) :
Getter(refSym);
JCExpression res = defaultFullExpression(lhsTranslated, rhsTranslated);
return res;
}
} else {
//TODO: possibly should use, or be unified with convertVariableReference
JCExpression lhsTranslated = selector != null ?
Select(tToCheck, refSym.name) :
reference(refSym);
JCExpression res = defaultFullExpression(lhsTranslated, rhsTranslated);
return res;
}
}
}
/**
* Override to change the translation type of the right-hand side
*/
protected Type rhsType() {
if (indexOrNull != null) {
// Indexed assignment
if (types.isArray(rhs.type) || types.isSequence(rhs.type)) {
return ref.type;
} else {
return types.arrayOrSequenceElementType(ref.type);
}
} else {
if (refSym == null) {
return ref.type;
} else {
// Handle type inferencing not reseting the ident type
return refSym.type;
}
}
}
/**
* Override to change result in the non-default case.
*/
protected JCExpression postProcessExpression(JCExpression built) {
return built;
}
JCExpression buildSetter(JCExpression tc, JCExpression rhsComplete) {
return Setter(tc, refSym, rhsComplete);
}
JCExpression buildGetter(JCExpression tc) {
return Getter(tc, refSym);
}
}
class UnaryOperationTranslator extends ExpressionTranslator {
private final VisageUnary tree;
private final VisageExpression expr;
private final JCExpression transExpr;
UnaryOperationTranslator(VisageUnary tree) {
super(tree.pos());
this.tree = tree;
this.expr = tree.getExpression();
this.transExpr = translateExpr(expr, expr.type);
}
protected AbstractStatementsResult doit() {
switch (tree.getVisageTag()) {
case SIZEOF:
if (expr.type.tag == TypeTags.ARRAY) {
return toResult(Select(transExpr, defs.length_ArrayFieldName), syms.intType);
}
return toResult(translateSizeof(expr, transExpr), syms.intType);
case REVERSE:
if (types.isSequence(expr.type)) {
// call runtime reverse of a sequence
return toResult(
Call(defs.Sequences_reverse, transExpr),
expr.type);
} else {
// this isn't a sequence, just make it a sequence
return toResult(convertTranslated(transExpr, diagPos, expr.type, targetType), targetType);
}
case NEG:
if (types.isSameType(tree.type, syms.visage_DurationType)) {
return toResult(
Call(translateExpr(tree.arg, tree.arg.type), defs.negate_DurationMethodName),
syms.visage_DurationType);
}
if (types.isSameType(tree.type, syms.visage_LengthType)) {
return toResult(
Call(translateExpr(tree.arg, tree.arg.type), defs.negate_LengthMethodName),
syms.visage_LengthType);
}
if (types.isSameType(tree.type, syms.visage_AngleType)) {
return toResult(
Call(translateExpr(tree.arg, tree.arg.type), defs.negate_AngleMethodName),
syms.visage_AngleType);
}
if (types.isSameType(tree.type, syms.visage_ColorType)) {
return toResult(
Call(translateExpr(tree.arg, tree.arg.type), defs.negate_ColorMethodName),
syms.visage_ColorType);
}
default:
return toResult(
m().Unary(tree.getOperatorTag(), transExpr),
tree.type);
}
}
}
class BinaryOperationTranslator extends ExpressionTranslator {
final VisageBinary tree;
final Type lhsType;
final Type rhsType;
BinaryOperationTranslator(DiagnosticPosition diagPos, final VisageBinary tree) {
super(diagPos);
this.tree = tree;
this.lhsType = tree.lhs.type;
this.rhsType = tree.rhs.type;
}
JCExpression lhs(Type type) {
return translateExpr(tree.lhs, type);
}
JCExpression lhs() {
return lhs(null);
}
JCExpression rhs(Type type) {
return translateExpr(tree.rhs, type);
}
JCExpression rhs() {
return rhs(null);
}
//TODO: after type system is figured out, this needs to be revisited
/**
* Check if a primitive has the default value for its type.
*/
private JCExpression makePrimitiveNullCheck(Type argType, JCExpression arg) {
VisageTypeRepresentation typeRep = types.typeRep(argType);
JCExpression defaultValue = makeLit(diagPos, argType, typeRep.defaultValue());
return EQ(arg, defaultValue);
}
/**
* Check if a non-primitive has the default value for its type.
*/
private JCExpression makeObjectNullCheck(Type argType, JCExpression arg) {
if (types.isSequence(argType) || types.isSameType(argType, syms.visage_StringType)) {
return Call(defs.Checks_isNull, arg);
} else {
return EQnull(arg);
}
}
/**
* Make a .equals() comparison with a null check on the receiver
*/
private JCExpression makeFullCheck(JCExpression lhs, JCExpression rhs) {
return Call(defs.Checks_equals, lhs, rhs);
}
/**
* Return the translation for a == comparision
*/
private JCExpression translateEqualsEquals() {
final boolean reqSeq = types.isSequence(lhsType) ||
types.isSequence(rhsType);
Type expected = tree.operator.type.getParameterTypes().head;
if (reqSeq) {
Type left = types.isSequence(lhsType) ? types.elementType(lhsType) : lhsType;
Type right = types.isSequence(rhsType) ? types.elementType(rhsType) : rhsType;
if (left.isPrimitive() && right.isPrimitive() && left == right) {
expected = left;
}
}
Type req = reqSeq ? types.sequenceType(expected) : null;
// this is an x == y
if (lhsType.getKind() == TypeKind.NULL) {
if (rhsType.getKind() == TypeKind.NULL) {
// both are known to be null
return True();
} else if (rhsType.isPrimitive()) {
// lhs is null, rhs is primitive, do default check
return makePrimitiveNullCheck(rhsType, rhs(req));
} else {
// lhs is null, rhs is non-primitive, figure out what check to do
return makeObjectNullCheck(rhsType, rhs(req));
}
} else if (lhsType.isPrimitive()) {
if (rhsType.getKind() == TypeKind.NULL) {
// lhs is primitive, rhs is null, do default check on lhs
return makePrimitiveNullCheck(lhsType, lhs(req));
} else if (rhsType.isPrimitive()) {
// both are primitive, use ==
return EQ(lhs(req), rhs(req));
} else {
// lhs is primitive, rhs is non-primitive, use equals(), but switch them
JCVariableDecl sl = TmpVar(req!=null? req : lhsType, lhs(req)); // eval first to keep the order correct
return BlockExpression(
sl,
makeFullCheck(rhs(req), id(sl.name)));
}
} else {
if (rhsType.getKind() == TypeKind.NULL) {
// lhs is non-primitive, rhs is null, figure out what check to do
return makeObjectNullCheck(lhsType, lhs(req));
} else {
// lhs is non-primitive, use equals()
return makeFullCheck(lhs(req), rhs(req));
}
}
}
JCExpression op(JCExpression leftSide, Name methodName, JCExpression rightSide) {
return Call(leftSide, methodName, rightSide);
}
boolean isDuration(Type type) {
return types.isSameType(type, syms.visage_DurationType);
}
final Type durationNumericType = syms.visage_NumberType;
JCExpression durationOp() {
switch (tree.getVisageTag()) {
case PLUS:
return op(lhs(), defs.add_DurationMethodName, rhs());
case MINUS:
return op(lhs(), defs.sub_DurationMethodName, rhs());
case DIV:
return op(lhs(), defs.div_DurationMethodName, rhs(isDuration(rhsType)? null : durationNumericType));
case MUL: {
// lhs.mul(rhs);
JCExpression rcvr;
JCExpression arg;
if (isDuration(lhsType)) {
rcvr = lhs();
arg = rhs(durationNumericType);
} else {
//TODO: This may get side-effects out-of-order.
// A simple fix is to use a static Duration.mul(double,Duration).
// Another is to use a Block and a temporary.
rcvr = rhs();
arg = lhs(durationNumericType);
}
return op(rcvr, defs.mul_DurationMethodName, arg);
}
case LT:
return op(lhs(), defs.lt_DurationMethodName, rhs());
case LE:
return op(lhs(), defs.le_DurationMethodName, rhs());
case GT:
return op(lhs(), defs.gt_DurationMethodName, rhs());
case GE:
return op(lhs(), defs.ge_DurationMethodName, rhs());
}
throw new RuntimeException("Internal Error: bad Duration operation");
}
boolean isLength(Type type) {
return types.isSameType(type, syms.visage_LengthType);
}
final Type lengthNumericType = syms.visage_NumberType;
JCExpression lengthOp() {
switch (tree.getVisageTag()) {
case PLUS:
return op(lhs(), defs.add_LengthMethodName, rhs());
case MINUS:
return op(lhs(), defs.sub_LengthMethodName, rhs());
case DIV:
return op(lhs(), defs.div_LengthMethodName, rhs(isLength(rhsType)? null : lengthNumericType));
case MUL: {
// lhs.mul(rhs);
JCExpression rcvr;
JCExpression arg;
if (isLength(lhsType)) {
rcvr = lhs();
arg = rhs(lengthNumericType);
} else {
//TODO: This may get side-effects out-of-order.
// A simple fix is to use a static Length.mul(double,Length).
// Another is to use a Block and a temporary.
rcvr = rhs();
arg = lhs(lengthNumericType);
}
return op(rcvr, defs.mul_LengthMethodName, arg);
}
case LT:
return op(lhs(), defs.lt_LengthMethodName, rhs());
case LE:
return op(lhs(), defs.le_LengthMethodName, rhs());
case GT:
return op(lhs(), defs.gt_LengthMethodName, rhs());
case GE:
return op(lhs(), defs.ge_LengthMethodName, rhs());
}
throw new RuntimeException("Internal Error: bad Length operation");
}
boolean isAngle(Type type) {
return types.isSameType(type, syms.visage_AngleType);
}
final Type angleNumericType = syms.visage_NumberType;
JCExpression angleOp() {
switch (tree.getVisageTag()) {
case PLUS:
return op(lhs(), defs.add_AngleMethodName, rhs());
case MINUS:
return op(lhs(), defs.sub_AngleMethodName, rhs());
case DIV:
return op(lhs(), defs.div_AngleMethodName, rhs(isAngle(rhsType)? null : angleNumericType));
case MUL: {
// lhs.mul(rhs);
JCExpression rcvr;
JCExpression arg;
if (isAngle(lhsType)) {
rcvr = lhs();
arg = rhs(angleNumericType);
} else {
//TODO: This may get side-effects out-of-order.
// A simple fix is to use a static Angle.mul(double,Angle).
// Another is to use a Block and a temporary.
rcvr = rhs();
arg = lhs(angleNumericType);
}
return op(rcvr, defs.mul_AngleMethodName, arg);
}
case LT:
return op(lhs(), defs.lt_AngleMethodName, rhs());
case LE:
return op(lhs(), defs.le_AngleMethodName, rhs());
case GT:
return op(lhs(), defs.gt_AngleMethodName, rhs());
case GE:
return op(lhs(), defs.ge_AngleMethodName, rhs());
}
throw new RuntimeException("Internal Error: bad Angle operation");
}
boolean isColor(Type type) {
return types.isSameType(type, syms.visage_ColorType);
}
final Type colorNumericType = syms.visage_NumberType;
JCExpression colorOp() {
switch (tree.getVisageTag()) {
case PLUS:
return op(lhs(), defs.add_ColorMethodName, rhs());
case MINUS:
return op(lhs(), defs.sub_ColorMethodName, rhs());
case DIV:
return op(lhs(), defs.div_ColorMethodName, rhs(isColor(rhsType)? null : colorNumericType));
case MUL: {
// lhs.mul(rhs);
JCExpression rcvr;
JCExpression arg;
if (isColor(lhsType)) {
rcvr = lhs();
arg = rhs(colorNumericType);
} else {
//TODO: This may get side-effects out-of-order.
// A simple fix is to use a static Color.mul(double,Color).
// Another is to use a Block and a temporary.
rcvr = rhs();
arg = lhs(colorNumericType);
}
return op(rcvr, defs.mul_ColorMethodName, arg);
}
case LT:
return op(lhs(), defs.lt_ColorMethodName, rhs());
case LE:
return op(lhs(), defs.le_ColorMethodName, rhs());
case GT:
return op(lhs(), defs.gt_ColorMethodName, rhs());
case GE:
return op(lhs(), defs.ge_ColorMethodName, rhs());
}
throw new RuntimeException("Internal Error: bad Color operation");
}
/**
* Translate a binary expressions
*/
protected ExpressionResult doit() {
return toResult(doitExpr(), tree.type);
}
JCExpression doitExpr() {
//TODO: handle <>
if (tree.getVisageTag() == VisageTag.EQ) {
return translateEqualsEquals();
} else if (tree.getVisageTag() == VisageTag.NE) {
return NOT(translateEqualsEquals());
} else {
// anything other than == or !=
// Duration type operator overloading
if ((isDuration(lhsType) || isDuration(rhsType)) &&
tree.operator == null) { // operator check is to try to get a decent error message by falling through if the Duration method isn't matched
return durationOp();
}
// Length type operator overloading
if ((isLength(lhsType) || isLength(rhsType)) &&
tree.operator == null) { // operator check is to try to get a decent error message by falling through if the Length method isn't matched
return lengthOp();
}
// Angle type operator overloading
if ((isAngle(lhsType) || isAngle(rhsType)) &&
tree.operator == null) { // operator check is to try to get a decent error message by falling through if the Angle method isn't matched
return angleOp();
}
// Color type operator overloading
if ((isColor(lhsType) || isColor(rhsType)) &&
tree.operator == null) { // operator check is to try to get a decent error message by falling through if the Color method isn't matched
return colorOp();
}
return m().Binary(tree.getOperatorTag(), lhs(), rhs());
}
}
}
class TypeConversionTranslator extends ExpressionTranslator {
final JCExpression translated;
final Type sourceType;
final Type targettedType;
final boolean sourceIsSequence;
final boolean targetIsSequence;
final boolean sourceIsArray;
final boolean targetIsArray;
TypeConversionTranslator(DiagnosticPosition diagPos, JCExpression translated, Type sourceType, Type targettedType) {
super(diagPos);
this.translated = translated;
this.sourceType = sourceType;
this.targettedType = targettedType;
this.sourceIsSequence = types.isSequence(sourceType);
this.targetIsSequence = types.isSequence(targettedType);
this.sourceIsArray = types.isArray(sourceType);
this.targetIsArray = types.isArray(targettedType);
}
private JCExpression convertNumericSequence(final DiagnosticPosition diagPos,
final JCExpression expr, final Type inElementType, final Type targetElementType) {
JCExpression inTypeInfo = TypeInfo(diagPos, inElementType);
JCExpression targetTypeInfo = TypeInfo(diagPos, targetElementType);
return Call(
defs.Sequences_convertNumberSequence,
targetTypeInfo, inTypeInfo, expr);
}
private JCExpression convertNumericToCharSequence(final DiagnosticPosition diagPos,
final JCExpression expr, final Type inElementType) {
JCExpression inTypeInfo = TypeInfo(diagPos, inElementType);
return Call(
defs.Sequences_convertNumberToCharSequence,
inTypeInfo, expr);
}
private JCExpression convertCharToNumericSequence(final DiagnosticPosition diagPos,
final JCExpression expr, final Type targetElementType) {
JCExpression targetTypeInfo = TypeInfo(diagPos, targetElementType);
return Call(
defs.Sequences_convertCharToNumberSequence,
targetTypeInfo, expr);
}
protected ExpressionResult doit() {
return toResult(doitExpr(), targettedType);
}
JCExpression doitExpr() {
assert sourceType != null;
assert targettedType != null;
if (targettedType.tag == TypeTags.UNKNOWN) {
//TODO: this is bad attribution
return translated;
}
if (types.isSameType(targettedType, sourceType)) {
return translated;
}
if (targetIsArray) {
Type elemType = types.elemtype(targettedType);
if (sourceIsSequence) {
if (elemType.isPrimitive()) {
return Call(defs.Sequences_toArray[types.typeRep(elemType).ordinal()], translated);
}
ListBuffer<JCStatement> stats = ListBuffer.lb();
JCVariableDecl tmpVar = TmpVar(sourceType, translated);
stats.append(tmpVar);
JCVariableDecl sizeVar = TmpVar(syms.intType, Call(id(tmpVar), defs.size_SequenceMethodName));
stats.append(sizeVar);
JCVariableDecl arrVar = TmpVar("arr", targettedType, m().NewArray(
makeType(elemType, true),
List.<JCExpression>of(id(sizeVar.name)),
null));
stats.append(arrVar);
stats.append(CallStmt(id(tmpVar.name), defs.toArray_SequenceMethodName, List.of(
Int(0),
id(sizeVar),
id(arrVar),
Int(0))));
return BlockExpression(stats, id(arrVar));
} else {
//TODO: conversion may be needed here, but this is better than what we had
return translated;
}
} else if (sourceIsArray && targetIsSequence) {
Type sourceElemType = types.elemtype(sourceType);
List<JCExpression> args;
if (sourceElemType.isPrimitive()) {
args = List.of(translated);
} else {
args = List.of(TypeInfo(diagPos, sourceElemType), translated);
}
return Call(defs.Sequences_fromArray, args);
}
if (targetIsSequence && sourceIsSequence) {
Type sourceElementType = types.elementType(sourceType);
Type targetElementType = types.elementType(targettedType);
if (types.isSameType(sourceElementType, targetElementType))
return translated;
else if (types.isNumeric(sourceElementType) && types.isNumeric(targetElementType)) {
return convertNumericSequence(diagPos,
translated,
sourceElementType,
targetElementType);
}
else if (types.isNumeric(sourceElementType) &&
types.isSameType(targetElementType, syms.charType)) {
//numeric seq to char seq
return convertNumericToCharSequence(diagPos,
translated,
sourceElementType);
}
else if (types.isNumeric(targetElementType) &&
types.isSameType(sourceElementType, syms.charType)) {
//char seq to numeric seq
return convertCharToNumericSequence(diagPos,
translated,
targetElementType);
}
}
// Convert primitive/Object types
if (sourceType.isCompound() || sourceType.isPrimitive()) {
return make.at(diagPos).TypeCast(makeType(types.erasure(targettedType), true), translated);
}
// We should add a cast "when needed". Then visitTypeCast would just
// call this function, and not need to call makeTypeCast on the result.
// However, getting that to work is a pain - giving up for now. FIXME
return translated;
}
}
class FunctionValueTranslator extends ExpressionTranslator {
private JCExpression meth;
private final VisageFunctionDefinition def;
private final MethodType mtype;
private final Type resultType;
private final Name name;
FunctionValueTranslator(JCExpression meth, DiagnosticPosition diagPos, MethodType mtype, Type resultType) {
super(diagPos);
this.meth = meth;
this.def = null;
this.mtype = mtype;
this.resultType = resultType;
this.name = null;
}
FunctionValueTranslator(JCExpression meth, VisageFunctionDefinition def, DiagnosticPosition diagPos, MethodType mtype, Type resultType) {
super(diagPos);
this.meth = meth;
this.def = def;
this.mtype = mtype;
this.resultType = resultType;
this.name = null;
}
protected ExpressionResult doit() {
return toResult(doitExpr(), resultType);
}
protected JCTree translateInvokeCase() {
setDiagPos(diagPos);
ListBuffer<JCStatement> stmts = ListBuffer.lb();
JCBlock body;
int argNum = 0;
VisageBlock bexpr = def.getBodyExpression();
for (VisageVar visageVar : def.getParams()) {
setDiagPos(visageVar);
Name paramName = visageVar.getName();
Type paramType = visageVar.sym.type;
JCExpression arg;
if (argNum < 2)
arg = id(argNum == 0 ? defs.arg1_ArgName : defs.arg2_ArgName);
else
arg = m().Indexed(id(defs.args_ArgName), Int(argNum-2));
JCExpression initialValue = typeCast(paramType, syms.objectType, arg);
stmts.append(Var(Flags.FINAL, paramType, paramName, initialValue));
if (types.isSequence(paramType)) {
stmts.append(CallStmt(id(visageVar.getName()), defs.incrementSharing_SequenceMethodName));
}
argNum++;
}
setDiagPos(bexpr);
stmts.appendList(translateToStatementsResult(bexpr, mtype.getReturnType()).statements());
JCBlock block = Block(stmts);
// Replace any void returns to return null.
if (mtype.getReturnType() == syms.voidType) {
new TreeTranslator() {
@Override
public void visitReturn(JCReturn tree) {
if (tree.expr == null) {
tree.expr = Null();
}
result = tree;
}
// do not descend into inner classes
@Override
public void visitClassDef(JCClassDecl tree) {
result = tree;
}
}.translate(block);
}
return block;
}
JCExpression doitExpr() {
boolean isScriptContext = receiverContext() == ReceiverContext.ScriptAsStatic;
int nargs = mtype.argtypes.size();
Type functionType = syms.visage_FunctionTypes[nargs];
JCExpression functionTypeExpr = QualifiedTree(functionType.tsym.getQualifiedName().toString());
ListBuffer<JCExpression> typeArgs = ListBuffer.lb();
Type resType = types.boxedTypeOrType(mtype.restype);
typeArgs.append(makeType(resType));
for (Type argType : mtype.argtypes) {
Type paramType = types.boxedTypeOrType(argType);
typeArgs.append(makeType(paramType));
}
JCExpression funcClassType = m().TypeApply(functionTypeExpr, typeArgs.toList());
JCExpression receiverExpr = getReceiverOrThis(isScriptContext);
int number = currentClass().addInvokeCase(translateInvokeCase(), isScriptContext);
List<JCExpression> funcValueArgs = List.<JCExpression>of(receiverExpr, FuncNum(number));
return m().NewClass(null, List.<JCExpression>nil(), funcClassType, funcValueArgs, null);
}
}
abstract class NewInstanceTranslator extends ExpressionTranslator {
// Statements to set visage symbols with initial values.
protected ListBuffer<JCStatement> visageVarInits = ListBuffer.lb();
// Symbols corresponding to visage caseStats.
protected ListBuffer<VisageVarSymbol> visageVarSyms = ListBuffer.lb();
// Statements to set java symbols with initial values.
protected ListBuffer<JCStatement> javaVarInits = ListBuffer.lb();
// Symbols corresponding to java caseStats.
protected ListBuffer<VisageVarSymbol> javaVarSyms = ListBuffer.lb();
// Name to use as a temp.
protected Name tmpVarName;
NewInstanceTranslator(DiagnosticPosition diagPos, Name tmpVarName) {
super(diagPos);
this.tmpVarName = tmpVarName;
}
/**
* Does this instance have any instance initializations?
*/
protected abstract boolean hasInstanceVariableInits();
/**
* Initialize the instance variables of the instance
* @param instName
*/
protected abstract void initInstanceVariables(Name instName);
/**
* buildInstance calls this just after object is created, but before
* it's instance variables are initialized. Override to generate
* statements/expressions just after new object is created.
*/
protected void postInstanceCreation(Name instName) {
makeInitSupportCall(defs.initVars_VisageObjectMethodName, instName);
}
/**
* @return the constructor args -- translating any supplied args
*/
protected abstract List<JCExpression> completeTranslatedConstructorArgs();
protected JCExpression translateInstanceVariableInit(VisageExpression init, VisageVarSymbol vsym) {
ExpressionResult eres = translateToExpressionResult(init, vsym.type);
mergeResults(eres);
return convertNullability(init.pos(), eres.expr(), init, vsym.type);
}
void setInstanceVariable(Name instanceName, VisageBindStatus bindStatus, VisageVarSymbol vsym, VisageExpression init) {
JCExpression transInit = translateInstanceVariableInit(init, vsym);
JCExpression tc = instanceName == null ? null : id(instanceName);
JCStatement def;
clearDiagPos();
if (vsym.useAccessors()) {
if (vsym.isSequence()) {
def = CallStmt(defs.Sequences_set, tc,
Offset(id(instanceName), vsym), transInit);
} else {
def = SetterStmt(tc, vsym, transInit);
}
} else {
def = SetStmt(tc, vsym, transInit);
}
if (vsym.isVisageMember()) {
visageVarInits.append(def);
visageVarSyms.append(vsym);
} else {
javaVarInits.append(def);
javaVarSyms.append(vsym);
}
}
void makeInitSupportCall(Name methName, Name receiverName) {
addPreface(CallStmt(id(receiverName), methName));
}
JCVariableDecl makeTmpLoopVar(int initValue) {
return m().VarDef(m().Modifiers(0),
getSyntheticName("loop"),
makeType(syms.intType),
Int(initValue));
}
void makeInitApplyDefaults(Type classType, Name receiverName) {
ClassSymbol classSym = (ClassSymbol)classType.tsym;
boolean visageClass = types.isVisageClass(classSym);
int visageCount = visageVarSyms.size();
clearDiagPos();
for (JCStatement varInit : javaVarInits) {
addPreface(varInit);
}
if (!visageClass) return;
JCVariableDecl loopVar = makeTmpLoopVar(0);
Name loopName = loopVar.name;
JCExpression loopLimit = Call(id(receiverName), defs.count_VisageObjectMethodName);
JCVariableDecl loopLimitVar = TmpVar("count", syms.intType, loopLimit);
addPreface(loopLimitVar);
JCExpression loopTest = LT(id(loopName), id(loopLimitVar.name));
List<JCExpressionStatement> loopStep = List.of(m().Exec(m().Assignop(JCTree.PLUS_ASG, id(loopName), Int(1))));
JCStatement loopBody;
JCStatement applyDefaultsExpr =
CallStmt(
id(receiverName),
defs.applyDefaults_VisageObjectMethodName,
id(loopName));
if (1 < visageCount) {
// final short[] visage$0map = GETMAP$X();
JCExpression getmapExpr = Call(null, varGetMapName(classSym)); //static method in toplevel class - no need for receiver
JCVariableDecl mapVar = TmpVar("map", syms.visage_ShortArray, getmapExpr);
addPreface(mapVar);
LiteralInitVarMap varMap = getLiteralInitClassMap().getVarMap(classSym);
int[] tags = new int[visageCount];
int index = 0;
for (VisageVarSymbol varSym : visageVarSyms) {
tags[index++] = varMap.addVar(varSym);
}
ListBuffer<JCCase> cases = ListBuffer.lb();
index = 0;
for (JCStatement varInit : visageVarInits) {
cases.append(m().Case(Int(tags[index++]), List.<JCStatement>of(varInit, m().Break(null))));
}
cases.append(m().Case(null, List.<JCStatement>of(applyDefaultsExpr, m().Break(null))));
JCExpression mapExpr = m().Indexed(id(mapVar), id(loopName));
loopBody = m().Switch(mapExpr, cases.toList());
} else if (0 < visageCount) {
VisageVarSymbol varSym = visageVarSyms.first();
JCExpression varOffsetExpr = Offset(id(receiverName), varSym);
JCVariableDecl offsetVar = TmpVar("off", syms.intType, varOffsetExpr);
addPreface(offsetVar);
loopBody = If(EQ(id(loopName), id(offsetVar)),
visageVarInits.first(),
applyDefaultsExpr);
} else {
loopBody = null;
}
// Ready to init value.
JCStatement clearFlagsStmt = CallStmt(id(receiverName), defs.varFlagActionChange, id(loopName),
Int(0), id(defs.varFlagINIT_READY));
addPreface(m().ForLoop(List.<JCStatement>of(loopVar), loopTest, loopStep, Block(clearFlagsStmt, loopBody)));
}
void makeSetVarFlags(Name receiverName, Type contextType) {
for (VisageVarSymbol vsym : visageVarSyms) {
if (vsym.useAccessors()) {
Name objLitFlag = vsym.isSequence() ?
defs.varFlagINIT_OBJ_LIT_SEQUENCE :
defs.varFlagINIT_OBJ_LIT;
JCExpression flagsToSet = id(objLitFlag);
addPreface(CallStmt(
id(receiverName),
defs.varFlagActionChange,
Offset(id(receiverName), vsym),
id(defs.varFlagALL_FLAGS),
flagsToSet));
}
}
}
/**
* Return the instance building expression
* @param declaredType
* @param cdef
* @param isVisage
* @return
*/
protected ExpressionResult buildInstance(Type declaredType, VisageClassDeclaration cdef, boolean isVisage) {
Type type;
if (cdef == null) {
type = declaredType;
} else {
translateStmt(cdef, syms.voidType);
type = cdef.type;
}
JCExpression classTypeExpr = makeType(type, false);
List<JCExpression> newClassArgs = completeTranslatedConstructorArgs();
if (tmpVarName == null) {
tmpVarName = getSyntheticName("objlit");
}
boolean hasVars = hasInstanceVariableInits();
JCExpression instExpression;
if (isVisage && (hasVars || newClassArgs.nonEmpty() || cdef != null)) {
// it is a instanciation of a Visage class which has instance variable initializers
// (or is anonymous, or has an outer class argument)
//
// {
// final X visage$0objlit = new X(true);
// final short[] visage$0map = GETMAP$X();
//
// for (int visage$0initloop = 0; i < X.$VAR_COUNT; i++) {
// if (!isInitialized(visage$0initloop) {
// switch (visage$0map[visage$0initloop]) {
// 1: visage$0objlit.set$a(0); break;
// 2: visage$0objlit.set$b(0); break;
// ...
// n: visage$0objlit.set$z(0); break;
// default: visage$0objlit.applyDefaults$(visage$0initloop);
// }
// }
// }
//
// visage$0objlit.complete$();
// visage$0objlit
// }
// Use the Visage constructor by adding a marker argument. The "true" in:
// ... new X(true);
newClassArgs = newClassArgs.append(True());
// Create the new instance, placing it in a temporary variable "visage$0objlit"
// final X visage$0objlit = new X(true);
addPreface(Var(
type,
tmpVarName,
m().NewClass(null, null, classTypeExpr, newClassArgs, null)));
// generate stuff just after new object is created
postInstanceCreation(tmpVarName);
// now initialize it's instance variables
initInstanceVariables(tmpVarName);
// (Re-)initialize the flags for the variables set in object literal
makeSetVarFlags(tmpVarName, declaredType);
// Apply defaults to the instance variables
//
// final short[] visage$0map = GETMAP$X();
// for (int visage$0initloop = 0; i < X.$VAR_COUNT; i++) {
// ...
// }
if (visageVarSyms.nonEmpty() || javaVarSyms.nonEmpty()) {
makeInitApplyDefaults(type, tmpVarName);
} else {
makeInitSupportCall(defs.applyDefaults_VisageObjectMethodName, tmpVarName);
}
// Call complete$ to do user's init and postinit blocks
// visage$0objlit.complete$();
makeInitSupportCall(defs.complete_VisageObjectMethodName, tmpVarName);
// Return the instance from the block expressions
// visage$0objlit
instExpression = id(tmpVarName);
} else if (hasVars) {
// This is a Java class that has instance variable initializers
addPreface(Var(
type,
tmpVarName,
m().NewClass(null, null, classTypeExpr, newClassArgs, null)));
initInstanceVariables(tmpVarName);
makeInitApplyDefaults(type, tmpVarName);
instExpression = id(tmpVarName);
} else {
// This is a Java class with no instance variable initializers,
// or a Visage class with no instance variable initializers or constructor args,
// so just instanciate it
instExpression = m().NewClass(null, null, classTypeExpr, newClassArgs, null);
}
return toResult(instExpression, type);
}
}
/**
* Translator for object literals
*/
class InstanciateTranslator extends NewInstanceTranslator {
protected final VisageInstanciate tree;
private final ClassSymbol idSym;
InstanciateTranslator(final VisageInstanciate tree) {
super(tree.pos(), tree.varDefinedByThis != null ? tree.varDefinedByThis.name : null);
this.tree = tree;
this.idSym = (ClassSymbol)VisageTreeInfo.symbol(tree.getIdentifier());
}
@Override
protected boolean hasInstanceVariableInits() {
return tree.getParts().nonEmpty();
}
@Override
protected void initInstanceVariables(Name instName) {
for (VisageObjectLiteralPart olpart : tree.getParts()) {
diagPos = olpart.pos(); // overwrite diagPos (must restore)
VisageBindStatus bindStatus = olpart.getBindStatus();
VisageExpression init = olpart.getExpression();
VisageVarSymbol vsym = (VisageVarSymbol) olpart.sym;
setInstanceVariable(instName, bindStatus, vsym, init);
}
diagPos = tree.pos();
}
protected List<JCExpression> translatedConstructorArgs() {
List<VisageExpression> args = tree.getArgs();
Symbol sym = tree.constructor;
if (sym != null && sym.type != null) {
ListBuffer<JCExpression> translated = ListBuffer.lb();
List<Type> formals = sym.type.asMethodType().getParameterTypes();
boolean usesVarArgs = (sym.flags() & Flags.VARARGS) != 0L &&
(formals.size() != args.size() ||
types.isConvertible(args.last().type, types.elemtype(formals.last())));
boolean handlingVarargs = false;
Type formal = null;
List<Type> t = formals;
for (List<VisageExpression> l = args; l.nonEmpty(); l = l.tail) {
if (!handlingVarargs) {
formal = t.head;
t = t.tail;
if (usesVarArgs && t.isEmpty()) {
formal = types.elemtype(formal);
handlingVarargs = true;
}
}
JCExpression targ = translateExpr(l.head, formal);
if (targ != null) {
translated.append(targ);
}
}
return translated.toList();
} else {
return translateExprs(args);
}
}
@Override
protected List<JCExpression> completeTranslatedConstructorArgs() {
List<JCExpression> translated = translatedConstructorArgs();
ClassSymbol clazz = tree.getClassBody() != null ?
tree.getClassBody().sym :
idSym;
if (getHasOuters().containsKey(clazz)) {
JCExpression receiver = resolveThis(getHasOuters().get(clazz), false);
translated = translated.prepend(receiver);
}
return translated;
}
protected ExpressionResult doit() {
return buildInstance(tree.type, tree.getClassBody(), types.isVisageClass(idSym));
}
}
class TypeCastTranslator extends ExpressionTranslator {
private final VisageExpression expr;
private final VisageTree clazz;
TypeCastTranslator(final VisageTypeCast tree) {
super(tree.pos());
this.expr = tree.getExpression();
this.clazz = tree.clazz;
}
protected ExpressionResult doit() {
JCExpression tExpr = translateExpr(expr, clazz.type);
// The makeTypeCast below is usually redundant, since translateAsValue
// takes care of most conversions - except in the case of a plain object cast.
// It would be cleaner to move the makeTypeCast to translateAsValue,
// but it's painful to get it right. FIXME.
JCExpression ret = typeCast(clazz.type, expr.type, tExpr);
ret = convertNullability(diagPos, ret, expr, clazz.type);
return toResult(ret, clazz.type);
}
}
class InstanceOfTranslator extends ExpressionTranslator {
private final Type classType;
private final VisageExpression expr;
InstanceOfTranslator(VisageInstanceOf tree) {
super(tree.pos());
this.classType = types.boxedTypeOrType(tree.clazz.type);
this.expr = tree.getExpression();
}
protected ExpressionResult doit() {
JCExpression tExpr = translateExpr(expr, null);
if (expr.type.isPrimitive()) {
tExpr = makeBox(expr.pos(), tExpr, expr.type);
}
if (types.isSequence(expr.type) && !types.isSequence(classType)) {
tExpr = Call(defs.Sequences_getSingleValue, tExpr);
}
tExpr = typeCast(syms.objectType, expr.type, tExpr);
JCTree clazz = makeType(classType);
return toResult(
m().TypeTest(tExpr, clazz),
syms.booleanType);
}
}
class SequenceEmptyTranslator extends ExpressionTranslator {
private final Type type;
SequenceEmptyTranslator(VisageSequenceEmpty tree) {
super(tree.pos());
this.type = tree.type;
}
protected ExpressionResult doit() {
return toResult(doitExpr(), type);
}
protected JCExpression doitExpr() {
if (types.isSequence(type)) {
Type elemType = types.boxedElementType(type);
JCExpression expr = accessEmptySequence(diagPos, elemType);
return castFromObject(expr, syms.visage_SequenceTypeErasure);
} else {
return Null();
}
}
}
/**
* Translate if-expression
*/
class IfTranslator extends ExpressionTranslator {
private final VisageIfExpression tree;
IfTranslator(VisageIfExpression tree) {
super(tree.pos());
this.tree = tree;
}
JCExpression sideExpr(VisageExpression expr) {
ExpressionResult res = translateToExpressionResult(expr, targetType);
addBindees(res.bindees());
addInterClassBindees(res.interClass());
return asExpression(res, targetType);
}
JCStatement sideStmt(VisageExpression expr) {
if (expr == null) {
return null;
} else {
return translateToStatement(expr, targetType);
}
}
protected AbstractStatementsResult doit() {
JCExpression cond = translateExpr(tree.getCondition(), syms.booleanType);
VisageExpression trueSide = tree.getTrueExpression();
VisageExpression falseSide = tree.getFalseExpression();
if (yield() == ToExpression) {
return toResult(
If (cond,
sideExpr(trueSide),
sideExpr(falseSide)),
targetType);
} else {
return toStatementResult(
If (cond,
sideStmt(trueSide),
sideStmt(falseSide)));
}
}
}
class IndexOfTranslator extends ExpressionTranslator {
final VisageIndexof tree;
IndexOfTranslator(VisageIndexof tree) {
super(tree.pos());
assert tree.clause.getIndexUsed() : "assert that index used is set correctly";
this.tree = tree;
}
protected ExpressionResult doit() {
return toResult(id(indexVarName(tree.fname)), tree.type);
}
}
class SequenceIndexedTranslator extends ExpressionTranslator {
private final VisageExpression seq;
private final JCExpression tSeq;
private final boolean isTSeqDirect;
private final JCExpression tIndex;
private final Type resultType;
SequenceIndexedTranslator(DiagnosticPosition diagPos, VisageExpression seq, JCExpression tSeq, JCExpression tIndex, Type resultType) {
super(diagPos);
this.seq = seq;
this.tSeq = tSeq;
this.isTSeqDirect = false;
this.tIndex = tIndex;
this.resultType = resultType;
}
SequenceIndexedTranslator(VisageSequenceIndexed tree) {
super(tree.pos());
this.seq = tree.getSequence();
this.tSeq = translateExpr(seq, null);
this.isTSeqDirect = true;
this.tIndex = translateExpr(tree.getIndex(), syms.intType);
this.resultType = tree.type;
}
protected ExpressionResult doit() {
return toResult(
doitExpr(),
resultType);
}
protected JCExpression doitExpr() {
if (seq.type.tag == TypeTags.ARRAY) {
// It is a native array, just index into it
return m().Indexed(tSeq, tIndex);
}
VisageTypeRepresentation typeRep = types.typeRep(resultType);
if (seq instanceof VisageIdent) {
VisageIdent var = (VisageIdent) seq;
OnReplaceInfo info = findOnReplaceInfo(var.sym);
if (info != null
&& (var.sym.flags_field & VisageFlags.VARUSE_OPT_TRIGGER) != 0) {
VisageOnReplace onReplace = info.onReplace;
ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
args.append(getReceiverOrThis(info.vsym));
args.append(Offset(info.vsym));
args.append(id(paramStartPosName(onReplace)));
args.append(id(paramNewElementsLengthName(onReplace)));
args.append(tIndex);
List<JCExpression> typeArgs;
if (typeRep.isObject()) {
/*
* We are calling SequencesBase.getFromNewElements() which
* accepts type argument for the returned sequence element type.
* If we don't pass correct type argument, we will get Object type.
* For example, for Sequence<? extends String> we want to pass "String"
* as type arg, so that the return type is "String" and not "Object".
*/
ListBuffer<JCExpression> typeArgsBuf = ListBuffer.lb();
typeArgsBuf.append(makeType(seq.type.getTypeArguments().head.removeBounds()));
typeArgs = typeArgsBuf.toList();
} else {
typeArgs = List.<JCExpression>nil();
}
return Call(defs.Sequences_getAsFromNewElements[typeRep.ordinal()], typeArgs, args.toList());
}
}
if (isTSeqDirect) {
VisageVarSymbol vsym = varSymbol(seq);
if (vsym != null
&& vsym.useAccessors()
&& types.isSameType(vsym.getElementType(), resultType)) {
// Using elem$ is critical to non-boxing behavior of bound sequences
// Use elem$seq(pos) form
switch (seq.getVisageTag()) {
case SELECT: {
Yield prevYield = yieldKind;
yieldKind = ToExpression; // Force expression result so that the merge works
try {
return mergeResults((ExpressionResult) new SelectElementTranslator((VisageSelect) seq, tIndex).doit());
} finally {
yieldKind = prevYield;
}
}
case IDENT:
return mergeResults(new IdentElementTranslator((VisageIdent) seq, tIndex).doit());
}
}
}
// Use seq.get(pos) form
Name getMethodName = defs.typedGet_SequenceMethodName[typeRep.ordinal()];
return Call(tSeq, getMethodName, tIndex);
}
}
class SequenceSliceTranslator extends ExpressionTranslator {
private final Type type;
private final VisageExpression seq;
private final int endKind;
private final VisageExpression firstIndex;
private final VisageExpression lastIndex;
SequenceSliceTranslator(VisageSequenceSlice tree) {
super(tree.pos());
this.type = tree.type;
this.seq = tree.getSequence();
this.endKind = tree.getEndKind();
this.firstIndex = tree.getFirstIndex();
this.lastIndex = tree.getLastIndex();
}
JCExpression computeSliceEnd() {
JCExpression endPos;
if (lastIndex == null) {
endPos = Call(translateExpr(seq, null), defs.size_SequenceMethodName);
if (endKind == SequenceSliceTree.END_EXCLUSIVE) {
endPos = MINUS(endPos, Int(1));
}
} else {
endPos = translateExpr(lastIndex, syms.intType);
if (endKind == SequenceSliceTree.END_INCLUSIVE) {
endPos = PLUS(endPos, Int(1));
}
}
return endPos;
}
protected ExpressionResult doit() {
return toResult(doitExpr(), type);
}
protected JCExpression doitExpr() {
JCExpression tFirstIndex = translateExpr(firstIndex, syms.intType);
return Call(translateExpr(seq, null), defs.getSlice_SequenceMethodName, tFirstIndex, computeSliceEnd());
}
}
class ExplicitSequenceTranslator extends ExpressionTranslator {
final List<VisageExpression> items;
final Type elemType;
final Type resultType;
ExplicitSequenceTranslator(DiagnosticPosition diagPos, List<VisageExpression> items, Type elemType, Type resultType) {
super(diagPos);
this.items = items;
this.elemType = elemType;
this.resultType = resultType;
}
/***
* In cases where the components of an explicitly constructed
* sequence are all singletons, we can revert to this (more
* optimal) implementation.
DiagnosticPosition diagPos = tree.pos();
JCExpression meth = ((VisageTreeMaker)make).at(diagPos).Identifier(sequencesMakeString);
Type elemType = tree.type.getTypeArguments().get(0);
ListBuffer<JCExpression> args = ListBuffer.<JCExpression>lb();
List<JCExpression> typeArgs = List.<JCExpression>of(makeTypeTree(elemType, diagPos));
// type name .class
args.append(makeTypeInfo(diagPos, elemType));
args.appendList( translate( tree.getItems() ) );
result = make.at(diagPos).Apply(typeArgs, meth, args.toList());
*/
protected ExpressionResult doit() {
UseSequenceBuilder builder = useSequenceBuilder(diagPos, elemType, items.length(), false);
addPreface(builder.makeBuilderVar());
for (VisageExpression item : items) {
if (item.getVisageKind() != VisageKind.NULL_LITERAL) {
// Insert all non-null elements
addPreface(builder.addElement(item));
}
}
return toResult(
builder.makeToSequence(),
resultType);
}
}
class SequenceRangeTranslator extends ExpressionTranslator {
private final VisageExpression lower;
private final VisageExpression upper;
private final VisageExpression step;
private final boolean hasStep;
private final boolean exclusive;
private final Type type;
SequenceRangeTranslator(VisageSequenceRange tree) {
super(tree.pos());
this.lower = tree.getLower();
this.upper = tree.getUpper();
this.hasStep = tree.getStepOrNull() != null;
this.step = tree.getStepOrNull();
this.exclusive = tree.isExclusive();
this.type = tree.type;
}
protected ExpressionResult doit() {
RuntimeMethod rm = exclusive ? defs.Sequences_rangeExclusive : defs.Sequences_range;
Type elemType = syms.visage_IntegerType;
int ltag = lower.type.tag;
int utag = upper.type.tag;
int stag = hasStep ? step.type.tag : TypeTags.INT;
if (ltag == TypeTags.FLOAT || ltag == TypeTags.DOUBLE ||
utag == TypeTags.FLOAT || utag == TypeTags.DOUBLE ||
stag == TypeTags.FLOAT || stag == TypeTags.DOUBLE) {
elemType = syms.visage_NumberType;
}
ListBuffer<JCExpression> args = ListBuffer.lb();
args.append(translateExpr(lower, elemType));
args.append(translateExpr(upper, elemType));
if (hasStep) {
args.append(translateExpr(step, elemType));
}
return toResult(
Call(rm, args),
type);
}
}
/**
* assume seq is a sequence of element type U
* convert for (x in seq where cond) { body }
* into the following block expression
*
* {
* SequenceBuilder<T> sb = new SequenceBuilder<T>(clazz);
* for (U x : seq) {
* if (!cond)
* continue;
* sb.add( { body } );
* }
* sb.toSequence()
* }
*
* **/
class ForExpressionTranslator extends ExpressionTranslator {
final VisageForExpression tree;
ForExpressionTranslator(VisageForExpression tree) {
super(tree.pos());
this.tree = tree;
}
private JCStatement wrapWithInClause(VisageForExpression tree, JCStatement coreStmt) {
JCStatement stmt = coreStmt;
for (int inx = tree.getInClauses().size() - 1; inx >= 0; --inx) {
VisageForExpressionInClause clause = (VisageForExpressionInClause) tree.getInClauses().get(inx);
stmt = new InClauseTranslator(clause, stmt).doitStmt();
}
return stmt;
}
protected AbstractStatementsResult doit() {
// sub-translation in done inline -- no super.visitForExpression(tree);
if (yield() == ToStatement && targetType == syms.voidType) {
return new StatementsResult(wrapWithInClause(tree, translateToStatement(tree.getBodyExpression(), targetType)));
} else {
// body has value (non-void)
assert tree.type != syms.voidType : "should be handled above";
JCStatement stmt;
JCExpression value;
// Compute the element type from the sequence type
assert tree.type.getTypeArguments().size() == 1;
Type elemType = types.elementType(tree.type);
UseSequenceBuilder builder = useSequenceBuilder(diagPos, elemType, true);
addPreface(builder.makeBuilderVar());
// Build innermost loop body
stmt = builder.addElement(tree.getBodyExpression());
stmt = wrapWithInClause(tree, stmt);
addPreface(stmt);
// Build the result value
value = builder.makeToSequence();
if (yield() == ToStatement) {
return toStatementResult(value, tree.type, targetType);
} else {
// Build the block expression -- which is what we translate to
return toResult(
convertTranslated(value, diagPos, tree.type, targetType),
targetType);
}
}
}
}
/**
* Translator class for for-expression in/where clauses
*/
private class InClauseTranslator extends ExpressionTranslator {
final VisageForExpressionInClause clause; // in clause being translated
final VisageVar var; // user named Visage induction variable
final Type inductionVarType; // type of the induction variable
final JCVariableDecl inductionVar; // generated induction variable
JCStatement body; // statement being generated by wrapping
boolean indexedLoop;
InClauseTranslator(VisageForExpressionInClause clause, JCStatement coreStmt) {
super(clause);
this.clause = clause;
this.var = clause.getVar();
this.inductionVarType = var.type;
this.body = coreStmt;
VisageExpression seq = clause.seqExpr;
this.indexedLoop =
(seq.getVisageTag() == VisageTag.SEQUENCE_SLICE ||
(seq.getVisageTag() != VisageTag.SEQUENCE_RANGE &&
types.isSequence(seq.type)));
this.inductionVar = MutableTmpVar("ind", indexedLoop ? syms.intType : inductionVarType, null);
}
private JCVariableDecl TmpVar(String root, JCExpression value) {
return TmpVar(root, inductionVarType, value);
}
private JCVariableDecl Var(Name varName, JCExpression value) {
return Var(inductionVarType, varName, value);
}
@Override
protected JCVariableDecl TmpVar(long flags, String root, Type varType, JCExpression initialValue) {
Name varName = names.fromString(var.name.toString() + "$" + root);
return Var(flags, varType, varName, initialValue);
}
/**
* Generate a range sequence conditional test.
* Result depends on if the range is ascending/descending and if the range is exclusive
*/
private JCExpression condTest(VisageSequenceRange range, boolean stepNegative, JCVariableDecl upperVar) {
int op;
if (stepNegative) {
if (range.isExclusive()) {
op = JCTree.GT;
} else {
op = JCTree.GE;
}
} else {
if (range.isExclusive()) {
op = JCTree.LT;
} else {
op = JCTree.LE;
}
}
return m().Binary(op, id(inductionVar), id(upperVar));
}
/**
* Determine if a literal is negative
*/
private boolean isNegative(VisageExpression expr) {
VisageLiteral lit = (VisageLiteral) expr;
Object val = lit.getValue();
switch (lit.typetag) {
case TypeTags.INT:
return ((int) (Integer) val) < 0;
case TypeTags.SHORT:
return ((short) (Short) val) < 0;
case TypeTags.BYTE:
return ((byte) (Byte) val) < 0;
case TypeTags.CHAR:
return ((char) (Character) val) < 0;
case TypeTags.LONG:
return ((long) (Long) val) < 0L;
case TypeTags.FLOAT:
return ((float) (Float) val) < 0.0f;
case TypeTags.DOUBLE:
return ((double) (Double) val) < 0.0;
default:
throw new AssertionError("unexpected literal kind " + this);
}
}
JCStatement makeForLoop(List<JCStatement> init, JCExpression cond, List<JCExpressionStatement> step, JCStatement body) {
JCStatement loop = m().ForLoop(init, cond, step, body);
if (clause.label != null) {
// Wrap in a labeled stmt if the for has a label (that was created because
// it translated into nested loops, and the body contained a break or continue.)
loop = m().Labelled(clause.label, loop);
}
return loop;
}
JCStatement makeForEachLoop(JCVariableDecl var, JCExpression iterable, JCStatement body) {
JCStatement loop = m().ForeachLoop(var, iterable, body);
if (clause.label != null) {
// Wrap in a labeled stmt if the for has a label (that was created because
// it translated into nested loops, and the body contained a break or continue.)
loop = m().Labelled(clause.label, loop);
}
return loop;
}
/**
* Generate the loop for a slice sequence. Loop wraps the current body.
* For the loop:
* for (x in seq[lo..<hi]) body
* Generate:
* for (int $i = max(lo,0); i++; $i < min(hi,seq.size())) { def x = seq[$i]; body }
* (We may need to put seq and/or hi in a temporary variable first.)
*/
void translateSliceInClause(VisageExpression seq, VisageExpression first, VisageExpression last, int endKind, JCVariableDecl seqVar) {
// Collect all the loop initializing statements (variable declarations)
ListBuffer<JCStatement> tinits = ListBuffer.lb();
boolean needSeqVar;
if (! (seq instanceof VisageIdent))
needSeqVar = true;
else {
Symbol seqsym = ((VisageIdent) seq).sym;
OnReplaceInfo info = findOnReplaceInfo(seqsym);
needSeqVar = info == null ||
(seqsym.flags_field & VisageFlags.VARUSE_OPT_TRIGGER) == 0;
}
if (needSeqVar)
tinits.append(seqVar);
JCExpression init;
boolean maxForStartNeeded = true;
if (first == null)
init = Int(0);
else {
init = translateToExpression(first, syms.intType);
if (first.getVisageTag() == VisageTag.LITERAL && ! isNegative(first))
maxForStartNeeded = false;
// FIXME set maxForStartNeeded false if first is replace-trigger startPos and seq is oldValue
if (maxForStartNeeded)
setDiagPos(first);
init = Call(defs.Math_max, init, Int(0));
}
setDiagPos(clause);
inductionVar.init = init;
tinits.append(inductionVar);
JCExpression sizeExpr = translateSizeof(seq, id(seqVar));
//call(diagPos, ident(seqVar), "size");
JCExpression limitExpr;
// Compare the logic in makeSliceEndPos.
if (last == null) {
limitExpr = sizeExpr;
if (endKind == SequenceSliceTree.END_EXCLUSIVE)
limitExpr = MINUS(limitExpr, Int(1));
}
else {
limitExpr = translateToExpression(last, syms.intType);
if (endKind == SequenceSliceTree.END_INCLUSIVE)
limitExpr = PLUS(limitExpr, Int(1));
// FIXME can optimize if last is replace-trigger endPos and seq is oldValue
if (true)
setDiagPos(last);
limitExpr = Call(defs.Math_min, limitExpr, sizeExpr);
}
setDiagPos(clause);
JCVariableDecl limitVar = TmpVar("limit", syms.intType, limitExpr);
tinits.append(limitVar);
// The condition that will be tested each time through the loop
JCExpression tcond = LT(id(inductionVar), id(limitVar));
// Generate the step statement as: x += 1
List<JCExpressionStatement> tstep = List.of(m().Exec(m().Assignop(JCTree.PLUS_ASG, id(inductionVar), m().Literal(TypeTags.INT, 1))));
tinits.append(makeForLoop(List.<JCStatement>nil(), tcond, tstep, body));
body = Block(tinits);
}
/**
* Generate the loop for a range sequence. Loop wraps the current body.
* For the loop:
* for (x in [lo..hi step st]) body
* Generate (assuming x is float):
* for (float x = lo, final float x$upper = up, final float x$step = st;, final boolean x$negative = x$step < 0.0;
* x$negative? x >= x$upper : x <= x$upper;
* x += x$step)
* body
* Without a step specified (or a literal step) the form reduces to:
* for (float x = lo, final float x$upper = up;
* x <= x$upper;
* x += 1)
* body
*/
void translateRangeInClause() {
VisageSequenceRange range = (VisageSequenceRange) clause.seqExpr;
// Collect all the loop initializing statements (variable declarations)
ListBuffer<JCStatement> tinits = ListBuffer.lb();
// Set the initial value of the induction variable to be the low end of the range, and add it the the initializing statements
inductionVar.init = translateToExpression(range.getLower(), inductionVarType);
tinits.append(inductionVar);
// Record the upper end of the range in a final variable, and add it the the initializing statements
JCVariableDecl upperVar = TmpVar("upper", translateToExpression(range.getUpper(), inductionVarType));
tinits.append(upperVar);
// The expression which will be used in increment the induction variable
JCExpression tstepIncrExpr;
// The condition that will be tested each time through the loop
JCExpression tcond;
// The user's step expression, or null if none specified
VisageExpression step = range.getStepOrNull();
if (step != null) {
// There is a user specified step expression
JCExpression stepVal = translateToExpression(step, inductionVarType);
if (step.getVisageTag() == VisageTag.LITERAL) {
// The step expression is a literal, no need for a variable to hold it, and we can test if the range is scending at compile time
tstepIncrExpr = stepVal;
tcond = condTest(range, isNegative(step), upperVar);
} else {
// Arbitrary step expression, do all the madness shown in the method comment
JCVariableDecl stepVar = TmpVar("step", stepVal);
tinits.append(stepVar);
tstepIncrExpr = id(stepVar);
JCVariableDecl negativeVar = TmpVar("negative", syms.booleanType, LT(id(stepVar), m().Literal(inductionVarType.tag, 0)));
tinits.append(negativeVar);
tcond =
If(id(negativeVar),
condTest(range, true, upperVar),
condTest(range, false, upperVar));
}
} else {
// No step expression, use one as the increment
tstepIncrExpr = m().Literal(inductionVarType.tag, 1);
tcond = condTest(range, false, upperVar);
}
// Generate the step statement as: x += x$step
List<JCExpressionStatement> tstep = List.of(m().Exec(m().Assignop(JCTree.PLUS_ASG, id(inductionVar), tstepIncrExpr)));
// Finally, build the for loop
body = makeForLoop(tinits.toList(), tcond, tstep, body);
}
/**
* Core of the in-clause translation. Given an in-clause and the current body, build the for-loop
*/
protected StatementsResult doit() {
return new StatementsResult(doitStmt());
}
protected JCStatement doitStmt() {
// If there is a where expression, make the execution of the body conditional on the where condition
if (clause.getWhereExpression() != null) {
body = If(translateExprToBlockExpression(clause.getWhereExpression(), syms.booleanType), body);
}
// Because the induction variable may be used in inner contexts, make a final
// variable inside the loop that holds the current iterations value.
// Same with the index if used. That is:
// for (x in seq) body
// Becomes (assume Number sequence):
// x$incrindex = 0;
// loop over x$ind {
// final int x$index = x$incrindex++;
// final float x = x$ind;
// body;
// }
JCVariableDecl incrementingIndexVar = null;
VisageExpression seq = clause.seqExpr;
JCVariableDecl seqVar = null;
{
ListBuffer<JCStatement> stmts = ListBuffer.lb();
setDiagPos(var);
if (clause.getIndexUsed()) {
incrementingIndexVar = MutableTmpVar("incrindex", syms.visage_IntegerType, Int(0));
JCVariableDecl finalIndexVar = Var(
syms.visage_IntegerType,
indexVarName(clause),m().Unary(JCTree.POSTINC, id(incrementingIndexVar)));
stmts.append(finalIndexVar);
}
JCExpression varInit; // Initializer for var.
if (indexedLoop) {
VisageExpression sseq;
if (clause.seqExpr instanceof VisageSequenceSlice) {
sseq = ((VisageSequenceSlice) clause.seqExpr).getSequence();
} else {
sseq = clause.seqExpr;
}
seqVar = TmpVar("seq", seq.type, translateToExpression(sseq, seq.type));
varInit = new SequenceIndexedTranslator(diagPos, sseq, id(seqVar), id(inductionVar), inductionVarType).doitExpr();
} else {
varInit = id(inductionVar);
}
stmts.append(Var(var.getName(), varInit));
stmts.append(body);
body = Block(stmts);
}
// Translate the sequence into the loop
setDiagPos(seq);
if (seq.getVisageTag() == VisageTag.SEQUENCE_RANGE) {
// Iterating over a range sequence
translateRangeInClause();
} else if (seq.getVisageTag() == VisageTag.SEQUENCE_SLICE) {
VisageSequenceSlice slice = (VisageSequenceSlice) clause.seqExpr;
translateSliceInClause(slice.getSequence(), slice.getFirstIndex(), slice.getLastIndex(),
slice.getEndKind(), seqVar);
} else {
// We will be using the sequence as a whole, so translate it
JCExpression tseq = translateToExpression(seq, null);
if (types.isSequence(seq.type)) {
// Iterating over a non-range sequence, use a foreach loop, but first convert null to an empty sequence
tseq = Call(defs.Sequences_forceNonNull,
TypeInfo(diagPos, inductionVarType), tseq);
translateSliceInClause(seq, null, null, SequenceSliceTree.END_INCLUSIVE, seqVar);
//body = m().ForeachLoop(inductionVar, tseq, body);
} else if (seq.type.tag == TypeTags.ARRAY ||
types.asSuper(seq.type, syms.iterableType.tsym) != null) {
// Iterating over an array or iterable type, use a foreach loop
body = makeForEachLoop(inductionVar, tseq, body);
} else {
// The "sequence" isn't aactually a sequence, treat it as a singleton.
// Compile: { var tmp = seq; if (tmp!=null) body; }
if (!inductionVarType.isPrimitive()) {
body = If (NEnull(id(inductionVar)),
body);
}
// the "induction" variable will have only one value, set it to that
inductionVar.init = tseq;
// wrap the induction variable and the body in a block to protect scope
body = Block(inductionVar, body);
}
}
if (clause.getIndexUsed()) {
// indexof is used, define the index counter variable at the top of everything
body = Block(incrementingIndexVar, body);
}
return body;
}
}
class VarInitTranslator extends ExpressionTranslator {
private final VisageVar var;
private final VisageVarSymbol vsym;
VarInitTranslator(VisageVarInit tree) {
super(tree.pos());
this.var = tree.getVar();
this.vsym = tree.getSymbol();
}
/**
* No longer waiting for the VarInit (this is it).
* applyDefaults.
* value is var value.
*/
ExpressionResult doit() {
JCExpression tor;
clearDiagPos();
if (!vsym.useAccessors() && var.isLiteralInit()) {
tor = Get(vsym);
} else if (vsym.isSynthetic()) {
tor = BlockExpression(
If (FlagTest(vsym, defs.varFlagINIT_MASK, defs.varFlagINIT_READY),
CallStmt(getReceiver(vsym), defs.applyDefaults_VisageObjectMethodName, Offset(vsym))
),
Get(vsym)
);
} else {
tor = BlockExpression(
FlagChangeStmt(vsym, defs.varFlagINIT_WITH_AWAIT_MASK, defs.varFlagINIT_READY),
CallStmt(getReceiver(vsym), defs.applyDefaults_VisageObjectMethodName, Offset(vsym)),
Get(vsym)
);
}
return toResult(tor, vsym.type);
}
}
class VarRefTranslator extends ExpressionTranslator {
Symbol varSymbol;
VisageExpression receiver;
VisageVarRef.RefKind kind;
Type expectedType;
VarRefTranslator(VisageVarRef tree) {
super(tree.pos());
this.varSymbol = tree.getVarSymbol();
this.receiver = tree.getReceiver();
this.kind = tree.getVarRefKind();
this.expectedType = tree.type;
}
ExpressionResult doit() {
JCExpression receiverExpr = receiver != null ? translateExpr(receiver, expectedType) : null;
switch (kind) {
case INST: return toResult(receiverExpr != null ? receiverExpr : getReceiverOrThis(varSymbol), expectedType);
case VARNUM: return toResult(Offset(receiverExpr, varSymbol), expectedType);
}
throw new AssertionError("Shouldn't be here!");
}
}
/***********************************************************************
* Bad visitor support
*/
void badVisitor(String msg) {
throw new AssertionError(msg);
}
private void disallowedInBind() {
badVisitor("should not be processed as part of a binding");
}
private void processedInParent() {
badVisitor("should be processed by parent tree");
}
/***********************************************************************
*
* Visitors (alphabetical order)
*
* Disallow constructs disallowed in bind -- override where non-bound contructs are allowed (VisageToJava)
* Assume non-bound non-notifying implementations -- override where needed
*/
public void visitAssign(VisageAssign tree) {
disallowedInBind();
}
public void visitAssignop(VisageAssignOp tree) {
disallowedInBind();
}
public void visitBinary(VisageBinary tree) {
result = (new BinaryOperationTranslator(tree.pos(), tree)).doit();
}
public void visitBreak(VisageBreak tree) {
disallowedInBind();
}
public void visitCatch(VisageCatch tree) {
processedInParent();
}
public void visitClassDeclaration(VisageClassDeclaration tree) {
// redirected back to VisageToJava
toJava.visitClassDeclaration(tree);
result = toJava.result;
}
public void visitContinue(VisageContinue tree) {
disallowedInBind();
}
public void visitErroneous(VisageErroneous tree) {
badVisitor("erroneous nodes shouldn't have gotten this far");
}
public void visitForExpression(VisageForExpression tree) {
result = (new ForExpressionTranslator(tree)).doit();
}
public void visitForExpressionInClause(VisageForExpressionInClause that) {
processedInParent();
}
public void visitFunctionDefinition(VisageFunctionDefinition tree) {
disallowedInBind();
}
public void visitFunctionInvocation(final VisageFunctionInvocation tree) {
result = new FunctionCallTranslator(tree).doit();
}
public void visitFunctionValue(VisageFunctionValue tree) {
disallowedInBind();
}
public abstract void visitIdent(VisageIdent tree);
public void visitIfExpression(VisageIfExpression tree) {
result = new IfTranslator(tree).doit();
}
public void visitImport(VisageImport tree) {
processedInParent();
}
public void visitIndexof(final VisageIndexof tree) {
result = new IndexOfTranslator(tree).doit();
}
public void visitInitDefinition(VisageInitDefinition tree) {
processedInParent();
}
public void visitInstanceOf(VisageInstanceOf tree) {
result = new InstanceOfTranslator(tree).doit();
}
public void visitInstanciate(VisageInstanciate tree) {
result = new InstanciateTranslator(tree).doit();
}
public void visitInterpolateValue(final VisageInterpolateValue tree) {
throw new AssertionError("KeyFrame should have been lowered");
}
public void visitInvalidate(VisageInvalidate tree) {
disallowedInBind();
}
public void visitKeyFrameLiteral(VisageKeyFrameLiteral tree) {
disallowedInBind();
}
public void visitLiteral(VisageLiteral tree) {
result = new LiteralTranslator(tree).doit();
}
public void visitModifiers(VisageModifiers tree) {
processedInParent();
}
public void visitObjectLiteralPart(VisageObjectLiteralPart that) {
processedInParent();
}
public void visitOnReplace(VisageOnReplace tree) {
processedInParent();
}
public void visitOverrideClassVar(VisageOverrideClassVar tree) {
processedInParent();
}
public void visitParens(VisageParens tree) {
result = translateToExpressionResult(tree.expr, targetType);
}
public void visitPostInitDefinition(VisagePostInitDefinition tree) {
processedInParent();
}
public void visitReturn(VisageReturn tree) {
disallowedInBind();
}
public void visitScript(VisageScript tree) {
disallowedInBind();
}
public void visitSequenceDelete(VisageSequenceDelete tree) {
disallowedInBind();
}
public void visitSequenceEmpty(VisageSequenceEmpty tree) {
result = new SequenceEmptyTranslator(tree).doit();
}
public void visitSequenceExplicit(VisageSequenceExplicit tree) {
result = new ExplicitSequenceTranslator(
tree.pos(),
tree.getItems(),
types.elementType(tree.type),
tree.type)
.doit();
}
public void visitSequenceIndexed(final VisageSequenceIndexed tree) {
result = new SequenceIndexedTranslator(tree).doit();
}
public void visitSequenceInsert(VisageSequenceInsert tree) {
disallowedInBind();
}
public void visitSequenceRange(VisageSequenceRange tree) {
result = new SequenceRangeTranslator(tree).doit();
}
public void visitSequenceSlice(VisageSequenceSlice tree) {
result = new SequenceSliceTranslator(tree).doit();
}
public void visitSkip(VisageSkip tree) {
disallowedInBind();
}
public void visitStringExpression(VisageStringExpression tree) {
result = new StringExpressionTranslator(tree).doit();
}
public void visitThrow(VisageThrow tree) {
disallowedInBind();
}
public void visitTimeLiteral(final VisageTimeLiteral tree) {
result = new TimeLiteralTranslator(tree).doit();
}
public void visitLengthLiteral(final VisageLengthLiteral tree) {
result = new LengthLiteralTranslator(tree).doit();
}
public void visitAngleLiteral(final VisageAngleLiteral tree) {
result = new AngleLiteralTranslator(tree).doit();
}
public void visitColorLiteral(final VisageColorLiteral tree) {
result = new ColorLiteralTranslator(tree).doit();
}
public void visitTree(VisageTree that) {
badVisitor("Should not be here!!!");
}
public void visitTry(VisageTry tree) {
disallowedInBind();
}
public void visitTypeAny(VisageTypeAny that) {
processedInParent();
}
public void visitTypeCast(final VisageTypeCast tree) {
result = new TypeCastTranslator(tree).doit();
}
public void visitTypeClass(VisageTypeClass that) {
processedInParent();
}
public void visitTypeFunctional(VisageTypeFunctional that) {
processedInParent();
}
public void visitTypeArray(VisageTypeArray tree) {
processedInParent();
}
public void visitTypeUnknown(VisageTypeUnknown that) {
processedInParent();
}
public void visitUnary(VisageUnary tree) {
if (tree.getVisageTag().isIncDec()) {
//we shouldn't be here - arithmetic unary expressions should
//have been lowered to standard binary expressions
badVisitor("Unexpected unary operator tag: " + tree.getVisageTag());
}
result = new UnaryOperationTranslator(tree).doit();
}
public void visitVar(VisageVar tree) {
disallowedInBind();
}
public void visitVarInit(VisageVarInit tree) {
result = new VarInitTranslator(tree).doit();
}
public void visitVarRef(VisageVarRef tree) {
result = new VarRefTranslator(tree).doit();
}
public void visitWhileLoop(VisageWhileLoop tree) {
disallowedInBind();
}
}