/* * 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 static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; import java.util.concurrent.locks.ReentrantLock; import org.junit.Assert; import org.junit.Test; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.ImplicitCast; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.TypeSystem; import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.dsl.UnsupportedSpecializationException; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ExecuteChildWithImplicitCast1NodeGen; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast0NodeFactory; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast1NodeFactory; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast2NodeFactory; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast3NodeGen; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast4NodeFactory; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCast5NodeFactory; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.ImplicitCastExecuteNodeGen; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.StringEquals1NodeGen; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.StringEquals2NodeGen; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.StringEquals3NodeGen; import com.oracle.truffle.api.dsl.test.ImplicitCastTestFactory.TestImplicitCastWithCacheNodeGen; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; import com.oracle.truffle.api.dsl.test.examples.ExampleNode; import com.oracle.truffle.api.dsl.test.examples.ExampleNode.ExampleArgumentNode; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.UnexpectedResultException; public class ImplicitCastTest { @TypeSystem({int.class, String.class, boolean.class}) static class ImplicitCast0Types { @ImplicitCast static boolean castInt(int intvalue) { return intvalue == 1 ? true : false; } @ImplicitCast static boolean castString(String strvalue) { return strvalue.equals("1"); } } private static int charSequenceCast; @TypeSystem static class ImplicitCast1Types { @ImplicitCast static CharSequence castCharSequence(String strvalue) { charSequenceCast++; return strvalue; } } @TypeSystemReference(ImplicitCast0Types.class) @NodeChild(value = "operand", type = ImplicitCast0Node.class) abstract static class ImplicitCast0Node extends ValueNode { public abstract Object executeEvaluated(VirtualFrame frame, Object value2); @Specialization public String op1(String value) { return value; } @Specialization public boolean op1(boolean value) { return value; } } @Test public void testImplicitCast0() { ImplicitCast0Node node = ImplicitCast0NodeFactory.create(null); TestRootNode<ImplicitCast0Node> root = new TestRootNode<>(node); root.adoptChildren(); 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)); } @TypeSystemReference(ImplicitCast0Types.class) @NodeChild(value = "operand", type = ImplicitCast1Node.class) abstract static class ImplicitCast1Node extends ValueNode { public abstract Object executeEvaluated(VirtualFrame frame, Object operand); @Specialization public String op0(String value) { return value; } @Specialization(rewriteOn = RuntimeException.class) public boolean op1(@SuppressWarnings("unused") boolean value) throws RuntimeException { throw new RuntimeException(); } @Specialization(replaces = "op1") public boolean op2(boolean value) { return value; } } @Test public void testImplicitCast1() { ImplicitCast1Node node = ImplicitCast1NodeFactory.create(null); TestRootNode<ImplicitCast1Node> root = new TestRootNode<>(node); root.adoptChildren(); 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)); } @TypeSystemReference(ImplicitCast0Types.class) @NodeChildren({@NodeChild(value = "operand0", type = ImplicitCast2Node.class), @NodeChild(value = "operand1", type = ImplicitCast2Node.class, executeWith = "operand0")}) // TODO temporary workaround abstract static class ImplicitCast2Node extends ValueNode { @Specialization public String op0(String v0, String v1) { return v0 + v1; } @SuppressWarnings("unused") @Specialization(rewriteOn = RuntimeException.class) public boolean op1(boolean v0, boolean v1) throws RuntimeException { throw new RuntimeException(); } @Specialization(replaces = "op1") public boolean op2(boolean v0, boolean v1) { return v0 && v1; } public abstract Object executeEvaluated(VirtualFrame frame, Object v1); public abstract Object executeEvaluated(VirtualFrame frame, Object v1, Object v2); public abstract Object executeEvaluated(VirtualFrame frame, boolean v1, boolean v2); } @Test public void testImplicitCast2() { ImplicitCast2Node node = ImplicitCast2NodeFactory.create(null, null); TestRootNode<ImplicitCast2Node> root = new TestRootNode<>(node); root.adoptChildren(); Assert.assertEquals("42", root.getNode().executeEvaluated(null, "4", "2")); Assert.assertEquals(true, root.getNode().executeEvaluated(null, 1, 1)); Assert.assertEquals("42", root.getNode().executeEvaluated(null, "4", "2")); Assert.assertEquals(true, root.getNode().executeEvaluated(null, 1, 1)); Assert.assertEquals(true, root.getNode().executeEvaluated(null, true, true)); } @TypeSystemReference(ImplicitCast1Types.class) abstract static class ImplicitCast3Node extends Node { @Specialization public CharSequence op0(CharSequence v0, @SuppressWarnings("unused") CharSequence v1) { return v0; } public abstract Object executeEvaluated(CharSequence v1, CharSequence v2); } @Test public void testImplicitCast3() { ImplicitCast3Node node = ImplicitCast3NodeGen.create(); CharSequence seq1 = "foo"; CharSequence seq2 = "bar"; charSequenceCast = 0; node.executeEvaluated(seq1, seq2); Assert.assertEquals(2, charSequenceCast); } @TypeSystem static class ImplicitCast3Types { @ImplicitCast static long castLong(int intValue) { return intValue; } @ImplicitCast static long castLong(boolean intValue) { return intValue ? 1 : 0; } } @NodeChild @TypeSystemReference(ImplicitCast3Types.class) abstract static class ImplicitCast4Node extends ValueNode { @Specialization(guards = "value != 1") public int doInt(int value) { return value; } @Specialization(guards = "value != 42") public long doLong(long value) { return -value; } protected abstract Object executeEvaluated(Object operand); } @NodeChildren({@NodeChild, @NodeChild}) @TypeSystemReference(ImplicitCast3Types.class) @SuppressWarnings("unused") abstract static class ImplicitCast5Node extends ValueNode { @Specialization(guards = "value != 1") public int doInt(int a, int value) { return value; } @Specialization(guards = "value != 42") public long doLong(long a, long value) { return -value; } } static class Test4Input extends ValueNode { int n = 0; @Override public Object execute(VirtualFrame frame) { n++; if (n == 1) { return 1; } else if (n == 2) { return 42; } else { throw new AssertionError(); } } } @Test public void testUseUncastedValuesForSlowPath1() { ImplicitCast4Node node = ImplicitCast4NodeFactory.create(new Test4Input()); Assert.assertEquals(-1L, node.execute(null)); Assert.assertEquals(42, node.execute(null)); } @Test public void testUseUncastedValuesForSlowPath2() { ImplicitCast5Node node = ImplicitCast5NodeFactory.create(new Test4Input(), new Test4Input()); Assert.assertEquals(-1L, node.execute(null)); Assert.assertEquals(42, node.execute(null)); } @TypeSystem({String.class, boolean.class}) static class ImplicitCastError1 { @ImplicitCast @ExpectError("Target type and source type of an @ImplicitCast must not be the same type.") static String castInvalid(@SuppressWarnings("unused") String value) { throw new AssertionError(); } } @TypeSystem({String.class, boolean.class}) static class ImplicitCastError2 { @ImplicitCast @ExpectError("Target type and source type of an @ImplicitCast must not be the same type.") static String castInvalid(@SuppressWarnings("unused") String value) { throw new AssertionError(); } } @TypeSystem static class ImplicitCast2Types { @ImplicitCast static String castString(CharSequence str) { return str.toString(); } } @Test public void testStringEquals1() { StringEquals1Node node = StringEquals1NodeGen.create(); Assert.assertTrue(node.executeBoolean("foo", "foo")); Assert.assertFalse(node.executeBoolean("foo", "bar")); } @TypeSystemReference(ImplicitCast2Types.class) abstract static class StringEquals1Node extends Node { protected abstract boolean executeBoolean(String arg1, String arg2); @SuppressWarnings("unused") @Specialization(guards = {"cachedArg1.equals(arg1)", "cachedArg2.equals(arg2)"}, limit = "1") protected static boolean doCached(String arg1, String arg2, @Cached("arg1") String cachedArg1, @Cached("arg2") String cachedArg2, @Cached("arg1.equals(arg2)") boolean result) { return result; } @Specialization protected static boolean doUncached(String arg1, String arg2) { return arg1.equals(arg2); } } @Test public void testStringEquals2() { StringEquals2Node node = StringEquals2NodeGen.create(); Assert.assertTrue(node.executeBoolean("foo", "foo")); Assert.assertFalse(node.executeBoolean("foo", "bar")); } @TypeSystemReference(ImplicitCast2Types.class) abstract static class StringEquals2Node extends Node { protected abstract boolean executeBoolean(CharSequence arg1, CharSequence arg2); @SuppressWarnings("unused") @Specialization(guards = {"cachedArg1.equals(arg1)", "cachedArg2.equals(arg2)"}, limit = "2") protected static boolean doCached(String arg1, String arg2, @Cached("arg1") String cachedArg1, @Cached("arg2") String cachedArg2, @Cached("arg1.equals(arg2)") boolean result) { return result; } @Specialization protected static boolean doUncached(String arg1, String arg2) { return arg1.equals(arg2); } } @Test public void testStringEquals3() { StringEquals3Node node = StringEquals3NodeGen.create(); Assert.assertTrue(node.executeBoolean("foo")); try { Assert.assertTrue(node.executeBoolean("bar")); Assert.fail(); } catch (UnsupportedSpecializationException e) { } } @TypeSystemReference(ImplicitCast2Types.class) abstract static class StringEquals3Node extends Node { protected abstract boolean executeBoolean(CharSequence arg1); @SuppressWarnings("unused") @Specialization(guards = {"cachedArg1.equals(arg1)"}, limit = "1") protected static boolean doCached(String arg1, @Cached("arg1") String cachedArg1, @Cached("arg1.equals(arg1)") boolean result) { return result; } } @TypeSystem static class ImplicitCast4Types { @ImplicitCast static long castLong(int value) { return value; } } @Test public void testExecuteChildWithImplicitCast1() throws UnexpectedResultException { ExecuteChildWithImplicitCast1Node node = ExecuteChildWithImplicitCast1NodeGen.create(createArguments(1)); /* * if executeLong is used for the initial execution of the node and the uninitialized case * is not checked then this executeLong method might return 0L instead of 2L. This test * verifies that this particular case does not happen. */ Assert.assertEquals(2L, node.executeLong(Truffle.getRuntime().createVirtualFrame(new Object[]{2L}, new FrameDescriptor()))); } @TypeSystemReference(ImplicitCast4Types.class) public abstract static class ExecuteChildWithImplicitCast1Node extends ExampleNode { @Specialization public long sleep(long duration) { return duration; } } @Test public void testImplicitCastExecute() { CallTarget target = ExampleNode.createTarget(ImplicitCastExecuteNodeGen.create(ExampleNode.createArguments(2))); Assert.assertEquals("s1", target.call(1, 2D)); Assert.assertEquals("s0", target.call(1, 1)); target = ExampleNode.createTarget(ImplicitCastExecuteNodeGen.create(ExampleNode.createArguments(2))); Assert.assertEquals("s0", target.call(1, 1)); Assert.assertEquals("s1", target.call(1, 2D)); target = ExampleNode.createTarget(ImplicitCastExecuteNodeGen.create(ExampleNode.createArguments(2))); Assert.assertEquals("s0", target.call(1, 1)); Assert.assertEquals("s2", target.call(1, 2L)); target = ExampleNode.createTarget(ImplicitCastExecuteNodeGen.create(ExampleNode.createArguments(2))); Assert.assertEquals("s2", target.call(1, 2L)); Assert.assertEquals("s0", target.call(1, 1)); } @TypeSystem public static class TS { @ImplicitCast public static int promoteToInt(byte value) { return value; } @ImplicitCast public static int promoteToInt(short value) { return value; } @ImplicitCast public static long promoteToLong(byte value) { return value; } @ImplicitCast public static long promoteToLong(short value) { return value; } @ImplicitCast public static long promoteToLong(int value) { return value; } @ImplicitCast public static double promoteToDouble(float value) { return value; } } @TypeSystemReference(TS.class) @SuppressWarnings("unused") public abstract static class ImplicitCastExecuteNode extends ExampleNode { @Specialization public String s0(int a, int b) { return "s0"; } @Specialization public String s1(long a, double b) { return "s1"; } @Specialization public String s2(long a, long b) { return "s2"; } } @Test public void testImplicitCastExecute2() { ExampleArgumentNode[] args = ExampleNode.createArguments(2); CallTarget target = ExampleNode.createTarget(ImplicitCastExecuteNodeGen.create(args)); Assert.assertEquals("s2", target.call(1L, 1L)); Assert.assertEquals(0, args[0].longInvocationCount); Assert.assertEquals(0, args[1].longInvocationCount); Assert.assertEquals(1, args[0].genericInvocationCount); Assert.assertEquals(1, args[1].genericInvocationCount); Assert.assertEquals("s2", target.call(1L, 1L)); Assert.assertEquals(1, args[0].longInvocationCount); Assert.assertEquals(1, args[1].longInvocationCount); Assert.assertEquals(2, args[0].genericInvocationCount); Assert.assertEquals(2, args[1].genericInvocationCount); Assert.assertEquals("s2", target.call(1L, 1L)); Assert.assertEquals(2, args[0].longInvocationCount); Assert.assertEquals(2, args[1].longInvocationCount); Assert.assertEquals(3, args[0].genericInvocationCount); Assert.assertEquals(3, args[1].genericInvocationCount); Assert.assertEquals(0, args[0].doubleInvocationCount); Assert.assertEquals(0, args[1].doubleInvocationCount); Assert.assertEquals(0, args[0].intInvocationCount); Assert.assertEquals(0, args[1].intInvocationCount); } @Test public void testImplicitCastWithCache() { TestImplicitCastWithCacheNode node = TestImplicitCastWithCacheNodeGen.create(); Assert.assertEquals(0, node.specializeCalls); ConcreteString concrete = new ConcreteString(); node.execute("a", true); Assert.assertEquals(1, node.specializeCalls); node.execute(concrete, true); Assert.assertEquals(2, node.specializeCalls); node.execute(concrete, true); node.execute(concrete, true); node.execute(concrete, true); // ensure we stabilize Assert.assertEquals(2, node.specializeCalls); } interface AbstractString { } static class ConcreteString implements AbstractString { } @TypeSystem static class TestTypeSystem { @ImplicitCast public static AbstractString toAbstractStringVector(@SuppressWarnings("unused") String vector) { return new ConcreteString(); } } @TypeSystemReference(TestTypeSystem.class) abstract static class TestImplicitCastWithCacheNode extends Node { int specializeCalls; public abstract int execute(Object arg, boolean flag); @Specialization(guards = {"specializeCall(flag)", "cachedFlag == flag"}) @SuppressWarnings("unused") protected static int test(AbstractString arg, boolean flag, @Cached("flag") boolean cachedFlag) { return flag ? 100 : -100; } boolean specializeCall(@SuppressWarnings("unused") boolean flag) { ReentrantLock lock = (ReentrantLock) getLock(); if (lock.isHeldByCurrentThread()) { // the lock is held for guards executed in executeAndSpecialize specializeCalls++; } return true; } } }