/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.dsl.processor.generator;
import static com.oracle.truffle.dsl.processor.generator.GeneratorUtils.createTransferToInterpreterAndInvalidate;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isObject;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isSubtypeBoxed;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.isVoid;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.modifiers;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.needsCastTo;
import static com.oracle.truffle.dsl.processor.java.ElementUtils.setVisibility;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Introspection;
import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.Node.Child;
import com.oracle.truffle.api.nodes.Node.Children;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.nodes.NodeInterface;
import com.oracle.truffle.api.nodes.SlowPathException;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.dsl.processor.ProcessorContext;
import com.oracle.truffle.dsl.processor.expression.DSLExpression;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Binary;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.BooleanLiteral;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Call;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.DSLExpressionVisitor;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.IntLiteral;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Negate;
import com.oracle.truffle.dsl.processor.expression.DSLExpression.Variable;
import com.oracle.truffle.dsl.processor.java.ElementUtils;
import com.oracle.truffle.dsl.processor.java.model.CodeAnnotationMirror;
import com.oracle.truffle.dsl.processor.java.model.CodeAnnotationValue;
import com.oracle.truffle.dsl.processor.java.model.CodeExecutableElement;
import com.oracle.truffle.dsl.processor.java.model.CodeTree;
import com.oracle.truffle.dsl.processor.java.model.CodeTreeBuilder;
import com.oracle.truffle.dsl.processor.java.model.CodeTypeElement;
import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.ArrayCodeTypeMirror;
import com.oracle.truffle.dsl.processor.java.model.CodeTypeMirror.DeclaredCodeTypeMirror;
import com.oracle.truffle.dsl.processor.java.model.CodeVariableElement;
import com.oracle.truffle.dsl.processor.model.AssumptionExpression;
import com.oracle.truffle.dsl.processor.model.CacheExpression;
import com.oracle.truffle.dsl.processor.model.CreateCastData;
import com.oracle.truffle.dsl.processor.model.ExecutableTypeData;
import com.oracle.truffle.dsl.processor.model.GuardExpression;
import com.oracle.truffle.dsl.processor.model.ImplicitCastData;
import com.oracle.truffle.dsl.processor.model.NodeChildData;
import com.oracle.truffle.dsl.processor.model.NodeData;
import com.oracle.truffle.dsl.processor.model.NodeExecutionData;
import com.oracle.truffle.dsl.processor.model.NodeFieldData;
import com.oracle.truffle.dsl.processor.model.Parameter;
import com.oracle.truffle.dsl.processor.model.ShortCircuitData;
import com.oracle.truffle.dsl.processor.model.SpecializationData;
import com.oracle.truffle.dsl.processor.model.TemplateMethod;
import com.oracle.truffle.dsl.processor.model.TypeSystemData;
import com.oracle.truffle.dsl.processor.parser.SpecializationGroup;
import com.oracle.truffle.dsl.processor.parser.SpecializationGroup.TypeGuard;
@SuppressWarnings("deprecation")
public class FlatNodeGenFactory {
private static final String METHOD_FALLBACK_GUARD = "fallbackGuard_";
private static final String FRAME_VALUE = TemplateMethod.FRAME_NAME;
private static final String STATE_VALUE = "state";
private static final String NAME_SUFFIX = "_";
private static final String VARARGS_NAME = "args";
private final ProcessorContext context;
private final NodeData node;
private final TypeSystemData typeSystem;
private final TypeMirror genericType;
private final com.oracle.truffle.api.dsl.internal.DSLOptions options;
private final Set<TypeMirror> expectedTypes = new HashSet<>();
private List<SpecializationData> reachableSpecializations;
private Map<String, TypeMirror> isValidSignatures = new HashMap<>();
private final boolean boxingEliminationEnabled;
private int boxingSplitIndex = 0;
private final BitSet state;
private final BitSet exclude;
private final ExecutableTypeData executeAndSpecializeType;
private boolean fallbackNeedsState = false;
public FlatNodeGenFactory(ProcessorContext context, NodeData node) {
this.context = context;
this.node = node;
this.typeSystem = node.getTypeSystem();
this.genericType = context.getType(Object.class);
this.options = typeSystem.getOptions();
this.boxingEliminationEnabled = options.flatLayoutBoxingElimination();
this.reachableSpecializations = calculateReachableSpecializations();
List<Object> objects = new ArrayList<>();
objects.add(node.getUninitializedSpecialization());
Set<TypeGuard> implicitCasts = new LinkedHashSet<>();
for (SpecializationData specialization : reachableSpecializations) {
objects.add(specialization);
int index = 0;
for (Parameter p : specialization.getSignatureParameters()) {
TypeMirror targetType = p.getType();
List<TypeMirror> sourceTypes = typeSystem.lookupSourceTypes(targetType);
if (sourceTypes.size() > 1) {
implicitCasts.add(new TypeGuard(targetType, index));
}
index++;
}
}
objects.addAll(implicitCasts);
this.state = new StateBitSet(objects);
this.exclude = new ExcludeBitSet(reachableSpecializations);
this.executeAndSpecializeType = createExecuteAndSpecializeType();
}
private static String createSpecializationTypeName(SpecializationData s) {
return ElementUtils.firstLetterUpperCase(s.getId()) + "Data";
}
private static String createSpecializationFieldName(SpecializationData s) {
return ElementUtils.firstLetterLowerCase(s.getId()) + "_cache";
}
private static String createFieldName(SpecializationData specialization, Parameter cacheParameter) {
if (useSpecializationClass(specialization)) {
return cacheParameter.getLocalName() + "_";
} else {
return ElementUtils.firstLetterLowerCase(specialization.getId()) + "_" + cacheParameter.getLocalName() + "_";
}
}
private static String createAssumptionFieldName(SpecializationData specialization, AssumptionExpression assumption) {
if (useSpecializationClass(specialization)) {
return assumption.getId() + "_";
} else {
return ElementUtils.firstLetterLowerCase(specialization.getId()) + "_" + assumption.getId() + "_";
}
}
private static String createSpecializationLocalName(SpecializationData s) {
return "s" + s.getIndex() + "_";
}
private static String assumptionName(AssumptionExpression assumption) {
return assumption.getId() + NAME_SUFFIX;
}
private static String nodeFieldName(NodeExecutionData execution) {
return execution.getName() + NAME_SUFFIX;
}
/* Whether a new class should be generated for specialization instance fields. */
private static boolean useSpecializationClass(SpecializationData specialization) {
int size = 0;
for (CacheExpression expression : specialization.getCaches()) {
TypeMirror type = expression.getParameter().getType();
if (ElementUtils.isPrimitive(type)) {
switch (type.getKind()) {
case BOOLEAN:
case BYTE:
size++;
break;
case CHAR:
case SHORT:
size += 2;
break;
case INT:
case FLOAT:
size += 4;
break;
case LONG:
case DOUBLE:
size += 8;
break;
}
} else {
size += 4;
}
}
// if we exceed the size of two references we generate a class
if (size > 8) {
return true;
}
// we need a data class if we need to support multiple specialization instances
return specialization.getMaximumNumberOfInstances() > 1;
}
private static boolean needsFrame(List<SpecializationData> specializations) {
for (SpecializationData specialization : specializations) {
if (specialization.isFrameUsed()) {
return true;
}
}
return false;
}
private static String createImplicitTypeStateLocalName(Parameter execution) {
String name = ElementUtils.firstLetterLowerCase(ElementUtils.getTypeId(execution.getType()));
return name + "Cast" + execution.getSpecification().getExecution().getIndex();
}
private static boolean mayBeExcluded(SpecializationData specialization) {
return !specialization.getExceptions().isEmpty() || !specialization.getExcludedBy().isEmpty();
}
public CodeTypeElement create(CodeTypeElement clazz) {
for (NodeChildData child : node.getChildren()) {
clazz.addOptional(createAccessChildMethod(child));
}
for (NodeFieldData field : node.getFields()) {
if (!field.isGenerated()) {
continue;
}
clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), field.getType(), field.getName()));
if (field.getGetter() != null && field.getGetter().getModifiers().contains(Modifier.ABSTRACT)) {
CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(),
field.getGetter());
method.getModifiers().remove(Modifier.ABSTRACT);
method.createBuilder().startReturn().string("this.").string(field.getName()).end();
clazz.add(method);
}
}
for (ExecutableElement superConstructor : GeneratorUtils.findUserConstructors(node.getTemplateType().asType())) {
clazz.add(createNodeConstructor(clazz, superConstructor));
}
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getChild() != null) {
clazz.add(createNodeField(PRIVATE, execution.getNodeType(), nodeFieldName(execution),
Child.class));
}
}
createFields(clazz);
TypeMirror genericReturnType = node.getPolymorphicSpecialization().getReturnType().getType();
List<ExecutableTypeData> executableTypes = filterExecutableTypes(node.getExecutableTypes(),
reachableSpecializations);
List<ExecutableTypeData> genericExecutableTypes = new ArrayList<>();
List<ExecutableTypeData> specializedExecutableTypes = new ArrayList<>();
List<ExecutableTypeData> voidExecutableTypes = new ArrayList<>();
for (ExecutableTypeData type : executableTypes) {
if (ElementUtils.isVoid(type.getReturnType())) {
voidExecutableTypes.add(type);
} else if (type.hasUnexpectedValue(context) && !ElementUtils.typeEquals(genericReturnType, type.getReturnType())) {
specializedExecutableTypes.add(type);
} else {
genericExecutableTypes.add(type);
}
}
if (genericExecutableTypes.size() > 1) {
boolean hasGenericTypeMatch = false;
for (ExecutableTypeData genericExecutable : genericExecutableTypes) {
if (ElementUtils.typeEquals(genericExecutable.getReturnType(), genericReturnType)) {
hasGenericTypeMatch = true;
break;
}
}
if (hasGenericTypeMatch) {
for (ListIterator<ExecutableTypeData> iterator = genericExecutableTypes.listIterator(); iterator.hasNext();) {
ExecutableTypeData executableTypeData = iterator.next();
if (!ElementUtils.typeEquals(genericReturnType, executableTypeData.getReturnType())) {
iterator.remove();
specializedExecutableTypes.add(executableTypeData);
}
}
}
}
SpecializationData fallback = node.getGenericSpecialization();
if (fallback.getMethod() != null && fallback.isReachable()) {
clazz.add(createFallbackGuard(fallback));
}
for (ExecutableTypeData type : genericExecutableTypes) {
createExecute(clazz, type, Collections.<ExecutableTypeData> emptyList());
}
for (ExecutableTypeData type : specializedExecutableTypes) {
createExecute(clazz, type, genericExecutableTypes);
}
for (ExecutableTypeData type : voidExecutableTypes) {
List<ExecutableTypeData> genericAndSpecialized = new ArrayList<>();
genericAndSpecialized.addAll(genericExecutableTypes);
genericAndSpecialized.addAll(specializedExecutableTypes);
createExecute(clazz, type, genericAndSpecialized);
}
clazz.addOptional(createExecuteAndSpecialize());
clazz.add(createGetCostMethod());
for (TypeMirror type : ElementUtils.uniqueSortedTypes(expectedTypes, false)) {
if (!typeSystem.hasType(type)) {
clazz.addOptional(TypeSystemCodeGenerator.createExpectMethod(PRIVATE, typeSystem,
context.getType(Object.class), type));
}
}
for (TypeMirror assumptionType : isValidSignatures.values()) {
clazz.add(createIsValid(assumptionType));
}
clazz.getEnclosedElements().addAll(removeThisMethods.values());
if (node.isReflectable()) {
generateReflectionInfo(clazz);
}
return clazz;
}
private void generateReflectionInfo(CodeTypeElement clazz) {
clazz.getImplements().add(context.getType(Introspection.Provider.class));
CodeExecutableElement reflection = new CodeExecutableElement(modifiers(PUBLIC), context.getType(Introspection.class), "getIntrospectionData");
CodeTreeBuilder builder = reflection.createBuilder();
List<SpecializationData> filteredSpecializations = new ArrayList<>();
for (SpecializationData s : node.getSpecializations()) {
if (s.getMethod() == null) {
continue;
}
filteredSpecializations.add(s);
}
ArrayCodeTypeMirror objectArray = new ArrayCodeTypeMirror(context.getType(Object.class));
builder.declaration(objectArray, "data", builder.create().startNewArray(objectArray, CodeTreeBuilder.singleString(String.valueOf(filteredSpecializations.size() + 1))).end().build());
builder.declaration(objectArray, "s", (CodeTree) null);
builder.statement("data[0] = 0"); // declare version 0
FrameState frameState = FrameState.load(this);
builder.tree(state.createLoad(frameState, null));
if (requiresExclude()) {
builder.tree(exclude.createLoad(frameState, null));
}
int index = 1;
for (SpecializationData specialization : filteredSpecializations) {
builder.startStatement().string("s = ").startNewArray(objectArray, CodeTreeBuilder.singleString("3")).end().end();
builder.startStatement().string("s[0] = ").doubleQuote(specialization.getMethodName()).end();
builder.startIf().tree(state.createContains(frameState, new Object[]{specialization})).end().startBlock();
builder.startStatement().string("s[1] = (byte)0b01 /* active */").end();
TypeMirror listType = new DeclaredCodeTypeMirror((TypeElement) context.getDeclaredType(ArrayList.class).asElement(), Arrays.asList(context.getType(Object.class)));
if (!specialization.getCaches().isEmpty()) {
builder.declaration(listType, "cached", "new ArrayList<>()");
boolean useSpecializationClass = useSpecializationClass(specialization);
String name = createSpecializationLocalName(specialization);
if (useSpecializationClass) {
builder.declaration(createSpecializationTypeName(specialization), name, CodeTreeBuilder.singleString(createSpecializationFieldName(specialization)));
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.startWhile();
} else {
builder.startIf();
}
builder.string(name, " != null");
builder.end();
builder.startBlock();
}
builder.startStatement().startCall("cached", "add");
builder.startStaticCall(context.getType(Arrays.class), "asList");
for (CacheExpression cache : specialization.getCaches()) {
builder.startGroup();
builder.tree(createCacheReference(specialization, cache.getParameter()));
builder.end();
}
builder.end();
builder.end().end();
if (useSpecializationClass) {
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.startStatement().string(name, " = ", name, ".next_").end();
}
builder.end(); // cache while or if
}
builder.statement("s[2] = cached");
}
builder.end();
if (mayBeExcluded(specialization)) {
builder.startElseIf().tree(exclude.createContains(frameState, new Object[]{specialization})).end().startBlock();
builder.startStatement().string("s[1] = (byte)0b10 /* excluded */").end();
builder.end();
}
builder.startElseBlock();
builder.startStatement().string("s[1] = (byte)0b00 /* inactive */").end();
builder.end();
builder.startStatement().string("data[", String.valueOf(index), "] = s").end();
index++;
}
builder.startReturn().startStaticCall(context.getType(Introspection.Provider.class), "create").string("data").end().end();
clazz.add(reflection);
}
private void createFields(CodeTypeElement clazz) {
CodeVariableElement var = state.declareFields(clazz);
var.createInitBuilder().string("1");
if (requiresExclude()) {
exclude.declareFields(clazz);
}
for (SpecializationData specialization : reachableSpecializations) {
List<CodeVariableElement> fields = new ArrayList<>();
boolean useSpecializationClass = useSpecializationClass(specialization);
for (CacheExpression cache : specialization.getCaches()) {
Parameter parameter = cache.getParameter();
String fieldName = createFieldName(specialization, parameter);
TypeMirror type = parameter.getType();
Modifier visibility = useSpecializationClass ? null : Modifier.PRIVATE;
CodeVariableElement cachedField;
if (ElementUtils.isAssignable(type, context.getType(NodeInterface.class))) {
cachedField = createNodeField(visibility, type, fieldName, Child.class);
} else if (type.getKind() == TypeKind.ARRAY && ElementUtils.isAssignable(((ArrayType) type).getComponentType(), context.getType(NodeInterface.class))) {
cachedField = createNodeField(visibility, type, fieldName, Children.class, Modifier.FINAL);
} else {
if (useSpecializationClass) {
cachedField = createNodeField(visibility, type, fieldName, null, Modifier.FINAL);
} else {
cachedField = createNodeField(visibility, type, fieldName, null);
}
setFieldCompilationFinal(cachedField, parameter.getVariableElement().getAnnotation(Cached.class).dimensions());
}
fields.add(cachedField);
}
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
String fieldName = createAssumptionFieldName(specialization, assumption);
TypeMirror type;
int compilationFinalDimensions;
if (assumption.getExpression().getResolvedType().getKind() == TypeKind.ARRAY) {
type = context.getType(Assumption[].class);
compilationFinalDimensions = 1;
} else {
type = context.getType(Assumption.class);
compilationFinalDimensions = -1;
}
CodeVariableElement assumptionField;
if (useSpecializationClass) {
assumptionField = createNodeField(null, type, fieldName, null, Modifier.FINAL);
} else {
assumptionField = createNodeField(PRIVATE, type, fieldName, null);
}
setFieldCompilationFinal(assumptionField, compilationFinalDimensions);
fields.add(assumptionField);
}
if (useSpecializationClass) {
TypeMirror baseType;
boolean useNode = specializationClassIsNode(specialization);
if (useNode) {
baseType = context.getType(Node.class);
} else {
baseType = context.getType(Object.class);
}
CodeTypeElement cacheType = GeneratorUtils.createClass(node, null, modifiers(PRIVATE, FINAL,
STATIC), createSpecializationTypeName(specialization), baseType);
Class<?> annotationType;
if (useNode) {
annotationType = Child.class;
if (specialization.getMaximumNumberOfInstances() > 1) {
cacheType.add(createNodeField(null, cacheType.asType(), "next_", Child.class));
}
CodeExecutableElement getNodeCost = new CodeExecutableElement(modifiers(PUBLIC),
context.getType(NodeCost.class), "getCost");
getNodeCost.createBuilder().startReturn().staticReference(context.getType(NodeCost.class),
"NONE").end();
cacheType.add(getNodeCost);
} else {
annotationType = CompilationFinal.class;
if (specialization.getMaximumNumberOfInstances() > 1) {
cacheType.add(createNodeField(null, cacheType.asType(), "next_", annotationType));
}
}
cacheType.getEnclosedElements().addAll(fields);
cacheType.add(GeneratorUtils.createConstructorUsingFields(modifiers(), cacheType));
clazz.add(createNodeField(PRIVATE, cacheType.asType(),
createSpecializationFieldName(specialization), annotationType));
clazz.add(cacheType);
} else {
clazz.getEnclosedElements().addAll(fields);
}
}
}
private static void setFieldCompilationFinal(CodeVariableElement field, int compilationFinalDimensions) {
if (field.getModifiers().contains(Modifier.FINAL) && compilationFinalDimensions <= 0) {
// no need for the compilation final annotation.
return;
}
CodeAnnotationMirror annotation = new CodeAnnotationMirror(ProcessorContext.getInstance().getDeclaredType(CompilationFinal.class));
if (compilationFinalDimensions > 0) {
annotation.setElementValue(annotation.findExecutableElement("dimensions"), new CodeAnnotationValue(compilationFinalDimensions));
}
field.getAnnotationMirrors().add(annotation);
}
/* Specialization class needs to be a Node in such a case. */
private boolean specializationClassIsNode(SpecializationData specialization) {
boolean useSpecializationClass = useSpecializationClass(specialization);
if (useSpecializationClass) {
for (CacheExpression cache : specialization.getCaches()) {
TypeMirror type = cache.getParameter().getType();
if (ElementUtils.isAssignable(type, context.getType(NodeInterface.class))) {
return true;
} else if (type.getKind() == TypeKind.ARRAY && ElementUtils.isAssignable(((ArrayType) type).getComponentType(), context.getType(NodeInterface.class))) {
return true;
}
}
}
return false;
}
private boolean requiresExclude() {
for (SpecializationData specialization : reachableSpecializations) {
if (mayBeExcluded(specialization)) {
return true;
}
}
return false;
}
private Element createIsValid(TypeMirror assumptionType) {
CodeExecutableElement isValid = new CodeExecutableElement(modifiers(PRIVATE, STATIC), getType(boolean.class), "isValid_");
CodeTreeBuilder builder = isValid.createBuilder();
if (assumptionType.getKind() == TypeKind.ARRAY) {
isValid.addParameter(new CodeVariableElement(getType(Assumption[].class), "assumptions"));
builder.startFor().startGroup().type(((ArrayType) assumptionType).getComponentType()).string(" assumption : assumptions").end().end();
builder.startBlock();
builder.startIf().string("assumption == null || !assumption.isValid()").end();
builder.startBlock();
builder.returnFalse();
builder.end();
builder.end();
builder.returnTrue();
} else {
isValid.addParameter(new CodeVariableElement(getType(Assumption.class), "assumption"));
builder.startReturn().string("assumption != null && assumption.isValid()").end();
}
return isValid;
}
private Element createFallbackGuard(SpecializationData fallback) {
boolean frameUsed = node.isFrameUsedByAnyGuard();
FrameState frameState = FrameState.load(this);
List<SpecializationData> specializations = new ArrayList<>(reachableSpecializations);
specializations.remove(fallback);
SpecializationGroup group = SpecializationGroup.createFlat(specializations);
ExecutableTypeData executableType = node.findAnyGenericExecutableType(context, -1);
if (!frameUsed) {
frameState.removeValue(FRAME_VALUE);
}
fallbackNeedsState = false;
state.createLoad(frameState, null);
CodeExecutableElement method = frameState.createMethod(modifiers(PRIVATE), getType(boolean.class), METHOD_FALLBACK_GUARD, FRAME_VALUE, STATE_VALUE);
CodeTree result = visitSpecializationGroup(CodeTreeBuilder.createBuilder(), group, executableType, frameState, null, NodeExecutionMode.FALLBACK_GUARD);
if (!fallbackNeedsState) {
VariableElement toRemove = null;
for (VariableElement v : method.getParameters()) {
if (v.getSimpleName().toString().equals(STATE_VALUE)) {
toRemove = v;
break;
}
}
if (toRemove != null) {
method.getParameters().remove(toRemove);
}
}
final CodeTreeBuilder builder = method.createBuilder();
for (SpecializationData implemented : specializations) {
if (implemented.getMaximumNumberOfInstances() > 1) {
method.getAnnotationMirrors().add(createExplodeLoop());
break;
}
}
builder.tree(result);
builder.returnTrue();
if (!accessesState(reachableSpecializations)) {
method.getModifiers().add(STATIC);
}
return method;
}
private static boolean accessesState(List<SpecializationData> specializations) {
final AtomicBoolean needsState = new AtomicBoolean(false);
for (final SpecializationData specialization : specializations) {
if (!specialization.getAssumptionExpressions().isEmpty()) {
needsState.set(true);
break;
}
for (GuardExpression expression : specialization.getGuards()) {
expression.getExpression().accept(new DSLExpressionVisitor() {
public void visitVariable(Variable binary) {
Parameter p = specialization.findByVariable(binary.getResolvedVariable());
if (p == null && binary.getResolvedVariable().getModifiers().contains(STATIC)) {
needsState.set(true);
} else if (p != null && p.getSpecification().isCached()) {
needsState.set(true);
}
}
public void visitBooleanLiteral(BooleanLiteral binary) {
}
public void visitNegate(Negate negate) {
}
public void visitIntLiteral(IntLiteral binary) {
}
public void visitCall(Call binary) {
if (!binary.getResolvedMethod().getModifiers().contains(STATIC)) {
needsState.set(true);
}
}
public void visitBinary(Binary binary) {
}
});
}
}
boolean needsStat = needsState.get();
return needsStat;
}
private CodeAnnotationMirror createExplodeLoop() {
DeclaredType explodeLoopType = context.getDeclaredType(ExplodeLoop.class);
CodeAnnotationMirror explodeLoop = new CodeAnnotationMirror(explodeLoopType);
DeclaredType loopExplosionKind = context.getDeclaredType(ExplodeLoop.LoopExplosionKind.class);
if (loopExplosionKind != null) {
VariableElement kindValue = ElementUtils.findVariableElement(loopExplosionKind, "FULL_EXPLODE_UNTIL_RETURN");
if (kindValue != null) {
explodeLoop.setElementValue(ElementUtils.findExecutableElement(explodeLoopType, "kind"), new CodeAnnotationValue(kindValue));
}
}
return explodeLoop;
}
private List<SpecializationData> filterCompatibleSpecializations(ExecutableTypeData executable, List<SpecializationData> specializations) {
List<SpecializationData> filteredSpecializations = new ArrayList<>();
outer: for (SpecializationData specialization : specializations) {
if (specialization.isFallback() && specialization.getMethod() == null) {
// undefined fallback can always deoptimize
continue;
}
List<TypeMirror> signatureParameters = executable.getSignatureParameters();
for (int i = 0; i < signatureParameters.size(); i++) {
TypeMirror evaluatedType = signatureParameters.get(i);
TypeMirror specializedType = specialization.findParameterOrDie(node.getChildExecutions().get(i)).getType();
if (!isSubtypeBoxed(context, evaluatedType, specializedType) && !isSubtypeBoxed(context, specializedType, evaluatedType)) {
// not compatible parameter
continue outer;
}
}
if (!isVoid(executable.getReturnType()) && !isSubtypeBoxed(context, specialization.getReturnType().getType(), executable.getReturnType()) &&
!isSubtypeBoxed(context, executable.getReturnType(), specialization.getReturnType().getType())) {
continue outer;
}
filteredSpecializations.add(specialization);
}
return filteredSpecializations;
}
private List<SpecializationData> filterImplementedSpecializations(ExecutableTypeData executable, List<SpecializationData> specializations) {
List<SpecializationData> filteredSpecializations = new ArrayList<>();
TypeMirror returnType = ElementUtils.boxType(context, executable.getReturnType());
for (SpecializationData specialization : specializations) {
TypeMirror specializationReturnType = ElementUtils.boxType(context, specialization.getReturnType().getType());
if (ElementUtils.typeEquals(specializationReturnType, returnType)) {
filteredSpecializations.add(specialization);
}
}
return filteredSpecializations;
}
private List<ExecutableTypeData> filterCompatibleExecutableTypes(ExecutableTypeData type, List<ExecutableTypeData> genericExecutes) {
List<ExecutableTypeData> compatible = new ArrayList<>();
outer: for (ExecutableTypeData genericExecute : genericExecutes) {
if (genericExecute.getEvaluatedCount() != type.getEvaluatedCount()) {
continue;
}
for (int i = 0; i < genericExecute.getEvaluatedCount(); i++) {
TypeMirror sourceType = type.getSignatureParameters().get(i);
TypeMirror targetType = genericExecute.getSignatureParameters().get(i);
if (!ElementUtils.isAssignable(sourceType, targetType)) {
continue outer;
}
}
if (!isVoid(type.getReturnType()) && !isSubtypeBoxed(context, type.getReturnType(), genericExecute.getReturnType()) &&
!isSubtypeBoxed(context, genericExecute.getReturnType(), type.getReturnType())) {
continue outer;
}
compatible.add(genericExecute);
}
return compatible;
}
private CodeExecutableElement createExecute(CodeTypeElement clazz, ExecutableTypeData type, List<ExecutableTypeData> delegateableTypes) {
final List<SpecializationData> allSpecializations = reachableSpecializations;
final List<SpecializationData> compatibleSpecializations = filterCompatibleSpecializations(type, allSpecializations);
List<SpecializationData> implementedSpecializations;
if (delegateableTypes.isEmpty()) {
implementedSpecializations = compatibleSpecializations;
} else {
implementedSpecializations = filterImplementedSpecializations(type, compatibleSpecializations);
}
FrameState frameState = FrameState.load(this, type, Integer.MAX_VALUE);
CodeExecutableElement method = createExecuteMethod(null, type, frameState, true);
clazz.add(method);
CodeTreeBuilder builder = method.createBuilder();
// do I miss specializations that are reachable from this executable?
if (compatibleSpecializations.size() != implementedSpecializations.size()) {
ExecuteDelegationResult delegation = createExecuteDelegation(builder, frameState, type, delegateableTypes, compatibleSpecializations, implementedSpecializations);
builder.tree(delegation.tree);
if (!delegation.hasFallthrough) {
return method;
}
}
if (implementedSpecializations.isEmpty()) {
implementedSpecializations = compatibleSpecializations;
}
if (implementedSpecializations.isEmpty()) {
builder.tree(createTransferToInterpreterAndInvalidate());
builder.startThrow().startNew(getType(AssertionError.class)).doubleQuote("Delegation failed.").end().end();
} else {
SpecializationGroup group = SpecializationGroup.createFlat(implementedSpecializations);
builder.tree(createFastPath(builder, implementedSpecializations, group, type, frameState));
}
return method;
}
private ExecuteDelegationResult createExecuteDelegation(CodeTreeBuilder parent, FrameState frameState, ExecutableTypeData type,
List<ExecutableTypeData> delegateableTypes, final List<SpecializationData> compatibleSpecializations, List<SpecializationData> implementedSpecializations) {
CodeTreeBuilder builder = parent.create();
List<SpecializationData> notImplemented = new ArrayList<>(compatibleSpecializations);
for (SpecializationData specialization : implementedSpecializations) {
notImplemented.remove(specialization);
}
if (notImplemented.isEmpty()) {
throw new AssertionError();
}
List<ExecutableTypeData> compatibleDelegateTypes = filterCompatibleExecutableTypes(type, delegateableTypes);
List<ExecutableTypeData> delegatedDelegateTypes = new ArrayList<>();
CodeTreeBuilder delegateBuilder = builder.create();
boolean elseIf = false;
boolean coversAllSpecializations = false;
if (boxingEliminationEnabled) {
Set<TypeMirror> optimizeTypes = new HashSet<>();
for (SpecializationData specialization : reachableSpecializations) {
TypeMirror returnType = specialization.getReturnType().getType();
if (ElementUtils.isPrimitive(returnType)) {
optimizeTypes.add(returnType);
}
}
for (TypeMirror optimizedType : ElementUtils.uniqueSortedTypes(optimizeTypes, true)) {
ExecutableTypeData delegateType = null;
for (ExecutableTypeData compatibleType : compatibleDelegateTypes) {
if (ElementUtils.typeEquals(compatibleType.getReturnType(), optimizedType)) {
delegateType = compatibleType;
break;
}
}
if (delegateType != null) {
List<SpecializationData> delegateSpecializations = filterImplementedSpecializations(delegateType, filterCompatibleSpecializations(delegateType, reachableSpecializations));
coversAllSpecializations = delegateSpecializations.size() == reachableSpecializations.size();
if (!coversAllSpecializations) {
builder.tree(state.createLoad(frameState, null));
elseIf = delegateBuilder.startIf(elseIf);
List<SpecializationData> allSpecializations = new ArrayList<>();
allSpecializations.add(node.getUninitializedSpecialization());
allSpecializations.addAll(reachableSpecializations);
delegateBuilder.tree(state.createContainsOnly(frameState, 0, -1, delegateSpecializations.toArray(), allSpecializations.toArray())).end();
delegateBuilder.startBlock();
}
delegatedDelegateTypes.add(delegateType);
delegateBuilder.tree(createCallExecute(type, delegateType, frameState));
if (!coversAllSpecializations) {
delegateBuilder.end();
}
if (coversAllSpecializations) {
break;
}
}
}
}
if (!compatibleDelegateTypes.isEmpty() && !coversAllSpecializations) {
ExecutableTypeData delegateType = compatibleDelegateTypes.get(0);
coversAllSpecializations = notImplemented.size() == reachableSpecializations.size();
if (!coversAllSpecializations) {
builder.tree(state.createLoad(frameState, null));
elseIf = delegateBuilder.startIf(elseIf);
delegateBuilder.tree(state.createContains(frameState, notImplemented.toArray())).end();
delegateBuilder.startBlock();
}
delegatedDelegateTypes.add(delegateType);
delegateBuilder.tree(createCallExecute(type, delegateType, frameState));
if (!coversAllSpecializations) {
delegateBuilder.end();
}
}
boolean hasUnexpected = false;
for (ExecutableTypeData delegateType : delegatedDelegateTypes) {
if (needsUnexpectedResultException(delegateType)) {
hasUnexpected = true;
break;
}
}
if (hasUnexpected) {
builder.startTryBlock();
builder.tree(delegateBuilder.build());
builder.end().startCatchBlock(context.getType(UnexpectedResultException.class), "ex");
if (isVoid(type.getReturnType())) {
builder.returnStatement();
} else {
builder.startReturn();
builder.tree(expectOrCast(getType(Object.class), type, CodeTreeBuilder.singleString("ex")));
builder.end();
}
builder.end();
} else {
builder.tree(delegateBuilder.build());
}
return new ExecuteDelegationResult(builder.build(), !coversAllSpecializations);
}
private CodeExecutableElement createExecuteAndSpecialize() {
if (!node.needsRewrites(context)) {
return null;
}
final FrameState frameState = FrameState.load(this);
String frame = null;
if (needsFrame(reachableSpecializations)) {
frame = FRAME_VALUE;
}
TypeMirror returnType = executeAndSpecializeType.getReturnType();
CodeExecutableElement method = frameState.createMethod(modifiers(PRIVATE), returnType, "executeAndSpecialize", frame);
final CodeTreeBuilder builder = method.createBuilder();
builder.declaration(context.getType(Lock.class), "lock", "getLock()");
builder.declaration(context.getType(boolean.class), "hasLock", "true");
builder.statement("lock.lock()");
builder.startTryBlock();
builder.tree(state.createLoad(frameState, node.getUninitializedSpecialization()));
if (requiresExclude()) {
builder.tree(exclude.createLoad(frameState, null));
}
FrameState originalFrameState = frameState.copy();
SpecializationGroup group = createSpecializationGroups();
CodeTree execution = visitSpecializationGroup(builder, group, executeAndSpecializeType, frameState, null, NodeExecutionMode.SLOW_PATH);
builder.tree(execution);
if (group.hasFallthrough()) {
builder.tree(createTransferToInterpreterAndInvalidate());
builder.tree(createThrowUnsupported(builder, originalFrameState));
}
builder.end().startFinallyBlock();
builder.startIf().string("hasLock").end().startBlock();
builder.statement("lock.unlock()");
builder.end();
builder.end();
return method;
}
private CodeTree createThrowUnsupported(final CodeTreeBuilder parent, final FrameState frameState) {
CodeTreeBuilder builder = parent.create();
builder.startThrow().startNew(context.getType(UnsupportedSpecializationException.class));
builder.string("this");
builder.startNewArray(new ArrayCodeTypeMirror(context.getType(Node.class)), null);
List<CodeTree> values = new ArrayList<>();
for (NodeExecutionData execution : node.getChildExecutions()) {
NodeChildData child = execution.getChild();
LocalVariable var = frameState.getValue(execution);
LocalVariable shortCircuit = frameState.getShortCircuit(execution);
if (shortCircuit != null) {
builder.string("null");
values.add(shortCircuit.createReference());
}
if (child != null) {
builder.string("this.", nodeFieldName(execution));
} else {
builder.string("null");
}
if (var != null) {
values.add(var.createReference());
}
}
builder.end();
builder.trees(values.toArray(new CodeTree[0]));
builder.end().end();
return builder.build();
}
private CodeTree createFastPath(CodeTreeBuilder parent, List<SpecializationData> allSpecializations, SpecializationGroup originalGroup, final ExecutableTypeData currentType,
FrameState frameState) {
final CodeTreeBuilder builder = parent.create();
builder.tree(state.createLoad(frameState, null));
int sharedExecutes = 0;
for (NodeExecutionData execution : node.getChildExecutions()) {
boolean canExecuteChild = execution.getIndex() < currentType.getEvaluatedCount();
for (TypeGuard checkedGuard : originalGroup.getTypeGuards()) {
if (checkedGuard.getSignatureIndex() == execution.getIndex()) {
canExecuteChild = true;
break;
}
}
if (!canExecuteChild) {
break;
}
for (TypeGuard checkedGuard : originalGroup.getTypeGuards()) {
// we cannot pull out guards that use optimized implicit source types
if (resolveOptimizedImplicitSourceTypes(execution, checkedGuard.getType()).size() > 1) {
canExecuteChild = false;
break;
}
}
if (!canExecuteChild) {
break;
}
builder.tree(createFastPathExecuteChild(builder, frameState.copy(), frameState, currentType, originalGroup, execution));
sharedExecutes++;
}
List<BoxingSplit> boxingSplits = parameterBoxingElimination(originalGroup, sharedExecutes);
if (boxingSplits.isEmpty()) {
builder.tree(executeFastPathGroup(builder, frameState, currentType, originalGroup, sharedExecutes, null));
addExplodeLoop(builder, originalGroup);
} else {
FrameState originalFrameState = frameState.copy();
boolean elseIf = false;
for (BoxingSplit split : boxingSplits) {
elseIf = builder.startIf(elseIf);
List<SpecializationData> specializations = split.group.collectSpecializations();
List<SpecializationData> containsOnlySpecializations = new ArrayList<>();
containsOnlySpecializations.add(node.getUninitializedSpecialization());
containsOnlySpecializations.addAll(allSpecializations);
List<SpecializationData> maskSpecializations = new ArrayList<>();
maskSpecializations.add(node.getUninitializedSpecialization());
maskSpecializations.addAll(reachableSpecializations);
CodeTree containsOnly = state.createContainsOnly(frameState, 0, -1, specializations.toArray(), maskSpecializations.toArray());
builder.tree(containsOnly);
builder.end().startBlock();
builder.tree(wrapInAMethod(builder, split.group, originalFrameState, split.getName(),
executeFastPathGroup(builder, frameState.copy(), currentType, split.group, sharedExecutes, specializations)));
builder.end();
}
builder.startElseBlock();
builder.tree(wrapInAMethod(builder, originalGroup, originalFrameState, "generic", executeFastPathGroup(builder, frameState, currentType, originalGroup, sharedExecutes, null)));
builder.end();
}
return builder.build();
}
private void addExplodeLoop(final CodeTreeBuilder builder, SpecializationGroup originalGroup) {
for (SpecializationData implemented : originalGroup.collectSpecializations()) {
if (implemented.getMaximumNumberOfInstances() > 1) {
((CodeExecutableElement) builder.findMethod()).getAnnotationMirrors().add(createExplodeLoop());
break;
}
}
}
private CodeTree wrapInAMethod(CodeTreeBuilder parent, SpecializationGroup group, FrameState frameState, String suffix, CodeTree codeTree) {
CodeExecutableElement parentMethod = (CodeExecutableElement) parent.findMethod();
CodeTypeElement parentClass = (CodeTypeElement) parentMethod.getEnclosingElement();
String name = parentMethod.getSimpleName().toString() + "_" + suffix + (boxingSplitIndex++);
CodeExecutableElement method = parentClass.add(
frameState.createMethod(modifiers(Modifier.PRIVATE), parentMethod.getReturnType(), name, FRAME_VALUE,
STATE_VALUE));
CodeTreeBuilder builder = method.createBuilder();
builder.tree(codeTree);
method.getThrownTypes().addAll(parentMethod.getThrownTypes());
addExplodeLoop(builder, group);
CodeTreeBuilder parentBuilder = parent.create();
parentBuilder.startReturn();
parentBuilder.startCall(method.getSimpleName().toString());
frameState.addReferencesTo(parentBuilder, FRAME_VALUE, STATE_VALUE);
parentBuilder.end();
parentBuilder.end();
return parentBuilder.build();
}
private CodeTree executeFastPathGroup(final CodeTreeBuilder parent, FrameState frameState, final ExecutableTypeData currentType, SpecializationGroup group, int sharedExecutes,
List<SpecializationData> allowedSpecializations) {
CodeTreeBuilder builder = parent.create();
FrameState originalFrameState = frameState.copy();
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getIndex() < sharedExecutes) {
// skip shared executes
continue;
}
builder.tree(createFastPathExecuteChild(builder, originalFrameState, frameState, currentType, group, execution));
}
builder.tree(visitSpecializationGroup(builder, group, currentType, frameState, allowedSpecializations, NodeExecutionMode.FAST_PATH));
if (group.hasFallthrough()) {
builder.tree(createTransferToInterpreterAndInvalidate());
builder.tree(createCallExecuteAndSpecialize(currentType, originalFrameState));
}
return builder.build();
}
/*
* It duplicates a group into small subgroups of specializations that don't need boxing when
* executing the children.
*/
private List<BoxingSplit> parameterBoxingElimination(SpecializationGroup group, int evaluatedcount) {
if (!boxingEliminationEnabled) {
return Collections.emptyList();
}
List<SpecializationData> allSpecializations = group.collectSpecializations();
List<Set<TypeGuard>> signatures = new ArrayList<>();
List<List<SpecializationData>> signatureSpecializations = new ArrayList<>();
for (SpecializationData specialization : allSpecializations) {
int index = -1;
List<TypeGuard> guards = new ArrayList<>();
for (Parameter p : specialization.getSignatureParameters()) {
index++;
if (!ElementUtils.isPrimitive(p.getType())) {
continue;
} else if (index < evaluatedcount) {
continue;
} else {
NodeChildData child = p.getSpecification().getExecution().getChild();
if (child != null && child.findExecutableType(p.getType()) == null) {
// type cannot be executed so it cannot be eliminated
continue;
}
}
guards.add(new TypeGuard(p.getType(), index));
}
if (!guards.isEmpty()) {
boolean directFound = false;
for (int i = 0; i < signatures.size(); i++) {
if (guards.containsAll(signatures.get(i))) {
if (signatures.get(i).containsAll(guards)) {
directFound = true;
}
signatureSpecializations.get(i).add(specialization);
}
}
if (!directFound) {
signatures.add(new LinkedHashSet<>(guards));
List<SpecializationData> specializations = new ArrayList<>();
specializations.add(specialization);
signatureSpecializations.add(specializations);
}
}
}
List<BoxingSplit> groups = new ArrayList<>();
for (int i = 0; i < signatureSpecializations.size(); i++) {
List<SpecializationData> groupedSpecialization = signatureSpecializations.get(i);
if (allSpecializations.size() == groupedSpecialization.size()) {
// contains all specializations does not make sense to group
continue;
}
Set<TypeGuard> signature = signatures.get(i);
TypeMirror[] signatureMirrors = new TypeMirror[signature.size()];
int index = 0;
for (TypeGuard typeGuard : signature) {
signatureMirrors[index] = typeGuard.getType();
index++;
}
groups.add(new BoxingSplit(SpecializationGroup.createFlat(groupedSpecialization), signatureMirrors));
}
Collections.sort(groups, new Comparator<BoxingSplit>() {
public int compare(BoxingSplit o1, BoxingSplit o2) {
return Integer.compare(o2.primitiveSignature.length, o1.primitiveSignature.length);
}
});
return groups;
}
private CodeTree createFastPathExecuteChild(final CodeTreeBuilder parent, FrameState originalFrameState, FrameState frameState, final ExecutableTypeData currentType, SpecializationGroup group,
NodeExecutionData execution) {
CodeTreeBuilder builder = parent.create();
LocalVariable var = frameState.getValue(execution);
if (var == null) {
TypeMirror targetType;
TypeGuard eliminatedGuard = null;
if (boxingEliminationEnabled) {
for (TypeGuard checkedGuard : group.getTypeGuards()) {
if (!ElementUtils.isPrimitive(checkedGuard.getType())) {
// no elimination for non primitive types
continue;
} else if (node.getChildExecutions().get(checkedGuard.getSignatureIndex()).getChild().findExecutableType(checkedGuard.getType()) == null) {
// type cannot be executed so it cannot be eliminated
continue;
}
if (checkedGuard.getSignatureIndex() == execution.getIndex()) {
eliminatedGuard = checkedGuard;
break;
}
}
}
if (eliminatedGuard != null) {
// we can optimize the type guard away by executing it
group.getTypeGuards().remove(eliminatedGuard);
targetType = eliminatedGuard.getType();
} else {
targetType = execution.getChild().findAnyGenericExecutableType(context).getReturnType();
}
LocalVariable shortCircuit = resolveShortCircuit(null, execution, frameState);
var = frameState.createValue(execution, targetType).nextName();
LocalVariable fallbackVar;
List<TypeMirror> originalSourceTypes = typeSystem.lookupSourceTypes(targetType);
List<TypeMirror> sourceTypes = resolveOptimizedImplicitSourceTypes(execution, targetType);
if (sourceTypes.size() > 1) {
TypeGuard typeGuard = new TypeGuard(targetType, execution.getIndex());
TypeMirror generic = node.getPolymorphicSpecialization().findParameterOrDie(execution).getType();
fallbackVar = originalFrameState.createValue(execution, generic);
// we want to create the check tree in reverse order
Collections.reverse(sourceTypes);
CodeTree access = var.createReference();
boolean first = true;
for (TypeMirror sType : sourceTypes) {
if (ElementUtils.typeEquals(sType, targetType)) {
continue;
}
String localName = createSourceTypeLocalName(var, sType);
builder.declaration(sType, localName, CodeTreeBuilder.createBuilder().defaultValue(sType).build());
CodeTreeBuilder accessBuilder = builder.create();
accessBuilder.startParantheses();
accessBuilder.tree(state.createContainsOnly(frameState, originalSourceTypes.indexOf(sType), 1, new Object[]{typeGuard},
new Object[]{typeGuard, node.getUninitializedSpecialization()}));
accessBuilder.string(" ? ");
if (ElementUtils.isPrimitive(sType)) {
accessBuilder.string("(").type(generic).string(") ");
}
accessBuilder.string(localName);
accessBuilder.string(" : ");
if (first && ElementUtils.isPrimitive(targetType)) {
accessBuilder.string("(").type(generic).string(") ");
}
accessBuilder.tree(access);
accessBuilder.end();
access = accessBuilder.build();
first = false;
}
fallbackVar = fallbackVar.accessWith(access);
} else {
fallbackVar = var;
}
builder.tree(createAssignExecuteChild(originalFrameState, frameState, builder, execution, currentType, var, shortCircuit));
frameState.setValue(execution, var);
originalFrameState.setValue(execution, fallbackVar);
}
return builder.build();
}
private CodeTree createAssignExecuteChild(FrameState originalFrameState, FrameState frameState, CodeTreeBuilder parent, NodeExecutionData execution, ExecutableTypeData forType,
LocalVariable targetValue,
LocalVariable shortCircuit) {
CodeTreeBuilder builder = parent.create();
ChildExecutionResult executeChild = createExecuteChild(builder, originalFrameState, frameState, execution, targetValue);
builder.tree(createTryExecuteChild(targetValue, executeChild.code, shortCircuit == null, executeChild.throwsUnexpectedResult));
if (shortCircuit != null) {
frameState.setShortCircuitValue(execution, shortCircuit.accessWith(null));
originalFrameState.setShortCircuitValue(execution, shortCircuit.accessWith(null));
}
builder.end();
if (executeChild.throwsUnexpectedResult) {
builder.startCatchBlock(getType(UnexpectedResultException.class), "ex");
FrameState slowPathFrameState = originalFrameState.copy();
slowPathFrameState.setValue(execution, targetValue.makeGeneric(context).accessWith(CodeTreeBuilder.singleString("ex.getResult()")));
ExecutableTypeData delegateType = node.getGenericExecutableType(forType);
boolean found = false;
for (NodeExecutionData otherExecution : node.getChildExecutions()) {
if (found) {
LocalVariable childEvaluatedValue = slowPathFrameState.createValue(otherExecution, genericType);
LocalVariable genericShortCircuit = resolveShortCircuit(null, otherExecution, slowPathFrameState);
builder.tree(createAssignExecuteChild(slowPathFrameState.copy(), slowPathFrameState, builder, otherExecution, delegateType, childEvaluatedValue, genericShortCircuit));
slowPathFrameState.setValue(otherExecution, childEvaluatedValue);
} else {
// skip forward already evaluated
found = execution == otherExecution;
}
}
builder.tree(createCallExecuteAndSpecialize(forType, slowPathFrameState));
builder.end();
}
return createShortCircuit(targetValue, shortCircuit, builder.build());
}
private static String createSourceTypeLocalName(LocalVariable targetValue, TypeMirror sType) {
return targetValue.getName() + ElementUtils.getSimpleName(sType);
}
private ChildExecutionResult createCallSingleChildExecute(NodeExecutionData execution, LocalVariable target, FrameState frameState, ExecutableTypeData executableType) {
CodeTree execute = callChildExecuteMethod(execution, executableType, frameState);
TypeMirror sourceType = executableType.getReturnType();
TypeMirror targetType = target.getTypeMirror();
CodeTree result = expect(sourceType, targetType, execute);
return new ChildExecutionResult(result, executableType.hasUnexpectedValue(context) || needsCastTo(sourceType, targetType));
}
private ChildExecutionResult createExecuteChild(CodeTreeBuilder parent, FrameState originalFrameState, FrameState frameState, NodeExecutionData execution, LocalVariable target) {
ChildExecutionResult result;
if (!typeSystem.hasImplicitSourceTypes(target.getTypeMirror())) {
ExecutableTypeData targetExecutable = resolveTargetExecutable(execution, target.typeMirror);
final CodeTreeBuilder builder = parent.create();
result = createCallSingleChildExecute(execution, target, frameState, targetExecutable);
builder.string(target.getName()).string(" = ");
builder.tree(result.code);
result.code = builder.build();
} else {
result = createExecuteChildImplicitCast(parent.create(), originalFrameState, frameState, execution, target);
}
return result;
}
// old code
private CodeExecutableElement createNodeConstructor(CodeTypeElement clazz, ExecutableElement superConstructor) {
CodeExecutableElement constructor = GeneratorUtils.createConstructorUsingFields(modifiers(), clazz, superConstructor);
ElementUtils.setVisibility(constructor.getModifiers(), ElementUtils.getVisibility(superConstructor.getModifiers()));
List<CodeVariableElement> childParameters = new ArrayList<>();
for (NodeChildData child : node.getChildren()) {
childParameters.add(new CodeVariableElement(child.getOriginalType(), child.getName()));
}
constructor.getParameters().addAll(superConstructor.getParameters().size(), childParameters);
CodeTreeBuilder builder = constructor.appendBuilder();
List<String> childValues = new ArrayList<>(node.getChildren().size());
if (!node.getChildExecutions().isEmpty()) {
for (NodeChildData child : node.getChildren()) {
String name = child.getName();
if (child.getCardinality().isMany()) {
CreateCastData createCast = node.findCast(child.getName());
if (createCast != null) {
CodeTree nameTree = CodeTreeBuilder.singleString(name);
CodeTreeBuilder callBuilder = builder.create();
callBuilder.string(name).string(" != null ? ");
callBuilder.tree(callMethod(null, createCast.getMethod(), nameTree));
callBuilder.string(" : null");
name += "_";
builder.declaration(child.getNodeType(), name, callBuilder.build());
}
}
childValues.add(name);
}
}
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getChild() == null) {
continue;
}
CreateCastData createCast = node.findCast(execution.getChild().getName());
builder.startStatement();
builder.string("this.").string(nodeFieldName(execution)).string(" = ");
String name = childValues.get(node.getChildren().indexOf(execution.getChild()));
CodeTreeBuilder accessorBuilder = builder.create();
accessorBuilder.string(name);
if (execution.isIndexed()) {
accessorBuilder.string("[").string(String.valueOf(execution.getChildIndex())).string("]");
}
CodeTree accessor = accessorBuilder.build();
if (createCast != null && execution.getChild().getCardinality().isOne()) {
accessor = callMethod(null, createCast.getMethod(), accessor);
}
if (execution.isIndexed()) {
CodeTreeBuilder nullCheck = builder.create();
nullCheck.string(name).string(" != null && ").string(String.valueOf(execution.getChildIndex())).string(" < ").string(name).string(".length").string(" ? ");
nullCheck.tree(accessor);
nullCheck.string(" : null");
accessor = nullCheck.build();
}
builder.tree(accessor);
builder.end();
}
return constructor;
}
private List<ExecutableTypeData> filterExecutableTypes(List<ExecutableTypeData> executableTypes, List<SpecializationData> specializations) {
Set<TypeMirror> specializedReturnTypes = new HashSet<>();
for (SpecializationData specialization : specializations) {
specializedReturnTypes.add(specialization.getReturnType().getType());
}
List<ExecutableTypeData> filteredTypes = new ArrayList<>();
outer: for (ExecutableTypeData executable : executableTypes) {
if (executable.getMethod() == null) {
continue;
}
if (executable.isAbstract()) {
filteredTypes.add(executable);
continue;
}
if (executable.isFinal()) {
// no way to implement that
continue;
}
if (!executable.hasUnexpectedValue(context)) {
filteredTypes.add(executable);
continue;
} else {
TypeMirror returnType = executable.getReturnType();
if (boxingEliminationEnabled && (isVoid(returnType) || ElementUtils.isPrimitive(returnType))) {
for (TypeMirror specializedReturnType : specializedReturnTypes) {
if (isSubtypeBoxed(context, specializedReturnType, returnType)) {
filteredTypes.add(executable);
continue outer;
}
}
}
}
}
Collections.sort(filteredTypes);
return filteredTypes;
}
private Element createGetCostMethod() {
TypeMirror returnType = getType(NodeCost.class);
CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), returnType, "getCost");
executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class)));
CodeTreeBuilder builder = executable.createBuilder();
FrameState frameState = FrameState.load(this);
builder.tree(state.createLoad(frameState, node.getUninitializedSpecialization()));
if (node.needsRewrites(context)) {
builder.startIf().tree(state.createIs(frameState, new Object[]{})).end();
builder.startBlock();
builder.startReturn().staticReference(getType(NodeCost.class), "UNINITIALIZED").end();
builder.end();
if (reachableSpecializations.size() == 1 && !reachableSpecializations.iterator().next().hasMultipleInstances()) {
builder.startElseBlock();
builder.startReturn().staticReference(getType(NodeCost.class), "MONOMORPHIC").end();
builder.end();
} else {
builder.startElseIf();
builder.tree(state.createIsOneBitOf(frameState, reachableSpecializations.toArray()));
builder.end();
builder.startBlock();
List<CodeTree> additionalChecks = new ArrayList<>();
for (SpecializationData specialization : reachableSpecializations) {
if (useSpecializationClass(specialization) && specialization.getMaximumNumberOfInstances() > 1) {
String typeName = createSpecializationTypeName(specialization);
String fieldName = createSpecializationFieldName(specialization);
String localName = createSpecializationLocalName(specialization);
builder.declaration(typeName, localName, "this." + fieldName);
CodeTree check = builder.create().startParantheses().string(localName, " == null || ",
localName, ".next_ == null").end().build();
additionalChecks.add(check);
}
}
if (!additionalChecks.isEmpty()) {
builder.startIf().tree(combineTrees(" && ", additionalChecks.toArray(new CodeTree[0]))).end().startBlock();
}
builder.startReturn().staticReference(getType(NodeCost.class), "MONOMORPHIC").end();
if (!additionalChecks.isEmpty()) {
builder.end();
}
builder.end();
builder.startReturn().staticReference(getType(NodeCost.class), "POLYMORPHIC").end();
}
} else {
builder.startReturn().staticReference(getType(NodeCost.class), "MONOMORPHIC").end();
}
return executable;
}
private ExecutableElement createAccessChildMethod(NodeChildData child) {
if (child.getAccessElement() != null && child.getAccessElement().getModifiers().contains(Modifier.ABSTRACT)) {
ExecutableElement getter = (ExecutableElement) child.getAccessElement();
CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), getter);
method.getModifiers().remove(Modifier.ABSTRACT);
List<NodeExecutionData> executions = new ArrayList<>();
for (NodeExecutionData execution : node.getChildExecutions()) {
if (execution.getChild() == child) {
executions.add(execution);
}
}
CodeTreeBuilder builder = method.createBuilder();
if (child.getCardinality().isMany()) {
builder.startReturn().startNewArray((ArrayType) child.getOriginalType(), null);
for (NodeExecutionData execution : executions) {
builder.string(nodeFieldName(execution));
}
builder.end().end();
} else {
for (NodeExecutionData execution : executions) {
builder.startReturn().string("this.").string(nodeFieldName(execution)).end();
break;
}
}
return method;
}
return null;
}
private List<SpecializationData> calculateReachableSpecializations() {
List<SpecializationData> specializations = new ArrayList<>();
for (SpecializationData specialization : node.getSpecializations()) {
if (specialization.isReachable() && //
(specialization.isSpecialized() //
|| (specialization.isFallback() && specialization.getMethod() != null))) {
specializations.add(specialization);
}
}
return specializations;
}
private TypeMirror getType(Class<?> clazz) {
return context.getType(clazz);
}
private static CodeVariableElement createNodeField(Modifier visibility, TypeMirror type, String name, Class<?> annotationType, Modifier... modifiers) {
CodeVariableElement childField = new CodeVariableElement(modifiers(modifiers), type, name);
if (annotationType != null) {
childField.getAnnotationMirrors().add(new CodeAnnotationMirror(ProcessorContext.getInstance().getDeclaredType(annotationType)));
}
setVisibility(childField.getModifiers(), visibility);
return childField;
}
private static CodeTree callMethod(CodeTree receiver, ExecutableElement method, CodeTree... boundValues) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
if (method.getModifiers().contains(STATIC)) {
builder.startStaticCall(method.getEnclosingElement().asType(), method.getSimpleName().toString());
} else {
builder.startCall(receiver, method.getSimpleName().toString());
}
int index = -1;
for (VariableElement parameter : method.getParameters()) {
index++;
if (index < boundValues.length) {
CodeTree tree = boundValues[index];
if (tree != null) {
builder.tree(tree);
continue;
}
}
builder.defaultValue(parameter.asType());
}
builder.end();
return builder.build();
}
private CodeTree[] bindExecuteMethodParameters(NodeExecutionData execution, ExecutableTypeData method, FrameState frameState) {
List<NodeExecutionData> executeWith = execution != null ? execution.getChild().getExecuteWith() : null;
List<CodeTree> values = new ArrayList<>();
if (method.getFrameParameter() != null) {
LocalVariable frameLocal = frameState.get(FRAME_VALUE);
if (frameLocal == null) {
values.add(CodeTreeBuilder.singleString("null"));
} else {
values.add(createTypeSafeReference(frameLocal, method.getFrameParameter()));
}
}
int evaluatedIndex = 0;
for (int executionIndex = 0; executionIndex < node.getExecutionCount(); executionIndex++) {
NodeExecutionData parameterExecution;
if (executeWith != null && executionIndex < executeWith.size()) {
parameterExecution = executeWith.get(executionIndex);
} else {
parameterExecution = node.getChildExecutions().get(executionIndex);
}
if (parameterExecution.isShortCircuit()) {
if (evaluatedIndex < method.getEvaluatedCount()) {
TypeMirror targetType = method.getEvaluatedParameters().get(evaluatedIndex);
LocalVariable shortCircuit = frameState.getShortCircuit(parameterExecution);
if (shortCircuit != null) {
values.add(createTypeSafeReference(shortCircuit, targetType));
} else {
values.add(CodeTreeBuilder.createBuilder().defaultValue(targetType).build());
}
evaluatedIndex++;
}
}
if (evaluatedIndex < method.getEvaluatedCount()) {
TypeMirror targetType = method.getEvaluatedParameters().get(evaluatedIndex);
LocalVariable value = frameState.getValue(parameterExecution);
if (value != null) {
values.add(createTypeSafeReference(value, targetType));
} else {
values.add(CodeTreeBuilder.createBuilder().defaultValue(targetType).build());
}
evaluatedIndex++;
}
}
return values.toArray(new CodeTree[values.size()]);
}
private CodeTree callChildExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, FrameState frameState) {
return callMethod(CodeTreeBuilder.singleString(nodeFieldName(execution)), method.getMethod(), bindExecuteMethodParameters(execution, method, frameState));
}
private CodeTree callTemplateMethod(CodeTree receiver, TemplateMethod method, FrameState frameState) {
CodeTree[] bindings = new CodeTree[method.getParameters().size()];
int signatureIndex = 0;
for (int i = 0; i < bindings.length; i++) {
Parameter parameter = method.getParameters().get(i);
if (parameter.getSpecification().isCached() && method instanceof SpecializationData) {
bindings[i] = createCacheReference((SpecializationData) method, parameter);
} else {
LocalVariable var = frameState.get(parameter, signatureIndex);
if (var == null) {
var = frameState.get(parameter.getLocalName());
}
if (var != null) {
bindings[i] = createTypeSafeReference(var, parameter.getType());
}
}
if (parameter.getSpecification().isSignature()) {
signatureIndex++;
}
}
return callMethod(receiver, method.getMethod(), bindings);
}
private CodeTree createTypeSafeReference(LocalVariable var, TypeMirror targetType) {
CodeTree valueReference = var.createReference();
TypeMirror sourceType = var.getTypeMirror();
if (targetType == null || sourceType == null) {
return valueReference;
}
if (needsCastTo(sourceType, targetType)) {
valueReference = TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference);
}
return valueReference;
}
private SpecializationGroup createSpecializationGroups() {
return SpecializationGroup.createFlat(reachableSpecializations);
}
private CodeTree expectOrCast(TypeMirror sourceType, ExecutableTypeData targetType, CodeTree content) {
if (needsUnexpectedResultException(targetType)) {
return expect(sourceType, targetType.getReturnType(), content);
} else {
return cast(sourceType, targetType.getReturnType(), content);
}
}
private CodeTree cast(TypeMirror sourceType, TypeMirror targetType, CodeTree content) {
if (ElementUtils.needsCastTo(sourceType, targetType) && !isVoid(sourceType)) {
return TypeSystemCodeGenerator.cast(typeSystem, targetType, content);
} else {
return content;
}
}
private CodeTree expect(TypeMirror sourceType, TypeMirror forType, CodeTree tree) {
if (sourceType == null || ElementUtils.needsCastTo(sourceType, forType)) {
expectedTypes.add(forType);
return TypeSystemCodeGenerator.expect(typeSystem, forType, tree);
}
return tree;
}
private CodeExecutableElement createExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, FrameState frameState, boolean originalOverride) {
TypeMirror returnType = executedType.getReturnType();
if (specialization != null) {
frameState.loadFastPathState(specialization);
}
String methodName;
if (originalOverride && executedType.getMethod() != null) {
methodName = executedType.getMethod().getSimpleName().toString();
} else {
methodName = executedType.getUniqueName();
}
CodeExecutableElement executable;
if (originalOverride && executedType.getMethod() != null) {
executable = CodeExecutableElement.clone(context.getEnvironment(), executedType.getMethod());
executable.getAnnotationMirrors().clear();
executable.getModifiers().remove(ABSTRACT);
for (VariableElement var : executable.getParameters()) {
((CodeVariableElement) var).getAnnotationMirrors().clear();
}
if (executedType.getFrameParameter() != null) {
((CodeVariableElement) executable.getParameters().get(0)).setName(FRAME_VALUE);
}
if (executable.isVarArgs()) {
((CodeVariableElement) executable.getParameters().get(executable.getParameters().size() - 1)).setName(VARARGS_NAME);
}
renameOriginalParameters(executedType, executable, frameState);
} else {
executable = frameState.createMethod(modifiers(PUBLIC), returnType, methodName, FRAME_VALUE);
}
executable.getThrownTypes().clear();
if (needsUnexpectedResultException(executedType)) {
executable.getThrownTypes().add(context.getDeclaredType(UnexpectedResultException.class));
}
return executable;
}
private void renameOriginalParameters(ExecutableTypeData executedType, CodeExecutableElement executable, FrameState frameState) {
// rename varargs parameter
int evaluatedIndex = 0;
for (int executionIndex = 0; executionIndex < node.getExecutionCount(); executionIndex++) {
NodeExecutionData execution = node.getChildExecutions().get(executionIndex);
if (execution.isShortCircuit()) {
if (evaluatedIndex < executedType.getEvaluatedCount()) {
TypeMirror evaluatedType = executedType.getEvaluatedParameters().get(evaluatedIndex);
LocalVariable shortCircuit = frameState.getShortCircuit(execution);
if (shortCircuit != null) {
frameState.setShortCircuitValue(execution, renameExecutableTypeParameter(executable, executedType, evaluatedIndex, evaluatedType, shortCircuit));
}
evaluatedIndex++;
}
}
if (evaluatedIndex < executedType.getEvaluatedCount()) {
TypeMirror evaluatedType = executedType.getEvaluatedParameters().get(evaluatedIndex);
LocalVariable value = frameState.getValue(execution);
if (value != null) {
frameState.setValue(execution, renameExecutableTypeParameter(executable, executedType, evaluatedIndex, evaluatedType, value));
}
evaluatedIndex++;
}
}
}
private static LocalVariable renameExecutableTypeParameter(CodeExecutableElement method, ExecutableTypeData executedType, int evaluatedIndex, TypeMirror targetType, LocalVariable var) {
int parameterIndex = executedType.getParameterIndex(evaluatedIndex);
int varArgsIndex = executedType.getVarArgsIndex(parameterIndex);
LocalVariable returnVar = var;
if (varArgsIndex >= 0) {
returnVar = returnVar.accessWith(CodeTreeBuilder.singleString(VARARGS_NAME + "[" + varArgsIndex + "]"));
} else {
((CodeVariableElement) method.getParameters().get(parameterIndex)).setName(returnVar.getName());
}
if (!isObject(targetType)) {
returnVar = returnVar.newType(targetType);
}
return returnVar;
}
private boolean needsUnexpectedResultException(ExecutableTypeData executedType) {
if (!executedType.hasUnexpectedValue(context)) {
return false;
}
if (isSubtypeBoxed(context, executeAndSpecializeType.getReturnType(), executedType.getReturnType())) {
return false;
} else {
return true;
}
}
private LocalVariable resolveShortCircuit(SpecializationData specialization, NodeExecutionData execution, FrameState frameState) {
LocalVariable shortCircuit = null;
if (execution.isShortCircuit()) {
shortCircuit = frameState.getShortCircuit(execution);
if (shortCircuit == null) {
SpecializationData resolvedSpecialization = specialization;
if (specialization == null) {
resolvedSpecialization = node.getGenericSpecialization();
}
ShortCircuitData shortCircuitData = resolvedSpecialization.getShortCircuits().get(calculateShortCircuitIndex(execution));
CodeTree access = callTemplateMethod(null, shortCircuitData, frameState);
shortCircuit = frameState.createShortCircuitValue(execution).accessWith(access);
} else {
CodeTree access = shortCircuit.createReference();
shortCircuit = shortCircuit.nextName().accessWith(access);
}
}
return shortCircuit;
}
private int calculateShortCircuitIndex(NodeExecutionData execution) {
int shortCircuitIndex = 0;
for (NodeExecutionData otherExectuion : node.getChildExecutions()) {
if (otherExectuion.isShortCircuit()) {
if (otherExectuion == execution) {
break;
}
shortCircuitIndex++;
}
}
return shortCircuitIndex;
}
private CodeTree createFastPathExecute(CodeTreeBuilder parent, final ExecutableTypeData forType, SpecializationData specialization, FrameState frameState) {
CodeTreeBuilder builder = parent.create();
int ifCount = 0;
if (specialization.isFallback()) {
builder.startIf().startCall(METHOD_FALLBACK_GUARD);
if (node.isFrameUsedByAnyGuard()) {
if (frameState.get(FRAME_VALUE) != null) {
builder.string(FRAME_VALUE);
} else {
builder.nullLiteral();
}
}
if (fallbackNeedsState) {
builder.string(STATE_VALUE);
}
frameState.addReferencesTo(builder);
builder.end();
builder.end();
builder.startBlock();
ifCount++;
}
builder.tree(createExecute(builder, frameState, forType, specialization, NodeExecutionMode.FAST_PATH));
builder.end(ifCount);
return builder.build();
}
private CodeTree createExecute(CodeTreeBuilder parent, FrameState frameState, final ExecutableTypeData forType, SpecializationData specialization, NodeExecutionMode mode) {
CodeTreeBuilder builder = parent.create();
if (mode.isSlowPath()) {
builder.statement("lock.unlock()");
builder.statement("hasLock = false");
}
if (specialization.getMethod() == null) {
builder.tree(createThrowUnsupported(builder, frameState));
} else {
if (isVoid(specialization.getMethod().getReturnType())) {
builder.statement(callTemplateMethod(null, specialization, frameState));
if (isVoid(forType.getReturnType())) {
builder.returnStatement();
} else {
builder.startReturn().defaultValue(forType.getReturnType()).end();
}
} else {
builder.startReturn();
builder.tree(expectOrCast(specialization.getReturnType().getType(), forType, callTemplateMethod(null, specialization, frameState)));
builder.end();
}
}
return createCatchRewriteException(builder, specialization, forType, frameState, builder.build(), mode);
}
private static class IfTriple {
private CodeTree prepare;
private CodeTree condition;
private CodeTree statements;
IfTriple(CodeTree prepare, CodeTree condition, CodeTree statements) {
this.prepare = prepare;
this.condition = condition;
this.statements = statements;
}
private boolean canBeMerged(IfTriple triple) {
boolean prepareSet = !isEmpty(triple.prepare) || !isEmpty(prepare);
boolean conditionSet = !isEmpty(triple.condition) || !isEmpty(condition);
boolean statementsSet = !isEmpty(triple.statements) || !isEmpty(statements);
return conditionSet ^ (prepareSet || statementsSet);
}
private static boolean isEmpty(CodeTree e) {
return e == null || e.isEmpty();
}
@Override
public String toString() {
CodeTreeBuilder b = CodeTreeBuilder.createBuilder();
b.startGroup();
if (!isEmpty(prepare)) {
b.tree(prepare);
}
if (!isEmpty(condition)) {
b.startIf().tree(condition).end().startBlock();
}
if (!isEmpty(statements)) {
b.tree(statements);
}
if (!isEmpty(condition)) {
b.end();
}
b.end();
return b.build().toString();
}
private static IfTriple merge(String conditionSep, Set<IfTriple> triples) {
if (triples.isEmpty()) {
throw new AssertionError();
}
if (triples.size() == 1) {
return triples.iterator().next();
}
CodeTree[] prepareTrees = new CodeTree[triples.size()];
CodeTree[] conditionTrees = new CodeTree[triples.size()];
CodeTree[] statementTrees = new CodeTree[triples.size()];
int index = 0;
for (IfTriple triple : triples) {
prepareTrees[index] = triple.prepare;
conditionTrees[index] = triple.condition;
statementTrees[index] = triple.statements;
index++;
}
return new IfTriple(combineTrees(null, prepareTrees),
combineTrees(conditionSep, conditionTrees),
combineTrees(null, statementTrees));
}
public static List<IfTriple> optimize(List<IfTriple> triples) {
List<IfTriple> newTriples = new ArrayList<>();
Set<IfTriple> mergable = new LinkedHashSet<>();
IfTriple prev = null;
for (IfTriple triple : triples) {
if (prev != null) {
if (prev.canBeMerged(triple)) {
mergable.add(triple);
} else {
newTriples.add(merge(" && ", mergable));
mergable.clear();
}
}
prev = triple;
mergable.add(prev);
}
if (prev != null) {
newTriples.add(merge(" && ", mergable));
}
return newTriples;
}
public static int materialize(CodeTreeBuilder builder, List<IfTriple> triples) {
int blockCount = 0;
boolean otherPrepare = false;
for (IfTriple triple : triples) {
if (triple.prepare != null && !triple.prepare.isEmpty()) {
if (!otherPrepare) {
if (blockCount == 0) {
builder.startBlock();
blockCount++;
}
otherPrepare = true;
}
builder.tree(triple.prepare);
}
if (triple.condition != null && !triple.condition.isEmpty()) {
builder.startIf().tree(triple.condition).end().startBlock();
blockCount++;
}
if (triple.statements != null && !triple.statements.isEmpty()) {
builder.tree(triple.statements);
}
}
return blockCount;
}
}
private CodeTree visitSpecializationGroup(CodeTreeBuilder parent, SpecializationGroup group, ExecutableTypeData forType, FrameState frameState, List<SpecializationData> allowedSpecializations,
NodeExecutionMode mode) {
CodeTreeBuilder builder = parent.create();
boolean hasFallthrough = false;
boolean hasImplicitCast = false;
List<IfTriple> cachedTriples = new ArrayList<>();
for (TypeGuard guard : group.getTypeGuards()) {
IfTriple triple = createTypeCheckOrCast(frameState, group, guard, mode, false, true);
if (triple != null) {
cachedTriples.add(triple);
}
hasImplicitCast = hasImplicitCast || node.getTypeSystem().hasImplicitSourceTypes(guard.getType());
if (!mode.isGuardFallback()) {
triple = createTypeCheckOrCast(frameState, group, guard, mode, true, true);
if (triple != null) {
cachedTriples.add(triple);
}
}
}
SpecializationData specialization = group.getSpecialization();
SpecializationData[] specializations = group.collectSpecializations().toArray(new SpecializationData[0]);
List<GuardExpression> guardExpressions = new ArrayList<>(group.getGuards());
// for specializations with multiple instances we can move certain guards
// out of the loop.
if (specialization != null && specialization.hasMultipleInstances()) {
List<GuardExpression> unboundGuards = new ArrayList<>();
for (GuardExpression guard : guardExpressions) {
if (!specialization.isGuardBoundWithCache(guard)) {
unboundGuards.add(guard);
} else {
// we need to stop as we need to ensure guard execution order
break;
}
}
cachedTriples.addAll(createMethodGuardCheck(frameState, group, unboundGuards, mode));
guardExpressions.removeAll(unboundGuards);
}
boolean useSpecializationClass = specialization != null && useSpecializationClass(specialization);
if (mode.isFastPath()) {
int ifCount = 0;
final boolean stateGuaranteed = group.isLast() && allowedSpecializations != null && allowedSpecializations.size() == 1 &&
group.getAllSpecializations().size() == allowedSpecializations.size();
if ((!group.isEmpty() || specialization != null)) {
CodeTree stateCheck = state.createContains(frameState, specializations);
CodeTree stateGuard = null;
CodeTree assertCheck = null;
if (stateGuaranteed) {
assertCheck = CodeTreeBuilder.createBuilder().startAssert().tree(stateCheck).end().build();
} else {
stateGuard = stateCheck;
}
cachedTriples.add(0, new IfTriple(null, stateGuard, assertCheck));
}
ifCount += IfTriple.materialize(builder, IfTriple.optimize(cachedTriples));
cachedTriples = new ArrayList<>(); // reset current triples
String specializationLocalName = null;
if (useSpecializationClass) {
specializationLocalName = createSpecializationLocalName(specialization);
builder.declaration(createSpecializationTypeName(specialization), specializationLocalName, CodeTreeBuilder.singleString(createSpecializationFieldName(specialization)));
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.startWhile();
} else {
builder.startIf();
}
builder.string(specializationLocalName, " != null");
builder.end();
builder.startBlock();
ifCount++;
}
if (specialization != null) {
if (!specialization.getAssumptionExpressions().isEmpty()) {
builder.tree(createFastPathAssumptionCheck(builder, specialization, forType, frameState));
}
}
cachedTriples = createMethodGuardCheck(frameState, group, guardExpressions, mode);
int innerIfCount = IfTriple.materialize(builder, IfTriple.optimize(cachedTriples));
SpecializationGroup prev = null;
for (SpecializationGroup child : group.getChildren()) {
if (prev != null && !prev.hasFallthrough()) {
break;
}
builder.tree(visitSpecializationGroup(builder, child, forType, frameState.copy(), allowedSpecializations, mode));
}
if (specialization != null && (prev == null || prev.hasFallthrough())) {
builder.tree(createFastPathExecute(builder, forType, specialization, frameState));
}
builder.end(innerIfCount);
hasFallthrough |= innerIfCount > 0;
if (useSpecializationClass && specialization.getMaximumNumberOfInstances() > 1) {
String name = createSpecializationLocalName(specialization);
builder.startStatement().string(name, " = ", name, ".next_").end();
}
builder.end(ifCount);
hasFallthrough |= ifCount > 0;
} else if (mode.isSlowPath()) {
if (specialization != null && mayBeExcluded(specialization)) {
CodeTree excludeCheck = exclude.createNotContains(frameState, specializations);
cachedTriples.add(0, new IfTriple(null, excludeCheck, null));
}
int outerIfCount = 0;
if (specialization == null) {
cachedTriples.addAll(createMethodGuardCheck(frameState, group, guardExpressions, mode));
outerIfCount += IfTriple.materialize(builder, IfTriple.optimize(cachedTriples));
SpecializationGroup prev = null;
for (SpecializationGroup child : group.getChildren()) {
if (prev != null && !prev.hasFallthrough()) {
break;
}
builder.tree(visitSpecializationGroup(builder, child, forType, frameState.copy(), allowedSpecializations, mode));
prev = child;
}
} else {
outerIfCount += IfTriple.materialize(builder, IfTriple.optimize(cachedTriples));
String countName = specialization != null ? "count" + specialization.getIndex() + "_" : null;
boolean needsDuplicationCheck = specialization.hasMultipleInstances();
String duplicateFoundName = specialization.getId() + "_duplicateFound_";
int innerIfCount = 0;
String specializationLocalName = createSpecializationLocalName(specialization);
if (needsDuplicationCheck) {
builder.tree(createDuplicationCheck(builder, frameState, group, guardExpressions, useSpecializationClass, countName, duplicateFoundName,
specializationLocalName));
builder.startIf();
if (useSpecializationClass) {
// we reuse the specialization class local name instead of a duplicate found
// name
builder.string(createSpecializationLocalName(specialization), " == null");
} else {
builder.string("!", duplicateFoundName);
}
builder.end().startBlock();
innerIfCount++;
}
List<IfTriple> innerTripples = createMethodGuardCheck(frameState, group, guardExpressions, mode);
List<AssumptionExpression> assumptions = specialization.getAssumptionExpressions();
if (!assumptions.isEmpty()) {
for (AssumptionExpression assumption : assumptions) {
innerTripples.addAll(createAssumptionSlowPathTriples(frameState, group, assumption));
}
}
if (specialization.hasMultipleInstances()) {
DSLExpression limit = specialization.getLimitExpression();
CodeTree limitExpression = DSLExpressionGenerator.write(limit, null, castBoundTypes(bindExpressionValues(frameState, limit, specialization)));
CodeTree limitCondition = CodeTreeBuilder.createBuilder().string(countName).string(" < ").tree(limitExpression).build();
innerTripples.add(new IfTriple(null, limitCondition, null));
}
innerIfCount += IfTriple.materialize(builder, IfTriple.optimize(innerTripples));
builder.tree(createSpecialize(builder, frameState, specialization, useSpecializationClass, needsDuplicationCheck));
if (needsDuplicationCheck) {
hasFallthrough = true;
if (!useSpecializationClass) {
builder.startStatement().string(duplicateFoundName, " = true").end();
}
builder.end(innerIfCount);
// need to ensure that we update the implicit cast specializations on duplicates
CodeTree updateImplicitCast = createUpdateImplicitCastState(builder, frameState, specialization);
if (updateImplicitCast != null) {
builder.startElseBlock();
builder.tree(createUpdateImplicitCastState(builder, frameState, specialization));
builder.tree(state.createSet(frameState, new SpecializationData[]{specialization}, true, true));
builder.end();
}
builder.startIf();
if (useSpecializationClass) {
builder.string(createSpecializationLocalName(specialization), " != null");
} else {
builder.string(duplicateFoundName);
}
builder.end().startBlock();
builder.tree(createExecute(builder, frameState, executeAndSpecializeType, specialization, mode));
builder.end();
} else {
builder.tree(createExecute(builder, frameState, executeAndSpecializeType, specialization, mode));
builder.end(innerIfCount);
hasFallthrough |= innerIfCount > 0;
}
}
builder.end(outerIfCount);
hasFallthrough |= outerIfCount > 0;
} else if (mode.isGuardFallback()) {
int ifCount = 0;
String specializationLocalName = null;
boolean useClass = useSpecializationClass && specialization.getMaximumNumberOfInstances() > 1;
if (useClass) {
specializationLocalName = createSpecializationLocalName(specialization);
ifCount += IfTriple.materialize(builder, IfTriple.optimize(cachedTriples));
cachedTriples.clear();
builder.declaration(createSpecializationTypeName(specialization), specializationLocalName,
CodeTreeBuilder.singleString(createSpecializationFieldName(specialization)));
builder.startWhile();
builder.string(specializationLocalName, " != null");
builder.end();
builder.startBlock();
hasFallthrough = true;
}
int innerIfCount = 0;
cachedTriples.addAll(createMethodGuardCheck(frameState, group, guardExpressions, mode));
cachedTriples.addAll(createAssumptionCheckTriples(specialization));
cachedTriples = IfTriple.optimize(cachedTriples);
if (!useClass && specialization != null && !hasImplicitCast) {
IfTriple singleCondition = null;
if (cachedTriples.size() == 1) {
singleCondition = cachedTriples.get(0);
}
if (singleCondition != null) {
int index = cachedTriples.indexOf(singleCondition);
CodeTree stateCheck = state.createNotContains(frameState, specializations);
cachedTriples.set(index, new IfTriple(singleCondition.prepare, combineTrees(" && ", stateCheck, singleCondition.condition), singleCondition.statements));
fallbackNeedsState = true;
}
}
innerIfCount += IfTriple.materialize(builder, cachedTriples);
SpecializationGroup prev = null;
for (SpecializationGroup child : group.getChildren()) {
if (prev != null && !prev.hasFallthrough()) {
break;
}
builder.tree(visitSpecializationGroup(builder, child, forType, frameState.copy(), allowedSpecializations, mode));
prev = child;
}
if (specialization != null) {
builder.returnFalse();
}
builder.end(innerIfCount);
if (useClass) {
builder.startStatement().string(specializationLocalName, " = ", specializationLocalName, ".next_").end();
builder.end();
}
builder.end(ifCount);
hasFallthrough |= ifCount > 0 || innerIfCount > 0;
} else {
throw new AssertionError("unexpected path");
}
group.setFallthrough(hasFallthrough);
return builder.build();
}
private List<IfTriple> createAssumptionCheckTriples(SpecializationData specialization) {
if (specialization == null || specialization.getAssumptionExpressions().isEmpty()) {
return Collections.emptyList();
}
List<IfTriple> triples = new ArrayList<>();
List<AssumptionExpression> assumptions = specialization.getAssumptionExpressions();
for (AssumptionExpression assumption : assumptions) {
CodeTree assumptionGuard = createAssumptionGuard(assumption, createAssumptionReference(specialization, assumption));
triples.add(new IfTriple(null, assumptionGuard, null));
}
return triples;
}
private List<IfTriple> createAssumptionSlowPathTriples(FrameState frameState, SpecializationGroup group, AssumptionExpression assumption) throws AssertionError {
List<IfTriple> triples = new ArrayList<>();
LocalVariable var = frameState.get(assumption.getId());
CodeTree declaration = null;
if (var == null) {
triples.addAll(initializeCaches(frameState, group, group.getSpecialization().getBoundCaches(assumption.getExpression()), NodeExecutionMode.SLOW_PATH));
CodeTree assumptionExpressions = DSLExpressionGenerator.write(assumption.getExpression(), null,
castBoundTypes(bindExpressionValues(frameState, assumption.getExpression(), group.getSpecialization())));
String name = createAssumptionFieldName(group.getSpecialization(), assumption);
var = new LocalVariable(assumption.getExpression().getResolvedType(), name.substring(0, name.length() - 1), null);
frameState.set(assumption.getId(), var);
declaration = var.createDeclaration(assumptionExpressions);
}
triples.add(new IfTriple(declaration, createAssumptionGuard(assumption, var.createReference()), null));
return triples;
}
private CodeTree createDuplicationCheck(CodeTreeBuilder parent, FrameState frameState, SpecializationGroup group, List<GuardExpression> guardExpressions,
boolean useSpecializationClass, String countName, String duplicateFoundName, String specializationLocalName) {
SpecializationData specialization = group.getSpecialization();
CodeTreeBuilder builder = parent.create();
builder.declaration("int", countName, CodeTreeBuilder.singleString("0"));
if (useSpecializationClass) {
builder.declaration(createSpecializationTypeName(specialization), specializationLocalName,
CodeTreeBuilder.singleString(createSpecializationFieldName(specialization)));
} else {
builder.declaration("boolean", duplicateFoundName, CodeTreeBuilder.singleString("false"));
}
builder.startIf().tree(state.createContains(frameState, new Object[]{specialization})).end().startBlock();
if (useSpecializationClass) {
builder.startWhile().string(specializationLocalName, " != null").end().startBlock();
}
List<IfTriple> duplicationtriples = new ArrayList<>();
duplicationtriples.addAll(createMethodGuardCheck(frameState, group, guardExpressions, NodeExecutionMode.FAST_PATH));
duplicationtriples.addAll(createAssumptionCheckTriples(specialization));
int duplicationIfCount = IfTriple.materialize(builder, IfTriple.optimize(duplicationtriples));
if (useSpecializationClass) {
builder.statement("break");
} else {
builder.startStatement().string(duplicateFoundName, " = true").end();
}
builder.end(duplicationIfCount);
if (useSpecializationClass) {
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.startStatement().string(specializationLocalName, " = ", specializationLocalName, ".next_").end();
} else {
builder.statement(specializationLocalName + " = null");
}
builder.statement(countName + "++");
builder.end();
} else {
builder.statement(countName + "++");
}
builder.end();
return builder.build();
}
private CodeTree createSpecialize(CodeTreeBuilder parent, FrameState frameState, SpecializationData specialization, boolean useSpecializationClass, boolean needsDuplicationCheck)
throws AssertionError {
CodeTreeBuilder builder = parent.create();
if (useSpecializationClass) {
// when using a specialization class we want to initialize the caches
for (CacheExpression cache : specialization.getCaches()) {
String cacheFieldName = createFieldName(specialization, cache.getParameter());
LocalVariable evaluatedVar = frameState.get(cacheFieldName);
if (evaluatedVar == null) {
CodeTree initializer = DSLExpressionGenerator.write(cache.getExpression(), null, castBoundTypes(bindExpressionValues(frameState, cache.getExpression(), specialization)));
TypeMirror type = cache.getExpression().getResolvedType();
LocalVariable var = new LocalVariable(type, cacheFieldName.substring(0, cacheFieldName.length() - 1), null);
frameState.set(cacheFieldName, var);
builder.tree(var.createDeclaration(initializer));
}
}
builder.startStatement();
if (!needsDuplicationCheck) {
// without duplication check the specialization type is not yet declared.
builder.string(createSpecializationTypeName(specialization)).string(" ");
}
builder.string(createSpecializationLocalName(specialization), " = ");
builder.startNew(createSpecializationTypeName(specialization));
if (specialization.getMaximumNumberOfInstances() > 1) {
builder.string(createSpecializationFieldName(specialization));
}
for (CacheExpression cache : specialization.getCaches()) {
builder.tree(frameState.get(createFieldName(specialization, cache.getParameter())).createReference());
}
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
builder.tree(frameState.get(assumption.getId()).createReference());
}
builder.end().end(); // statement, new
} else {
for (CacheExpression cache : specialization.getCaches()) {
LocalVariable evaluatedVar = frameState.get(createFieldName(specialization, cache.getParameter()));
CodeTree cacheValue;
if (evaluatedVar == null) {
cacheValue = DSLExpressionGenerator.write(cache.getExpression(), null, castBoundTypes(bindExpressionValues(frameState, cache.getExpression(), specialization)));
} else {
cacheValue = evaluatedVar.createReference();
}
Parameter parameter = cache.getParameter();
if (ElementUtils.isAssignable(parameter.getType(), context.getType(Node.class)) || ElementUtils.isAssignable(parameter.getType(), context.getType(Node[].class))) {
cacheValue = builder.create().startCall("super", "insert").tree(cacheValue).end().build();
}
String name = createFieldName(specialization, cache.getParameter());
builder.startStatement().string("this.").string(name).string(" = ").tree(cacheValue).end();
}
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
LocalVariable var = frameState.get(assumption.getId());
String name = createAssumptionFieldName(specialization, assumption);
builder.startStatement();
builder.string("this.", name, " = ").tree(var.createReference());
builder.end();
}
}
List<SpecializationData> excludesSpecializations = new ArrayList<>();
for (SpecializationData otherSpeciailzation : reachableSpecializations) {
if (otherSpeciailzation == specialization) {
continue;
}
if (otherSpeciailzation.getExcludedBy().contains(specialization)) {
excludesSpecializations.add(otherSpeciailzation);
}
}
if (useSpecializationClass) {
boolean isNode = specializationClassIsNode(specialization);
builder.startStatement().string("this.", createSpecializationFieldName(specialization), " = ");
if (isNode) {
builder.startCall("super", "insert").string(createSpecializationLocalName(specialization)).end();
} else {
builder.string(createSpecializationLocalName(specialization));
}
builder.end();
}
if (!excludesSpecializations.isEmpty()) {
SpecializationData[] excludesArray = excludesSpecializations.toArray(new SpecializationData[0]);
builder.tree(exclude.createSet(frameState, excludesArray, true, true));
for (SpecializationData excludes : excludesArray) {
if (useSpecializationClass(excludes)) {
builder.statement("this." + createSpecializationFieldName(excludes) + " = null");
}
}
builder.tree((state.createSet(frameState, excludesArray, false, false)));
}
CodeTree updateImplicitCast = createUpdateImplicitCastState(builder, frameState, specialization);
if (updateImplicitCast != null) {
builder.tree(createUpdateImplicitCastState(builder, frameState, specialization));
}
builder.tree(state.createSet(frameState, new SpecializationData[]{specialization}, true, true));
return builder.build();
}
private CodeTree createUpdateImplicitCastState(CodeTreeBuilder parent, FrameState frameState, SpecializationData specialization) {
CodeTreeBuilder builder = null;
int signatureIndex = 0;
for (Parameter p : specialization.getSignatureParameters()) {
TypeMirror targetType = p.getType();
TypeMirror polymorphicType = node.getPolymorphicSpecialization().findParameterOrDie(p.getSpecification().getExecution()).getType();
if (typeSystem.hasImplicitSourceTypes(targetType) && needsCastTo(polymorphicType, targetType)) {
String implicitFieldName = createImplicitTypeStateLocalName(p);
if (builder == null) {
builder = parent.create();
}
builder.tree(state.createSetInteger(frameState, new TypeGuard(p.getType(), signatureIndex), CodeTreeBuilder.singleString(implicitFieldName)));
}
signatureIndex++;
}
return builder == null ? null : builder.build();
}
private CodeTree createAssumptionGuard(AssumptionExpression assumption, CodeTree assumptionValue) {
CodeTree assumptionGuard = CodeTreeBuilder.createBuilder().startCall("isValid_").tree(assumptionValue).end().build();
isValidSignatures.put(ElementUtils.getQualifiedName(assumption.getExpression().getResolvedType()), assumption.getExpression().getResolvedType());
return assumptionGuard;
}
private static CodeTree combineTrees(String sep, CodeTree... trees) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
String s = "";
for (CodeTree tree : trees) {
if (tree != null && !tree.isEmpty()) {
if (sep != null) {
builder.string(s);
}
builder.tree(tree);
s = sep;
}
}
return builder.build();
}
private CodeTree createFastPathAssumptionCheck(CodeTreeBuilder parent, SpecializationData specialization, ExecutableTypeData forType, FrameState frameState)
throws AssertionError {
CodeTreeBuilder builder = parent.create();
builder.startIf();
String sep = "";
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
builder.string(sep);
builder.string("!");
builder.startCall("isValid_").tree(createAssumptionReference(specialization, assumption)).end();
isValidSignatures.put(ElementUtils.getQualifiedName(assumption.getExpression().getResolvedType()), assumption.getExpression().getResolvedType());
sep = " || ";
}
builder.end().startBlock();
builder.tree(createTransferToInterpreterAndInvalidate());
builder.tree(createRemoveThis(builder, frameState, forType, specialization));
builder.end();
return builder.build();
}
private static CodeTree createShortCircuit(LocalVariable targetValue, LocalVariable shortCircuitValue, CodeTree tryExecute) {
if (shortCircuitValue == null) {
return tryExecute;
}
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.tree(shortCircuitValue.createDeclaration(shortCircuitValue.createReference()));
builder.tree(targetValue.createDeclaration(builder.create().defaultValue(targetValue.getTypeMirror()).build()));
builder.startIf().string(shortCircuitValue.getName()).end().startBlock();
builder.tree(tryExecute);
builder.end();
return builder.build();
}
private static CodeTree createTryExecuteChild(LocalVariable value, CodeTree executeChild, boolean needDeclaration, boolean hasTry) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
boolean hasDeclaration = false;
if ((hasTry || !executeChild.isSingleLine()) && needDeclaration) {
builder.tree(value.createDeclaration(null));
hasDeclaration = true;
}
if (hasTry) {
builder.startTryBlock();
} else {
builder.startGroup();
}
if (executeChild.isSingleLine()) {
builder.startStatement();
if (hasDeclaration || !needDeclaration) {
builder.tree(executeChild);
} else {
builder.type(value.getTypeMirror()).string(" ").tree(executeChild);
}
builder.end();
} else {
builder.tree(executeChild);
}
builder.end();
return builder.build();
}
private ExecutableTypeData resolveTargetExecutable(NodeExecutionData execution, TypeMirror target) {
NodeChildData child = execution.getChild();
if (child == null) {
return null;
}
ExecutableTypeData targetExecutable = child.findExecutableType(target);
if (targetExecutable == null) {
targetExecutable = child.findAnyGenericExecutableType(context);
}
return targetExecutable;
}
private CodeTree createCatchRewriteException(CodeTreeBuilder parent, SpecializationData specialization, ExecutableTypeData forType, FrameState frameState, CodeTree execution,
NodeExecutionMode mode) {
if (specialization.getExceptions().isEmpty()) {
return execution;
}
CodeTreeBuilder builder = parent.create();
builder.startTryBlock();
builder.tree(execution);
boolean nonSlowPath = false;
TypeMirror[] exceptionTypes = new TypeMirror[specialization.getExceptions().size()];
for (int i = 0; i < exceptionTypes.length; i++) {
TypeMirror type = specialization.getExceptions().get(i).getJavaClass();
if (!ElementUtils.isAssignable(type, context.getType(SlowPathException.class)) && !ElementUtils.isAssignable(type, context.getType(ArithmeticException.class))) {
nonSlowPath = true;
}
exceptionTypes[i] = type;
}
builder.end().startCatchBlock(exceptionTypes, "ex");
if (nonSlowPath) {
builder.tree(createTransferToInterpreterAndInvalidate());
} else {
builder.lineComment("implicit transferToInterpreterAndInvalidate()");
}
builder.tree(createExcludeThis(builder, frameState, forType, specialization, mode));
builder.end();
return builder.build();
}
private Map<SpecializationData, CodeExecutableElement> removeThisMethods = new HashMap<>();
private CodeTree createExcludeThis(CodeTreeBuilder parent, FrameState frameState, ExecutableTypeData forType, SpecializationData specialization, NodeExecutionMode mode) {
CodeTreeBuilder builder = parent.create();
// slow path might be already already locked
if (!mode.isSlowPath()) {
builder.declaration(context.getType(Lock.class), "lock", "getLock()");
}
builder.statement("lock.lock()");
builder.startTryBlock();
// pass null frame state to ensure values are reloaded.
builder.tree(this.exclude.createSet(null, Arrays.asList(specialization).toArray(new SpecializationData[0]), true, true));
// single instance remove
builder.tree((state.createSet(null, Arrays.asList(specialization).toArray(new SpecializationData[0]), false, true)));
if (useSpecializationClass(specialization)) {
String fieldName = createSpecializationFieldName(specialization);
builder.statement("this." + fieldName + " = null");
}
builder.end().startFinallyBlock();
builder.statement("lock.unlock()");
builder.end();
builder.tree(createCallExecuteAndSpecialize(forType, frameState));
builder.end();
return builder.build();
}
private CodeTree createRemoveThis(CodeTreeBuilder parent, FrameState frameState, ExecutableTypeData forType, SpecializationData specialization) {
CodeExecutableElement method = removeThisMethods.get(specialization);
String specializationLocalName = createSpecializationLocalName(specialization);
boolean useSpecializationClass = useSpecializationClass(specialization);
if (method == null) {
method = new CodeExecutableElement(context.getType(void.class), "remove" + specialization.getId() + "_");
if (useSpecializationClass) {
method.addParameter(new CodeVariableElement(context.getType(Object.class), specializationLocalName));
}
CodeTreeBuilder builder = method.createBuilder();
builder.declaration(context.getType(Lock.class), "lock", "getLock()");
builder.statement("lock.lock()");
builder.startTryBlock();
String fieldName = createSpecializationFieldName(specialization);
if (!useSpecializationClass || specialization.getMaximumNumberOfInstances() == 1) {
// single instance remove
builder.tree((state.createSet(null, Arrays.asList(specialization).toArray(new SpecializationData[0]), false, true)));
if (useSpecializationClass) {
builder.statement("this." + fieldName + " = null");
}
} else {
// multi instance remove
String typeName = createSpecializationTypeName(specialization);
boolean specializedIsNode = specializationClassIsNode(specialization);
builder.declaration(typeName, "prev", "null");
builder.declaration(typeName, "cur", "this." + fieldName);
builder.startWhile();
builder.string("cur != null");
builder.end().startBlock();
builder.startIf().string("cur == ").string(specializationLocalName).end().startBlock();
builder.startIf().string("prev == null").end().startBlock();
builder.statement("this." + fieldName + " = cur.next_");
if (specializedIsNode) {
builder.statement("this.adoptChildren()");
}
builder.end().startElseBlock();
builder.statement("prev.next_ = cur.next_");
if (specializedIsNode) {
builder.statement("prev.adoptChildren()");
}
builder.end();
builder.statement("break");
builder.end(); // if block
builder.statement("prev = cur");
builder.statement("cur = cur.next_");
builder.end(); // while block
builder.startIf().string("this." + fieldName).string(" == null").end().startBlock();
builder.tree((state.createSet(null, Arrays.asList(specialization).toArray(new SpecializationData[0]), false, true)));
builder.end();
}
builder.end().startFinallyBlock();
builder.statement("lock.unlock()");
builder.end();
removeThisMethods.put(specialization, method);
}
CodeTreeBuilder builder = parent.create();
builder.startStatement().startCall(method.getSimpleName().toString());
if (useSpecializationClass) {
builder.string(specializationLocalName);
}
builder.end().end();
builder.tree(createCallExecuteAndSpecialize(forType, frameState));
return builder.build();
}
private CodeTree createCallExecute(ExecutableTypeData forType, ExecutableTypeData targetType, FrameState frameState) {
TypeMirror returnType = targetType.getReturnType();
List<CodeTree> bindings = new ArrayList<>();
List<TypeMirror> sourceTypes = forType.getSignatureParameters();
List<TypeMirror> targetTypes = targetType.getSignatureParameters();
if (sourceTypes.size() != targetTypes.size()) {
throw new IllegalArgumentException();
}
if (targetType.getFrameParameter() != null) {
LocalVariable parameterLocal = frameState.get(FRAME_VALUE);
TypeMirror parameterTargetType = targetType.getFrameParameter();
if (parameterLocal == null) {
bindings.add(CodeTreeBuilder.createBuilder().defaultValue(parameterTargetType).build());
} else {
bindings.add(parameterLocal.createReference());
}
}
for (int i = 0; i < sourceTypes.size(); i++) {
LocalVariable parameterLocal = frameState.getValue(i);
TypeMirror parameterTargetType = targetTypes.get(i);
if (parameterLocal == null) {
bindings.add(CodeTreeBuilder.createBuilder().defaultValue(parameterTargetType).build());
} else {
bindings.add(parameterLocal.createReference());
}
}
CodeTree call = callMethod(null, targetType.getMethod(), bindings.toArray(new CodeTree[0]));
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder = builder.create();
if (isVoid(forType.getReturnType())) {
builder.statement(call);
builder.returnStatement();
} else {
builder.startReturn();
builder.tree(expectOrCast(returnType, forType, call));
builder.end();
}
return builder.build();
}
private CodeTree createCallExecuteAndSpecialize(ExecutableTypeData forType, FrameState frameState) {
TypeMirror returnType = node.getPolymorphicSpecialization().getReturnType().getType();
String frame = null;
if (needsFrame(reachableSpecializations)) {
frame = FRAME_VALUE;
}
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startCall("executeAndSpecialize");
frameState.addReferencesTo(builder, frame);
builder.end();
CodeTree call = builder.build();
builder = builder.create();
if (isVoid(forType.getReturnType())) {
builder.statement(call);
builder.returnStatement();
} else {
builder.startReturn();
builder.tree(expectOrCast(returnType, forType, call));
builder.end();
}
return builder.build();
}
private List<IfTriple> createMethodGuardCheck(FrameState frameState, SpecializationGroup group, List<GuardExpression> guardExpressions, NodeExecutionMode mode) {
List<IfTriple> triples = new ArrayList<>();
for (GuardExpression guard : guardExpressions) {
triples.addAll(initializeCaches(frameState, group, group.getSpecialization().getBoundCaches(guard.getExpression()), mode));
triples.addAll(initializeCasts(frameState, group, guard.getExpression(), mode));
triples.add(createMethodGuardCheck(frameState, group.getSpecialization(), guard, mode.isFastPath() || mode.isGuardFallback()));
}
return triples;
}
private List<IfTriple> initializeCaches(FrameState frameState, SpecializationGroup group, Set<CacheExpression> caches, NodeExecutionMode mode) {
if (mode != NodeExecutionMode.SLOW_PATH || group.getSpecialization() == null) {
return Collections.emptyList();
}
List<IfTriple> triples = new ArrayList<>();
if (!caches.isEmpty()) {
// preinitialize caches for guards in local variables
for (CacheExpression cache : caches) {
triples.addAll(initializeCasts(frameState, group, cache.getExpression(), mode));
IfTriple triple = createCacheInitializer(frameState, group.getSpecialization(), cache);
if (triple != null) {
triples.add(triple);
}
}
}
return triples;
}
private static IfTriple createCacheInitializer(FrameState frameState, SpecializationData specialization, CacheExpression cache) {
String name = createFieldName(specialization, cache.getParameter());
// already initialized
if (frameState.get(name) != null) {
return null;
}
CodeTree initializer = DSLExpressionGenerator.write(cache.getExpression(), null, castBoundTypes(bindExpressionValues(frameState, cache.getExpression(), specialization)));
TypeMirror type = cache.getParameter().getType();
LocalVariable var = new LocalVariable(type, name.substring(0, name.length() - 1), null);
frameState.set(name, var);
return new IfTriple(var.createDeclaration(initializer), null, null);
}
private IfTriple createMethodGuardCheck(FrameState frameState, SpecializationData specialization, GuardExpression guard, boolean fastPath) {
DSLExpression expression = guard.getExpression();
Map<Variable, CodeTree> resolvedBindings = castBoundTypes(bindExpressionValues(frameState, expression, specialization));
CodeTree expressionCode = DSLExpressionGenerator.write(expression, null, resolvedBindings);
CodeTree assertion = null; // overrule with assertion
if (fastPath) {
if (!specialization.isDynamicParameterBound(expression)) {
assertion = CodeTreeBuilder.createBuilder().startAssert().tree(expressionCode).end().build();
}
} else {
if (guard.isConstantTrueInSlowPath(context)) {
assertion = CodeTreeBuilder.createBuilder().startStatement().string("// assert ").tree(expressionCode).end().build();
}
}
return new IfTriple(null, assertion == null ? expressionCode : null, assertion);
}
private static Map<Variable, CodeTree> castBoundTypes(Map<Variable, LocalVariable> bindings) {
Map<Variable, CodeTree> resolvedBindings = new HashMap<>();
for (Variable variable : bindings.keySet()) {
LocalVariable localVariable = bindings.get(variable);
CodeTree resolved = localVariable.createReference();
TypeMirror sourceType = localVariable.getTypeMirror();
TypeMirror targetType = variable.getResolvedTargetType();
if (targetType == null) {
targetType = variable.getResolvedType();
}
if (!ElementUtils.isAssignable(sourceType, targetType)) {
resolved = CodeTreeBuilder.createBuilder().startParantheses().cast(targetType, resolved).end().build();
}
resolvedBindings.put(variable, resolved);
}
return resolvedBindings;
}
private static Map<Variable, LocalVariable> bindExpressionValues(FrameState frameState, DSLExpression expression, SpecializationData specialization) throws AssertionError {
Map<Variable, LocalVariable> bindings = new HashMap<>();
Set<Variable> boundVariables = expression.findBoundVariables();
if (specialization == null && !boundVariables.isEmpty()) {
throw new AssertionError("Cannot bind guard variable in non-specialization group. yet.");
}
// resolve bindings for local context
for (Variable variable : boundVariables) {
Parameter resolvedParameter = specialization.findByVariable(variable.getResolvedVariable());
if (resolvedParameter != null) {
LocalVariable localVariable;
if (resolvedParameter.getSpecification().isCached()) {
// bind cached variable
String cachedMemberName = createFieldName(specialization, resolvedParameter);
CodeTree cacheReference = createCacheReference(specialization, resolvedParameter);
LocalVariable var = frameState.get(cachedMemberName);
if (var == null) {
var = new LocalVariable(resolvedParameter.getType(), cachedMemberName, cacheReference);
}
bindings.put(variable, var);
} else {
// bind local variable
if (resolvedParameter.getSpecification().isSignature()) {
NodeExecutionData execution = resolvedParameter.getSpecification().getExecution();
localVariable = frameState.getValue(execution);
} else {
localVariable = frameState.get(resolvedParameter.getLocalName());
}
if (localVariable != null) {
bindings.put(variable, localVariable);
}
}
}
}
return bindings;
}
private static CodeTree createCacheReference(SpecializationData s, Parameter p) {
String cacheFieldName = createFieldName(s, p);
return accessInSpecializationClass(s, cacheFieldName);
}
private static CodeTree createAssumptionReference(SpecializationData s, AssumptionExpression a) {
String assumptionFieldName = createAssumptionFieldName(s, a);
return accessInSpecializationClass(s, assumptionFieldName);
}
private static CodeTree accessInSpecializationClass(SpecializationData s, String cacheFieldName) {
if (!useSpecializationClass(s)) {
return CodeTreeBuilder.singleString(cacheFieldName);
} else {
return CodeTreeBuilder.createBuilder().string(createSpecializationLocalName(s), ".", cacheFieldName).build();
}
}
private IfTriple createTypeCheckOrCast(FrameState frameState, SpecializationGroup group, TypeGuard typeGuard,
NodeExecutionMode specializationExecution, boolean castOnly, boolean forceImplicitCast) {
CodeTreeBuilder prepareBuilder = CodeTreeBuilder.createBuilder();
CodeTreeBuilder checkBuilder = CodeTreeBuilder.createBuilder();
int signatureIndex = typeGuard.getSignatureIndex();
LocalVariable value = frameState.getValue(signatureIndex);
TypeMirror targetType = typeGuard.getType();
if (!ElementUtils.needsCastTo(value.getTypeMirror(), targetType)) {
boolean foundImplicitSubType = false;
if (forceImplicitCast) {
List<ImplicitCastData> casts = typeSystem.lookupByTargetType(targetType);
for (ImplicitCastData cast : casts) {
if (ElementUtils.isSubtype(cast.getSourceType(), targetType)) {
foundImplicitSubType = true;
break;
}
}
}
if (!foundImplicitSubType) {
return null;
}
}
NodeExecutionData execution = node.getChildExecutions().get(signatureIndex);
CodeTreeBuilder castBuilder = prepareBuilder.create();
LocalVariable shortCircuit = frameState.getShortCircuit(execution);
if (shortCircuit != null) {
checkBuilder.string("(");
CodeTreeBuilder referenceBuilder = checkBuilder.create();
if (!ElementUtils.isPrimitive(shortCircuit.getTypeMirror())) {
referenceBuilder.string("(boolean) ");
}
referenceBuilder.tree(shortCircuit.createReference());
checkBuilder.string("!").tree(referenceBuilder.build());
checkBuilder.string(" || ");
castBuilder.tree(referenceBuilder.build()).string(" ? ");
}
List<ImplicitCastData> sourceTypes = typeSystem.lookupByTargetType(targetType);
CodeTree valueReference = value.createReference();
if (sourceTypes.isEmpty()) {
checkBuilder.tree(TypeSystemCodeGenerator.check(typeSystem, targetType, valueReference));
castBuilder.tree(TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference));
} else {
List<SpecializationData> specializations = group.collectSpecializations();
List<Parameter> parameters = new ArrayList<>();
for (SpecializationData otherSpecialization : specializations) {
parameters.add(otherSpecialization.findParameterOrDie(execution));
}
if (specializationExecution.isFastPath() || specializationExecution.isGuardFallback()) {
CodeTree implicitState;
if (specializationExecution.isGuardFallback()) {
implicitState = CodeTreeBuilder.singleString("0b" + allsetMask(sourceTypes.size() + 1));
} else {
implicitState = state.createExtractInteger(frameState, typeGuard);
}
checkBuilder.tree(TypeSystemCodeGenerator.implicitCheckFlat(typeSystem, targetType, valueReference, implicitState));
castBuilder.tree(TypeSystemCodeGenerator.implicitCastFlat(typeSystem, targetType, valueReference, implicitState));
} else {
Parameter parameter = parameters.get(0);
String implicitStateName = createImplicitTypeStateLocalName(parameter);
CodeTree defaultValue = null;
if (parameter.getSpecification().getExecution().isShortCircuit()) {
defaultValue = CodeTreeBuilder.singleString("0");
}
prepareBuilder.declaration(context.getType(int.class), implicitStateName, defaultValue);
CodeTree specializeCall = TypeSystemCodeGenerator.implicitSpecializeFlat(typeSystem, targetType, valueReference);
checkBuilder.startParantheses();
checkBuilder.string(implicitStateName, " = ").tree(specializeCall);
checkBuilder.end();
checkBuilder.string(" != 0");
castBuilder.tree(TypeSystemCodeGenerator.implicitCastFlat(typeSystem, targetType, valueReference, CodeTreeBuilder.singleString(implicitStateName)));
}
}
if (shortCircuit != null) {
checkBuilder.string(")");
castBuilder.string(" : ").defaultValue(targetType);
}
if (castOnly) {
LocalVariable currentValue = frameState.getValue(execution);
CodeTreeBuilder localsBuilder = CodeTreeBuilder.createBuilder();
LocalVariable castVariable = currentValue.nextName().newType(typeGuard.getType()).accessWith(null);
frameState.setValue(execution, castVariable);
localsBuilder.tree(castVariable.createDeclaration(castBuilder.build()));
return new IfTriple(localsBuilder.build(), null, null);
} else {
return new IfTriple(prepareBuilder.build(), checkBuilder.build(), null);
}
}
private List<IfTriple> initializeCasts(FrameState frameState, SpecializationGroup group, DSLExpression expression, NodeExecutionMode specializationExecution) {
Set<VariableElement> boundElements = expression.findBoundVariableElements();
if (boundElements.isEmpty()) {
return Collections.emptyList();
}
List<IfTriple> triples = new ArrayList<>();
for (VariableElement variable : boundElements) {
Parameter p = group.getSpecialization().findByVariable(variable);
if (p != null) {
NodeExecutionData execution = p.getSpecification().getExecution();
if (execution != null) {
LocalVariable var = frameState.getValue(execution);
if (var == null) {
throw new AssertionError();
}
IfTriple triple = createTypeCheckOrCast(frameState, group, new TypeGuard(p.getType(), execution.getIndex()), specializationExecution, true, false);
if (triple != null) {
triples.add(triple);
}
}
}
}
return triples;
}
private static String allsetMask(int size) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < size; i++) {
b.append("1");
}
return b.toString();
}
private ExecutableTypeData createExecuteAndSpecializeType() {
SpecializationData polymorphicSpecialization = node.getPolymorphicSpecialization();
TypeMirror polymorphicType = polymorphicSpecialization.getReturnType().getType();
List<TypeMirror> parameters = new ArrayList<>();
for (Parameter param : polymorphicSpecialization.getSignatureParameters()) {
if (param.getSpecification().getExecution().isShortCircuit()) {
parameters.add(context.getType(boolean.class));
}
parameters.add(param.getType());
}
return new ExecutableTypeData(node, polymorphicType, "executeAndSpecialize", node.getFrameType(), parameters);
}
private List<TypeMirror> resolveOptimizedImplicitSourceTypes(NodeExecutionData execution, TypeMirror targetType) {
List<TypeMirror> allSourceTypes = typeSystem.lookupSourceTypes(targetType);
List<TypeMirror> filteredSourceTypes = new ArrayList<>();
for (TypeMirror sourceType : allSourceTypes) {
ExecutableTypeData executableType = resolveTargetExecutable(execution, sourceType);
if (executableType == null) {
continue;
}
if (!ElementUtils.isPrimitive(sourceType) || !boxingEliminationEnabled) {
// don't optimize non primitives
continue;
}
if (!ElementUtils.typeEquals(executableType.getReturnType(), sourceType)) {
// no boxing optimization possible
continue;
}
filteredSourceTypes.add(sourceType);
}
return filteredSourceTypes;
}
private ChildExecutionResult createExecuteChildImplicitCast(CodeTreeBuilder parent, FrameState originalFrameState, FrameState frameState, NodeExecutionData execution, LocalVariable target) {
CodeTreeBuilder builder = parent.create();
List<TypeMirror> originalSourceTypes = typeSystem.lookupSourceTypes(target.getTypeMirror());
List<TypeMirror> sourceTypes = resolveOptimizedImplicitSourceTypes(execution, target.getTypeMirror());
TypeGuard typeGuard = new TypeGuard(target.getTypeMirror(), execution.getIndex());
boolean throwsUnexpected = false;
boolean elseIf = false;
for (TypeMirror sourceType : sourceTypes) {
ExecutableTypeData executableType = resolveTargetExecutable(execution, sourceType);
elseIf = builder.startIf(elseIf);
throwsUnexpected |= executableType.hasUnexpectedValue(context);
builder.tree(state.createContainsOnly(frameState, originalSourceTypes.indexOf(sourceType), 1, new Object[]{typeGuard}, new Object[]{typeGuard, node.getUninitializedSpecialization()}));
builder.end();
builder.startBlock();
CodeTree value = callChildExecuteMethod(execution, executableType, frameState);
value = expect(executableType.getReturnType(), sourceType, value);
throwsUnexpected |= needsCastTo(executableType.getReturnType(), sourceType);
ImplicitCastData cast = typeSystem.lookupCast(sourceType, target.getTypeMirror());
if (cast != null) {
// we need to store the original value to restore it in
// case of a deopt
String localName = createSourceTypeLocalName(target, sourceType);
builder.startStatement().string(localName).string(" = ").tree(value).end();
value = callMethod(null, cast.getMethod(), CodeTreeBuilder.singleString(localName));
}
builder.startStatement().string(target.getName()).string(" = ").tree(value).end();
builder.end();
}
if (elseIf) {
builder.startElseBlock();
}
LocalVariable genericValue = target.makeGeneric(context).nextName();
builder.tree(createAssignExecuteChild(originalFrameState, frameState, builder, execution, node.getGenericExecutableType(null), genericValue, null));
builder.startStatement().string(target.getName()).string(" = ");
CodeTree implicitState = state.createExtractInteger(frameState, typeGuard);
builder.tree(TypeSystemCodeGenerator.implicitExpectFlat(typeSystem, target.getTypeMirror(), genericValue.createReference(), implicitState));
builder.end();
if (!sourceTypes.isEmpty()) {
builder.end();
}
return new ChildExecutionResult(builder.build(), throwsUnexpected);
}
private static class ChildExecutionResult {
CodeTree code;
final boolean throwsUnexpectedResult;
ChildExecutionResult(CodeTree code, boolean throwsUnexpectedResult) {
this.code = code;
this.throwsUnexpectedResult = throwsUnexpectedResult;
}
}
private static class ExecuteDelegationResult {
public final CodeTree tree;
public final boolean hasFallthrough;
ExecuteDelegationResult(CodeTree tree, boolean hasFallthrough) {
this.tree = tree;
this.hasFallthrough = hasFallthrough;
}
}
private abstract static class BitSet {
private final int capacity;
private final String name;
private final Map<Object, Integer> offsets = new HashMap<>();
private final List<? extends Object> allElements;
private final ProcessorContext context = ProcessorContext.getInstance();
private final TypeMirror bitSetType;
BitSet(String name, List<? extends Object> specializations) {
this.name = name;
this.allElements = specializations;
this.capacity = computeStateLength();
if (capacity <= 32) {
bitSetType = context.getType(int.class);
} else if (capacity <= 64) {
bitSetType = context.getType(long.class);
} else {
throw new UnsupportedOperationException("State space too big " + capacity + ". Only <= 64 supported.");
}
}
private int computeStateLength() {
if (allElements.size() == 0) {
return 0;
}
int bitIndex = 0;
for (Object specialization : allElements) {
int specializationSize = calculateRequiredBits(specialization);
offsets.put(specialization, bitIndex);
bitIndex += specializationSize;
}
return bitIndex - 1;
}
public CodeVariableElement declareFields(CodeTypeElement clazz) {
return clazz.add(createNodeField(PRIVATE, bitSetType, name + "_", CompilationFinal.class));
}
public CodeTree createLoad(FrameState frameState, Object maskObject) {
if (frameState.get(name) != null) {
// already loaded
return CodeTreeBuilder.singleString("");
}
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
String fieldName = name + "_";
LocalVariable var = new LocalVariable(bitSetType, name, null);
CodeTreeBuilder init = builder.create();
init.tree(CodeTreeBuilder.singleString(fieldName));
if (maskObject != null) {
long mask = ~createMask(0, -1, new Object[]{maskObject});
init.string(" & ").string(formatMask(mask));
init.string("/* mask-active ", toString(maskObject), "*/");
}
builder.tree(var.createDeclaration(init.build()));
frameState.set(name, var);
return builder.build();
}
public CodeTree createContainsOnly(FrameState frameState, int offset, int length, Object[] selectedElements, @SuppressWarnings("hiding") Object[] allElements) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startParantheses();
long mask = ~createMask(offset, length, selectedElements) & createMask(allElements);
builder.tree(createReference(frameState)).string(" & ").string(formatMask(mask));
builder.end();
builder.string(" == 0");
builder.string(" /* only-active ", toString(selectedElements, " && "), " */");
return builder.build();
}
public CodeTree createIs(FrameState frameState, Object[] elements) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.tree(createReference(frameState)).string(" == ").string(formatMask(createMask(elements)));
return builder.build();
}
private String formatMask(long mask) {
int bitsUsed = 64 - Long.numberOfLeadingZeros(mask);
if (bitsUsed <= 16) {
return "0b" + Integer.toBinaryString((int) mask);
} else {
if (bitsUsed <= 32 || capacity <= 32) {
return "0x" + Integer.toHexString((int) mask);
} else {
return "0x" + Long.toHexString(mask) + "L";
}
}
}
public CodeTree createIsOneBitOf(FrameState frameState, Object[] elements) {
String mask = formatMask(createMask(elements));
CodeTree masked = CodeTreeBuilder.createBuilder().startParantheses().tree(createReference(frameState)).string(" & ").string(mask).end().build();
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
// use the calculation of power of two
// (state & (state - 1L)) == 0L
builder.startParantheses().tree(masked).string(" & ").startParantheses().tree(masked).string(" - 1").end().end().string(" == 0");
builder.string(" /* ", label("is-single"), " */");
return builder.build();
}
public CodeTree createContains(FrameState frameState, Object[] elements) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startParantheses();
builder.tree(createReference(frameState)).string(" & ").string(formatMask(createMask(elements)));
builder.end();
builder.string(" != 0");
builder.string(" /* ", label("is"), toString(elements, " || "), " */");
return builder.build();
}
private String toString(Object[] elements, String elementSep) {
StringBuilder b = new StringBuilder();
String sep = "";
for (int i = 0; i < elements.length; i++) {
b.append(sep).append(toString(elements[i]));
sep = elementSep;
}
return b.toString();
}
protected String toString(Object element) {
if (element instanceof SpecializationData) {
SpecializationData specialization = (SpecializationData) element;
if (specialization.isUninitialized()) {
return "uninitialized";
}
return ElementUtils.createReferenceName(specialization.getMethod());
} else if (element instanceof TypeGuard) {
int index = ((TypeGuard) element).getSignatureIndex();
String simpleName = ElementUtils.getSimpleName(((TypeGuard) element).getType());
return index + ":" + simpleName;
}
return element.toString();
}
private CodeTree createReference(FrameState frameState) {
LocalVariable var = frameState != null ? frameState.get(name) : null;
if (var != null) {
return var.createReference();
} else {
return CodeTreeBuilder.createBuilder().string("this.", name, "_").build();
}
}
public CodeTree createNotContains(FrameState frameState, Object[] elements) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startParantheses();
builder.tree(createReference(frameState)).string(" & ").string(formatMask(createMask(elements)));
builder.end();
builder.string(" == 0");
builder.string(" /* ", label("is-not"), toString(elements, " && "), " */");
return builder.build();
}
private String label(String message) {
return message + "-" + getName() + " ";
}
protected String getName() {
if (this instanceof ExcludeBitSet) {
return "excluded";
} else {
return "active";
}
}
public CodeTree createExtractInteger(FrameState frameState, Object element) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
if (capacity > 32) {
builder.string("(int)(");
}
builder.startParantheses();
builder.tree(createReference(frameState)).string(" & ");
builder.string(formatMask(createMask(element)));
builder.end();
builder.string(" >>> ", Integer.toString(getStateOffset(element)));
if (capacity > 32) {
builder.string(")");
}
builder.string(" /* ", label("extract-implicit"), toString(element), " */");
return builder.build();
}
public CodeTree createSet(FrameState frameState, Object[] elements, boolean value, boolean persist) {
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startStatement();
if (persist) {
builder.string("this.", name, "_ = ");
} else {
builder.tree(createReference(frameState)).string(" = ");
}
builder.tree(createReference(frameState));
if (value) {
builder.string(" | ");
builder.string(formatMask(createMask(elements)));
builder.string(" /* ", label("add"), toString(elements, ", "), " */");
} else {
builder.string(" & ");
builder.string(formatMask(~createMask(elements)));
builder.string(" /* ", label("remove"), toString(elements, ", "), " */");
}
builder.end();
return builder.build();
}
public CodeTree createSetInteger(FrameState frameState, Object element, CodeTree value) {
int offset = getStateOffset(element);
CodeTreeBuilder builder = CodeTreeBuilder.createBuilder();
builder.startStatement();
builder.tree(createReference(frameState)).string(" = ");
builder.startParantheses();
builder.tree(createReference(frameState));
builder.string(" | (");
if (capacity > 32) {
builder.string("(long) ");
}
builder.tree(value).string(" << ", Integer.toString(offset), ")");
builder.string(" /* ", label("set-implicit"), toString(element), " */");
builder.end();
builder.end();
return builder.build();
}
private long createMask(Object e) {
return createMask(new Object[]{e});
}
private long createMask(Object[] e) {
return createMask(0, -1, e);
}
private long createMask(int offset, int length, Object[] e) {
long mask = 0;
for (Object element : e) {
if (!offsets.containsKey(element)) {
continue;
}
int stateOffset = getStateOffset(element);
int stateLength = calculateRequiredBits(element);
int realLength = length < 0 ? stateLength : Math.min(stateLength, offset + length);
for (int i = offset; i < realLength; i++) {
mask |= 1L << (stateOffset + i);
}
}
return mask;
}
private int getStateOffset(Object stateSpecialization) {
Integer value = offsets.get(stateSpecialization);
if (value == null) {
return 0;
}
return value;
}
protected abstract int calculateRequiredBits(Object specialization);
}
private class StateBitSet extends BitSet {
StateBitSet(List<Object> objects) {
super(STATE_VALUE, objects);
}
@Override
protected int calculateRequiredBits(Object object) {
if (object instanceof SpecializationData) {
SpecializationData specialization = (SpecializationData) object;
if (specialization.isPolymorphic()) {
return 0;
} else {
return 1;
}
} else if (object instanceof TypeGuard) {
TypeGuard guard = (TypeGuard) object;
TypeMirror type = guard.getType();
List<TypeMirror> sourceTypes = typeSystem.lookupSourceTypes(type);
if (sourceTypes.size() > 1) {
return sourceTypes.size();
}
throw new AssertionError();
} else {
throw new AssertionError();
}
}
}
private static class ExcludeBitSet extends BitSet {
ExcludeBitSet(List<SpecializationData> specializations) {
super("exclude", specializations);
}
@Override
protected int calculateRequiredBits(Object object) {
if (object instanceof SpecializationData) {
SpecializationData specialization = (SpecializationData) object;
if (specialization.isPolymorphic()) {
return 0;
} else if (specialization.isUninitialized()) {
return 0;
}
if (!specialization.getExceptions().isEmpty() || !specialization.getExcludedBy().isEmpty()) {
return 1;
}
return 0;
}
throw new IllegalArgumentException();
}
}
private static final class FrameState {
private final FlatNodeGenFactory factory;
private final Map<String, LocalVariable> values = new HashMap<>();
private FrameState(FlatNodeGenFactory factory) {
this.factory = factory;
}
public void loadFastPathState(SpecializationData specialization) {
for (CacheExpression cache : specialization.getCaches()) {
Parameter cacheParameter = cache.getParameter();
String name = cacheParameter.getVariableElement().getSimpleName().toString();
set(cacheParameter.getLocalName(), new LocalVariable(cacheParameter.getType(), name, CodeTreeBuilder.singleString("this." + name)));
}
for (AssumptionExpression assumption : specialization.getAssumptionExpressions()) {
String name = assumptionName(assumption);
TypeMirror type = assumption.getExpression().getResolvedType();
set(name, new LocalVariable(type, name, CodeTreeBuilder.singleString("this." + name)));
}
}
public CodeExecutableElement createMethod(Set<Modifier> modifiers, TypeMirror returnType, String name, String... optionalArguments) {
CodeExecutableElement method = new CodeExecutableElement(modifiers, returnType, name);
addParametersTo(method, Integer.MAX_VALUE, optionalArguments);
return method;
}
public static FrameState load(FlatNodeGenFactory factory, ExecutableTypeData type, int varargsThreshold) {
FrameState context = new FrameState(factory);
context.loadEvaluatedValues(type, varargsThreshold);
return context;
}
private void loadEvaluatedValues(ExecutableTypeData executedType, int varargsThreshold) {
TypeMirror frame = executedType.getFrameParameter();
if (frame == null) {
removeValue(FRAME_VALUE);
} else {
set(FRAME_VALUE, new LocalVariable(frame, FRAME_VALUE, null));
}
for (NodeFieldData field : factory.node.getFields()) {
String fieldName = fieldValueName(field);
values.put(fieldName, new LocalVariable(field.getType(), fieldName, CodeTreeBuilder.singleString(field.getName())));
}
boolean varargs = needsVarargs(false, varargsThreshold);
List<TypeMirror> evaluatedParameter = executedType.getEvaluatedParameters();
int evaluatedIndex = 0;
for (int executionIndex = 0; executionIndex < factory.node.getExecutionCount(); executionIndex++) {
NodeExecutionData execution = factory.node.getChildExecutions().get(executionIndex);
if (execution.isShortCircuit()) {
if (evaluatedIndex < executedType.getEvaluatedCount()) {
TypeMirror evaluatedType = evaluatedParameter.get(evaluatedIndex);
LocalVariable shortCircuit = createShortCircuitValue(execution).newType(evaluatedType);
if (varargs) {
shortCircuit = shortCircuit.accessWith(createReadVarargs(evaluatedIndex));
}
values.put(shortCircuit.getName(), shortCircuit.makeOriginal());
evaluatedIndex++;
}
}
if (evaluatedIndex < executedType.getEvaluatedCount()) {
TypeMirror evaluatedType = evaluatedParameter.get(evaluatedIndex);
LocalVariable value = createValue(execution, evaluatedType);
if (varargs) {
value = value.accessWith(createReadVarargs(evaluatedIndex));
}
values.put(value.getName(), value.makeOriginal());
evaluatedIndex++;
}
}
}
public static FrameState load(FlatNodeGenFactory factory) {
return load(factory, factory.createExecuteAndSpecializeType(), Integer.MAX_VALUE);
}
public FrameState copy() {
FrameState copy = new FrameState(factory);
copy.values.putAll(values);
return copy;
}
private static String fieldValueName(NodeFieldData field) {
return field.getName() + "Value";
}
@SuppressWarnings("static-method")
public LocalVariable createValue(NodeExecutionData execution, TypeMirror type) {
return new LocalVariable(type, valueName(execution), null);
}
public LocalVariable createShortCircuitValue(NodeExecutionData execution) {
return new LocalVariable(factory.getType(boolean.class), shortCircuitName(execution), null);
}
private static String valueName(NodeExecutionData execution) {
return execution.getName() + "Value";
}
private static String shortCircuitName(NodeExecutionData execution) {
return "has" + ElementUtils.firstLetterUpperCase(valueName(execution));
}
public void set(String id, LocalVariable var) {
values.put(id, var);
}
public LocalVariable get(String id) {
return values.get(id);
}
public LocalVariable get(Parameter parameter, int signatureIndex) {
LocalVariable var = get(parameter.getLocalName());
if (var == null && parameter.getSpecification().isSignature()) {
List<NodeExecutionData> childExecutions = factory.node.getChildExecutions();
if (signatureIndex < childExecutions.size() && signatureIndex >= 0) {
NodeExecutionData execution = childExecutions.get(signatureIndex);
var = getValue(execution);
}
}
return var;
}
public LocalVariable getValue(NodeExecutionData execution) {
return get(valueName(execution));
}
public LocalVariable getValue(int signatureIndex) {
List<NodeExecutionData> childExecutions = factory.node.getChildExecutions();
if (signatureIndex < childExecutions.size()) {
return getValue(childExecutions.get(signatureIndex));
} else {
return null;
}
}
public void removeValue(String id) {
values.remove(id);
}
public void setValue(NodeExecutionData execution, LocalVariable var) {
values.put(valueName(execution), var);
}
public void setShortCircuitValue(NodeExecutionData execution, LocalVariable var) {
if (var == null) {
return;
}
values.put(shortCircuitName(execution), var);
}
private boolean needsVarargs(boolean requireLoaded, int varArgsThreshold) {
int size = 0;
for (NodeExecutionData execution : factory.node.getChildExecutions()) {
if (requireLoaded && getValue(execution) == null) {
continue;
}
if (execution.isShortCircuit()) {
size += 2;
} else {
size++;
}
}
return size >= varArgsThreshold;
}
private static CodeTree createReadVarargs(int i) {
return CodeTreeBuilder.createBuilder().string("args_[").string(String.valueOf(i)).string("]").build();
}
public void addReferencesTo(CodeTreeBuilder builder, String... optionalNames) {
for (String var : optionalNames) {
LocalVariable local = values.get(var);
if (local != null) {
builder.tree(local.createReference());
}
}
List<NodeExecutionData> executions = factory.node.getChildExecutions();
for (NodeExecutionData execution : executions) {
if (execution.isShortCircuit()) {
LocalVariable shortCircuitVar = getShortCircuit(execution);
if (shortCircuitVar != null) {
builder.tree(shortCircuitVar.createReference());
}
}
LocalVariable var = getValue(execution);
if (var != null) {
builder.startGroup().tree(var.createReference()).end();
}
}
}
public void addParametersTo(CodeExecutableElement method, int varArgsThreshold, String... optionalNames) {
for (String var : optionalNames) {
LocalVariable local = values.get(var);
if (local != null) {
method.addParameter(local.createParameter());
}
}
if (needsVarargs(true, varArgsThreshold)) {
method.addParameter(new CodeVariableElement(factory.getType(Object[].class), "args_"));
method.setVarArgs(true);
} else {
for (NodeExecutionData execution : factory.node.getChildExecutions()) {
if (execution.isShortCircuit()) {
LocalVariable shortCircuitVar = getShortCircuit(execution);
if (shortCircuitVar != null) {
method.addParameter(shortCircuitVar.createParameter());
}
}
LocalVariable var = getValue(execution);
if (var != null) {
method.addParameter(var.createParameter());
}
}
}
}
private LocalVariable getShortCircuit(NodeExecutionData execution) {
return values.get(shortCircuitName(execution));
}
@Override
public String toString() {
return "LocalContext [values=" + values + "]";
}
}
private static final class LocalVariable {
private final TypeMirror typeMirror;
private final CodeTree accessorTree;
private final String name;
private LocalVariable(TypeMirror typeMirror, String name, CodeTree accessorTree) {
Objects.requireNonNull(typeMirror);
this.typeMirror = typeMirror;
this.accessorTree = accessorTree;
this.name = name;
}
public String getName() {
return name;
}
private static String createNextName(String name) {
return name + "_";
}
public TypeMirror getTypeMirror() {
return typeMirror;
}
public CodeVariableElement createParameter() {
return new CodeVariableElement(getTypeMirror(), getName());
}
public CodeTree createDeclaration(CodeTree init) {
return CodeTreeBuilder.createBuilder().declaration(getTypeMirror(), getName(), init).build();
}
public CodeTree createReference() {
if (accessorTree != null) {
return accessorTree;
} else {
return CodeTreeBuilder.singleString(getName());
}
}
public LocalVariable newType(TypeMirror newType) {
return new LocalVariable(newType, name, accessorTree);
}
public LocalVariable accessWith(CodeTree tree) {
return new LocalVariable(typeMirror, name, tree);
}
public LocalVariable nextName() {
return new LocalVariable(typeMirror, createNextName(name), accessorTree);
}
public LocalVariable makeOriginal() {
return new LocalVariable(typeMirror, name, accessorTree);
}
public LocalVariable makeGeneric(ProcessorContext context) {
return newType(context.getType(Object.class));
}
@Override
public String toString() {
return "Local[type = " + getTypeMirror() + ", name = " + name + ", accessWith = " + accessorTree + "]";
}
}
private static class BoxingSplit {
private final SpecializationGroup group;
private final TypeMirror[] primitiveSignature;
BoxingSplit(SpecializationGroup group, TypeMirror[] primitiveSignature) {
this.group = group;
this.primitiveSignature = primitiveSignature;
}
public String getName() {
StringBuilder b = new StringBuilder();
String sep = "";
for (TypeMirror typeMirror : primitiveSignature) {
b.append(sep).append(ElementUtils.firstLetterLowerCase(ElementUtils.getSimpleName(typeMirror)));
sep = "_";
}
return b.toString();
}
}
private enum NodeExecutionMode {
FAST_PATH,
SLOW_PATH,
FALLBACK_GUARD;
public boolean isGuardFallback() {
return this == FALLBACK_GUARD;
}
public boolean isSlowPath() {
return this == NodeExecutionMode.SLOW_PATH;
}
public final boolean isFastPath() {
return this == FAST_PATH;
}
}
}