# HG changeset patch # User Christian Humer # Date 1429017168 -7200 # Node ID 476374f3fe9a4c3061afac6f27614902e4c10b4e # Parent f83fd99b2962e13e83f8fb58654efbe66224b43e Truffle-DSL: generate better polymorphic execute signatures diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteGroupingTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteGroupingTest.java Tue Apr 14 15:12:48 2015 +0200 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2015, 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.api.dsl.test; + +import org.junit.*; +import org.junit.experimental.theories.*; +import org.junit.runner.*; +import org.junit.runners.Parameterized.Parameters; + +import static org.junit.Assert.*; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.dsl.test.ExecuteGroupingTestFactory.ExecuteGrouping1NodeGen; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/* + * This test aims to test the reuse of execute methods with evaluated parameters as much as possible. + */ +@SuppressWarnings("unused") +@RunWith(Theories.class) +public class ExecuteGroupingTest { + + @DataPoints public static final Object[] parameters = new Object[]{1, 2}; + + static final class ExecuteGroupingChild extends Node { + + int invocationCount = 0; + + private final Object returnValue; + + public ExecuteGroupingChild(Object returnValue) { + this.returnValue = returnValue; + } + + Object execute() { + invocationCount++; + return returnValue; + } + + } + + @Theory + public void testExecuteGrouping1Node(Object a, Object b, Object c) throws UnexpectedResultException { + ExecuteGroupingChild child0 = new ExecuteGroupingChild(a); + ExecuteGroupingChild child1 = new ExecuteGroupingChild(b); + ExecuteGroupingChild child2 = new ExecuteGroupingChild(c); + + int result = ((int) a) + ((int) b) + ((int) c); + + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(child0, child1, child2)).execute()); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(child0, child1, child2)).execute((VirtualFrame) null)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, child1, child2)).execute(a)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, child1, child2)).executeInt(a)); + + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute(a, b)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute((int) a, b)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute(a, (int) b)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).execute((int) a, (int) b)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, child2)).executeInt((int) a, (int) b)); + + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, null)).execute(a, b, c)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, null)).execute((int) a, (int) b, c)); + assertEquals(result, TestHelper.createRoot(ExecuteGrouping1NodeGen.create(null, null, null)).execute((int) a, (int) b, (int) c)); + + } + + @NodeChildren({@NodeChild(type = ExecuteGroupingChild.class), @NodeChild(type = ExecuteGroupingChild.class), @NodeChild(type = ExecuteGroupingChild.class)}) + abstract static class ExecuteGrouping1Node extends Node { + + abstract Object execute(); + + int executeInt() throws UnexpectedResultException { + Object value = execute(); + if (value instanceof Integer) { + return (int) value; + } + throw new UnexpectedResultException(value); + } + + abstract double executeDouble() throws UnexpectedResultException; + + abstract Object execute(VirtualFrame frame); + + abstract Object execute(Object o1); + + abstract int executeInt(Object o1) throws UnexpectedResultException; + + abstract Object execute(Object o1, Object o2); + + abstract Object execute(int o1, int o2); + + abstract Object execute(int o1, int o2, Object o3); + + abstract int executeInt(int o1, int o2) throws UnexpectedResultException; + + abstract Object execute(Object o1, int o2); + + abstract Object execute(int o1, Object o2); + + abstract Object execute(Object o1, Object o2, Object o3); + + abstract Object execute(int o1, int o2, int o3); + + @Specialization + int s1(int a, int b, int c) { + return a + b + c; + } + + @Specialization + int s2(Object a, Object b, Object c) { + return ((int) a) + ((int) b) + ((int) c); + } + + } + + abstract static class StrangeReturnCase extends Node { + + // we don't know how to implement executeDouble + public abstract double executeDouble(); + + public int executeInt() { + return 42; + } + + @Specialization(rewriteOn = RuntimeException.class) + int s1() { + return 42; + } + + @Specialization + int s2() { + return 42; + } + + } + + @ExpectError("Incompatible abstract execute methods found [executeDouble(), executeInt()].%") + abstract static class IncompatibleAbstract1 extends Node { + + // we don't know how to implement executeDouble + abstract double executeDouble(); + + abstract int executeInt(); + + @Specialization + int s1() { + return 42; + } + + } + + abstract static class IncompatibleAbstract2 extends Node { + + abstract double executeDouble(); + + // we can resolve duplicate path errors by making an execute method final + @SuppressWarnings("static-method") + public final int executeInt() { + return 42; + } + + @ExpectError("The provided return type \"int\" does not match expected return type \"double\".%") + @Specialization(rewriteOn = RuntimeException.class) + int s1() { + return 42; + } + + @Specialization + double s2() { + return 42; + } + + } + +} diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteMethodTest.java Tue Apr 14 15:12:48 2015 +0200 @@ -137,7 +137,6 @@ @TypeSystemReference(ExecuteMethodTypes.class) @NodeChild(value = "a", type = ChildNoFrame.class) - @ExpectError("Multiple accessible and overridable generic execute methods found [executeInt(), executeObject()]. Remove all but one or mark all but one as final.") abstract static class ExecuteThis8 extends Node { abstract int executeInt(); @@ -196,7 +195,6 @@ @TypeSystemReference(ExecuteMethodTypes.class) @NodeChild(value = "a", type = ChildNoFrame.class) - @ExpectError("Multiple accessible and overridable generic execute methods found [executeVoid1(), executeVoid2()]. Remove all but one or mark all but one as final.") abstract static class ExecuteThisVoid3 extends Node { // allow only one execute void diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java Tue Apr 14 15:12:48 2015 +0200 @@ -32,6 +32,7 @@ import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback3Factory; import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback4Factory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.*; +import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; public class FallbackTest { @@ -53,6 +54,9 @@ @NodeChild("a") abstract static class Fallback1 extends ValueNode { + @Override + public abstract String executeString(VirtualFrame frame); + @Specialization String f1(int a) { return "(int)"; diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NoTypeSystemTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NoTypeSystemTest.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NoTypeSystemTest.java Tue Apr 14 15:12:48 2015 +0200 @@ -37,6 +37,10 @@ public class NoTypeSystemTest { + abstract static class DummyChild extends Node { + abstract Object execute(); + } + abstract static class NoParameterTestNode extends Node { abstract void execute(); @@ -138,9 +142,9 @@ abstract int[] executeIntArray(Object primitive) throws UnexpectedResultException; - abstract void executeVoid(Object primitive) throws UnexpectedResultException; + abstract void executeVoid(Object primitive); - abstract void executeChar(Object primitive) throws UnexpectedResultException; + abstract void executeChar(Object primitive); abstract int executeInt(int primitive) throws UnexpectedResultException; @@ -150,7 +154,7 @@ abstract int[] executeIntArray(int[] primitive) throws UnexpectedResultException; - abstract void executeChar(char primitive) throws UnexpectedResultException; + abstract void executeChar(char primitive); @Specialization int s1(int primitive) { @@ -178,19 +182,6 @@ } - // make nodes replacable - private static T createRoot(final T node) { - new RootNode() { - @Child T child = node; - - @Override - public Object execute(VirtualFrame frame) { - return null; - } - }.adoptChildren(); - return node; - } - @Test public void testTypesNotInTypeSystem() throws UnexpectedResultException { int[] someArray = {1, 2, 3}; @@ -232,7 +223,7 @@ } private static TypesNotInTypeSystemTest createTypesNotInTypeSystem() { - return createRoot(TypesNotInTypeSystemTestNodeGen.create()); + return TestHelper.createRoot(TypesNotInTypeSystemTestNodeGen.create()); } abstract static class ErrorImpossibleTypes1 extends Node { @@ -281,7 +272,7 @@ @ExpectError("Not enough child node declarations found. Please annotate the node class with addtional @NodeChild annotations or remove all execute methods that do not provide all evaluated values. " + "The following execute methods do not provide all evaluated values for the expected signature size 2: [execute(int)].") - @NodeChild + @NodeChild(type = DummyChild.class) abstract static class ErrorMissingNodeChild2 extends Node { abstract int execute(int arg0); @@ -308,7 +299,7 @@ } @ExpectError("Unnecessary @NodeChild declaration. All evaluated child values are provided as parameters in execute methods.") - @NodeChild + @NodeChild(type = DummyChild.class) abstract static class ErrorAdditionalNodeChild1 extends Node { abstract int execute(int arg0); @@ -319,7 +310,7 @@ } } - @NodeChild + @NodeChild(type = DummyChild.class) @ExpectError("Not enough child node declarations found. Please annotate the node class with addtional @NodeChild annotations or remove all execute methods that do not provide all evaluated values. " + "The following execute methods do not provide all evaluated values for the expected signature size 2: [execute(int)].") abstract static class ErrorAdditionalNodeChild2 extends Node { diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java Tue Apr 14 15:12:48 2015 +0200 @@ -123,7 +123,7 @@ } @Specialization(contains = "alwaysRewrite") - String returnField() { + Object returnField() { return getField(); } } diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java Tue Apr 14 15:12:48 2015 +0200 @@ -32,12 +32,27 @@ import com.oracle.truffle.api.dsl.test.TypeSystemTest.ChildrenNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; /** * Utility class to provide some test helper functions. */ class TestHelper { + // make nodes replacable + public static T createRoot(final T node) { + new RootNode() { + @Child T child = node; + + @Override + public Object execute(VirtualFrame frame) { + return null; + } + }.adoptChildren(); + return node; + } + private static ArgumentNode[] arguments(int count) { ArgumentNode[] nodes = new ArgumentNode[count]; for (int i = 0; i < nodes.length; i++) { diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java Tue Apr 14 15:12:48 2015 +0200 @@ -96,12 +96,12 @@ public static class RubyHeadNode extends ExampleNode { - @Child private RubyLookupNode lookup = RubyLookupNodeGen.create(null); - @Child private RubyDispatchNode dispatch = RubyDispatchNodeGen.create(null); + @Child private RubyLookupNode lookup = RubyLookupNodeGen.create(); + @Child private RubyDispatchNode dispatch = RubyDispatchNodeGen.create(); @Specialization - public Object doCall(VirtualFrame frame, Object receiverObject, Object methodName, Object blockObject, Object... argumentsObjects) { - InternalMethod method = lookup.executeLookup(frame, receiverObject, methodName); + public Object doCall(VirtualFrame frame, RubyObject receiverObject, Object methodName, Object blockObject, Object... argumentsObjects) { + InternalMethod method = lookup.executeLookup(receiverObject, methodName); Object[] packedArguments = new Object[argumentsObjects.length + 3]; packedArguments[0] = method; @@ -113,9 +113,9 @@ } } - public abstract static class RubyLookupNode extends ExampleNode { + public abstract static class RubyLookupNode extends Node { - public abstract InternalMethod executeLookup(VirtualFrame frame, Object receiverObject, Object methodName); + public abstract InternalMethod executeLookup(RubyObject receiver, Object method); @Specialization(guards = "receiver.getRubyClass() == cachedClass", assumptions = "cachedClass.getDependentAssumptions()") protected static InternalMethod cachedLookup(RubyObject receiver, Object name, // @@ -132,7 +132,7 @@ } @ImportStatic(InternalMethod.class) - public abstract static class RubyDispatchNode extends ExampleNode { + public abstract static class RubyDispatchNode extends Node { public abstract Object executeDispatch(VirtualFrame frame, InternalMethod function, Object[] packedArguments); diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java Tue Apr 14 15:12:48 2015 +0200 @@ -62,18 +62,17 @@ private final NodeData node; private final TypeSystemData typeSystem; private final TypeMirror genericType; - private final TypeMirror voidType; private final DSLOptions options; private final boolean singleSpecializable; private final int varArgsThreshold; private final Set expectedTypes = new HashSet<>(); + private boolean nextUsed; public NodeGenFactory(ProcessorContext context, NodeData node) { this.context = context; this.node = node; this.typeSystem = node.getTypeSystem(); this.genericType = context.getType(Object.class); - this.voidType = context.getType(void.class); this.options = typeSystem.getOptions(); this.singleSpecializable = isSingleSpecializableImpl(); this.varArgsThreshold = calculateVarArgsThreshold(); @@ -206,16 +205,14 @@ } } - Collection specializedTypes = node.findSpecializedReturnTypes(); - List implementedExecutables = new ArrayList<>(); - for (ExecutableTypeData execType : node.getExecutableTypes()) { - if (shouldImplementExecutableType(specializedTypes, execType)) { - implementedExecutables.add(execType); + List usedTypes = filterBaseExecutableTypes(node.getExecutableTypes(), getReachableSpecializations()); + for (ExecutableTypeData execType : usedTypes) { + if (execType.getMethod() == null) { + continue; } + clazz.add(createExecutableTypeOverride(usedTypes, execType)); } - for (ExecutableTypeData execType : implementedExecutables) { - clazz.add(createExecutableTypeOverride(implementedExecutables, execType)); - } + clazz.add(createGetCostMethod()); avoidFindbugsProblems(clazz); @@ -377,7 +374,9 @@ generated.put(specialization, clazz.add(createSpecialization(specialization, baseSpecializationType))); } - baseSpecialization.addOptional(createCreateNext(generated)); + if (nextUsed) { + baseSpecialization.addOptional(createCreateNext(generated)); + } baseSpecialization.addOptional(createCreateFallback(generated)); baseSpecialization.addOptional(createCreatePolymorphic(generated)); @@ -414,13 +413,12 @@ clazz.addOptional(createUnsupported()); clazz.add(createGetSuppliedChildrenMethod()); - - int signatureSize = node.getSignatureSize(); - Set evaluatedCount = getEvaluatedCounts(); - for (int evaluated : evaluatedCount) { - if (signatureSize != evaluated || signatureSize == 0) { - clazz.add(createFastPathExecuteMethod(null, evaluated > 0 ? null : genericType, evaluated)); - } + clazz.add(createGetNext(clazz)); + clazz.add(createAcceptAndExecute()); + + List usedTypes = filterBaseExecutableTypes(node.getExecutableTypes(), getReachableSpecializations()); + for (ExecutableTypeData type : usedTypes) { + clazz.add(createFastPathExecuteMethod(null, type, usedTypes)); } for (NodeExecutionData execution : node.getChildExecutions()) { @@ -436,6 +434,126 @@ return clazz; } + private Element createAcceptAndExecute() { + + TypeMirror[] parameters = new TypeMirror[node.getSignatureSize()]; + Arrays.fill(parameters, genericType); + + ExecutableTypeData executableElement = new ExecutableTypeData(genericType, "acceptAndExecute", context.getType(Frame.class), Arrays.asList(parameters)); + + LocalContext currentLocals = LocalContext.load(this, node.getSignatureSize(), varArgsThreshold); + CodeExecutableElement executable = createExecuteMethod(null, executableElement, currentLocals, false); + + executable.getModifiers().add(FINAL); + CodeTreeBuilder builder = executable.createBuilder(); + + CodeTree receiver = CodeTreeBuilder.singleString("this"); + + builder.tree(createCallDelegateExecute(builder, receiver, currentLocals, executableElement, node.getGenericExecutableType(null))); + + return executable; + } + + private boolean shouldImplementExecutableType(SpecializationData specialization, ExecutableTypeData executableType) { + // always implement the root execute method. they are declared abstract in the base node. + if (executableType.getDelegatedTo() == null) { + return true; + } + + if (!isSubtypeBoxed(context, specialization.getReturnType().getType(), executableType.getReturnType())) { + return false; + } + + // specializations with more parameters are just ignored + if (executableType.getEvaluatedCount() > node.getSignatureSize()) { + return false; + } + + // the evaluated signature might be compatible to the specialization + boolean specializationCompatible = true; + for (int i = 0; i < executableType.getEvaluatedCount(); i++) { + TypeMirror evaluatedType = executableType.getEvaluatedParameters().get(i); + TypeMirror specializedType = specialization.findParameterOrDie(node.getChildExecutions().get(i)).getType(); + + if (!isSubtypeBoxed(context, evaluatedType, specializedType) && !isSubtypeBoxed(context, specializedType, evaluatedType)) { + specializationCompatible = false; + break; + } + } + if (!specializationCompatible) { + return false; + } + + // possibly trigger void optimization for a specialization if it is enabled + if (isVoid(executableType.getReturnType())) { + if (isTypeBoxingOptimized(options.voidBoxingOptimization(), specialization.getReturnType().getType())) { + return true; + } + } + + // trigger type boxing elimination for unevaluated arguments + for (int i = executableType.getEvaluatedCount(); i < node.getSignatureSize(); i++) { + NodeExecutionData execution = node.getChildExecutions().get(i); + TypeMirror specializedType = specialization.findParameterOrDie(execution).getType(); + if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), specializedType)) { + // it does not make sense to do type boxing elimination for children with + // no type specialized execute method + if (execution.getChild() != null) { + ExecutableTypeData executedType = execution.getChild().findExecutableType(specializedType); + if (executedType != null) { + return true; + } + } + } + } + + // trigger type boxing elimination for return types + if (typeEquals(executableType.getReturnType(), specialization.getReturnType().getType())) { + if (isTypeBoxingOptimized(options.monomorphicTypeBoxingOptimization(), executableType.getReturnType())) { + return true; + } + } + + // trigger generation for evaluated assignable type matches other than generic + for (int i = 0; i < executableType.getEvaluatedCount(); i++) { + TypeMirror evaluatedType = executableType.getEvaluatedParameters().get(i); + NodeExecutionData execution = node.getChildExecutions().get(i); + TypeMirror specializedType = specialization.findParameterOrDie(execution).getType(); + + if (isSubtypeBoxed(context, evaluatedType, specializedType) && !isObject(specializedType)) { + return true; + } + } + + return false; + } + + private List filterBaseExecutableTypes(List executableTypes, List specializations) { + Set usedTypes = new HashSet<>(); + type: for (ExecutableTypeData type : executableTypes) { + for (SpecializationData specialization : specializations) { + if (shouldImplementExecutableType(specialization, type) || type.isAbstract() || !(type.hasUnexpectedValue(context) && type.getMethod() != null)) { + usedTypes.add(type); + continue type; + } + } + } + Set delegatesToAdd = new HashSet<>(); + do { + delegatesToAdd.clear(); + for (ExecutableTypeData type : usedTypes) { + ExecutableTypeData delegate = type.getDelegatedTo(); + if (delegate != null && !usedTypes.contains(delegate)) { + delegatesToAdd.add(delegate); + } + } + usedTypes.addAll(delegatesToAdd); + } while (!delegatesToAdd.isEmpty()); + List newUsedTypes = new ArrayList<>(usedTypes); + Collections.sort(newUsedTypes); + return newUsedTypes; + } + private CodeTypeElement createSpecialization(SpecializationData specialization, TypeMirror baseType) { CodeTypeElement clazz = createClass(node, specialization, modifiers(PRIVATE, STATIC, FINAL), specializationTypeName(specialization), baseType); @@ -463,27 +581,35 @@ clazz.addOptional(createIsSameMethod(specialization)); clazz.addOptional(createIsIdenticalMethod(specialization)); - TypeMirror returnType = specialization.getReturnType().getType(); - int signatureSize = specialization.getSignatureSize(); - - clazz.add(createFastPathExecuteMethod(specialization, null, signatureSize)); - - if (isTypeBoxingEliminated(specialization)) { - if (node.getMinimalEvaluatedParameters() == 0 || signatureSize == 0) { - clazz.add(createFastPathExecuteMethod(specialization, returnType, 0)); - if (signatureSize > 0 && !isObject(returnType)) { - clazz.add(createFastPathWrapExecuteMethod(genericType, returnType)); - } - ExecutableTypeData voidExecutableType = node.findExecutableType(voidType, 0); - if (voidExecutableType != null && isTypeBoxingOptimized(options.voidBoxingOptimization(), returnType)) { - clazz.add(createFastPathWrapVoidMethod(returnType)); - } + // get types that should get implemented + List types = new ArrayList<>(); + for (ExecutableTypeData type : node.getExecutableTypes()) { + if (shouldImplementExecutableType(specialization, type)) { + types.add(type); } } + for (ExecutableTypeData type : types) { + clazz.add(createFastPathExecuteMethod(specialization, type, types)); + } return clazz; } + public static List getDynamicParameters(TemplateMethod method) { + List parameters = new ArrayList<>(); + for (Parameter param : method.getReturnTypeAndParameters()) { + if (param.getSpecification().isLocal()) { + // ignore parameters passed by locals + continue; + } else if (param.getVariableElement() != null && param.getVariableElement().getAnnotation(Cached.class) != null) { + // ignore cached parameters + continue; + } + parameters.add(param); + } + return parameters; + } + private Element createDeepCopyMethod() { if (singleSpecializable) { return null; @@ -506,6 +632,7 @@ builder.startReturn().startCall(specializationStartFieldName(), "getNodeCost").end().end(); } return executable; + } private Element createIsIdenticalMethod(SpecializationData specialization) { @@ -619,43 +746,6 @@ return executable; } - private Element createFastPathWrapVoidMethod(TypeMirror wrap) { - - CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), voidType, TypeSystemNodeFactory.executeName(voidType)); - executable.addParameter(new CodeVariableElement(getType(Frame.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(TypeMirror override, TypeMirror wrap) { - CodeExecutableElement executable = new CodeExecutableElement(modifiers(PUBLIC), override, TypeSystemNodeFactory.executeName(override)); - executable.addParameter(new CodeVariableElement(getType(Frame.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 Element createCreateFallback(Map generatedSpecializationClasses) { SpecializationData fallback = node.getGenericSpecialization(); if (fallback == null) { @@ -767,35 +857,6 @@ return null; } - private boolean isTypeBoxingEliminated(SpecializationData specialization) { - if (specialization.getMethod() == null) { - return false; - } - - TypeBoxingOptimization optimization = options.monomorphicTypeBoxingOptimization(); - if (isTypeBoxingOptimized(optimization, specialization.getReturnType().getType())) { - return true; - } - for (Parameter p : specialization.getSignatureParameters()) { - if (isTypeBoxingOptimized(optimization, p.getType())) { - return true; - } - } - return false; - - } - - private Set getEvaluatedCounts() { - Set evaluatedCount = new TreeSet<>(); - Collection returnSpecializedTypes = node.findSpecializedReturnTypes(); - for (ExecutableTypeData execType : node.getExecutableTypes()) { - if (shouldImplementExecutableType(returnSpecializedTypes, execType)) { - evaluatedCount.add(execType.getEvaluatedCount()); - } - } - return evaluatedCount; - } - private Element createUnsupported() { SpecializationData fallback = node.getGenericSpecialization(); if (fallback == null || optimizeFallback(fallback) || fallback.getMethod() == null) { @@ -866,166 +927,23 @@ } } - private CodeExecutableElement createExecutableTypeOverride(List implementedExecutables, ExecutableTypeData execType) { - final String varArgsName = "args"; - final TypeMirror returnType = execType.getReturnType(); - final TypeMirror executedType = execType.getEvaluatedCount() > 0 ? null : returnType; - + private CodeExecutableElement createExecutableTypeOverride(List usedExecutables, ExecutableTypeData execType) { LocalContext locals = LocalContext.load(this, execType.getEvaluatedCount(), Integer.MAX_VALUE); - CodeExecutableElement method = cloneExecutableTypeOverride(locals, execType, varArgsName); - - // rename varargs parameter - int signatureIndex = 0; - for (TypeMirror parameter : execType.getEvaluatedParameters()) { - LocalVariable var = locals.getValue(signatureIndex); - if (var != null) { - int varArgsIndex = execType.getVarArgsIndex(execType.getParameterIndex(signatureIndex)); - if (varArgsIndex >= 0) { - var = var.accessWith(CodeTreeBuilder.singleString(varArgsName + "[" + varArgsIndex + "]")); - } - if (!isObject(parameter)) { - var = var.newType(parameter); - } - locals.setValue(node.getChildExecutions().get(signatureIndex), var); - } - - signatureIndex++; - } - - TypeMirror frame = execType.getFrameParameter(); + CodeExecutableElement method = createExecuteMethod(null, execType, locals, true); + CodeTreeBuilder builder = method.createBuilder(); if (singleSpecializable) { - LocalVariable frameVar = null; - if (frame != null) { - frameVar = locals.get(FRAME_VALUE).newType(frame); - } - method.getThrownTypes().clear(); - locals.set(FRAME_VALUE, frameVar); - SpecializationData specialization = getReachableSpecializations().iterator().next(); - ExecutableTypeData wrappedExecutableType = findWrappedExecutable(specialization, implementedExecutables, execType); - if (wrappedExecutableType != null) { - builder.startReturn().tree(callExecuteMethod(null, wrappedExecutableType, locals)).end(); - } else { - builder.tree(createFastPath(builder, specialization, execType.getReturnType(), locals)); - } + builder.tree(createFastPath(builder, specialization, execType, usedExecutables, locals)); } else { // create acceptAndExecute - CodeTreeBuilder executeBuilder = builder.create(); - executeBuilder.startCall(specializationStartFieldName(), TypeSystemNodeFactory.executeName(executedType)); - if (frame == null) { - executeBuilder.nullLiteral(); - } else { - executeBuilder.string(locals.get(FRAME_VALUE).getName()); - } - locals.addReferencesTo(executeBuilder); - executeBuilder.end(); - - boolean hasExecutedUnexpected = executedType != null && !isObject(executedType) && !isVoid(executedType); - - CodeTreeBuilder contentBuilder = builder.create(); - contentBuilder.startReturn(); - if (!hasExecutedUnexpected && !execType.hasUnexpectedValue(context)) { - if (executedType == null || needsCastTo(executedType, returnType)) { - contentBuilder.cast(returnType, executeBuilder.build()); - } else { - contentBuilder.tree(executeBuilder.build()); - } - } else { - contentBuilder.tree(expect(executedType, returnType, executeBuilder.build())); - } - contentBuilder.end(); - // try catch assert if unexpected value is not expected - CodeTree content = contentBuilder.build(); - if (!execType.hasUnexpectedValue(context) && hasExecutedUnexpected) { - content = wrapTryCatchUnexpected(content); - } - builder.tree(content); + ExecutableTypeData delegate = execType; + CodeTree receiver = CodeTreeBuilder.singleString(specializationStartFieldName()); + builder.tree(createCallDelegateExecute(builder, receiver, locals, execType, delegate)); } return method; } - private CodeTree wrapTryCatchUnexpected(CodeTree content) { - CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); - builder.startTryBlock(); - builder.tree(content); - builder.end().startCatchBlock(getType(UnexpectedResultException.class), "ex"); - builder.startThrow().startNew(getType(AssertionError.class)).end().end(); - builder.end(); - return builder.build(); - } - - private static ExecutableTypeData findWrappedExecutable(SpecializationData specialization, List implementedExecutables, ExecutableTypeData executedType) { - if (specialization.getReturnType().getType() == executedType.getReturnType()) { - return null; - } - for (ExecutableTypeData otherType : implementedExecutables) { - if (otherType != executedType && // - otherType.getReturnType() == specialization.getReturnType().getType() && // - otherType.getEvaluatedCount() == executedType.getEvaluatedCount()) { - return otherType; - } - } - return null; - } - - private CodeExecutableElement cloneExecutableTypeOverride(LocalContext locals, ExecutableTypeData execType, final String varArgsName) throws AssertionError { - CodeExecutableElement method = CodeExecutableElement.clone(context.getEnvironment(), execType.getMethod()); - - method.getAnnotationMirrors().clear(); - method.getModifiers().remove(Modifier.ABSTRACT); - - // align argument names - int parameterIndex = 0; - if (execType.getFrameParameter() != null) { - CodeVariableElement frameParameter = (CodeVariableElement) method.getParameters().get(0); - frameParameter.setName(FRAME_VALUE); - frameParameter.getAnnotationMirrors().clear(); - parameterIndex++; - } - - for (int signatureIndex = 0; signatureIndex < execType.getEvaluatedCount(); signatureIndex++) { - CodeVariableElement var = (CodeVariableElement) method.getParameters().get(parameterIndex); - if (signatureIndex < node.getSignatureSize()) { - if (execType.getVarArgsIndex(parameterIndex) >= 0) { - var.getAnnotationMirrors().clear(); - var.setName(varArgsName); - break; - } - var.setName(locals.getValue(signatureIndex).getName()); - var.getAnnotationMirrors().clear(); - } else { - var.setName("other" + signatureIndex); - } - parameterIndex++; - } - return method; - } - - private boolean shouldImplementExecutableType(Collection specializedTypes, ExecutableTypeData execType) { - TypeMirror type = execType.getReturnType(); - Set modifiers = execType.getMethod().getModifiers(); - if (modifiers.contains(FINAL) || modifiers.contains(STATIC) || modifiers.contains(PRIVATE)) { - return false; - } else if (execType.isAbstract()) { - return true; - } else if (ElementUtils.isObject(type)) { - return true; - } else if (ElementUtils.isVoid(type)) { - for (TypeMirror 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"); @@ -1091,8 +1009,7 @@ return builder.build(); } - private CodeTree callExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) { - CodeTree receiver = execution != null ? accessParent(nodeFieldName(execution)) : null; + private CodeTree[] bindExecuteMethodParameters(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) { List executeWith = execution != null ? execution.getChild().getExecuteWith() : null; List values = new ArrayList<>(); @@ -1114,7 +1031,13 @@ } values.add(createTypeSafeReference(variable, targetParameter)); } - return callMethod(receiver, method.getMethod(), values.toArray(new CodeTree[values.size()])); + + return values.toArray(new CodeTree[values.size()]); + } + + private CodeTree callExecuteMethod(NodeExecutionData execution, ExecutableTypeData method, LocalContext currentValues) { + CodeTree receiver = execution != null ? accessParent(nodeFieldName(execution)) : null; + return callMethod(receiver, method.getMethod(), bindExecuteMethodParameters(execution, method, currentValues)); } private CodeTree callTemplateMethod(CodeTree receiver, TemplateMethod method, LocalContext currentValues) { @@ -1148,8 +1071,6 @@ } if (needsCastTo(sourceType, targetType)) { valueReference = TypeSystemCodeGenerator.cast(typeSystem, targetType, valueReference); - } else if (ElementUtils.needsCastTo(sourceType, targetType)) { - valueReference = CodeTreeBuilder.createBuilder().cast(targetType, valueReference).build(); } return valueReference; } @@ -1304,6 +1225,13 @@ return false; } + private static Element createGetNext(CodeTypeElement type) { + CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), type.asType(), "getNext"); + CodeTreeBuilder builder = method.createBuilder(); + builder.startReturn().cast(type.asType(), CodeTreeBuilder.singleString("this.next")).end(); + return method; + } + private Element createGetSuppliedChildrenMethod() { ArrayType nodeArray = context.getEnvironment().getTypeUtils().getArrayType(getType(Node.class)); @@ -1507,18 +1435,17 @@ return builder.build(); } - private CodeTree createCallNext(TypeMirror forType, LocalContext currentValues) { + private CodeTree createCallNext(CodeTreeBuilder parent, ExecutableTypeData currentType, ExecutableTypeData callType, LocalContext currentValues) { if (singleSpecializable) { return createThrowUnsupported(currentValues); } - CodeTreeBuilder callBuilder = CodeTreeBuilder.createBuilder(); - callBuilder.startCall("next", TypeSystemNodeFactory.executeName(null)); - currentValues.addReferencesTo(callBuilder, FRAME_VALUE); - callBuilder.end(); - return CodeTreeBuilder.createBuilder().startReturn().tree(expect(genericType, forType, callBuilder.build())).end().build(); + CodeTreeBuilder callBuilder = parent.create(); + callBuilder.tree(createCallDelegateExecute(callBuilder, CodeTreeBuilder.singleString("getNext()"), currentValues, currentType, callType)); + nextUsed = true; + return callBuilder.build(); } - private CodeTree createCallRemove(String reason, TypeMirror forType, LocalContext currentValues) { + private CodeTree createCallRemove(String reason, ExecutableTypeData forType, LocalContext currentValues) { if (singleSpecializable) { return createThrowUnsupported(currentValues); } @@ -1531,12 +1458,12 @@ builder = builder.create(); builder.startReturn(); - builder.tree(expect(genericType, forType, call)); + builder.tree(expectOrCast(genericType, forType, call)); builder.end(); return builder.build(); } - private CodeTree createCallDelegate(String methodName, String reason, TypeMirror forType, LocalContext currentValues) { + private CodeTree createCallDelegate(String methodName, String reason, ExecutableTypeData forType, LocalContext currentValues) { CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); builder.startCall(methodName); if (reason != null) { @@ -1545,7 +1472,24 @@ currentValues.addReferencesTo(builder, FRAME_VALUE); builder.end(); - return expect(genericType, forType, builder.build()); + CodeTree expectOrCast = expectOrCast(genericType, forType, builder.build()); + return expectOrCast; + } + + private CodeTree expectOrCast(TypeMirror sourceType, ExecutableTypeData targetType, CodeTree content) { + if (targetType.hasUnexpectedValue(context)) { + 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) { @@ -1584,70 +1528,236 @@ return false; } - private Element createFastPathExecuteMethod(SpecializationData specialization, final TypeMirror forType, int evaluatedArguments) { - TypeMirror type = forType == null ? genericType : forType; - LocalContext currentLocals = LocalContext.load(this, evaluatedArguments, varArgsThreshold); + private Element createFastPathExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, List allTypes) { + LocalContext currentLocals = LocalContext.load(this, executedType.getEvaluatedCount(), varArgsThreshold); + CodeExecutableElement executable = createExecuteMethod(specialization, executedType, currentLocals, false); + CodeTreeBuilder builder = executable.createBuilder(); + if (specialization == null) { + if (executedType.getDelegatedTo() == null) { + executable.getModifiers().add(ABSTRACT); + } + } else { + executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); + } + builder.tree(createFastPath(builder, specialization, executedType, allTypes, currentLocals)); + + return executable; + } + + private CodeExecutableElement createExecuteMethod(SpecializationData specialization, ExecutableTypeData executedType, LocalContext currentLocals, boolean originalOverride) { + TypeMirror returnType = executedType.getReturnType(); + TypeMirror frame = executedType.getFrameParameter(); + List evaluatedParameters = executedType.getEvaluatedParameters(); if (specialization != null) { currentLocals.loadFastPathState(specialization); } - CodeExecutableElement executable = currentLocals.createMethod(modifiers(PUBLIC), type, TypeSystemNodeFactory.executeName(forType), FRAME_VALUE); - executable.getAnnotationMirrors().add(new CodeAnnotationMirror(context.getDeclaredType(Override.class))); - - if (!isObject(type)) { - executable.getThrownTypes().add(getType(UnexpectedResultException.class)); + if (frame == null) { + currentLocals.removeValue(FRAME_VALUE); + } else { + currentLocals.set(FRAME_VALUE, currentLocals.get(FRAME_VALUE).newType(frame)); + } + + for (int i = 0; i < Math.min(node.getChildExecutions().size(), evaluatedParameters.size()); i++) { + NodeExecutionData execution = node.getChildExecutions().get(i); + currentLocals.setValue(execution, currentLocals.getValue(execution).newType(evaluatedParameters.get(i))); + } + + String methodName; + if (originalOverride) { + methodName = executedType.getMethod().getSimpleName().toString(); + } else { + methodName = executedType.getUniqueName(); } - CodeTreeBuilder builder = executable.createBuilder(); - builder.tree(createFastPath(builder, specialization, type, currentLocals)); + 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); + } + + final String varArgsName = "args"; + if (executable.isVarArgs()) { + ((CodeVariableElement) executable.getParameters().get(executable.getParameters().size() - 1)).setName(varArgsName); + } + + // rename varargs parameter + int signatureIndex = 0; + for (TypeMirror parameter : executedType.getEvaluatedParameters()) { + LocalVariable var = currentLocals.getValue(signatureIndex); + if (var != null) { + int varArgsIndex = executedType.getVarArgsIndex(executedType.getParameterIndex(signatureIndex)); + if (varArgsIndex >= 0) { + var = var.accessWith(CodeTreeBuilder.singleString(varArgsName + "[" + varArgsIndex + "]")); + } else { + ((CodeVariableElement) executable.getParameters().get(executedType.getParameterIndex(signatureIndex))).setName(var.getName()); + } + if (!isObject(parameter)) { + var = var.newType(parameter); + } + currentLocals.setValue(node.getChildExecutions().get(signatureIndex), var); + } + + signatureIndex++; + } + } else { + executable = currentLocals.createMethod(modifiers(PUBLIC), returnType, methodName, FRAME_VALUE); + if (executedType.hasUnexpectedValue(context)) { + executable.getThrownTypes().add(context.getDeclaredType(UnexpectedResultException.class)); + } + } return executable; } - private CodeTree createFastPath(CodeTreeBuilder parent, SpecializationData specialization, TypeMirror type, LocalContext currentLocals) { + private CodeTree createFastPath(CodeTreeBuilder parent, SpecializationData specialization, final ExecutableTypeData executableType, List allTypes, LocalContext currentLocals) { final CodeTreeBuilder builder = parent.create(); + TypeMirror returnType = executableType.getReturnType(); + + ExecutableTypeData delegate = null; + if (specialization == null) { + delegate = executableType.getDelegatedTo(); + } + + if (delegate == null) { + delegate = findFastPathDelegate((specialization != null ? specialization.getReturnType().getType() : genericType), executableType, allTypes); + } for (NodeExecutionData execution : node.getChildExecutions()) { + if (specialization == null && delegate != null && execution.getIndex() >= delegate.getEvaluatedCount()) { + // we just evaluate children for the next delegate + continue; + } else if (specialization != null && delegate != null) { + // skip if already delegated + break; + } + LocalVariable var = currentLocals.getValue(execution); if (var == null) { TypeMirror targetType; if (specialization == null) { - targetType = genericType; + List genericTypes = node.getGenericTypes(execution); + if (genericTypes.isEmpty()) { + targetType = genericType; + } else { + targetType = genericTypes.get(0); + } } else { targetType = specialization.findParameterOrDie(execution).getType(); } 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); + var = currentLocals.createValue(execution, targetType).nextName(); + builder.tree(createAssignExecuteChild(builder, execution, executableType, var, shortCircuit, currentLocals)); + currentLocals.setValue(execution, var); + } } LocalContext originalValues = currentLocals.copy(); - if (specialization == null) { - builder.startReturn().tree(createCallDelegate("acceptAndExecute", null, type, currentLocals)).end(); + if (delegate != null) { + builder.tree(createCallDelegateExecute(builder, null, currentLocals, executableType, delegate)); + } else if (specialization == null) { + // nothing to do. abstract anyway } else if (specialization.isPolymorphic()) { - builder.tree(createCallNext(type, currentLocals)); + builder.tree(createCallNext(builder, executableType, node.getGenericExecutableType(executableType), currentLocals)); } else if (specialization.isUninitialized()) { - builder.startReturn().tree(createCallDelegate("uninitialized", null, type, currentLocals)).end(); + builder.startReturn().tree(createCallDelegate("uninitialized", null, executableType, currentLocals)).end(); } else { - final TypeMirror finalType = type; SpecializationGroup group = SpecializationGroup.create(specialization); SpecializationBody executionFactory = new SpecializationBody(true, true) { @Override public CodeTree createBody(SpecializationData s, LocalContext values) { - return createFastPathExecute(builder, finalType, s, values); + return createFastPathExecute(builder, executableType, s, values); } }; - builder.tree(createGuardAndCast(group, type, currentLocals, executionFactory)); - if (hasFallthrough(group, type, originalValues, true, null) || group.getSpecialization().isFallback()) { - builder.tree(createCallNext(type, originalValues)); + builder.tree(createGuardAndCast(group, returnType, currentLocals, executionFactory)); + if (hasFallthrough(group, returnType, originalValues, true, null) || group.getSpecialization().isFallback()) { + builder.tree(createCallNext(builder, executableType, executableType, originalValues)); } } return builder.build(); } + private CodeTree createCallDelegateExecute(final CodeTreeBuilder parent, CodeTree receiver, LocalContext currentLocals, ExecutableTypeData source, ExecutableTypeData delegate) { + CodeTreeBuilder callBuilder = parent.create(); + + if (singleSpecializable) { + callBuilder.startCall(receiver, delegate.getMethod().getSimpleName().toString()); + } else { + callBuilder.startCall(receiver, delegate.getUniqueName()); + } + callBuilder.trees(bindExecuteMethodParameters(null, delegate, currentLocals)); + callBuilder.end(); + CodeTree call = expectOrCast(delegate.getReturnType(), source, callBuilder.build()); + + CodeTreeBuilder returnBuilder = parent.create(); + if (isVoid(source.getReturnType())) { + returnBuilder.statement(call); + returnBuilder.returnStatement(); + } else if (isVoid(delegate.getReturnType())) { + returnBuilder.statement(call); + returnBuilder.returnDefault(); + } else { + returnBuilder.startReturn().tree(call).end(); + } + + CodeTreeBuilder builder = parent.create(); + + if (!source.hasUnexpectedValue(context) && delegate.hasUnexpectedValue(context)) { + builder.startTryBlock(); + builder.tree(returnBuilder.build()); + builder.end().startCatchBlock(context.getType(UnexpectedResultException.class), "ex"); + if (!isVoid(source.getReturnType())) { + builder.startReturn().tree(cast(context.getType(Object.class), source.getReturnType(), CodeTreeBuilder.singleString("ex.getResult()"))).end(); + } + builder.end(); + } else { + builder.tree(returnBuilder.build()); + } + return builder.build(); + } + + private ExecutableTypeData findFastPathDelegate(TypeMirror targetType, ExecutableTypeData executableType, List allTypes) { + if (typeEquals(executableType.getReturnType(), targetType)) { + // type matches look for even better delegates + for (ExecutableTypeData type : allTypes) { + if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) { + if (type != executableType) { + return type; + } + } + } + return null; + } else { + for (ExecutableTypeData type : allTypes) { + if (typeEquals(type.getReturnType(), targetType) && executableType.sameParameters(type)) { + return type; + } + } + int executableIndex = allTypes.indexOf(executableType); + int compareIndex = 0; + for (ExecutableTypeData type : allTypes) { + if (executableIndex != compareIndex && executableType.sameParameters(type)) { + int result = ExecutableTypeData.compareType(context, type.getReturnType(), executableType.getReturnType()); + if (result < 0) { + return type; + } else if (result == 0 && executableIndex < compareIndex) { + return type; + } + } + compareIndex++; + } + return null; + } + } + private LocalVariable resolveShortCircuit(SpecializationData specialization, NodeExecutionData execution, LocalContext currentLocals) { LocalVariable shortCircuit = null; SpecializationData resolvedSpecialization = specialization; @@ -1676,7 +1786,7 @@ return shortCircuitIndex; } - private CodeTree createFastPathExecute(CodeTreeBuilder parent, final TypeMirror forType, SpecializationData specialization, LocalContext currentValues) { + private CodeTree createFastPathExecute(CodeTreeBuilder parent, final ExecutableTypeData forType, SpecializationData specialization, LocalContext currentValues) { CodeTreeBuilder builder = parent.create(); int ifCount = 0; if (specialization.isFallback()) { @@ -1729,11 +1839,11 @@ execute.tree(callTemplateMethod(accessParent(null), specialization, currentValues)); execute.end(); if (!doReturn) { - if (isVoid(forType)) { + if (isVoid(forType.getReturnType())) { execute.returnStatement(); } else { execute.startReturn(); - execute.defaultValue(forType); + execute.defaultValue(forType.getReturnType()); execute.end(); } } @@ -1941,8 +2051,9 @@ } } - private CodeTree createAssignExecuteChild(NodeExecutionData execution, TypeMirror returnType, LocalVariable targetValue, LocalVariable shortCircuit, LocalContext currentValues) { - CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + private CodeTree createAssignExecuteChild(CodeTreeBuilder parent, NodeExecutionData execution, ExecutableTypeData type, LocalVariable targetValue, LocalVariable shortCircuit, + LocalContext currentValues) { + CodeTreeBuilder builder = parent.create(); boolean hasUnexpected = hasUnexpectedResult(execution, targetValue.getTypeMirror()); CodeTree executeChild; @@ -1962,21 +2073,22 @@ if (hasUnexpected) { builder.startCatchBlock(getType(UnexpectedResultException.class), "ex"); LocalContext slowPathValues = currentValues.copy(); - slowPathValues.setValue(execution, targetValue.makeGeneric(context).accessWith(CodeTreeBuilder.singleString("ex.getResult()"))); + + ExecutableTypeData delegateType = node.getGenericExecutableType(type); 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)); + builder.tree(createAssignExecuteChild(builder, otherExecution, delegateType, childEvaluatedValue, genericShortCircuit, slowPathValues)); slowPathValues.setValue(otherExecution, childEvaluatedValue); } else { // skip forward already evaluated found = execution == otherExecution; } } - builder.tree(createCallNext(returnType, slowPathValues)); + builder.tree(createCallNext(builder, type, delegateType, slowPathValues)); builder.end(); } @@ -2223,7 +2335,7 @@ } LocalVariable genericValue = target.makeGeneric(context).nextName(); - builder.tree(createAssignExecuteChild(execution, genericValue.getTypeMirror(), genericValue, null, currentValues)); + builder.tree(createAssignExecuteChild(builder, execution, node.getGenericExecutableType(null), genericValue, null, currentValues)); if (executableTypes.size() == sourceTypes.size()) { builder.startThrow().startNew(getType(UnexpectedResultException.class)).tree(genericValue.createReference()).end().end(); } else { @@ -2238,7 +2350,7 @@ return builder.build(); } - private CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, TypeMirror forType, LocalContext currentValues, CodeTree execution) { + private CodeTree createFastPathTryCatchRewriteException(SpecializationData specialization, ExecutableTypeData forType, LocalContext currentValues, CodeTree execution) { if (specialization.getExceptions().isEmpty()) { return execution; } diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemNodeFactory.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemNodeFactory.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/TypeSystemNodeFactory.java Tue Apr 14 15:12:48 2015 +0200 @@ -50,12 +50,7 @@ } public static TypeMirror nodeType(TypeSystemData typeSystem) { - if (typeSystem.isDefault()) { - return typeSystem.getContext().getType(SpecializationNode.class); - } else { - TypeMirror parentType = TypeSystemCodeGenerator.createTypeSystemGen(typeSystem); - return new GeneratedTypeMirror(getQualifiedName(parentType), typeName(typeSystem)); - } + return typeSystem.getContext().getType(SpecializationNode.class); } public static String typeName(TypeSystemData typeSystem) { diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/ElementUtils.java Tue Apr 14 15:12:48 2015 +0200 @@ -120,6 +120,9 @@ } public static TypeMirror boxType(ProcessorContext context, TypeMirror primitiveType) { + if (primitiveType == null) { + return null; + } TypeMirror boxedType = primitiveType; if (boxedType.getKind().isPrimitive()) { boxedType = context.getEnvironment().getTypeUtils().boxedClass((PrimitiveType) boxedType).asType(); @@ -217,6 +220,10 @@ } } + public static boolean isSubtypeBoxed(ProcessorContext context, TypeMirror from, TypeMirror to) { + return isSubtype(boxType(context, from), boxType(context, to)); + } + public static boolean isSubtype(TypeMirror type1, TypeMirror type2) { if (type1 instanceof CodeTypeMirror || type2 instanceof CodeTypeMirror) { throw new UnsupportedOperationException(); @@ -1178,7 +1185,7 @@ public static List uniqueSortedTypes(Collection types) { if (types.isEmpty()) { - return Collections.emptyList(); + return new ArrayList<>(0); } else if (types.size() <= 1) { if (types instanceof List) { return (List) types; diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/java/model/CodeTreeBuilder.java Tue Apr 14 15:12:48 2015 +0200 @@ -316,6 +316,13 @@ } } + public CodeTreeBuilder trees(CodeTree... trees) { + for (CodeTree tree : trees) { + tree(tree); + } + return this; + } + public CodeTreeBuilder string(String chunk1, String chunk2, String chunk3, String chunk4, String... chunks) { push(GROUP).string(chunk1).string(chunk2).string(chunk3).string(chunk4); for (int i = 0; i < chunks.length; i++) { @@ -861,4 +868,15 @@ } } + public CodeTreeBuilder returnDefault() { + ExecutableElement method = findMethod(); + if (ElementUtils.isVoid(method.getReturnType())) { + returnStatement(); + } else { + startReturn().defaultValue(method.getReturnType()).end(); + } + return this; + + } + } diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ExecutableTypeData.java Tue Apr 14 15:12:48 2015 +0200 @@ -24,6 +24,8 @@ import java.util.*; +import static com.oracle.truffle.dsl.processor.java.ElementUtils.*; + import javax.lang.model.element.*; import javax.lang.model.type.*; @@ -34,11 +36,25 @@ public class ExecutableTypeData extends MessageContainer implements Comparable { private final ExecutableElement method; + private final TypeMirror returnType; private final TypeMirror frameParameter; private final List evaluatedParameters; + private ExecutableTypeData delegatedTo; + private final List delegatedFrom = new ArrayList<>(); + + private String uniqueName; + + public ExecutableTypeData(TypeMirror returnType, String uniqueName, TypeMirror frameParameter, List evaluatedParameters) { + this.returnType = returnType; + this.frameParameter = frameParameter; + this.evaluatedParameters = evaluatedParameters; + this.uniqueName = uniqueName; + this.method = null; + } public ExecutableTypeData(ExecutableElement method, int signatureSize, List frameTypes) { this.method = method; + this.returnType = method.getReturnType(); TypeMirror foundFrameParameter = null; List parameters = method.getParameters(); @@ -70,14 +86,32 @@ evaluatedParameters.add(parameter); } this.frameParameter = foundFrameParameter; + this.uniqueName = "execute" + (ElementUtils.isObject(getReturnType()) ? "" : ElementUtils.getTypeId(getReturnType())); + } + + public void addDelegatedFrom(ExecutableTypeData child) { + this.delegatedFrom.add(child); + child.delegatedTo = this; + } + + public List getDelegatedFrom() { + return delegatedFrom; + } + + public ExecutableTypeData getDelegatedTo() { + return delegatedTo; } public ExecutableElement getMethod() { return method; } - public String getName() { - return method.getSimpleName().toString(); + public String getUniqueName() { + return uniqueName; + } + + public void setUniqueName(String name) { + this.uniqueName = name; } @Override @@ -106,27 +140,198 @@ } public TypeMirror getReturnType() { - return method.getReturnType(); + return returnType; } public boolean hasUnexpectedValue(ProcessorContext context) { - return ElementUtils.canThrowType(method.getThrownTypes(), context.getType(UnexpectedResultException.class)); + return method == null ? false : ElementUtils.canThrowType(method.getThrownTypes(), context.getType(UnexpectedResultException.class)); } public boolean isFinal() { - return method.getModifiers().contains(Modifier.FINAL); + return method == null ? false : method.getModifiers().contains(Modifier.FINAL); } public boolean isAbstract() { - return method.getModifiers().contains(Modifier.ABSTRACT); + return method == null ? false : method.getModifiers().contains(Modifier.ABSTRACT); } public int getEvaluatedCount() { return evaluatedParameters.size(); } - public int compareTo(ExecutableTypeData o) { - return ElementUtils.compareMethod(method, o.getMethod()); + public boolean canDelegateTo(NodeData node, ExecutableTypeData to) { + ExecutableTypeData from = this; + if (to.getEvaluatedCount() < from.getEvaluatedCount()) { + return false; + } + + ProcessorContext context = node.getContext(); + + // we cannot delegate from generic to unexpected + if (!from.hasUnexpectedValue(context) && to.hasUnexpectedValue(context)) { + return false; + } + + // we can skip the return type check for void. everything is assignable to void. + if (!isVoid(from.getReturnType())) { + if (!isSubtypeBoxed(context, from.getReturnType(), to.getReturnType()) && !isSubtypeBoxed(context, to.getReturnType(), from.getReturnType())) { + return false; + } + } + if (from.getFrameParameter() != to.getFrameParameter() && from.getFrameParameter() != null && to.getFrameParameter() != null && + !isSubtypeBoxed(context, from.getFrameParameter(), to.getFrameParameter())) { + return false; + } + + for (int i = 0; i < from.getEvaluatedCount(); i++) { + if (!isSubtypeBoxed(context, from.getEvaluatedParameters().get(i), to.getEvaluatedParameters().get(i))) { + return false; + } + } + + for (int i = from.getEvaluatedCount(); i < to.getEvaluatedCount(); i++) { + TypeMirror delegateToParameter = to.getEvaluatedParameters().get(i); + if (i < node.getChildExecutions().size()) { + List genericTypes = node.getGenericTypes(node.getChildExecutions().get(i)); + + boolean typeFound = false; + for (TypeMirror generic : genericTypes) { + if (isSubtypeBoxed(context, generic, delegateToParameter)) { + typeFound = true; + } + } + if (!typeFound) { + return false; + } + } + } + + return true; } + public int compareTo(ExecutableTypeData o2) { + ExecutableTypeData o1 = this; + ProcessorContext context = ProcessorContext.getInstance(); + + int result = Integer.compare(o2.getEvaluatedCount(), o1.getEvaluatedCount()); + if (result != 0) { + return result; + } + + result = Boolean.compare(o1.hasUnexpectedValue(context), o2.hasUnexpectedValue(context)); + if (result != 0) { + return result; + } + + result = compareType(context, o1.getReturnType(), o2.getReturnType()); + if (result != 0) { + return result; + } + result = compareType(context, o1.getFrameParameter(), o2.getFrameParameter()); + if (result != 0) { + return result; + } + + for (int i = 0; i < o1.getEvaluatedCount(); i++) { + result = compareType(context, o1.getEvaluatedParameters().get(i), o2.getEvaluatedParameters().get(i)); + if (result != 0) { + return result; + } + } + + result = o1.getUniqueName().compareTo(o2.getUniqueName()); + if (result != 0) { + return result; + } + + if (o1.getMethod() != null && o2.getMethod() != null) { + result = ElementUtils.compareMethod(o1.getMethod(), o2.getMethod()); + if (result != 0) { + return result; + } + } + return 0; + } + + public static int compareType(ProcessorContext context, TypeMirror signature1, TypeMirror signature2) { + if (signature1 == null) { + if (signature2 == null) { + return 0; + } + return -1; + } else if (signature2 == null) { + return 1; + } + if (ElementUtils.typeEquals(signature1, signature2)) { + return 0; + } + if (isVoid(signature1)) { + if (isVoid(signature2)) { + return 0; + } + return 1; + } else if (isVoid(signature2)) { + return -1; + } + + TypeMirror boxedType1 = ElementUtils.boxType(context, signature1); + TypeMirror boxedType2 = ElementUtils.boxType(context, signature2); + + if (ElementUtils.isSubtype(boxedType1, boxedType2)) { + if (ElementUtils.isSubtype(boxedType2, boxedType1)) { + return 0; + } + return 1; + } else if (ElementUtils.isSubtype(boxedType2, boxedType1)) { + return -1; + } else { + return ElementUtils.getSimpleName(signature1).compareTo(ElementUtils.getSimpleName(signature2)); + } + } + + @Override + public String toString() { + return method != null ? ElementUtils.createReferenceName(method) : getUniqueName() + evaluatedParameters.toString(); + } + + public boolean sameParameters(ExecutableTypeData other) { + if (!typeEquals(other.getFrameParameter(), getFrameParameter())) { + return false; + } + + if (getEvaluatedCount() != other.getEvaluatedCount()) { + return false; + } + + for (int i = 0; i < getEvaluatedCount(); i++) { + if (!typeEquals(getEvaluatedParameters().get(i), other.getEvaluatedParameters().get(i))) { + return false; + } + } + return true; + } + + public boolean sameSignature(ExecutableTypeData other) { + if (!typeEquals(other.getReturnType(), getReturnType())) { + return false; + } + + if (other.getFrameParameter() != null) { + if (!typeEquals(getFrameParameter(), other.getFrameParameter())) { + return false; + } + } + + if (getEvaluatedCount() != other.getEvaluatedCount()) { + return false; + } + + for (int i = 0; i < getEvaluatedCount(); i++) { + if (!typeEquals(getEvaluatedParameters().get(i), other.getEvaluatedParameters().get(i))) { + return false; + } + } + + return true; + } } diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/MessageContainer.java Tue Apr 14 15:12:48 2015 +0200 @@ -106,7 +106,7 @@ visitedSinks.add(this); List foundMessages = new ArrayList<>(); - if (ElementUtils.typeEquals(getMessageElement().asType(), e.asType())) { + if (getMessageElement() != null && ElementUtils.typeEquals(getMessageElement().asType(), e.asType())) { foundMessages.addAll(getMessages()); } for (MessageContainer sink : findChildContainers()) { @@ -119,14 +119,16 @@ TypeElement expectError = context.getTruffleTypes().getExpectError(); if (expectError != null) { Element element = getMessageElement(); - AnnotationMirror mirror = ElementUtils.findAnnotationMirror(element.getAnnotationMirrors(), expectError.asType()); - if (mirror != null) { - List values = ElementUtils.getAnnotationValueList(String.class, mirror, "value"); - if (values == null) { - values = Collections.emptyList(); - } - if (values.size() != msgs.size()) { - log.message(Kind.ERROR, element, mirror, ElementUtils.getAnnotationValue(mirror, "value"), String.format("Error count expected %s but was %s.", values.size(), msgs.size())); + if (element != null) { + AnnotationMirror mirror = ElementUtils.findAnnotationMirror(element.getAnnotationMirrors(), expectError.asType()); + if (mirror != null) { + List values = ElementUtils.getAnnotationValueList(String.class, mirror, "value"); + if (values == null) { + values = Collections.emptyList(); + } + if (values.size() != msgs.size()) { + log.message(Kind.ERROR, element, mirror, ElementUtils.getAnnotationValue(mirror, "value"), String.format("Error count expected %s but was %s.", values.size(), msgs.size())); + } } } } diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/NodeData.java Tue Apr 14 15:12:48 2015 +0200 @@ -48,7 +48,7 @@ private final List specializations = new ArrayList<>(); private final List shortCircuits = new ArrayList<>(); private final List casts = new ArrayList<>(); - private Map> executableTypes; + private final List executableTypes = new ArrayList<>(); private final NodeExecutionData thisExecution; private final boolean generateFactory; @@ -135,10 +135,7 @@ } public int getSignatureSize() { - if (getSpecializations() != null && !getSpecializations().isEmpty()) { - return getSpecializations().get(0).getSignatureSize(); - } - return 0; + return getChildExecutions().size(); } public boolean isFrameUsedByAnyGuard() { @@ -292,9 +289,10 @@ } for (ExecutableTypeData execType : getExecutableTypes()) { - methods.add(execType.getMethod()); + if (execType.getMethod() != null) { + methods.add(execType.getMethod()); + } } - for (ShortCircuitData shortcircuit : getShortCircuits()) { methods.add(shortcircuit.getMethod()); } @@ -329,21 +327,16 @@ } public List getExecutableTypes(int evaluatedCount) { - if (executableTypes == null) { - return Collections.emptyList(); - } if (evaluatedCount == -1) { - List typeData = new ArrayList<>(); - for (int currentEvaluationCount : executableTypes.keySet()) { - typeData.addAll(executableTypes.get(currentEvaluationCount)); + return executableTypes; + } else { + List filteredTypes = new ArrayList<>(); + for (ExecutableTypeData type : executableTypes) { + if (type.getEvaluatedCount() == evaluatedCount) { + filteredTypes.add(type); + } } - return typeData; - } else { - List types = executableTypes.get(evaluatedCount); - if (types == null) { - return Collections.emptyList(); - } - return types; + return filteredTypes; } } @@ -496,6 +489,25 @@ return specializations; } + public ExecutableTypeData getGenericExecutableType(ExecutableTypeData typeHint) { + ExecutableTypeData polymorphicDelegate = null; + if (typeHint != null) { + polymorphicDelegate = typeHint; + while (polymorphicDelegate.getDelegatedTo() != null && polymorphicDelegate.getEvaluatedCount() != getSignatureSize()) { + polymorphicDelegate = polymorphicDelegate.getDelegatedTo(); + } + } + if (polymorphicDelegate == null) { + for (ExecutableTypeData type : getExecutableTypes()) { + if (type.getDelegatedTo() == null && type.getEvaluatedCount() == getSignatureSize()) { + polymorphicDelegate = type; + break; + } + } + } + return polymorphicDelegate; + } + public List getExecutableTypes() { return getExecutableTypes(-1); } @@ -512,10 +524,6 @@ return minimalEvaluatedParameters; } - public void setExecutableTypes(Map> executableTypes) { - this.executableTypes = executableTypes; - } - @Override public String toString() { return getClass().getSimpleName() + "[" + getNodeId() + "]"; @@ -536,7 +544,7 @@ return getNodeId().compareTo(o.getNodeId()); } - public List getPossibleTypes(NodeExecutionData execution) { + public List getGenericTypes(NodeExecutionData execution) { List types = new ArrayList<>(); // add types possible through return types and evaluated parameters in execute methods diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ParameterSpec.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ParameterSpec.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/ParameterSpec.java Tue Apr 14 15:12:48 2015 +0200 @@ -27,6 +27,7 @@ import javax.lang.model.element.*; import javax.lang.model.type.*; +import com.oracle.truffle.dsl.processor.*; import com.oracle.truffle.dsl.processor.java.*; import com.oracle.truffle.dsl.processor.model.MethodSpec.TypeDef; @@ -127,7 +128,7 @@ } if (allowSubclasses) { for (TypeMirror type : allowedTypes) { - if (ElementUtils.isAssignable(variable.asType(), type)) { + if (ElementUtils.isSubtypeBoxed(ProcessorContext.getInstance(), variable.asType(), type)) { return true; } } diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/SpecializationData.java Tue Apr 14 15:12:48 2015 +0200 @@ -41,7 +41,7 @@ } private final NodeData node; - private final SpecializationKind kind; + private SpecializationKind kind; private final List exceptions; private List guards = Collections.emptyList(); private List caches = Collections.emptyList(); @@ -92,6 +92,10 @@ return false; } + public void setKind(SpecializationKind kind) { + this.kind = kind; + } + public boolean isDynamicParameterBound(DSLExpression expression) { Set boundVariables = expression.findBoundVariableElements(); for (Parameter parameter : getDynamicParameters()) { diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/model/TemplateMethod.java Tue Apr 14 15:12:48 2015 +0200 @@ -73,6 +73,18 @@ return findParameter(FRAME_NAME); } + public void removeParameter(Parameter p) { + this.parameters.remove(p); + this.parameterCache.remove(p.getLocalName()); + p.setMethod(this); + } + + public void addParameter(int index, Parameter p) { + this.parameters.add(index, p); + this.parameterCache.put(p.getLocalName(), p); + p.setMethod(this); + } + public String createReferenceName() { if (getMethod() == null) { return "-"; @@ -132,12 +144,13 @@ public void replaceParameter(String localName, Parameter newParameter) { if (returnType.getLocalName().equals(localName)) { returnType = newParameter; - returnType.setMethod(this); } else { Parameter local = findParameter(localName); int index = parameters.indexOf(local); parameters.set(index, newParameter); } + parameterCache.put(newParameter.getLocalName(), newParameter); + newParameter.setMethod(this); } public Iterable getSignatureParameters() { diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/GenericParser.java Tue Apr 14 15:12:48 2015 +0200 @@ -23,11 +23,14 @@ package com.oracle.truffle.dsl.processor.parser; import java.lang.annotation.*; +import java.util.*; import javax.lang.model.element.*; +import javax.lang.model.type.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.dsl.processor.*; +import com.oracle.truffle.dsl.processor.java.*; import com.oracle.truffle.dsl.processor.model.*; import com.oracle.truffle.dsl.processor.model.SpecializationData.SpecializationKind; diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/MethodSpecParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/MethodSpecParser.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/MethodSpecParser.java Tue Apr 14 15:12:48 2015 +0200 @@ -81,7 +81,7 @@ ParameterSpec returnTypeSpec = methodSpecification.getReturnType(); Parameter returnTypeMirror = matchParameter(returnTypeSpec, new CodeVariableElement(returnType, "returnType"), -1, -1); if (returnTypeMirror == null) { - if (emitErrors) { + if (isEmitErrors() && method != null) { TemplateMethod invalidMethod = new TemplateMethod(id, naturalOrder, template, methodSpecification, method, annotation, returnTypeMirror, Collections. emptyList()); String expectedReturnType = returnTypeSpec.toSignatureString(true); String actualReturnType = ElementUtils.getSimpleName(returnType); diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeMethodParser.java Tue Apr 14 15:12:48 2015 +0200 @@ -48,7 +48,7 @@ } protected Collection getPossibleParameterTypes(NodeExecutionData execution) { - return getNode().getPossibleTypes(execution); + return getNode().getGenericTypes(execution); } protected ParameterSpec createReturnParameterSpec() { @@ -58,7 +58,12 @@ } protected Collection getPossibleReturnTypes() { - return getNode().getPossibleTypes(getNode().getThisExecution()); + List possibleTypes = getNode().getGenericTypes(getNode().getThisExecution()); + if (possibleTypes.size() > 1) { + return Arrays.asList(ElementUtils.getCommonSuperType(getContext(), possibleTypes.toArray(new TypeMirror[0]))); + } else { + return possibleTypes; + } } @Override diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/NodeParser.java Tue Apr 14 15:12:48 2015 +0200 @@ -33,6 +33,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.dsl.internal.*; +import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.dsl.processor.*; import com.oracle.truffle.dsl.processor.expression.*; @@ -43,7 +44,6 @@ import com.oracle.truffle.dsl.processor.model.*; import com.oracle.truffle.dsl.processor.model.NodeChildData.Cardinality; import com.oracle.truffle.dsl.processor.model.SpecializationData.SpecializationKind; -import com.oracle.truffle.dsl.processor.model.TemplateMethod.TypeSignature; @DSLOptions public class NodeParser extends AbstractParser { @@ -104,8 +104,9 @@ } catch (CompileErrorException e) { throw e; } catch (Throwable e) { - e.addSuppressed(new RuntimeException(String.format("Parsing of Node %s failed.", ElementUtils.getQualifiedName(rootType)), e)); - throw e; + RuntimeException e2 = new RuntimeException(String.format("Parsing of Node %s failed.", ElementUtils.getQualifiedName(rootType))); + e2.addSuppressed(e); + throw e2; } if (node == null && !enclosedNodes.isEmpty()) { node = new NodeData(context, rootType); @@ -147,17 +148,16 @@ node.getFields().addAll(parseFields(lookupTypes, members)); node.getChildren().addAll(parseChildren(lookupTypes, members)); node.getChildExecutions().addAll(parseExecutions(node.getFields(), node.getChildren(), members)); - node.setExecutableTypes(groupExecutableTypes(parseExecutableTypeData(members, node.getChildExecutions().size(), context.getFrameTypes()))); + node.getExecutableTypes().addAll(parseExecutableTypeData(members, node.getChildExecutions().size(), context.getFrameTypes(), false)); initializeExecutableTypes(node); initializeImportGuards(node, lookupTypes, members); + initializeChildren(node); if (node.hasErrors()) { return node; // error sync point } - initializeChildren(node); - if (node.hasErrors()) { return node; // error sync point } @@ -170,9 +170,10 @@ if (node.hasErrors()) { return node; // error sync point } + initializeSpecializations(members, node); + initializeExecutableTypeHierarchy(node); verifySpecializationSameLength(node); - initializeSpecializations(members, node); initializeShortCircuits(node); // requires specializations and polymorphic specializations verifyVisibilities(node); @@ -183,6 +184,67 @@ return node; } + private static void initializeExecutableTypeHierarchy(NodeData node) { + SpecializationData polymorphic = node.getPolymorphicSpecialization(); + if (polymorphic != null) { + boolean polymorphicSignatureFound = false; + TypeMirror frame = polymorphic.getFrame() != null ? polymorphic.getFrame().getType() : null; + ExecutableTypeData polymorphicType = new ExecutableTypeData(polymorphic.getReturnType().getType(), "execute", frame, TemplateMethod.getSignatureTypes(polymorphic)); + for (ExecutableTypeData type : node.getExecutableTypes()) { + if (polymorphicType.sameSignature(type)) { + polymorphicSignatureFound = true; + break; + } + } + + if (!polymorphicSignatureFound) { + node.getExecutableTypes().add(polymorphicType); + } + } + + List rootTypes = buildExecutableHierarchy(node); + List additionalAbstractRootTypes = new ArrayList<>(); + for (int i = 1; i < rootTypes.size(); i++) { + ExecutableTypeData rootType = rootTypes.get(i); + if (rootType.isAbstract()) { + // cannot implemement root + additionalAbstractRootTypes.add(rootType); + } else { + node.getExecutableTypes().remove(rootType); + } + } + if (!additionalAbstractRootTypes.isEmpty()) { + node.addError("Incompatible abstract execute methods found %s.", rootTypes); + } + + } + + private static List buildExecutableHierarchy(NodeData node) { + List executes = node.getExecutableTypes(); + if (executes.isEmpty()) { + return Collections.emptyList(); + } + List hierarchyExecutes = new ArrayList<>(executes); + Collections.sort(hierarchyExecutes); + ExecutableTypeData parent = hierarchyExecutes.get(0); + ListIterator executesIterator = hierarchyExecutes.listIterator(1); + buildExecutableHierarchy(node, parent, executesIterator); + return hierarchyExecutes; + } + + private static void buildExecutableHierarchy(NodeData node, ExecutableTypeData parent, ListIterator executesIterator) { + while (executesIterator.hasNext()) { + ExecutableTypeData other = executesIterator.next(); + if (other.canDelegateTo(node, parent)) { + parent.addDelegatedFrom(other); + executesIterator.remove(); + } + } + for (int i = 1; i < parent.getDelegatedFrom().size(); i++) { + buildExecutableHierarchy(node, parent.getDelegatedFrom().get(i - 1), parent.getDelegatedFrom().listIterator(i)); + } + } + private List loadMembers(TypeElement templateType) { List members = new ArrayList<>(CompilerFactory.getCompiler(templateType).getAllMembersInDeclarationOrder(context.getEnvironment(), templateType)); @@ -512,13 +574,17 @@ return executions; } - private List parseExecutableTypeData(List elements, int signatureSize, List frameTypes) { + private List parseExecutableTypeData(List elements, int signatureSize, List frameTypes, boolean includeFinals) { List typeData = new ArrayList<>(); for (ExecutableElement method : ElementFilter.methodsIn(elements)) { Set modifiers = method.getModifiers(); if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.STATIC)) { continue; } + if (!includeFinals && modifiers.contains(Modifier.FINAL)) { + continue; + } + if (!method.getSimpleName().toString().startsWith("execute")) { continue; } @@ -545,6 +611,19 @@ } Collections.sort(typeData); + + List names = new ArrayList<>(); + for (ExecutableTypeData type : typeData) { + names.add(type.getUniqueName()); + } + while (renameDuplicateIds(names)) { + // fix point + } + + for (int i = 0; i < typeData.size(); i++) { + typeData.get(i).setUniqueName(names.get(i)); + } + return typeData; } @@ -582,47 +661,16 @@ node.setFrameType(frameType); - int totalGenericCount = 0; - int totalVoidCount = 0; - for (Integer evaluatedCount : evaluatedCounts) { - List genericExecutes = node.findGenericExecutableTypes(context, evaluatedCount); - int genericCount = 0; - int voidCount = 0; - for (ExecutableTypeData executableTypeData : genericExecutes) { - if (!executableTypeData.getMethod().getModifiers().contains(Modifier.FINAL)) { - if (ElementUtils.isVoid(executableTypeData.getReturnType())) { - voidCount++; - } else { - genericCount++; - } - } + boolean genericFound = false; + for (ExecutableTypeData type : node.getExecutableTypes()) { + if (!type.hasUnexpectedValue(context)) { + genericFound = true; + break; } - // multiple generic execute - if (evaluatedCount == 0) { - if (voidCount > 1) { - List methodSignatures = new ArrayList<>(); - for (ExecutableTypeData type : genericExecutes) { - if (context.isType(type.getReturnType(), void.class)) { - methodSignatures.add(ElementUtils.createReferenceName(type.getMethod())); - } - } - node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures); - } else if (genericCount > 1) { - List methodSignatures = new ArrayList<>(); - for (ExecutableTypeData type : genericExecutes) { - if (!context.isType(type.getReturnType(), void.class)) { - methodSignatures.add(ElementUtils.createReferenceName(type.getMethod())); - } - } - node.addWarning("Multiple accessible and overridable generic execute methods found %s. Remove all but one or mark all but one as final.", methodSignatures); - } - } - totalGenericCount += genericCount; - totalVoidCount += voidCount; } // no generic executes - if (totalGenericCount + totalVoidCount == 0) { + if (!genericFound) { node.addError("No accessible and overridable generic execute method found. Generic execute methods usually have the " + "signature 'public abstract {Type} execute(VirtualFrame)' and must not throw any checked exceptions."); } @@ -658,25 +706,6 @@ } - private static Map> groupExecutableTypes(List executableTypes) { - Map> groupedTypes = new TreeMap<>(); - for (ExecutableTypeData type : executableTypes) { - int evaluatedCount = type.getEvaluatedCount(); - - List types = groupedTypes.get(evaluatedCount); - if (types == null) { - types = new ArrayList<>(); - groupedTypes.put(evaluatedCount, types); - } - types.add(type); - } - - for (List types : groupedTypes.values()) { - Collections.sort(types); - } - return groupedTypes; - } - private void initializeChildren(NodeData node) { initializeExecuteWith(node); @@ -759,7 +788,7 @@ if (parentNode.getFrameType() != null) { frameTypes = Arrays.asList(parentNode.getFrameType()); } - node.setExecutableTypes(groupExecutableTypes(parseExecutableTypeData(members, child.getExecuteWith().size(), frameTypes))); + node.getExecutableTypes().addAll(parseExecutableTypeData(members, child.getExecuteWith().size(), frameTypes, true)); node.setFrameType(parentNode.getFrameType()); return node; } @@ -1259,7 +1288,7 @@ if (execution == null) { allowedTypes = spec.getAllowedTypes(); } else { - allowedTypes = node.getPossibleTypes(execution); + allowedTypes = node.getGenericTypes(execution); } if (allowedTypes.size() == 1) { return allowedTypes.iterator().next(); @@ -1301,36 +1330,68 @@ SpecializationData generic = node.getGenericSpecialization(); - List polymorphicSignature = new ArrayList<>(); - List updatePolymorphic = Arrays.asList(); - for (Parameter genericParameter : updatePolymorphic) { - if (!genericParameter.getSpecification().isSignature()) { - continue; - } + List types = new ArrayList<>(); - Set usedTypes = new HashSet<>(); - for (SpecializationData specialization : node.getSpecializations()) { - if (!specialization.isSpecialized()) { - continue; - } - Parameter parameter = specialization.findParameter(genericParameter.getLocalName()); - if (parameter == null) { - throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName()); - } - usedTypes.add(parameter.getType()); + Set frameTypes = new HashSet<>(); + for (SpecializationData specialization : node.getSpecializations()) { + if (specialization.getFrame() != null) { + frameTypes.add(specialization.getFrame().getType()); } - - TypeMirror polymorphicType; - if (usedTypes.size() == 1) { - polymorphicType = usedTypes.iterator().next(); + } + if (!frameTypes.isEmpty()) { + TypeMirror frameType; + if (frameTypes.size() == 1) { + frameType = frameTypes.iterator().next(); } else { - polymorphicType = context.getType(Object.class); + frameType = context.getType(Frame.class); } - polymorphicSignature.add(polymorphicType); + types.add(new CodeVariableElement(frameType, "frameValue")); } - SpecializationData polymorphic = new SpecializationData(node, generic, SpecializationKind.POLYMORPHIC); - polymorphic.updateSignature(new TypeSignature(polymorphicSignature)); + TypeMirror returnType = null; + int index = 0; + for (Parameter genericParameter : generic.getReturnTypeAndParameters()) { + TypeMirror polymorphicType; + if (!genericParameter.getSpecification().isSignature()) { + polymorphicType = genericParameter.getType(); + } else { + Set usedTypes = new HashSet<>(); + for (SpecializationData specialization : node.getSpecializations()) { + if (specialization.isUninitialized()) { + continue; + } + Parameter parameter = specialization.findParameter(genericParameter.getLocalName()); + if (parameter == specialization.getReturnType() && specialization.isFallback() && specialization.getMethod() == null) { + continue; + } + if (parameter == null) { + throw new AssertionError("Parameter existed in generic specialization but not in specialized. param = " + genericParameter.getLocalName()); + } + usedTypes.add(parameter.getType()); + } + + if (usedTypes.size() == 1) { + polymorphicType = usedTypes.iterator().next(); + + if (node.getTypeSystem().hasImplicitSourceTypes(polymorphicType)) { + polymorphicType = context.getType(Object.class); + } + } else { + polymorphicType = context.getType(Object.class); + } + } + if (genericParameter == generic.getReturnType()) { + returnType = polymorphicType; + } else { + types.add(new CodeVariableElement(polymorphicType, "param" + index)); + } + index++; + } + + SpecializationMethodParser parser = new SpecializationMethodParser(context, node); + + SpecializationData polymorphic = parser.create("Polymorphic", TemplateMethod.NO_NATURAL_ORDER, null, null, returnType, types); + polymorphic.setKind(SpecializationKind.POLYMORPHIC); node.getSpecializations().add(polymorphic); } diff -r f83fd99b2962 -r 476374f3fe9a graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationMethodParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationMethodParser.java Tue Apr 14 15:12:48 2015 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/parser/SpecializationMethodParser.java Tue Apr 14 15:12:48 2015 +0200 @@ -58,55 +58,59 @@ } private SpecializationData parseSpecialization(TemplateMethod method) { - AnnotationValue rewriteValue = ElementUtils.getAnnotationValue(method.getMarkerAnnotation(), "rewriteOn"); - List exceptionTypes = ElementUtils.getAnnotationValueList(TypeMirror.class, method.getMarkerAnnotation(), "rewriteOn"); List exceptionData = new ArrayList<>(); - List rewriteOnTypes = new ArrayList<>(); - for (TypeMirror exceptionType : exceptionTypes) { - SpecializationThrowsData throwsData = new SpecializationThrowsData(method.getMarkerAnnotation(), rewriteValue, exceptionType); - if (!ElementUtils.canThrowType(method.getMethod().getThrownTypes(), exceptionType)) { - method.addError("A rewriteOn checked exception was specified but not thrown in the method's throws clause. The @%s method must specify a throws clause with the exception type '%s'.", - Specialization.class.getSimpleName(), ElementUtils.getQualifiedName(exceptionType)); + if (method.getMethod() != null) { + AnnotationValue rewriteValue = ElementUtils.getAnnotationValue(method.getMarkerAnnotation(), "rewriteOn"); + List exceptionTypes = ElementUtils.getAnnotationValueList(TypeMirror.class, method.getMarkerAnnotation(), "rewriteOn"); + List rewriteOnTypes = new ArrayList<>(); + for (TypeMirror exceptionType : exceptionTypes) { + SpecializationThrowsData throwsData = new SpecializationThrowsData(method.getMarkerAnnotation(), rewriteValue, exceptionType); + if (!ElementUtils.canThrowType(method.getMethod().getThrownTypes(), exceptionType)) { + method.addError("A rewriteOn checked exception was specified but not thrown in the method's throws clause. The @%s method must specify a throws clause with the exception type '%s'.", + Specialization.class.getSimpleName(), ElementUtils.getQualifiedName(exceptionType)); + } + rewriteOnTypes.add(throwsData.getJavaClass()); + exceptionData.add(throwsData); } - rewriteOnTypes.add(throwsData.getJavaClass()); - exceptionData.add(throwsData); - } - - for (TypeMirror typeMirror : method.getMethod().getThrownTypes()) { - if (!ElementUtils.canThrowType(rewriteOnTypes, typeMirror)) { - method.addError(rewriteValue, - "A checked exception '%s' is thrown but is not specified using the rewriteOn property. Checked exceptions that are not used for rewriting are not handled by the DSL. Use RuntimeExceptions for this purpose instead.", - ElementUtils.getQualifiedName(typeMirror)); - } - } - Collections.sort(exceptionData, new Comparator() { - - @Override - public int compare(SpecializationThrowsData o1, SpecializationThrowsData o2) { - return ElementUtils.compareByTypeHierarchy(o1.getJavaClass(), o2.getJavaClass()); - } - }); - SpecializationData specialization = new SpecializationData(getNode(), method, SpecializationKind.SPECIALIZED, exceptionData); - - String insertBeforeName = ElementUtils.getAnnotationValue(String.class, method.getMarkerAnnotation(), "insertBefore"); - if (!insertBeforeName.equals("")) { - specialization.setInsertBeforeName(insertBeforeName); - } - - List containsDefs = ElementUtils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "contains"); - Set containsNames = specialization.getContainsNames(); - containsNames.clear(); - if (containsDefs != null) { - for (String include : containsDefs) { - if (!containsNames.contains(include)) { - specialization.getContainsNames().add(include); - } else { - AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains"); - specialization.addError(value, "Duplicate contains declaration '%s'.", include); + for (TypeMirror typeMirror : method.getMethod().getThrownTypes()) { + if (!ElementUtils.canThrowType(rewriteOnTypes, typeMirror)) { + method.addError(rewriteValue, "A checked exception '%s' is thrown but is not specified using the rewriteOn property. " + + "Checked exceptions that are not used for rewriting are not handled by the DSL. Use RuntimeExceptions for this purpose instead.", + ElementUtils.getQualifiedName(typeMirror)); } } + Collections.sort(exceptionData, new Comparator() { + + @Override + public int compare(SpecializationThrowsData o1, SpecializationThrowsData o2) { + return ElementUtils.compareByTypeHierarchy(o1.getJavaClass(), o2.getJavaClass()); + } + }); + } + SpecializationData specialization = new SpecializationData(getNode(), method, SpecializationKind.SPECIALIZED, exceptionData); + + if (method.getMethod() != null) { + String insertBeforeName = ElementUtils.getAnnotationValue(String.class, method.getMarkerAnnotation(), "insertBefore"); + if (!insertBeforeName.equals("")) { + specialization.setInsertBeforeName(insertBeforeName); + } + + List containsDefs = ElementUtils.getAnnotationValueList(String.class, specialization.getMarkerAnnotation(), "contains"); + Set containsNames = specialization.getContainsNames(); + containsNames.clear(); + if (containsDefs != null) { + for (String include : containsDefs) { + if (!containsNames.contains(include)) { + specialization.getContainsNames().add(include); + } else { + AnnotationValue value = ElementUtils.getAnnotationValue(specialization.getMarkerAnnotation(), "contains"); + specialization.addError(value, "Duplicate contains declaration '%s'.", include); + } + } + + } } return specialization;