/*
* 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.tree.SequenceSliceTree;
import org.visage.tools.code.VisageVarSymbol;
import org.visage.tools.tree.*;
import org.visage.tools.comp.VisageAbstractTranslation.ExpressionResult;
import org.visage.tools.comp.VisageDefs.RuntimeMethod;
import com.sun.tools.mjavac.code.Symbol;
import com.sun.tools.mjavac.code.Symbol.VarSymbol;
import com.sun.tools.mjavac.code.Type;
import com.sun.tools.mjavac.code.Flags;
import com.sun.tools.mjavac.code.Kinds;
import com.sun.tools.mjavac.code.Symbol.ClassSymbol;
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.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.mjavac.util.List;
import com.sun.tools.mjavac.util.ListBuffer;
import com.sun.tools.mjavac.util.Name;
/**
* Translate bind expressions into code in bind defining methods
*
* @author Robert Field
*/
public class VisageTranslateBind extends VisageAbstractTranslation implements VisageVisitor {
protected static final Context.Key<VisageTranslateBind> visageBoundTranslation =
new Context.Key<VisageTranslateBind>();
private VisageToJava toJava;
// Symbol for the var whose bound expression we are translating.
private VisageVarSymbol targetSymbol;
// The outermost bound expression
private VisageExpression boundExpression;
private DependencyGraphWriter depGraphWriter;
public static VisageTranslateBind instance(Context context) {
VisageTranslateBind instance = context.get(visageBoundTranslation);
if (instance == null) {
VisageToJava toJava = VisageToJava.instance(context);
instance = new VisageTranslateBind(context, toJava);
}
return instance;
}
public VisageTranslateBind(Context context, VisageToJava toJava) {
super(context, toJava);
this.toJava = toJava;
context.put(visageBoundTranslation, this);
this.depGraphWriter = DependencyGraphWriter.instance(context);
}
static JCExpression TODO(String msg, VisageExpression tree) {
return TODO(msg + " -- " + tree.getClass());
}
/**
* Entry-point into VisageTranslateBind.
*
* @param expr Bound expression to translate. Directly held by a var (or bound function?). Not a sub-expression/
* @param targetSymbol Symbol for the var whose bound expression we are translating.
* @param isBidiBind Is this a bi-directional bind?
* @return
*/
ExpressionResult translateBoundExpression(VisageExpression expr, VisageVarSymbol targetSymbol) {
// Bind translation is re-entrant -- save and restore state
VisageVarSymbol prevTargetSymbol = this.targetSymbol;
VisageExpression prevBoundExpression = this.boundExpression;
this.targetSymbol = targetSymbol;
this.boundExpression = expr;
// Special case: If the targetSymbol is a bound function result, then
// make the expected type to be Pointer or else make it null.
ExpressionResult res = translateToExpressionResult(
expr,
isBoundFunctionResult(targetSymbol) ?
syms.visage_PointerType :
targetSymbol.type);
this.targetSymbol = prevTargetSymbol;
this.boundExpression = prevBoundExpression;
return res;
}
/****************************************************************************
* Bound Non-Sequence Translators
****************************************************************************/
/**
* Translator for bound object literals
*/
class BoundInstanciateTranslator extends InstanciateTranslator {
// Call function only if conditions met
private JCExpression condition = null;
// If we are wrapping with a test, collect the preface
private ListBuffer<JCStatement> wrappingPreface = ListBuffer.lb();
BoundInstanciateTranslator(VisageInstanciate tree) {
super(tree);
}
@Override
protected void initInstanceVariables(Name instName) {
// True if any of the initializers are sequences, we can't pre-test arguments
boolean hasSequenceInitializer = false;
for (VisageObjectLiteralPart olpart : tree.getParts()) {
if (types.isSequence(olpart.sym.type)) {
hasSequenceInitializer = true;
}
}
if (!hasSequenceInitializer) {
condition = bindNeedsDefault(targetSymbol);
}
super.initInstanceVariables(instName);
}
@Override
protected JCExpression translateInstanceVariableInit(VisageExpression init, VisageVarSymbol vsym) {
if (init instanceof VisageIdent) {
Symbol isym = ((VisageIdent) init).sym;
addBindee((VisageVarSymbol) isym);
if (condition != null) {
JCVariableDecl oldVar = TmpVar("old", vsym.type, Get(isym));
JCVariableDecl newVar = TmpVar("new", vsym.type, Getter(isym));
wrappingPreface.append(oldVar);
wrappingPreface.append(newVar);
// concatenate with OR -- oldArg1 != newArg1 || oldArg2 != newArg2
condition = OR(condition, NE(id(oldVar), id(newVar)));
return id(newVar);
} else {
return super.translateInstanceVariableInit(init, vsym);
}
} else {
return super.translateInstanceVariableInit(init, vsym);
}
}
/**
* If we can, wrap the instance creation in a test to be sure an initializer really changed
*
* T res;
* if (DefaultsNotApplied || oldArg1 != newArg1 || oldArg2 != newArg2) {
* T objlit = ... instance creation stuff
* res = objectLit;
* } else {
* res = prevValue;
* }
*/
@Override
protected ExpressionResult doit() {
ExpressionResult eres = super.doit();
if (condition != null) {
// if no initializers have changed, don't create a new instance, just return previous value\
JCVariableDecl resVar = MutableTmpVar("res", targetSymbol.type, null);
JCStatement setRes =
If(condition,
Block(
eres.statements().append(Stmt(m().Assign(id(resVar), eres.expr())))),
Stmt(m().Assign(id(resVar), Get(targetSymbol))));
return new ExpressionResult(
diagPos,
wrappingPreface.toList().append(resVar).append(setRes),
id(resVar),
eres.bindees(),
eres.invalidators(),
eres.interClass(),
eres.setterPreface(),
eres.resultType());
} else {
return eres;
}
}
}
/**
* Translate a bound function call
*/
private class BoundFunctionCallTranslator extends FunctionCallTranslator {
/*
* True if the (bind) call is made conditionally, false if function call
* is executed always (no condition check). This conditional evaluation
* is an optimization for calls in bind expressions - we would like to
* avoid calling the function whenever possible.
*/
private boolean conditionallyReevaluate = false;
// Call function only if conditions met
private JCExpression condition = null;
// True if any arguments are sequences, we can't pre-test arguments
private boolean hasSequenceArg = false;
BoundFunctionCallTranslator(VisageFunctionInvocation tree) {
super(tree);
// Determine if any arguments are sequences, if so, we can't pre-test arguments
for (VisageExpression arg : args) {
if (types.isSequence(arg.type)) {
hasSequenceArg = true;
}
}
// If the function has a sequence arg or if this is a Function.invoke or
// if this is a Java call, we avoid conditional reevaluation. (i.e., force
// re-evaluation always)
boolean isJavaCall = (msym != null) && !types.isVisageClass(msym.owner);
conditionallyReevaluate = ! (hasSequenceArg || useInvoke || isJavaCall);
// If the receiver changes, then we have to call the function again
// If selector is local var, then it is going to be final, and thus won't change (and doesn't have a getter)
if (conditionallyReevaluate && !knownNonNull && selectorSym instanceof VarSymbol && selectorSym.owner.kind == Kinds.TYP) {
JCVariableDecl oldVar = TmpVar("old", selectorSym.type, Get(selectorSym));
JCVariableDecl newVar = TmpVar("new", selectorSym.type, Getter(selectorSym));
addPreface(oldVar);
addPreface(newVar);
// oldRcvr != newRcvr
condition = NE(id(oldVar), id(newVar));
}
}
@Override
List<JCExpression> determineArgsImpl() {
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) {
for (VisageExpression arg : args) {
if (arg.getVisageTag() == VisageTag.IDENT) {
VisageIdent ident = (VisageIdent)arg;
targs.append(getReceiverOrThis(ident.sym));
targs.append(Offset(ident.sym));
} else if (false/*disable-VSGC-4079*/ && preTrans.isImmutable(arg)) {
// pass VisageConstant wrapper for argument expression
targs.append(Call(defs.VisageConstant_make, translateExpr(arg, arg.type)));
// pass VisageConstant.VOFF$value as offset value
targs.append(Select(makeType(syms.visage_ConstantType), defs.varOFF$valueName));
} else {
TODO("non-Ident and non-immutable in bound call");
}
}
return targs.toList();
} else {
return super.determineArgsImpl();
}
}
@Override
JCExpression translateArg(VisageExpression arg, Type formal) {
if (conditionallyReevaluate && arg instanceof VisageIdent /*disable-VSGC-4079: && !preTrans.isImmutable(arg)*/) {
// if no args have changed, don't call function, just return previous value
Symbol sym = ((VisageIdent) arg).sym;
addBindee((VisageVarSymbol) sym); //TODO: isn't this redundant?
JCVariableDecl oldVar = TmpVar("old", formal, Get(sym));
JCVariableDecl newVar = TmpVar("new", formal, Getter(sym));
addPreface(oldVar);
addPreface(newVar);
// oldArg != newArg
JCExpression compare = NE(id(oldVar), id(newVar));
// concatenate with OR -- oldArg1 != newArg1 || oldArg2 != newArg2
condition = condition != null? OR(condition, compare) : compare;
return id(newVar);
} else {
return super.translateArg(arg, formal);
}
}
@Override
JCExpression fullExpression(JCExpression mungedToCheckTranslated) {
if (callBound) {
// call to a bound function in bind context
JCExpression tMeth = Select(mungedToCheckTranslated, methodName());
return m().Apply(translateExprs(typeargs), tMeth, determineArgs());
} else {
JCExpression full = super.fullExpression(mungedToCheckTranslated);
if (condition != null) {
// Always call function if the default has not been applied yet
full = TypeCast(targetType, Type.noType,
If (OR(condition, bindNeedsDefault(targetSymbol)),
full,
Get(targetSymbol)));
}
return full;
}
}
}
/**
* Translate if-expression
*
* bind if (cond) foo else bar
*
* becomes preface statements:
*
* T res;
* cond.preface;
* if (cond) {
* foo.preface;
* res = foo;
* } else {
* bar.preface;
* res = bar;
* }
*
* result value:
*
* res
*
*/
private class BoundIfExpressionTranslator extends ExpressionTranslator {
private final VisageIfExpression tree;
private final JCVariableDecl resVar;
private final Type type;
BoundIfExpressionTranslator(VisageIfExpression tree) {
super(tree.pos());
this.tree = tree;
this.type = (targetType != null)? targetType : tree.type;
this.resVar = TmpVar("res", type, null);
}
JCStatement side(VisageExpression expr) {
ExpressionResult res = translateToExpressionResult(expr, type);
addBindees(res.bindees());
addInterClassBindees(res.interClass());
return Block(res.statements().append(Stmt(m().Assign(id(resVar), res.expr()))));
}
protected ExpressionResult doit() {
JCExpression cond = translateExpr(tree.getCondition(), syms.booleanType);
addPreface(resVar);
addPreface(If (cond,
side(tree.getTrueExpression()),
side(tree.getFalseExpression())));
return toResult( id(resVar), type );
}
}
/****************************************************************************
* Bound Sequence Translators
****************************************************************************/
/**
* Abstract super class of bound sequence Translators.
*
* Provides the framework of abstract methods that must be implemented:
* makeSizeBody(), makeGetElementBody(), setupInvalidators()
*
* And provides common utilities
*/
private abstract class BoundSequenceTranslator extends ExpressionTranslator {
abstract JCStatement makeSizeBody();
abstract JCStatement makeGetElementBody();
abstract void setupInvalidators();
BoundSequenceTranslator(DiagnosticPosition diagPos) {
super(diagPos);
}
BoundSequenceResult doit() {
setupInvalidators();
return new BoundSequenceResult(bindees(), invalidators(), interClass(), makeGetElementBody(), makeSizeBody());
}
protected JCExpression getReceiverForCallHack(Symbol sym) {
if (sym.isStatic()) {
return makeType(sym.owner.type, false);
}
return getReceiver(sym);
}
JCExpression CallSize(Symbol sym) {
return CallSize(getReceiverForCallHack(sym), sym);
}
JCExpression CallSize(JCExpression rcvr, Symbol sym) {
if (types.isArray(sym.type)) {
JCVariableDecl vArr = TmpVar("arr", sym.type, Getter(rcvr,sym));
return
BlockExpression(
vArr,
If (NEnull(id(vArr)),
Select(id(vArr), names.length),
Int(0)
)
);
}
else if (((VisageVarSymbol) sym).useAccessors())
return Call(rcvr, attributeSizeName(sym));
else
return Call(defs.Sequences_size, Getter(rcvr, sym));
}
JCExpression CallGetElement(Symbol sym, JCExpression pos) {
return CallGetElement(getReceiverForCallHack(sym), sym, pos);
}
JCExpression CallGetElement(JCExpression rcvr, Symbol sym, JCExpression pos) {
if (types.isArray(sym.type)) {
JCVariableDecl vArr = TmpVar("arr", sym.type, Getter(rcvr,sym));
JCVariableDecl vPos = TmpVar("pos", syms.intType, pos);
return
BlockExpression(
vArr,
vPos,
If (OR(OR(
EQnull(id(vArr)),
LT(id(vPos), Int(0))),
LE(Select(id(vArr), names.length), id(vPos))),
DefaultValue(types.elemtype(sym.type)),
m().Indexed(id(vArr), id(vPos))
)
);
}
else if (((VisageVarSymbol) sym).useAccessors())
return Call(rcvr, attributeGetElementName(sym), pos);
else
return Call(Getter(rcvr, sym), defs.get_SequenceMethodName, pos);
}
private Name activeFlagBit = defs.varFlagSEQUENCE_LIVE;
private VisageVarSymbol flagSymbol = (VisageVarSymbol)targetSymbol;
JCExpression isSequenceActive() {
return FlagTest(flagSymbol, activeFlagBit, activeFlagBit);
}
JCExpression isSequenceDormant() {
return FlagTest(flagSymbol, activeFlagBit, null);
}
JCStatement setSequenceActive() {
return FlagChangeStmt(flagSymbol, null, BITOR(id(defs.varFlagSEQUENCE_LIVE), id(defs.varFlagINIT_INITIALIZED_DEFAULT)));
}
JCStatement Assign(JCExpression vid, JCExpression value) {
return Stmt(m().Assign(vid, value));
}
JCStatement Assign(JCVariableDecl var, JCExpression value) {
return Assign(id(var), value);
}
@Override
void addInvalidator(VisageVarSymbol sym, JCStatement invStmt) {
super.addInvalidator(sym, invStmt);
if (depGraphWriter != null) {
depGraphWriter.writeDependency(targetSymbol, sym);
}
}
}
/**
* Bound identifier Translator for identifiers referencing sequences
*
* Just forward the requests for size and elements
*/
class BoundIdentSequenceTranslator extends BoundSequenceTranslator {
// Symbol of the referenced
private final VisageVarSymbol sym;
// ExpressionResult for etracting bindee info
private final ExpressionResult exprResult;
BoundIdentSequenceTranslator(VisageIdent tree, ExpressionResult exprResult) {
super(tree.pos());
this.sym = (VisageVarSymbol) tree.sym;
this.exprResult = exprResult;
}
JCStatement makeSizeBody() {
JCVariableDecl vSize = TmpVar("size", syms.intType, CallSize(sym));
return
Block(
vSize,
If (isSequenceDormant(),
Block(
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vSize))
)
),
Return(id(vSize))
);
}
JCStatement makeGetElementBody() {
return
Block(
If (isSequenceDormant(),
Stmt(CallSize(targetSymbol))
),
Return(CallGetElement(sym, posArg()))
);
}
/**
* Simple bindee info from normal translation will do it
*/
void setupInvalidators() {
mergeResults(exprResult);
}
}
/**
* Bound identifier Translator for identifiers referencing sequences but pretending to be non-sequences
*
* Use the referenced as a sequence
*/
class BoundIdentSequenceFromNonTranslator extends BoundSequenceTranslator {
// Symbol of the referenced
private final VisageVarSymbol sym;
// Size holder
private final VisageVarSymbol sizeSym;
BoundIdentSequenceFromNonTranslator(VisageIdentSequenceProxy tree) {
super(tree.pos());
this.sym = (VisageVarSymbol) tree.sym;
this.sizeSym = tree.boundSizeSym();
}
JCExpression makeSizeValue() {
return Call(Getter(sym), defs.size_SequenceMethodName);
}
/**
* Body of the sequence size method.
*
* Get the stored size.
* If the sequence is uninitialized (size is invalid)
* Set the size var, from the proxied result. (thus initializing the sequence).
* Send initial update nodification.
* Return the size
*/
JCStatement makeSizeBody() {
JCVariableDecl sizeVar = MutableTmpVar("size", syms.intType, Get(sizeSym));
return
Block(
sizeVar,
If(EQ(id(sizeVar), Undefined()),
Block(
Stmt(m().Assign(id(sizeVar), makeSizeValue())),
SetStmt(sizeSym, id(sizeVar)),
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(sizeVar))
)
),
Return(id(sizeVar))
);
}
/**
* Body of the sequence get element method.
*
* Make sure the sequence is initialized, by calling the size method.
* Redirect to the proxied sequence to get the element.
*/
JCStatement makeGetElementBody() {
return
Block(
If(EQ(Get(sizeSym), Undefined()),
Stmt(CallSize(targetSymbol))
),
Return (Call(Getter(sym), defs.get_SequenceMethodName, posArg()))
);
}
/**
* Body of a invalidate$ method for the proxied sequences
*
* Do nothing if the sequence is uninitialized.
* If this is invalidation phase,
* send a blanket invalidation of the sequence.
* If this is trigger phase,
* send an invalidation of the whole sequence
* update the sequence size,
*/
private JCStatement makeInvalidateFuncValue() {
JCVariableDecl oldSizeVar = TmpVar("oldSize", syms.intType, Get(sizeSym));
JCVariableDecl newSizeVar = TmpVar("newSize", syms.intType, makeSizeValue());
return
Block(
oldSizeVar,
If(NE(id(oldSizeVar), Undefined()),
PhaseCheckedBlock(sym,
If(IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
newSizeVar,
SetStmt(sizeSym, id(newSizeVar)),
CallSeqTrigger(targetSymbol, Int(0), id(oldSizeVar), id(newSizeVar))
)
)
)
)
);
}
/**
* Set-up proxy's invalidator.
*/
void setupInvalidators() {
addInvalidator(sym, makeInvalidateFuncValue());
}
}
/**
* Bound block Translator block of sequence type
*
* Assumptions:
* Block vars have been moved out to class and replaced with VarInits.
* Block value has been made into a synthetic value, and a VarInit for has
* been added to block vars
*
* Core is that VarInits are run when size is first queried.
*/
class BoundBlockSequenceTranslator extends BoundSequenceTranslator {
// Symbol of the referenced
private final VisageVarSymbol vsym;
// The VarInits aka the non-value part of the block
private final List<VisageExpression> varInits;
BoundBlockSequenceTranslator(VisageBlock tree) {
super(tree.pos());
VisageIdent id = (VisageIdent) (tree.value);
this.vsym = (VisageVarSymbol) id.sym;
this.varInits = tree.stats;
}
JCStatement makeSizeBody() {
JCVariableDecl vSize = TmpVar("size", syms.intType, CallSize(vsym));
ListBuffer<JCStatement> tVarInits = ListBuffer.lb();
for (VisageExpression init : varInits) {
tVarInits.append(translateToStatement(init, syms.voidType));
}
tVarInits.append(vSize);
tVarInits.append(setSequenceActive());
tVarInits.append(CallSeqInvalidateUndefined(targetSymbol));
tVarInits.append(CallSeqTriggerInitial(targetSymbol, id(vSize)));
tVarInits.append(Return(id(vSize)));
return
Block(
If (isSequenceDormant(),
Block(
tVarInits.toList()
)
),
Return(CallSize(vsym))
);
}
JCStatement makeGetElementBody() {
return
Block(
If (isSequenceDormant(),
Stmt(CallSize(targetSymbol))
),
Return(CallGetElement(vsym, posArg()))
);
}
/**
* Updates through value
*/
void setupInvalidators() {
addBindee(vsym);
}
}
/**
* Bound type-cast Translator for type-cast from-and-to sequences
*
* Just forward the requests for size and elements, the latter type-converted
* to its the desired element type.
*/
class BoundTypeCastSequenceTranslator extends BoundSequenceTranslator {
final VisageVarSymbol exprSym;
final Type elemType;
BoundTypeCastSequenceTranslator(VisageTypeCast tree) {
super(tree.pos());
assert types.isSequence(tree.type);
assert tree.getExpression() instanceof VisageIdent; // Decompose should shred
this.exprSym = (VisageVarSymbol)((VisageIdent)tree.getExpression()).sym;
assert types.isSequence(tree.getExpression().type) || types.isArray(tree.getExpression().type);
this.elemType = types.elementType(tree.type);
}
JCStatement makeSizeBody() {
JCVariableDecl vSize = TmpVar(syms.intType, CallSize(exprSym));
return
Block(
vSize,
If (isSequenceDormant(),
Block(
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vSize))
)
),
Return(id(vSize))
);
}
JCStatement makeGetElementBody() {
//we could have a sequence of Object converted into sequence of Integers
//in this case the target type of the cast should be the boxed sequence
//element type (this doesn't get handled in lower)
Type targetType = elemType.isPrimitive() &&
!types.elementType(exprSym.type).isPrimitive() ?
types.boxedTypeOrType(elemType) :
elemType;
return Return(m().TypeCast(makeType(targetType), CallGetElement(exprSym, posArg())));
}
/**
* Simple bindee info from normal translation will do it
*/
void setupInvalidators() {
addBindee(exprSym);
}
}
/**
* Bound type-cast Translator for type-cast from nativearray to sequence
*/
class BoundTypeCastArrayToSequenceTranslator extends BoundTypeCastSequenceTranslator {
private final VisageVarSymbol sizeSym;
BoundTypeCastArrayToSequenceTranslator(VisageTypeCast tree) {
super(tree);
this.sizeSym = tree.boundArraySizeSym;
}
@Override
JCStatement makeSizeBody() {
JCVariableDecl vSize = TmpVar(syms.intType, CallSize(exprSym));
return
Block(
vSize,
If (isSequenceDormant(),
Block(
setSequenceActive(),
SetStmt(sizeSym, id(vSize)),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vSize))
)
),
Return(id(vSize))
);
}
/**
* Body of a invalidate$ method for the nativearray
*/
private JCStatement makeInvalidateArray() {
JCVariableDecl oldSizeVar = TmpVar("oldSize", syms.intType, Get(sizeSym));
JCVariableDecl newSizeVar = TmpVar("newSize", syms.intType, CallSize(exprSym));
return
PhaseCheckedBlock(exprSym,
If (isSequenceActive(),
Block(
If(IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
oldSizeVar,
newSizeVar,
SetStmt(sizeSym, id(newSizeVar)),
CallSeqTrigger(targetSymbol, Int(0), id(oldSizeVar), id(newSizeVar))
)
)
)
)
);
}
/**
* Set-up array's invalidator.
*/
@Override
void setupInvalidators() {
addInvalidator(exprSym, makeInvalidateArray());
}
}
/**
* Bound empty sequence Translator
*
* Size is always zero, element is always an error (so return default value)
*/
class BoundEmptySequenceTranslator extends BoundSequenceTranslator {
private final Type elemType;
BoundEmptySequenceTranslator(VisageSequenceEmpty tree) {
super(tree.pos());
this.elemType = types.elementType(tree.type);
}
JCStatement makeSizeBody() {
return Return(Int(0));
}
JCStatement makeGetElementBody() {
return Return(DefaultValue(elemType));
}
void setupInvalidators() {
// nada
}
}
/**
* Bound member-select reference to a sequence Translator
*
*
*/
private class BoundSelectSequenceTranslator extends BoundSequenceTranslator {
private final SelectTranslator strans;
private final Symbol refSym;
private final VisageVarSymbol selectorSym;
private final VisageVarSymbol sizeSym;
BoundSelectSequenceTranslator(VisageSelect tree) {
super(tree.pos());
this.strans = new SelectTranslator(tree);
this.refSym = strans.refSym;
this.sizeSym = tree.boundSize.sym;
VisageExpression selectorExpr = tree.getExpression();
assert canChange();
assert (selectorExpr instanceof VisageIdent);
VisageIdent selector = (VisageIdent) selectorExpr;
this.selectorSym = (VisageVarSymbol) selector.sym;
}
/*** forward to SelectTranslator ***/
private VisageExpression getToCheck() { return strans.getToCheck(); }
private JCExpression translateToCheck(VisageExpression expr) { return strans.translateToCheck(expr); }
private boolean canChange() { return strans.canChange(); }
private JCExpression wrapInNullCheckExpression(JCExpression full, JCExpression tToCheck, Type theResultType, Type theFullType) {
return strans.wrapInNullCheckExpression(full, tToCheck, theResultType, theFullType);
}
private JCStatement wrapInNullCheckStatement(JCExpression full, JCExpression tToCheck, Type theResultType, Type theFullType) {
return strans.wrapInNullCheckStatement(full, tToCheck, theResultType, theFullType);
}
private JCExpression selector() {
return Get(selectorSym);
}
/**
* Size accessor
* (
* if ( default-not-set ) {
* set-default-flag;
* clear selector's trigger flag
* invalidate the selector
* }
* // redirect to the size of the referenced sequence, updating the selector
* return get$selector()==null? 0 : get$selector().size$ref();
* }
*
*/
JCStatement makeSizeBody() {
assert selectorSym.useAccessors() : "Would need redesign to implement without accessors";
JCExpression tToCheck = translateToCheck(getToCheck());
JCStatement callSize = buildBody(tToCheck, CallSize(tToCheck, refSym), syms.intType);
return
Block(
If (isSequenceDormant(),
Block(
setSequenceActive(),
FlagChangeStmt(selectorSym, defs.varFlagINIT_STATE_MASK, defs.varFlagVALID_DEFAULT_APPLIED),
CallBeInvalidate(selectorSym),
CallBeTrigger(selectorSym)
)
),
callSize
);
}
/**
* Get sequence element
* {
* size$self(); // Access size to make sure we are initialized
* return get$selector()==null? <default> : get$selector().get$ref(pos);
* }
*/
JCStatement makeGetElementBody() {
JCExpression tToCheck = translateToCheck(getToCheck());
return
Block(
If (isSequenceDormant(),
Stmt(CallSize(targetSymbol))
),
buildBody(tToCheck, CallGetElement(tToCheck, refSym, posArg()), types.elementType(refSym.type))
);
}
protected JCStatement buildBody(JCExpression tToCheck, JCExpression full, Type theResultType) {
return wrapInNullCheckStatement(full, tToCheck, theResultType, theResultType);
}
/**
* $selector === null? 0 : $selector.size$ref();
*/
private JCExpression getSize() {
if (refSym.isStatic()) {
return CallSize(makeType(refSym.owner), refSym);
} else {
return
If (EQnull(selector()),
Int(0),
CallSize(selector(), refSym));
}
}
/**
* Invalidator for the selector
*
* invalidator$selector(int phase) {
* if (valid-for-this-phase) {
* clear-valid-for-this-phase;
* if ( is-invalidate-phase) {
* // remove dependent, do it now, so updates from referenced don't interject
* removeDependent($selector, VOFF$ref);
* // invalidate with undefined newSize, since we can't compute without enter trigger phase
* invalidate$self(0, undefined, undefined, phase);
* } else {
* int oldSize = $selector === null? 0 : $selector.size$ref();
* get$selector(); // update selector
* int newSize = $selector === null? 0 : $selector.size$ref();
* addDependent($selector, VOFF$ref);
* invalidate$self(0, oldSize, newSize, phase);
* }
* }
* }
*/
private JCStatement makeInvalidateSelector() {
JCVariableDecl oldSize = TmpVar(syms.intType, Get(sizeSym));
JCVariableDecl newSize = TmpVar(syms.intType, getSize());
return
PhaseCheckedBlock(selectorSym,
If (IsInvalidatePhase(),
Block(
If (NEnull(selector()),
CallStmt(defs.VisageBase_removeDependent,
selector(),
Offset(selector(), refSym),
getReceiverOrThis(selectorSym)
)
),
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
oldSize,
Stmt(Getter(selectorSym)),
newSize,
If (NEnull(selector()),
CallStmt(defs.VisageBase_addDependent,
selector(),
Offset(selector(), refSym),
getReceiverOrThis(selectorSym),
DepNum(getReceiver(selectorSym), selectorSym, refSym)
)
),
CallSeqTrigger(targetSymbol,
Int(0),
id(oldSize),
id(newSize)
)
)
)
);
}
/**
* Addition to the invalidate for this bound select sequence
*
* invalidate$self(int start, int end, int newLen, int phase) {
* if ( is-trigger-phase ) {
* $size = $size + newLen - (end - start);
* }
* ....
* }
*/
private JCStatement makeInvalidateSelf() {
return
Block(
// setSequenceActive(),
If (IsTriggerPhase(),
SetStmt(sizeSym,
PLUS(
Get(sizeSym),
MINUS(
newLengthArg(),
MINUS(
endPosArg(),
startPosArg()
)
)
)
)
)
);
}
void setupInvalidators() {
addInvalidator(selectorSym, makeInvalidateSelector());
addInvalidator(targetSymbol, makeInvalidateSelf());
addInterClassBindee(selectorSym, refSym);
}
}
/**
* Bound reverse operator sequence Translator
*/
private class BoundReverseSequenceTranslator extends BoundSequenceTranslator {
private final VisageVarSymbol argSym;
BoundReverseSequenceTranslator(VisageUnary tree) {
super(tree.pos());
VisageIdent arg = (VisageIdent) tree.arg;
this.argSym = (VisageVarSymbol) arg.sym;
}
/**
* Size accessor -- pass through
*/
JCStatement makeSizeBody() {
JCVariableDecl vSize = TmpVar("size", syms.intType, CallSize(argSym));
return
Block(
vSize,
If (isSequenceDormant(),
Block(
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vSize))
)
),
Return(id(vSize))
);
}
/**
* Get sequence element
* Element is underlying element at size - 1 - index
*/
JCStatement makeGetElementBody() {
return
Block(
If (isSequenceDormant(),
Stmt(CallSize(targetSymbol))
),
Return ( CallGetElement(argSym, MINUS( MINUS(CallSize(argSym), Int(1)), posArg()) ) )
);
}
/**
* Pass-through the invalidation reversing the start and end
*
* Compute the old size:
* oldSize = newSize - (newLen - (end - start));
*
* reversedStart = oldSize - 1 - (end - 1) = oldSize - end
* reversedEnd = oldSize - 1 + 1 - start = oldSize - start
*/
private JCStatement makeInvalidateArg() {
JCVariableDecl oldSize = TmpVar(syms.intType,
MINUS(
CallSize(argSym),
MINUS(
newLengthArg(),
MINUS(
endPosArg(),
startPosArg()
)
)
));
return
If (IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
If(IsUnchangedTrigger(),
Block(
CallSeqTriggerUnchanged(targetSymbol) // Pass it on
),
/*else (real trigger)*/
Block(
oldSize,
CallSeqTrigger(targetSymbol,
MINUS(id(oldSize), endPosArg()),
MINUS(id(oldSize), startPosArg()),
newLengthArg()
)
)
)
)
);
}
void setupInvalidators() {
addInvalidator(argSym, makeInvalidateArg());
}
}
/**
* Bound explicit sequence Translator ["hi", [2..k], x]
*
*
*/
private abstract class AbstractBoundExplicitSequenceTranslator extends BoundSequenceTranslator {
private final List<VisageVarSymbol> itemSyms;
private final List<VisageVarSymbol> itemLengthSyms;
final VisageVarSymbol ignoreInvalidationsSym;
final Type elemType;
final int length;
boolean DEBUG = false;
AbstractBoundExplicitSequenceTranslator(VisageSequenceExplicit tree) {
super(tree.pos());
this.itemSyms = tree.boundItemsSyms;
this.itemLengthSyms = tree.boundItemLengthSyms;
this.length = itemSyms.length();
this.elemType = types.elementType(tree.type);
this.ignoreInvalidationsSym = tree.boundIgnoreInvalidationsSym;
}
abstract JCStatement makeItemInvalidatePhase(int index);
abstract JCStatement makeItemTriggerPhase(int index, boolean isSequence);
boolean isSequence(int index) {
return types.isSequence(type(index));
}
boolean isFixedLength(int index) {
return itemLengthSym(index) == null;
}
VisageVarSymbol itemLengthSym(int index) {
return itemLengthSyms.get(index);
}
VisageVarSymbol itemSym(int index) {
return itemSyms.get(index);
}
Type type(int index) {
return itemSym(index).type;
}
JCExpression IsInvalid(VisageVarSymbol sym) {
return FlagTest(sym, defs.varFlagINVALID_STATE_BIT, defs.varFlagINVALID_STATE_BIT);
}
JCExpression IsInvalid(int index) {
return IsInvalid(itemSym(index));
}
JCStatement UpdateValue(int index) {
if (isFixedLength(index)) {
return SetStmt(itemSym(index), Getter(itemSym(index)));
} else {
return SetStmt(itemLengthSym(index),
isSequence(index)?
CallSize(itemSym(index)) :
If (EQnull(Set(itemSym(index), Getter(itemSym(index)))), Int(0), Int(1))
);
}
}
JCStatement Update(int index) {
return
Block(
UpdateValue(index),
FlagChangeStmt(itemSym(index), defs.varFlagSTATE_MASK, defs.varFlagVALID_DEFAULT_APPLIED)
);
}
JCStatement UpdateAll() {
ListBuffer<JCStatement> upds = ListBuffer.lb();
for (int i = 0; i < length; ++i) {
upds.append(Update(i));
}
return
upds.length()==1?
upds.first() :
Block(upds);
}
JCExpression TransientLength(int index) {
if (isFixedLength(index)) {
return Int(1);
} else {
return isSequence(index)?
CallSize(itemSym(index)) :
If (IsInvalid(index),
If (EQnull(Getter(itemSym(index))), Int(0), Int(1)),
Get(itemLengthSym(index))
);
}
}
JCExpression CummulativeTransientLength(int index) {
JCExpression sum = index==0? Int(0) : TransientLength(0);
for (int i = 1; i < index; ++i) {
sum = PLUS(sum, TransientLength(i));
}
return sum;
}
JCExpression CachedLength(int index) {
VisageVarSymbol lenSym = itemLengthSym(index);
return lenSym==null? Int(1) : Get(lenSym);
}
JCExpression CummulativeCachedSize(int index) {
JCExpression sum = index==0? Int(0) : CachedLength(0);
for (int i = 1; i < index; ++i) {
sum = PLUS(sum, CachedLength(i));
}
return sum;
}
JCExpression CachedGetElement(int index, JCExpression pos) {
if (isSequence(index)) {
return Call(attributeGetElementName(itemSym(index)), pos);
} else {
return Get(itemSym(index));
}
}
JCExpression TransientGetElement(int index, JCExpression pos) {
if (isSequence(index)) {
return Call(attributeGetElementName(itemSym(index)), pos);
} else {
return
If (IsInvalid(index),
Getter(itemSym(index)),
Get(itemSym(index))
);
}
}
/**
* Invalidate addition for an element in the sequence
*/
private JCStatement makeItemInvalidateInner(int index) {
return
If (AND(isSequenceActive(), NOT(Get(ignoreInvalidationsSym))),
Block(
If (IsInvalidatePhase(),
makeItemInvalidatePhase(index),
/*Else (Trigger phase)*/
makeItemTriggerPhase(index, isSequence(index))
)
));
}
/**
* Invalidate addition for an element in the sequence
*/
private JCStatement makeItemInvalidate(int index) {
if (isSequence(index)) {
return
makeItemInvalidateInner(index);
} else {
return
PhaseCheckedBlock(itemSym(index),
makeItemInvalidateInner(index)
);
}
}
/**
* For each item, and for size, set-up the invalidate method
*/
void setupInvalidators() {
for (int index = 0; index < length; ++index) {
addInvalidator(itemSym(index), makeItemInvalidate(index));
}
}
}
/**
* Bound explicit sequence Translator ["hi", [2..k], x]
*
*
*/
private class BoundExplicitSequenceTranslator extends AbstractBoundExplicitSequenceTranslator {
private final VisageVarSymbol sizeSym;
private final VisageVarSymbol lowestSym;
private final VisageVarSymbol highestSym;
private final VisageVarSymbol pendingSym;
private final VisageVarSymbol deltaSym;
private final VisageVarSymbol changeStartSym;
private final VisageVarSymbol changeEndSym;
BoundExplicitSequenceTranslator(VisageSequenceExplicit tree) {
super(tree);
this.sizeSym = tree.boundSizeSym;
this.lowestSym = tree.boundLowestInvalidPartSym;
this.highestSym = tree.boundHighestInvalidPartSym;
this.pendingSym = tree.boundPendingTriggersSym;
this.deltaSym = tree.boundDeltaSym;
this.changeStartSym = tree.boundChangeStartPosSym;
this.changeEndSym = tree.boundChangeEndPosSym;
}
private boolean isFixedLength() {
return sizeSym == null;
}
private JCExpression CachedSize() {
if (isFixedLength()) {
return Int(length);
} else {
return Get(sizeSym);
}
}
private JCStatement SetSizeStmt(JCExpression value) {
if (isFixedLength()) {
return null;
} else {
return SetStmt(sizeSym, value);
}
}
private JCExpression GetChangeStart() {
if (isFixedLength()) {
return Get(lowestSym);
} else {
return Get(changeStartSym);
}
}
private JCStatement SetChangeStartStmt(JCExpression value) {
if (isFixedLength()) {
return null;
} else {
return SetStmt(changeStartSym, value);
}
}
private JCStatement SetChangeEndStmt(JCExpression value) {
if (isFixedLength()) {
return null;
} else {
return SetStmt(changeEndSym, value);
}
}
JCStatement makeSizeBody() {
JCVariableDecl vSize = TmpVar("size", syms.intType, CummulativeCachedSize(length));
return
Block(
If(isSequenceDormant(),
Block(
UpdateAll(),
vSize,
setSequenceActive(),
SetSizeStmt(id(vSize)),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vSize)),
Return(id(vSize))
)
),
DEBUG? Debug("size pending=", Get(pendingSym)) : null,
DEBUG? Debug(" size=", CachedSize()) : null,
Return(
isFixedLength()?
Int(length) :
If (EQ(Get(pendingSym), Int(0)),
CachedSize(),
CummulativeTransientLength(length)
)
)
);
}
/**
* if (pos < 0) {
* return null; // default
* }
* int start = 0;
* int next = 0;
* next += size$s1();
* if (pos < next) {
* return get$s1(pos – start);
* }
* start = next;
* next += size$s2();
* if (pos < next) {
* return get$s2(pos – start);
* }
* start = next;
* next += size$s3();
* if (pos < next) {
* return get$s3(pos – start);
* )
* return null; // default
*/
JCStatement makeGetElementBody() {
JCVariableDecl vStart = MutableTmpVar("start", syms.intType, Int(0));
JCVariableDecl vNext = MutableTmpVar("next", syms.intType, Int(0));
ListBuffer<JCStatement> stmts = ListBuffer.lb();
ListBuffer<JCStatement> pendStmts = ListBuffer.lb();
ListBuffer<JCStatement> normalStmts = ListBuffer.lb();
stmts.appendList(Stmts(
DEBUG? Debug("GetElement pending=", Get(pendingSym)) : null,
DEBUG? Debug(" size=", CachedSize()) : null,
If(LT(posArg(), Int(0)),
Return(DefaultValue(elemType))
),
If (isSequenceDormant(),
Stmt(CallSize(targetSymbol))
),
vStart,
vNext
));
//TODO: switch-statement if isFixedLength
for (int index = 0; index < length; ++index) {
pendStmts.appendList(Stmts(
Assign(vNext, PLUS(id(vNext), TransientLength(index))),
If(LT(posArg(), id(vNext)),
Return(TransientGetElement(index, MINUS(posArg(), id(vStart))))
),
Assign(vStart, id(vNext))
));
}
for (int index = 0; index < length; ++index) {
normalStmts.appendList(Stmts(
Assign(vNext, PLUS(id(vNext), CachedLength(index))),
If(LT(posArg(), id(vNext)),
Return(CachedGetElement(index, MINUS(posArg(), id(vStart))))
),
Assign(vStart, id(vNext))
));
}
stmts.appendList(Stmts(
If (EQ(Get(pendingSym), Int(0)),
Block(normalStmts),
Block(pendStmts)
),
Return(DefaultValue(elemType)
)));
return Block(stmts);
}
JCStatement makeItemInvalidatePhase(int index) {
return
Block(
If (LT(Get(highestSym), Int(0)),
Block(
Assert(EQ(Get(highestSym), Undefined())),
Assert(EQ(Get(pendingSym), Int(0))),
DEBUG? Debug("inv 1st #"+index+" pending=", Get(pendingSym)) : null,
SetStmt(highestSym, Int(index)),
SetStmt(lowestSym, Int(index)),
SetStmt(pendingSym, Int(1)),
isFixedLength()? null :
SetStmt(deltaSym, Int(0)),
SetChangeStartStmt(CummulativeCachedSize(index)),
SetChangeEndStmt(PLUS(GetChangeStart(), CachedLength(index))),
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (already have invalid parts)*/
Block(
DEBUG? Debug("inv #"+index+" pending=", Get(pendingSym)) : null,
SetStmt(pendingSym, PLUS(Get(pendingSym), Int(1))),
If (LT(Int(index), Get(lowestSym)),
Block(
SetChangeStartStmt(CummulativeCachedSize(index)),
SetStmt(lowestSym, Int(index))
)
),
If (GT(Int(index), Get(highestSym)),
Block(
SetChangeEndStmt(CummulativeCachedSize(index+1)),
SetStmt(highestSym, Int(index))
)
)
)
)
);
}
JCStatement makeItemTriggerPhase(int index, boolean isSequence) {
JCVariableDecl vEnd = TmpVar("hi", syms.intType, PLUS(Get(highestSym), Int(1)));
JCStatement triggerChanged =
isFixedLength()?
Block(
vEnd,
SetStmt(highestSym, Undefined()),
DEBUG? Debug("bulk #"+index+" changeStart=", Get(lowestSym)) : null,
CallSeqTrigger(targetSymbol,
Get(lowestSym),
id(vEnd),
MINUS(id(vEnd), Get(lowestSym))
)
) :
Block(
SetStmt(highestSym, Undefined()),
DEBUG? Debug("bulk #"+index+" changeStart=", GetChangeStart()) : null,
CallSeqTrigger(targetSymbol,
GetChangeStart(),
Get(changeEndSym),
PLUS(Get(deltaSym), MINUS(Get(changeEndSym), GetChangeStart()))
)
);
JCStatement fire;
if (isSequence) {
fire =
Block(
If (
AND(
EQ(Get(highestSym), Int(index)),
EQ(Get(lowestSym), Int(index))
),
Block(
SetStmt(highestSym, Undefined()),
If (IsUnchangedTrigger(),
Block(
DEBUG? Debug("uncng #"+index, Int(index)) : null,
CallSeqTriggerUnchanged(targetSymbol) // Pass it on
),
/*else (real trigger)*/
Block(
DEBUG? Debug("one #"+index+" changeStart=", GetChangeStart()) : null,
DEBUG? Debug(" endPos=", endPosArg()) : null,
CallSeqTrigger(targetSymbol,
PLUS(GetChangeStart(), startPosArg()),
If (EQ(endPosArg(), Undefined()),
Undefined(),
PLUS(GetChangeStart(), endPosArg())
),
newLengthArg()
)
)
)
),
triggerChanged
)
);
} else {
fire = triggerChanged;
}
JCVariableDecl vOldLength = TmpVar("oldLen", syms.intType, CachedLength(index));
JCVariableDecl vNewLength = TmpVar("newLen", syms.intType, CachedLength(index));
return
Block(
Assert(AND(GE(Int(index), Get(lowestSym)), LE(Int(index), Get(highestSym)))),
Assert(GT(Get(pendingSym), Int(0))),
SetStmt(pendingSym, MINUS(Get(pendingSym), Int(1))),
isFixedLength(index)? null :
vOldLength,
SetStmt(ignoreInvalidationsSym, True()),
Update(index),
SetStmt(ignoreInvalidationsSym, False()),
isFixedLength(index)? null :
vNewLength,
isFixedLength(index)? null :
SetSizeStmt(PLUS(CachedSize(), MINUS(id(vNewLength), id(vOldLength)))),
isFixedLength(index)? null :
SetStmt(deltaSym, PLUS(Get(deltaSym), MINUS(id(vNewLength), id(vOldLength)))),
DEBUG? Debug("trig #"+index+" pending = ", Get(pendingSym)) : null,
If (EQ(Get(pendingSym), Int(0)),
fire
)
);
}
}
/**
* Bound explicit sequence Translator [x]
*
*
*/
private class BoundExplicitSingletonSequenceTranslator extends AbstractBoundExplicitSequenceTranslator {
BoundExplicitSingletonSequenceTranslator(VisageSequenceExplicit tree) {
super(tree);
}
JCStatement makeSizeBody() {
JCVariableDecl vSize = TmpVar("size", syms.intType, TransientLength(0));
return
Block(
If(isSequenceDormant(),
Block(
UpdateAll(),
vSize,
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vSize)),
Return(id(vSize))
)
),
Return(TransientLength(0))
);
}
JCStatement makeGetElementBody() {
return
Block(
If(NE(posArg(), Int(0)),
Return(DefaultValue(elemType))
),
If (isSequenceDormant(),
Stmt(CallSize(targetSymbol))
),
Return(TransientGetElement(0, null))
);
}
JCStatement makeItemInvalidatePhase(int index) {
return
CallSeqInvalidateUndefined(targetSymbol);
}
JCStatement makeItemTriggerPhase(int index, boolean isSequence) {
JCVariableDecl vOldLength = TmpVar("oldLen", syms.intType, CachedLength(index));
return
Block(
vOldLength,
SetStmt(ignoreInvalidationsSym, True()),
Update(index),
SetStmt(ignoreInvalidationsSym, False()),
CallSeqTrigger(targetSymbol,
Int(0),
id(vOldLength),
CachedLength(index)
)
);
}
}
/**
* Bound range sequence Translator [10..100 step 10]
*/
private class BoundRangeSequenceTranslator extends BoundSequenceTranslator {
private final VisageVar varLower;
private final VisageVar varUpper;
private final VisageVar varStep;
private final VisageVar varSize;
private final Type elemType;
private final boolean exclusive;
BoundRangeSequenceTranslator(VisageSequenceRange tree) {
super(tree.pos());
this.varLower = (VisageVar)tree.getLower();
this.varUpper = (VisageVar)tree.getUpper();
this.varStep = (VisageVar)tree.getStepOrNull();
this.varSize = tree.boundSizeVar;
if (varLower.type == syms.visage_IntegerType) {
this.elemType = syms.visage_IntegerType;
} else {
this.elemType = syms.visage_NumberType;
}
this.exclusive = tree.isExclusive();
}
private JCExpression zero() {
return m().Literal(elemType.tag, 0);
}
private JCExpression one() {
return m().Literal(elemType.tag, 1);
}
private JCExpression lower() {
return Get(varLower.getSymbol());
}
private JCExpression upper() {
return Get(varUpper.getSymbol());
}
private JCExpression step() {
return varStep == null?
one()
: Get(varStep.getSymbol());
}
private JCExpression size() {
return Get(varSize.getSymbol());
}
private JCStatement setLower(JCExpression value) {
return SetStmt(varLower.getSymbol(), value);
}
private JCStatement setUpper(JCExpression value) {
return SetStmt(varUpper.getSymbol(), value);
}
private JCStatement setStep(JCExpression value) {
return SetStmt(varStep.getSymbol(), value);
}
private JCStatement setSize(JCExpression value) {
return SetStmt(varSize.getSymbol(), value);
}
private JCExpression CallGetter(VisageVar var) {
return Getter(var.getSymbol());
}
private JCExpression CallLower() {
return CallGetter(varLower);
}
private JCExpression CallUpper() {
return CallGetter(varUpper);
}
private JCExpression CallStep() {
return varStep == null?
one()
: CallGetter(varStep);
}
private JCExpression DIVstep(JCExpression v1) {
return varStep == null?
v1
: DIV(v1, step());
}
private JCExpression MULstep(JCExpression v1) {
return varStep == null?
v1
: MUL(v1, step());
}
private JCExpression exclusive() {
return Boolean(exclusive);
}
private JCExpression calculateSize(JCExpression vl, JCExpression vu, JCExpression vs) {
RuntimeMethod rm =
(elemType == syms.visage_NumberType)?
defs.Sequences_calculateFloatRangeSize
: defs.Sequences_calculateIntRangeSize;
return Call(rm, vl, vu, vs, exclusive());
}
private JCExpression isInvalid(VisageVar var) {
if (var == null) {
return False();
} else {
return FlagTest(var.getSymbol(), defs.varFlagINVALID_STATE_BIT, defs.varFlagINVALID_STATE_BIT);
}
}
private JCStatement setValid(VisageVar var) {
if (var == null) {
return null;
} else {
return FlagChangeStmt(var.getSymbol(), defs.varFlagSTATE_MASK, defs.varFlagSTATE_VALID);
}
}
/**
* int size$range()
*/
JCStatement makeSizeBody() {
JCVariableDecl vNewLower = TmpVar("newLower", elemType, CallLower());
JCVariableDecl vNewUpper = TmpVar("newUpper", elemType, CallUpper());
JCVariableDecl vNewStep = TmpVar("newStep", elemType, CallStep());
JCVariableDecl vNewSize = TmpVar("newSize", syms.intType,
calculateSize(id(vNewLower), id(vNewUpper), id(vNewStep)));
return
Block(
If (isSequenceDormant(),
Block(
vNewLower,
vNewUpper,
vNewStep,
vNewSize,
setLower(id(vNewLower)),
setUpper(id(vNewUpper)),
(varStep == null)? null :
setStep(id(vNewStep)),
setSize(id(vNewSize)),
setValid(varLower),
setValid(varUpper),
setValid(varStep),
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vNewSize))
),
/*else (it is live) */
If (OR(OR(isInvalid(varLower), isInvalid(varUpper)), isInvalid(varStep)),
Block(
// We are being asked for the size during an invalidation
// Calculate without changing state
Return (calculateSize(CallLower(), CallUpper(), CallStep()))
)
)
),
Return (size())
);
}
/**
* float get$range(int pos) {
* return (pos >= 0 && pos < getSize())?
* pos * step + lower
* : 0.0f;
* }
*/
JCStatement makeGetElementBody() {
JCVariableDecl vNewLower = TmpVar("newLower", elemType, CallLower());
JCVariableDecl vNewUpper = TmpVar("newUpper", elemType, CallUpper());
JCVariableDecl vNewStep = TmpVar("newStep", elemType, CallStep());
JCVariableDecl vNewSize = TmpVar("newSize", syms.intType,
calculateSize(id(vNewLower), id(vNewUpper), id(vNewStep)));
return
Block(
If (isSequenceDormant(),
Block(
// Force initialization
Stmt(CallSize(targetSymbol))
),
/*else (it is live) */
If (OR(OR(isInvalid(varLower), isInvalid(varUpper)), isInvalid(varStep)),
Block(
// We are being asked for an element during an invalidation
// Calculate without changing state
vNewLower,
vNewUpper,
vNewStep,
vNewSize,
Return (
If (AND(
GE(posArg(), Int(0)),
LT(posArg(), id(vNewSize))
),
PLUS(MUL(posArg(), id(vNewStep)), id(vNewLower)),
zero()
)
)
)
)
),
Return(
If (AND(
GE(posArg(), Int(0)),
LT(posArg(), size())
),
PLUS(MULstep(posArg()), lower()),
zero()
)
)
);
}
/**
* float newLower = getLower();
* if (step != 0 && lower != newLower) {
* int newSize = Sequences.calculateFloatRangeSize(newLower, upper, step, false);
* int loss = 0;
* int gain = 0;
* float delta = newLower - lower;
* if (size == 0 || ((delta % step) != 0)) {
* // invalidate everything - new or start point different
* loss = size;
* gain = newSize;
* } else if (newLower > lower) {
* // shrink -- chop off the front
* loss = (int) delta / step;
* if (loss > size)
* loss = size
* } else {
* // grow -- add to the beginning
* gain = (int) -delta / step;
* }
* if (phase == TRIGGER_PHASE) {
* lower = newLower;
* size = newSize;
* }
* invalidate$range(0, loss, gain, phase);
* }
*/
private JCStatement makeInvalidateLower() {
JCVariableDecl vNewLower = TmpVar("newLower", elemType, CallLower());
JCVariableDecl vNewSize = TmpVar("newSize", syms.intType,
calculateSize(id(vNewLower), upper(), step()));
JCVariableDecl vLoss = MutableTmpVar("loss", syms.intType, Int(0));
JCVariableDecl vGain = MutableTmpVar("gain", syms.intType, Int(0));
JCVariableDecl vDelta = TmpVar("delta", elemType, MINUS(id(vNewLower), lower()));
JCVariableDecl vUnits = TmpVar("units", elemType, DIVstep(id(vDelta)));
return
PhaseCheckedBlock(varLower.sym,
If (isSequenceActive(),
Block(
If (IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
vNewLower,
setValid(varLower),
//Debug("Trig Lower ", id(vNewLower)),
If (AND(NE(step(), zero()), NE(lower(), id(vNewLower))),
Block(
vNewSize,
vLoss,
vGain,
vDelta,
If (OR(EQ(size(), Int(0)), NE(MOD(id(vDelta), step()), zero())),
Block( // was empty, or re-aligned on step
Assign(vLoss, size()),
Assign(vGain, id(vNewSize))
),
/* else (not a redo) */
Block(
vUnits,
If (GT(id(vUnits), zero()),
Block(
Assign(vLoss, m().TypeCast(syms.intType, id(vUnits))),
If (GT(id(vLoss), size()),
Assign(vLoss, size())
)
),
/* else */
Block(
Assign(vGain, m().TypeCast(syms.intType, NEG(id(vUnits))))
)
)
)
),
setLower(id(vNewLower)),
setSize(id(vNewSize)),
CallSeqTrigger(targetSymbol, Int(0), id(vLoss), id(vGain))
),
/*else (no change in lower, send no-change trigger)*/
Block(
CallSeqTriggerUnchanged(targetSymbol)
)
)
)
)
)));
}
/**
* float newUpper = getUpper();
* if (step != 0 && upper != newUpper) {
* int newSize = Sequences.calculateFloatRangeSize(lower, newUpper, step, false);
* int oldSize = size();
* if (phase == TRIGGER_PHASE) {
* upper = newUpper;
* size = newSize;
* }
* if (newSize >= oldSize)
* // grow
* invalidate$range(oldSize, oldSize, newSize-oldSize, phase);
* else
* // shrink
* invalidate$range(newSize, oldSize, 0, phase);
* }
*/
private JCStatement makeInvalidateUpper() {
JCVariableDecl vNewUpper = TmpVar("newUpper", elemType, CallUpper());
JCVariableDecl vOldSize = TmpVar("oldSize", syms.intType, size());
JCVariableDecl vNewSize = TmpVar("newSize", syms.intType,
calculateSize(lower(), id(vNewUpper), step()));
return
PhaseCheckedBlock(varUpper.sym,
If (isSequenceActive(),
Block(
If (IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
vNewUpper,
setValid(varUpper),
//Debug("Trig Upper ", id(vNewUpper)),
If (AND(NE(step(), zero()), NE(upper(), id(vNewUpper))),
Block(
vNewSize,
vOldSize,
setUpper(id(vNewUpper)),
setSize(id(vNewSize)),
If (GE(id(vNewSize), id(vOldSize)),
CallSeqTrigger(targetSymbol, id(vOldSize), id(vOldSize), MINUS(id(vNewSize), id(vOldSize))),
/*else*/
CallSeqTrigger(targetSymbol, id(vNewSize), id(vOldSize), Int(0))
)
),
/*else (no change in upper, send no-change trigger)*/
Block(
CallSeqTriggerUnchanged(targetSymbol)
)
)
)
))));
}
/**
* float newStep = getStep();
* if (step != newStep) {
* int newSize = Sequences.calculateFloatRangeSize(lower, upper, newStep, false);
* int oldSize = size();
* if (phase == TRIGGER_PHASE) {
* step = newStep;
* size = newSize;
* }
* // Invalidate everything
* invalidate$range(0, oldSize, newSize, phase);
* }
*/
private JCStatement makeInvalidateStep() {
JCVariableDecl vNewStep = TmpVar("newStep", elemType, CallStep());
JCVariableDecl vOldSize = TmpVar("oldSize", syms.intType, size());
JCVariableDecl vNewSize = TmpVar("newSize", syms.intType,
calculateSize(lower(), upper(), id(vNewStep)));
return
PhaseCheckedBlock(varStep.sym,
If (isSequenceActive(),
Block(
If (IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
vNewStep,
setValid(varStep),
//Debug("Trig Step ", id(vNewStep)),
If (NE(step(), id(vNewStep)),
Block(
vNewSize,
vOldSize,
setStep(id(vNewStep)),
setSize(id(vNewSize)),
CallSeqTrigger(targetSymbol, Int(0), id(vOldSize), id(vNewSize))
),
/*else (no change in step, send no-change trigger)*/
Block(
CallSeqTriggerUnchanged(targetSymbol)
)
)
)))));
}
/**
* Set invalidators for the synthetic support variables
*/
void setupInvalidators() {
addInvalidator(varLower.sym, makeInvalidateLower());
addInvalidator(varUpper.sym, makeInvalidateUpper());
if (varStep != null) {
addInvalidator(varStep.sym, makeInvalidateStep());
}
}
}
/**
* Bound slice sequence Translator seq[3..5]
*/
private class BoundSliceSequenceTranslator extends BoundSequenceTranslator {
private final VisageVarSymbol seqSym;
private final VisageVarSymbol lowerSym;
private final VisageVarSymbol upperSym;
private final boolean isExclusive;
private final Type elemType;
BoundSliceSequenceTranslator(VisageSequenceSlice tree) {
super(tree.pos());
this.seqSym = (VisageVarSymbol) (((VisageIdent) tree.getSequence()).sym);
this.lowerSym = (VisageVarSymbol) (((VisageIdent) tree.getFirstIndex()).sym);
this.upperSym = tree.getLastIndex() == null?
null :
(VisageVarSymbol) (((VisageIdent) tree.getLastIndex()).sym);
this.isExclusive = tree.getEndKind() == SequenceSliceTree.END_EXCLUSIVE;
this.elemType = types.elementType(tree.type);
}
private JCExpression lower() {
return Get(lowerSym);
}
private JCExpression upper() {
return Get(upperSym);
}
private JCStatement setLower(JCExpression value) {
return SetStmt(lowerSym, value);
}
private JCExpression CallSeqSize() {
return CallSize(seqSym);
}
private JCExpression CallSeqGetElement(JCExpression pos) {
return CallGetElement(seqSym, pos);
}
private JCExpression CallLower() {
return Getter(lowerSym);
}
private JCExpression CallUpper() {
return Getter(upperSym);
}
/**
* if (lower < 0) lower = 0;
* if (lower > underlyingSize) lower = underlyingSize;
* if (upper < lower) upper = lower;
* if (upper > underlyingSize) upper = underlyingSize;
* int size = upper - lower;
* if (sequence-inactive) {
* set-active;
* invalidate
* }
* return size
*/
JCStatement makeSizeBody() {
JCVariableDecl vSeqSize = TmpVar("seqSize", syms.intType, CallSeqSize());
JCVariableDecl vLower = MutableTmpVar("lower", syms.intType, CallLower());
// standardize on exclusive upper
JCVariableDecl vUpper = MutableTmpVar("upper", syms.intType,
upperSym==null?
isExclusive?
MINUS(id(vSeqSize), Int(1)) :
id(vSeqSize) :
isExclusive?
CallUpper() :
PLUS(CallUpper(), Int(1)));
JCVariableDecl vSize = TmpVar("size", syms.intType, MINUS(id(vUpper), id(vLower)));
return
Block(
vSeqSize,
vLower,
vUpper,
If (GT(id(vLower), id(vSeqSize)),
Assign(vLower, id(vSeqSize))
),
If (LT(id(vUpper), id(vLower)),
Assign(vUpper, id(vLower))
),
If (GT(id(vUpper), id(vSeqSize)),
Assign(vUpper, id(vSeqSize))
),
vSize,
If (isSequenceDormant(),
Block(
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, id(vSize))
)
),
Return (id(vSize))
);
}
/**
* if (sequence-is-dormant) {
* call size -- to initialize it
* }
* if (lower < 0) lower = 0;
* if (pos < 0 || pos >= (upper - lower)) return default-value;
* return seq[pos + lower];
*/
JCStatement makeGetElementBody() {
JCVariableDecl vLower = TmpVar("lower", syms.intType, CallLower());
// standardize on exclusive upper
JCVariableDecl vUpper = MutableTmpVar("upper", syms.intType,
upperSym==null?
isExclusive?
MINUS(CallSeqSize(), Int(1)) :
CallSeqSize() :
isExclusive?
CallUpper() :
PLUS(CallUpper(), Int(1)));
return
Block(
If (isSequenceDormant(),
Block(
Stmt(CallSize(targetSymbol))
)
),
vLower,
vUpper,
If (LT(id(vUpper), id(vLower)),
Assign(vUpper, id(vLower))
),
If (OR(LT(posArg(), Int(0)), GE(posArg(), MINUS(id(vUpper), id(vLower)))),
Return (DefaultValue(elemType))
),
Return (CallSeqGetElement(PLUS(posArg(), id(vLower))))
);
}
/**
* Invalidator for lower
*/
private JCStatement makeInvalidateLower() {
JCVariableDecl vOldLower = MutableTmpVar("oldLower", syms.intType, lower());
JCVariableDecl vNewLower = MutableTmpVar("newLower", syms.intType, CallLower());
JCVariableDecl vSeqSize = TmpVar("seqSize", syms.intType, CallSeqSize());
JCVariableDecl vUpper = MutableTmpVar("upper", syms.intType,
upperSym==null?
isExclusive?
MINUS(id(vSeqSize), Int(1)) :
id(vSeqSize) :
isExclusive?
CallUpper() :
PLUS(CallUpper(), Int(1)));
return
PhaseCheckedBlock(lowerSym,
If (isSequenceActive(),
Block(
If (IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
vOldLower,
vNewLower,
vSeqSize,
vUpper,
If (LT(id(vNewLower), Int(0)),
Assign(vNewLower, Int(0))
),
setLower(id(vNewLower)),
If (GT(id(vUpper), id(vSeqSize)),
Assign(vUpper, id(vSeqSize))
),
If (GT(id(vNewLower), id(vUpper)),
Assign(vNewLower, id(vUpper))
),
If (GT(id(vOldLower), id(vUpper)),
Assign(vOldLower, id(vUpper))
),
If (GT(id(vNewLower), id(vOldLower)),
Block(
// lose elements from the front
CallSeqTrigger(targetSymbol, Int(0), MINUS(id(vNewLower), id(vOldLower)), Int(0))
),
/*else*/ If (LT(id(vNewLower), id(vOldLower)),
Block(
// Gain elements in the front
CallSeqTrigger(targetSymbol, Int(0), Int(0), MINUS(id(vOldLower), id(vNewLower)))
),
/*else (no change, send no-change trigger)*/
Block(
CallSeqTriggerUnchanged(targetSymbol)
)
))
)
)
)
)
);
}
/**
* Invalidator for upper
*
* Adjust uppers (old and new) between lower and the underlying sequence size.
* If upper is greater, elements added at end:
* invalidate(oldSize, oldSize, delta)
* If upper is lesser, elements removed from end
* invalidate(newSize, oldSize, 0)
*/
private JCStatement makeInvalidateUpper() {
JCVariableDecl vOldUpper = MutableTmpVar("oldUpper", syms.intType,
isExclusive?
upper() :
PLUS(upper(), Int(1)));
JCVariableDecl vNewUpper = MutableTmpVar("newUpper", syms.intType,
isExclusive?
CallUpper() :
PLUS(CallUpper(), Int(1)));
JCVariableDecl vSeqSize = TmpVar("seqSize", syms.intType, CallSeqSize());
JCVariableDecl vLower = MutableTmpVar("lower", syms.intType, lower());
JCVariableDecl vOldSize = TmpVar("oldSize", syms.intType, MINUS(id(vOldUpper), id(vLower)));
return
PhaseCheckedBlock(upperSym,
If (isSequenceActive(),
Block(
If (IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
vOldUpper,
vNewUpper,
vSeqSize,
vLower,
If (GT(id(vLower), id(vSeqSize)),
Assign(vLower, id(vSeqSize))
),
If (GT(id(vOldUpper), id(vSeqSize)),
Assign(vOldUpper, id(vSeqSize))
),
If (GT(id(vNewUpper), id(vSeqSize)),
Assign(vNewUpper, id(vSeqSize))
),
If (LT(id(vOldUpper), id(vLower)),
Assign(vOldUpper, id(vLower))
),
If (LT(id(vNewUpper), id(vLower)),
Assign(vNewUpper, id(vLower))
),
vOldSize,
If (GT(id(vNewUpper), id(vOldUpper)),
Block(
// Gain elements at the end
CallSeqTrigger(targetSymbol, id(vOldSize), id(vOldSize), MINUS(id(vNewUpper), id(vOldUpper)))
),
/*else*/ If (LT(id(vNewUpper), id(vOldUpper)),
Block(
// Lose elements in the end
CallSeqTrigger(targetSymbol, MINUS(id(vNewUpper), id(vLower)), id(vOldSize), Int(0))
),
/*else (no change, send no-change trigger)*/
Block(
CallSeqTriggerUnchanged(targetSymbol)
)
))
)
)
)
)
);
}
/**
* Invalidator for the underlying sequence
*
* Adjust uppers (old and new) between lower and the underlying sequence size.
* If upper is greater, elements added at end:
* invalidate(oldSize, oldSize, delta)
* If upper is lesser, elements removed from end
* invalidate(newSize, oldSize, 0)
*/
private JCStatement makeInvalidateUnderlying() {
// startPosArg(), endPosArg(), newLengthArg()
JCVariableDecl vDelta = TmpVar("delta", syms.intType, MINUS(newLengthArg(), MINUS(endPosArg(), startPosArg())));
JCVariableDecl vSeqSize = TmpVar("seqSize", syms.intType, CallSeqSize());
JCVariableDecl vLower = MutableTmpVar("lower", syms.intType, lower());
JCVariableDecl vUpper = MutableTmpVar("upper", syms.intType,
upperSym==null?
isExclusive?
MINUS(id(vSeqSize), Int(1)) :
id(vSeqSize) :
isExclusive?
upper() :
PLUS(upper(), Int(1)));
JCVariableDecl vOldSeqSize = TmpVar("oldSeqSize", syms.intType, MINUS(id(vSeqSize), id(vDelta)));
JCVariableDecl vOldUpper = MutableTmpVar("adjOldUpper", syms.intType, id(vUpper));
JCVariableDecl vNewUpper = MutableTmpVar("adjNewUpper", syms.intType, id(vUpper));
JCVariableDecl vOldLower = MutableTmpVar("adjOldLower", syms.intType, id(vLower));
JCVariableDecl vNewLower = MutableTmpVar("adjNewLower", syms.intType, id(vLower));
JCVariableDecl vBegin = MutableTmpVar("begin", syms.intType, id(vLower));
JCVariableDecl vEnd = MutableTmpVar("end", syms.intType, id(vUpper));
JCVariableDecl vOldBegin = MutableTmpVar("beginOld", syms.intType, id(vBegin));
JCVariableDecl vOldEnd = MutableTmpVar("endOld", syms.intType, id(vEnd));
JCVariableDecl vNewBegin = MutableTmpVar("beginNew", syms.intType, id(vBegin));
JCVariableDecl vNewEnd = MutableTmpVar("endNew", syms.intType, id(vEnd));
return
If (isSequenceActive(),
Block(
If (IsInvalidatePhase(),
Block(
CallSeqInvalidateUndefined(targetSymbol)
),
/*Else (Trigger phase)*/
Block(
If (IsUnchangedTrigger(),
Block(
CallSeqTriggerUnchanged(targetSymbol) // Pass it on
),
/*else (real trigger)*/
Block(
vDelta,
vSeqSize,
vLower,
vUpper,
vOldSeqSize,
vOldLower,
vOldUpper,
If(LT(id(vOldSeqSize), id(vOldUpper)),
Assign(vOldUpper, id(vOldSeqSize))
),
If(LT(id(vOldUpper), id(vOldLower)),
Assign(vOldLower, id(vOldUpper))
),
// Change is before or in slice
If (LT(startPosArg(), id(vUpper)),
Block(
// If change is entirely before slice
If (LE(endPosArg(), id(vLower)),
Block(
If (NE(id(vDelta), Int(0)),
Block(
vNewUpper,
vNewLower,
If(LT(id(vSeqSize), id(vNewUpper)),
Assign(vNewUpper, id(vSeqSize))
),
If(LT(id(vNewUpper), id(vNewLower)),
Assign(vNewLower, id(vNewUpper))
),
CallSeqTrigger(targetSymbol,
Int(0),
MINUS(id(vOldUpper), id(vOldLower)),
MINUS(id(vNewUpper), id(vNewLower))
)
)
)
),
/*else -- change is within slice */
Block(
vBegin,
vEnd,
// Starts at the greater of the change start and the slice lower
If(GT(startPosArg(), id(vBegin)),
Assign(vBegin, startPosArg())
),
// If the change does not shift elements, limit to end position
If(AND(EQ(id(vDelta), Int(0)), LT(endPosArg(), id(vEnd))),
Assign(vEnd, endPosArg())
),
// Now constrain to be within old/new underlying sequence length
vOldEnd,
vNewEnd,
If(LT(id(vOldSeqSize), id(vOldEnd)),
Assign(vOldEnd, id(vOldSeqSize))
),
If(LT(id(vSeqSize), id(vNewEnd)),
Assign(vNewEnd, id(vSeqSize))
),
vOldBegin,
vNewBegin,
If(LT(id(vOldEnd), id(vOldBegin)),
Assign(vOldBegin, id(vOldEnd))
),
If(LT(id(vNewEnd), id(vNewBegin)),
Assign(vNewBegin, id(vNewEnd))
),
// Invalidate
CallSeqTrigger(targetSymbol,
MINUS(id(vOldBegin), id(vOldLower)),
MINUS(id(vOldEnd), id(vOldLower)),
MINUS(id(vNewEnd), id(vNewBegin))
)
)
)
)
)
)
)
)
)));
}
/**
* Set invalidators for the synthetic support variables
*/
void setupInvalidators() {
addInvalidator(lowerSym, makeInvalidateLower());
if (upperSym!=null)
addInvalidator(upperSym, makeInvalidateUpper());
addInvalidator(seqSym, makeInvalidateUnderlying());
}
}
private class BoundForExpressionTranslator extends BoundSequenceTranslator {
VisageForExpression forExpr;
VisageForExpressionInClause clause;
BoundForExpressionTranslator(VisageForExpression tree) {
super(tree.pos());
this.forExpr = tree;
this.clause = tree.inClauses.head; // KLUDGE - FIXME
}
JCStatement makeSizeBody() {
VisageVarSymbol helperSym = clause.boundHelper.sym;
JCExpression createHelper;
// Translate
// var y = bind for (x in xs) body(x, indexof x)
// to (roughly, using a hybrid of Java with object-literals):
// y$helper = new BoundForHelper() {
// VisageForPart makeForPart(int $index$) {
// // The following body of makeForPart
// // is the body of the for
// class anon implements VisageForPart {
// var $indexof$x: Integer = $index$;
// var x;
// def result = bind value
// };
// }
Type inductionType = types.boxedTypeOrType(clause.inductionVarSym.type);
Type bodyType = forExpr.bodyExpr.type;
// Translate the part created in the Visage AST
JCExpression makePart = toJava.translateToExpression(forExpr.bodyExpr, bodyType);
BlockExprJCBlockExpression jcb = (BlockExprJCBlockExpression) makePart;
// Add access methods
JCClassDecl tcdecl = (JCClassDecl) jcb.stats.head;
ClassSymbol csym = tcdecl.sym;
tcdecl.defs = tcdecl.defs
.append(makeSetInductionVarMethod(csym, inductionType))
.append(makeGetIndexMethod(csym))
.append(makeAdjustIndexMethod(csym));
jcb.stats = jcb.stats
.append(CallStmt(makeType(((VisageBlock)forExpr.bodyExpr).value.type), defs.count_VisageObjectFieldName))
.append(Stmt(m().Assign(Select(Get(helperSym), defs.partResultVarNum_BoundForHelper), Offset(clause.boundResultVarSym)))
);
Type helperType = clause.boundHelper.type;
JCVariableDecl indexParam = Var(syms.intType, names.fromString(defs.dollarIndexNamePrefix()), null);
Type partType = types.applySimpleGenericType(syms.visage_ForPartInterfaceType, inductionType);
JCMethodDecl makeDecl = Method(Flags.PUBLIC,
partType,
names.fromString(VisageDefs.makeForPart_AttributeMethodPrefix),
List.<JCVariableDecl>of(indexParam),
currentClass().sym,
Return(makePart)
);
JCClassDecl helperClass = m().AnonymousClassDef(m().Modifiers(0), List.<JCTree>of(makeDecl));
Symbol seqSym = ((VisageIdent)(clause.getSequenceExpression())).sym;
createHelper = m().NewClass(null, null, // FIXME
makeType(helperType),
List.<JCExpression>of(
getReceiverOrThis(targetSymbol),
Offset(targetSymbol),
Offset(seqSym),
Boolean(clause.getIndexUsed())
),
helperClass);
return
Block(
If(EQnull(Get(helperSym)),
Block(
setSequenceActive(),
Stmt(Set(clause.boundHelper.sym, createHelper))
)
),
Return(Call(Get(clause.boundHelper.sym), defs.size_SequenceMethodName))
);
}
private JCMethodDecl Method(long flags, Type returnType, Name methodName, List<JCVariableDecl> params, Symbol owner, JCStatement stmt) {
ListBuffer<Type> paramTypes = ListBuffer.lb();
for (JCVariableDecl param : params) {
paramTypes.append(param.getType().type);
}
return Method(flags, returnType, methodName, paramTypes.toList(), params, owner, Stmts(stmt));
}
// Make a set induction variable method
private JCTree makeSetInductionVarMethod(ClassSymbol owner, Type inductionType) {
return
Method(
Flags.PUBLIC,
syms.voidType,
defs.setInductionVar_BoundForPartMethodName,
List.of(Param(inductionType, defs.value_ArgName)),
owner,
SetterStmt(null, clause.inductionVarSym, id(defs.value_ArgName))
);
}
// Make an adjust index variable method
private JCTree makeAdjustIndexMethod(ClassSymbol owner) {
return
Method(
Flags.PUBLIC,
syms.voidType,
defs.adjustIndex_BoundForPartMethodName,
List.of(Param(syms.intType, defs.value_ArgName)),
owner,
SetterStmt(clause.indexVarSym,
PLUS(
Get(null, clause.indexVarSym),
id(defs.value_ArgName)))
);
}
// Make a get index variable method
private JCTree makeGetIndexMethod(ClassSymbol owner) {
return
Method(
Flags.PUBLIC,
syms.intType,
defs.getIndex_BoundForPartMethodName,
List.<JCVariableDecl>nil(),
owner,
Return(Get(null, clause.indexVarSym))
);
}
/**
* Create the elem$ body
* For primitives, handle null (out-of-range)
* by returning default value.
*/
JCStatement makeGetElementBody() {
JCStatement initAssure =
If(EQnull(Get(clause.boundHelper.sym)),
Stmt(CallSize(targetSymbol))
);
Type elemType = types.elementType(forExpr.type);
JCExpression elemFromHelper =
Call(Get(clause.boundHelper.sym), defs.get_SequenceMethodName, posArg());
JCVariableDecl vValue = TmpVar("val", types.boxedTypeOrType(elemType), elemFromHelper);
if (elemType.isPrimitive() || isValueType(elemType)) {
return
Block(
initAssure,
vValue,
Return(
If(EQnull(id(vValue)),
DefaultValue(elemType),
id(vValue)
)
)
);
} else {
return
Block(
initAssure,
Return(elemFromHelper)
);
}
}
void setupInvalidators() {
if (clause.seqExpr instanceof VisageIdent) {
Symbol bindee = ((VisageIdent) clause.seqExpr).sym;
JCStatement inv =
If(NEnull(Get(clause.boundHelper.sym)),
CallStmt(Get(clause.boundHelper.sym),
defs.replaceParts_BoundForMethodName,
startPosArg(), endPosArg(), newLengthArg(), phaseArg()));
addInvalidator((VisageVarSymbol) bindee, inv);
}
}
}
/**
* Bound if-expression over sequences.
*
* Assumptions:
* No eager compution and no invalate calls until sequence is active.
* Sequence is made active by a call to size.
* Once the sequence is active,
* the cond field is kept up-to-date (by condition invalidator);
* the size field is kept up-to-date (by the condition and arm invalidators
*/
private class BoundIfSequenceTranslator extends BoundSequenceTranslator {
private final VisageVarSymbol condSym;
private final VisageVarSymbol thenSym;
private final VisageVarSymbol elseSym;
private final VisageVarSymbol sizeSym;
BoundIfSequenceTranslator(VisageIfExpression tree) {
super(tree.pos());
this.condSym = tree.boundCondVar.sym;
this.thenSym = tree.boundThenVar.sym;
this.elseSym = tree.boundElseVar.sym;
this.sizeSym = tree.boundSizeVar.sym;
}
JCExpression CallGetCond() {
return Getter(condSym);
}
private JCStatement MarkValid(VisageVarSymbol sym) {
if (sym.hasFlags()) {
return FlagChangeStmt(sym, defs.varFlagSTATE_MASK, defs.varFlagSTATE_VALID);
} else {
// Block() skips null statements.
return null;
}
}
private JCStatement MarkInvalid(VisageVarSymbol sym) {
if (sym.hasFlags()) {
return FlagChangeStmt(sym, defs.varFlagSTATE_MASK, defs.varFlagINVALID_STATE_BIT);
} else {
// Block() skips null statements.
return null;
}
}
private JCExpression IsInvalid(VisageVarSymbol sym) {
if (sym.hasFlags()) {
return FlagTest(sym, defs.varFlagINVALID_STATE_BIT, defs.varFlagINVALID_STATE_BIT);
} else {
return False();
}
}
private JCExpression IsValid(VisageVarSymbol sym) {
return NOT(IsInvalid(sym));
}
/**
* Body of the sequence size method.
*
* If the sequence is dormant
* Set it active.
* Eagerly calculate the if-condition.
* Use that to determine (and set) the size from appropriate branch arm.
* Send initial update nodification.
* Else (already active)
* If the size has been marked invalid,
* determine (and set) the size
* In either case, return the (possibly updated) size field
*/
JCStatement makeSizeBody() {
return
Block(
If (isSequenceDormant(),
Block(
SetStmt(condSym, CallGetCond()),
SetStmt(sizeSym,
If (Get(condSym),
CallSize(thenSym),
CallSize(elseSym)
)
),
MarkValid(condSym),
MarkValid(thenSym),
MarkValid(elseSym),
MarkValid(sizeSym),
setSequenceActive(),
CallSeqInvalidateUndefined(targetSymbol),
CallSeqTriggerInitial(targetSymbol, Get(sizeSym))
),
/*Else (already active)*/
Block(
If (IsInvalid(sizeSym),
If (OR(OR(
IsInvalid(condSym),
IsInvalid(thenSym)),
IsInvalid(elseSym)),
// Accessing in between invalidation and triggering, compute, but don't smash size
Block(
Return (
If (CallGetCond(),
CallSize(thenSym),
CallSize(elseSym)
)
)
),
/*else (nothing actually invalid (note that for next time))*/
Block(
MarkValid(sizeSym)
)
)
)
)
),
Return(Get(sizeSym))
);
}
/**
* Body of the sequence get element method.
*
* Make sure the sequence is initialized, by calling the size method.
* Redirect to the arm sequence to get the element.
*/
JCStatement makeGetElementBody() {
return
Block(
If (isSequenceDormant(),
Stmt(CallSize(targetSymbol)) // Assure initialized
),
If (IsInvalid(sizeSym),
If (CallGetCond(),
Return(CallGetElement(thenSym, posArg())),
Return(CallGetElement(elseSym, posArg()))
),
If (Get(condSym),
Return(CallGetElement(thenSym, posArg())),
Return(CallGetElement(elseSym, posArg()))
)
)
);
}
/**
* Body of a invalidate$ method for the synthetic condition boolean.
*/
private JCStatement makeInvalidateCond() {
JCVariableDecl oldCondVar = TmpVar("oldCond", syms.booleanType, Get(condSym));
JCVariableDecl newCondVar = TmpVar("newCond", syms.booleanType, CallGetCond());
JCVariableDecl oldSizeVar = TmpVar("oldSize", syms.intType, Get(sizeSym));
return
If(isSequenceActive(),
If(IsInvalidatePhase(),
Block(
// Mark mid invalidation
MarkInvalid(condSym),
MarkInvalid(sizeSym),
// Whole sequence potentially invalid
If (AND(
IsValid(thenSym),
IsValid(elseSym)),
CallSeqInvalidateUndefined(targetSymbol)
)
),
/*Else (Trigger phase)*/
Block(
If (IsInvalid(condSym),
Block(
oldCondVar,
newCondVar,
If (NE(id(newCondVar), id(oldCondVar)),
Block(
oldSizeVar,
SetStmt(sizeSym,
If (id(newCondVar),
CallSize(thenSym),
CallSize(elseSym)
)
),
SetStmt(condSym, id(newCondVar)),
MarkValid(condSym),
MarkValid(thenSym),
MarkValid(elseSym),
MarkValid(sizeSym),
CallSeqTrigger(targetSymbol, Int(0), id(oldSizeVar), Get(sizeSym))
),
/*else (condition did not actually change, no or empty change trigger)*/
Block(
MarkValid(condSym),
If (AND(
IsValid(thenSym),
IsValid(elseSym)),
CallSeqTriggerUnchanged(targetSymbol)
)
)
)
)
)
)
)
);
}
/**
* Body of a invalidate$ method for the synthetic bound sequences
* that is a branch arm.
*
* Do nothing if the sequence is dormant.
* If this is invalidation phase, send a blanket invalidation of the sequence.
* If this is trigger phase and we are the active arm, update the sequence size,
* and just pass the invalidation through.
*/
private JCStatement makeInvalidateArm(VisageVarSymbol armSym, boolean take) {
return
If(AND(isSequenceActive(), EQ(Get(condSym), Boolean(take))),
If(IsInvalidatePhase(),
Block(
// Mark mid invalidation
MarkInvalid(armSym),
MarkInvalid(sizeSym),
// Whole sequence potentially invalid
If (IsValid(condSym),
CallSeqInvalidateUndefined(targetSymbol)
)
),
/*Else (Trigger phase)*/
Block(
If (AND(
IsInvalid(armSym),
OR(
IsValid(condSym),
EQ(Get(condSym), CallGetCond())
)),
Block(
SetStmt(sizeSym, CallSize(armSym)), // update the size
MarkValid(thenSym),
MarkValid(elseSym),
MarkValid(condSym),
MarkValid(sizeSym),
CallSeqTrigger(targetSymbol, startPosArg(), endPosArg(), newLengthArg())
)
)
)
)
);
}
/**
* Set-up the condition and branch arm invalidators.
*/
void setupInvalidators() {
addInvalidator(condSym, makeInvalidateCond());
addInvalidator(thenSym, makeInvalidateArm(thenSym, true));
addInvalidator(elseSym, makeInvalidateArm(elseSym, false));
}
}
/***********************************************************************
*
* Visitors (alphabetical order)
*
* Override those that need special bind handling
*/
private boolean isTargettedToSequence() {
return types.isSequence(targetSymbol.type);
}
public void visitBlockExpression(VisageBlock tree) {
if (tree == boundExpression && isTargettedToSequence()) {
// We are translating to a bound sequence
result = new BoundBlockSequenceTranslator(tree).doit();
} else {
result = new BoundBlockExpressionTranslator(tree).doit();
}
}
@Override
public void visitForExpression(VisageForExpression tree) {
VisageClassDeclaration prevClass = VisageTranslateBind.this.currentClass();
try {
VisageTranslateBind.this.setCurrentClass((VisageClassDeclaration)((VisageBlock)tree.bodyExpr).stats.head);
result = new BoundForExpressionTranslator(tree).doit();
}
finally {
VisageTranslateBind.this.setCurrentClass(prevClass);
}
}
@Override
public void visitFunctionInvocation(final VisageFunctionInvocation tree) {
result = new BoundFunctionCallTranslator(tree).doit();
}
@Override
public void visitFunctionValue(final VisageFunctionValue tree) {
result = toJava().translateToExpressionResult(tree, targetSymbol.type);
}
public void visitIdent(VisageIdent tree) {
if (tree == boundExpression && isTargettedToSequence()) {
// We are translating to a bound sequence
if (tree instanceof VisageIdentSequenceProxy) {
result = new BoundIdentSequenceFromNonTranslator((VisageIdentSequenceProxy) tree).doit();
} else {
final ExpressionResult exprResult = new BoundIdentTranslator(tree).doit();
result = new BoundIdentSequenceTranslator(tree, exprResult).doit();
}
} else {
final ExpressionResult exprResult = new BoundIdentTranslator(tree).doit();
result = exprResult;
}
}
@Override
public void visitIfExpression(VisageIfExpression tree) {
if (tree == boundExpression && isTargettedToSequence()) {
// We are translating to a bound sequence
result = new BoundIfSequenceTranslator(tree).doit();
} else {
result = new BoundIfExpressionTranslator(tree).doit();
}
}
@Override
public void visitInstanciate(VisageInstanciate tree) {
result = new BoundInstanciateTranslator(tree).doit();
}
@Override
public void visitParens(VisageParens tree) {
result = translateBoundExpression(tree.expr, targetSymbol);
}
public void visitSelect(VisageSelect tree) {
if (tree == boundExpression && isTargettedToSequence()) {
// We want to translate to a bound sequence
result = new BoundSelectSequenceTranslator(tree).doit();
} else {
result = new BoundSelectTranslator(tree, targetSymbol).doit();
}
}
@Override
public void visitSequenceEmpty(VisageSequenceEmpty tree) {
if (tree == boundExpression && isTargettedToSequence()) {
// We want to translate to a bound sequence
result = new BoundEmptySequenceTranslator(tree).doit();
} else {
super.visitSequenceEmpty(tree);
}
}
@Override
public void visitSequenceExplicit(VisageSequenceExplicit tree) {
if (tree.boundPendingTriggersSym == null) {
result = new BoundExplicitSingletonSequenceTranslator(tree).doit();
} else {
result = new BoundExplicitSequenceTranslator(tree).doit();
}
}
@Override
public void visitSequenceRange(VisageSequenceRange tree) {
result = new BoundRangeSequenceTranslator(tree).doit();
}
@Override
public void visitSequenceSlice(VisageSequenceSlice tree) {
result = new BoundSliceSequenceTranslator(tree).doit();
}
@Override
public void visitTypeCast(final VisageTypeCast tree) {
if (tree == boundExpression && isTargettedToSequence()) {
// We want to translate to a bound sequence
if (tree.boundArraySizeSym != null) {
result = new BoundTypeCastArrayToSequenceTranslator(tree).doit();
} else {
result = new BoundTypeCastSequenceTranslator(tree).doit();
}
} else {
super.visitTypeCast(tree);
}
}
@Override
public void visitUnary(VisageUnary tree) {
if (tree == boundExpression && isTargettedToSequence()) {
// We want to translate to a bound sequence
assert tree.getVisageTag() == VisageTag.REVERSE : "should be reverse operator";
result = new BoundReverseSequenceTranslator(tree).doit();
} else {
super.visitUnary(tree);
}
}
/***********************************************************************
*
* Utilities
*
*/
protected String getSyntheticPrefix() {
return "bvisage$";
}
}