# HG changeset patch # User Christian Humer # Date 1375198970 -7200 # Node ID 7a8835ec5e7d6ef7144cfe0af6024aecd19405a2 # Parent 4a9936bb03a4121a72912b8fd3735a23f6899f69 Truffle-DSL: Added new @NodeField and @NodeFields annotation. Which can be used to avoid the cumbersome definition of copy constructors. diff -r 4a9936bb03a4 -r 7a8835ec5e7d graal/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.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/NodeFieldTest.java Tue Jul 30 17:42:50 2013 +0200 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2012, 2012, 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 static com.oracle.truffle.api.dsl.test.TestHelper.*; +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.*; +import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.TestContainerFactory.TestContainerContainerFieldFactory; +import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; + +public class NodeFieldTest { + + @Test + public void testIntField() { + assertEquals(42, createCallTarget(IntFieldTestNodeFactory.create(42)).call()); + } + + @NodeField(name = "field", type = int.class) + abstract static class IntFieldTestNode extends ValueNode { + + public abstract int getField(); + + @Specialization + int intField() { + return getField(); + } + + } + + @Test + public void testIntFieldNoGetter() { + assertEquals(42, createCallTarget(IntFieldNoGetterTestNodeFactory.create(42)).call()); + } + + @NodeField(name = "field", type = int.class) + abstract static class IntFieldNoGetterTestNode extends ValueNode { + + @Specialization + int intField(int field) { + return field; + } + + } + + @Test + public void testMultipleFields() { + assertEquals(42, createCallTarget(MultipleFieldsTestNodeFactory.create(21, 21)).call()); + } + + @NodeFields({@NodeField(name = "field0", type = int.class), @NodeField(name = "field1", type = int.class)}) + abstract static class MultipleFieldsTestNode extends ValueNode { + + public abstract int getField0(); + + public abstract int getField1(); + + @Specialization + int intField() { + return getField0() + getField1(); + } + + } + + @Test + public void testStringField() { + assertEquals("42", createCallTarget(StringFieldTestNodeFactory.create("42")).call()); + } + + @NodeField(name = "field", type = String.class) + abstract static class StringFieldTestNode extends ValueNode { + + public abstract String getField(); + + @Specialization + String stringField() { + return getField(); + } + + } + + @Test + public void testRewrite() { + assertEquals("42", createCallTarget(RewriteTestNodeFactory.create("42")).call()); + } + + @NodeField(name = "field", type = String.class) + abstract static class RewriteTestNode extends ValueNode { + + public abstract String getField(); + + @Specialization(order = 1, rewriteOn = RuntimeException.class) + String alwaysRewrite() { + throw new RuntimeException(); + } + + @Specialization(order = 2) + String returnField() { + return getField(); + } + } + + @Test + public void testStringContainer() { + assertEquals(42, createCallTarget(TestContainerContainerFieldFactory.create(42, "42")).call()); + } + + @NodeField(name = "field", type = int.class) + abstract static class IntContainerNode extends ValueNode { + + public abstract int getField(); + + } + + @NodeContainer(IntContainerNode.class) + @NodeField(name = "anotherField", type = String.class) + abstract static class TestContainer { + + @Specialization + static int containerField(int field, String anotherField) { + return anotherField.equals("42") ? field : -1; + } + + } + +} diff -r 4a9936bb03a4 -r 7a8835ec5e7d graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeField.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeField.java Tue Jul 30 17:42:50 2013 +0200 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012, 2012, 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; + +import java.lang.annotation.*; + +import com.oracle.truffle.api.nodes.*; + +/** + * A {@link NodeField} element defines a field for the generated {@link Node}. A {@link Node} + * contains multiple {@link NodeFields} specified in linear declaration order. The field can be + * accessed by declaring an abstract getter named + * "get" + firstLetterUpperCase({@link #name()})(). + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE}) +public @interface NodeField { + + String name(); + + Class type(); + +} diff -r 4a9936bb03a4 -r 7a8835ec5e7d graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeFields.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/NodeFields.java Tue Jul 30 17:42:50 2013 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012, 2012, 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; + +import java.lang.annotation.*; + +import com.oracle.truffle.api.nodes.*; + +/** + * A {@link NodeFields} element defines a field for the generated {@link Node}. A {@link Node} + * contains multiple {@link NodeFields} specified in linear declaration order. The field can be + * accessed by declaring an abstract getter named + * "get" + firstLetterUpperCase({@link #value()})(). + */ +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.TYPE}) +public @interface NodeFields { + + NodeField[] value() default {}; + +} diff -r 4a9936bb03a4 -r 7a8835ec5e7d graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java Tue Jul 30 16:12:26 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/CreateCastParser.java Tue Jul 30 17:42:50 2013 +0200 @@ -55,7 +55,7 @@ } } MethodSpec spec = new MethodSpec(new InheritsParameterSpec(getContext(), "child", baseType)); - addDefaultFieldMethodSpec(method, spec); + addDefaultFieldMethodSpec(spec); spec.addRequired(new ParameterSpec("castedChild", baseType)).setSignature(true); return spec; } diff -r 4a9936bb03a4 -r 7a8835ec5e7d 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 Tue Jul 30 16:12:26 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeCodeGenerator.java Tue Jul 30 17:42:50 2013 +0200 @@ -1073,6 +1073,20 @@ } } + for (NodeFieldData field : node.getFields()) { + if (!field.isGenerated()) { + continue; + } + + clazz.add(new CodeVariableElement(modifiers(PROTECTED, FINAL), field.getType(), field.getName())); + if (field.getGetter() != null && field.getGetter().getModifiers().contains(Modifier.ABSTRACT)) { + CodeExecutableElement method = CodeExecutableElement.clone(getContext().getEnvironment(), field.getGetter()); + method.getModifiers().remove(Modifier.ABSTRACT); + method.createBuilder().startReturn().string("this.").string(field.getName()).end(); + clazz.add(method); + } + } + for (String assumption : node.getAssumptions()) { clazz.add(createAssumptionField(assumption)); } diff -r 4a9936bb03a4 -r 7a8835ec5e7d graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeFieldData.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeFieldData.java Tue Jul 30 16:12:26 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeFieldData.java Tue Jul 30 17:42:50 2013 +0200 @@ -29,27 +29,49 @@ public class NodeFieldData extends MessageContainer { - private VariableElement variable; + private final Element messageElement; + private final AnnotationMirror messageAnnotation; + private final String name; + private final TypeMirror type; + private final boolean generated; + private ExecutableElement getter; - public NodeFieldData(VariableElement var) { - this.variable = var; + public NodeFieldData(Element messageElement, AnnotationMirror messageAnnotation, TypeMirror type, String name, boolean generated) { + this.messageElement = messageElement; + this.messageAnnotation = messageAnnotation; + this.name = name; + this.type = type; + this.generated = generated; + } + + void setGetter(ExecutableElement getter) { + this.getter = getter; } @Override public Element getMessageElement() { - return variable; + return messageElement; + } + + @Override + public AnnotationMirror getMessageAnnotation() { + return messageAnnotation; } public String getName() { - return variable.getSimpleName().toString(); + return name; } public TypeMirror getType() { - return variable.asType(); + return type; } - public VariableElement getVariable() { - return variable; + public boolean isGenerated() { + return generated; + } + + public ExecutableElement getGetter() { + return getter; } } diff -r 4a9936bb03a4 -r 7a8835ec5e7d graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeMethodParser.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeMethodParser.java Tue Jul 30 16:12:26 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeMethodParser.java Tue Jul 30 17:42:50 2013 +0200 @@ -79,7 +79,7 @@ addDefaultFrame(methodSpec); addDefaultImplicitThis(method, methodSpec); - addDefaultFieldMethodSpec(method, methodSpec); + addDefaultFieldMethodSpec(methodSpec); addDefaultChildren(shortCircuitsEnabled, shortCircuitName, methodSpec); return methodSpec; @@ -119,9 +119,9 @@ } } - protected void addDefaultFieldMethodSpec(ExecutableElement method, MethodSpec methodSpec) { + protected void addDefaultFieldMethodSpec(MethodSpec methodSpec) { for (NodeFieldData field : getNode().getFields()) { - if (!Utils.isFieldAccessible(method, field.getVariable())) { + if (getNode().isNodeContainer() || field.getGetter() == null) { ParameterSpec spec = new ParameterSpec(field.getName(), field.getType()); spec.setLocal(true); methodSpec.addOptional(spec); diff -r 4a9936bb03a4 -r 7a8835ec5e7d 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 Tue Jul 30 16:12:26 2013 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/node/NodeParser.java Tue Jul 30 17:42:50 2013 +0200 @@ -277,10 +277,10 @@ node.setPolymorphicSpecializations(specializations); } - private NodeData parseNodeData(TypeElement templateType, TypeMirror nodeType, List elements, List lookupTypes) { + private NodeData parseNodeData(TypeElement templateType, TypeMirror nodeType, List elements, List typeHierarchy) { NodeData nodeData = new NodeData(templateType, templateType.getSimpleName().toString()); - AnnotationMirror typeSystemMirror = findFirstAnnotation(lookupTypes, TypeSystemReference.class); + AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class); if (typeSystemMirror == null) { nodeData.addError("No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), Utils.getQualifiedName(nodeType)); return nodeData; @@ -293,7 +293,7 @@ return nodeData; } - AnnotationMirror polymorphicMirror = findFirstAnnotation(lookupTypes, PolymorphicLimit.class); + AnnotationMirror polymorphicMirror = findFirstAnnotation(typeHierarchy, PolymorphicLimit.class); if (polymorphicMirror != null) { AnnotationValue limitValue = Utils.getAnnotationValue(polymorphicMirror, "value"); int polymorphicLimit = Utils.getAnnotationValue(Integer.class, polymorphicMirror, "value"); @@ -304,8 +304,8 @@ } List assumptionsList = new ArrayList<>(); - for (int i = lookupTypes.size() - 1; i >= 0; i--) { - TypeElement type = lookupTypes.get(i); + for (int i = typeHierarchy.size() - 1; i >= 0; i--) { + TypeElement type = typeHierarchy.get(i); AnnotationMirror assumptions = Utils.findAnnotationMirror(context.getEnvironment(), type, NodeAssumptions.class); if (assumptions != null) { List assumptionStrings = Utils.getAnnotationValueList(String.class, assumptions, "value"); @@ -317,35 +317,66 @@ } } } - AnnotationMirror nodeInfoMirror = findFirstAnnotation(lookupTypes, NodeInfo.class); + AnnotationMirror nodeInfoMirror = findFirstAnnotation(typeHierarchy, NodeInfo.class); if (nodeInfoMirror != null) { nodeData.setShortName(Utils.getAnnotationValue(String.class, nodeInfoMirror, "shortName")); } nodeData.setAssumptions(new ArrayList<>(assumptionsList)); nodeData.setNodeType(nodeType); - AnnotationMirror nodeContainer = findFirstAnnotation(lookupTypes, NodeContainer.class); + AnnotationMirror nodeContainer = findFirstAnnotation(typeHierarchy, NodeContainer.class); nodeData.setNodeContainer(nodeContainer != null); nodeData.setTypeSystem(typeSystem); - nodeData.setFields(parseFields(elements)); + nodeData.setFields(parseFields(typeHierarchy, elements)); parsedNodes.put(Utils.getQualifiedName(templateType), nodeData); // parseChildren invokes cyclic parsing. - nodeData.setChildren(parseChildren(elements, lookupTypes)); + nodeData.setChildren(parseChildren(elements, typeHierarchy)); nodeData.setExecutableTypes(groupExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements))); return nodeData; } - private static List parseFields(List elements) { + private List parseFields(List typeHierarchy, List elements) { + Set names = new HashSet<>(); + List fields = new ArrayList<>(); for (VariableElement field : ElementFilter.fieldsIn(elements)) { if (field.getModifiers().contains(Modifier.STATIC)) { continue; } if (field.getModifiers().contains(Modifier.PUBLIC) || field.getModifiers().contains(Modifier.PROTECTED)) { - fields.add(new NodeFieldData(field)); + String name = field.getSimpleName().toString(); + fields.add(new NodeFieldData(field, null, field.asType(), name, false)); + names.add(name); } } + + List reversedTypeHierarchy = new ArrayList<>(typeHierarchy); + Collections.reverse(reversedTypeHierarchy); + for (TypeElement typeElement : reversedTypeHierarchy) { + AnnotationMirror nodeChildrenMirror = Utils.findAnnotationMirror(processingEnv, typeElement, NodeFields.class); + List children = Utils.collectAnnotations(context, nodeChildrenMirror, "value", typeElement, NodeField.class); + + for (AnnotationMirror mirror : children) { + String name = Utils.firstLetterLowerCase(Utils.getAnnotationValue(String.class, mirror, "name")); + TypeMirror type = Utils.getAnnotationValue(TypeMirror.class, mirror, "type"); + + NodeFieldData field = new NodeFieldData(typeElement, mirror, type, name, true); + if (name.isEmpty()) { + field.addError(Utils.getAnnotationValue(mirror, "name"), "Field name cannot be empty."); + } else if (names.contains(name)) { + field.addError(Utils.getAnnotationValue(mirror, "name"), "Duplicate field name '%s'.", name); + } + names.add(name); + + fields.add(field); + } + } + + for (NodeFieldData nodeFieldData : fields) { + nodeFieldData.setGetter(findGetter(elements, nodeFieldData.getName(), nodeFieldData.getType())); + } + return fields; } @@ -975,6 +1006,12 @@ unusedElements.removeAll(nodeData.getExtensionElements()); } + for (NodeFieldData field : nodeData.getFields()) { + if (field.getGetter() != null) { + unusedElements.remove(field.getGetter()); + } + } + for (NodeChildData child : nodeData.getChildren()) { if (child.getAccessElement() != null) { unusedElements.remove(child.getAccessElement()); @@ -1142,7 +1179,7 @@ } } - private Element findGetter(List elements, String variableName, TypeMirror type) { + private ExecutableElement findGetter(List elements, String variableName, TypeMirror type) { if (type == null) { return null; }