Mercurial > hg > graal-compiler
diff graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java @ 18761:a665483c3881
Truffle-DSL: new node layout implementation.
author | Christian Humer <christian.humer@gmail.com> |
---|---|
date | Mon, 29 Dec 2014 23:38:54 +0100 |
parents | |
children | a720bf2e2f43 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java Mon Dec 29 23:38:54 2014 +0100 @@ -0,0 +1,2230 @@ +/* + * 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.*; +import static com.oracle.truffle.dsl.processor.java.ElementUtils.*; +import static javax.lang.model.element.Modifier.*; + +import java.util.*; + +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.internal.*; +import com.oracle.truffle.api.dsl.internal.DSLOptions.ImplicitCastOptimization; +import com.oracle.truffle.api.dsl.internal.DSLOptions.TypeBoxingOptimization; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.Node.Child; +import com.oracle.truffle.dsl.processor.*; +import com.oracle.truffle.dsl.processor.java.*; +import com.oracle.truffle.dsl.processor.java.model.*; +import com.oracle.truffle.dsl.processor.model.*; +import com.oracle.truffle.dsl.processor.parser.*; +import com.oracle.truffle.dsl.processor.parser.SpecializationGroup.TypeGuard; + +public class NodeGenFactory { + + private static final String FRAME_VALUE = "frameValue"; + + private final ProcessorContext context; + private final NodeData node; + private final TypeSystemData typeSystem; + private final TypeData genericType; + private final DSLOptions options; + + public NodeGenFactory(ProcessorContext context, NodeData node) { + this.context = context; + this.node = node; + this.typeSystem = node.getTypeSystem(); + this.genericType = typeSystem.getGenericTypeData(); + this.options = typeSystem.getOptions(); + } + + public static String nodeTypeName(NodeData node) { + return resolveNodeId(node) + "NodeGen"; + } + + private static String resolveNodeId(NodeData node) { + String nodeid = node.getNodeId(); + if (nodeid.endsWith("Node") && !nodeid.equals("Node")) { + nodeid = nodeid.substring(0, nodeid.length() - 4); + } + return nodeid; + } + + public static TypeMirror nodeType(NodeData node) { + return new GeneratedTypeMirror(ElementUtils.getPackageName(node.getTemplateType()), nodeTypeName(node)); + } + + private static String specializationTypeName(SpecializationData specialization) { + return specialization.getId() + "Node"; + } + + private static TypeMirror specializationType(SpecializationData specialization) { + return new GeneratedTypeMirror(ElementUtils.getPackageName(specialization.getNode().getTemplateType()) + "." + nodeTypeName(specialization.getNode()), specializationTypeName(specialization)); + } + + private static String polymorphicTypeProfileFieldName(NodeExecutionData execution) { + return execution.getName() + "Type_"; + } + + private static String nodeFieldName(NodeExecutionData execution) { + return execution.getName() + "_"; + } + + private static String specializationStartFieldName() { + return "specialization_"; + } + + private static String excludedFieldName(SpecializationData specialization) { + return "exclude" + specialization.getId() + "_"; + } + + private static String executeChildMethodName(NodeExecutionData execution, TypeData type) { + return "execute" + ElementUtils.firstLetterUpperCase(execution.getName()) + (type.isGeneric() ? "" : getTypeId(type.getBoxedType())) + "_"; + } + + private static CodeTree accessParent(String name) { + if (name == null) { + return CodeTreeBuilder.singleString("root"); + } else { + return CodeTreeBuilder.createBuilder().string("root.").string(name).build(); + } + } + + private static String assumptionName(String assumption) { + return assumption + "_"; + } + + public CodeTypeElement create() { + String typeName = nodeTypeName(node); + TypeMirror baseType = node.getTemplateType().asType(); + CodeTypeElement clazz = GeneratorUtils.createClass(node, null, modifiers(PUBLIC, FINAL), typeName, baseType); + + clazz.getImplements().add(getType(SpecializedNode.class)); + + for (String assumption : node.getAssumptions()) { + clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), getType(Assumption.class), assumptionName(assumption))); + } + + 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); + } + } + + List<? extends ExecutableElement> superConstructors = ElementFilter.constructorsIn(node.getTemplateType().getEnclosedElements()); + for (ExecutableElement superConstructor : superConstructors) { + if (getVisibility(superConstructor.getModifiers()) == PRIVATE) { + continue; + } + if (superConstructors.size() > 1 && superConstructor.getParameters().size() > 0 && + ElementUtils.typeEquals(superConstructor.getEnclosingElement().asType(), superConstructor.getParameters().get(0).asType())) { + // constructor is copy constructor + continue; + } + clazz.add(createNodeConstructor(clazz, superConstructor)); + } + + for (NodeExecutionData execution : node.getChildExecutions()) { + clazz.add(createNodeField(PRIVATE, execution.getNodeType(), nodeFieldName(execution), Child.class)); + } + + for (NodeExecutionData execution : node.getChildExecutions()) { + if (findSpecializedExecutables(execution, node.findSpecializedTypes(execution), options.polymorphicTypeBoxingElimination()).isEmpty()) { + continue; + } + clazz.add(createNodeField(PRIVATE, getType(Class.class), polymorphicTypeProfileFieldName(execution), CompilationFinal.class)); + } + + for (SpecializationData specialization : node.getSpecializations()) { + if (mayBeExcluded(specialization)) { + clazz.add(createNodeField(PRIVATE, getType(boolean.class), excludedFieldName(specialization), CompilationFinal.class)); + } + } + + clazz.add(createNodeField(PRIVATE, TypeSystemNodeFactory.nodeType(node.getTypeSystem()), specializationStartFieldName(), Child.class)); + clazz.add(createMethodGetSpecializationNode()); + clazz.add(createDeepCopyMethod()); + clazz.add(createGetCostMethod()); + + Collection<TypeData> specializedTypes = node.findSpecializedReturnTypes(); + for (ExecutableTypeData execType : node.getExecutableTypes()) { + if (shouldImplementExecutableType(specializedTypes, execType)) { + clazz.add(createExecutableTypeOverride(execType)); + } + } + + SpecializationData initialSpecialization = createSpecializations(clazz); + + for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) { + CodeTreeBuilder builder = ((CodeExecutableElement) constructor).appendBuilder(); + builder.startStatement(); + builder.string("this.").string(specializationStartFieldName()); + builder.string(" = ").tree(createCallCreateMethod(initialSpecialization, "this", null)); + builder.end(); + } + + return clazz; + } + + private CodeExecutableElement createNodeConstructor(CodeTypeElement clazz, ExecutableElement superConstructor) { + CodeExecutableElement constructor = GeneratorUtils.createConstructorUsingFields(modifiers(PUBLIC), clazz, superConstructor); + + 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()); + 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(callTemplateMethod(builder, null, createCast, nameTree)); + callBuilder.string(" : null"); + name += "_"; + builder.declaration(child.getNodeType(), name, callBuilder.build()); + } + } + childValues.add(name); + } + + for (NodeExecutionData execution : node.getChildExecutions()) { + 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.getIndex())).string("]"); + } + + CodeTree accessor = accessorBuilder.build(); + + if (createCast != null && execution.getChild().getCardinality().isOne()) { + accessor = callTemplateMethod(builder, null, createCast, accessor); + } + + if (execution.isIndexed()) { + CodeTreeBuilder nullCheck = builder.create(); + nullCheck.string(name).string(" != null ? "); + nullCheck.tree(accessor); + nullCheck.string(" : null"); + accessor = nullCheck.build(); + } + + builder.tree(accessor); + + builder.end(); + } + + return constructor; + } + + private static boolean mayBeExcluded(SpecializationData specialization) { + return !specialization.getExceptions().isEmpty() || !specialization.getExcludedBy().isEmpty(); + } + + private SpecializationData createSpecializations(CodeTypeElement clazz) { + List<SpecializationData> reachableSpecializations = getReachableSpecializations(); + + if (isSingleSpecializable(reachableSpecializations)) { + SpecializationData single = reachableSpecializations.get(0); + clazz.add(createSingleSpecialization(single)); + return single; + } else { + CodeTypeElement baseSpecialization = clazz.add(createBaseSpecialization(clazz)); + TypeMirror baseSpecializationType = baseSpecialization.asType(); + + Map<SpecializationData, CodeTypeElement> generated = new LinkedHashMap<>(); + + List<SpecializationData> generateSpecializations = new ArrayList<>(); + generateSpecializations.add(node.getUninitializedSpecialization()); + if (needsPolymorphic(reachableSpecializations)) { + generateSpecializations.add(node.getPolymorphicSpecialization()); + } + generateSpecializations.addAll(reachableSpecializations); + + for (SpecializationData specialization : generateSpecializations) { + generated.put(specialization, clazz.add(createSpecialization(specialization, baseSpecializationType))); + } + + baseSpecialization.addOptional(createCreateNext(generated)); + baseSpecialization.addOptional(createCreateFallback(generated)); + baseSpecialization.addOptional(createCreatePolymorphic(generated)); + + return node.getUninitializedSpecialization(); + } + } + + // create specialization + + private CodeTypeElement createBaseSpecialization(CodeTypeElement parentClass) { + CodeTypeElement clazz = createClass(node, null, modifiers(PRIVATE, STATIC, ABSTRACT), "BaseNode", TypeSystemNodeFactory.nodeType(typeSystem)); + + clazz.addOptional(createSpecializationConstructor(clazz, null, null)); + clazz.add(new CodeVariableElement(modifiers(PROTECTED, FINAL), nodeType(node), "root")); + + clazz.addOptional(createUnsupported()); + clazz.add(createGetSuppliedChildren()); + + int signatureSize = node.getSignatureSize(); + Set<Integer> evaluatedCount = getEvaluatedCounts(); + for (int evaluated : evaluatedCount) { + if (signatureSize != evaluated || signatureSize == 0) { + clazz.add(createFastPathExecuteMethod(null, evaluated > 0 ? null : genericType, evaluated)); + } + } + + for (NodeExecutionData execution : node.getChildExecutions()) { + Collection<TypeData> specializedTypes = node.findSpecializedTypes(execution); + specializedTypes.add(genericType); + for (TypeData specializedType : specializedTypes) { + if (isExecuteChildShared(execution, specializedType)) { + if (specializedType.isGeneric()) { + parentClass.add(createExecuteChildMethod(execution, specializedType)); + } else { + clazz.add(createExecuteChildMethod(execution, specializedType)); + } + } + } + } + + return clazz; + } + + private CodeTypeElement createSingleSpecialization(SpecializationData specialization) { + CodeTypeElement clazz = createClass(node, specialization, modifiers(PRIVATE, STATIC, FINAL), specializationTypeName(specialization), TypeSystemNodeFactory.nodeType(typeSystem)); + CodeExecutableElement constructor = clazz.addOptional(createSpecializationConstructor(clazz, null, "0")); + clazz.add(new CodeVariableElement(modifiers(PROTECTED, FINAL), nodeType(node), "root")); + TypeData returnType = specialization.getReturnType().getTypeSystemType(); + Set<Integer> evaluatedCount = getEvaluatedCounts(); + for (int evaluated : evaluatedCount) { + clazz.add(createFastPathExecuteMethod(specialization, null, evaluated)); + } + if (isTypeBoxingEliminated(specialization)) { + clazz.add(createFastPathExecuteMethod(specialization, returnType, 0)); + } + clazz.add(createFastPathWrapExecuteMethod(genericType, null)); + + clazz.addOptional(createUnsupported()); + clazz.addOptional(createSpecializationCreateMethod(specialization, constructor)); + clazz.add(createGetSuppliedChildren()); + + return clazz; + } + + private CodeTypeElement createSpecialization(SpecializationData specialization, TypeMirror baseType) { + CodeTypeElement clazz = createClass(node, specialization, modifiers(PRIVATE, STATIC, FINAL), specializationTypeName(specialization), baseType); + + CodeExecutableElement constructor = clazz.addOptional(createSpecializationConstructor(clazz, specialization, null)); + + for (Parameter p : specialization.getSignatureParameters()) { + TypeData targetType = p.getTypeSystemType(); + if (targetType.hasImplicitSourceTypes()) { + NodeExecutionData execution = p.getSpecification().getExecution(); + CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, p.getTypeSystemType()); + if (implicitProfile != null) { + implicitProfile.getModifiers().add(PRIVATE); + implicitProfile.getModifiers().add(FINAL); + clazz.add(implicitProfile); + } + } + } + + if (specialization.isFallback()) { + clazz.add(createFallbackGuardMethod()); + } + + clazz.addOptional(createSpecializationCreateMethod(specialization, constructor)); + clazz.addOptional(createMergeMethod(specialization)); + clazz.addOptional(createIsSameMethod(specialization)); + + TypeData returnType = specialization.getReturnType().getTypeSystemType(); + int signatureSize = specialization.getSignatureSize(); + + clazz.add(createFastPathExecuteMethod(specialization, null, signatureSize)); + + if (isTypeBoxingEliminated(specialization)) { + clazz.add(createFastPathExecuteMethod(specialization, returnType, 0)); + + if (signatureSize > 0 && !returnType.isGeneric()) { + clazz.add(createFastPathWrapExecuteMethod(genericType, returnType)); + } + + ExecutableTypeData voidExecutableType = node.findExecutableType(typeSystem.getVoidType(), 0); + if (voidExecutableType != null && isTypeBoxingOptimized(options.voidBoxingOptimization(), returnType)) { + clazz.add(createFastPathWrapVoidMethod(returnType)); + } + } + + return clazz; + } + + private Element createDeepCopyMethod() { + CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), getType(Node.class), "deepCopy"); + executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + CodeTreeBuilder builder = executable.createBuilder(); + builder.startReturn().startStaticCall(getType(SpecializationNode.class), "updateRoot").string("super.deepCopy()").end().end(); + return executable; + } + + 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(); + builder.startReturn().startCall(specializationStartFieldName(), "getNodeCost").end().end(); + return executable; + } + + private CodeExecutableElement createIsSameMethod(SpecializationData specialization) { + if (!specialization.isSpecialized() || !options.implicitCastOptimization().isDuplicateTail()) { + return null; + } + + List<CodeVariableElement> profiles = new ArrayList<>(); + for (Parameter parameter : specialization.getSignatureParameters()) { + NodeExecutionData execution = parameter.getSpecification().getExecution(); + if (execution == null) { + continue; + } + CodeVariableElement var = createImplicitProfileParameter(execution, parameter.getTypeSystemType()); + if (var != null) { + profiles.add(var); + } + } + + if (profiles.isEmpty()) { + return null; + } + + CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getType(boolean.class), "isSame"); + method.addParameter(new CodeVariableElement(getType(SpecializationNode.class), "other")); + method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + CodeTreeBuilder builder = method.createBuilder(); + + builder.startReturn(); + builder.string("super.isSame(other)"); + + for (CodeVariableElement profile : profiles) { + builder.string(" && "); + builder.string("this.").string(profile.getName()).string(" == ").string("(").cast(specializationType(specialization)).string("other).").string(profile.getName()); + } + + builder.end(); + return method; + } + + private Element createMergeMethod(SpecializationData specialization) { + if (specialization.getExcludedBy().isEmpty() && !specialization.isPolymorphic()) { + return null; + } + TypeMirror specializationNodeType = getType(SpecializationNode.class); + CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), specializationNodeType, "merge"); + executable.addParameter(new CodeVariableElement(specializationNodeType, "newNode")); + executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + CodeTreeBuilder builder = executable.createBuilder(); + + if (specialization.isPolymorphic()) { + builder.statement("return polymorphicMerge(newNode)"); + } else { + boolean elseIf = false; + for (SpecializationData containedSpecialization : specialization.getExcludedBy()) { + elseIf = builder.startIf(elseIf); + builder.string("newNode.getClass() == ").typeLiteral(specializationType(containedSpecialization)); + builder.end(); + builder.startBlock(); + builder.statement("removeSame(\"Contained by " + containedSpecialization.createReferenceName() + "\")"); + builder.end(); + } + builder.statement("return super.merge(newNode)"); + } + + return executable; + } + + private Element createFastPathWrapVoidMethod(TypeData wrap) { + CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), typeSystem.getVoidType().getPrimitiveType(), TypeSystemNodeFactory.executeName(typeSystem.getVoidType())); + executable.addParameter(new CodeVariableElement(getType(VirtualFrame.class), FRAME_VALUE)); + executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + CodeTreeBuilder builder = executable.createBuilder(); + builder.startStatement(); + builder.startCall(TypeSystemNodeFactory.voidBoxingExecuteName(wrap)); + builder.string(FRAME_VALUE); + builder.end(); + builder.end(); + + return executable; + } + + private Element createFastPathWrapExecuteMethod(TypeData override, TypeData wrap) { + CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), override.getPrimitiveType(), TypeSystemNodeFactory.executeName(override)); + executable.addParameter(new CodeVariableElement(getType(VirtualFrame.class), FRAME_VALUE)); + executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + CodeTreeBuilder builder = executable.createBuilder(); + if (wrap != null) { + builder.startTryBlock(); + } + builder.startReturn(); + builder.startCall(TypeSystemNodeFactory.executeName(wrap)); + builder.string(FRAME_VALUE); + builder.end(); + builder.end(); + if (wrap != null) { + builder.end().startCatchBlock(getType(UnexpectedResultException.class), "ex"); + builder.statement("return ex.getResult()"); + builder.end(); + } + + return executable; + } + + private boolean needsPolymorphic(List<SpecializationData> reachableSpecializations) { + if (reachableSpecializations.size() > 1) { + return true; + } + if (options.implicitCastOptimization().isDuplicateTail()) { + SpecializationData specialization = reachableSpecializations.get(0); + for (Parameter parameter : specialization.getSignatureParameters()) { + if (parameter.getTypeSystemType().hasImplicitSourceTypes()) { + return true; + } + } + } + return false; + } + + private Element createCreateFallback(Map<SpecializationData, CodeTypeElement> generatedSpecializationClasses) { + SpecializationData fallback = node.getGenericSpecialization(); + if (fallback == null) { + return null; + } + CodeTypeElement generatedType = generatedSpecializationClasses.get(fallback); + if (generatedType == null) { + return null; + } + + TypeMirror returnType = getType(SpecializationNode.class); + CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), returnType, "createFallback"); + method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + method.createBuilder().startReturn().tree(createCallCreateMethod(fallback, null, null)).end(); + return method; + } + + private Element createCreatePolymorphic(Map<SpecializationData, CodeTypeElement> generatedSpecializationClasses) { + SpecializationData polymorphic = node.getPolymorphicSpecialization(); + CodeTypeElement generatedPolymorphic = generatedSpecializationClasses.get(polymorphic); + if (generatedPolymorphic == null) { + return null; + } + TypeMirror returnType = getType(SpecializationNode.class); + CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), returnType, "createPolymorphic"); + method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + method.createBuilder().startReturn().tree(createCallCreateMethod(polymorphic, null, null)).end(); + return method; + } + + private CodeExecutableElement createCreateNext(final Map<SpecializationData, CodeTypeElement> specializationClasses) { + final LocalContext locals = LocalContext.load(this); + + CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), getType(SpecializationNode.class), "createNext", FRAME_VALUE); + method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + + CodeTreeBuilder builder = method.createBuilder(); + SpecializationGroup group = createSpecializationGroups(); + CodeTree execution = createGuardAndCast(group, genericType, locals, new SpecializationExecution() { + public CodeTree createExecute(SpecializationData specialization, LocalContext values) { + CodeTypeElement generatedType = specializationClasses.get(specialization); + if (generatedType == null) { + throw new AssertionError("No generated type for " + specialization); + } + return createSlowPathExecute(specialization, locals); + } + + public boolean isFastPath() { + return false; + } + }); + + builder.tree(execution); + + if (hasFallthrough(group, genericType, locals, false)) { + builder.returnNull(); + } + return method; + } + + private CodeExecutableElement createFallbackGuardMethod() { + boolean frameUsed = node.isFrameUsedByAnyGuard(context); + LocalContext locals = LocalContext.load(this); + + if (!frameUsed) { + locals.removeValue(FRAME_VALUE); + } + + CodeExecutableElement boundaryMethod = locals.createMethod(modifiers(PRIVATE), getType(boolean.class), "guardFallback", FRAME_VALUE); + if (!frameUsed) { + boundaryMethod.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(TruffleBoundary.class))); + } + + CodeTreeBuilder builder = boundaryMethod.createBuilder(); + builder.startReturn(); + builder.startCall("createNext"); + locals.addReferencesTo(builder, FRAME_VALUE); + builder.end(); + builder.string(" == null"); + builder.end(); + return boundaryMethod; + } + + 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 boolean isTypeBoxingEliminated(SpecializationData specialization) { + if (specialization.getMethod() == null) { + return false; + } + + TypeBoxingOptimization optimization = options.monomorphicTypeBoxingOptimization(); + if (isTypeBoxingOptimized(optimization, specialization.getReturnType().getTypeSystemType())) { + return true; + } + for (Parameter p : specialization.getSignatureParameters()) { + if (isTypeBoxingOptimized(optimization, p.getTypeSystemType())) { + return true; + } + } + return false; + + } + + private Set<Integer> getEvaluatedCounts() { + Set<Integer> evaluatedCount = new TreeSet<>(); + Collection<TypeData> returnSpecializedTypes = node.findSpecializedReturnTypes(); + for (ExecutableTypeData execType : node.getExecutableTypes()) { + if (shouldImplementExecutableType(returnSpecializedTypes, execType)) { + evaluatedCount.add(execType.getEvaluatedCount()); + } + } + return evaluatedCount; + } + + // create specialization + + private Element createUnsupported() { + SpecializationData fallback = node.getGenericSpecialization(); + if (fallback == null || optimizeFallback(fallback) || fallback.getMethod() == null) { + return null; + } + LocalContext locals = LocalContext.load(this); + + CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), genericType.getPrimitiveType(), "unsupported", FRAME_VALUE); + method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + + CodeTreeBuilder builder = method.createBuilder(); + builder.startReturn(); + builder.tree(callTemplateMethod(builder, accessParent(null), fallback, locals)); + builder.end(); + + return method; + } + + private boolean isSingleSpecializable(List<SpecializationData> reachableSpecializations) { + if (reachableSpecializations.size() != 1) { + return false; + } + return !reachableSpecializations.get(0).hasRewrite(context); + } + + private List<SpecializationData> getReachableSpecializations() { + List<SpecializationData> specializations = new ArrayList<>(); + for (SpecializationData specialization : node.getSpecializations()) { + if (specialization.isReachable() && // + (specialization.isSpecialized() // + || (specialization.isFallback() && optimizeFallback(specialization)))) { + specializations.add(specialization); + } + } + return specializations; + } + + private boolean optimizeFallback(SpecializationData specialization) { + switch (options.optimizeFallback()) { + case NEVER: + return false; + case DECLARED: + return specialization.getMethod() != null; + case ALWAYS: + return true; + default: + throw new AssertionError(); + } + } + + private CodeExecutableElement createExecutableTypeOverride(ExecutableTypeData execType) { + final String varArgsName = "args"; + final TypeData returnType = execType.getType(); + final TypeData executedType = execType.getEvaluatedCount() > 0 ? null : returnType; + + CodeExecutableElement method = cloneExecutableTypeOverride(execType, varArgsName); + + LocalContext locals = LocalContext.load(this, execType.getSignatureSize()); + + // rename varargs parameter + int signatureIndex = 0; + for (Parameter parameter : execType.getSignatureParameters()) { + if (parameter.isTypeVarArgs()) { + String newName = varArgsName + "[" + parameter.getTypeVarArgsIndex() + "]"; + NodeExecutionData execution = node.getChildExecutions().get(signatureIndex); + locals.setValue(execution, locals.getValue(execution).accessWith(CodeTreeBuilder.singleString(newName))); + } + signatureIndex++; + } + + CodeTreeBuilder builder = method.createBuilder(); + + // create acceptAndExecute + CodeTreeBuilder executeBuilder = builder.create(); + executeBuilder.startCall(specializationStartFieldName(), TypeSystemNodeFactory.executeName(executedType)); + Parameter frame = execType.getFrame(); + if (frame == null) { + executeBuilder.nullLiteral(); + } else { + executeBuilder.string(frame.getLocalName()); + } + locals.addReferencesTo(executeBuilder); + executeBuilder.end(); + + CodeTreeBuilder contentBuilder = builder.create(); + contentBuilder.startReturn(); + contentBuilder.tree(TypeSystemCodeGenerator.expect(executedType, returnType, executeBuilder.build())); + contentBuilder.end(); + + // try catch assert if unexpected value is not expected + if (!execType.hasUnexpectedValue(context) && !returnType.isGeneric() && !returnType.isVoid()) { + builder.startTryBlock(); + builder.tree(contentBuilder.build()); + builder.end().startCatchBlock(getType(UnexpectedResultException.class), "ex"); + builder.startThrow().startNew(getType(AssertionError.class)).end().end(); + builder.end(); + } else { + builder.tree(contentBuilder.build()); + } + + return method; + } + + private CodeExecutableElement cloneExecutableTypeOverride(ExecutableTypeData execType, final String varArgsName) throws AssertionError { + CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), execType.getMethod()); + + method.getAnnotationMirrors().clear(); + method.getModifiers().remove(Modifier.ABSTRACT); + + if (!execType.getMethod().isVarArgs() && execType.getParameters().size() != method.getParameters().size()) { + throw new AssertionError("Should be verified in the parser"); + } + + // align argument names + int index = 0; + for (Parameter parameter : execType.getParameters()) { + CodeVariableElement var = (CodeVariableElement) method.getParameters().get(index); + if (parameter.isTypeVarArgs()) { + var.getAnnotationMirrors().clear(); + var.setName(varArgsName); + break; + } + var.setName(LocalVariable.fromParameter(parameter).createParameter().getName()); + var.getAnnotationMirrors().clear(); + index++; + } + return method; + } + + private boolean shouldImplementExecutableType(Collection<TypeData> specializedTypes, ExecutableTypeData execType) { + TypeData type = execType.getType(); + Set<Modifier> modifiers = execType.getMethod().getModifiers(); + if (modifiers.contains(FINAL) || modifiers.contains(STATIC) || modifiers.contains(PRIVATE)) { + return false; + } else if (execType.isAbstract()) { + return true; + } else if (type.isGeneric()) { + return true; + } else if (type.isVoid()) { + for (TypeData specializedType : specializedTypes) { + if (isTypeBoxingOptimized(options.voidBoxingOptimization(), specializedType)) { + return true; + } + } + return false; + } else if (!specializedTypes.contains(type)) { + return false; + } else if (!isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), type)) { + return false; + } + return true; + } + + private Element createMethodGetSpecializationNode() { + TypeMirror returntype = getType(SpecializationNode.class); + CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), returntype, "getSpecializationNode"); + method.createBuilder().startReturn().string(specializationStartFieldName()).end(); + return method; + } + + private TypeMirror getType(Class<?> clazz) { + return context.getType(clazz); + } + + private CodeVariableElement createNodeField(Modifier visibility, TypeMirror type, String name, Class<?> annotationType) { + CodeVariableElement childField = new CodeVariableElement(modifiers(), type, name); + childField.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(annotationType))); + setVisibility(childField.getModifiers(), visibility); + return childField; + } + + private static List<ExecutableTypeData> findSpecializedExecutables(NodeExecutionData execution, Collection<TypeData> types, TypeBoxingOptimization optimization) { + if (optimization == TypeBoxingOptimization.NONE) { + return Collections.emptyList(); + } + + List<ExecutableTypeData> executables = new ArrayList<>(); + for (TypeData type : types) { + if (!isTypeBoxingOptimized(optimization, type)) { + continue; + } + ExecutableTypeData foundType = execution.getChild().getNodeData().findExecutableType(type, execution.getChild().getExecuteWith().size()); + if (foundType != null) { + executables.add(foundType); + } + } + return executables; + } + + private static CodeTree callTemplateMethod(CodeTreeBuilder parent, CodeTree receiver, TemplateMethod method, CodeTree... boundValues) { + CodeTreeBuilder builder = parent.create(); + if (method.getMethod().getModifiers().contains(STATIC)) { + builder.startStaticCall(method.getMethod().getEnclosingElement().asType(), method.getMethodName()); + } else { + builder.startCall(receiver, method.getMethodName()); + } + int index = -1; + for (Parameter parameter : method.getParameters()) { + index++; + if (index < boundValues.length) { + CodeTree tree = boundValues[index]; + if (tree != null) { + builder.tree(tree); + continue; + } + } + builder.string(parameter.getLocalName()); + } + builder.end(); + return builder.build(); + } + + private static CodeTree callTemplateMethod(CodeTreeBuilder parent, CodeTree receiver, TemplateMethod method, LocalContext currentValues) { + CodeTree[] bindings = new CodeTree[method.getParameters().size()]; + + int signatureIndex = 0; + for (int i = 0; i < bindings.length; i++) { + Parameter parameter = method.getParameters().get(i); + LocalVariable var = currentValues.get(parameter, signatureIndex); + if (var != null) { + CodeTree valueReference = bindings[i] = var.createReference(); + if (parameter.getTypeSystemType() != null && var.getType() != null && var.getType().needsCastTo(parameter.getTypeSystemType())) { + valueReference = TypeSystemCodeGenerator.cast(parameter.getTypeSystemType(), valueReference); + } + bindings[i] = valueReference; + } + if (parameter.getSpecification().isSignature()) { + signatureIndex++; + } + } + return callTemplateMethod(parent, receiver, method, bindings); + } + + private SpecializationGroup createSpecializationGroups() { + return SpecializationGroup.create(getReachableSpecializations()); + } + + private CodeTree createSlowPathExecute(SpecializationData specialization, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + if (specialization.isFallback()) { + return builder.returnNull().build(); + } + if (node.isFrameUsedByAnyGuard(context)) { + builder.tree(createTransferToInterpreterAndInvalidate()); + } + for (SpecializationData otherSpeciailzation : node.getSpecializations()) { + if (otherSpeciailzation == specialization) { + continue; + } + if (otherSpeciailzation.getExcludedBy().contains(specialization)) { + builder.startStatement(); + builder.tree(accessParent(excludedFieldName(otherSpeciailzation))); + builder.string(" = true"); + builder.end(); + } + } + + builder.startReturn().tree(createCallCreateMethod(specialization, null, currentValues)).end(); + + if (mayBeExcluded(specialization)) { + CodeTreeBuilder checkHasSeenBuilder = builder.create(); + checkHasSeenBuilder.startIf().string("!").tree(accessParent(excludedFieldName(specialization))).end().startBlock(); + checkHasSeenBuilder.tree(builder.build()); + checkHasSeenBuilder.end(); + return checkHasSeenBuilder.build(); + } + return builder.build(); + } + + private static boolean hasFallthrough(SpecializationGroup group, TypeData forType, LocalContext currentValues, boolean fastPath) { + for (TypeGuard guard : group.getTypeGuards()) { + if (currentValues.getValue(guard.getSignatureIndex()) == null) { + // not evaluated + return true; + } + LocalVariable value = currentValues.getValue(guard.getSignatureIndex()); + if (value.getType().needsCastTo(guard.getType())) { + return true; + } + } + + List<GuardExpression> expressions = new ArrayList<>(group.getGuards()); + expressions.removeAll(group.findElseConnectableGuards()); + if (!expressions.isEmpty()) { + return true; + } + + if ((!fastPath || forType.isGeneric()) && !group.getAssumptions().isEmpty()) { + return true; + } + + if (!fastPath && group.getSpecialization() != null && !group.getSpecialization().getExceptions().isEmpty()) { + return true; + } + + if (!group.getChildren().isEmpty()) { + return hasFallthrough(group.getChildren().get(group.getChildren().size() - 1), forType, currentValues, fastPath); + } + + return false; + } + + private Element createGetSuppliedChildren() { + ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class)); + + CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), nodeArray, "getSuppliedChildren"); + method.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + + CodeTreeBuilder builder = method.createBuilder(); + + builder.startReturn().startNewArray(nodeArray, null); + for (int i = 0; i < node.getChildExecutions().size(); i++) { + NodeExecutionData execution = node.getChildExecutions().get(i); + if (execution.isShortCircuit()) { + builder.nullLiteral(); + } + builder.tree(accessParent(nodeFieldName(execution))); + } + builder.end().end(); + + return method; + } + + // create specialization + + private CodeTree createCallCreateMethod(SpecializationData specialization, String rootName, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + + TypeMirror specializationType = specializationType(specialization); + if (options.useLazyClassLoading()) { + builder.startStaticCall(specializationType(specialization), "create"); + } else { + builder.startNew(specializationType); + } + if (rootName != null) { + builder.string(rootName); + } else { + builder.string("root"); + } + if (currentValues != null) { + for (Parameter p : specialization.getSignatureParameters()) { + LocalVariable local = currentValues.get(p.getLocalName()); + CodeVariableElement var = createImplicitProfileParameter(p.getSpecification().getExecution(), p.getTypeSystemType()); + if (var != null) { + builder.tree(local.createReference()); + } + } + } + builder.end(); + + return builder.build(); + } + + private Element createSpecializationCreateMethod(SpecializationData specialization, CodeExecutableElement constructor) { + if (!options.useLazyClassLoading()) { + return null; + } + + CodeExecutableElement executable = CodeExecutableElement.clone(context.getEnvironment(), constructor); + + TypeMirror specializationType = specializationType(specialization); + + executable.setReturnType(TypeSystemNodeFactory.nodeType(typeSystem)); + executable.setSimpleName(CodeNames.of("create")); + executable.getModifiers().add(STATIC); + + CodeTreeBuilder builder = executable.createBuilder(); + builder.startReturn().startNew(specializationType); + for (VariableElement parameter : executable.getParameters()) { + builder.string(parameter.getSimpleName().toString()); + } + builder.end().end(); + return executable; + } + + private static String implicitClassFieldName(NodeExecutionData execution) { + return execution.getName() + "ImplicitType"; + } + + private static String implicitNodeFieldName(NodeExecutionData execution) { + return execution.getName() + "Cast"; + } + + private CodeExecutableElement createSpecializationConstructor(CodeTypeElement clazz, SpecializationData specialization, String constantIndex) { + CodeExecutableElement constructor = new CodeExecutableElement(modifiers(), null, clazz.getSimpleName().toString()); + + constructor.addParameter(new CodeVariableElement(nodeType(node), "root")); + CodeTreeBuilder builder = constructor.createBuilder(); + + if (specialization == null) { + if (constantIndex == null) { + builder.statement("super(index)"); + constructor.addParameter(new CodeVariableElement(getType(int.class), "index")); + } else { + builder.startStatement().startSuperCall().string(constantIndex).end().end(); + } + builder.statement("this.root = root"); + } else { + int index = resolveSpecializationIndex(specialization); + builder.startStatement().startSuperCall().string("root").string(String.valueOf(index)).end().end(); + + for (Parameter p : specialization.getSignatureParameters()) { + NodeExecutionData execution = p.getSpecification().getExecution(); + + CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, p.getTypeSystemType()); + if (implicitProfile != null) { + LocalVariable var = LocalVariable.fromParameter(p).makeGeneric(); + + String implicitFieldName = implicitProfile.getName(); + if (options.implicitCastOptimization().isDuplicateTail()) { + constructor.addParameter(var.createParameter()); + CodeTree implicitType = TypeSystemCodeGenerator.implicitType(p.getTypeSystemType(), var.createReference()); + builder.startStatement().string("this.").string(implicitFieldName).string(" = ").tree(implicitType).end(); + } else if (options.implicitCastOptimization().isMergeCasts()) { + // use node that supports polymorphism + constructor.addParameter(var.createParameter()); + builder.startStatement().string("this.").string(implicitFieldName).string(" = ").tree(ImplicitCastNodeFactory.create(p.getTypeSystemType(), var.createReference())).end(); + } else { + throw new AssertionError(); + } + } + } + } + + if (constructor.getParameters().isEmpty()) { + // do not generate default constructor + return null; + } + return constructor; + } + + // TODO this logic can be inlined to the parser as soon as the old NodeGen layout is gone + private static int resolveSpecializationIndex(SpecializationData specialization) { + if (specialization.isFallback()) { + return Integer.MAX_VALUE - 1; + } else if (specialization.isUninitialized()) { + return Integer.MAX_VALUE; + } else if (specialization.isPolymorphic()) { + return 0; + } else { + return specialization.getIndex(); + } + } + + private CodeTree createCallNext(TypeData forType, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + builder.startCall("next", TypeSystemNodeFactory.executeName(null)); + currentValues.addReferencesTo(builder, FRAME_VALUE); + builder.end(); + return TypeSystemCodeGenerator.expect(genericType, forType, builder.build()); + } + + private static CodeTree createCallDelegate(String methodName, String reason, TypeData forType, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + builder.startCall(methodName); + if (reason != null) { + builder.doubleQuote(reason); + } + currentValues.addReferencesTo(builder, FRAME_VALUE); + builder.end(); + + TypeData executedType = forType.getTypeSystem().getGenericTypeData(); + return TypeSystemCodeGenerator.expect(executedType, forType, builder.build()); + } + + private static ExecutableTypeData findSpecializedExecutableType(NodeExecutionData execution, TypeData type) { + NodeChildData child = execution.getChild(); + int executeWithCount = child.getExecuteWith().size(); + return child.getNodeData().findExecutableType(type, executeWithCount); + } + + private boolean hasUnexpectedResult(NodeExecutionData execution, TypeData type) { + if (type.isGeneric() || type.isVoid()) { + return false; + } + List<ExecutableTypeData> executableTypes = new ArrayList<>(); + executableTypes.add(findSpecializedExecutableType(execution, type)); + + if (!options.implicitCastOptimization().isNone()) { + executableTypes.addAll(findSpecializedExecutables(execution, type.getImplicitSourceTypes(), options.implicitTypeBoxingOptimization())); + } + + for (ExecutableTypeData executableType : executableTypes) { + if (executableType != null && executableType.hasUnexpectedValue(context)) { + return true; + } + } + return false; + } + + private Element createFastPathExecuteMethod(SpecializationData specialization, final TypeData forType, int evaluatedArguments) { + TypeData type = forType == null ? genericType : forType; + LocalContext currentLocals = LocalContext.load(this, evaluatedArguments); + + CodeExecutableElement executable = currentLocals.createMethod(modifiers(PUBLIC), type.getPrimitiveType(), TypeSystemNodeFactory.executeName(forType), FRAME_VALUE); + executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + + if (!type.isGeneric()) { + executable.getThrownTypes().add(getType(UnexpectedResultException.class)); + } + + CodeTreeBuilder builder = executable.createBuilder(); + + for (NodeExecutionData execution : node.getChildExecutions()) { + LocalVariable var = currentLocals.getValue(execution); + if (var == null) { + TypeData targetType; + if (specialization == null) { + targetType = genericType; + } else { + targetType = specialization.findParameterOrDie(execution).getTypeSystemType(); + } + LocalVariable shortCircuit = resolveShortCircuit(specialization, execution, currentLocals); + LocalVariable value = currentLocals.createValue(execution, targetType).nextName(); + builder.tree(createAssignExecuteChild(execution, type, value, shortCircuit, currentLocals)); + currentLocals.setValue(execution, value); + } + } + + LocalContext originalValues = currentLocals.copy(); + if (specialization == null) { + builder.startReturn().tree(createCallDelegate("acceptAndExecute", null, type, currentLocals)).end(); + } else if (specialization.isPolymorphic()) { + builder.startReturn().tree(createCallNext(type, currentLocals)).end(); + } else if (specialization.isUninitialized()) { + builder.startReturn().tree(createCallDelegate("uninitialized", null, type, currentLocals)).end(); + } else { + final TypeData type_ = type; + SpecializationGroup group = SpecializationGroup.create(specialization); + SpecializationExecution executionFactory = new SpecializationExecution() { + public CodeTree createExecute(SpecializationData s, LocalContext values) { + return createFastPathExecute(type_, s, values); + } + + public boolean isFastPath() { + return true; + } + }; + builder.tree(createGuardAndCast(group, type, currentLocals, executionFactory)); + if (hasFallthrough(group, type, originalValues, true) || group.getSpecialization().isFallback()) { + builder.startReturn().tree(createCallNext(type, originalValues)).end(); + } + } + + return executable; + } + + private LocalVariable resolveShortCircuit(SpecializationData specialization, NodeExecutionData execution, LocalContext currentLocals) { + LocalVariable shortCircuit = null; + SpecializationData resolvedSpecialization = specialization; + if (specialization == null) { + resolvedSpecialization = node.getGenericSpecialization(); + } + + if (execution.isShortCircuit()) { + ShortCircuitData shortCircuitData = resolvedSpecialization.getShortCircuits().get(calculateShortCircuitIndex(execution)); + CodeTree access = callTemplateMethod(CodeTreeBuilder.createBuilder(), accessParent(null), shortCircuitData, currentLocals); + shortCircuit = currentLocals.createShortCircuitValue(execution).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(final TypeData forType, SpecializationData specialization, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + int ifCount = 0; + if (specialization.isFallback()) { + builder.startIf().startCall("guardFallback"); + if (node.isFrameUsedByAnyGuard(context)) { + builder.string(FRAME_VALUE); + } + currentValues.addReferencesTo(builder); + + builder.end(); + builder.end(); + builder.startBlock(); + ifCount++; + } + CodeTreeBuilder execute = builder.create(); + execute.startReturn(); + if (specialization.getMethod() == null) { + execute.startCall("unsupported"); + currentValues.addReferencesTo(execute, FRAME_VALUE); + execute.end(); + } else { + execute.tree(callTemplateMethod(execute, accessParent(null), specialization, currentValues)); + } + execute.end(); + builder.tree(createFastPathTryCatchRewriteException(specialization, forType, currentValues, execute.build())); + + builder.end(ifCount); + return builder.build(); + } + + private CodeTree createGuardAndCast(SpecializationGroup group, TypeData forType, LocalContext currentValues, SpecializationExecution execution) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + + Set<TypeGuard> castGuards; + if (execution.isFastPath()) { + castGuards = null; // cast all + } else { + castGuards = new HashSet<>(); + for (TypeGuard castGuard : group.getTypeGuards()) { + if (isTypeGuardUsedInAnyGuardBelow(group, currentValues, castGuard)) { + castGuards.add(castGuard); + } + } + } + CodeTree[] checkAndCast = createTypeCheckAndCast(group.getTypeGuards(), castGuards, currentValues, execution); + CodeTree check = checkAndCast[0]; + CodeTree cast = checkAndCast[1]; + + List<GuardExpression> elseGuardExpressions = group.findElseConnectableGuards(); + List<GuardExpression> guardExpressions = new ArrayList<>(group.getGuards()); + guardExpressions.removeAll(elseGuardExpressions); + CodeTree methodGuards = createMethodGuardCheck(guardExpressions, currentValues); + + if (!group.getAssumptions().isEmpty()) { + if (execution.isFastPath() && !forType.isGeneric()) { + cast = appendAssumptionFastPath(cast, group.getAssumptions(), forType, currentValues); + } else { + methodGuards = appendAssumptionSlowPath(methodGuards, group.getAssumptions()); + } + } + + int ifCount = 0; + if (!check.isEmpty()) { + builder.startIf(); + builder.tree(check).end(); + builder.startBlock(); + ifCount++; + } + if (!cast.isEmpty()) { + builder.tree(cast); + } + boolean elseIf = !elseGuardExpressions.isEmpty(); + if (!methodGuards.isEmpty()) { + builder.startIf(elseIf); + builder.tree(methodGuards).end(); + builder.startBlock(); + ifCount++; + } else if (elseIf) { + builder.startElseBlock(); + ifCount++; + } + + boolean reachable = isReachableGroup(group, ifCount); + if (reachable) { + for (SpecializationGroup child : group.getChildren()) { + builder.tree(createGuardAndCast(child, forType, currentValues.copy(), execution)); + } + SpecializationData specialization = group.getSpecialization(); + if (specialization != null) { + builder.tree(execution.createExecute(specialization, currentValues)); + } + } + builder.end(ifCount); + + return builder.build(); + } + + private static CodeTree appendAssumptionSlowPath(CodeTree methodGuards, List<String> assumptions) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + + builder.tree(methodGuards); + String connect = methodGuards.isEmpty() ? "" : " && "; + for (String assumption : assumptions) { + builder.string(connect); + builder.startCall(accessParent(assumptionName(assumption)), "isValid").end(); + connect = " && "; + } + + return builder.build(); + } + + private CodeTree appendAssumptionFastPath(CodeTree casts, List<String> assumptions, TypeData forType, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + builder.tree(casts); + builder.startTryBlock(); + for (String assumption : assumptions) { + builder.startStatement().startCall(accessParent(assumptionName(assumption)), "check").end().end(); + } + builder.end().startCatchBlock(getType(InvalidAssumptionException.class), "ae"); + builder.startReturn().tree(createCallNext(forType, currentValues)).end(); + builder.end(); + return builder.build(); + } + + private static boolean isReachableGroup(SpecializationGroup group, int ifCount) { + if (ifCount != 0) { + return true; + } + SpecializationGroup previous = group.getPreviousGroup(); + if (previous == null || previous.findElseConnectableGuards().isEmpty()) { + return true; + } + + /* + * Hacky else case. In this case the specialization is not reachable due to previous else + * branch. This is only true if the minimum state is not checked. + */ + if (previous.getGuards().size() == 1 && previous.getTypeGuards().isEmpty() && previous.getAssumptions().isEmpty() && + (previous.getParent() == null || previous.getMaxSpecializationIndex() != previous.getParent().getMaxSpecializationIndex())) { + return false; + } + + return true; + } + + private boolean isTypeGuardUsedInAnyGuardBelow(SpecializationGroup group, LocalContext currentValues, TypeGuard typeGuard) { + NodeExecutionData execution = node.getChildExecutions().get(typeGuard.getSignatureIndex()); + + for (GuardExpression guard : group.getGuards()) { + List<Parameter> guardParameters = guard.getResolvedGuard().findByExecutionData(execution); + TypeData sourceType = currentValues.getValue(typeGuard.getSignatureIndex()).getType(); + + for (Parameter guardParameter : guardParameters) { + if (sourceType.needsCastTo(guardParameter.getType())) { + return true; + } + } + } + + for (SpecializationGroup child : group.getChildren()) { + if (isTypeGuardUsedInAnyGuardBelow(child, currentValues, typeGuard)) { + return true; + } + } + + return false; + } + + private CodeExecutableElement createExecuteChildMethod(NodeExecutionData execution, TypeData targetType) { + LocalContext locals = LocalContext.load(this, 0); + + CodeExecutableElement method = locals.createMethod(modifiers(PROTECTED, FINAL), targetType.getPrimitiveType(), executeChildMethodName(execution, targetType), FRAME_VALUE); + if (hasUnexpectedResult(execution, targetType)) { + method.getThrownTypes().add(getType(UnexpectedResultException.class)); + } + + CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, targetType); + if (implicitProfile != null) { + method.addParameter(implicitProfile); + } + + for (int i = 0; i < execution.getChild().getExecuteWith().size(); i++) { + NodeExecutionData executeWith = node.getChildExecutions().get(i); + LocalVariable var = locals.createValue(executeWith, genericType); + method.addParameter(var.createParameter()); + locals.setValue(executeWith, var); + } + + CodeTreeBuilder builder = method.createBuilder(); + CodeTree executeChild = createExecuteChild(execution, targetType, locals.createValue(execution, targetType), locals, true); + if (executeChild.isSingleLine()) { + builder.statement(executeChild); + } else { + builder.tree(executeChild); + } + return method; + } + + private CodeVariableElement createImplicitProfileParameter(NodeExecutionData execution, TypeData targetType) { + if (targetType.hasImplicitSourceTypes()) { + switch (options.implicitCastOptimization()) { + case NONE: + return null; + case DUPLICATE_TAIL: + return new CodeVariableElement(getType(Class.class), implicitClassFieldName(execution)); + case MERGE_CASTS: + return new CodeVariableElement(ImplicitCastNodeFactory.type(targetType), implicitNodeFieldName(execution)); + } + } + return null; + } + + private boolean isExecuteChildShared(NodeExecutionData execution, TypeData targetType) { + if (targetType.isVoid()) { + return false; + } else if (targetType.isGeneric()) { + if (isSingleSpecializable(getReachableSpecializations())) { + return false; + } + return findSpecializedExecutables(execution, node.findSpecializedTypes(execution), options.polymorphicTypeBoxingElimination()).size() >= 1; + } else { + if (!isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), targetType)) { + return false; + } + if (!targetType.hasImplicitSourceTypes()) { + return false; + } + + int uses = 0; + for (SpecializationData specialization : node.getSpecializations()) { + List<Parameter> parameters = specialization.findByExecutionData(execution); + for (Parameter parameter : parameters) { + if (targetType.equals(parameter.getTypeSystemType())) { + uses++; + } + } + } + if (uses > 1) { + return findSpecializedExecutables(execution, targetType.getImplicitSourceTypes(), options.implicitTypeBoxingOptimization()).size() > 1; + } else { + return false; + } + } + } + + private CodeTree createAssignExecuteChild(NodeExecutionData execution, TypeData returnType, LocalVariable targetValue, LocalVariable shortCircuit, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + boolean hasUnexpected = hasUnexpectedResult(execution, targetValue.getType()); + + CodeTree executeChild; + if (isExecuteChildShared(execution, targetValue.getType())) { + executeChild = createCallSharedExecuteChild(execution, targetValue, currentValues); + } else { + executeChild = createExecuteChild(execution, targetValue.getType(), targetValue, currentValues, false); + } + + builder.tree(createTryExecuteChild(targetValue, executeChild, shortCircuit == null, hasUnexpected)); + builder.end(); + if (hasUnexpected) { + builder.startCatchBlock(getType(UnexpectedResultException.class), "ex"); + + LocalContext slowPathValues = currentValues.copy(); + slowPathValues.setValue(execution, targetValue.makeGeneric().accessWith(CodeTreeBuilder.singleString("ex.getResult()"))); + boolean found = false; + for (NodeExecutionData otherExecution : node.getChildExecutions()) { + if (found) { + LocalVariable childEvaluatedValue = slowPathValues.createValue(otherExecution, genericType); + LocalVariable genericShortCircuit = resolveShortCircuit(null, otherExecution, slowPathValues); + builder.tree(createAssignExecuteChild(otherExecution, genericType, childEvaluatedValue, genericShortCircuit, slowPathValues)); + slowPathValues.setValue(otherExecution, childEvaluatedValue); + } else { + // skip forward already evaluated + found = execution == otherExecution; + } + } + builder.startReturn().tree(createCallNext(returnType, slowPathValues)).end(); + builder.end(); + } + + if (shortCircuit != null) { + currentValues.setShortCircuitValue(execution, shortCircuit.accessWith(null)); + } + return createShortCircuit(targetValue, shortCircuit, 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 CodeTree createCallSharedExecuteChild(NodeExecutionData execution, LocalVariable targetValue, LocalContext currentValues) { + if (!isExecuteChildShared(execution, targetValue.getType())) { + throw new AssertionError("Execute child not shared with method but called."); + } + + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + builder.tree(targetValue.createReference()).string(" = "); + if (targetValue.getType().isGeneric()) { + builder.startCall("root", executeChildMethodName(execution, targetValue.getType())); + } else { + builder.startCall(executeChildMethodName(execution, targetValue.getType())); + } + builder.string(FRAME_VALUE); + + CodeVariableElement implicitProfile = createImplicitProfileParameter(execution, targetValue.getType()); + if (implicitProfile != null) { + builder.string(implicitProfile.getName()); + } + for (int i = 0; i < execution.getChild().getExecuteWith().size(); i++) { + builder.tree(currentValues.getValue(i).createReference()); + } + builder.end(); + return builder.build(); + } + + private CodeTree createExecuteChild(NodeExecutionData execution, TypeData returnType, LocalVariable target, LocalContext currentValues, boolean shared) { + final CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + final ExecutableTypeData executableType = findSpecializedExecutableType(execution, target.getType()); + + CodeTree assignment = createAssignmentStart(target, shared, false); + + if (executableType == null) { + if (target.getType().isGeneric()) { + throw new AssertionError("Should be caught by the parser."); + } + CodeTree genericExecute = createExecuteChild(execution, returnType, target.makeGeneric(), currentValues, shared); + builder.tree(genericExecute); + } else { + if (target.getType().isGeneric() && executableType.getEvaluatedCount() == 0) { + return createPolymorphicExecuteChild(execution, target, currentValues, shared); + } else if (target.getType().hasImplicitSourceTypes()) { + if (options.implicitCastOptimization().isNone()) { + CodeTree execute = createCallSharedExecuteChild(execution, target.makeGeneric(), currentValues); + return TypeSystemCodeGenerator.implicitExpect(target.getType(), execute, null); + } else if (options.implicitCastOptimization().isDuplicateTail()) { + builder.tree(createExecuteChildDuplicateTail(builder, execution, assignment, target, currentValues)); + } else if (options.implicitCastOptimization().isMergeCasts()) { + // TODO + } else { + throw new AssertionError(); + } + } else { + builder.tree(assignment); + + CodeTree accessChild; + if (shared && target.getType().isGeneric()) { + accessChild = CodeTreeBuilder.singleString(nodeFieldName(execution)); + } else { + accessChild = accessParent(nodeFieldName(execution)); + } + + CodeTree execute = callTemplateMethod(builder, accessChild, executableType, currentValues); + CodeTree expect = TypeSystemCodeGenerator.expect(executableType.getType(), returnType, execute); + builder.tree(expect); + } + } + return builder.build(); + } + + private CodeTree createPolymorphicExecuteChild(NodeExecutionData execution, LocalVariable target, LocalContext currentValues, boolean shared) throws AssertionError { + ExecutableTypeData genericExecutableType = execution.getChild().getNodeData().findAnyGenericExecutableType(context, execution.getChild().getExecuteWith().size()); + if (genericExecutableType == null) { + throw new AssertionError("error should be caught by the parser"); + } + + CodeTree assignment = createAssignmentStart(target, shared, true); + + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + CodeTreeBuilder polyChainBuilder = builder.create(); + boolean hasUnexpectedResult = false; + + Set<TypeData> specializedTypes = new HashSet<>(); + for (TypeData type : node.findSpecializedTypes(execution)) { + specializedTypes.addAll(type.getImplicitSourceTypes()); + } + + List<ExecutableTypeData> specializedExecutables = findSpecializedExecutables(execution, specializedTypes, options.polymorphicTypeBoxingElimination()); + + Collections.sort(specializedExecutables, new Comparator<ExecutableTypeData>() { + public int compare(ExecutableTypeData o1, ExecutableTypeData o2) { + return o1.getType().compareTo(o2.getType()); + } + }); + + if (isSingleSpecializable(getReachableSpecializations())) { + specializedExecutables = Collections.emptyList(); + } + + boolean hasSpecializedTypes = false; + for (ExecutableTypeData executableType : specializedExecutables) { + hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes); + polyChainBuilder.tree(createAccessPolymorphicField(execution, shared)); + polyChainBuilder.string(" == ").typeLiteral(executableType.getType().getPrimitiveType()); + polyChainBuilder.end(); + polyChainBuilder.startBlock(); + polyChainBuilder.startStatement(); + polyChainBuilder.tree(assignment); + polyChainBuilder.tree(callTemplateMethod(polyChainBuilder, CodeTreeBuilder.singleString(nodeFieldName(execution)), executableType, currentValues)).end(); + polyChainBuilder.end(); + hasUnexpectedResult |= executableType.hasUnexpectedValue(context); + } + + CodeTree executeGeneric = callTemplateMethod(polyChainBuilder, CodeTreeBuilder.singleString(nodeFieldName(execution)), genericExecutableType, currentValues); + + if (specializedExecutables.isEmpty()) { + builder.tree(assignment); + builder.tree(executeGeneric); + } else { + CodeTree accessPolymorphicProfile = createAccessPolymorphicField(execution, shared); + polyChainBuilder.startElseIf().tree(accessPolymorphicProfile).string(" == null").end(); + polyChainBuilder.startBlock(); + polyChainBuilder.tree(createTransferToInterpreterAndInvalidate()); + polyChainBuilder.declaration(genericExecutableType.getType().getPrimitiveType(), "value_", executeGeneric); + + hasSpecializedTypes = false; + for (ExecutableTypeData executableType : specializedExecutables) { + hasSpecializedTypes = polyChainBuilder.startIf(hasSpecializedTypes); + polyChainBuilder.tree(TypeSystemCodeGenerator.check(executableType.getType(), CodeTreeBuilder.singleString("value_"))); + polyChainBuilder.end(); + polyChainBuilder.startBlock(); + polyChainBuilder.startStatement().tree(accessPolymorphicProfile).string(" = ").typeLiteral(executableType.getType().getPrimitiveType()).end(); + polyChainBuilder.end(); + } + + polyChainBuilder.startElseBlock(); + polyChainBuilder.startStatement().tree(accessPolymorphicProfile).string(" = ").typeLiteral(genericType.getPrimitiveType()).end(); + polyChainBuilder.end(); + + polyChainBuilder.startReturn().string("value_").end(); + + polyChainBuilder.end(); + polyChainBuilder.startElseBlock(); + polyChainBuilder.startStatement().tree(assignment).tree(executeGeneric).end(); + polyChainBuilder.end(); + + if (hasUnexpectedResult) { + builder.startTryBlock(); + } + + builder.tree(polyChainBuilder.build()); + + if (hasUnexpectedResult) { + builder.end(); + builder.startCatchBlock(getType(UnexpectedResultException.class), "ex"); + builder.startStatement().tree(accessPolymorphicProfile).string(" = ").typeLiteral(genericType.getPrimitiveType()).end(); + builder.startReturn().string("ex.getResult()").end(); + builder.end(); + } + } + return builder.build(); + } + + private static CodeTree createAssignmentStart(LocalVariable target, boolean shared, boolean accessParent) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + if (shared) { + builder.string("return "); + } else { + builder.string(target.getName()).string(" = "); + if (accessParent) { + builder.tree(accessParent(null)).string("."); + } + } + return builder.build(); + } + + private static CodeTree createAccessPolymorphicField(NodeExecutionData execution, boolean shared) { + String name = polymorphicTypeProfileFieldName(execution); + if (shared) { + return CodeTreeBuilder.singleString(name); + } else { + return accessParent(name); + } + } + + private CodeTree createExecuteChildDuplicateTail(CodeTreeBuilder parent, NodeExecutionData execution, CodeTree assignment, LocalVariable target, LocalContext currentValues) { + CodeTreeBuilder builder = parent.create(); + List<TypeData> sourceTypes = target.getType().getImplicitSourceTypes(); + String implicitClassFieldName = implicitClassFieldName(execution); + String nodeFieldName = nodeFieldName(execution); + List<ExecutableTypeData> executableTypes = findSpecializedExecutables(execution, sourceTypes, options.implicitTypeBoxingOptimization()); + + boolean elseIf = false; + for (ExecutableTypeData executableType : executableTypes) { + elseIf = builder.startIf(elseIf); + builder.string(implicitClassFieldName).string(" == ").typeLiteral(executableType.getType().getBoxedType()); + builder.end(); + builder.startBlock(); + builder.startStatement().tree(assignment); + + CodeTree execute = callTemplateMethod(builder, accessParent(nodeFieldName), executableType, currentValues); + ImplicitCastData cast = typeSystem.lookupCast(executableType.getType(), target.getType()); + if (cast != null) { + execute = callTemplateMethod(builder, null, cast, execute); + } + builder.tree(execute); + builder.end(); + builder.end(); + } + + if (!executableTypes.isEmpty()) { + builder.startElseBlock(); + } + + LocalVariable genericValue = target.makeGeneric().nextName(); + LocalVariable genericShortCircuit = resolveShortCircuit(null, execution, currentValues); + + builder.tree(createAssignExecuteChild(execution, genericValue.getType(), genericValue, genericShortCircuit, currentValues)); + if (executableTypes.size() == sourceTypes.size()) { + builder.startThrow().startNew(getType(UnexpectedResultException.class)).tree(genericValue.createReference()).end().end(); + } else { + builder.startStatement().tree(assignment); + builder.tree(TypeSystemCodeGenerator.implicitExpect(target.getType(), genericValue.createReference(), implicitClassFieldName)); + builder.end(); + } + + if (!executableTypes.isEmpty()) { + builder.end(); + } + return builder.build(); + } + + private static CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, TypeData forType, LocalContext currentValues, CodeTree execution) { + if (specialization.getExceptions().isEmpty()) { + return execution; + } + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + builder.startTryBlock(); + builder.tree(execution); + TypeMirror[] exceptionTypes = new TypeMirror[specialization.getExceptions().size()]; + for (int i = 0; i < exceptionTypes.length; i++) { + exceptionTypes[i] = specialization.getExceptions().get(i).getJavaClass(); + } + builder.end().startCatchBlock(exceptionTypes, "ex"); + builder.startStatement().tree(accessParent(excludedFieldName(specialization))).string(" = true").end(); + builder.startReturn(); + builder.tree(createCallDelegate("remove", "threw rewrite exception", forType, currentValues)); + builder.end(); + builder.end(); + return builder.build(); + } + + private static CodeTree createMethodGuardCheck(List<GuardExpression> guardExpressions, LocalContext currentValues) { + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + String and = ""; + for (GuardExpression guard : guardExpressions) { + builder.string(and); + if (guard.isNegated()) { + builder.string("!"); + } + builder.tree(callTemplateMethod(builder, accessParent(null), guard.getResolvedGuard(), currentValues)); + and = " && "; + } + return builder.build(); + } + + private CodeTree[] createTypeCheckAndCast(List<TypeGuard> typeGuards, Set<TypeGuard> castGuards, LocalContext currentValues, SpecializationExecution specializationExecution) { + CodeTreeBuilder checksBuilder = CodeTreeBuilder.createBuilder(); + CodeTreeBuilder castsBuilder = CodeTreeBuilder.createBuilder(); + for (TypeGuard typeGuard : typeGuards) { + int signatureIndex = typeGuard.getSignatureIndex(); + LocalVariable value = currentValues.getValue(signatureIndex); + TypeData targetType = typeGuard.getType(); + if (!value.getType().needsCastTo(targetType)) { + continue; + } + NodeExecutionData execution = node.getChildExecutions().get(signatureIndex); + if (!checksBuilder.isEmpty()) { + checksBuilder.string(" && "); + } + + CodeTreeBuilder checkBuilder = checksBuilder.create(); + CodeTreeBuilder castBuilder = checksBuilder.create(); + + LocalVariable shortCircuit = currentValues.getShortCircuit(execution); + if (shortCircuit != null) { + checkBuilder.string("("); + CodeTreeBuilder referenceBuilder = checkBuilder.create(); + if (!shortCircuit.getType().isPrimitive()) { + 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(targetType, value.createReference())); + castBuilder.tree(TypeSystemCodeGenerator.cast(targetType, valueReference)); + } else { + ImplicitCastOptimization opt = options.implicitCastOptimization(); + if (specializationExecution.isFastPath() && !opt.isNone()) { + if (opt.isDuplicateTail()) { + String typeHintField = implicitClassFieldName(execution); + checkBuilder.tree(TypeSystemCodeGenerator.implicitCheck(targetType, valueReference, typeHintField)); + castBuilder.tree(TypeSystemCodeGenerator.implicitCast(targetType, valueReference, typeHintField)); + } else if (opt.isMergeCasts()) { + checkBuilder.tree(ImplicitCastNodeFactory.check(implicitNodeFieldName(execution), valueReference)); + castBuilder.tree(ImplicitCastNodeFactory.cast(implicitNodeFieldName(execution), valueReference)); + } else { + throw new AssertionError("implicit cast opt"); + } + } else { + checkBuilder.tree(TypeSystemCodeGenerator.implicitCheck(targetType, valueReference, null)); + castBuilder.tree(TypeSystemCodeGenerator.implicitCast(targetType, valueReference, null)); + } + } + + if (shortCircuit != null) { + checkBuilder.string(")"); + castBuilder.string(" : ").defaultValue(targetType.getPrimitiveType()); + } + + if (castGuards == null || castGuards.contains(typeGuard)) { + LocalVariable castVariable = currentValues.getValue(execution).nextName().newType(typeGuard.getType()).accessWith(null); + currentValues.setValue(execution, castVariable); + castsBuilder.tree(castVariable.createDeclaration(castBuilder.build())); + } + + checksBuilder.tree(checkBuilder.build()); + } + return new CodeTree[]{checksBuilder.build(), castsBuilder.build()}; + } + + public static final class LocalContext { + + private final NodeGenFactory factory; + private final Map<String, LocalVariable> values = new HashMap<>(); + + private LocalContext(NodeGenFactory factory) { + this.factory = factory; + } + + public CodeExecutableElement createMethod(Set<Modifier> modifiers, TypeMirror returnType, String name, String... optionalArguments) { + CodeExecutableElement method = new CodeExecutableElement(modifiers, returnType, name); + addParametersTo(method, optionalArguments); + return method; + } + + public static LocalContext load(NodeGenFactory factory, int signatureSize) { + LocalContext context = new LocalContext(factory); + context.loadValues(signatureSize); + return context; + } + + public static LocalContext load(NodeGenFactory factory) { + return load(factory, factory.node.getSignatureSize()); + } + + public LocalContext copy() { + LocalContext copy = new LocalContext(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, TypeData type) { + return new LocalVariable(type, type.getPrimitiveType(), valueName(execution), null); + } + + public LocalVariable createShortCircuitValue(NodeExecutionData execution) { + return new LocalVariable(factory.typeSystem.getBooleanType(), 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 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()) { + // lookup by signature index for executeWith + NodeExecutionData execution = factory.node.getChildExecutions().get(signatureIndex); + var = getValue(execution); + } + return var; + } + + public LocalVariable getValue(NodeExecutionData execution) { + return get(valueName(execution)); + } + + public LocalVariable getValue(int signatureIndex) { + return getValue(factory.node.getChildExecutions().get(signatureIndex)); + } + + 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 size = 0; + for (NodeExecutionData execution : factory.node.getChildExecutions()) { + if (requireLoaded && getValue(execution) == null) { + continue; + } + if (execution.isShortCircuit()) { + size += 2; + } else { + size++; + } + } + return size > 4; + } + + private void loadValues(int evaluatedArguments) { + values.put(FRAME_VALUE, new LocalVariable(null, factory.getType(VirtualFrame.class), FRAME_VALUE, null)); + + for (NodeFieldData field : factory.node.getFields()) { + String fieldName = fieldValueName(field); + values.put(fieldName, new LocalVariable(null, field.getType(), fieldName, NodeGenFactory.accessParent(field.getName()))); + } + + boolean varargs = needsVarargs(false); + for (int i = 0; i < evaluatedArguments; i++) { + NodeExecutionData execution = factory.node.getChildExecutions().get(i); + if (execution.isShortCircuit()) { + LocalVariable shortCircuit = createShortCircuitValue(execution).makeGeneric(); + if (varargs) { + shortCircuit = shortCircuit.accessWith(createReadVarargs(i)); + } + values.put(shortCircuit.getName(), shortCircuit); + } + LocalVariable value = createValue(execution, factory.genericType); + if (varargs) { + value = value.accessWith(createReadVarargs(i)); + } + values.put(value.getName(), value); + } + } + + 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.nullLiteral(); + } else { + 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(); + if (executions.size() == 1 && ElementUtils.typeEquals(var.getTypeMirror(), factory.getType(Object[].class))) { + // if the current type is Object[] do not use varargs for a single argument + builder.string("(Object) "); + } + builder.tree(var.createReference()); + builder.end(); + } + } + } + + public void addParametersTo(CodeExecutableElement method, String... optionalNames) { + for (String var : optionalNames) { + LocalVariable local = values.get(var); + if (local != null) { + method.addParameter(local.createParameter()); + } + } + if (needsVarargs(true)) { + 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)); + } + + } + + public final static class LocalVariable { + + private final TypeData type; + private final TypeMirror typeMirror; + private final CodeTree accessorTree; + private final String name; + + public static LocalVariable fromParameter(Parameter parameter) { + NodeExecutionData execution = parameter.getSpecification().getExecution(); + String name = null; + if (execution == null) { + name = parameter.getLocalName(); + } else { + name = createName(execution); + } + return new LocalVariable(parameter.getTypeSystemType(), parameter.getType(), name, null); + } + + private LocalVariable(TypeData type, TypeMirror typeMirror, String name, CodeTree accessorTree) { + Objects.requireNonNull(typeMirror); + this.typeMirror = typeMirror; + this.accessorTree = accessorTree; + this.type = type; + this.name = name; + } + + public TypeData getType() { + return type; + } + + public String getShortCircuitName() { + return "has" + ElementUtils.firstLetterUpperCase(getName()); + } + + public String getName() { + return name; + } + + private static String createNextName(String name) { + return name + "_"; + } + + private static String createName(NodeExecutionData execution) { + if (execution == null) { + return "<error>"; + } + return execution.getName() + "Value"; + } + + 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(TypeData newType) { + return new LocalVariable(newType, newType.getPrimitiveType(), name, accessorTree); + } + + public final LocalVariable accessWith(CodeTree tree) { + return new LocalVariable(type, typeMirror, name, tree); + } + + public final LocalVariable nextName() { + return new LocalVariable(type, typeMirror, createNextName(name), accessorTree); + } + + public final LocalVariable makeGeneric() { + return newType(type.getTypeSystem().getGenericTypeData()); + } + + } + + private interface SpecializationExecution { + + boolean isFastPath(); + + CodeTree createExecute(SpecializationData specialization, LocalContext currentValues); + + } + +}