/*
* LambdaCompiler.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.compilerservices.Closure;
import com.strobel.compilerservices.DebugInfoGenerator;
import com.strobel.core.KeyedQueue;
import com.strobel.core.Pair;
import com.strobel.core.ReadOnlyList;
import com.strobel.core.StringUtilities;
import com.strobel.reflection.*;
import com.strobel.reflection.emit.*;
import com.strobel.util.ContractUtils;
import com.strobel.util.TypeUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Mike Strobel
*/
@SuppressWarnings({ "unchecked", "PackageVisibleField", "UnusedParameters", "UnusedDeclaration", "ConstantConditions" })
final class LambdaCompiler {
final static AtomicInteger nextId = new AtomicInteger();
final static Type<Closure> closureType = Type.of(Closure.class);
final LambdaExpression<?> lambda;
final TypeBuilder typeBuilder;
final MethodBuilder methodBuilder;
final CodeGenerator generator;
private final AnalyzedTree _tree;
private final KeyedQueue<Type<?>, LocalBuilder> _freeLocals;
private final BoundConstants _boundConstants;
private final Map<LabelTarget, LabelInfo> _labelInfo = new HashMap<>();
private ConstructorBuilder _constructorBuilder;
private boolean _hasClosureArgument;
private FieldBuilder _closureField;
private CompilerScope _scope;
private LabelScopeInfo _labelBlock = new LabelScopeInfo(null, LabelScopeKind.Lambda);
private FinallyInfo _finallyInfo = new FinallyInfo(null, null);
LambdaCompiler(final AnalyzedTree tree, final LambdaExpression<?> lambda) {
this.lambda = lambda;
typeBuilder = new TypeBuilder(
getUniqueLambdaName(lambda.getName(), lambda.getCreationContext()),
Modifier.PUBLIC | Modifier.FINAL,
Types.Object,
Type.list(lambda.getType())
);
final MethodInfo interfaceMethod = lambda.getType().getMethods().get(0);
methodBuilder = typeBuilder.defineMethod(
interfaceMethod.getName(),
Modifier.PUBLIC | Modifier.FINAL,
interfaceMethod.getReturnType(),
interfaceMethod.getParameters().getParameterTypes(),
interfaceMethod.getThrownTypes()
);
final ParameterExpressionList lambdaParameters = lambda.getParameters();
for (int i = 0, n = lambdaParameters.size(); i < n; i++) {
methodBuilder.defineParameter(i, lambdaParameters.get(i).getName());
}
typeBuilder.defineMethodOverride(methodBuilder, interfaceMethod);
generator = methodBuilder.getCodeGenerator();
_tree = tree;
_scope = tree.scopes.get(lambda);
_boundConstants = tree.constants.get(lambda);
_freeLocals = new KeyedQueue<>();
if (_scope.needsClosure || _boundConstants.count() > 0) {
ensureClosure();
}
initializeMethod();
}
LambdaCompiler(
final AnalyzedTree tree,
final LambdaExpression<?> lambda,
final MethodBuilder method,
final ConstructorBuilder constructor) {
this.lambda = lambda;
_hasClosureArgument = false;
final TypeList parameterTypes = getParameterTypes(lambda);
method.setReturnType(lambda.getReturnType());
method.setParameters(parameterTypes);
final ParameterExpressionList lambdaParameters = lambda.getParameters();
for (int i = 0, n = lambdaParameters.size(); i < n; i++) {
method.defineParameter(i, lambdaParameters.get(i).getName());
}
this.typeBuilder = method.getDeclaringType();
this.methodBuilder = method;
this._constructorBuilder = constructor;
this._closureField = null;
this.generator = methodBuilder.getCodeGenerator();
_freeLocals = new KeyedQueue<>();
_tree = tree;
_scope = tree.scopes.get(lambda);
_boundConstants = tree.constants.get(lambda);
initializeMethod();
}
private LambdaCompiler(final LambdaCompiler parent, final LambdaExpression lambda) {
_tree = parent._tree;
_freeLocals = parent._freeLocals;
this.lambda = lambda;
this.methodBuilder = parent.methodBuilder;
this._closureField = parent._closureField;
this._constructorBuilder = parent._constructorBuilder;
this.generator = parent.generator;
_hasClosureArgument = parent._hasClosureArgument;
this.typeBuilder = parent.typeBuilder;
_scope = _tree.scopes.get(lambda);
_boundConstants = parent._boundConstants;
}
private TypeList getParameterTypes(final LambdaExpression<?> lambda) {
final ParameterExpressionList parameters = lambda.getParameters();
if (parameters.isEmpty()) {
return TypeList.empty();
}
final Type<?>[] types = new Type<?>[parameters.size()];
for (int i = 0, n = parameters.size(); i < n; i++) {
final ParameterExpression parameter = parameters.get(i);
types[i] = parameter.getType();
}
return Type.list(types);
}
ParameterExpressionList getParameters() {
return lambda.getParameters();
}
boolean canEmitBoundConstants() {
return _hasClosureArgument;
}
boolean emitDebugSymbols() {
return _tree.getDebugInfoGenerator() != null;
}
void emitClosureArgument() {
assert _hasClosureArgument
: "must have a Closure argument";
generator.emitThis();
generator.getField(_closureField);
}
void emitLambdaArgument(final int index) {
generator.emitLoadArgument(getLambdaArgument(index));
}
private FieldBuilder createStaticField(final String name, final Type type) {
return typeBuilder.defineField(
"<ExpressionCompilerImplementationDetails>{" + nextId.getAndIncrement() + "}" + name,
type,
Modifier.STATIC | Modifier.PRIVATE
);
}
void initializeMethod() {
// See if we can find a return label, so we can emit better IL
addReturnLabel(lambda);
_boundConstants.emitCacheConstants(this);
}
// See if this lambda has a return label
// If so, we'll create it now and mark it as allowing the "ret" opcode
// This allows us to generate better IL
@SuppressWarnings("ConstantConditions")
private void addReturnLabel(final LambdaExpression lambda) {
Expression expression = lambda.getBody();
while (true) {
switch (expression.getNodeType()) {
default:
// Didn't find return label.
return;
case Label:
// Found the label. We can directly return from this place only if
// the label type is reference assignable to the lambda return type.
final LabelTarget label = ((LabelExpression) expression).getTarget();
_labelInfo.put(
label,
new LabelInfo(
generator,
label,
TypeUtils.hasIdentityPrimitiveOrBoxingConversion(
lambda.getReturnType(),
label.getType()
)
)
);
return;
case Block:
// Look in the last significant expression of a block.
final BlockExpression body = (BlockExpression) expression;
// Omit empty and debug info at the end of the block since they
// are not going to emit any bytecode.
for (int i = body.getExpressionCount() - 1; i >= 0; i--) {
expression = body.getExpression(i);
if (significant(expression)) {
break;
}
}
//noinspection UnnecessaryContinue
continue;
}
}
}
private static boolean notEmpty(final Expression node) {
return !(node instanceof DefaultValueExpression) ||
node.getType() != PrimitiveTypes.Void;
}
private static boolean significant(final Expression node) {
if (node instanceof BlockExpression) {
final BlockExpression block = (BlockExpression) node;
for (int i = 0; i < block.getExpressionCount(); i++) {
if (significant(block.getExpression(i))) {
return true;
}
}
return false;
}
return notEmpty(node)/* && !(node instanceof DebugInfoExpression)*/;
}
@SuppressWarnings("unchecked")
static <T> Delegate<T> compile(
final LambdaExpression<T> lambda,
final DebugInfoGenerator debugInfoGenerator) {
// 1. Bind lambda
final Pair<AnalyzedTree, LambdaExpression<T>> result = analyzeLambda(lambda);
final AnalyzedTree tree = result.getFirst();
final LambdaExpression<T> analyzedLambda = result.getSecond();
tree.setDebugInfoGenerator(debugInfoGenerator);
// 2. Create lambda compiler
final LambdaCompiler c = new LambdaCompiler(tree, analyzedLambda);
// 3. emit
c.emitLambdaBody();
final Type<T> generatedType = (Type<T>) c.typeBuilder.createType();
final Class<T> generatedClass = generatedType.getErasedClass();
return c.createDelegate(generatedClass);
}
@SuppressWarnings("unchecked")
private <T> Delegate<T> createDelegate(final Class<T> generatedClass) {
try {
final T instance;
if (_hasClosureArgument) {
final Constructor<?> constructor = generatedClass.getConstructor(Closure.class);
final Closure closure = new Closure(_boundConstants.toArray(), null);
instance = (T) constructor.newInstance(closure);
}
else {
instance = generatedClass.newInstance();
}
final MemberList<? extends MemberInfo> method = Type.of(generatedClass).findMembers(
MemberType.methodsOnly(),
BindingFlags.PublicInstanceDeclared,
Type.FilterMethodOverride,
lambda.getType().getMethods().get(0)
);
return new Delegate<>(
instance,
(MethodInfo) method.get(0)
);
}
catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw Error.couldNotCreateDelegate(e);
}
}
static <T> void compile(
final LambdaExpression<T> lambda,
final MethodBuilder methodBuilder,
final DebugInfoGenerator debugInfoGenerator) {
// 1. Bind lambda
final Pair<AnalyzedTree, LambdaExpression<T>> result = analyzeLambda(lambda);
final AnalyzedTree tree = result.getFirst();
final LambdaExpression<T> analyzedLambda = result.getSecond();
tree.setDebugInfoGenerator(debugInfoGenerator);
// 2. Create lambda compiler
final LambdaCompiler c = new LambdaCompiler(tree, analyzedLambda, methodBuilder, null);
// 3. emit
c.emitLambdaBody();
}
private static <T> Pair<AnalyzedTree, LambdaExpression<T>> analyzeLambda(final LambdaExpression<T> lambda) {
// Spill the stack for any exception handling blocks or other
// constructs which require entering with an empty stack.
final LambdaExpression<T> analyzedLambda = StackSpiller.analyzeLambda(lambda);
// Bind any variable references in this lambda.
return Pair.create(
VariableBinder.bind(analyzedLambda),
analyzedLambda
);
}
LocalBuilder getNamedLocal(final Type type, final ParameterExpression variable) {
assert type != null && variable != null
: "type != null && variable != null";
final LocalBuilder lb = generator.declareLocal(variable.getName(), type);
if (emitDebugSymbols() && variable.getName() != null) {
_tree.getDebugInfoGenerator().setLocalName(lb, variable.getName());
}
return lb;
}
int getLambdaArgument(final int index) {
return index;// + (methodBuilder.isStatic() ? 0 : 1);
}
LocalBuilder getLocal(final Type<?> type) {
assert type != null
: "type != null";
final LocalBuilder local = _freeLocals.poll(type);
if (local != null) {
assert type.equals(local.getLocalType())
: "type.equals(local.getLocalType())";
return local;
}
return generator.declareLocal(type);
}
void freeLocal(final LocalBuilder local) {
if (local != null) {
_freeLocals.offer(local.getLocalType(), local);
}
}
private static final class CompilationFlags {
private static final int EmitExpressionStart = 0x0001;
private static final int EmitNoExpressionStart = 0x0002;
private static final int EmitAsDefaultType = 0x0010;
private static final int EmitAsVoidType = 0x0020;
private static final int EmitAsTail = 0x0100; // at the tail position of a lambda; tail call can be safely emitted
private static final int EmitAsMiddle = 0x0200; // in the middle of a lambda; tail call can be emitted if it is in a return
private static final int EmitAsNoTail = 0x0400; // neither at the tail or in a return; or tail call is not turned on; no tail call is emitted
private static final int EmitExpressionStartMask = 0x000f;
private static final int EmitAsTypeMask = 0x00f0;
private static final int EmitAsTailCallMask = 0x0f00;
private static final int EmitAsSuperCall = 0x1000;
}
private static int updateEmitAsTailCallFlag(final int flags, final int newValue) {
assert newValue == CompilationFlags.EmitAsTail ||
newValue == CompilationFlags.EmitAsMiddle ||
newValue == CompilationFlags.EmitAsNoTail;
final int oldValue = flags & CompilationFlags.EmitAsTailCallMask;
return flags ^ oldValue | newValue;
}
private static int updateEmitExpressionStartFlag(final int flags, final int newValue) {
assert newValue == CompilationFlags.EmitExpressionStart ||
newValue == CompilationFlags.EmitNoExpressionStart;
final int oldValue = flags & CompilationFlags.EmitExpressionStartMask;
return flags ^ oldValue | newValue;
}
private static int updateEmitAsTypeFlag(final int flags, final int newValue) {
assert newValue == CompilationFlags.EmitAsDefaultType ||
newValue == CompilationFlags.EmitAsVoidType;
final int oldValue = flags & CompilationFlags.EmitAsTypeMask;
return flags ^ oldValue | newValue;
}
void emitExpression(final Expression node) {
emitExpression(
node,
CompilationFlags.EmitAsNoTail | CompilationFlags.EmitExpressionStart
);
}
private int emitExpressionStart(final Expression node) {
if (tryPushLabelBlock(node)) {
return CompilationFlags.EmitExpressionStart;
}
return CompilationFlags.EmitNoExpressionStart;
}
private void emitExpressionEnd(final int flags) {
if ((flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart) {
popLabelBlock(_labelBlock.kind);
}
}
private void emitExpression(final Expression node, final int flags) {
assert node != null;
final boolean emitStart = (flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart;
final int startEmitted = emitStart
? emitExpressionStart(node)
: CompilationFlags.EmitNoExpressionStart;
// only pass tail call flags to emit the expression
final int compilationFlags = flags & CompilationFlags.EmitAsTailCallMask;
switch (node.getNodeType()) {
case Add:
emitBinaryExpression(node, compilationFlags);
break;
case And:
emitBinaryExpression(node, compilationFlags);
break;
case AndAlso:
emitAndAlsoBinaryExpression(node, compilationFlags);
break;
case ArrayLength:
emitUnaryExpression(node, compilationFlags);
break;
case ArrayIndex:
emitBinaryExpression(node, compilationFlags);
break;
case Call:
emitMethodCallExpression(node, compilationFlags);
break;
case Coalesce:
emitCoalesceBinaryExpression(node);
break;
case Conditional:
emitConditionalExpression(node, compilationFlags);
break;
case Constant:
emitConstantExpression(node);
break;
case Convert:
emitConvertUnaryExpression(node, compilationFlags);
break;
case ConvertChecked:
emitConvertUnaryExpression(node, compilationFlags);
break;
case Divide:
emitBinaryExpression(node, compilationFlags);
break;
case Equal:
emitBinaryExpression(node, compilationFlags);
break;
case ExclusiveOr:
emitBinaryExpression(node, compilationFlags);
break;
case GreaterThan:
emitBinaryExpression(node, compilationFlags);
break;
case GreaterThanOrEqual:
emitBinaryExpression(node, compilationFlags);
break;
case Invoke:
emitInvocationExpression(node, compilationFlags);
break;
case Lambda:
emitLambdaExpression(node);
break;
case LeftShift:
emitBinaryExpression(node, compilationFlags);
break;
case LessThan:
emitBinaryExpression(node, compilationFlags);
break;
case LessThanOrEqual:
emitBinaryExpression(node, compilationFlags);
break;
case MemberAccess:
emitMemberExpression(node);
break;
case Modulo:
emitBinaryExpression(node, compilationFlags);
break;
case Multiply:
emitBinaryExpression(node, compilationFlags);
break;
case Negate:
emitUnaryExpression(node, compilationFlags);
break;
case UnaryPlus:
emitUnaryExpression(node, compilationFlags);
break;
case New:
emitNewExpression(node);
break;
case NewArrayInit:
emitNewArrayExpression(node);
break;
case NewArrayBounds:
emitNewArrayExpression(node);
break;
case Not:
emitUnaryExpression(node, compilationFlags);
break;
case NotEqual:
emitBinaryExpression(node, compilationFlags);
break;
case Or:
emitBinaryExpression(node, compilationFlags);
break;
case OrElse:
emitOrElseBinaryExpression(node, compilationFlags);
break;
case Parameter:
emitParameterExpression(node);
break;
/*
case Quote:
emitQuoteUnaryExpression(node);
break;
*/
case RightShift:
emitBinaryExpression(node, compilationFlags);
break;
case UnsignedRightShift:
emitBinaryExpression(node, compilationFlags);
break;
case Subtract:
emitBinaryExpression(node, compilationFlags);
break;
case InstanceOf:
emitTypeBinaryExpression(node);
break;
case Assign:
emitAssignBinaryExpression(node);
break;
case Block:
emitBlockExpression(node, compilationFlags);
break;
case Decrement:
emitUnaryExpression(node, compilationFlags);
break;
case DefaultValue:
emitDefaultValueExpression(node);
break;
case Extension:
emitExtensionExpression(node);
break;
case Goto:
emitGotoExpression(node, compilationFlags);
break;
case Increment:
emitUnaryExpression(node, compilationFlags);
break;
case Label:
emitLabelExpression(node, compilationFlags);
break;
case RuntimeVariables:
emitRuntimeVariablesExpression(node);
break;
case Loop:
emitLoopExpression(node);
break;
case Switch:
emitSwitchExpression(node, compilationFlags);
break;
case Throw:
emitThrowUnaryExpression(node);
break;
case Try:
emitTryExpression(node);
break;
case Unbox:
emitUnboxUnaryExpression(node);
break;
case TypeEqual:
emitTypeBinaryExpression(node);
break;
case OnesComplement:
emitUnaryExpression(node, compilationFlags);
break;
case IsTrue:
emitUnaryExpression(node, compilationFlags);
break;
case IsFalse:
emitUnaryExpression(node, compilationFlags);
break;
case IsNull:
emitUnaryExpression(node, compilationFlags);
break;
case IsNotNull:
emitUnaryExpression(node, compilationFlags);
break;
case ReferenceEqual:
emitBinaryExpression(node, compilationFlags);
break;
case ReferenceNotEqual:
emitBinaryExpression(node, compilationFlags);
break;
default:
throw ContractUtils.unreachable();
}
if (emitStart) {
emitExpressionEnd(startEmitted);
}
}
private void emitExtensionExpression(final Expression node) {
throw Error.extensionNotReduced();
}
// <editor-fold defaultstate="collapsed" desc="Emit as Void/Type">
private void emitExpressionAsVoid(final Expression node) {
emitExpressionAsVoid(node, CompilationFlags.EmitAsNoTail);
}
private void emitExpressionAsVoid(final Expression node, final int flags) {
assert node != null;
final int startEmitted = emitExpressionStart(node);
switch (node.getNodeType()) {
case Assign:
emitAssign((BinaryExpression) node, CompilationFlags.EmitAsVoidType);
break;
case Block:
emit((BlockExpression) node, updateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
break;
case Throw:
emitThrow((UnaryExpression) node, CompilationFlags.EmitAsVoidType);
break;
case Goto:
emitGotoExpression(node, updateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
break;
case Constant:
case DefaultValue:
case Parameter:
// no-op
break;
default:
if (node.getType() == PrimitiveTypes.Void) {
emitExpression(node, updateEmitExpressionStartFlag(flags, CompilationFlags.EmitNoExpressionStart));
}
else {
emitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
generator.pop(node.getType());
}
break;
}
emitExpressionEnd(startEmitted);
}
private void emitExpressionAsType(final Expression node, final Type type, final int flags) {
if (type == PrimitiveTypes.Void) {
emitExpressionAsVoid(node, flags);
}
else {
// if the node is emitted as a different type, CastClass IL is emitted at the end,
// should not emit with tail calls.
if (!TypeUtils.areEquivalent(node.getType(), type)) {
emitExpression(node);
assert TypeUtils.hasIdentityPrimitiveOrBoxingConversion(type, node.getType()) |
TypeUtils.areReferenceAssignable(type, node.getType());
generator.emitConversion(node.getType(), type);
}
else {
// emit the with the flags and emit emit expression start
emitExpression(
node,
updateEmitExpressionStartFlag(
flags,
CompilationFlags.EmitExpressionStart
)
);
}
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Assignments">
private void emitAssignBinaryExpression(final Expression expr) {
emitAssign((BinaryExpression) expr, CompilationFlags.EmitAsDefaultType);
}
private void emitAssign(final BinaryExpression node, final int emitAs) {
switch (node.getLeft().getNodeType()) {
case ArrayIndex:
emitIndexAssignment(node, emitAs);
return;
case MemberAccess:
emitMemberAssignment(node, emitAs);
return;
case Parameter:
emitVariableAssignment(node, emitAs);
return;
default:
throw Error.invalidLValue(node.getLeft().getNodeType());
}
}
private void emitMemberAssignment(final BinaryExpression node, final int flags) {
final MemberExpression lValue = (MemberExpression) node.getLeft();
final Expression rValue = node.getRight();
final MemberInfo member = lValue.getMember();
// emit "this", if any
if (lValue.getTarget() != null) {
emitExpression(lValue.getTarget());
}
// emit value
emitExpression(rValue);
if (!TypeUtils.hasReferenceConversion(rValue.getType(), lValue.getType())) {
generator.emitConversion(rValue.getType(), lValue.getType());
}
LocalBuilder temp = null;
final int emitAs = flags & CompilationFlags.EmitAsTypeMask;
if (emitAs != CompilationFlags.EmitAsVoidType) {
// Save the value so we can return it.
generator.dup();
generator.emitStore(temp = getLocal(node.getType()));
}
switch (member.getMemberType()) {
case Field:
generator.putField((FieldInfo) member);
break;
default:
throw Error.invalidMemberType(member.getMemberType());
}
if (emitAs != CompilationFlags.EmitAsVoidType) {
generator.emitLoad(temp);
freeLocal(temp);
}
}
private void emitVariableAssignment(final BinaryExpression node, final int flags) {
final ParameterExpression variable = (ParameterExpression) node.getLeft();
final int emitAs = flags & CompilationFlags.EmitAsTypeMask;
final Expression right = node.getRight();
final ExpressionType rightNodeType = right.getNodeType();
if ((rightNodeType == ExpressionType.Increment ||
rightNodeType == ExpressionType.Decrement) &&
right.getType() == PrimitiveTypes.Integer) {
final LocalBuilder local = _scope.getLocalForVariable(variable);
if (local != null) {
generator.increment(
local,
rightNodeType == ExpressionType.Increment ? 1 : -1
);
if (emitAs != CompilationFlags.EmitAsVoidType) {
emitParameterExpression(variable);
}
return;
}
}
emitExpression(right);
if (!TypeUtils.hasReferenceConversion(right.getType(), variable.getType())) {
generator.emitConversion(right.getType(), variable.getType());
}
if (emitAs != CompilationFlags.EmitAsVoidType) {
generator.dup();
}
_scope.emitSet(variable);
}
private void emitIndexAssignment(final BinaryExpression node, final int flags) {
final Expression left = node.getLeft();
final Expression right = node.getRight();
final BinaryExpression index = (BinaryExpression) left;
final int emitAs = flags & CompilationFlags.EmitAsTypeMask;
// Emit the target array.
emitExpression(left);
// Emit the index.
emitExpression(index.getRight());
// Emit the value
emitExpression(right);
if (!TypeUtils.hasReferenceConversion(right.getType(), left.getType())) {
generator.emitConversion(right.getType(), left.getType());
}
// Save the expression value, if needed
LocalBuilder temp = null;
if (emitAs != CompilationFlags.EmitAsVoidType) {
generator.dup();
generator.emitStore(temp = getLocal(node.getType()));
}
emitSetIndexCall(index);
// Restore the value
if (emitAs != CompilationFlags.EmitAsVoidType) {
generator.emitLoad(temp);
freeLocal(temp);
}
}
private void emitSetIndexCall(final BinaryExpression index) {
generator.emitStoreElement(index.getType());
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Blocks">
private static boolean hasVariables(final Object node) {
if (node instanceof BlockExpression) {
return ((BlockExpression) node).getVariables().size() > 0;
}
return ((CatchBlock) node).getVariable() != null;
}
private void enterTry(final TryExpression tryExpression) {
final Expression finallyBlock = tryExpression.getFinallyBlock();
if (finallyBlock != null) {
_finallyInfo = new FinallyInfo(_finallyInfo, tryExpression);
}
}
private void exitTry(final TryExpression tryExpression) {
if (tryExpression != null && _finallyInfo.tryExpression == tryExpression) {
_finallyInfo = _finallyInfo.parent;
}
}
private void enterScope(final Object node) {
if (hasVariables(node) &&
(_scope.mergedScopes == null || !_scope.mergedScopes.contains(node))) {
CompilerScope scope = _tree.scopes.get(node);
if (scope == null) {
//
// Very often, we want to compile nodes as reductions
// rather than as IL, but usually they need to allocate
// some IL locals. To support this, we allow emitting a
// BlockExpression that was not bound by VariableBinder.
// This works as long as the variables are only used
// locally -- i.e. not closed over.
//
// User-created blocks will never hit this case; only our
// internally reduced nodes will.
//
scope = new CompilerScope(node, false);
scope.needsClosure = _scope.needsClosure;
}
_scope = scope.enter(this, _scope);
assert (_scope.node == node);
}
}
private void exitScope(final Object node) {
if (_scope.node == node) {
_scope = _scope.exit();
}
}
private void emitBlockExpression(final Expression expr, final int flags) {
emit((BlockExpression) expr, updateEmitAsTypeFlag(flags, CompilationFlags.EmitAsDefaultType));
}
private void emit(final BlockExpression node, final int flags) {
enterScope(node);
final int emitAs = flags & CompilationFlags.EmitAsTypeMask;
final int count = node.getExpressionCount();
final int tailCall = flags & CompilationFlags.EmitAsTailCallMask;
final int middleTailCall = tailCall == CompilationFlags.EmitAsNoTail
? CompilationFlags.EmitAsNoTail
: CompilationFlags.EmitAsMiddle;
for (int index = 0; index < count - 1; index++) {
final Expression e = node.getExpression(index);
final Expression next = node.getExpression(index + 1);
/*
if (emitDebugSymbols()) {
// No need to emit a clearance if the next expression in the block is also a
// DebugInfoExpression.
if (e instanceof DebugInfoExpression &&
((DebugInfoExpression)e).isClear() &&
next instanceof DebugInfoExpression) {
continue;
}
}
*/
//
// In the middle of the block.
// We may do better here by marking it as Tail if the following expressions are
// not going to emit any bytecode.
//
int tailCallFlag = middleTailCall;
if (next instanceof GotoExpression) {
final GotoExpression g = (GotoExpression) next;
if (g.getValue() == null || !significant(g.getValue())) {
final LabelInfo labelInfo = referenceLabel(g.getTarget());
if (labelInfo.canReturn()) {
//
// Since tail call flags are not passed into emitTryExpression(),
// canReturn() means the goto will be emitted as [T]RETURN. Therefore
// we can emit the current expression with tail call.
//
tailCallFlag = CompilationFlags.EmitAsTail;
}
}
}
final int updatedFlags = updateEmitAsTailCallFlag(flags, tailCallFlag);
emitExpressionAsVoid(e, updatedFlags);
}
// if the type of Block it means this is not a Comma
// so we will force the last expression to emit as void.
// We don't need EmitAsType flag anymore, should only pass
// the EmitTailCall field in flags to emitting the last expression.
if (emitAs == CompilationFlags.EmitAsVoidType || node.getType() == PrimitiveTypes.Void) {
emitExpressionAsVoid(node.getExpression(count - 1), tailCall);
}
else {
emitExpressionAsType(node.getExpression(count - 1), node.getType(), tailCall);
}
exitScope(node);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="AndAlso Binary Expressions">
private void emitAndAlsoBinaryExpression(final Expression expr, final int flags) {
final BinaryExpression b = (BinaryExpression) expr;
if (b.getMethod() != null) {
throw Error.andAlsoCannotProvideMethod();
}
else if (b.getLeft().getType() == Types.Boolean) {
emitUnboxingAndAlso(b);
}
else {
emitPrimitiveAndAlso(b);
}
}
private void emitPrimitiveAndAlso(final BinaryExpression b) {
final Expression left = b.getLeft();
final Expression right = b.getRight();
final Type<?> leftType = left.getType();
final Type<?> rightType = right.getType();
final Label returnFalse = generator.defineLabel();
final Label exit = generator.defineLabel();
// final LocalBuilder leftStorage = getLocal(leftType);
// final LocalBuilder rightStorage = getLocal(rightType);
emitExpression(left);
// generator.emitStore(leftStorage);
// generator.emitLoad(leftStorage);
generator.emit(OpCode.IFEQ, returnFalse);
emitExpression(right);
// generator.emitStore(rightStorage);
// generator.emitLoad(rightStorage);
generator.emit(OpCode.IFEQ, returnFalse);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(returnFalse);
generator.emitBoolean(false);
generator.markLabel(exit);
}
private void emitUnboxingAndAlso(final BinaryExpression b) {
final Expression left = b.getLeft();
final Expression right = b.getRight();
final Type<?> leftType = left.getType();
final Type<?> rightType = right.getType();
final Label returnFalse = generator.defineLabel();
final Label exit = generator.defineLabel();
// final LocalBuilder leftStorage = getLocal(leftType);
// final LocalBuilder rightStorage = getLocal(rightType);
emitExpression(left);
// generator.emitStore(leftStorage);
// generator.emitLoad(leftStorage);
if (leftType != PrimitiveTypes.Boolean) {
generator.emitConversion(leftType, PrimitiveTypes.Boolean);
}
generator.emit(OpCode.IFEQ, returnFalse);
emitExpression(right);
// generator.emitStore(rightStorage);
// generator.emitLoad(rightStorage);
if (rightType != PrimitiveTypes.Boolean) {
generator.emitConversion(rightType, PrimitiveTypes.Boolean);
}
generator.emit(OpCode.IFEQ, returnFalse);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(returnFalse);
generator.emitBoolean(false);
generator.markLabel(exit);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="OrElse Binary Expressions">
private void emitOrElseBinaryExpression(final Expression expr, final int flags) {
final BinaryExpression b = (BinaryExpression) expr;
if (b.getMethod() != null) {
throw Error.orElseCannotProvideMethod();
}
else if (b.getLeft().getType() == Types.Boolean) {
emitUnboxingOrElse(b);
}
else {
emitPrimitiveOrElse(b);
}
}
private void emitPrimitiveOrElse(final BinaryExpression b) {
final Expression left = b.getLeft();
final Expression right = b.getRight();
final Type<?> leftType = left.getType();
final Type<?> rightType = right.getType();
final Label returnTrue = generator.defineLabel();
final Label exit = generator.defineLabel();
// final LocalBuilder leftStorage = getLocal(leftType);
// final LocalBuilder rightStorage = getLocal(rightType);
emitExpression(left);
// generator.emitStore(leftStorage);
// generator.emitLoad(leftStorage);
generator.emit(OpCode.IFNE, returnTrue);
emitExpression(right);
// generator.emitStore(rightStorage);
// generator.emitLoad(rightStorage);
generator.emit(OpCode.IFNE, returnTrue);
generator.emitBoolean(false);
generator.emitGoto(exit);
generator.markLabel(returnTrue);
generator.emitBoolean(true);
generator.markLabel(exit);
}
private void emitUnboxingOrElse(final BinaryExpression b) {
final Expression left = b.getLeft();
final Expression right = b.getRight();
final Type<?> leftType = left.getType();
final Type<?> rightType = right.getType();
final Label returnTrue = generator.defineLabel();
final Label exit = generator.defineLabel();
// final LocalBuilder leftStorage = getLocal(leftType);
// final LocalBuilder rightStorage = getLocal(rightType);
emitExpression(left);
// generator.emitStore(leftStorage);
// generator.emitLoad(leftStorage);
if (leftType != PrimitiveTypes.Boolean) {
generator.emitConversion(leftType, PrimitiveTypes.Boolean);
}
generator.emit(OpCode.IFNE, returnTrue);
emitExpression(right);
// generator.emitStore(rightStorage);
// generator.emitLoad(rightStorage);
if (rightType != PrimitiveTypes.Boolean) {
generator.emitConversion(rightType, PrimitiveTypes.Boolean);
}
generator.emit(OpCode.IFNE, returnTrue);
generator.emitBoolean(false);
generator.emitGoto(exit);
generator.markLabel(returnTrue);
generator.emitBoolean(true);
generator.markLabel(exit);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Coalesce Expressions">
private void emitCoalesceBinaryExpression(final Expression expr) {
final BinaryExpression b = (BinaryExpression) expr;
assert (b.getMethod() == null);
if (b.getLeft().getType().isPrimitive()) {
throw Error.coalesceUsedOnNonNullableType();
}
else if (b.getConversion() != null) {
emitLambdaReferenceCoalesce(b);
}
else {
emitReferenceCoalesceWithoutConversion(b);
}
}
private void emitReferenceCoalesceWithoutConversion(final BinaryExpression b) {
final Label end = generator.defineLabel();
final boolean needConvertLeft = !TypeUtils.areEquivalent(b.getLeft().getType(), b.getType());
final boolean needConvertRight = !TypeUtils.areEquivalent(b.getRight().getType(), b.getType());
final Label convertLeft = needConvertLeft ? generator.defineLabel() : null;
emitExpression(b.getLeft());
generator.dup();
generator.emit(OpCode.IFNONNULL, needConvertLeft ? convertLeft : end);
generator.pop();
emitExpression(b.getRight());
if (needConvertRight) {
generator.emitConversion(b.getRight().getType(), b.getType());
}
if (needConvertLeft) {
generator.emitGoto(end);
generator.markLabel(convertLeft);
generator.emitConversion(b.getLeft().getType(), b.getType());
}
generator.markLabel(end);
}
private void emitLambdaReferenceCoalesce(final BinaryExpression b) {
final LocalBuilder operandStorage = getLocal(b.getLeft().getType());
final Label end = generator.defineLabel();
final Label notNull = generator.defineLabel();
emitExpression(b.getLeft());
generator.dup();
generator.emitStore(operandStorage);
generator.emit(OpCode.IFNONNULL, notNull);
emitExpression(b.getRight());
generator.emitGoto(end);
// If not null, call conversion.
generator.markLabel(notNull);
final LambdaExpression lambda = b.getConversion();
final ParameterExpressionList conversionParameters = lambda.getParameters();
assert (conversionParameters.size() == 1);
// Emit the delegate instance.
emitLambdaExpression(lambda);
// Emit argument.
generator.emitLoad(operandStorage);
freeLocal(operandStorage);
final MethodInfo method = Expression.getInvokeMethod(lambda);
// Emit call to invoke.
generator.call(method);
generator.markLabel(end);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Conditional Expressions">
private void emitConditionalExpression(final Expression expr, final int flags) {
final ConditionalExpression node = (ConditionalExpression) expr;
assert node.getTest().getType() == PrimitiveTypes.Boolean;
final Label ifFalse = generator.defineLabel();
emitExpressionAndBranch(false, node.getTest(), ifFalse);
emitExpressionAsType(node.getIfTrue(), node.getType(), flags);
if (notEmpty(node.getIfFalse())) {
final Label end = generator.defineLabel();
generator.emitGoto(end);
generator.markLabel(ifFalse);
emitExpressionAsType(node.getIfFalse(), node.getType(), flags);
generator.markLabel(end);
}
else {
generator.markLabel(ifFalse);
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Binary Expressions">
private void emitBinaryExpression(final Expression expr) {
emitBinaryExpression(expr, CompilationFlags.EmitAsNoTail);
}
private void emitBinaryExpression(final Expression expr, final int flags) {
final BinaryExpression b = (BinaryExpression) expr;
final ExpressionType nodeType = b.getNodeType();
assert nodeType != ExpressionType.AndAlso &&
nodeType != ExpressionType.OrElse &&
nodeType != ExpressionType.Coalesce;
if (b.getMethod() != null) {
emitBinaryMethod(b, flags);
return;
}
final Expression left = b.getLeft();
final Expression right = b.getRight();
final Type leftType = left.getType();
final Type rightType = right.getType();
if (nodeType.isEqualityOperator() &&
(b.getType() == PrimitiveTypes.Boolean || b.getType() == Types.Boolean)) {
emitExpression(getEqualityOperand(left));
emitExpression(getEqualityOperand(right));
}
else {
emitExpression(left);
emitExpression(right);
}
emitBinaryOperator(nodeType, leftType, rightType, b.getType());
}
private Expression getEqualityOperand(final Expression expression) {
if (expression.getNodeType() == ExpressionType.Convert) {
final UnaryExpression convert = (UnaryExpression) expression;
if (TypeUtils.areReferenceAssignable(convert.getType(), convert.getOperand().getType())) {
return convert.getOperand();
}
}
return expression;
}
private void emitBinaryMethod(final BinaryExpression b, final int flags) {
final MethodInfo method = b.getMethod();
final Expression left = b.getLeft();
final Expression right = b.getRight();
final Expression instance;
if (method.isStatic()) {
emitMethodCallExpression(Expression.call(null, method, left, right), flags);
}
else if (TypeUtils.isSameOrSubType(method.getDeclaringType(), left.getType())) {
emitMethodCallExpression(Expression.call(left, method, right), flags);
}
else {
emitMethodCallExpression(Expression.call(right, method, left), flags);
}
}
private void emitBinaryOperator(final ExpressionType op, final Type<?> leftType, final Type<?> rightType, final Type resultType) {
final boolean leftIsNullable = TypeUtils.isAutoUnboxed(leftType);
final boolean rightIsNullable = TypeUtils.isAutoUnboxed(rightType);
switch (op) {
case ArrayIndex: {
if (rightType != PrimitiveTypes.Integer && rightType != Types.Integer) {
throw ContractUtils.unreachable();
}
generator.emitLoadElement(leftType.getElementType());
return;
}
case Coalesce: {
throw Error.unexpectedCoalesceOperator();
}
case ReferenceEqual:
case ReferenceNotEqual: {
emitObjectBinaryOp(op);
return;
}
}
if (leftIsNullable && TypeUtils.isArithmetic(rightType) || rightIsNullable && TypeUtils.isArithmetic(leftType)) {
emitUnboxingBinaryOp(op, leftType, rightType, resultType);
}
else {
final Type<?> opResultType = emitPrimitiveBinaryOp(op, leftType, rightType);
emitConvertArithmeticResult(op, opResultType, resultType);
}
}
private void emitConvertArithmeticResult(final ExpressionType op, final Type sourceType, final Type resultType) {
generator.emitConversion(sourceType, resultType);
}
private Type<?> emitPrimitiveBinaryOp(final ExpressionType op, final Type leftType, final Type rightType) {
final Type<?> operandType;
if (TypeUtils.isArithmetic(leftType)) {
operandType = TypeUtils.isArithmetic(rightType)
? Expression.performBinaryNumericPromotion(leftType, rightType)
: leftType;
}
else {
operandType = leftType;
}
if (leftType != operandType) {
final LocalBuilder rightStorage = getLocal(rightType);
generator.emitStore(rightStorage);
generator.emitConversion(leftType, operandType);
generator.emitLoad(rightStorage);
freeLocal(rightStorage);
}
final Type<?> rightOperandType;
switch (op) {
case LeftShift:
case RightShift:
case UnsignedRightShift:
rightOperandType = PrimitiveTypes.Integer;
break;
default:
rightOperandType = operandType;
break;
}
if (rightType != rightOperandType) {
generator.emitConversion(rightType, rightOperandType);
}
switch (op) {
case Equal:
case GreaterThan:
case GreaterThanOrEqual:
case LessThan:
case LessThanOrEqual:
case NotEqual:
case ReferenceEqual:
case ReferenceNotEqual: {
final Label ifFalse = generator.defineLabel();
final Label exit = generator.defineLabel();
emitRelationalBranchOp(op, operandType, false, ifFalse);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifFalse);
generator.emitBoolean(false);
generator.emitGoto(exit);
generator.markLabel(exit);
return PrimitiveTypes.Boolean;
}
}
emitArithmeticBinaryOp(op, operandType);
return operandType;
}
private void emitUnboxingBinaryOp(final ExpressionType op, final Type leftType, final Type rightType, final Type resultType) {
assert TypeUtils.isAutoUnboxed(leftType) || TypeUtils.isAutoUnboxed(rightType);
switch (op) {
case And: {
if (leftType == Types.Boolean) {
emitLiftedBooleanAnd(leftType, rightType);
}
else {
emitUnboxingBinaryArithmetic(op, leftType, rightType, resultType);
}
break;
}
case Or: {
if (leftType == Types.Boolean) {
emitLiftedBooleanOr(leftType, rightType);
}
else {
emitUnboxingBinaryArithmetic(op, leftType, rightType, resultType);
}
break;
}
case ExclusiveOr:
case Add:
case Subtract:
case Multiply:
case Divide:
case Modulo:
case LeftShift:
case RightShift: {
emitUnboxingBinaryArithmetic(op, leftType, rightType, resultType);
break;
}
case LessThan:
case LessThanOrEqual:
case GreaterThan:
case GreaterThanOrEqual:
case Equal:
case NotEqual: {
emitLiftedRelational(op, leftType, rightType, resultType);
break;
}
case AndAlso:
case OrElse:
default:
throw ContractUtils.unreachable();
}
}
private void emitUnboxingBinaryArithmetic(
final ExpressionType op,
final Type leftType,
final Type rightType,
final Type resultType) {
final boolean leftIsBoxed = TypeUtils.isAutoUnboxed(leftType);
final boolean rightIsBoxed = TypeUtils.isAutoUnboxed(rightType);
Type finalLeftType = leftType;
Type finalRightType = rightType;
if (leftIsBoxed) {
finalLeftType = unboxLeftBinaryOperand(leftType, rightType);
}
if (rightIsBoxed) {
finalRightType = unboxRightBinaryOperand(rightType);
}
emitBinaryOperator(
op,
finalLeftType,
finalRightType,
TypeUtils.getUnderlyingPrimitiveOrSelf(resultType)
);
}
private void emitLiftedRelational(
final ExpressionType op,
final Type leftType,
final Type rightType,
final Type resultType) {
final boolean leftIsBoxed = TypeUtils.isAutoUnboxed(leftType);
final boolean rightIsBoxed = TypeUtils.isAutoUnboxed(rightType);
Type finalLeftType = leftType;
Type finalRightType = rightType;
if (leftIsBoxed) {
finalLeftType = unboxLeftBinaryOperand(leftType, rightType);
}
if (rightIsBoxed) {
finalRightType = unboxRightBinaryOperand(rightType);
}
emitBinaryOperator(
op,
finalLeftType,
finalRightType,
TypeUtils.getUnderlyingPrimitiveOrSelf(resultType)
);
}
private Type unboxRightBinaryOperand(final Type rightType) {
final Type finalRightType = TypeUtils.getUnderlyingPrimitive(rightType);
generator.emitUnbox(finalRightType);
return finalRightType;
}
private Type unboxLeftBinaryOperand(final Type leftType, final Type rightType) {
final Type finalLeftType = TypeUtils.getUnderlyingPrimitive(leftType);
final LocalBuilder rightStorage = getLocal(rightType);
generator.emitStore(rightStorage);
generator.emitUnbox(finalLeftType);
generator.emitLoad(rightStorage);
freeLocal(rightStorage);
return finalLeftType;
}
private void emitLiftedBooleanAnd(final Type leftType, final Type rightType) {
final Type type = PrimitiveTypes.Boolean;
final Label returnFalse = generator.defineLabel();
final Label exit = generator.defineLabel();
final LocalBuilder rightStorage = getLocal(type);
generator.emitStore(rightStorage);
generator.emitConversion(leftType, PrimitiveTypes.Boolean);
generator.emit(OpCode.IFEQ, returnFalse);
generator.emitLoad(rightStorage);
generator.emitConversion(rightType, PrimitiveTypes.Boolean);
generator.emit(OpCode.IFEQ, returnFalse);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(returnFalse);
generator.emitBoolean(false);
generator.emitGoto(exit);
generator.markLabel(exit);
freeLocal(rightStorage);
}
private void emitLiftedBooleanOr(final Type leftType, final Type rightType) {
final Type type = PrimitiveTypes.Boolean;
final Label returnTrue = generator.defineLabel();
final Label exit = generator.defineLabel();
final LocalBuilder rightStorage = getLocal(type);
generator.emitStore(rightStorage);
generator.emitConversion(leftType, PrimitiveTypes.Boolean);
generator.emit(OpCode.IFNE, returnTrue);
generator.emitLoad(rightStorage);
generator.emitConversion(rightType, PrimitiveTypes.Boolean);
generator.emit(OpCode.IFNE, returnTrue);
generator.emitBoolean(false);
generator.emitGoto(exit);
generator.markLabel(returnTrue);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(exit);
freeLocal(rightStorage);
}
private void emitObjectBinaryOp(final ExpressionType op) {
switch (op) {
case Equal:
case ReferenceEqual: {
final Label ifNotEqual = generator.defineLabel();
final Label exit = generator.defineLabel();
generator.emit(OpCode.IF_ACMPNE, ifNotEqual);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifNotEqual);
generator.emitBoolean(false);
generator.emitGoto(exit);
generator.markLabel(exit);
break;
}
case NotEqual:
case ReferenceNotEqual: {
final Label ifEqual = generator.defineLabel();
final Label exit = generator.defineLabel();
generator.emit(OpCode.IF_ACMPEQ, ifEqual);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifEqual);
generator.emitBoolean(false);
generator.emitGoto(exit);
generator.markLabel(exit);
break;
}
default: {
throw ContractUtils.unreachable();
}
}
}
private void emitArithmeticBinaryOp(final ExpressionType op, final Type<?> operandType) {
switch (op) {
case Add: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IADD);
return;
case LONG:
generator.emit(OpCode.LADD);
return;
case CHAR:
generator.emit(OpCode.IADD);
return;
case FLOAT:
generator.emit(OpCode.FADD);
return;
case DOUBLE:
generator.emit(OpCode.DADD);
return;
}
break;
}
case And:
case AndAlso: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IAND);
return;
case LONG:
generator.emit(OpCode.LAND);
return;
case CHAR:
generator.emit(OpCode.IAND);
return;
}
break;
}
case Divide: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IDIV);
return;
case LONG:
generator.emit(OpCode.LDIV);
return;
case CHAR:
generator.emit(OpCode.IDIV);
return;
case FLOAT:
generator.emit(OpCode.FDIV);
return;
case DOUBLE:
generator.emit(OpCode.DDIV);
return;
}
break;
}
case ExclusiveOr: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IXOR);
return;
case LONG:
generator.emit(OpCode.LXOR);
return;
case CHAR:
generator.emit(OpCode.IXOR);
return;
}
break;
}
case LeftShift: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.ISHL);
return;
case LONG:
generator.emit(OpCode.LSHL);
return;
case CHAR:
generator.emit(OpCode.ISHL);
return;
}
break;
}
case Modulo: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IREM);
return;
case LONG:
generator.emit(OpCode.LREM);
return;
case CHAR:
generator.emit(OpCode.IREM);
return;
case FLOAT:
generator.emit(OpCode.FREM);
return;
case DOUBLE:
generator.emit(OpCode.DREM);
return;
}
break;
}
case Multiply: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IMUL);
return;
case LONG:
generator.emit(OpCode.LMUL);
return;
case CHAR:
generator.emit(OpCode.IMUL);
return;
case FLOAT:
generator.emit(OpCode.FMUL);
return;
case DOUBLE:
generator.emit(OpCode.DMUL);
return;
}
break;
}
case Or:
case OrElse: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IOR);
return;
case LONG:
generator.emit(OpCode.LOR);
return;
case CHAR:
generator.emit(OpCode.IOR);
return;
}
break;
}
case RightShift: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.ISHR);
return;
case LONG:
generator.emit(OpCode.LSHR);
return;
case CHAR:
generator.emit(OpCode.ISHR);
return;
}
break;
}
case UnsignedRightShift: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.IUSHR);
return;
case LONG:
generator.emit(OpCode.LUSHR);
return;
case CHAR:
generator.emit(OpCode.IUSHR);
return;
}
break;
}
case Subtract: {
switch (operandType.getKind()) {
case BYTE:
case SHORT:
case INT:
generator.emit(OpCode.ISUB);
return;
case LONG:
generator.emit(OpCode.LSUB);
return;
case CHAR:
generator.emit(OpCode.ISUB);
return;
case FLOAT:
generator.emit(OpCode.FSUB);
return;
case DOUBLE:
generator.emit(OpCode.DSUB);
return;
}
break;
}
}
throw ContractUtils.unreachable();
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Constants and Default Values">
private void emitConstantExpression(final Expression expr) {
final ConstantExpression node = (ConstantExpression) expr;
emitConstant(node.getValue(), node.getType());
}
private void emitConstant(final Object value, final Type<?> type) {
// Try to emit the constant directly into IL
if (CodeGenerator.canEmitConstant(value, type)) {
generator.emitConstant(value, type);
return;
}
_boundConstants.emitConstant(this, value, type);
}
private void emitDefaultValueExpression(final Expression node) {
generator.emitDefaultValue(node.getType());
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Branching">
private void emitGotoExpression(final Expression expr, final int flags) {
final GotoExpression node = (GotoExpression) expr;
final LabelInfo labelInfo = referenceLabel(node.getTarget());
int finalFlags = flags;
if (node.getValue() != null) {
final Type targetType = node.getTarget().getType();
if (targetType == PrimitiveTypes.Void) {
emitExpressionAsVoid(node.getValue(), flags);
}
else {
final Type<?> valueType = node.getValue().getType();
finalFlags = updateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
emitExpression(node.getValue(), finalFlags);
if (!TypeUtils.hasReferenceConversion(valueType, targetType)) {
generator.emitConversion(valueType, targetType);
}
}
}
if (node.getKind() == GotoExpressionKind.Return) {
for (FinallyInfo finallyInfo = _finallyInfo;
finallyInfo != null && finallyInfo.tryExpression != null;
finallyInfo = finallyInfo.parent) {
emitExpression(finallyInfo.tryExpression.getFinallyBlock());
}
}
labelInfo.emitJump();
emitUnreachable(node, finalFlags);
}
private void emitUnreachable(final Expression node, final int flags) {
if (node.getType() != PrimitiveTypes.Void && (flags & CompilationFlags.EmitAsVoidType) == 0) {
generator.emitDefaultValue(node.getType());
}
}
private void emitExpressionAndBranch(final boolean branchValue, final Expression node, final Label label) {
final int startEmitted = emitExpressionStart(node);
try {
if (node.getType() == PrimitiveTypes.Boolean) {
switch (node.getNodeType()) {
case Not:
case IsFalse:
emitBranchNot(branchValue, (UnaryExpression) node, label);
return;
case AndAlso:
case OrElse:
emitBranchLogical(branchValue, (BinaryExpression) node, label);
return;
case Block:
emitBranchBlock(branchValue, (BlockExpression) node, label);
return;
case Equal:
case NotEqual:
case GreaterThan:
case GreaterThanOrEqual:
case LessThan:
case LessThanOrEqual:
case ReferenceEqual:
case ReferenceNotEqual:
emitBranchRelation(branchValue, (BinaryExpression) node, label);
return;
case IsNull:
case IsNotNull:
emitBranchNullCheck(branchValue, (UnaryExpression) node, label);
return;
}
}
emitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
emitBranchOp(branchValue, label);
}
finally {
emitExpressionEnd(startEmitted);
}
}
private void emitBranchNot(final boolean branch, final UnaryExpression node, final Label label) {
if (node.getMethod() != null) {
emitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
emitBranchOp(branch, label);
return;
}
emitExpressionAndBranch(!branch, node.getOperand(), label);
}
private void emitBranchNullCheck(final boolean branch, final UnaryExpression node, final Label label) {
emitExpression(node.getOperand());
emitRelationalBranchOp(node.getNodeType(), node.getOperand().getType(), branch, label);
}
private void emitBranchRelation(final boolean branch, final BinaryExpression node, final Label label) {
final ExpressionType op = node.getNodeType();
assert op == ExpressionType.Equal ||
op == ExpressionType.NotEqual ||
op == ExpressionType.LessThan ||
op == ExpressionType.LessThanOrEqual ||
op == ExpressionType.GreaterThan ||
op == ExpressionType.GreaterThanOrEqual ||
op == ExpressionType.ReferenceEqual ||
op == ExpressionType.ReferenceNotEqual;
if (node.getMethod() != null) {
emitBinaryMethod(node, CompilationFlags.EmitAsNoTail);
//
// emitBinaryMethod() takes into account the Equal/NotEqual
// node kind, so use the original branch value
//
emitBranchOp(branch, label);
return;
}
if (op == ExpressionType.ReferenceEqual ||
op == ExpressionType.ReferenceNotEqual) {
if (ConstantCheck.isNull(node.getLeft())) {
if (ConstantCheck.isNull(node.getRight())) {
generator.emitBoolean(op == ExpressionType.ReferenceEqual);
emitBranchOp(branch, label);
return;
}
emitExpression(getEqualityOperand(node.getRight()));
generator.emit(
branch ? (op == ExpressionType.ReferenceEqual ? OpCode.IFNULL : OpCode.IFNONNULL)
: (op == ExpressionType.ReferenceEqual ? OpCode.IFNONNULL : OpCode.IFNULL),
label
);
return;
}
if (ConstantCheck.isNull(node.getRight())) {
emitExpression(getEqualityOperand(node.getLeft()));
generator.emit(
branch ? (op == ExpressionType.ReferenceEqual ? OpCode.IFNULL : OpCode.IFNONNULL)
: (op == ExpressionType.ReferenceEqual ? OpCode.IFNONNULL : OpCode.IFNULL),
label
);
return;
}
}
else if (op == ExpressionType.Equal ||
op == ExpressionType.NotEqual) {
if (ConstantCheck.isTrue(node.getLeft())) {
if (ConstantCheck.isTrue(node.getRight())) {
if (branch == (op == ExpressionType.Equal)) {
generator.emitGoto(label);
}
return;
}
if (ConstantCheck.isFalse(node.getRight())) {
if (branch == (op == ExpressionType.NotEqual)) {
generator.emitGoto(label);
}
return;
}
emitExpression(getEqualityOperand(node.getRight()));
emitBranchOp(
branch == (op == ExpressionType.Equal),
label
);
return;
}
if (ConstantCheck.isTrue(node.getRight())) {
emitExpression(getEqualityOperand(node.getLeft()));
emitBranchOp(
branch == (op == ExpressionType.Equal),
label
);
return;
}
}
if (TypeUtils.isAutoUnboxed(node.getLeft().getType()) ||
TypeUtils.isAutoUnboxed(node.getRight().getType())) {
emitBinaryExpression(node);
//
// emitBinaryExpression() takes into account the Equal/NotEqual
// node kind, so use the original branch value
//
emitBranchOp(branch, label);
return;
}
final Expression equalityOperand = getEqualityOperand(node.getLeft());
emitExpression(equalityOperand);
emitExpression(getEqualityOperand(node.getRight()));
final Type<?> compareType;
if (TypeUtils.isArithmetic(equalityOperand.getType())) {
compareType = TypeUtils.getUnderlyingPrimitiveOrSelf(equalityOperand.getType());
}
else {
compareType = equalityOperand.getType();
}
final OpCode opCode;
emitRelationalBranchOp(op, compareType, branch, label);
}
private void emitRelationalBranchOp(final ExpressionType op, final Type<?> operandType, final boolean branch, final Label label) {
final boolean reallyBranch;
switch (operandType.getKind()) {
case BOOLEAN:
case BYTE:
case SHORT:
case INT:
case CHAR: {
switch (op) {
case Equal:
reallyBranch = branch == (op == ExpressionType.Equal);
generator.emit(reallyBranch ? OpCode.IF_ICMPEQ : OpCode.IF_ICMPNE, label);
break;
case GreaterThan:
reallyBranch = branch == (op == ExpressionType.GreaterThan);
generator.emit(reallyBranch ? OpCode.IF_ICMPGT : OpCode.IF_ICMPLE, label);
break;
case GreaterThanOrEqual:
reallyBranch = branch == (op == ExpressionType.GreaterThanOrEqual);
generator.emit(reallyBranch ? OpCode.IF_ICMPGE : OpCode.IF_ICMPLT, label);
break;
case LessThan:
reallyBranch = branch == (op == ExpressionType.LessThan);
generator.emit(reallyBranch ? OpCode.IF_ICMPLT : OpCode.IF_ICMPGE, label);
break;
case LessThanOrEqual:
reallyBranch = branch == (op == ExpressionType.LessThanOrEqual);
generator.emit(reallyBranch ? OpCode.IF_ICMPLE : OpCode.IF_ICMPGT, label);
break;
case NotEqual:
reallyBranch = branch == (op == ExpressionType.NotEqual);
generator.emit(reallyBranch ? OpCode.IF_ICMPNE : OpCode.IF_ICMPEQ, label);
break;
}
break;
}
case LONG:
generator.emit(OpCode.LCMP);
switch (op) {
case Equal:
reallyBranch = branch == (op == ExpressionType.Equal);
generator.emit(reallyBranch ? OpCode.IFEQ : OpCode.IFNE, label);
break;
case GreaterThan:
reallyBranch = branch == (op == ExpressionType.GreaterThan);
generator.emit(reallyBranch ? OpCode.IFGT : OpCode.IFLE, label);
break;
case GreaterThanOrEqual:
reallyBranch = branch == (op == ExpressionType.GreaterThanOrEqual);
generator.emit(reallyBranch ? OpCode.IFGE : OpCode.IFLT, label);
break;
case LessThan:
reallyBranch = branch == (op == ExpressionType.LessThan);
generator.emit(reallyBranch ? OpCode.IFLT : OpCode.IFGE, label);
break;
case LessThanOrEqual:
reallyBranch = branch == (op == ExpressionType.LessThanOrEqual);
generator.emit(reallyBranch ? OpCode.IFLE : OpCode.IFGT, label);
break;
case NotEqual:
reallyBranch = branch == (op == ExpressionType.NotEqual);
generator.emit(reallyBranch ? OpCode.IFNE : OpCode.IFEQ, label);
break;
}
break;
case FLOAT:
switch (op) {
case Equal:
reallyBranch = branch == (op == ExpressionType.Equal);
generator.emit(OpCode.FCMPL);
generator.emit(reallyBranch ? OpCode.IFEQ : OpCode.IFNE, label);
break;
case GreaterThan:
reallyBranch = branch == (op == ExpressionType.GreaterThan);
generator.emit(reallyBranch ? OpCode.FCMPG : OpCode.FCMPL);
generator.emit(reallyBranch ? OpCode.IFGT : OpCode.IFLE, label);
break;
case GreaterThanOrEqual:
reallyBranch = branch == (op == ExpressionType.GreaterThanOrEqual);
generator.emit(reallyBranch ? OpCode.FCMPG : OpCode.FCMPL);
generator.emit(reallyBranch ? OpCode.IFGE : OpCode.IFLT, label);
break;
case LessThan:
reallyBranch = branch == (op == ExpressionType.LessThan);
generator.emit(reallyBranch ? OpCode.FCMPL : OpCode.FCMPG);
generator.emit(reallyBranch ? OpCode.IFLT : OpCode.IFGE, label);
break;
case LessThanOrEqual:
reallyBranch = branch == (op == ExpressionType.LessThanOrEqual);
generator.emit(reallyBranch ? OpCode.FCMPL : OpCode.FCMPG);
generator.emit(reallyBranch ? OpCode.IFLE : OpCode.IFGT, label);
break;
case NotEqual:
reallyBranch = branch == (op == ExpressionType.NotEqual);
generator.emit(OpCode.FCMPL);
generator.emit(reallyBranch ? OpCode.IFNE : OpCode.IFEQ, label);
break;
}
break;
case DOUBLE:
switch (op) {
case Equal:
reallyBranch = branch == (op == ExpressionType.Equal);
generator.emit(OpCode.DCMPL);
generator.emit(reallyBranch ? OpCode.IFEQ : OpCode.IFNE, label);
break;
case GreaterThan:
reallyBranch = branch == (op == ExpressionType.GreaterThan);
generator.emit(reallyBranch ? OpCode.DCMPG : OpCode.DCMPL);
generator.emit(reallyBranch ? OpCode.IFGT : OpCode.IFLE, label);
break;
case GreaterThanOrEqual:
reallyBranch = branch == (op == ExpressionType.GreaterThanOrEqual);
generator.emit(reallyBranch ? OpCode.DCMPG : OpCode.DCMPL);
generator.emit(reallyBranch ? OpCode.IFGE : OpCode.IFLT, label);
break;
case LessThan:
reallyBranch = branch == (op == ExpressionType.LessThan);
generator.emit(reallyBranch ? OpCode.DCMPL : OpCode.DCMPG);
generator.emit(reallyBranch ? OpCode.IFLT : OpCode.IFGE, label);
break;
case LessThanOrEqual:
reallyBranch = branch == (op == ExpressionType.LessThanOrEqual);
generator.emit(reallyBranch ? OpCode.DCMPL : OpCode.DCMPG);
generator.emit(reallyBranch ? OpCode.IFLE : OpCode.IFGT, label);
break;
case NotEqual:
reallyBranch = branch == (op == ExpressionType.NotEqual);
generator.emit(OpCode.DCMPL);
generator.emit(reallyBranch ? OpCode.IFNE : OpCode.IFEQ, label);
break;
}
break;
default:
switch (op) {
case Equal:
case ReferenceEqual:
reallyBranch = branch == (op == ExpressionType.Equal || op == ExpressionType.ReferenceEqual);
generator.emit(reallyBranch ? OpCode.IF_ACMPEQ : OpCode.IF_ACMPNE, label);
break;
case NotEqual:
case ReferenceNotEqual:
reallyBranch = branch == (op == ExpressionType.NotEqual || op == ExpressionType.ReferenceNotEqual);
generator.emit(reallyBranch ? OpCode.IF_ACMPNE : OpCode.IF_ACMPEQ, label);
break;
case IsNull:
reallyBranch = branch == (op == ExpressionType.IsNull);
generator.emit(reallyBranch ? OpCode.IFNULL : OpCode.IFNONNULL, label);
break;
case IsNotNull:
reallyBranch = branch == (op == ExpressionType.IsNotNull);
generator.emit(reallyBranch ? OpCode.IFNONNULL : OpCode.IFNULL, label);
break;
}
break;
}
}
private void emitBranchOp(final boolean branch, final Label label) {
generator.emit(branch ? OpCode.IFNE : OpCode.IFEQ, label);
}
private void emitBranchLogical(final boolean branch, final BinaryExpression node, final Label label) {
assert (node.getNodeType() == ExpressionType.AndAlso || node.getNodeType() == ExpressionType.OrElse);
if (node.getMethod() != null) {
emitExpression(node);
emitBranchOp(branch, label);
return;
}
final boolean isAnd = node.getNodeType() == ExpressionType.AndAlso;
//
// To share code, we make the following substitutions:
// if (!(left || right)) branch value
// becomes:
// if (!left && !right) branch value
// and:
// if (!(left && right)) branch value
// becomes:
// if (!left || !right) branch value
//
//
if (branch == isAnd) {
emitBranchAnd(branch, node, label);
}
else {
emitBranchOr(branch, node, label);
}
}
private void emitBranchAnd(final boolean branch, final BinaryExpression node, final Label label) {
// if (left) then
// if (right) branch label
// endIf
final Label endIf = generator.defineLabel();
emitExpressionAndBranch(!branch, node.getLeft(), endIf);
emitExpressionAndBranch(branch, node.getRight(), label);
generator.markLabel(endIf);
}
private void emitBranchOr(final boolean branch, final BinaryExpression node, final Label label) {
// if (left OR right) branch label
emitExpressionAndBranch(branch, node.getLeft(), label);
emitExpressionAndBranch(branch, node.getRight(), label);
}
private void emitBranchBlock(final boolean branch, final BlockExpression node, final Label label) {
enterScope(node);
final int count = node.getExpressionCount();
for (int i = 0; i < count - 1; i++) {
emitExpressionAsVoid(node.getExpression(i));
}
emitExpressionAndBranch(branch, node.getExpression(count - 1), label);
exitScope(node);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Invocation Expressions">
private void emitInvocationExpression(final Expression expr, final int flags) {
final InvocationExpression node = (InvocationExpression) expr;
final LambdaExpression<?> lambdaOperand = node.getLambdaOperand();
// Optimization: inline code for literal lambda's directly
//
// This is worth it because otherwise we end up with a extra call
// to DynamicMethod.CreateDelegate, which is expensive.
//
if (lambdaOperand != null) {
emitInlinedInvoke(node, flags);
return;
}
Expression e = node.getExpression();
if (Type.of(LambdaExpression.class).isAssignableFrom(e.getType())) {
// if the invoke target is a lambda expression tree, first compile it into a delegate
e = Expression.call(e, e.getType().getMethod("compile"));
}
e = Expression.call(e, Expression.getInvokeMethod(e), node.getArguments());
emitExpression(e);
}
private void emitInlinedInvoke(final InvocationExpression invoke, final int flags) {
final LambdaExpression<?> lambda = invoke.getLambdaOperand();
// This is tricky: we need to emit the arguments outside of the
// scope, but set them inside the scope. Fortunately, using the
// stack it is entirely doable.
// 1. Emit invoke arguments.
emitArguments(Expression.getInvokeMethod(lambda), invoke);
// 2. Create the nested LambdaCompiler.
final LambdaCompiler inner = new LambdaCompiler(this, lambda);
// 3. Emit the body.
inner.emitLambdaBody(_scope, true, flags);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Labels">
private void emitLabelExpression(final Expression expr, int flags) {
final LabelExpression node = (LabelExpression) expr;
assert node.getTarget() != null;
// If we're an immediate child of a block, our label will already
// be defined. If not, we need to define our own block so this
// label isn't exposed except to its own child expression.
LabelInfo label = null;
if (_labelBlock.kind == LabelScopeKind.Block) {
label = _labelBlock.tryGetLabelInfo(node.getTarget());
// We're in a block but didn't find our label, try switch
if (label == null && _labelBlock.parent.kind == LabelScopeKind.Switch) {
label = _labelBlock.parent.tryGetLabelInfo(node.getTarget());
}
// if we're in a switch or block, we should have found the label
assert label != null;
}
if (label == null) {
label = defineLabel(node.getTarget());
}
if (node.getDefaultValue() != null) {
if (node.getTarget().getType() == PrimitiveTypes.Void) {
emitExpressionAsVoid(node.getDefaultValue(), flags);
}
else {
flags = updateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
emitExpression(node.getDefaultValue(), flags);
}
}
label.mark();
}
private void pushLabelBlock(final LabelScopeKind type) {
_labelBlock = new LabelScopeInfo(_labelBlock, type);
}
@SuppressWarnings("ConstantConditions")
private boolean tryPushLabelBlock(final Expression node) {
ExpressionType nodeType = node.getNodeType();
// Anything that is "statement-like" -- e.g. has no associated
// stack state can be jumped into, with the exception of try-blocks
// We indicate this by a "Block"
//
// Otherwise, we push an "Expression" to indicate that it can't be
// jumped into
while (true) {
switch (nodeType) {
default: {
if (_labelBlock.kind != LabelScopeKind.Expression) {
pushLabelBlock(LabelScopeKind.Expression);
return true;
}
return false;
}
case Label: {
// LabelExpression is a bit special, if it's directly in a
// block it becomes associate with the block's scope. Same
// thing if it's in a switch case body.
if (_labelBlock.kind == LabelScopeKind.Block) {
final LabelTarget label = ((LabelExpression) node).getTarget();
if (_labelBlock.containsTarget(label)) {
return false;
}
if (_labelBlock.parent.kind == LabelScopeKind.Switch &&
_labelBlock.parent.containsTarget(label)) {
return false;
}
}
pushLabelBlock(LabelScopeKind.Statement);
return true;
}
case Block: {
if (node instanceof StackSpiller.SpilledExpressionBlock) {
// treat it as an expression
nodeType = ExpressionType.Extension;
continue;
}
pushLabelBlock(LabelScopeKind.Block);
// Labels defined immediately in the block are valid for
// the whole block.
if (_labelBlock.parent.kind != LabelScopeKind.Switch) {
defineBlockLabels(node);
}
return true;
}
case Switch: {
pushLabelBlock(LabelScopeKind.Switch);
// Define labels inside of the switch cases so they are in
// scope for the whole switch. This allows "goto case" and
// "goto default" to be considered as local jumps.
final SwitchExpression s = (SwitchExpression) node;
for (final SwitchCase c : s.getCases()) {
defineBlockLabels(c.getBody());
}
defineBlockLabels(s.getDefaultBody());
return true;
}
// Remove this when Convert(Void) goes away.
case Convert: {
if (node.getType() != PrimitiveTypes.Void) {
// treat it as an expression
nodeType = ExpressionType.Extension;
continue;
}
pushLabelBlock(LabelScopeKind.Statement);
return true;
}
case Conditional:
case Loop:
case Goto: {
pushLabelBlock(LabelScopeKind.Statement);
return true;
}
}
}
}
private void popLabelBlock(final LabelScopeKind kind) {
assert _labelBlock != null && _labelBlock.kind == kind;
_labelBlock = _labelBlock.parent;
}
private void defineBlockLabels(final Expression node) {
if (!(node instanceof BlockExpression)) {
return;
}
if (node instanceof StackSpiller.SpilledExpressionBlock) {
return;
}
final BlockExpression block = (BlockExpression) node;
for (int i = 0, n = block.getExpressionCount(); i < n; i++) {
final Expression e = block.getExpression(i);
if (e instanceof LabelExpression) {
defineLabel(((LabelExpression) e).getTarget());
}
}
}
private LabelInfo ensureLabel(final LabelTarget node) {
LabelInfo result = _labelInfo.get(node);
if (result == null) {
_labelInfo.put(node, result = new LabelInfo(generator, node, false));
}
return result;
}
private LabelInfo referenceLabel(final LabelTarget node) {
final LabelInfo result = ensureLabel(node);
result.reference(_labelBlock);
return result;
}
private LabelInfo defineLabel(final LabelTarget node) {
if (node == null) {
return new LabelInfo(generator, null, false);
}
final LabelInfo result = ensureLabel(node);
result.define(_labelBlock);
return result;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Lambda Expressions">
private void emitLambdaExpression(final Expression expr) {
final LambdaExpression node = (LambdaExpression) expr;
emitDelegateConstruction(node);
}
private void emitDelegateConstruction(final LambdaExpression lambda) {
// 1. Create the new compiler
final LambdaCompiler compiler;
// When the lambda does not have a name or the name is empty, generate a unique name for it.
final String name = StringUtilities.isNullOrEmpty(lambda.getName()) ? getUniqueMethodName() : lambda.getName();
/*
final MethodBuilder mb = typeBuilder.defineMethod(
name,
Modifier.PRIVATE | Modifier.STATIC,
lambda.getReturnType(),
lambda.getParameters().getParameterTypes()
);
compiler = new LambdaCompiler(_tree, lambda, mb, this._constructorBuilder);
*/
compiler = new LambdaCompiler(_tree, lambda);
// 2. Emit the lambda
compiler.emitLambdaBody(_scope, false, CompilationFlags.EmitAsNoTail);
if (_scope.needsClosure || _boundConstants.count() != 0) {
compiler.ensureClosure();
}
// 3. emit the delegate creation in the outer lambda
emitDelegateConstruction(compiler);
compiler.typeBuilder.createType();
}
static String getUniqueMethodName() {
return String.format("lambda_method_%d", nextId.getAndIncrement());
}
static String getUniqueLambdaName(final String name, final Class<?> creationContext) {
Package p;
if (creationContext != null) {
p = creationContext.getPackage();
if (p == null) {
p = LambdaCompiler.class.getPackage();
}
}
else {
p = LambdaCompiler.class.getPackage();
}
if (name != null) {
return String.format("%s.%s[0x%3$04x]", p.getName(), name, nextId.getAndIncrement());
}
return String.format("%s.f__Lambda[0x%2$04x]", p.getName(), nextId.getAndIncrement());
}
private void emitLambdaBody() {
// The lambda body is the "last" expression of the lambda
final int tailCallFlag = lambda.isTailCall() ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;
emitLambdaBody(null, false, tailCallFlag);
}
private void emitLambdaBody(final CompilerScope parent, final boolean inlined, int flags) {
_scope.enter(this, parent);
if (inlined) {
final ParameterExpressionList parameters = lambda.getParameters();
//
// The arguments were already pushed onto the stack.
// Store them into locals, popping in reverse order.
//
for (int i = parameters.size() - 1; i >= 0; i--) {
_scope.emitSet(parameters.get(i));
}
}
// Need to emit the expression start for the lambda body
flags = updateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
final Expression body = lambda.getBody();
final Type<?> bodyType = body.getType();
final Type returnType = lambda.getReturnType();
if (returnType == PrimitiveTypes.Void) {
emitExpressionAsVoid(body, flags);
}
else {
emitExpression(body, flags);
if (!TypeUtils.hasReferenceConversion(bodyType, returnType)) {
generator.emitConversion(bodyType, returnType);
}
}
// Return must be the last instruction in a CLI method.
// But if we're inlining the lambda, we want to leave the return
// value on the IL stack.
if (!inlined) {
generator.emitReturn(returnType);
}
_scope.exit();
// Validate labels
assert (_labelBlock.parent == null && _labelBlock.kind == LabelScopeKind.Lambda);
for (final LabelInfo label : _labelInfo.values()) {
label.validateFinish();
}
}
private void emitDelegateConstruction(final LambdaCompiler inner) {
generator.emit(OpCode.NEW, inner.typeBuilder);
generator.dup();
emitClosureCreation(inner);
inner.ensureConstructor();
generator.call(inner._constructorBuilder);
}
private void emitClosureCreation(final LambdaCompiler inner) {
final boolean closure = inner._scope.needsClosure;
final boolean boundConstants = inner._boundConstants.count() > 0;
if (!closure && !boundConstants) {
return;
}
ensureClosure();
final Type<Object[]> objectArrayType = Type.of(Object[].class);
//
// new Closure(constantPool, currentHoistedLocals)
//
generator.emit(OpCode.NEW, closureType);
generator.dup();
if (boundConstants) {
_boundConstants.emitConstant(this, inner._boundConstants.toArray(), objectArrayType);
}
else {
generator.emitNull();
}
if (closure) {
_scope.emitGet(_scope.getNearestHoistedLocals().selfVariable);
}
else {
generator.emitNull();
}
generator.call(closureType.getConstructor(objectArrayType, objectArrayType));
}
private void ensureConstructor() {
if (_constructorBuilder == null) {
_constructorBuilder = typeBuilder.defineDefaultConstructor();
}
}
private void ensureClosure() {
if (_hasClosureArgument) {
return;
}
_hasClosureArgument = true;
_closureField = typeBuilder.defineField(
"$__closure",
Type.of(Closure.class),
Modifier.PRIVATE | Modifier.FINAL
);
_constructorBuilder = typeBuilder.defineConstructor(
Modifier.PUBLIC,
Type.list(closureType)
);
final CodeGenerator ctor = _constructorBuilder.getCodeGenerator();
ctor.emitThis();
ctor.call(Types.Object.getConstructors().get(0));
ctor.emitThis();
ctor.emitLoadArgument(0);
ctor.putField(_closureField);
ctor.emitReturn();
}
final void emitConstantArray(final Object array) {
// Emit as runtime constant if possible
// if not, emit into IL
if (typeBuilder != null) {
// store into field in our type builder, we will initialize
// the value only once.
final FieldBuilder fb = createStaticField("ConstantArray", Type.getType(array));
final Label l = generator.defineLabel();
generator.getField(fb);
generator.emit(OpCode.IFNONNULL, l);
generator.emitConstantArray(array);
generator.putField(fb);
generator.markLabel(l);
generator.getField(fb);
}
else {
generator.emitConstantArray(array);
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Loop Expressions">
private void emitLoopExpression(final Expression expr) {
final LoopExpression node = (LoopExpression) expr;
pushLabelBlock(LabelScopeKind.Statement);
final LabelInfo breakTarget = defineLabel(node.getBreakTarget());
final LabelInfo continueTarget = defineLabel(node.getContinueTarget());
continueTarget.markWithEmptyStack();
emitExpressionAsVoid(node.getBody());
generator.emitGoto(continueTarget.getLabel());
popLabelBlock(LabelScopeKind.Statement);
breakTarget.markWithEmptyStack();
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Member Expressions">
private void emitMemberExpression(final Expression expr) {
final MemberExpression node = (MemberExpression) expr;
// Emit "this", if any.
if (node.getTarget() != null) {
emitExpression(node.getTarget());
}
emitMemberGet(node.getMember());
}
// assumes instance is already on the stack
private void emitMemberGet(final MemberInfo member) {
switch (member.getMemberType()) {
case Field:
final FieldInfo field = (FieldInfo) member;
if (field.getFieldType().isPrimitive() && field.isStatic() && field.isFinal()) {
try {
emitConstant(field.getRawField().get(null), field.getFieldType());
}
catch (IllegalAccessException e) {
generator.getField(field);
}
}
else {
generator.getField(field);
}
break;
default:
throw ContractUtils.unreachable();
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Method Call Expressions">
private void emitMethodCallExpression(final Expression expr) {
emitMethodCallExpression(expr, CompilationFlags.EmitAsNoTail);
}
private void emitMethodCallExpression(final Expression expr, final int flags) {
final MethodCallExpression node = (MethodCallExpression) expr;
emitMethodCall(node.getTarget(), node.getMethod(), node, flags);
}
private void emitMethodCall(final Expression target, final MethodInfo method, final IArgumentProvider methodCallExpr) {
emitMethodCall(target, method, methodCallExpr, CompilationFlags.EmitAsNoTail);
}
private void emitMethodCall(
final Expression target,
final MethodInfo method,
final IArgumentProvider expr,
final int flags) {
// Emit instance, if calling an instance method
Type targetType = null;
if (!method.isStatic()) {
targetType = target.getType();
emitExpression(target);
}
emitMethodCall(method, expr, targetType, flags);
}
private void emitMethodCall(final Expression target, final MethodInfo method, final MethodCallExpression expr, final int flags) {
// Emit instance, if calling an instance method
Type targetType = null;
if (!method.isStatic()) {
targetType = target.getType();
emitExpression(target);
}
emitMethodCall(
method,
expr,
targetType,
target instanceof SuperExpression ? flags | CompilationFlags.EmitAsSuperCall
: flags
);
}
private void emitMethodCall(final MethodInfo method, final IArgumentProvider args, final Type<?> objectType, final int flags) {
// Emit arguments
emitArguments(method, args);
if ((flags & CompilationFlags.EmitAsSuperCall) != 0) {
final MethodInfo superMethod = objectType.getMethod(
method.getName(),
BindingFlags.AllInstance,
method.getParameters().getParameterTypes().toArray()
);
generator.call(OpCode.INVOKESPECIAL, superMethod);
}
else {
generator.call(method);
}
final Type returnType = method.getReturnType();
if (returnType != PrimitiveTypes.Void &&
(method.isGenericMethod() || method.getDeclaringType().isGenericType())) {
final MethodInfo erasedDefinition = method.getErasedMethodDefinition();
if (erasedDefinition != null) {
generator.emitConversion(
erasedDefinition.getReturnType(),
returnType
);
}
}
}
private void emitArguments(final MethodBase method, final IArgumentProvider args) {
emitArguments(method, args, 0);
}
private void emitArguments(final MethodBase method, final IArgumentProvider args, final int skipParameters) {
final TypeList parameters;
if (method instanceof MethodBuilder) {
parameters = ((MethodBuilder) method).getParameterTypes();
}
else if (method instanceof ConstructorBuilder) {
parameters = ((ConstructorBuilder) method).getMethodBuilder().getParameterTypes();
}
else {
parameters = method.getParameters().getParameterTypes();
}
assert args.getArgumentCount() + skipParameters == parameters.size();
for (int i = skipParameters, n = parameters.size(); i < n; i++) {
final Type<?> parameterType = parameters.get(i);
final Expression argument = args.getArgument(i - skipParameters);
emitExpression(argument);
final Type argumentType = argument.getType();
if (method instanceof DynamicMethod) {
if (argumentType != parameterType) {
generator.emitConversion(argumentType, parameterType);
}
}
else if (!TypeUtils.hasReferenceConversion(argumentType, parameterType)) {
generator.emitConversion(argumentType, parameterType);
}
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="New and New Array Expressions">
private void emitNewExpression(final Expression expr) {
final NewExpression node = (NewExpression) expr;
final ConstructorInfo constructor = node.getConstructor();
if (constructor == null) {
assert node.getArguments().size() == 0
: "Node with arguments must have a constructor.";
assert node.getType().isPrimitive()
:
"Only primitive type may have no constructor set.";
generator.emitDefaultValue(node.getType());
return;
}
generator.emitNew(constructor.getDeclaringType());
generator.dup();
emitArguments(constructor, node);
generator.call(constructor);
}
private void emitNewArrayExpression(final Expression expr) {
final NewArrayExpression node = (NewArrayExpression) expr;
final ExpressionList<? extends Expression> expressions = node.getExpressions();
final Type elementType = node.getType().getElementType();
if (node.getNodeType() == ExpressionType.NewArrayInit) {
generator.emitArray(
node.getType().getElementType(),
node.getExpressions().size(),
new CodeGenerator.EmitArrayElementCallback() {
@Override
public void emit(final int index) {
final Expression element = expressions.get(index);
emitExpression(element);
generator.emitConversion(element.getType(), elementType);
}
}
);
}
else {
for (int i = 0, n = expressions.size(); i < n; i++) {
final Expression x = expressions.get(i);
emitExpression(x);
generator.emitConversion(x.getType(), PrimitiveTypes.Integer);
}
generator.emitNewArray(node.getType());
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Parameter Expressions">
private void emitParameterExpression(final Expression expr) {
final ParameterExpression node = (ParameterExpression) expr;
if (node instanceof SelfExpression || node instanceof SuperExpression) {
if (methodBuilder.isStatic()) {
throw Error.cannotAccessThisFromStaticMember();
}
if (node instanceof SelfExpression) {
if (node.getType() != typeBuilder) {
throw Error.incorrectlyTypedSelfExpression(typeBuilder, node.getType());
}
}
else {
if (node.getType() != typeBuilder.getBaseType()) {
throw Error.incorrectlyTypedSuperExpression(typeBuilder, node.getType());
}
}
generator.emitThis();
return;
}
_scope.emitGet(node);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Runtime Variables">
private void emitRuntimeVariablesExpression(final Expression expr) {
final RuntimeVariablesExpression node = (RuntimeVariablesExpression) expr;
_scope.emitVariableAccess(this, node.getVariables());
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Type Binary Expressions">
private void emitTypeBinaryExpression(final Expression expr) {
final TypeBinaryExpression node = (TypeBinaryExpression) expr;
if (node.getNodeType() == ExpressionType.TypeEqual) {
emitExpression(node.reduceTypeEqual());
return;
}
final Type type = node.getTypeOperand();
//
// Try to determine the result statically
//
final AnalyzeTypeIsResult result = ConstantCheck.analyzeInstanceOf(node);
if (result == AnalyzeTypeIsResult.KnownTrue ||
result == AnalyzeTypeIsResult.KnownFalse) {
//
// Result is known statically, so just emit the expression for
// its side effects and return the result.
//
emitExpressionAsVoid(node.getOperand());
generator.emitBoolean(result == AnalyzeTypeIsResult.KnownTrue);
return;
}
if (result == AnalyzeTypeIsResult.KnownAssignable) {
//
// We know the type can be assigned, but still need to check
// for null at runtime.
//
assert !type.isPrimitive();
final Label ifNull = generator.defineLabel();
final Label exit = generator.defineLabel();
emitExpression(node.getOperand());
generator.emit(OpCode.IFNULL, ifNull);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifNull);
generator.emitBoolean(false);
generator.markLabel(exit);
return;
}
assert result == AnalyzeTypeIsResult.Unknown;
emitExpression(node.getOperand());
generator.emit(OpCode.INSTANCEOF, type);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Unary Expressions">
private void emitUnaryExpression(final Expression expr, final int flags) {
emitUnary((UnaryExpression) expr, flags);
}
private void emitUnary(final UnaryExpression node, final int flags) {
if (node.getMethod() != null) {
emitUnaryMethod(node, flags);
}
else {
emitExpression(node.getOperand());
emitUnaryOperator(node.getNodeType(), node.getOperand().getType(), node.getType());
}
}
private void emitUnaryOperator(final ExpressionType op, final Type operandType, final Type resultType) {
final boolean operandIsBoxed = TypeUtils.isAutoUnboxed(operandType);
switch (op) {
case ArrayLength: {
generator.emit(OpCode.ARRAYLENGTH);
return;
}
case IsNull: {
final Label ifNonNull = generator.defineLabel();
final Label exit = generator.defineLabel();
generator.emit(OpCode.IFNONNULL, ifNonNull);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifNonNull);
generator.emitBoolean(false);
generator.markLabel(exit);
return;
}
case IsNotNull: {
final Label ifNull = generator.defineLabel();
final Label exit = generator.defineLabel();
generator.emit(OpCode.IFNULL, ifNull);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifNull);
generator.emitBoolean(false);
generator.markLabel(exit);
return;
}
}
final Type unboxedType = TypeUtils.getUnderlyingPrimitiveOrSelf(operandType);
if (operandIsBoxed) {
generator.emitUnbox(operandType);
}
switch (op) {
case Not:
case OnesComplement: {
switch (unboxedType.getKind()) {
case BOOLEAN: {
final Label ifTrue = generator.defineLabel();
final Label exit = generator.defineLabel();
generator.emitBoolean(false);
generator.emit(OpCode.IF_ICMPNE, ifTrue);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifTrue);
generator.emitBoolean(false);
generator.markLabel(exit);
break;
}
case BYTE:
case SHORT:
case INT: {
generator.emitInteger(-1);
generator.emit(OpCode.IXOR);
break;
}
case LONG: {
generator.emitLong(-1L);
generator.emit(OpCode.LXOR);
break;
}
case CHAR: {
generator.emitInteger(-1);
generator.emit(OpCode.IXOR);
break;
}
default: {
throw Error.unaryOperatorNotDefined(op, unboxedType);
}
}
break;
}
case IsFalse: {
final Label ifTrue = generator.defineLabel();
final Label exit = generator.defineLabel();
generator.emitBoolean(false);
generator.emit(OpCode.IF_ICMPNE, ifTrue);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifTrue);
generator.emitBoolean(false);
generator.markLabel(exit);
// Not an arithmetic operation -> no conversion
return;
}
case IsTrue: {
final Label ifFalse = generator.defineLabel();
final Label exit = generator.defineLabel();
generator.emitBoolean(false);
generator.emit(OpCode.IF_ICMPEQ, ifFalse);
generator.emitBoolean(true);
generator.emitGoto(exit);
generator.markLabel(ifFalse);
generator.emitBoolean(false);
generator.markLabel(exit);
// Not an arithmetic operation -> no conversion
return;
}
case UnaryPlus: {
generator.emit(OpCode.NOP);
break;
}
case Negate: {
switch (unboxedType.getKind()) {
case BYTE:
case SHORT:
case INT: {
generator.emit(OpCode.INEG);
break;
}
case LONG: {
generator.emit(OpCode.LNEG);
break;
}
case CHAR: {
generator.emit(OpCode.INEG);
break;
}
case FLOAT: {
generator.emit(OpCode.FNEG);
break;
}
case DOUBLE: {
generator.emit(OpCode.DNEG);
break;
}
default: {
throw Error.unaryOperatorNotDefined(op, unboxedType);
}
}
break;
}
case Increment: {
switch (unboxedType.getKind()) {
case BYTE:
case SHORT:
case INT: {
generator.emitInteger(1);
generator.emit(OpCode.IADD);
break;
}
case LONG: {
generator.emitLong(1L);
generator.emit(OpCode.LADD);
break;
}
case CHAR: {
generator.emitInteger(1);
generator.emit(OpCode.IADD);
break;
}
case FLOAT: {
generator.emitFloat(1f);
generator.emit(OpCode.FADD);
break;
}
case DOUBLE: {
generator.emitDouble(1d);
generator.emit(OpCode.DADD);
break;
}
default: {
throw Error.unaryOperatorNotDefined(op, unboxedType);
}
}
break;
}
case Decrement: {
switch (unboxedType.getKind()) {
case BYTE:
case SHORT:
case INT: {
generator.emitInteger(1);
generator.emit(OpCode.ISUB);
break;
}
case LONG: {
generator.emitLong(1L);
generator.emit(OpCode.LSUB);
break;
}
case CHAR: {
generator.emitInteger(1);
generator.emit(OpCode.ISUB);
break;
}
case FLOAT: {
generator.emitFloat(1f);
generator.emit(OpCode.FSUB);
break;
}
case DOUBLE: {
generator.emitDouble(1d);
generator.emit(OpCode.DSUB);
break;
}
default: {
throw Error.unaryOperatorNotDefined(op, unboxedType);
}
}
break;
}
default: {
throw Error.unhandledUnary(op);
}
}
emitConvertArithmeticResult(op, unboxedType, resultType);
}
private void emitUnaryMethod(final UnaryExpression node, final int flags) {
emitMethodCallExpression(
Expression.call(node.getMethod(), node.getOperand()),
flags
);
}
private void emitConvertUnaryExpression(final Expression expr, final int flags) {
emitConvert((UnaryExpression) expr, flags);
}
private void emitConvert(final UnaryExpression node, final int flags) {
if (node.getMethod() != null) {
emitUnaryMethod(node, flags);
}
else if (node.getType() == PrimitiveTypes.Void) {
emitExpressionAsVoid(node.getOperand(), flags);
}
else {
if (TypeUtils.areEquivalent(node.getOperand().getType(), node.getType())) {
emitExpression(node.getOperand(), flags);
}
else {
emitExpression(node.getOperand());
generator.emitConversion(node.getOperand().getType(), node.getType());
}
}
}
private void emitUnboxUnaryExpression(final Expression expr) {
final UnaryExpression node = (UnaryExpression) expr;
assert node.getType().isPrimitive();
// Unboxing leaves the unboxed value on the stack
emitExpression(node.getOperand());
generator.emitUnbox(node.getType());
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Throw Expressions">
private void emitThrowUnaryExpression(final Expression expr) {
emitThrow((UnaryExpression) expr, CompilationFlags.EmitAsDefaultType);
}
private void emitThrow(final UnaryExpression expr, final int flags) {
emitExpression(expr.getOperand());
generator.emit(OpCode.ATHROW);
emitUnreachable(expr, flags);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Try Expressions">
private void emitTryExpression(final Expression expr) {
final TryExpression node = (TryExpression) expr;
checkTry();
//
// Entering 'try' block...
//
pushLabelBlock(LabelScopeKind.Try);
generator.beginExceptionBlock();
enterTry(node);
//
// Emit the try statement body.
//
emitExpression(node.getBody());
exitTry(node);
generator.endTryBlock();
final Expression finallyBlock = node.getFinallyBlock();
final Type tryType = expr.getType();
final LocalBuilder value;
if (tryType != PrimitiveTypes.Void) {
//
// Store the value of the try body.
//
value = getLocal(tryType);
generator.emitStore(value);
}
else {
value = null;
}
if (finallyBlock != null) {
emitExpression(finallyBlock);
}
//
// Emit the catch blocks.
//
for (final CatchBlock cb : node.getHandlers()) {
pushLabelBlock(LabelScopeKind.Catch);
//
// Begin the strongly typed exception block.
//
if (cb.getFilter() == null) {
generator.beginCatchBlock(cb.getTest());
}
else {
throw new UnsupportedOperationException("Filter blocks are not yet supported");
// generator.beginExceptFilterBlock();
}
enterScope(cb);
emitCatchStart(cb);
//
// Emit the catch block body.
//
emitExpression(cb.getBody());
if (tryType != PrimitiveTypes.Void) {
//
// Store the value of the catch block body.
//
generator.emitStore(value);
}
if (finallyBlock != null) {
emitExpression(finallyBlock);
}
exitScope(cb);
popLabelBlock(LabelScopeKind.Catch);
}
//
// Emit the finally block.
//
if (finallyBlock != null) {
pushLabelBlock(LabelScopeKind.Finally);
generator.beginFinallyBlock();
final LocalBuilder exceptionTemp = getLocal(Types.Throwable);
generator.emitStore(exceptionTemp);
//
// Emit the body.
//
emitExpression(finallyBlock);
generator.emitLoad(exceptionTemp);
generator.emit(OpCode.ATHROW);
generator.endExceptionBlock();
popLabelBlock(LabelScopeKind.Finally);
}
else {
generator.endExceptionBlock();
}
if (tryType != PrimitiveTypes.Void) {
generator.emitLoad(value);
freeLocal(value);
}
popLabelBlock(LabelScopeKind.Try);
}
private void emitCatchStart(final CatchBlock cb) {
if (cb.getFilter() == null) {
emitSaveExceptionOrPop(cb);
return;
}
//
// Emit filter block. Filter blocks are untyped so we need to do
// the type check ourselves.
//
final Label endFilter = generator.defineLabel();
final Label rightType = generator.defineLabel();
//
// Skip if it's not our exception type, but save the exception if it is
// so it's available to the filter.
//
generator.emit(OpCode.INSTANCEOF, cb.getTest());
generator.dup();
generator.emit(OpCode.IFNE, rightType);
generator.pop();
generator.emitBoolean(false);
generator.emitGoto(endFilter);
//
// It's our type, save it and emit the filter.
//
generator.markLabel(rightType);
emitSaveExceptionOrPop(cb);
pushLabelBlock(LabelScopeKind.Filter);
emitExpression(cb.getFilter());
popLabelBlock(LabelScopeKind.Filter);
//
// Begin the catch, clear the exception; we've already saved it.
//
generator.markLabel(endFilter);
generator.beginCatchBlock(null);
generator.pop();
}
private void checkTry() {
//
// Try inside a filter is not verifiable
//
for (LabelScopeInfo j = _labelBlock; j != null; j = j.parent) {
if (j.kind == LabelScopeKind.Filter) {
throw Error.tryNotAllowedInFilter();
}
}
}
private void emitSaveExceptionOrPop(final CatchBlock cb) {
if (cb.getVariable() != null) {
//
// If the variable is present, store the exception in the variable.
//
_scope.emitSet(cb.getVariable());
}
else {
//
// Otherwise, pop it off the stack.
//
generator.pop();
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Switch Expressions">
private void emitSwitchExpression(final Expression expr, final int flags) {
final SwitchExpression node = (SwitchExpression) expr;
if (tryEmitLookupSwitch(node, flags)) {
return;
}
if (tryEmitStringSwitch(node, flags)) {
return;
}
throw ContractUtils.unsupported();
}
private boolean tryEmitStringSwitch(final SwitchExpression node, final int flags) {
final Type<?> type = node.getSwitchValue().getType();
//
// If we're testing anything other than strings, bail.
//
if (!TypeUtils.areEquivalent(type, Types.String)) {
return false;
}
final MethodInfo comparison = node.getComparison();
final MethodInfo comparerEquals = Types.Comparer.getMethod(
"equals",
BindingFlags.PublicStatic,
Types.Object,
Types.Object
);
//
// If we have a comparison other than string equality, bail.
//
if (comparison.getRawMethod() != comparerEquals.getRawMethod()) {
return false;
}
int tests = 0;
//
// All test values must be constant.
//
for (final SwitchCase c : node.getCases()) {
for (final Expression t : c.getTestValues()) {
if (t instanceof ConstantExpression) {
++tests;
}
else {
return false;
}
}
}
final String[] keys = new String[tests];
final HashMap<String, Expression> caseBodies = new HashMap<>();
int i = 0;
for (final SwitchCase c : node.getCases()) {
final ExpressionList<? extends Expression> testValues = c.getTestValues();
for (int j = 0, n = testValues.size(); j < n; j++) {
final String s = (String) ((ConstantExpression) testValues.get(j)).getValue();
keys[i++] = s;
caseBodies.put(s, c.getBody());
}
}
final SwitchOptions options = node.getOptions();
emitExpression(node.getSwitchValue());
generator.emitSwitch(
keys,
new StringSwitchCallback() {
@Override
public void emitCase(final String key, final Label breakTarget) throws Exception {
final Expression body = caseBodies.get(key);
if (body == null) {
return;
}
final Type nodeType = node.getType();
if (nodeType == PrimitiveTypes.Void) {
emitExpressionAsVoid(body, flags);
}
else {
emitExpressionAsType(body, nodeType, flags);
}
generator.emitGoto(breakTarget);
}
@Override
public void emitDefault(final Label breakTarget) throws Exception {
final Expression defaultBody = node.getDefaultBody();
if (defaultBody == null) {
return;
}
final Type nodeType = node.getType();
if (nodeType == PrimitiveTypes.Void) {
emitExpressionAsVoid(defaultBody, flags);
}
else {
emitExpressionAsType(defaultBody, nodeType, flags);
}
}
},
options
);
return true;
}
private boolean tryEmitLookupSwitch(final SwitchExpression node, final int flags) {
//
// If we have a comparison, bail.
//
if (node.getComparison() != null) {
return false;
}
final Type<?> type = node.getSwitchValue().getType();
final ReadOnlyList<SwitchCase> cases = node.getCases();
//
// Make sure the switch value type and the right side type are types that
// we can optimize.
//
if (!canOptimizeSwitchType(type) ||
!TypeUtils.areEquivalent(type, cases.get(0).getTestValues().get(0).getType())) {
return false;
}
int tests = 0;
//
// If not all expressions are constant, then we can't emit the jump table.
//
for (final SwitchCase c : node.getCases()) {
for (final Expression t : c.getTestValues()) {
if (t instanceof ConstantExpression) {
++tests;
}
else {
return false;
}
}
}
final boolean isEnum = type.isEnum();
final int[] keys = new int[tests];
final HashMap<Integer, Expression> caseBodies = new HashMap<>();
int i = 0;
for (int j = 0, n = cases.size(); j < n; j++) {
final SwitchCase switchCase = cases.get(j);
final ExpressionList<? extends Expression> testValues = switchCase.getTestValues();
for (int k = 0, m = testValues.size(); k < m; k++) {
final int key;
final ConstantExpression test = (ConstantExpression) testValues.get(k);
if (isEnum) {
key = ((Enum) test.getValue()).ordinal();
}
else {
key = ((Number) test.getValue()).intValue();
}
keys[i++] = key;
if (k == m - 1) {
caseBodies.put(key, switchCase.getBody());
}
}
}
Arrays.sort(keys);
emitExpression(node.getSwitchValue());
if (isEnum) {
generator.call(type.getMethod("ordinal"));
}
generator.emitSwitch(
keys,
new SwitchCallback() {
@Override
public void emitCase(final int key, final Label breakTarget) throws Exception {
final Expression body = caseBodies.get(key);
if (body == null) {
return;
}
final Type nodeType = node.getType();
if (nodeType == PrimitiveTypes.Void) {
emitExpressionAsVoid(body, flags);
}
else {
emitExpressionAsType(body, nodeType, flags);
}
generator.emitGoto(breakTarget);
}
@Override
public void emitDefault(final Label breakTarget) throws Exception {
final Expression defaultBody = node.getDefaultBody();
if (defaultBody == null) {
return;
}
final Type nodeType = node.getType();
if (nodeType == PrimitiveTypes.Void) {
emitExpressionAsVoid(defaultBody, flags);
}
else {
emitExpressionAsType(defaultBody, nodeType, flags);
}
}
},
node.getOptions()
);
return true;
}
private static boolean canOptimizeSwitchType(final Type<?> valueType) {
final Type<?> actualValueType = TypeUtils.getUnderlyingPrimitiveOrSelf(valueType);
switch (actualValueType.getKind()) {
case BYTE:
case SHORT:
case INT:
case LONG:
case CHAR:
return true;
default:
return actualValueType.isEnum();
}
}
// </editor-fold>
}
enum LabelScopeKind {
// any "statement like" node that can be jumped into
Statement,
// these correspond to the node of the same name
Block,
Switch,
Lambda,
Try,
// these correspond to the part of the try block we're in
Catch,
Finally,
Filter,
// the catch-all value for any other expression type
// (means we can't jump into it)
Expression,
}
@SuppressWarnings("PackageVisibleField")
final class LabelScopeInfo {
// lazily allocated, we typically use this only once every 6th-7th block
private HashMap<LabelTarget, LabelInfo> labels;
final LabelScopeKind kind;
final LabelScopeInfo parent;
LabelScopeInfo(final LabelScopeInfo parent, final LabelScopeKind kind) {
this.parent = parent;
this.kind = kind;
}
/**
* Returns true if we can jump into this node
*/
boolean canJumpInto() {
switch (kind) {
case Block:
case Statement:
case Switch:
case Lambda:
return true;
}
return false;
}
boolean containsTarget(final LabelTarget target) {
return labels != null &&
labels.containsKey(target);
}
LabelInfo tryGetLabelInfo(final LabelTarget target) {
if (labels == null) {
return null;
}
return labels.get(target);
}
void addLabelInfo(final LabelTarget target, final LabelInfo info) {
assert canJumpInto();
if (labels == null) {
labels = new HashMap<>();
}
labels.put(target, info);
}
}
@SuppressWarnings("PackageVisibleField")
final class FinallyInfo {
final FinallyInfo parent;
final TryExpression tryExpression;
FinallyInfo(final FinallyInfo parent, final TryExpression tryExpression) {
this.parent = parent;
this.tryExpression = tryExpression;
}
}