![JAR search and dependency download from the Maven repository](/logo.png)
com.strobel.expressions.StackSpiller Maven / Gradle / Ivy
/*
* StackSpiller.java
*
* Copyright (c) 2012 Mike Strobel
*
* This source code is based on the Dynamic Language Runtime from Microsoft,
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.expressions;
import com.strobel.core.ReadOnlyList;
import com.strobel.core.StrongBox;
import com.strobel.core.VerifyArgument;
import com.strobel.reflection.Type;
import com.strobel.util.ContractUtils;
import com.strobel.util.TypeUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @author Mike Strobel
*/
@SuppressWarnings("UnusedParameters")
final class StackSpiller {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS AND UTILITY METHODS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private final TempMaker _tm = new TempMaker();
/**
* Initial stack state. Normally empty, but when inlining the lambda we
* might have a non-empty starting stack state.
*/
private final Stack _startingStack;
/**
* Lambda rewrite result. We need this for inlined lambdas to figure out
* whether we need to guarantee it an empty stack.
*/
private RewriteAction _lambdaRewrite;
private StackSpiller(final Stack stack) {
_startingStack = stack;
}
private void verifyTemps() {
_tm.verifyTemps();
}
private ParameterExpression makeTemp(final Type type) {
return _tm.temp(type);
}
private int mark() {
return _tm.mark();
}
private void free(final int mark) {
_tm.free(mark);
}
private ParameterExpression toTemp(final Expression expression, final StrongBox save) {
final ParameterExpression temp = makeTemp(expression.getType());
save.value = Expression.assign(temp, expression);
return temp;
}
/**
* Creates a special block that is marked as not allowing jumps in. This should not
* be used for rewriting BlockExpression itself, or anything else that supports jumping.
* @param expressions the expressions within the block
* @return the new block
*/
private static Expression makeBlock(final ExpressionList extends Expression> expressions) {
return new SpilledExpressionBlock(expressions);
}
private static void verifyRewrite(final Result result, final Expression node) {
assert result.Node != null;
// (result.Action == RewriteAction.None) if and only if (node == result.Node)
assert (result.Action == RewriteAction.None) ^ (node != result.Node)
:"rewrite action does not match node object identity";
// if the original node is an extension node, it should have been rewritten
assert result.Node.getNodeType() != ExpressionType.Extension : "extension nodes must be rewritten";
// if we have Copy, then node type must match
assert result.Action != RewriteAction.Copy || node.getNodeType() == result.Node.getNodeType() || node.canReduce()
: "rewrite action does not match node object kind";
// New type must be reference assignable to the old type
// (our rewrites preserve type exactly, but the rules for rewriting
// an extension node are more lenient, see Expression.ReduceAndCheck())
assert TypeUtils.areReferenceAssignable(node.getType(), result.Node.getType())
: "rewritten object must be reference assignable to the original type";
}
private static T[] clone(final ExpressionList original, final int max) {
assert original != null;
assert max < original.size();
final T[] clone = original.toArray();
for (int i = max; i < clone.length; i++) {
clone[i] = null;
}
return clone;
}
private static T[] clone(final ReadOnlyList original, final int max) {
assert original != null;
assert max < original.size();
final T[] clone = original.toArray();
for (int i = max; i < clone.length; i++) {
clone[i] = null;
}
return clone;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ENTRY POINT //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static LambdaExpression analyzeLambda(final LambdaExpression lambda) {
return /*Optimizer.optimize(lambda)*/lambda.accept(new StackSpiller(Stack.Empty));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// REWRITE METHODS //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
LambdaExpression rewrite(final LambdaExpression lambda) {
verifyTemps();
final Result body = rewriteExpressionFreeTemps(lambda.getBody(), _startingStack);
_lambdaRewrite = body.getAction();
verifyTemps();
if (body.getAction() != RewriteAction.None) {
// Create a new scope for temps
// (none of these will be hoisted so there is no closure impact)
Expression newBody = body.Node;
if (!_tm.getTemps().isEmpty()) {
newBody = Expression.block(_tm.getTempsList(), newBody);
}
// Clone the lambda, replacing the body & variables
return lambda.update(
newBody,
lambda.getParameters()
);
}
return lambda;
}
private Result rewriteExpressionFreeTemps(final Expression expression, final Stack stack) {
final int mark = mark();
final Result result = rewriteExpression(expression, stack);
free(mark);
return result;
}
private Result rewriteExpression(final Expression node, final Stack stack) {
if (node == null) {
return new Result(RewriteAction.None, null);
}
if (node.canReduce()) {
return rewriteReducibleExpression(node, stack);
}
final Result result;
switch (node.getNodeType()) {
case Add:
result = rewriteBinaryExpression(node, stack);
break;
case And:
result = rewriteBinaryExpression(node, stack);
break;
case AndAlso:
result = rewriteLogicalBinaryExpression(node, stack);
break;
case ArrayLength:
result = rewriteUnaryExpression(node, stack);
break;
case ArrayIndex:
result = rewriteBinaryExpression(node, stack);
break;
case Call:
result = rewriteMethodCallExpression(node, stack);
break;
case Coalesce:
result = rewriteLogicalBinaryExpression(node, stack);
break;
case Conditional:
result = rewriteConditionalExpression(node, stack);
break;
case Convert:
result = rewriteUnaryExpression(node, stack);
break;
case ConvertChecked:
result = rewriteUnaryExpression(node, stack);
break;
case Divide:
result = rewriteBinaryExpression(node, stack);
break;
case Equal:
result = rewriteBinaryExpression(node, stack);
break;
case ExclusiveOr:
result = rewriteBinaryExpression(node, stack);
break;
case GreaterThan:
result = rewriteBinaryExpression(node, stack);
break;
case GreaterThanOrEqual:
result = rewriteBinaryExpression(node, stack);
break;
case Invoke:
result = rewriteInvocationExpression(node, stack);
break;
case Lambda:
result = rewriteLambdaExpression(node, stack);
break;
case LeftShift:
result = rewriteBinaryExpression(node, stack);
break;
case LessThan:
result = rewriteBinaryExpression(node, stack);
break;
case LessThanOrEqual:
result = rewriteBinaryExpression(node, stack);
break;
case MemberAccess:
result = rewriteMemberExpression(node, stack);
break;
case Modulo:
result = rewriteBinaryExpression(node, stack);
break;
case Multiply:
result = rewriteBinaryExpression(node, stack);
break;
case Negate:
result = rewriteUnaryExpression(node, stack);
break;
case UnaryPlus:
result = rewriteUnaryExpression(node, stack);
break;
case New:
result = rewriteNewExpression(node, stack);
break;
case NewArrayInit:
result = rewriteNewArrayExpression(node, stack);
break;
case NewArrayBounds:
result = rewriteNewArrayExpression(node, stack);
break;
case Not:
result = rewriteUnaryExpression(node, stack);
break;
case NotEqual:
result = rewriteBinaryExpression(node, stack);
break;
case Or:
result = rewriteBinaryExpression(node, stack);
break;
case OrElse:
result = rewriteLogicalBinaryExpression(node, stack);
break;
case RightShift:
result = rewriteBinaryExpression(node, stack);
break;
case UnsignedRightShift:
result = rewriteBinaryExpression(node, stack);
break;
case Subtract:
result = rewriteBinaryExpression(node, stack);
break;
case InstanceOf:
result = rewriteTypeBinaryExpression(node, stack);
break;
case Assign:
result = rewriteAssignBinaryExpression(node, stack);
break;
case Block:
result = rewriteBlockExpression(node, stack);
break;
case Decrement:
result = rewriteUnaryExpression(node, stack);
break;
case Extension:
result = rewriteExtensionExpression(node, stack);
break;
case Goto:
result = rewriteGotoExpression(node, stack);
break;
case Increment:
result = rewriteUnaryExpression(node, stack);
break;
case Label:
result = rewriteLabelExpression(node, stack);
break;
case Loop:
result = rewriteLoopExpression(node, stack);
break;
case Switch:
result = rewriteSwitchExpression(node, stack);
break;
case Throw:
result = rewriteThrowUnaryExpression(node, stack);
break;
case Try:
result = rewriteTryExpression(node, stack);
break;
case Unbox:
result = rewriteUnaryExpression(node, stack);
break;
case TypeEqual:
result = rewriteTypeBinaryExpression(node, stack);
break;
case OnesComplement:
result = rewriteUnaryExpression(node, stack);
break;
case IsTrue:
result = rewriteUnaryExpression(node, stack);
break;
case IsFalse:
result = rewriteUnaryExpression(node, stack);
break;
case ReferenceEqual:
result = rewriteBinaryExpression(node, stack);
break;
case ReferenceNotEqual:
result = rewriteBinaryExpression(node, stack);
break;
case IsNull:
result = rewriteUnaryExpression(node, stack);
break;
case IsNotNull:
result = rewriteUnaryExpression(node, stack);
break;
case AddAssign:
case AndAssign:
case DivideAssign:
case ExclusiveOrAssign:
case LeftShiftAssign:
case ModuloAssign:
case MultiplyAssign:
case OrAssign:
case RightShiftAssign:
case UnsignedRightShiftAssign:
case SubtractAssign:
case PreIncrementAssign:
case PreDecrementAssign:
case PostIncrementAssign:
case PostDecrementAssign:
result = rewriteReducibleExpression(node, stack);
break;
case Quote:
case Parameter:
case Constant:
case RuntimeVariables:
case DefaultValue:
return new Result(RewriteAction.None, node);
default:
throw ContractUtils.unreachable();
}
verifyRewrite(result, node);
return result;
}
private Result rewriteReducibleExpression(final Expression expr, final Stack stack) {
final Result result = rewriteExpression(expr.reduce(), stack);
// It's at least Copy because we reduced the node
return new Result(result.Action.or(RewriteAction.Copy), result.Node);
}
private Result rewriteTryExpression(final Expression expr, final Stack stack) {
final TryExpression node = (TryExpression) expr;
// Try statement definitely needs an empty stack so its
// child nodes execute at empty stack.
final Result body = rewriteExpression(node.getBody(), Stack.Empty);
ReadOnlyList handlers = node.getHandlers();
CatchBlock[] clone = null;
RewriteAction action = body.Action;
if (handlers != null) {
for (int i = 0, n = handlers.size(); i < n; i++) {
RewriteAction currentAction = body.Action;
CatchBlock handler = handlers.get(i);
Expression filter = handler.getFilter();
if (handler.getFilter() != null) {
// Our code gen saves the incoming filter value and provides it as a
// variable so the stack is empty.
final Result rFault = rewriteExpression(handler.getFilter(), Stack.Empty);
action = action.or(rFault.Action);
currentAction = currentAction.or(rFault.Action);
filter = rFault.Node;
}
// Catch block starts with an empty stack (guaranteed by TryStatement)
final Result rBody = rewriteExpression(handler.getBody(), Stack.Empty);
action = action.or(rBody.Action);
currentAction = currentAction.or(rBody.Action);
if (currentAction != RewriteAction.None) {
handler = Expression.makeCatch(
handler.getTest(),
handler.getVariable(),
rBody.Node,
filter);
if (clone == null) {
clone = clone(handlers, i);
}
}
if (clone != null) {
clone[i] = handler;
}
}
}
final Result rFinally = rewriteExpression(node.getFinallyBlock(), Stack.Empty);
action = action.or(rFinally.Action);
// If the stack is initially not empty, rewrite to spill the stack
if (stack != Stack.Empty) {
action = RewriteAction.SpillStack;
}
if (action != RewriteAction.None) {
if (clone != null) {
// okay to wrap because we aren't modifying the array
handlers = new ReadOnlyList<>(clone);
}
return new Result(
action,
new TryExpression(
node.getType(),
body.Node,
handlers,
rFinally.Node
)
);
}
return new Result(action, expr);
}
private Result rewriteThrowUnaryExpression(final Expression expr, final Stack stack) {
final UnaryExpression node = (UnaryExpression) expr;
// Throw statement itself does not care about the stack, but it will empty
// the stack, and it may cause stack imbalance. We need to restore the stack
// after unconditional throw to make JIT happy; this has an effect of executing
// 'throw' on an empty stack.
final Result value = rewriteExpressionFreeTemps(node.getOperand(), Stack.Empty);
RewriteAction action = value.Action;
if (stack != Stack.Empty) {
action = RewriteAction.SpillStack;
}
if (action != RewriteAction.None) {
return new Result(
action,
Expression.makeThrow(value.Node, node.getType())
);
}
return new Result(action, expr);
}
private Result rewriteSwitchExpression(final Expression expr, final Stack stack) {
final SwitchExpression node = (SwitchExpression)expr;
// The switch statement test is emitted on the stack in current state
final Result switchValue = rewriteExpressionFreeTemps(node.getSwitchValue(), stack);
RewriteAction action = switchValue.Action;
ReadOnlyList cases = node.getCases();
SwitchCase[] clone = null;
for (int i = 0, n = cases.size(); i < n; i++) {
Expression[] cloneTests = null;
SwitchCase switchCase = cases.get(i);
ExpressionList extends Expression> testValues = switchCase.getTestValues();
for (int j = 0, m = testValues.size(); j < m; j++) {
// All tests execute at the same stack state as the switch.
// This is guaranteed by the compiler (to simplify spilling)
final Result test = rewriteExpression(testValues.get(j), stack);
action = action.or(test.Action);
if (cloneTests == null && test.Action != RewriteAction.None) {
cloneTests = clone(testValues, j);
}
if (cloneTests != null) {
cloneTests[j] = test.Node;
}
}
// And all the cases also run on the same stack level.
final Result body = rewriteExpression(switchCase.getBody(), stack);
action = action.or(body.Action);
if (body.Action != RewriteAction.None || cloneTests != null) {
if (cloneTests != null) {
testValues = new ExpressionList<>(cloneTests);
}
switchCase = new SwitchCase(body.Node, testValues);
if (clone == null) {
clone = clone(cases, i);
}
}
if (clone != null) {
clone[i] = switchCase;
}
}
// Default body also runs on initial stack
final Result defaultBody = rewriteExpression(node.getDefaultBody(), stack);
action = action.or(defaultBody.Action);
if (action != RewriteAction.None) {
if (clone != null) {
// Okay to wrap because we aren't modifying the array
cases = new ReadOnlyList<>(clone);
}
return new Result(
action,
new SwitchExpression(
node.getType(),
switchValue.Node,
defaultBody.Node,
node.getComparison(),
cases,
node.getOptions()
)
);
}
return new Result(action, expr);
}
private Result rewriteLoopExpression(final Expression expr, final Stack stack) {
final LoopExpression node = (LoopExpression) expr;
// The loop statement requires empty stack for itself, so it
// can guarantee it to the child nodes.
final Result body = rewriteExpression(node.getBody(), Stack.Empty);
RewriteAction action = body.Action;
// However, the loop itself requires that it executes on an empty stack
// so we need to rewrite if the stack is not empty.
if (stack != Stack.Empty) {
action = RewriteAction.SpillStack;
}
if (action != RewriteAction.None) {
return new Result(
action,
new LoopExpression(
body.getNode(),
node.getBreakTarget(),
node.getContinueTarget()
)
);
}
return new Result(action, expr);
}
private Result rewriteLabelExpression(final Expression expr, final Stack stack) {
final LabelExpression node = (LabelExpression)expr;
final Result expression = rewriteExpression(node.getDefaultValue(), stack);
if (expression.Action != RewriteAction.None) {
return new Result(
expression.Action,
Expression.label(node.getTarget(), expression.Node)
);
}
return new Result(expression.Action, expr);
}
private Result rewriteGotoExpression(final Expression expr, final Stack stack) {
final GotoExpression node = (GotoExpression) expr;
// Goto requires empty stack to execute so the expression is
// going to execute on an empty stack.
final Result value = rewriteExpressionFreeTemps(node.getValue(), Stack.Empty);
// However, the statement itself needs an empty stack for itself
// so if stack is not empty, rewrite to empty the stack.
RewriteAction action = value.Action;
if (stack != Stack.Empty) {
action = RewriteAction.SpillStack;
}
if (action != RewriteAction.None) {
return new Result(
action,
Expression.makeGoto(
node.getKind(),
node.getTarget(),
value.Node,
node.getType()
)
);
}
return new Result(action, expr);
}
private Result rewriteExtensionExpression(final Expression expr, final Stack stack) {
final Result result = rewriteExpression(expr.reduceExtensions(), stack);
// It's at least Copy because we reduced the node
return new Result(result.Action.or(RewriteAction.Copy), result.Node);
}
private Result rewriteBlockExpression(final Expression expr, final Stack stack) {
final BlockExpression node = (BlockExpression) expr;
final int count = node.getExpressionCount();
RewriteAction action = RewriteAction.None;
Expression[] clone = null;
for (int i = 0; i < count; i++) {
final Expression expression = node.getExpression(i);
// All statements within the block execute at the same stack state.
final Result rewritten = rewriteExpression(expression, stack);
action = action.or(rewritten.Action);
if (clone == null && rewritten.Action != RewriteAction.None) {
clone = clone(node.getExpressions(), i);
}
if (clone != null) {
clone[i] = rewritten.Node;
}
}
if (action != RewriteAction.None) {
// okay to wrap since we know no one can mutate the clone array
return new Result(
action,
node.rewrite(null, clone));
}
return new Result(action, expr);
}
private Result rewriteAssignBinaryExpression(final Expression expr, final Stack stack) {
final BinaryExpression node = (BinaryExpression)expr;
switch (node.getLeft().getNodeType()) {
case MemberAccess:
return rewriteMemberAssignment(node, stack);
case Parameter:
return rewriteVariableAssignment(node, stack);
case Extension:
return rewriteExtensionAssignment(node, stack);
default:
throw Error.invalidLValue(node.getLeft().getNodeType());
}
}
private Result rewriteExtensionAssignment(final BinaryExpression node, final Stack stack) {
final Result result = rewriteAssignBinaryExpression(
Expression.assign(
node.getLeft().reduceExtensions(),
node.getRight()),
stack);
// It's at least Copy because we reduced the node.
return new Result(result.Action.or(RewriteAction.Copy), result.Node);
}
private Result rewriteVariableAssignment(final BinaryExpression node, final Stack stack) {
// Expression is evaluated on a stack in current state
final Result right = rewriteExpression(node.getRight(), stack);
if (right.Action != RewriteAction.None) {
return new Result(right.Action, Expression.assign(node.getLeft(), right.Node));
}
return new Result(right.Action, node);
}
private Result rewriteMemberAssignment(final BinaryExpression node, final Stack stack) {
final MemberExpression lValue = (MemberExpression)node.getLeft();
final ChildRewriter cr = new ChildRewriter(stack, 2);
// If there's an instance, it executes on the stack in current state, and the rest
// is executed on non-empty stack. Otherwise the stack is left unchanged.
cr.add(lValue.getTarget());
cr.add(node.getRight());
if (cr.didRewrite()) {
return cr.Finish(
new AssignBinaryExpression(
MemberExpression.make(cr.get(0), lValue.getMember()),
cr.get(1)
)
);
}
return new Result(RewriteAction.None, node);
}
private Result rewriteNewArrayExpression(final Expression expr, final Stack stack) {
final NewArrayExpression node = (NewArrayExpression)expr;
final Stack newStack;
if (node.getNodeType() == ExpressionType.NewArrayInit) {
// In a case of array construction with element initialization
// the element expressions are never emitted on an empty stack because
// the array reference and the index are on the stack.
newStack = Stack.NonEmpty;
}
else {
// In a case of NewArrayBounds we make no modifications to the stack
// before emitting bounds expressions.
newStack = stack;
}
final ChildRewriter cr = new ChildRewriter(newStack, node.getExpressions().size());
cr.add(node.getExpressions());
if (cr.didRewrite()) {
final Type element = node.getType().getElementType();
if (node.getNodeType() == ExpressionType.NewArrayInit) {
return cr.Finish(Expression.newArrayInit(element, cr.get(0, -1)));
}
else {
return cr.Finish(Expression.newArrayBounds(element, cr.get(0)));
}
}
return cr.Finish(expr);
}
private Result rewriteNewExpression(final Expression expr, final Stack stack) {
final NewExpression node = (NewExpression) expr;
// The first expression starts on a stack as provided by parent,
// rest are definitely non-emtpy (which ChildRewriter guarantees)
final ChildRewriter cr = new ChildRewriter(stack, node.getArgumentCount());
cr.addArguments(node);
return cr.Finish(
cr.didRewrite()
? new NewExpression(node.getConstructor(), cr.get(0, -1))
: expr);
}
private Result rewriteMemberExpression(final Expression expr, final Stack stack) {
final MemberExpression node = (MemberExpression)expr;
// Expression is emitted on top of the stack in current state
final Result expression = rewriteExpression(node.getTarget(), stack);
if (expression.Action != RewriteAction.None) {
return new Result(
expression.Action,
MemberExpression.make(expression.Node, node.getMember())
);
}
return new Result(expression.Action, expr);
}
private Result rewriteLambdaExpression(final Expression expr, final Stack stack) {
final LambdaExpression> node = (LambdaExpression>) expr;
// Call back into the rewriter.
final LambdaExpression> analyzedLambda = analyzeLambda(node);
// If the lambda gets rewritten, we don't need to spill the stack, but
// we do need to rebuild the tree above us so it includes the new node.
final RewriteAction action = (analyzedLambda == node) ? RewriteAction.None : RewriteAction.Copy;
return new Result(action, analyzedLambda);
}
private Result rewriteInvocationExpression(final Expression expr, final Stack stack) {
InvocationExpression node = (InvocationExpression) expr;
final ChildRewriter cr;
// See if the lambda will be inlined...
LambdaExpression lambda = node.getLambdaOperand();
if (lambda != null) {
// Arguments execute on current stack
cr = new ChildRewriter(stack, node.getArgumentCount());
cr.add(node.getArguments());
// Lambda body also executes on current stack
final StackSpiller spiller = new StackSpiller(stack);
lambda = lambda.accept(spiller);
if (cr.didRewrite() || spiller._lambdaRewrite != RewriteAction.None) {
node = new InvocationExpression(lambda, cr.get(0, -1), node.getType());
}
final Result result = cr.Finish(node);
return new Result(result.Action.or(spiller._lambdaRewrite), result.Node);
}
cr = new ChildRewriter(stack, node.getArgumentCount() + 1);
// First argument starts on stack as provided.
cr.add(node.getExpression());
// Rest of arguments have non-empty stack (delegate instance on the stack).
cr.add(node.getArguments());
return cr.Finish(
cr.didRewrite()
? new InvocationExpression(
cr.get(0),
cr.get(1, -1),
node.getType())
: expr);
}
private Result rewriteTypeBinaryExpression(final Expression expr, final Stack stack) {
final TypeBinaryExpression node = (TypeBinaryExpression) expr;
// The expression is emitted on top of current stack.
final Result expression = rewriteExpression(node.getOperand(), stack);
if (expression.Action != RewriteAction.None) {
if (node.getNodeType() == ExpressionType.InstanceOf) {
return new Result(
expression.Action,
Expression.instanceOf(
expression.Node,
node.getTypeOperand()
)
);
}
else {
return new Result(
expression.Action,
Expression.typeEqual(
expression.Node,
node.getTypeOperand()
)
);
}
}
return new Result(expression.Action, expr);
}
private Result rewriteConditionalExpression(final Expression expr, final Stack stack) {
final ConditionalExpression node = (ConditionalExpression)expr;
// Test executes at the stack as left by parent.
final Result test = rewriteExpression(node.getTest(), stack);
// The test is popped by conditional jump so branches execute
// at the stack as left by parent too.
final Result ifTrue = rewriteExpression(node.getIfTrue(), stack);
final Result ifFalse = rewriteExpression(node.getIfFalse(), stack);
final RewriteAction action = test.Action.or(ifTrue.Action).or(ifFalse.Action);
if (action != RewriteAction.None) {
return new Result(
action,
Expression.condition(
test.Node,
ifTrue.Node,
ifFalse.Node,
node.getType()
)
);
}
return new Result(action, expr);
}
private Result rewriteMethodCallExpression(final Expression expr, final Stack stack) {
final MethodCallExpression node = (MethodCallExpression)expr;
final ChildRewriter cr = new ChildRewriter(stack, node.getArgumentCount() + 1);
// For instance methods, the instance executes on the stack as is,
// but stays on the stack, making it non-empty.
cr.add(node.getTarget());
cr.addArguments(node);
return cr.Finish(cr.didRewrite() ? node.rewrite(cr.get(0), cr.get(1, -1)) : expr);
}
private Result rewriteUnaryExpression(final Expression expr, final Stack stack) {
final UnaryExpression node = (UnaryExpression)expr;
assert node.getNodeType() != ExpressionType.Quote : "unexpected Quote";
assert node.getNodeType() != ExpressionType.Throw : "unexpected Throw";
// Operand is emitted on top of the stack as is
final Result expression = rewriteExpression(node.getOperand(), stack);
if (expression.Action != RewriteAction.None) {
return new Result(
expression.Action,
new UnaryExpression(
node.getNodeType(),
expression.Node,
node.getType(),
node.getMethod())
);
}
return new Result(expression.Action, expr);
}
private Result rewriteLogicalBinaryExpression(final Expression expr, final Stack stack) {
final BinaryExpression node = (BinaryExpression)expr;
// Left expression runs on a stack as left by parent
final Result left = rewriteExpression(node.getLeft(), stack);
// ... and so does the right one
final Result right = rewriteExpression(node.getRight(), stack);
//conversion is a lambda. stack state will be ignored.
final Result conversion = rewriteExpression(node.getConversion(), stack);
final RewriteAction action = left.Action.or(right.Action).or(conversion.Action);
if (action != RewriteAction.None) {
return new Result(
action,
BinaryExpression.create(
node.getNodeType(),
left.Node,
right.Node,
node.getType(),
node.getMethod(),
(LambdaExpression) conversion.Node
)
);
}
return new Result(action, expr);
}
private Result rewriteBinaryExpression(final Expression expr, final Stack stack) {
final BinaryExpression node = (BinaryExpression) expr;
final ChildRewriter cr = new ChildRewriter(stack, 3);
// Left expression executes on the stack as left by parent
cr.add(node.getLeft());
// Right expression always has non-empty stack (left is on it)
cr.add(node.getRight());
// conversion is a lambda, stack state will be ignored
cr.add(node.getConversion());
if (cr.didRewrite()) {
return cr.Finish(
BinaryExpression.create(
node.getNodeType(),
cr.get(0),
cr.get(1),
node.getType(),
node.getMethod(),
(LambdaExpression) cr.get(2)));
}
return cr.Finish(expr);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DEPENDENT TYPES //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private static enum Stack {
Empty,
NonEmpty
}
private static enum RewriteAction {
None(0),
Copy(1),
SpillStack(3);
private final int _flags;
RewriteAction(final int flags) {
_flags = flags;
}
public RewriteAction or(final RewriteAction action) {
final int flags = action._flags | _flags;
switch (flags) {
case 0:
return None;
case 1:
return Copy;
case 3:
return SpillStack;
}
throw new IllegalArgumentException();
}
}
private static class Result {
private final RewriteAction Action;
private final Expression Node;
Result(final RewriteAction action, final Expression node) {
Action = action;
Node = node;
}
public RewriteAction getAction() {
return Action;
}
public Expression getNode() {
return Node;
}
}
private static class TempMaker {
private int _temp;
private ArrayList _freeTemps;
private java.util.Stack _usedTemps;
private final ArrayList _temps = new ArrayList<>();
List getTemps() {
return _temps;
}
ParameterExpressionList getTempsList() {
final ParameterExpression[] temps = new ParameterExpression[_temps.size()];
_temps.toArray(temps);
return new ParameterExpressionList(temps);
}
ParameterExpression temp(final Type type) {
ParameterExpression temp;
if (_freeTemps != null) {
// Recycle from the free-list if possible.
for (int i = _freeTemps.size() - 1; i >= 0; i--) {
temp = _freeTemps.get(i);
if (temp.getType() == type) {
_freeTemps.remove(i);
return useTemp(temp);
}
}
}
// Not on the free-list, create a brand new one.
temp = Expression.variable(type, "$temp$" + _temp++);
_temps.add(temp);
return useTemp(temp);
}
private ParameterExpression useTemp(final ParameterExpression temp) {
assert _freeTemps == null || !_freeTemps.contains(temp);
assert _usedTemps == null || !_usedTemps.contains(temp);
if (_usedTemps == null) {
_usedTemps = new java.util.Stack<>();
}
_usedTemps.push(temp);
return temp;
}
private void freeTemp(final ParameterExpression temp) {
assert _freeTemps == null || !_freeTemps.contains(temp);
if (_freeTemps == null) {
_freeTemps = new ArrayList<>();
}
_freeTemps.add(temp);
}
int mark() {
return _usedTemps != null ? _usedTemps.size() : 0;
}
void free(final int mark) {
// (_usedTemps != null) ==> (mark <= _usedTemps.Count)
assert _usedTemps == null || mark <= _usedTemps.size();
// (_usedTemps == null) ==> (mark == 0)
assert mark == 0 || _usedTemps != null;
if (_usedTemps != null) {
while (mark < _usedTemps.size()) {
freeTemp(_usedTemps.pop());
}
}
}
void verifyTemps() {
assert _usedTemps == null || _usedTemps.isEmpty();
}
}
/**
* A special subtype of BlockExpression that indicates to the compiler
* that this block is a spilled expression and should not allow jumps in.
*/
final static class SpilledExpressionBlock extends BlockN {
SpilledExpressionBlock(final ExpressionList extends Expression> expressions) {
super(expressions);
}
@Override
BlockExpression rewrite(final ParameterExpressionList variables, final Expression[] args) {
throw ContractUtils.unreachable();
}
}
/**
* Rewrites child expressions, spilling them into temps if needed. The stack
* starts in the initial state, and after the first sub-expression is added,
* it is change to non-empty. This behavior can be overridden by setting the
* stack manually between adds.
*
* When all children have been added, the caller should rewrite the node if
* didRewrite() is true. Then, it should call finish() with either the original
* expression or the rewritten expression. finish() will call Expression.comma()
* if necessary and return a new Result.
*/
private final class ChildRewriter {
private final Expression[] _expressions;
private int _expressionsCount;
private List _comma;
private RewriteAction _action = RewriteAction.None;
private Stack _stack;
private boolean _done;
ChildRewriter(final Stack stack, final int count) {
_stack = stack;
_expressions = new Expression[count];
}
void add(final Expression node) {
assert !_done;
if (node == null) {
_expressions[_expressionsCount++] = null;
return;
}
final Result exp = rewriteExpression(node, _stack);
_action = _action.or(exp.Action);
_stack = Stack.NonEmpty;
// track items in case we need to copy or spill stack
_expressions[_expressionsCount++] = exp.Node;
}
void add(final ExpressionList expressions) {
for (int i = 0, count = expressions.size(); i < count; i++) {
add(expressions.get(i));
}
}
void addArguments(final IArgumentProvider expressions) {
for (int i = 0, count = expressions.getArgumentCount(); i < count; i++) {
add(expressions.getArgument(i));
}
}
private void ensureDone() {
// done adding arguments, build the comma if necessary
if (!_done) {
_done = true;
if (_action == RewriteAction.SpillStack) {
final Expression[] clone = _expressions;
final int count = clone.length;
final List comma = new ArrayList<>(count + 1);
for (int i = 0; i < count; i++) {
if (clone[i] != null) {
final StrongBox temp = new StrongBox<>();
clone[i] = toTemp(clone[i], temp);
comma.add(temp.value);
}
}
_comma = comma;
}
}
}
boolean didRewrite() {
return _action != RewriteAction.None;
}
RewriteAction getAction() {
return _action;
}
Result Finish(Expression expr) {
ensureDone();
if (_action == RewriteAction.SpillStack) {
assert(_comma.size() == _expressions.length + 1);
_comma.add(expr);
final Expression[] expressions = _comma.toArray(new Expression[_comma.size()]);
expr = makeBlock(new ExpressionList<>(expressions));
}
return new Result(_action, expr);
}
Expression get(int index) {
ensureDone();
if (index < 0) {
index += _expressions.length;
}
return _expressions[index];
}
ExpressionList extends Expression> get(final int first, final int last) {
ensureDone();
int end = last;
if (end < 0) {
end += _expressions.length;
}
final int count = end - first + 1;
VerifyArgument.validElementRange(_expressions.length, first, first + count);
if (count == _expressions.length) {
assert (first == 0);
// if the entire array is requested just return it so we don't make a new array
return new ExpressionList<>(_expressions);
}
final Expression[] clone = new Expression[count];
System.arraycopy(_expressions, first, clone, 0, count);
return new ExpressionList<>(clone);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy