# HG changeset patch # User Christian Humer # Date 1378477000 -7200 # Node ID 2fb276f5e3e90dae7d595794062982f11ffde832 # Parent e5b5a5cb0ac7bfa9c88cdaf5b21c72f88bb579bd Truffle-DSL: implemented implicit casts. diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.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/ImplicitCastTest.java Fri Sep 06 16:16:40 2013 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012, 2013, 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 com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast0NodeFactory; +import com.oracle.truffle.api.dsl.test.NodeContainerTest.Str; +import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; +import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.frame.*; + +public class ImplicitCastTest { + + @TypeSystem({int.class, boolean.class, String.class, Str.class}) + static class ImplicitCast0Types { + + @ImplicitCast + boolean castInt(int intvalue) { + return intvalue == 1 ? true : false; + } + + @ImplicitCast + boolean castString(String strvalue) { + return strvalue.equals("1"); + } + + } + + @TypeSystemReference(ImplicitCast0Types.class) + @NodeChild(value = "operand", type = ImplicitCast0Node.class) + abstract static class ImplicitCast0Node extends ValueNode { + + public abstract Object executeEvaluated(VirtualFrame frame, Object value2); + + @Specialization(order = 1) + public String op1(String value) throws RuntimeException { + return value; + } + + @Specialization(order = 2) + public boolean op1(boolean value) throws RuntimeException { + return value; + } + + } + + @Test + public void testImplicitCast0() { + ImplicitCast0Node node = ImplicitCast0NodeFactory.create(null); + TestRootNode root = new TestRootNode<>(node); + Assert.assertEquals("2", root.getNode().executeEvaluated(null, "2")); + Assert.assertEquals(true, root.getNode().executeEvaluated(null, 1)); + Assert.assertEquals("1", root.getNode().executeEvaluated(null, "1")); + Assert.assertEquals(true, root.getNode().executeEvaluated(null, 1)); + Assert.assertEquals(true, root.getNode().executeEvaluated(null, true)); + } + + // TODO assert implicit casts only in one direction + + // test example that covers the most cases + +} diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java --- a/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java Fri Sep 06 16:16:40 2013 +0200 @@ -48,6 +48,11 @@ return (int) value; } + @ImplicitCast + Str castStr(String strvalue) { + return new Str(strvalue); + } + } @TypeSystemReference(SimpleTypes.class) diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeChildData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeChildData.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeChildData.java Fri Sep 06 16:16:40 2013 +0200 @@ -49,6 +49,7 @@ DEFAULT, SHORT_CIRCUIT } + private final NodeData parent; private final Element sourceElement; private final AnnotationMirror sourceAnnotationMirror; @@ -64,8 +65,9 @@ private NodeData nodeData; - public NodeChildData(Element sourceElement, AnnotationMirror sourceMirror, String name, TypeMirror nodeType, TypeMirror originalNodeType, Element accessElement, Cardinality cardinality, - ExecutionKind executionKind) { + public NodeChildData(NodeData parent, Element sourceElement, AnnotationMirror sourceMirror, String name, TypeMirror nodeType, TypeMirror originalNodeType, Element accessElement, + Cardinality cardinality, ExecutionKind executionKind) { + this.parent = parent; this.sourceElement = sourceElement; this.sourceAnnotationMirror = sourceMirror; this.name = name; @@ -84,6 +86,31 @@ this.executeWith = executeWith; } + public boolean needsImplicitCast(ProcessorContext context) { + if (!parent.needsRewrites(context)) { + return false; + } + + boolean used = false; + SpecializationData generic = parent.getGenericSpecialization(); + for (ActualParameter param : generic.getParameters()) { + if (!param.getSpecification().isSignature()) { + continue; + } + NodeChildData child = parent.findChild(param.getSpecification().getName()); + if (child == this) { + used = true; + break; + } + } + + if (!used) { + return false; + } + + return getNodeData().getTypeSystem().getImplicitCasts() != null && !getNodeData().getTypeSystem().getImplicitCasts().isEmpty(); + } + public ExecutableTypeData findExecutableType(ProcessorContext context, TypeData targetType) { ExecutableTypeData executableType = nodeData.findExecutableType(targetType, getExecuteWith().size()); if (executableType == null) { diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java Fri Sep 06 16:16:40 2013 +0200 @@ -62,12 +62,17 @@ return node.getNodeId() + "Factory"; } + private static String nodeCastClassName(NodeData node, TypeData type) { + String nodeid = resolveNodeId(node); + if (type == null) { + return nodeid + "ImplicitCast"; + } else { + return Utils.firstLetterUpperCase(Utils.getSimpleName(type.getPrimitiveType())) + "Cast"; + } + } + private static String nodeSpecializationClassName(SpecializationData specialization) { - String nodeid = specialization.getNode().getNodeId(); - if (nodeid.endsWith("Node") && !nodeid.equals("Node")) { - nodeid = nodeid.substring(0, nodeid.length() - 4); - } - + String nodeid = resolveNodeId(specialization.getNode()); String name = Utils.firstLetterUpperCase(nodeid); name += Utils.firstLetterUpperCase(specialization.getId()); name += "Node"; @@ -75,10 +80,7 @@ } private static String nodePolymorphicClassName(NodeData node, SpecializationData specialization) { - String nodeid = node.getNodeId(); - if (nodeid.endsWith("Node") && !nodeid.equals("Node")) { - nodeid = nodeid.substring(0, nodeid.length() - 4); - } + String nodeid = resolveNodeId(node); String name = Utils.firstLetterUpperCase(nodeid); if (specialization == node.getGenericPolymorphicSpecialization()) { @@ -89,10 +91,22 @@ return name; } + private static String resolveNodeId(NodeData node) { + String nodeid = node.getNodeId(); + if (nodeid.endsWith("Node") && !nodeid.equals("Node")) { + nodeid = nodeid.substring(0, nodeid.length() - 4); + } + return nodeid; + } + private static String valueNameEvaluated(ActualParameter targetParameter) { return valueName(targetParameter) + "Evaluated"; } + private static String typeName(ActualParameter param) { + return param.getLocalName() + "Type"; + } + private static String valueName(ActualParameter param) { return param.getLocalName(); } @@ -147,7 +161,8 @@ } } - private void addInternalValueParameterNames(CodeTreeBuilder builder, TemplateMethod source, TemplateMethod specialization, String unexpectedValueName, boolean forceFrame, boolean includeImplicit) { + private void addInternalValueParameterNames(CodeTreeBuilder builder, TemplateMethod source, TemplateMethod specialization, String unexpectedValueName, boolean forceFrame, boolean includeImplicit, + Map customNames) { if (forceFrame && specialization.getSpecification().findParameterSpec("frame") != null) { builder.string("frameValue"); } @@ -166,7 +181,9 @@ ActualParameter sourceParameter = source.findParameter(parameter.getLocalName()); - if (unexpectedValueName != null && parameter.getLocalName().equals(unexpectedValueName)) { + if (customNames != null && customNames.containsKey(parameter.getLocalName())) { + builder.string(customNames.get(parameter.getLocalName())); + } else if (unexpectedValueName != null && parameter.getLocalName().equals(unexpectedValueName)) { builder.cast(parameter.getType(), CodeTreeBuilder.singleString("ex.getResult()")); } else if (sourceParameter != null) { builder.string(valueName(sourceParameter, parameter)); @@ -298,10 +315,7 @@ } private static String baseClassName(NodeData node) { - String nodeid = node.getNodeId(); - if (nodeid.endsWith("Node") && !nodeid.equals("Node")) { - nodeid = nodeid.substring(0, nodeid.length() - 4); - } + String nodeid = resolveNodeId(node); String name = Utils.firstLetterUpperCase(nodeid); name += "BaseNode"; return name; @@ -362,7 +376,7 @@ builder.startThrow().startNew(getContext().getType(UnsupportedOperationException.class)); builder.startCall("createInfo0"); builder.doubleQuote("Unsupported values"); - addInternalValueParameterNames(builder, current, current, null, false, true); + addInternalValueParameterNames(builder, current, current, null, false, true, null); builder.end().end().end(); } @@ -422,7 +436,11 @@ } @Override + @SuppressWarnings("unchecked") protected void createChildren(NodeData node) { + List casts = new ArrayList<>(getElement().getEnclosedElements()); + getElement().getEnclosedElements().clear(); + Map> childTypes = new LinkedHashMap<>(); if (node.getDeclaredNodes() != null && !node.getDeclaredNodes().isEmpty()) { for (NodeData nodeChild : node.getDeclaredNodes()) { @@ -432,14 +450,35 @@ } if (node.needsFactory() || node.getNodeDeclaringChildren().size() > 0) { - add(new NodeFactoryFactory(context, childTypes), node); + NodeFactoryFactory factory = new NodeFactoryFactory(context, childTypes); + add(factory, node); + factory.getElement().getEnclosedElements().addAll(casts); + } + } + + protected CodeTree createCastType(NodeData node, TypeData sourceType, TypeData targetType, boolean expect, CodeTree value) { + if (targetType == null) { + return value; + } else if (sourceType != null && !sourceType.needsCastTo(getContext(), targetType)) { + return value; } + + CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); + String targetMethodName; + if (expect) { + targetMethodName = TypeSystemCodeGenerator.expectTypeMethodName(targetType); + } else { + targetMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType); + } + startCallTypeSystemMethod(getContext(), builder, node, targetMethodName); + builder.tree(value); + builder.end().end(); + return builder.getRoot(); } private class NodeFactoryFactory extends ClassElementFactory { private final Map> childTypes; - private CodeTypeElement generatedNode; public NodeFactoryFactory(ProcessorContext context, Map> childElements) { @@ -853,7 +892,8 @@ ExecutableElement getter = (ExecutableElement) child.getAccessElement(); CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), getter); method.getModifiers().remove(Modifier.ABSTRACT); - method.createBuilder().startReturn().string("this.").string(child.getName()).end(); + CodeTreeBuilder builder = method.createBuilder(); + builder.startReturn().string("this.").string(child.getName()).end(); clazz.add(method); } } @@ -1107,29 +1147,58 @@ builder.startStatement(); String fieldName = var.getSimpleName().toString(); - CodeTree fieldInit = CodeTreeBuilder.singleString(var.getSimpleName().toString()); - builder.string("this.").string(var.getSimpleName().toString()); - NodeChildData child = node.findChild(fieldName); - if (child != null) { - CreateCastData createCast = node.findCast(child.getName()); - if (createCast != null) { - fieldInit = createTemplateMethodCall(builder, null, node.getGenericSpecialization(), createCast, null, child.getName()); - } - } - - if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNode())) { - builder.string(" = adoptChild(").tree(fieldInit).string(")"); - } else if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) { - builder.string(" = adoptChildren(").tree(fieldInit).string(")"); - } else { - builder.string(" = ").tree(fieldInit); - } + + CodeTree init = createStaticCast(builder, child, fieldName); + init = createAdoptChild(builder, var.asType(), init); + + builder.string("this.").string(fieldName).string(" = ").tree(init); builder.end(); } return method; } + private CodeTree createStaticCast(CodeTreeBuilder parent, NodeChildData child, String fieldName) { + NodeData parentNode = getModel().getNode(); + if (child != null) { + CreateCastData createCast = parentNode.findCast(child.getName()); + if (createCast != null) { + return createTemplateMethodCall(parent, null, parentNode.getGenericSpecialization(), createCast, null, fieldName); + } + } + return CodeTreeBuilder.singleString(fieldName); + } + + private CodeTree createAdoptChild(CodeTreeBuilder parent, TypeMirror type, CodeTree value) { + CodeTreeBuilder builder = new CodeTreeBuilder(parent); + if (Utils.isAssignable(getContext(), type, getContext().getTruffleTypes().getNode())) { + builder.string("adoptChild(").tree(value).string(")"); + } else if (Utils.isAssignable(getContext(), type, getContext().getTruffleTypes().getNodeArray())) { + builder.string("adoptChildren(").tree(value).string(")"); + } else { + builder.tree(value); + } + return builder.getRoot(); + } + + private CodeTree createCopyArray(CodeTreeBuilder parent, NodeChildData child, TypeMirror arrayType, CodeBlock accessElement) { + CodeTreeBuilder builder = parent.create(); + NodeData node = getModel().getNode(); + builder.string("new ").type(arrayType).string(" {"); + builder.startCommaGroup(); + for (ActualParameter parameter : getModel().getParameters()) { + NodeChildData foundChild = node.findChild(parameter.getSpecification().getName()); + if (foundChild == child) { + builder.startGroup(); + builder.tree(accessElement.create(builder, String.valueOf(parameter.getIndex()))); + builder.end(); + } + } + builder.end(); + builder.end().string("}"); + return builder.getRoot(); + } + private CodeExecutableElement createCopyConstructor(CodeTypeElement type, ExecutableElement superConstructor) { CodeExecutableElement method = new CodeExecutableElement(null, type.getSimpleName().toString()); CodeTreeBuilder builder = method.createBuilder(); @@ -1141,34 +1210,22 @@ for (VariableElement var : type.getFields()) { builder.startStatement(); - String varName = var.getSimpleName().toString(); - builder.string("this.").string(varName); - if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNode())) { - builder.string(" = adoptChild(copy.").string(varName).string(")"); - } else if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) { - NodeData node = getModel().getNode(); - NodeChildData child = node.findChild(varName); - if (child != null) { - builder.string(" = adoptChildren("); - builder.string("new ").type((child.getNodeType())).string(" {"); - builder.startCommaGroup(); - for (ActualParameter parameter : getModel().getParameters()) { - NodeChildData foundChild = node.findChild(parameter.getSpecification().getName()); - if (foundChild == child) { - builder.startGroup(); - builder.string("copy.").string(varName).string("[").string(String.valueOf(parameter.getIndex())).string("]"); - builder.end(); - } + final String varName = var.getSimpleName().toString(); + final TypeMirror varType = var.asType(); + + final String copyAccess = "copy." + varName; + CodeTree init = CodeTreeBuilder.singleString(copyAccess); + if (Utils.isAssignable(getContext(), var.asType(), getContext().getTruffleTypes().getNodeArray())) { + NodeChildData child = getModel().getNode().findChild(varName); + init = createCopyArray(builder, child, varType, new CodeBlock() { + + public CodeTree create(CodeTreeBuilder parent, String index) { + return CodeTreeBuilder.singleString(copyAccess + "[" + index + "]"); } - - builder.end().string("})"); - } else { - builder.string(" = adoptChildren(copy.").string(varName).string(")"); - } - } else { - builder.string(" = copy.").string(varName); + }); } - builder.end(); + init = createAdoptChild(builder, varType, init); + builder.startStatement().string("this.").string(varName).string(" = ").tree(init).end(); } if (getModel().getNode().isPolymorphic()) { builder.statement("this.next0 = adoptChild(copy.next0)"); @@ -1184,7 +1241,8 @@ } private CodeVariableElement createChildField(NodeChildData child) { - CodeVariableElement var = new CodeVariableElement(child.getNodeType(), child.getName()); + TypeMirror type = child.getNodeType(); + CodeVariableElement var = new CodeVariableElement(type, child.getName()); var.getModifiers().add(Modifier.PROTECTED); DeclaredType annotationType; @@ -1223,7 +1281,7 @@ } builder.startStatement().string("String message = ").startCall("createInfo0").string("reason"); - addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, false, true); + addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, false, true, null); builder.end().end(); final String currentNodeVar = currentNode; @@ -1434,7 +1492,7 @@ guardsAnd = " && "; } - CodeTree cast = createCast(castBuilder, child, valueParam, typeGuard.getType()); + CodeTree cast = createCast(castBuilder, child, valueParam, typeGuard.getType(), minimumState); if (cast != null) { castBuilder.tree(cast); } @@ -1526,7 +1584,15 @@ builder.string(" || "); } - startCallTypeSystemMethod(getContext(), builder, node, TypeSystemCodeGenerator.isTypeMethodName(targetType)); + String castMethodName; + List types = getModel().getNode().getTypeSystem().lookupSourceTypes(targetType); + if (types.size() > 1) { + castMethodName = TypeSystemCodeGenerator.isImplicitTypeMethodName(targetType); + } else { + castMethodName = TypeSystemCodeGenerator.isTypeMethodName(targetType); + } + + startCallTypeSystemMethod(getContext(), builder, node, castMethodName); builder.string(valueName(source)); builder.end().end(); // call @@ -1539,7 +1605,7 @@ return builder.getRoot(); } - private CodeTree createCast(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, TypeData targetType) { + private CodeTree createCast(CodeTreeBuilder parent, NodeChildData field, ActualParameter source, TypeData targetType, boolean minimumState) { NodeData node = field.getNodeData(); TypeData sourceType = source.getTypeSystemType(); @@ -1554,9 +1620,24 @@ condition = CodeTreeBuilder.singleString(valueName(shortCircuit)); } - CodeTree value = createCallTypeSystemMethod(context, parent, node, TypeSystemCodeGenerator.asTypeMethodName(targetType), CodeTreeBuilder.singleString(valueName(source))); - - return createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, value); + String castMethodName; + List types = getModel().getNode().getTypeSystem().lookupSourceTypes(targetType); + if (types.size() > 1) { + castMethodName = TypeSystemCodeGenerator.asImplicitTypeMethodName(targetType); + } else { + castMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType); + } + + CodeTree value = createCallTypeSystemMethod(context, parent, node, castMethodName, CodeTreeBuilder.singleString(valueName(source))); + + CodeTreeBuilder builder = parent.create(); + builder.tree(createLazyAssignment(parent, castValueName(source), targetType.getPrimitiveType(), condition, value)); + if (minimumState && types.size() > 1) { + CodeTree castType = createCallTypeSystemMethod(context, parent, node, TypeSystemCodeGenerator.getImplicitClass(targetType), CodeTreeBuilder.singleString(valueName(source))); + builder.tree(createLazyAssignment(builder, typeName(source), getContext().getType(Class.class), condition, castType)); + } + + return builder.getRoot(); } private CodeTree createMethodGuard(CodeTreeBuilder parent, String prefix, SpecializationData source, GuardData guard) { @@ -1595,7 +1676,6 @@ builder.tree(createRewriteGeneric(builder, source, current, currentNodeVar)); builder.end(); } else { - // simple rewrite if (current.getExceptions().isEmpty()) { builder.tree(createGenericInvoke(builder, source, current, createReplaceCall(builder, current, currentNodeVar, currentNodeVar, null), null)); } else { @@ -1669,7 +1749,7 @@ } if (current.isGeneric()) { builder.startReturn().tree(replace).string(".").startCall(EXECUTE_GENERIC_NAME); - addInternalValueParameterNames(builder, source, current, null, current.getNode().needsFrame(), true); + addInternalValueParameterNames(builder, source, current, null, current.getNode().needsFrame(), true, null); builder.end().end(); } else if (current.getMethod() == null) { if (replaceCall != null) { @@ -1696,7 +1776,19 @@ } else { replaceCall.startCall("replace"); } - replaceCall.startGroup().startNew(className).string(source).end().end(); + replaceCall.startGroup().startNew(className).string(source); + for (ActualParameter param : current.getParameters()) { + if (!param.getSpecification().isSignature()) { + continue; + } + NodeChildData child = getModel().getNode().findChild(param.getSpecification().getName()); + List types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType()); + if (types.size() > 1) { + replaceCall.string(typeName(param)); + } + } + replaceCall.end().end(); + if (message == null) { replaceCall.string("message"); } else { @@ -1732,7 +1824,7 @@ builder.startReturn(); builder.startCall(currentNode + ".next0", executeCachedName(node.getGenericPolymorphicSpecialization())); - addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, true, true); + addInternalValueParameterNames(builder, node.getGenericSpecialization(), node.getGenericSpecialization(), null, true, true, null); builder.end(); builder.end(); @@ -1776,7 +1868,7 @@ executeParameterNames[i] = valueName(executeParameters.get(i)); } - builder.tree(createExecuteChildren(builder, executable, specialization, executeParameters, null, true)); + builder.tree(createExecuteChildren(builder, executable, specialization, executeParameters, null)); CodeTree primaryExecuteCall = createTemplateMethodCall(builder, null, executable, castExecutable, null, executeParameterNames); if (needsTry) { @@ -1827,134 +1919,180 @@ return createCastType(node, sourceType, castedType.getType(), hasUnexpected, value); } - protected CodeTree createCastType(NodeData node, TypeData sourceType, TypeData targetType, boolean expect, CodeTree value) { - if (targetType == null) { - return value; - } else if (!sourceType.needsCastTo(getContext(), targetType)) { - return value; - } - - CodeTreeBuilder builder = CodeTreeBuilder.createBuilder(); - String targetMethodName; - if (expect) { - targetMethodName = TypeSystemCodeGenerator.expectTypeMethodName(targetType); - } else { - targetMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetType); - } - startCallTypeSystemMethod(getContext(), builder, node, targetMethodName); - - builder.tree(value); - builder.end().end(); - return builder.getRoot(); - } - protected CodeTree createExecuteChildren(CodeTreeBuilder parent, ExecutableTypeData sourceExecutable, SpecializationData specialization, List targetParameters, - ActualParameter unexpectedParameter, boolean cast) { - NodeData sourceNode = specialization.getNode(); - - CodeTreeBuilder builder = new CodeTreeBuilder(parent); - + ActualParameter unexpectedParameter) { + CodeTreeBuilder builder = parent.create(); + NodeData node = specialization.getNode(); for (ActualParameter targetParameter : targetParameters) { - NodeChildData field = sourceNode.findChild(targetParameter.getSpecification().getName()); + NodeChildData child = node.findChild(targetParameter.getSpecification().getName()); if (!targetParameter.getSpecification().isSignature()) { continue; } - TypeData targetType = targetParameter.getTypeSystemType(); ExecutableTypeData targetExecutable = null; - if (field != null) { - targetExecutable = field.findExecutableType(getContext(), targetType); + if (child != null) { + targetExecutable = child.findExecutableType(getContext(), targetType); + } + + if (targetExecutable == null) { + // TODO what to do? assertion? + continue; } - ActualParameter sourceParameter = sourceExecutable.findParameter(targetParameter.getLocalName()); - - String targetVariableName = valueName(targetParameter); - - CodeTree executionExpression = null; - if ((sourceParameter != null && cast) || sourceParameter != null) { - TypeData sourceType = sourceParameter.getTypeSystemType(); - if (targetExecutable == null || !sourceType.needsCastTo(getContext(), targetType)) { - if (field != null && field.isShortCircuit() && sourceParameter != null) { - builder.tree(createShortCircuitValue(builder, specialization, field, targetParameter.getPreviousParameter(), unexpectedParameter)); - } - builder.startStatement(); - builder.type(targetParameter.getType()).string(" "); - builder.string(targetVariableName).string(" = "); - builder.tree(CodeTreeBuilder.singleString(valueNameEvaluated(targetParameter))); - builder.end(); - continue; + CodeTree executionExpressions = createExecutionExpresssions(builder, child, sourceExecutable, targetExecutable, targetParameter, unexpectedParameter); + + String targetVarName = valueName(targetParameter); + CodeTree unexpectedTree = createCatchUnexpectedTree(builder, executionExpressions, targetVarName, specialization, sourceExecutable, targetExecutable, targetParameter, + isShortCircuit(child)); + CodeTree shortCircuitTree = createShortCircuitTree(builder, unexpectedTree, targetVarName, specialization, targetParameter, unexpectedParameter); + + if (shortCircuitTree == executionExpressions) { + if (containsNewLine(executionExpressions)) { + builder.declaration(sourceExecutable.getType().getPrimitiveType(), targetVarName); + builder.tree(shortCircuitTree); } else { - CodeTree valueTree = CodeTreeBuilder.singleString(valueNameEvaluated(targetParameter)); - executionExpression = createExpectExecutableType(sourceNode, sourceType, targetExecutable, valueTree); + builder.startStatement().type(targetParameter.getType()).string(" ").tree(shortCircuitTree).end(); } - } else if (sourceParameter == null) { - executionExpression = createExecuteChildExpression(builder, field, targetParameter, unexpectedParameter); - } - - if (executionExpression != null) { - CodeTreeVariable executionVar = new CodeTreeVariable(); - CodeTree shortCircuitTree = createShortCircuitTree(builder, executionVar, targetVariableName, specialization, targetParameter, unexpectedParameter); - CodeTree unexpectedTree = createCatchUnexpectedTree(builder, executionExpression, targetVariableName, specialization, sourceExecutable, targetExecutable, targetParameter, - shortCircuitTree != executionVar); - - executionVar.set(unexpectedTree); + } else { builder.tree(shortCircuitTree); } + + } + return builder.getRoot(); + } + + private CodeTree createExecutionExpresssions(CodeTreeBuilder parent, NodeChildData child, ExecutableTypeData sourceExecutable, ExecutableTypeData targetExecutable, ActualParameter param, + ActualParameter unexpectedParameter) { + CodeTreeBuilder builder = parent.create(); + + TypeData type = param.getTypeSystemType(); + List targetTypes = child.getNodeData().getTypeSystem().lookupSourceTypes(type); + + if (targetTypes.size() > 1) { + boolean elseIf = false; + int index = 0; + for (TypeData typeData : targetTypes) { + if (index < targetTypes.size() - 1) { + elseIf = builder.startIf(elseIf); + builder.string(typeName(param)).string(" == ").typeLiteral(typeData.getPrimitiveType()); + builder.end(); + builder.startBlock(); + } else { + builder.startElseBlock(); + } + + ExecutableTypeData implictExecutableTypeData = child.getNodeData().findExecutableType(typeData, targetExecutable.getEvaluatedCount()); + ImplicitCastData cast = child.getNodeData().getTypeSystem().lookupCast(typeData, param.getTypeSystemType()); + CodeTree execute = createExecuteExpression(parent, child, sourceExecutable, implictExecutableTypeData, param, unexpectedParameter, cast); + builder.statement(execute); + builder.end(); + index++; + } + } else { + builder.tree(createExecuteExpression(parent, child, sourceExecutable, targetExecutable, param, unexpectedParameter, null)); } return builder.getRoot(); } + private CodeTree createExecuteExpression(CodeTreeBuilder parent, NodeChildData child, ExecutableTypeData sourceExecutable, ExecutableTypeData targetExecutable, + ActualParameter targetParameter, ActualParameter unexpectedParameter, ImplicitCastData cast) { + CodeTreeBuilder builder = parent.create(); + builder.string(valueName(targetParameter)); + builder.string(" = "); + if (cast != null) { + startCallTypeSystemMethod(getContext(), builder, child.getNodeData(), cast.getMethodName()); + } + + NodeData node = getModel().getNode(); + ActualParameter sourceParameter = sourceExecutable.findParameter(targetParameter.getLocalName()); + if (sourceParameter == null) { + builder.tree(createExecuteChildExpression(builder, child, targetParameter, targetExecutable, unexpectedParameter)); + } else { + CodeTree var = CodeTreeBuilder.singleString(valueNameEvaluated(targetParameter)); + builder.tree(createExpectExecutableType(node, sourceParameter.getTypeSystemType(), targetExecutable, var)); + } + if (cast != null) { + builder.end().end(); + } + + return builder.getRoot(); + } + + private boolean containsNewLine(CodeTree tree) { + if (tree.getCodeKind() == CodeTreeKind.NEW_LINE) { + return true; + } + + for (CodeTree codeTree : tree.getEnclosedElements()) { + if (containsNewLine(codeTree)) { + return true; + } + } + return false; + } + + private boolean hasUnexpected(ExecutableTypeData target, ActualParameter sourceParameter, ActualParameter targetParameter) { + List types = getModel().getNode().getTypeSystem().lookupSourceTypes(targetParameter.getTypeSystemType()); + NodeChildData child = getModel().getNode().findChild(targetParameter.getSpecification().getName()); + boolean hasUnexpected = false; + for (TypeData type : types) { + if (hasUnexpected) { + continue; + } + ExecutableTypeData execTarget = target; + if (type != execTarget.getType()) { + execTarget = child.findExecutableType(getContext(), type); + } + hasUnexpected = hasUnexpected || hasUnexpectedType(execTarget, sourceParameter, type); + } + return hasUnexpected; + } + + private boolean hasUnexpectedType(ExecutableTypeData target, ActualParameter sourceParameter, TypeData type) { + if (sourceParameter == null) { + return target.hasUnexpectedValue(getContext()); + } else { + if (sourceParameter.getTypeSystemType().needsCastTo(getContext(), type)) { + return target.hasUnexpectedValue(getContext()); + } + return false; + } + } + private CodeTree createCatchUnexpectedTree(CodeTreeBuilder parent, CodeTree body, String targetVariableName, SpecializationData specialization, ExecutableTypeData currentExecutable, ExecutableTypeData targetExecutable, ActualParameter param, boolean shortCircuit) { CodeTreeBuilder builder = new CodeTreeBuilder(parent); - boolean unexpected = targetExecutable.hasUnexpectedValue(getContext()); - boolean cast = false; - if (targetExecutable.getType().needsCastTo(getContext(), param.getTypeSystemType())) { - unexpected = true; - cast = true; + ActualParameter sourceParameter = currentExecutable.findParameter(param.getLocalName()); + boolean unexpected = hasUnexpected(targetExecutable, sourceParameter, param); + if (!unexpected) { + return body; } - builder.startStatement(); - if (!shortCircuit) { - builder.type(param.getType()).string(" ").string(targetVariableName); + builder.declaration(param.getType(), targetVariableName); } - - if (unexpected) { - if (!shortCircuit) { - builder.end(); - } - builder.startTryBlock(); - builder.startStatement(); - builder.string(targetVariableName); - } else if (shortCircuit) { - builder.startStatement(); - builder.string(targetVariableName); - } - builder.string(" = "); - if (cast) { - builder.tree(createCastType(specialization.getNode(), targetExecutable.getType(), param.getTypeSystemType(), true, body)); - } else { + builder.startTryBlock(); + + if (containsNewLine(body)) { builder.tree(body); + } else { + builder.statement(body); } - builder.end(); - - if (unexpected) { - builder.end().startCatchBlock(getUnexpectedValueException(), "ex"); - SpecializationData generic = specialization.getNode().getGenericSpecialization(); - ActualParameter genericParameter = generic.findParameter(param.getLocalName()); - - List genericParameters = generic.getParametersAfter(genericParameter); - builder.tree(createDeoptimize(builder)); - builder.tree(createExecuteChildren(parent, currentExecutable, generic, genericParameters, genericParameter, false)); - if (specialization.isPolymorphic()) { - builder.tree(createReturnOptimizeTypes(builder, currentExecutable, specialization, param)); - } else { - builder.tree(createReturnExecuteAndSpecialize(builder, currentExecutable, specialization, param, - "Expected " + param.getLocalName() + " instanceof " + Utils.getSimpleName(param.getType()))); - } - builder.end(); // catch block + + builder.end().startCatchBlock(getUnexpectedValueException(), "ex"); + SpecializationData generic = specialization.getNode().getGenericSpecialization(); + ActualParameter genericParameter = generic.findParameter(param.getLocalName()); + + List genericParameters = generic.getParametersAfter(genericParameter); + builder.tree(createDeoptimize(builder)); + builder.tree(createExecuteChildren(parent, currentExecutable, generic, genericParameters, genericParameter)); + if (specialization.isPolymorphic()) { + builder.tree(createReturnOptimizeTypes(builder, currentExecutable, specialization, param)); + } else { + builder.tree(createReturnExecuteAndSpecialize(builder, currentExecutable, specialization, param, + "Expected " + param.getLocalName() + " instanceof " + Utils.getSimpleName(param.getType()))); } + builder.end(); // catch block return builder.getRoot(); } @@ -1969,7 +2107,7 @@ CodeTreeBuilder execute = new CodeTreeBuilder(builder); execute.startCall("next0", executeCachedName(generic)); - addInternalValueParameterNames(execute, specialization, generic, param.getLocalName(), true, true); + addInternalValueParameterNames(execute, specialization, generic, param.getLocalName(), true, true, null); execute.end(); TypeData sourceType = generic.getReturnType().getTypeSystemType(); @@ -1980,37 +2118,26 @@ return builder.getRoot(); } - private CodeTree createExecuteChildExpression(CodeTreeBuilder parent, NodeChildData targetField, ActualParameter sourceParameter, ActualParameter unexpectedParameter) { - TypeData type = sourceParameter.getTypeSystemType(); - ExecutableTypeData execType = targetField.findExecutableType(getContext(), type); - + private CodeTree createExecuteChildExpression(CodeTreeBuilder parent, NodeChildData targetChild, ActualParameter targetParameter, ExecutableTypeData targetExecutable, + ActualParameter unexpectedParameter) { CodeTreeBuilder builder = new CodeTreeBuilder(parent); - if (targetField != null) { - Element accessElement = targetField.getAccessElement(); - if (accessElement == null || accessElement.getKind() == ElementKind.METHOD) { - builder.string("this.").string(targetField.getName()); - } else if (accessElement.getKind() == ElementKind.FIELD) { - builder.string("this.").string(accessElement.getSimpleName().toString()); - } else { - throw new AssertionError(); - } - if (sourceParameter.getSpecification().isIndexed()) { - builder.string("[" + sourceParameter.getIndex() + "]"); - } + if (targetChild != null) { + builder.tree(createAccessChild(builder, targetChild, targetParameter)); builder.string("."); } - builder.startCall(execType.getMethodName()); - + builder.startCall(targetExecutable.getMethodName()); + + // TODO this should be merged with #createTemplateMethodCall int index = 0; - for (ActualParameter parameter : execType.getParameters()) { + for (ActualParameter parameter : targetExecutable.getParameters()) { if (!parameter.getSpecification().isSignature()) { builder.string(parameter.getLocalName()); } else { - if (index < targetField.getExecuteWith().size()) { - NodeChildData child = targetField.getExecuteWith().get(index); + if (index < targetChild.getExecuteWith().size()) { + NodeChildData child = targetChild.getExecuteWith().get(index); ParameterSpec spec = getModel().getSpecification().findParameterSpec(child.getName()); List specializationParams = getModel().findParameters(spec); @@ -2049,32 +2176,50 @@ return builder.getRoot(); } + private CodeTree createAccessChild(CodeTreeBuilder parent, NodeChildData targetChild, ActualParameter targetParameter) throws AssertionError { + CodeTreeBuilder builder = parent.create(); + Element accessElement = targetChild.getAccessElement(); + if (accessElement == null || accessElement.getKind() == ElementKind.METHOD) { + builder.string("this.").string(targetChild.getName()); + } else if (accessElement.getKind() == ElementKind.FIELD) { + builder.string("this.").string(accessElement.getSimpleName().toString()); + } else { + throw new AssertionError(); + } + if (targetParameter.getSpecification().isIndexed()) { + builder.string("[" + targetParameter.getIndex() + "]"); + } + return builder.getRoot(); + } + private CodeTree createShortCircuitTree(CodeTreeBuilder parent, CodeTree body, String targetVariableName, SpecializationData specialization, ActualParameter parameter, ActualParameter exceptionParam) { - CodeTreeBuilder builder = new CodeTreeBuilder(parent); - NodeChildData forField = specialization.getNode().findChild(parameter.getSpecification().getName()); - if (forField == null) { + if (!isShortCircuit(forField)) { return body; } - if (forField.getExecutionKind() != ExecutionKind.SHORT_CIRCUIT) { - return body; - } - + CodeTreeBuilder builder = new CodeTreeBuilder(parent); ActualParameter shortCircuitParam = specialization.getPreviousParam(parameter); - builder.tree(createShortCircuitValue(builder, specialization, forField, shortCircuitParam, exceptionParam)); - builder.declaration(parameter.getType(), targetVariableName, CodeTreeBuilder.createBuilder().defaultValue(parameter.getType())); builder.startIf().string(shortCircuitParam.getLocalName()).end(); builder.startBlock(); - builder.tree(body); + + if (containsNewLine(body)) { + builder.tree(body); + } else { + builder.statement(body); + } builder.end(); return builder.getRoot(); } + private boolean isShortCircuit(NodeChildData forField) { + return forField != null && forField.getExecutionKind() == ExecutionKind.SHORT_CIRCUIT; + } + private CodeTree createShortCircuitValue(CodeTreeBuilder parent, SpecializationData specialization, NodeChildData forField, ActualParameter shortCircuitParam, ActualParameter exceptionParam) { CodeTreeBuilder builder = new CodeTreeBuilder(parent); int shortCircuitIndex = 0; @@ -2109,7 +2254,7 @@ CodeTreeBuilder specializeCall = new CodeTreeBuilder(parent); specializeCall.startCall(EXECUTE_SPECIALIZE_NAME); specializeCall.string(String.valueOf(node.getSpecializations().indexOf(current))); - addInternalValueParameterNames(specializeCall, generic, node.getGenericSpecialization(), exceptionParam != null ? exceptionParam.getLocalName() : null, true, true); + addInternalValueParameterNames(specializeCall, generic, node.getGenericSpecialization(), exceptionParam != null ? exceptionParam.getLocalName() : null, true, true, null); specializeCall.doubleQuote(reason); specializeCall.end().end(); @@ -2199,6 +2344,233 @@ } } + private class BaseCastNodeFactory extends ClassElementFactory { + + protected final Set usedTargetTypes; + + public BaseCastNodeFactory(ProcessorContext context, Set usedTargetTypes) { + super(context); + this.usedTargetTypes = usedTargetTypes; + } + + @Override + protected CodeTypeElement create(NodeData m) { + CodeTypeElement type = createClass(m, modifiers(STATIC), nodeCastClassName(m, null), context.getTruffleTypes().getNode(), false); + + CodeVariableElement delegate = new CodeVariableElement(m.getNodeType(), "delegate"); + delegate.getModifiers().add(PROTECTED); + delegate.getAnnotationMirrors().add(new CodeAnnotationMirror(getContext().getTruffleTypes().getChildAnnotation())); + + type.add(delegate); + type.add(createConstructorUsingFields(modifiers(), type)); + return type; + } + + @Override + protected void createChildren(NodeData m) { + CodeTypeElement type = getElement(); + type.add(createExecute(EXECUTE_SPECIALIZE_NAME, true)); + type.add(createExecute(EXECUTE_GENERIC_NAME, false)); + + for (ExecutableTypeData targetExecutable : m.getExecutableTypes()) { + if (!usedTargetTypes.contains(targetExecutable.getType()) && targetExecutable.hasUnexpectedValue(getContext())) { + continue; + } + CodeExecutableElement execute = createCastExecute(targetExecutable, targetExecutable, false); + CodeExecutableElement expect = createCastExecute(targetExecutable, targetExecutable, true); + if (execute != null) { + getElement().add(execute); + } + if (expect != null) { + getElement().add(expect); + } + } + Set sourceTypes = new TreeSet<>(); + List casts = getModel().getTypeSystem().getImplicitCasts(); + for (ImplicitCastData cast : casts) { + sourceTypes.add(cast.getSourceType()); + } + + CodeTypeElement baseType = getElement(); + for (TypeData sourceType : sourceTypes) { + add(new SpecializedCastNodeFactory(context, baseType, sourceType, usedTargetTypes), getModel()); + } + } + + private CodeExecutableElement createExecute(String name, boolean specialize) { + NodeData node = getModel(); + TypeMirror objectType = node.getTypeSystem().getGenericType(); + CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), objectType, name, new CodeVariableElement(objectType, "value")); + if (specialize) { + method.getModifiers().add(FINAL); + } + CodeTreeBuilder builder = method.createBuilder(); + + List casts = node.getTypeSystem().getImplicitCasts(); + boolean elseIf = false; + for (ImplicitCastData cast : casts) { + elseIf = builder.startIf(elseIf); + startCallTypeSystemMethod(context, builder, getModel(), TypeSystemCodeGenerator.isTypeMethodName(cast.getSourceType())); + builder.string("value"); + builder.end().end(); + builder.end(); + builder.startBlock(); + + if (specialize) { + builder.startStatement().startCall("replace").startNew(nodeCastClassName(getModel(), cast.getSourceType())).string("delegate").end().doubleQuote("Added cast").end().end(); + } + builder.startReturn(); + + startCallTypeSystemMethod(context, builder, getModel(), cast.getMethodName()); + startCallTypeSystemMethod(context, builder, getModel(), TypeSystemCodeGenerator.asTypeMethodName(cast.getSourceType())); + builder.string("value"); + builder.end().end(); + builder.end().end(); + + builder.end(); + builder.end(); + } + + builder.startReturn().string("value").end(); + + return method; + } + + protected CodeExecutableElement createCastExecute(ExecutableTypeData sourceExecutable, ExecutableTypeData targetExecutable, boolean expect) { + ImplicitCastData cast = null; + if (!sourceExecutable.getType().equals(targetExecutable.getType())) { + cast = getModel().getTypeSystem().lookupCast(sourceExecutable.getType(), targetExecutable.getType()); + if (cast == null) { + return null; + } + } + + if (expect) { + if (targetExecutable.getEvaluatedCount() > 0) { + return null; + } else if (Utils.isObject(targetExecutable.getType().getPrimitiveType())) { + return null; + } + } + + boolean hasTargetUnexpected = targetExecutable.hasUnexpectedValue(getContext()); + boolean hasSourceUnexpected = sourceExecutable.hasUnexpectedValue(getContext()); + + CodeExecutableElement method = copyTemplateMethod(targetExecutable); + method.getModifiers().add(PUBLIC); + + CodeTreeBuilder builder = method.createBuilder(); + + if (hasSourceUnexpected && cast != null) { + builder.startTryBlock(); + } + + if (expect) { + method.getParameters().clear(); + String expectMethodName; + if (hasTargetUnexpected) { + expectMethodName = TypeSystemCodeGenerator.expectTypeMethodName(targetExecutable.getType()); + } else { + expectMethodName = TypeSystemCodeGenerator.asTypeMethodName(targetExecutable.getType()); + } + method.setSimpleName(CodeNames.of(expectMethodName)); + method.addParameter(new CodeVariableElement(getModel().getTypeSystem().getGenericType(), "value")); + } + + builder.startReturn(); + CodeTree executeCall; + if (expect) { + executeCall = createCastType(getModel(), getModel().getTypeSystem().getGenericTypeData(), sourceExecutable.getType(), hasSourceUnexpected, CodeTreeBuilder.singleString("value")); + } else { + executeCall = createTemplateMethodCall(builder, CodeTreeBuilder.singleString("delegate."), targetExecutable, sourceExecutable, null); + } + if (cast != null) { + startCallTypeSystemMethod(context, builder, getModel(), cast.getMethodName()); + builder.tree(executeCall); + builder.end().end(); + } else { + builder.tree(executeCall); + } + builder.end(); + + if (hasSourceUnexpected && cast != null) { + builder.end(); + builder.startCatchBlock(getContext().getTruffleTypes().getUnexpectedValueException(), "ex"); + builder.startStatement().startCall("replace").startNew(nodeCastClassName(getModel(), null)).string("delegate").end().doubleQuote("Removed cast").end().end(); + + if (hasTargetUnexpected) { + builder.startThrow().string("ex").end(); + } else { + builder.startThrow().startNew(getContext().getType(AssertionError.class)).end().end(); + } + builder.end(); + } + + return method; + } + + private CodeExecutableElement copyTemplateMethod(TemplateMethod targetExecutable) { + CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), targetExecutable.getMethod()); + method.getModifiers().remove(ABSTRACT); + method.getAnnotationMirrors().clear(); + Modifier visibility = Utils.getVisibility(method.getModifiers()); + if (visibility != null) { + method.getModifiers().remove(visibility); + } + int index = 0; + for (ActualParameter parameter : targetExecutable.getParameters()) { + ((CodeVariableElement) method.getParameters().get(index)).setName(parameter.getLocalName()); + index++; + } + return method; + } + + } + + private class SpecializedCastNodeFactory extends BaseCastNodeFactory { + + private final CodeTypeElement baseType; + private final TypeData sourceType; + + public SpecializedCastNodeFactory(ProcessorContext context, CodeTypeElement baseType, TypeData type, Set usedTargetTypes) { + super(context, usedTargetTypes); + this.baseType = baseType; + this.sourceType = type; + } + + @Override + protected CodeTypeElement create(NodeData m) { + CodeTypeElement type = createClass(m, modifiers(PRIVATE, STATIC, FINAL), nodeCastClassName(m, sourceType), baseType.asType(), false); + type.add(createConstructorUsingFields(modifiers(), type)); + return type; + } + + @Override + protected void createChildren(NodeData node) { + for (TypeData targetType : usedTargetTypes) { + for (ExecutableTypeData targetExecutable : node.getExecutableTypes()) { + if (targetExecutable.getType().equals(targetType)) { + ExecutableTypeData sourceExecutable = node.findExecutableType(sourceType, targetExecutable.getEvaluatedCount()); + if (sourceExecutable == null) { + // TODO what if there is no evaluated version? + continue; + } + CodeExecutableElement execute = createCastExecute(sourceExecutable, targetExecutable, false); + CodeExecutableElement expect = createCastExecute(sourceExecutable, targetExecutable, true); + if (execute != null) { + getElement().add(execute); + } + if (expect != null) { + getElement().add(expect); + } + } + } + + } + } + + } + private class SpecializedNodeFactory extends NodeBaseFactory { protected final CodeTypeElement nodeGen; @@ -2277,15 +2649,31 @@ } CodeExecutableElement superConstructor = createSuperConstructor(clazz, constructor); + CodeTree body = superConstructor.getBodyTree(); + CodeTreeBuilder builder = superConstructor.createBuilder(); + builder.tree(body); if (superConstructor != null) { if (getModel().isGeneric() && node.isPolymorphic()) { - CodeTree body = superConstructor.getBodyTree(); - CodeTreeBuilder builder = superConstructor.createBuilder(); - builder.tree(body); builder.statement("this.next0 = null"); } + for (ActualParameter param : getModel().getParameters()) { + if (!param.getSpecification().isSignature()) { + continue; + } + NodeChildData child = getModel().getNode().findChild(param.getSpecification().getName()); + List types = child.getNodeData().getTypeSystem().lookupSourceTypes(param.getTypeSystemType()); + if (types.size() > 1) { + clazz.add(new CodeVariableElement(modifiers(PRIVATE, FINAL), getContext().getType(Class.class), typeName(param))); + superConstructor.getParameters().add(new CodeVariableElement(getContext().getType(Class.class), typeName(param))); + + builder.startStatement(); + builder.string("this.").string(typeName(param)).string(" = ").string(typeName(param)); + builder.end(); + } + } + clazz.add(superConstructor); } } @@ -2333,7 +2721,7 @@ } else { CodeTreeBuilder elseBuilder = new CodeTreeBuilder(builder); elseBuilder.startReturn().startCall("this.next0", executeCachedName(polymorphic)); - addInternalValueParameterNames(elseBuilder, polymorphic, polymorphic, null, true, true); + addInternalValueParameterNames(elseBuilder, polymorphic, polymorphic, null, true, true, null); elseBuilder.end().end(); boolean forceElse = specialization.getExceptions().size() > 0; @@ -2375,7 +2763,7 @@ CodeTreeBuilder specializeCall = new CodeTreeBuilder(builder); specializeCall.startCall(EXECUTE_SPECIALIZE_NAME); specializeCall.string("0"); - addInternalValueParameterNames(specializeCall, specialization, node.getGenericSpecialization(), null, true, true); + addInternalValueParameterNames(specializeCall, specialization, node.getGenericSpecialization(), null, true, true, null); specializeCall.startGroup().doubleQuote("Uninitialized polymorphic (").string(" + depth + ").doubleQuote("/" + node.getPolymorphicDepth() + ")").end(); specializeCall.end().end(); @@ -2482,7 +2870,7 @@ builder.tree(createDeoptimize(builder)); } - builder.tree(createExecuteChildren(builder, executable, specialization, specialization.getParameters(), null, false)); + builder.tree(createExecuteChildren(builder, executable, specialization, specialization.getParameters(), null)); CodeTree returnSpecialized = null; @@ -2519,26 +2907,25 @@ CodeTreeBuilder returnBuilder = new CodeTreeBuilder(parent); if (specialization.isPolymorphic()) { returnBuilder.startCall("next0", executeCachedName(specialization)); - addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true); + addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true, null); returnBuilder.end(); } else if (specialization.isUninitialized()) { returnBuilder.startCall("super", EXECUTE_SPECIALIZE_NAME); returnBuilder.string("0"); - addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true); + addInternalValueParameterNames(returnBuilder, specialization, specialization, null, true, true, null); returnBuilder.doubleQuote("Uninitialized monomorphic"); returnBuilder.end(); } else if (specialization.getMethod() == null && !node.needsRewrites(context)) { emitEncounteredSynthetic(builder, specialization); } else if (specialization.isGeneric()) { returnBuilder.startCall("super", EXECUTE_GENERIC_NAME); - addInternalValueParameterNames(returnBuilder, specialization, specialization, null, node.needsFrame(), true); + addInternalValueParameterNames(returnBuilder, specialization, specialization, null, node.needsFrame(), true, null); returnBuilder.end(); } else { returnBuilder.tree(createTemplateMethodCall(returnBuilder, null, specialization, specialization, null)); } if (!returnBuilder.isEmpty()) { - ExecutableTypeData sourceExecutableType = node.findExecutableType(specialization.getReturnType().getTypeSystemType(), 0); boolean sourceThrowsUnexpected = sourceExecutableType != null && sourceExecutableType.hasUnexpectedValue(getContext()); boolean targetSupportsUnexpected = executable.hasUnexpectedValue(getContext()); diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeData.java Fri Sep 06 16:16:40 2013 +0200 @@ -32,7 +32,7 @@ import com.oracle.truffle.dsl.processor.template.*; import com.oracle.truffle.dsl.processor.typesystem.*; -public class NodeData extends Template { +public class NodeData extends Template implements Comparable { private final String nodeId; private NodeData declaringNode; @@ -90,6 +90,15 @@ return false; } + public boolean needsImplicitCast(ProcessorContext context) { + for (NodeChildData child : getChildren()) { + if (child.needsImplicitCast(context)) { + return true; + } + } + return false; + } + public int getPolymorphicDepth() { return polymorphicDepth; } @@ -574,4 +583,7 @@ return null; } + public int compareTo(NodeData o) { + return getNodeId().compareTo(o.getNodeId()); + } } diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java Fri Sep 06 16:16:40 2013 +0200 @@ -332,7 +332,7 @@ nodeData.setNodeContainer(nodeContainer != null); nodeData.setTypeSystem(typeSystem); nodeData.setFields(parseFields(typeHierarchy, elements)); - nodeData.setChildren(parseChildren(elements, typeHierarchy)); + nodeData.setChildren(parseChildren(nodeData, elements, typeHierarchy)); nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements))); // resolveChildren invokes cyclic parsing. @@ -407,7 +407,7 @@ } } - private List parseChildren(List elements, final List typeHierarchy) { + private List parseChildren(NodeData parent, List elements, final List typeHierarchy) { Set shortCircuits = new HashSet<>(); for (ExecutableElement method : ElementFilter.methodsIn(elements)) { AnnotationMirror mirror = Utils.findAnnotationMirror(processingEnv, method, ShortCircuit.class); @@ -472,7 +472,7 @@ kind = ExecutionKind.SHORT_CIRCUIT; } - NodeChildData nodeChild = new NodeChildData(type, childMirror, name, childType, originalChildType, getter, cardinality, kind); + NodeChildData nodeChild = new NodeChildData(parent, type, childMirror, name, childType, originalChildType, getter, cardinality, kind); parsedChildren.add(nodeChild); diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastData.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastData.java Fri Sep 06 16:16:40 2013 +0200 @@ -26,8 +26,32 @@ public class ImplicitCastData extends TemplateMethod { - public ImplicitCastData(TemplateMethod method) { + private final TypeData sourceType; + private final TypeData targetType; + + public ImplicitCastData(TemplateMethod method, TypeData sourceType, TypeData targetType) { super(method); + this.sourceType = sourceType; + this.targetType = targetType; + } + + public TypeData getSourceType() { + return sourceType; + } + + public TypeData getTargetType() { + return targetType; + } + + @Override + public int compareTo(TemplateMethod o) { + if (o instanceof ImplicitCastData) { + // implicit casts are ordered by source type since + // its also the order in which they are checked. + TypeData otherSourceType = ((ImplicitCastData) o).getSourceType(); + return this.sourceType.compareTo(otherSourceType); + } + return super.compareTo(o); } } diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastParser.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/ImplicitCastParser.java Fri Sep 06 16:16:40 2013 +0200 @@ -56,7 +56,20 @@ @Override public ImplicitCastData create(TemplateMethod method, boolean invalid) { - return new ImplicitCastData(method); + if (invalid) { + return new ImplicitCastData(method, null, null); + } + + ActualParameter target = method.findParameter("targetValue"); + ActualParameter source = method.findParameter("sourceValue"); + + TypeData targetType = target.getTypeSystemType(); + TypeData sourceType = source.getTypeSystemType(); + + if (targetType.equals(sourceType)) { + method.addError("Target type and source type of an @%s must not be the same type.", ImplicitCast.class.getSimpleName()); + } + + return new ImplicitCastData(method, sourceType, targetType); } - } diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemCodeGenerator.java Fri Sep 06 16:16:40 2013 +0200 @@ -44,10 +44,22 @@ return "is" + Utils.getTypeId(type.getBoxedType()); } + public static String isImplicitTypeMethodName(TypeData type) { + return "isImplicit" + Utils.getTypeId(type.getBoxedType()); + } + public static String asTypeMethodName(TypeData type) { return "as" + Utils.getTypeId(type.getBoxedType()); } + public static String asImplicitTypeMethodName(TypeData type) { + return "asImplicit" + Utils.getTypeId(type.getBoxedType()); + } + + public static String getImplicitClass(TypeData type) { + return "getImplicit" + Utils.getTypeId(type.getBoxedType()) + "Class"; + } + public static String expectTypeMethodName(TypeData type) { return "expect" + Utils.getTypeId(type.getBoxedType()); } @@ -100,6 +112,20 @@ clazz.add(expect); } } + + CodeExecutableElement asImplicit = createAsImplicitTypeMethod(type); + if (asImplicit != null) { + clazz.add(asImplicit); + } + CodeExecutableElement isImplicit = createIsImplicitTypeMethod(type); + if (isImplicit != null) { + clazz.add(isImplicit); + } + + CodeExecutableElement typeIndex = createGetTypeIndex(type); + if (typeIndex != null) { + clazz.add(typeIndex); + } } } @@ -133,6 +159,95 @@ return field; } + private CodeExecutableElement createIsImplicitTypeMethod(TypeData type) { + TypeSystemData typeSystem = getModel(); + List casts = typeSystem.lookupByTargetType(type); + if (casts.isEmpty()) { + return null; + } + CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getContext().getType(boolean.class), TypeSystemCodeGenerator.isImplicitTypeMethodName(type)); + method.addParameter(new CodeVariableElement(getContext().getType(Object.class), LOCAL_VALUE)); + CodeTreeBuilder builder = method.createBuilder(); + + List sourceTypes = typeSystem.lookupSourceTypes(type); + + builder.startReturn(); + String sep = ""; + for (TypeData sourceType : sourceTypes) { + builder.string(sep); + builder.startCall(isTypeMethodName(sourceType)).string(LOCAL_VALUE).end(); + sep = " || "; + } + builder.end(); + return method; + } + + private CodeExecutableElement createAsImplicitTypeMethod(TypeData type) { + TypeSystemData typeSystem = getModel(); + List casts = typeSystem.lookupByTargetType(type); + if (casts.isEmpty()) { + return null; + } + CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), type.getPrimitiveType(), TypeSystemCodeGenerator.asImplicitTypeMethodName(type)); + method.addParameter(new CodeVariableElement(getContext().getType(Object.class), LOCAL_VALUE)); + + List sourceTypes = typeSystem.lookupSourceTypes(type); + + CodeTreeBuilder builder = method.createBuilder(); + boolean elseIf = false; + for (TypeData sourceType : sourceTypes) { + elseIf = builder.startIf(elseIf); + builder.startCall(isTypeMethodName(sourceType)).string(LOCAL_VALUE).end(); + builder.end().startBlock(); + + builder.startReturn(); + ImplicitCastData cast = typeSystem.lookupCast(sourceType, type); + if (cast != null) { + builder.startCall(cast.getMethodName()); + } + builder.startCall(asTypeMethodName(sourceType)).string(LOCAL_VALUE).end(); + if (cast != null) { + builder.end(); + } + builder.end(); + builder.end(); + } + + builder.startElseBlock(); + builder.startStatement().startStaticCall(getContext().getTruffleTypes().getCompilerDirectives(), "transferToInterpreter").end().end(); + builder.startThrow().startNew(getContext().getType(IllegalArgumentException.class)).doubleQuote("Illegal type ").end().end(); + builder.end(); + return method; + } + + private CodeExecutableElement createGetTypeIndex(TypeData type) { + TypeSystemData typeSystem = getModel(); + List casts = typeSystem.lookupByTargetType(type); + if (casts.isEmpty()) { + return null; + } + CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getContext().getType(Class.class), TypeSystemCodeGenerator.getImplicitClass(type)); + method.addParameter(new CodeVariableElement(getContext().getType(Object.class), LOCAL_VALUE)); + + List sourceTypes = typeSystem.lookupSourceTypes(type); + CodeTreeBuilder builder = method.createBuilder(); + boolean elseIf = false; + for (TypeData sourceType : sourceTypes) { + elseIf = builder.startIf(elseIf); + builder.startCall(isTypeMethodName(sourceType)).string(LOCAL_VALUE).end(); + builder.end().startBlock(); + builder.startReturn().typeLiteral(sourceType.getPrimitiveType()).end(); + builder.end(); + } + + builder.startElseBlock(); + builder.startStatement().startStaticCall(getContext().getTruffleTypes().getCompilerDirectives(), "transferToInterpreter").end().end(); + builder.startThrow().startNew(getContext().getType(IllegalArgumentException.class)).doubleQuote("Illegal type ").end().end(); + builder.end(); + + return method; + } + private CodeExecutableElement createIsTypeMethod(TypeData type) { if (!type.getTypeChecks().isEmpty()) { return null; @@ -174,8 +289,8 @@ method.addThrownType(getContext().getTruffleTypes().getUnexpectedValueException()); CodeTreeBuilder body = method.createBuilder(); - body.startIf().startCall(null, TypeSystemCodeGenerator.isTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end().startBlock(); - body.startReturn().startCall(null, TypeSystemCodeGenerator.asTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end(); + body.startIf().startCall(TypeSystemCodeGenerator.isTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end().startBlock(); + body.startReturn().startCall(TypeSystemCodeGenerator.asTypeMethodName(expectedType)).string(LOCAL_VALUE).end().end(); body.end(); // if-block body.startThrow().startNew(getContext().getTruffleTypes().getUnexpectedValueException()).string(LOCAL_VALUE).end().end(); diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemData.java Fri Sep 06 16:16:40 2013 +0200 @@ -36,6 +36,7 @@ private List primitiveTypeMirrors = new ArrayList<>(); private List boxedTypeMirrors = new ArrayList<>(); + private List implicitCasts; private List casts; private List checks; @@ -61,6 +62,14 @@ } } + public void setImplicitCasts(List implicitCasts) { + this.implicitCasts = implicitCasts; + } + + public List getImplicitCasts() { + return implicitCasts; + } + public void setCasts(List casts) { this.casts = casts; } @@ -89,6 +98,9 @@ if (casts != null) { sinks.addAll(casts); } + if (implicitCasts != null) { + sinks.addAll(implicitCasts); + } return sinks; } @@ -161,4 +173,55 @@ return getClass().getSimpleName() + "[template = " + Utils.getSimpleName(getTemplateType()) + ", types = " + types + "]"; } + public Set lookupCastSourceTypes() { + if (getImplicitCasts() == null) { + return null; + } + + Set sourceTypes = new TreeSet<>(); + for (ImplicitCastData cast : getImplicitCasts()) { + sourceTypes.add(cast.getSourceType()); + } + return sourceTypes; + } + + public List lookupByTargetType(TypeData targetType) { + if (getImplicitCasts() == null) { + return Collections.emptyList(); + } + List foundCasts = new ArrayList<>(); + for (ImplicitCastData cast : getImplicitCasts()) { + if (cast.getTargetType().equals(targetType)) { + foundCasts.add(cast); + } + } + return foundCasts; + } + + public ImplicitCastData lookupCast(TypeData sourceType, TypeData targetType) { + if (getImplicitCasts() == null) { + return null; + } + for (ImplicitCastData cast : getImplicitCasts()) { + if (cast.getSourceType().equals(sourceType) && cast.getTargetType().equals(targetType)) { + return cast; + } + } + return null; + } + + public List lookupSourceTypes(TypeData type) { + List sourceTypes = new ArrayList<>(); + sourceTypes.add(type); + if (getImplicitCasts() != null) { + for (ImplicitCastData cast : getImplicitCasts()) { + if (cast.getTargetType() == type) { + sourceTypes.add(cast.getSourceType()); + } + } + } + Collections.sort(sourceTypes); + return sourceTypes; + } + } diff -r e5b5a5cb0ac7 -r 2fb276f5e3e9 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java Fri Sep 06 16:11:15 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/typesystem/TypeSystemParser.java Fri Sep 06 16:16:40 2013 +0200 @@ -71,7 +71,7 @@ } typeSystem.setTypes(parseTypes(typeSystem)); - if (typeSystem.getTypes() == null) { + if (typeSystem.hasErrors()) { return typeSystem; } @@ -91,6 +91,8 @@ if (casts == null || checks == null || implicitCasts == null) { return typeSystem; } + + typeSystem.setImplicitCasts(implicitCasts); typeSystem.setCasts(casts); typeSystem.setChecks(checks); @@ -106,6 +108,7 @@ cast.getTargetType().addTypeCast(cast); } + verifyImplicitCasts(typeSystem); verifyGenericTypeChecksAndCasts(typeSystem); verifyMethodSignatures(typeSystem); verifyNamesUnique(typeSystem); @@ -113,6 +116,24 @@ return typeSystem; } + private static void verifyImplicitCasts(TypeSystemData typeSystem) { + Set types = new HashSet<>(); + Set duplicateSourceTypes = new HashSet<>(); + for (ImplicitCastData cast : typeSystem.getImplicitCasts()) { + if (types.contains(cast.getSourceType())) { + duplicateSourceTypes.add(cast.getSourceType()); + } + types.add(cast.getSourceType()); + } + for (TypeData duplicateType : duplicateSourceTypes) { + for (ImplicitCastData cast : typeSystem.getImplicitCasts()) { + if (cast.getSourceType().equals(duplicateType)) { + cast.addError("Duplicate cast source type %s.", Utils.getSimpleName(duplicateType.getPrimitiveType()), ImplicitCast.class.getSimpleName()); + } + } + } + } + private static void verifyGenericTypeChecksAndCasts(TypeSystemData typeSystem) { for (TypeData type : typeSystem.getTypes()) { if (!type.getTypeChecks().isEmpty()) {