diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 3e917c8cc138..9af86374f0e6 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -12,11 +12,11 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-50017 TruffleStringIterator.NextNode and TruffleStringIterator.PreviousNode now require the iterated string's encoding as a parameter for performance reasons. * GR-63075 Java host interop again inherits public method methods from non-public base classes if public access is enabled. This was originally changed in 24.1. * GR-63201 Added `TruffleLanguage.Registration.optionalResources` and `TruffleInstrument.Registration.optionalResources` attributes to support optional resources which implementations are not available at the runtime. Optional resources, if omitted at runtime, can still be used as long as their resource path is specified via the `polyglot.engine.resourcePath.` system property. +* GR-61282 Bytecode DSL: Bytecode builders now also allow emitting source sections using the start and length source indices in the end method in addition to the begin method. This was added to support linear parsing without look-ahead. +* GR-61282 Bytecode DSL: (breaking) If multiple source sections were specified around root operations, only the innermost source section directly encapsulating the root will be accessible. Other encapsulating source sections will be discarded for outer most root operations. ## Version 24.2.0 * GR-60636 Truffle now stops compiling when the code cache fills up on HotSpot. A warning is printed when that happens. - -## Version 24.2.0 * GR-57658 Added `TruffleLanguage.Env.getLanguageInfo(Class)` to lookup a `LanguageInfo` instance for a language class returned by `InteropLibrary.getLanguage(Object)`. * GR-57164 Added support for reading unaligned ints, shorts and long to `ByteArraySupport`. * GR-57164 `RootNode.translateStackTraceElement()` is now always consulted for polyglot and debugger stack traces. Stack traces now use the source section, the executable name, the name of the declared meta-object to build `StackTraceElement` instances. @@ -37,8 +37,6 @@ This changelog summarizes major changes between Truffle versions relevant to lan * GR-55296 Added support to convert any string to a `byte[]` with a given `Value.StringEncoding` using `Value.asStringBytes(...)`. * GR-40323 Deprecated `Shape.Builder.layout(Class)` for removal and added replacement API [`Shape.Builder.layout(Class, MethodHandles.Lookup)`](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/object/Shape.Builder.html#layout(java.lang.Class,java.lang.MethodHandles.Lookup)). Replace usages with the new method, additionally providing a `Lookup` that has full privilege access to the layout class or the module in which it is declared, as obtained by `MethodHandles.lookup()`. See javadoc for the updated usage. * GR-55296 Added support for UTF-16 and UTF-32 in non-system-endianness without dependency on the JCodings library in TruffleString. - - * GR-58550 Added `FrameDescriptor.Builder.illegalDefaultValue()` which initializes all frame slots as `FrameSlotKind.Illegal` for newly created frames. This is different from the default behavior, which initializes all frame slot kinds as `FrameSlotKind.Object`. This means that frame slots, when they are read before they were written, throw a `FrameSlotTypeException`, consistent with the behavior after clearing a frame slot. * GR-58550 Deprecated the default constructor for `FrameSlotTypeException` and replaced it with `FrameSlotTypeException.create(...)`. Exceptions of this kind thrown by the `Frame` now contain the slot index and the expected and the actual frame slot kind which are accessible with the respective instance methods. * GR-58550 Fixed invalid `PolyglotException.getMessage()` javadoc. A polyglot exception may in fact return a `null` message. diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/AbstractBasicInterpreterTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/AbstractBasicInterpreterTest.java index 8d07fbb71c5c..9a40c86c4ee9 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/AbstractBasicInterpreterTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/AbstractBasicInterpreterTest.java @@ -65,7 +65,6 @@ import org.junit.function.ThrowingRunnable; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import com.oracle.truffle.api.CompilerDirectives; @@ -263,7 +262,11 @@ public static List getParameters() { return result; } - @Parameter(0) public TestRun run; + public final TestRun run; + + public AbstractBasicInterpreterTest(TestRun run) { + this.run = run; + } public RootCallTarget parse(String rootName, BytecodeParser builder) { BytecodeRootNode rootNode = parseNode(run.interpreterClass, LANGUAGE, run.testSerialize, rootName, builder); @@ -305,7 +308,11 @@ public static BytecodeRootNodes { diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BranchTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BranchTest.java index dcdb73155e15..123347642278 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BranchTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BranchTest.java @@ -55,6 +55,11 @@ public class BranchTest extends AbstractBasicInterpreterTest { // @formatter:off + public BranchTest(TestRun run) { + super(run); + } + + @Test public void testBranchForward() { // goto lbl; diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BytecodeLocationTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BytecodeLocationTest.java index 1afe1f585c3e..3ff26ea70ef0 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BytecodeLocationTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/BytecodeLocationTest.java @@ -60,6 +60,11 @@ @RunWith(Parameterized.class) public class BytecodeLocationTest extends AbstractBasicInterpreterTest { + + public BytecodeLocationTest(TestRun run) { + super(run); + } + @Test public void testGetBytecodeLocation() { Source source = Source.newBuilder("test", "getBytecodeLocation", "baz").build(); diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/CopyLocalsTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/CopyLocalsTest.java index e07badb63162..df4940e28edf 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/CopyLocalsTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/CopyLocalsTest.java @@ -53,6 +53,10 @@ @RunWith(Parameterized.class) public class CopyLocalsTest extends AbstractBasicInterpreterTest { + public CopyLocalsTest(TestRun run) { + super(run); + } + @Test public void testCopyAllLocals() { /** diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/ExceptionHandlerTableTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/ExceptionHandlerTableTest.java index 1ff4621172c9..5c44f28cca7e 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/ExceptionHandlerTableTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/ExceptionHandlerTableTest.java @@ -69,6 +69,11 @@ @RunWith(Parameterized.class) public class ExceptionHandlerTableTest extends AbstractBasicInterpreterTest { + + public ExceptionHandlerTableTest(TestRun run) { + super(run); + } + private record ExceptionRangeTree(int index, String name, HandlerKind kind, ExceptionRangeTree[] nested) { } diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/LocalsTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/LocalsTest.java index 664fcaba441e..e426a8e87e3d 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/LocalsTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/LocalsTest.java @@ -68,6 +68,10 @@ public class LocalsTest extends AbstractBasicInterpreterTest { + public LocalsTest(TestRun run) { + super(run); + } + @Test public void testBasicLocals() { for (int i = 0; i < 100; i++) { diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SourcesTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SourcesTest.java index 9cabecb64875..e07ae46f877e 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SourcesTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SourcesTest.java @@ -47,11 +47,13 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import java.util.ArrayList; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import com.oracle.truffle.api.bytecode.BytecodeConfig; import com.oracle.truffle.api.bytecode.BytecodeLocation; @@ -64,31 +66,37 @@ @RunWith(Parameterized.class) public class SourcesTest extends AbstractBasicInterpreterTest { - private static void assertInstructionSourceSection(Instruction i, Source source, int startIndex, int length) { - assertSourceSection(i.getLocation().getSourceLocation(), source, startIndex, length); - } + private final SourceTestRun sourceRun; - private static void assertSourceSection(SourceSection section, Source source, int startIndex, int length) { - assertSame(source, section.getSource()); - assertEquals(startIndex, section.getCharIndex()); - assertEquals(length, section.getCharLength()); + public SourcesTest(SourceTestRun run) { + super(run.run); + this.sourceRun = run; } - private static void assertSourceSections(SourceSection[] sections, Source source, int... pairs) { - assert pairs.length % 2 == 0; - assertEquals(pairs.length / 2, sections.length); - - for (int i = 0; i < sections.length; i++) { - assertSourceSection(sections[i], source, pairs[2 * i], pairs[2 * i + 1]); + @Parameters(name = "{0}") + public static List getRuns() { + List runs = new ArrayList<>(); + for (TestRun run : getParameters()) { + runs.add(new SourceTestRun(Mode.PREFIX, run)); + runs.add(new SourceTestRun(Mode.SUFFIX, run)); } + return runs; } - private static ExpectedSourceTree est(String contents, ExpectedSourceTree... children) { - return expectedSourceTree(contents, children); + private void beginSourceSection(BasicInterpreterBuilder b, int start, int length) { + if (sourceRun.mode() == Mode.PREFIX) { + b.beginSourceSection(start, length); + } else { + b.beginSourceSection(); + } } - private static void assertSourceInformationTree(BytecodeNode bytecode, ExpectedSourceTree expected) { - expected.assertTreeEquals(bytecode.getSourceInformationTree()); + private void endSourceSection(BasicInterpreterBuilder b, int start, int length) { + if (sourceRun.mode() == Mode.PREFIX) { + b.endSourceSection(); + } else { + b.endSourceSection(start, length); + } } @Test @@ -97,17 +105,17 @@ public void testSource() { BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginRoot(); b.beginSource(source); - b.beginSourceSection(0, 8); + beginSourceSection(b, 0, 8); b.beginReturn(); - b.beginSourceSection(7, 1); + beginSourceSection(b, 7, 1); b.emitLoadConstant(1L); - b.endSourceSection(); + endSourceSection(b, 7, 1); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 8); b.endSource(); b.endRoot(); }); @@ -135,7 +143,7 @@ public void testManySourceSections() { BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginSource(source); for (int i = 0; i < numSourceSections; i++) { - b.beginSourceSection(0, numSourceSections - i); + beginSourceSection(b, 0, numSourceSections - i); } b.beginRoot(); b.beginReturn(); @@ -143,22 +151,17 @@ public void testManySourceSections() { b.endReturn(); b.endRoot(); for (int i = 0; i < numSourceSections; i++) { - b.endSourceSection(); + endSourceSection(b, 0, i + 1); } b.endSource(); }); SourceSection[] result = (SourceSection[]) node.getCallTarget().call(); - assertEquals(numSourceSections, result.length); - for (int i = 0; i < numSourceSections; i++) { - // sections are emitted in order of closing - assertSourceSection(result[i], source, 0, i + 1); - } + assertEquals(1, result.length); + // sections are emitted in order of closing + assertSourceSection(result[0], source, 0, 1); ExpectedSourceTree expectedSourceTree = est(sourceString.substring(0, 1)); - for (int i = 1; i < numSourceSections; i++) { - expectedSourceTree = est(sourceString.substring(0, i + 1), expectedSourceTree); - } assertSourceInformationTree(node.getBytecodeNode(), expectedSourceTree); } @@ -169,23 +172,23 @@ public void testSourceSplitByUnwind() { Source source = Source.newBuilder("test", "try finally", "test.test").build(); BasicInterpreter node = parseNodeWithSource("sourceSplitByUnwind", b -> { b.beginSource(source); - b.beginSourceSection(0, 11); + beginSourceSection(b, 0, 11); // root b.beginRoot(); b.beginTryFinally(() -> { - b.beginSourceSection(4, 7); // finally + beginSourceSection(b, 4, 7); b.emitVoidOperation(); - b.endSourceSection(); + endSourceSection(b, 4, 7); }); - b.beginSourceSection(0, 3); // try + beginSourceSection(b, 0, 3); // try b.beginIfThen(); b.emitLoadArgument(0); b.beginReturn(); // early return causes finally handler to be emitted. b.emitLoadConstant(42L); b.endReturn(); b.endIfThen(); - b.endSourceSection(); + endSourceSection(b, 0, 3); b.endTryFinally(); b.beginReturn(); @@ -193,7 +196,7 @@ public void testSourceSplitByUnwind() { b.endReturn(); b.endRoot(); - b.endSourceSection(); + endSourceSection(b, 0, 11); // root b.endSource(); }); @@ -213,31 +216,31 @@ public void testRootNodeSourceSection() { Source source = Source.newBuilder("test", "0123456789", "test.test").build(); BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginSource(source); - b.beginSourceSection(0, 10); - b.beginSourceSection(1, 9); - b.beginSourceSection(2, 8); + beginSourceSection(b, 0, 10); + beginSourceSection(b, 1, 9); + beginSourceSection(b, 2, 8); b.beginRoot(); b.emitLoadArgument(0); - b.beginSourceSection(3, 7); + beginSourceSection(b, 3, 7); b.beginReturn(); - b.beginSourceSection(4, 6); + beginSourceSection(b, 4, 6); b.emitLoadConstant(1L); - b.endSourceSection(); + endSourceSection(b, 4, 6); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 3, 7); b.endRoot(); - b.endSourceSection(); - b.endSourceSection(); - b.endSourceSection(); + endSourceSection(b, 2, 8); + endSourceSection(b, 1, 9); + endSourceSection(b, 0, 10); b.endSource(); }); // The most specific source section should be chosen. assertSourceSection(node.getSourceSection(), source, 2, 8); assertSourceInformationTree(node.getBytecodeNode(), - est("0123456789", est("123456789", est("23456789", est("3456789", est("456789")))))); + est("23456789", est("3456789", est("456789")))); } @Test @@ -281,17 +284,17 @@ public void testSourceUnavailable() { Source source = Source.newBuilder("test", "return 1", "test.test").build(); BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginSource(source); - b.beginSourceSectionUnavailable(); + beginSourceSection(b, -1, -1); b.beginRoot(); b.beginReturn(); - b.beginSourceSection(7, 1); + beginSourceSection(b, 7, 1); b.emitLoadConstant(1L); - b.endSourceSection(); + endSourceSection(b, 7, 1); b.endReturn(); b.endRoot(); - b.endSourceSectionUnavailable(); + endSourceSection(b, -1, -1); b.endSource(); }); @@ -310,17 +313,17 @@ public void testSourceNoSourceSet() { assertThrowsWithMessage("No enclosing Source operation found - each SourceSection must be enclosed in a Source operation.", IllegalStateException.class, () -> { parseNodeWithSource("sourceNoSourceSet", b -> { b.beginRoot(); - b.beginSourceSection(0, 8); + beginSourceSection(b, 0, 8); b.beginReturn(); - b.beginSourceSection(7, 1); + beginSourceSection(b, 7, 1); b.emitLoadConstant(1L); - b.endSourceSection(); + endSourceSection(b, 7, 1); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 8); b.endRoot(); }); }); @@ -332,7 +335,7 @@ public void testSourceMultipleSources() { Source source2 = Source.newBuilder("test", "01234567", "test2.test").build(); BasicInterpreter root = parseNodeWithSource("sourceMultipleSources", b -> { b.beginSource(source1); - b.beginSourceSection(0, source1.getLength()); + beginSourceSection(b, 0, source1.getLength()); b.beginRoot(); @@ -340,7 +343,7 @@ public void testSourceMultipleSources() { b.beginBlock(); b.emitVoidOperation(); // source1, 0, length - b.beginSourceSection(1, 2); + beginSourceSection(b, 1, 2); b.beginBlock(); b.emitVoidOperation(); // source1, 1, 2 @@ -348,19 +351,19 @@ public void testSourceMultipleSources() { b.beginBlock(); b.emitVoidOperation(); // source1, 1, 2 - b.beginSourceSection(3, 4); + beginSourceSection(b, 3, 4); b.beginBlock(); b.emitVoidOperation(); // source2, 3, 4 - b.beginSourceSection(5, 1); + beginSourceSection(b, 5, 1); b.beginBlock(); b.emitVoidOperation(); // source2, 5, 1 b.endBlock(); - b.endSourceSection(); + endSourceSection(b, 5, 1); b.emitVoidOperation(); // source2, 3, 4 b.endBlock(); - b.endSourceSection(); + endSourceSection(b, 3, 4); b.emitVoidOperation(); // source1, 1, 2 b.endBlock(); @@ -369,7 +372,7 @@ public void testSourceMultipleSources() { b.emitVoidOperation(); // source1, 1, 2 b.endBlock(); - b.endSourceSection(); + endSourceSection(b, 1, 2); b.emitVoidOperation(); // source1, 0, length @@ -379,7 +382,7 @@ public void testSourceMultipleSources() { b.endRoot(); - b.endSourceSection(); + endSourceSection(b, 0, source1.getLength()); b.endSource(); }); @@ -408,7 +411,7 @@ public void testGetSourcePosition() { BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginRoot(); b.beginSource(source); - b.beginSourceSection(0, 8); + beginSourceSection(b, 0, 8); b.beginReturn(); @@ -418,7 +421,7 @@ public void testGetSourcePosition() { b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 8); b.endSource(); b.endRoot(); }); @@ -434,17 +437,17 @@ public void testGetSourcePositions() { BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginRoot(); b.beginSource(source); - b.beginSourceSection(0, 8); + beginSourceSection(b, 0, 8); b.beginReturn(); - b.beginSourceSection(7, 1); + beginSourceSection(b, 7, 1); b.emitGetSourcePositions(); - b.endSourceSection(); + endSourceSection(b, 7, 1); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 8); b.endSource(); b.endRoot(); }); @@ -454,25 +457,58 @@ public void testGetSourcePositions() { assertSourceSections(result, source, 7, 1, 0, 8); } + @Test + public void testGetSourcePositionsEnclosingRootSections() { + Source source = Source.newBuilder("test", "other; def test() { return 1 }", "testGetSourcePositions").build(); + BasicInterpreter node = parseNodeWithSource("source", b -> { + b.beginSource(source); + beginSourceSection(b, 0, 30); + beginSourceSection(b, 7, 23); + + b.beginRoot(); + beginSourceSection(b, 20, 8); + + b.beginReturn(); + + beginSourceSection(b, 27, 1); + b.emitGetSourcePositions(); + endSourceSection(b, 27, 1); + + b.endReturn(); + + endSourceSection(b, 20, 8); + b.endRoot(); + + endSourceSection(b, 7, 23); + endSourceSection(b, 0, 30); + b.endSource(); + }); + + SourceSection[] result = (SourceSection[]) node.getCallTarget().call(); + + // Only the directly enclosing source section should be included. + assertSourceSections(result, source, 27, 1, 20, 8, 7, 23); + } + @Test public void testGetSourcePositionFrameInstance() { Source fooSource = Source.newBuilder("test", "return arg0()", "testGetSourcePositionFrameInstance#foo").build(); BasicInterpreter foo = parseNodeWithSource("foo", b -> { b.beginRoot(); b.beginSource(fooSource); - b.beginSourceSection(0, 13); + beginSourceSection(b, 0, 13); b.beginReturn(); - b.beginSourceSection(7, 6); + beginSourceSection(b, 7, 6); b.beginInvoke(); - b.beginSourceSection(7, 4); + beginSourceSection(b, 7, 4); b.emitLoadArgument(0); - b.endSourceSection(); + endSourceSection(b, 7, 4); b.endInvoke(); - b.endSourceSection(); + endSourceSection(b, 7, 6); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 13); b.endSource(); b.endRoot(); }); @@ -481,17 +517,17 @@ public void testGetSourcePositionFrameInstance() { BasicInterpreter bar = parseNodeWithSource("bar", b -> { b.beginRoot(); b.beginSource(barSource); - b.beginSourceSection(0, 17); + beginSourceSection(b, 0, 17); b.beginReturn(); - b.beginSourceSection(7, 10); + beginSourceSection(b, 7, 10); b.emitCollectSourceLocations(); - b.endSourceSection(); + endSourceSection(b, 7, 10); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 17); b.endSource(); b.endRoot(); }); @@ -509,19 +545,19 @@ public void testGetSourcePositionsFrameInstance() { BasicInterpreter foo = parseNodeWithSource("foo", b -> { b.beginRoot(); b.beginSource(fooSource); - b.beginSourceSection(0, 13); + beginSourceSection(b, 0, 13); b.beginReturn(); - b.beginSourceSection(7, 6); + beginSourceSection(b, 7, 6); b.beginInvoke(); - b.beginSourceSection(7, 4); + beginSourceSection(b, 7, 4); b.emitLoadArgument(0); - b.endSourceSection(); + endSourceSection(b, 7, 4); b.endInvoke(); - b.endSourceSection(); + endSourceSection(b, 7, 6); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 13); b.endSource(); b.endRoot(); }); @@ -530,17 +566,17 @@ public void testGetSourcePositionsFrameInstance() { BasicInterpreter bar = parseNodeWithSource("bar", b -> { b.beginRoot(); b.beginSource(barSource); - b.beginSourceSection(0, 17); + beginSourceSection(b, 0, 17); b.beginReturn(); - b.beginSourceSection(7, 10); + beginSourceSection(b, 7, 10); b.emitCollectAllSourceLocations(); - b.endSourceSection(); + endSourceSection(b, 7, 10); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 17); b.endSource(); b.endRoot(); }); @@ -570,18 +606,18 @@ public void testSourceTryFinally() { BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginRoot(); b.beginSource(source); - b.beginSourceSection(0, 11); + beginSourceSection(b, 0, 11); b.beginTryFinally(() -> { // finally - b.beginSourceSection(4, 7); + beginSourceSection(b, 4, 7); b.beginReturn(); b.emitGetSourcePositions(); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 4, 7); }); // try - b.beginSourceSection(0, 4); + beginSourceSection(b, 0, 4); b.beginBlock(); // if arg0 < 0, throw b.beginIfThen(); @@ -611,11 +647,11 @@ public void testSourceTryFinally() { b.endIfThen(); b.endBlock(); - b.endSourceSection(); + endSourceSection(b, 0, 4); b.endTryFinally(); - b.endSourceSection(); + endSourceSection(b, 0, 11); b.endSource(); b.endRoot(); }); @@ -643,27 +679,27 @@ public void testSourceOfRootWithTryFinally() { Source source = Source.newBuilder("test", "try finally", "sourceOfRootWithTryFinally").build(); BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginSource(source); - b.beginSourceSection(0, 11); + beginSourceSection(b, 0, 11); b.beginRoot(); b.beginTryFinally(() -> { // finally - b.beginSourceSection(4, 7); + beginSourceSection(b, 4, 7); b.emitVoidOperation(); - b.endSourceSection(); + endSourceSection(b, 4, 7); }); // try - b.beginSourceSection(0, 3); + beginSourceSection(b, 0, 3); b.beginBlock(); emitReturnIf(b, 0, 42L); b.emitVoidOperation(); b.endBlock(); - b.endSourceSection(); + endSourceSection(b, 0, 3); b.endTryFinally(); b.endRoot(); - b.endSourceSection(); + endSourceSection(b, 0, 11); b.endSource(); }); assertEquals(42L, node.getCallTarget().call(true)); @@ -708,25 +744,25 @@ public void testSourceOfRootWithTryFinallyNotNestedInSource() { BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginRoot(); b.beginSource(source); - b.beginSourceSection(0, 11); + beginSourceSection(b, 0, 11); b.beginTryFinally(() -> { // finally - b.beginSourceSection(4, 7); + beginSourceSection(b, 4, 7); b.emitVoidOperation(); - b.endSourceSection(); + endSourceSection(b, 4, 7); }); // try - b.beginSourceSection(0, 3); + beginSourceSection(b, 0, 3); b.beginBlock(); emitReturnIf(b, 0, 42L); b.emitVoidOperation(); b.endBlock(); - b.endSourceSection(); + endSourceSection(b, 0, 3); b.endTryFinally(); - b.endSourceSection(); + endSourceSection(b, 0, 11); b.endSource(); b.endRoot(); }); @@ -777,32 +813,32 @@ public void testSourceRootNodeDeclaredInTryFinally() { BasicInterpreter node = parseNodeWithSource("source", b -> { b.beginRoot(); b.beginSource(source); - b.beginSourceSection(0, 39); + beginSourceSection(b, 0, 39); b.beginTryFinally(() -> { // finally - b.beginSourceSection(14, 23); + beginSourceSection(b, 14, 23); - b.beginSourceSection(14, 13); + beginSourceSection(b, 14, 13); b.beginRoot(); - b.beginSourceSection(22, 4); + beginSourceSection(b, 22, 4); b.emitGetSourcePositions(); - b.endSourceSection(); + endSourceSection(b, 22, 4); BasicInterpreter f = b.endRoot(); - b.endSourceSection(); + endSourceSection(b, 14, 13); - b.beginSourceSection(29, 8); + beginSourceSection(b, 29, 8); b.beginReturn(); b.beginInvoke(); b.emitLoadConstant(f); b.endInvoke(); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 29, 8); - b.endSourceSection(); + endSourceSection(b, 14, 23); }); // try - b.beginSourceSection(0, 3); + beginSourceSection(b, 0, 3); b.beginBlock(); // if arg0 < 0, throw b.beginIfThen(); @@ -832,11 +868,11 @@ public void testSourceRootNodeDeclaredInTryFinally() { b.endIfThen(); b.endBlock(); - b.endSourceSection(); + endSourceSection(b, 0, 3); b.endTryFinally(); - b.endSourceSection(); + endSourceSection(b, 0, 39); b.endSource(); b.endRoot(); }); @@ -844,7 +880,7 @@ public void testSourceRootNodeDeclaredInTryFinally() { long[] inputs = new long[]{0, -1, 1}; for (int i = 0; i < inputs.length; i++) { SourceSection[] result = (SourceSection[]) node.getCallTarget().call(inputs[i]); - assertSourceSections(result, source, 22, 4, 14, 13, 14, 23, 0, 39); + assertSourceSections(result, source, 22, 4, 14, 13); } } @@ -855,17 +891,17 @@ public void testSourceReparse() { BytecodeRootNodes nodes = createNodes(BytecodeConfig.DEFAULT, b -> { b.beginRoot(); b.beginSource(source); - b.beginSourceSection(0, 8); + beginSourceSection(b, 0, 8); b.beginReturn(); - b.beginSourceSection(7, 1); + beginSourceSection(b, 7, 1); b.emitLoadConstant(1L); - b.endSourceSection(); + endSourceSection(b, 7, 1); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 8); b.endSource(); b.endRoot(); }); @@ -891,25 +927,25 @@ public void testReparseAfterTransitionToCached() { b.beginSource(source); b.beginRoot(); - b.beginSourceSection(0, 27); + beginSourceSection(b, 0, 27); b.beginReturn(); b.beginConditional(); - b.beginSourceSection(7, 4); + beginSourceSection(b, 7, 4); b.emitLoadArgument(0); - b.endSourceSection(); + endSourceSection(b, 7, 4); - b.beginSourceSection(14, 2); + beginSourceSection(b, 14, 2); b.emitLoadConstant(42L); - b.endSourceSection(); + endSourceSection(b, 14, 2); - b.beginSourceSection(19, 8); + beginSourceSection(b, 19, 8); b.emitGetSourcePositions(); - b.endSourceSection(); + endSourceSection(b, 19, 8); b.endConditional(); b.endReturn(); - b.endSourceSection(); + endSourceSection(b, 0, 27); b.endRoot(); b.endSource(); @@ -932,4 +968,45 @@ public void testReparseAfterTransitionToCached() { assertNotNull(aLocation.getSourceInformation()); } + + private static void assertInstructionSourceSection(Instruction i, Source source, int startIndex, int length) { + assertSourceSection(i.getLocation().getSourceLocation(), source, startIndex, length); + } + + private static void assertSourceSection(SourceSection section, Source source, int startIndex, int length) { + assertSame(source, section.getSource()); + assertEquals(startIndex, section.getCharIndex()); + assertEquals(length, section.getCharLength()); + } + + private static void assertSourceSections(SourceSection[] sections, Source source, int... pairs) { + assert pairs.length % 2 == 0; + assertEquals(pairs.length / 2, sections.length); + + for (int i = 0; i < sections.length; i++) { + assertSourceSection(sections[i], source, pairs[2 * i], pairs[2 * i + 1]); + } + } + + private static ExpectedSourceTree est(String contents, ExpectedSourceTree... children) { + return expectedSourceTree(contents, children); + } + + private static void assertSourceInformationTree(BytecodeNode bytecode, ExpectedSourceTree expected) { + expected.assertTreeEquals(bytecode.getSourceInformationTree()); + } + + enum Mode { + PREFIX, + SUFFIX + } + + record SourceTestRun(Mode mode, TestRun run) { + + @Override + public String toString() { + return mode.toString() + ":" + run.toString(); + } + } + } diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SplittingTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SplittingTest.java index 0c04f9f1a84e..338912c0a585 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SplittingTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/SplittingTest.java @@ -64,6 +64,10 @@ public class SplittingTest extends AbstractBasicInterpreterTest { + public SplittingTest(TestRun run) { + super(run); + } + Context context; @Before diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/TryFinallyTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/TryFinallyTest.java index f420a7f463a6..d0e2786996cd 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/TryFinallyTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/TryFinallyTest.java @@ -60,6 +60,10 @@ public class TryFinallyTest extends AbstractBasicInterpreterTest { // @formatter:off + public TryFinallyTest(TestRun run) { + super(run); + } + private static void testOrdering(boolean expectException, RootCallTarget root, Long... order) { testOrderingWithArguments(expectException, root, null, order); } diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/VariadicTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/VariadicTest.java index 2a5f864d5a92..8a326c41b271 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/VariadicTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/VariadicTest.java @@ -53,6 +53,11 @@ import com.oracle.truffle.api.bytecode.Instruction.Argument; public class VariadicTest extends AbstractBasicInterpreterTest { + + public VariadicTest(TestRun run) { + super(run); + } + @Test public void testVariadic0Arguments() { for (int i = 0; i < 32; i++) { diff --git a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/YieldTest.java b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/YieldTest.java index d924c4ebda83..9b0a53c7195b 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/YieldTest.java +++ b/truffle/src/com.oracle.truffle.api.bytecode.test/src/com/oracle/truffle/api/bytecode/test/basic_interpreter/YieldTest.java @@ -57,6 +57,10 @@ public class YieldTest extends AbstractBasicInterpreterTest { + public YieldTest(TestRun run) { + super(run); + } + @Test public void testYield() { // yield 1; diff --git a/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/BytecodeNode.java b/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/BytecodeNode.java index 5927e35818f5..25a91851ccb3 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/BytecodeNode.java +++ b/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/BytecodeNode.java @@ -1015,28 +1015,29 @@ record IndexedInstruction(Instruction instruction, int index) { for (Instruction i : instructions) { indexedInstructions.add(new IndexedInstruction(i, indexedInstructions.size())); } + List errors = new ArrayList<>(); - String instructionsDump = formatList(indexedInstructions, + String instructionsDump = formatList(errors, indexedInstructions, (i) -> i.instruction().getBytecodeIndex() == highlightedBci, - (i) -> Instruction.formatInstruction(i.index(), i.instruction(), maxLabelSize, maxArgumentSize)); + (i) -> Instruction.formatInstruction(errors, i.index(), i.instruction(), maxLabelSize, maxArgumentSize)); int exceptionCount = exceptions.size(); - String exceptionDump = formatList(exceptions, + String exceptionDump = formatList(errors, exceptions, (e) -> highlightedBci >= e.getStartBytecodeIndex() && highlightedBci < e.getEndBytecodeIndex(), ExceptionHandler::toString); int localsCount = locals.size(); - String localsDump = formatList(locals, + String localsDump = formatList(errors, locals, (e) -> highlightedBci >= e.getStartIndex() && highlightedBci < e.getEndIndex(), LocalVariable::toString); String sourceInfoCount = sourceInformation != null ? String.valueOf(sourceInformation.size()) : "-"; - String sourceDump = formatList(sourceInformation, + String sourceDump = formatList(errors, sourceInformation, (s) -> highlightedBci >= s.getStartBytecodeIndex() && highlightedBci < s.getEndBytecodeIndex(), SourceInformation::toString); - String tagDump = formatTagTree(getTagTree(), (s) -> highlightedBci >= s.getEnterBytecodeIndex() && highlightedBci <= s.getReturnBytecodeIndex()); - return String.format(""" + String tagDump = formatTagTree(errors, getTagTree(), (s) -> highlightedBci >= s.getEnterBytecodeIndex() && highlightedBci <= s.getReturnBytecodeIndex()); + String result = String.format(""" %s(name=%s)[ instructions(%s) = %s exceptionHandlers(%s) = %s @@ -1050,9 +1051,19 @@ record IndexedInstruction(Instruction instruction, int index) { localsCount, localsDump, sourceInfoCount, sourceDump, tagDump); + + if (!errors.isEmpty()) { + IllegalStateException s = new IllegalStateException("Failed to dump. Individual errors are attached as suppressed errors. Incomplete dump: " + result); + for (Throwable e : errors) { + s.addSuppressed(e); + } + throw s; + } + return result; + } - private static String formatList(List list, Predicate highlight, Function toString) { + private static String formatList(List errors, List list, Predicate highlight, Function toString) { if (list == null) { return "Not Available"; } else if (list.isEmpty()) { @@ -1065,19 +1076,26 @@ private static String formatList(List list, Predicate highlight, Funct } else { b.append("\n "); } - b.append(toString.apply(o)); + String v; + try { + v = toString.apply(o); + } catch (Throwable t) { + v = t.toString(); + errors.add(t); + } + b.append(v); } return b.toString(); } - private static String formatTagTree(TagTree tree, Predicate highlight) { + private static String formatTagTree(List errors, TagTree tree, Predicate highlight) { if (tree == null) { return " = Not Available"; } int maxWidth = maxTagTreeWidth(0, tree); StringBuilder b = new StringBuilder(); - int count = appendTagTree(b, 0, maxWidth, tree, highlight); + int count = appendTagTree(errors, b, 0, maxWidth, tree, highlight); b.insert(0, "(" + count + ") = "); return b.toString(); } @@ -1090,27 +1108,31 @@ private static int maxTagTreeWidth(int indentation, TagTree tree) { return width; } - private static int appendTagTree(StringBuilder sb, int indentation, int maxWidth, TagTree tree, Predicate highlight) { + private static int appendTagTree(List errors, StringBuilder sb, int indentation, int maxWidth, TagTree tree, Predicate highlight) { TagTreeNode node = (TagTreeNode) tree; sb.append("\n"); - String line = formatTagTreeLabel(indentation, tree, highlight, node); - sb.append(line); + try { + String line = formatTagTreeLabel(indentation, tree, highlight, node); + sb.append(line); - int spaces = maxWidth - line.length(); - for (int i = 0; i < spaces; i++) { - sb.append(" "); - } - sb.append(" | "); + int spaces = maxWidth - line.length(); + for (int i = 0; i < spaces; i++) { + sb.append(" "); + } + sb.append(" | "); - SourceSection sourceSection = node.getSourceSection(); - if (sourceSection != null) { - sb.append(SourceInformation.formatSourceSection(sourceSection, 60)); + SourceSection sourceSection = node.getSourceSection(); + if (sourceSection != null) { + sb.append(SourceInformation.formatSourceSection(sourceSection, 60)); + } + } catch (Throwable t) { + sb.append(t.toString()); + errors.add(t); } - int count = 1; for (TagTree child : tree.getTreeChildren()) { - count += appendTagTree(sb, indentation + 2, maxWidth, child, highlight); + count += appendTagTree(errors, sb, indentation + 2, maxWidth, child, highlight); } return count; } diff --git a/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/Instruction.java b/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/Instruction.java index ebd63c8c8ab9..8dab0208c7a5 100644 --- a/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/Instruction.java +++ b/truffle/src/com.oracle.truffle.api.bytecode/src/com/oracle/truffle/api/bytecode/Instruction.java @@ -255,10 +255,10 @@ public final boolean equals(Object obj) { */ @Override public final String toString() { - return formatInstruction(-1, this, 40, 60); + return formatInstruction(null, -1, this, 40, 60); } - static String formatInstruction(int index, Instruction instruction, int maxLabelWidth, int maxArgumentWidth) { + static String formatInstruction(List errors, int index, Instruction instruction, int maxLabelWidth, int maxArgumentWidth) { StringBuilder sb = new StringBuilder(); if (index != -1) { sb.append(String.format("%3d ", index)); @@ -269,10 +269,19 @@ static String formatInstruction(int index, Instruction instruction, int maxLabel String arguments = formatArguments(instruction); sb.append(arguments); appendSpaces(sb, maxArgumentWidth - arguments.length()); - SourceSection s = instruction.getSourceSection(); - if (s != null) { - sb.append(" | "); - sb.append(SourceInformation.formatSourceSection(s, 60)); + try { + SourceSection s = instruction.getSourceSection(); + if (s != null) { + sb.append(" | "); + sb.append(SourceInformation.formatSourceSection(s, 60)); + } + } catch (Throwable t) { + if (errors != null) { + errors.add(t); + sb.append(" | " + t.toString()); + } else { + throw t; + } } return sb.toString(); } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java index 5a1659df9bdd..0123bcbe5dc5 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/generator/BytecodeRootNodeElement.java @@ -2596,12 +2596,17 @@ void lazyInit() { this.add(createDoEmitFinallyHandler()); this.add(createDoCreateExceptionHandler()); + + this.add(new CodeVariableElement(Set.of(PRIVATE, STATIC, FINAL), type(int.class), "PATCH_CURRENT_SOURCE")).createInitBuilder().string("-2"); + this.add(new CodeVariableElement(Set.of(PRIVATE, STATIC, FINAL), type(int.class), "PATCH_NODE_SOURCE")).createInitBuilder().string("-3"); + + this.add(createDoPatchSourceInfo()); this.add(createDoEmitSourceInfo()); this.add(createFinish()); this.add(createBeforeEmitBranch()); this.add(createBeforeEmitReturn()); this.add(createPatchHandlerTable()); - this.add(createDoEmitRoot()); + this.add(createDoEmitRootSourceSection()); this.add(createAllocateNode()); this.add(createAllocateBytecodeLocal()); this.add(createAllocateBranchProfile()); @@ -3256,9 +3261,9 @@ private CodeExecutableElement createDeserialize() { b.startStatement(); if (operation.hasChildren()) { - b.startCall("begin" + operation.name); + b.startCall("begin" + operation.builderName); } else { - b.startCall("emit" + operation.name); + b.startCall("emit" + operation.builderName); } for (int i = 0; i < operation.operationBeginArguments.length; i++) { @@ -3280,7 +3285,7 @@ private CodeExecutableElement createDeserialize() { if (operation.kind == OperationKind.ROOT) { b.startStatement(); - b.type(BytecodeRootNodeElement.this.asType()).string(" node = ").cast(BytecodeRootNodeElement.this.asType()).string("end" + operation.name + "()"); + b.type(BytecodeRootNodeElement.this.asType()).string(" node = ").cast(BytecodeRootNodeElement.this.asType()).string("end" + operation.builderName + "()"); b.end(); b.declaration(type(int.class), "serializedContextDepth", "buffer.readInt()"); @@ -3295,7 +3300,7 @@ private CodeExecutableElement createDeserialize() { b.startStatement().startCall("context.builtNodes.set").string("buffer.readInt()").string("node").end().end(); } else { - b.startStatement().startCall("end" + operation.name); + b.startStatement().startCall("end" + operation.builderName); for (int i = 0; i < operation.operationEndArguments.length; i++) { b.string(operation.getOperationEndArgumentName(i)); } @@ -3680,7 +3685,7 @@ private CodeVariableElement createOperationNames() { int i = 1; for (OperationModel op : model.getOperations()) { if (op.id != i) { - throw new AssertionError(); + throw new AssertionError(op.toString()); } i++; @@ -3882,7 +3887,7 @@ private CodeExecutableElement createBegin(OperationModel operation) { return createBeginRoot(operation); } Modifier visibility = operation.isInternal ? PRIVATE : PUBLIC; - CodeExecutableElement ex = new CodeExecutableElement(Set.of(visibility), type(void.class), "begin" + operation.name); + CodeExecutableElement ex = new CodeExecutableElement(Set.of(visibility), type(void.class), "begin" + operation.builderName); for (OperationArgument arg : operation.operationBeginArguments) { ex.addParameter(arg.toVariableElement()); @@ -4373,12 +4378,6 @@ private CodeTree createOperationBeginData(CodeTreeBuilder b, OperationModel oper b.startThrow().startCall("failState").doubleQuote("No enclosing Source operation found - each SourceSection must be enclosed in a Source operation.").end().end(); b.end(); - String index = operation.getOperationBeginArgumentName(0); - String length = operation.getOperationBeginArgumentName(1); - - // Negative values are only permitted for the combination (-1, -1). - b.startAssert().string("(", index, " == -1 && ", length, " == -1) || (", index, " >= 0 && ", length, " >= 0)").end(); - b.declaration(type(int.class), "startBci"); b.startIf().string("rootOperationSp == -1").end().startBlock(); b.lineComment("not in a root yet"); @@ -4387,7 +4386,16 @@ private CodeTree createOperationBeginData(CodeTreeBuilder b, OperationModel oper b.statement("startBci = bci"); b.end(); - yield createOperationData(className, "foundSourceIndex", "startBci", index, length); + if (operation.operationBeginArguments.length == 0) { + yield createOperationData(className, "foundSourceIndex", "startBci", "-2", "-2"); + } else { + String index = operation.getOperationBeginArgumentName(0); + String length = operation.getOperationBeginArgumentName(1); + + emitValidateSourceSection(b, index, length); + + yield createOperationData(className, "foundSourceIndex", "startBci", index, length); + } } default -> { if (operation.isTransparent) { @@ -4460,7 +4468,7 @@ private CodeExecutableElement createEnd(OperationModel operation) { } Modifier visibility = operation.isInternal ? PRIVATE : PUBLIC; - CodeExecutableElement ex = new CodeExecutableElement(Set.of(visibility), type(void.class), "end" + operation.name); + CodeExecutableElement ex = new CodeExecutableElement(Set.of(visibility), type(void.class), "end" + operation.builderName); if (operation.kind == OperationKind.TAG) { ex.setVarArgs(true); @@ -4565,13 +4573,38 @@ private CodeExecutableElement createEnd(OperationModel operation) { b.end(); break; case SOURCE_SECTION: - b.startStatement().startCall("doEmitSourceInfo"); - b.string("operationData.sourceIndex"); - b.string("operationData.startBci"); - b.string("bci"); - b.string("operationData.start"); - b.string("operationData.length"); - b.end(2); + + String index; + String length; + if (operation.operationBeginArguments.length == 0) { + index = operation.getOperationEndArgumentName(0); + length = operation.getOperationEndArgumentName(1); + + emitValidateSourceSection(b, index, length); + + b.startStatement().startCall("doPatchSourceInfo"); + b.string("operationData.nodeId"); + b.string("operationData.start"); + b.string(index); + b.string(length); + b.end(2); + + b.startStatement().startCall("doEmitSourceInfo"); + b.string("operationData.sourceIndex"); + b.string("operationData.startBci"); + b.string("bci"); + b.string(index); + b.string(length); + b.end(2); + } else { + b.startStatement().startCall("doEmitSourceInfo"); + b.string("operationData.sourceIndex"); + b.string("operationData.startBci"); + b.string("bci"); + b.string("operationData.start"); + b.string("operationData.length"); + b.end(2); + } break; case SOURCE: break; @@ -4766,6 +4799,28 @@ private CodeExecutableElement createEnd(OperationModel operation) { return ex; } + private void emitValidateSourceSection(CodeTreeBuilder b, String index, String length) { + b.startIf().string(index, " != -1 && ", length, " != -1").end().startBlock(); + + b.startIf().string(index, " < 0 ").end().startBlock(); + b.startThrow().startNew(type(IllegalArgumentException.class)); + b.startGroup(); + b.doubleQuote("Invalid " + index + " provided:").string(" + ", index); + b.end(); + b.end().end(); + b.end(); // block + + b.startIf().string(length, " < 0").end().startBlock(); + b.startThrow().startNew(type(IllegalArgumentException.class)); + b.startGroup(); + b.doubleQuote("Invalid " + length + " provided:").string(" + ", index); + b.end(); + b.end().end(); + b.end(); // block + + b.end(); // block + } + private void emitCallAfterChild(CodeTreeBuilder b, OperationModel op, String producedValue, String childBci) { b.startStatement().startCall("afterChild"); b.tree(createOperationConstant(op)); @@ -4899,7 +4954,7 @@ private CodeExecutableElement createEndRoot(OperationModel rootOperation) { b.defaultDeclaration(e.asType(), e.getSimpleName().toString() + "_"); } - b.statement("doEmitRoot()"); + b.statement("doEmitRootSourceSection(operationData.index)"); b.startIf().string("parseSources").end().startBlock(); CodeTree copyOf = CodeTreeBuilder.createBuilder().startStaticCall(type(Arrays.class), "copyOf").string("sourceInfo").string("sourceInfoIndex").end().build(); b.startAssign("sourceInfo_").tree(copyOf).end(); @@ -5094,7 +5149,7 @@ private String maxLocals() { } private void buildBegin(CodeTreeBuilder b, OperationModel operation, String... args) { - b.startStatement().startCall("begin" + operation.name); + b.startStatement().startCall("begin" + operation.builderName); for (String arg : args) { b.string(arg); } @@ -5102,7 +5157,7 @@ private void buildBegin(CodeTreeBuilder b, OperationModel operation, String... a } private void buildEnd(CodeTreeBuilder b, OperationModel operation, String... args) { - b.startStatement().startCall("end" + operation.name); + b.startStatement().startCall("end" + operation.builderName); for (String arg : args) { b.string(arg); } @@ -5110,7 +5165,7 @@ private void buildEnd(CodeTreeBuilder b, OperationModel operation, String... arg } private void buildEmit(CodeTreeBuilder b, OperationModel operation, String... args) { - b.startStatement().startCall("emit" + operation.name); + b.startStatement().startCall("emit" + operation.builderName); for (String arg : args) { b.string(arg); } @@ -5509,7 +5564,7 @@ private CodeExecutableElement createGetCurrentRootOperationData() { private CodeExecutableElement createEmit(OperationModel operation) { Modifier visibility = operation.isInternal ? PRIVATE : PUBLIC; - CodeExecutableElement ex = new CodeExecutableElement(Set.of(visibility), type(void.class), "emit" + operation.name); + CodeExecutableElement ex = new CodeExecutableElement(Set.of(visibility), type(void.class), "emit" + operation.builderName); ex.setVarArgs(operation.operationBeginArgumentVarArgs); for (OperationArgument arg : operation.operationBeginArguments) { @@ -6907,8 +6962,38 @@ private void buildEmitInstructionStackEffect(CodeTreeBuilder b, InstructionModel b.end(2); } + private CodeExecutableElement createDoPatchSourceInfo() { + CodeExecutableElement ex = new CodeExecutableElement(Set.of(PRIVATE), type(void.class), "doPatchSourceInfo"); + ex.addParameter(new CodeVariableElement(type(int.class), "nodeId")); + ex.addParameter(new CodeVariableElement(type(int.class), "sourceIndex")); + ex.addParameter(new CodeVariableElement(type(int.class), "start")); + ex.addParameter(new CodeVariableElement(type(int.class), "length")); + + CodeTreeBuilder b = ex.createBuilder(); + + b.declaration(type(int[].class), "info"); + b.startIf().string("nodeId >= 0").end().startBlock(); + b.statement("info = builtNodes.get(nodeId).bytecode.sourceInfo"); + b.end().startElseBlock(); + b.statement("info = this.sourceInfo"); + b.end(); + + b.declaration(type(int.class), "index", "sourceIndex"); + b.startWhile().string("index >= 0").end().startBlock(); + + b.declaration(type(int.class), "oldStart", "info[index + SOURCE_INFO_OFFSET_START]"); + b.declaration(type(int.class), "oldEnd", "info[index + SOURCE_INFO_OFFSET_LENGTH]"); + b.statement("assert nodeId >= 0 ? oldEnd == PATCH_NODE_SOURCE : oldEnd == PATCH_CURRENT_SOURCE"); + b.statement("info[index + SOURCE_INFO_OFFSET_START] = start"); + b.statement("info[index + SOURCE_INFO_OFFSET_LENGTH] = length"); + b.statement("index = oldStart"); + b.end(); + + return ex; + } + private CodeExecutableElement createDoEmitSourceInfo() { - CodeExecutableElement ex = new CodeExecutableElement(Set.of(PRIVATE), type(void.class), "doEmitSourceInfo"); + CodeExecutableElement ex = new CodeExecutableElement(Set.of(PRIVATE), type(int.class), "doEmitSourceInfo"); ex.addParameter(new CodeVariableElement(type(int.class), "sourceIndex")); ex.addParameter(new CodeVariableElement(type(int.class), "startBci")); ex.addParameter(new CodeVariableElement(type(int.class), "endBci")); @@ -6920,7 +7005,7 @@ private CodeExecutableElement createDoEmitSourceInfo() { b.startAssert().string("parseSources").end(); b.startIf().string("rootOperationSp == -1").end().startBlock(); - b.returnStatement(); + b.statement("return -1"); b.end(); b.declaration(type(int.class), "index", "sourceInfoIndex"); @@ -6928,6 +7013,7 @@ private CodeExecutableElement createDoEmitSourceInfo() { b.startIf(); b.string("prevIndex >= 0").newLine().startIndention(); + b.string(" && start >= -1 && length >= -1"); b.string(" && (sourceInfo[prevIndex + SOURCE_INFO_OFFSET_SOURCE]) == sourceIndex").newLine(); b.string(" && (sourceInfo[prevIndex + SOURCE_INFO_OFFSET_START]) == start").newLine(); b.string(" && (sourceInfo[prevIndex + SOURCE_INFO_OFFSET_LENGTH]) == length"); @@ -6937,13 +7023,13 @@ private CodeExecutableElement createDoEmitSourceInfo() { b.string(" && (sourceInfo[prevIndex + SOURCE_INFO_OFFSET_END_BCI]) == endBci"); b.end(2).startBlock(); b.lineComment("duplicate entry"); - b.statement("return"); + b.statement("return prevIndex"); b.end(); b.startElseIf().string("(sourceInfo[prevIndex + SOURCE_INFO_OFFSET_END_BCI]) == startBci").end().startBlock(); b.lineComment("contiguous entry"); b.statement("sourceInfo[prevIndex + SOURCE_INFO_OFFSET_END_BCI] = endBci"); - b.statement("return"); + b.statement("return prevIndex"); b.end(); b.end(); // if source, start, length match @@ -6967,6 +7053,8 @@ private CodeExecutableElement createDoEmitSourceInfo() { BytecodeRootNodeElement.this.add(new CodeVariableElement(Set.of(PRIVATE, STATIC, FINAL), type(int.class), "SOURCE_INFO_OFFSET_LENGTH")).createInitBuilder().string("4"); BytecodeRootNodeElement.this.add(new CodeVariableElement(Set.of(PRIVATE, STATIC, FINAL), type(int.class), "SOURCE_INFO_LENGTH")).createInitBuilder().string("5"); + b.statement("return index"); + return ex; } @@ -7175,8 +7263,10 @@ private CodeExecutableElement createToString() { return ex; } - private CodeExecutableElement createDoEmitRoot() { - CodeExecutableElement ex = new CodeExecutableElement(Set.of(PRIVATE), type(void.class), "doEmitRoot"); + private CodeExecutableElement createDoEmitRootSourceSection() { + CodeExecutableElement ex = new CodeExecutableElement(Set.of(PRIVATE), + type(void.class), "doEmitRootSourceSection"); + ex.addParameter(new CodeVariableElement(type(int.class), "nodeId")); CodeTreeBuilder b = ex.createBuilder(); b.startIf().string("!parseSources").end().startBlock(); @@ -7191,9 +7281,9 @@ private CodeExecutableElement createDoEmitRoot() { buildOperationStackWalk(b, "0", () -> { b.startSwitch().string("operationStack[i].operation").end().startBlock(); - b.startCase().tree(createOperationConstant(model.sourceSectionOperation)).end(); - b.startCaseBlock(); - emitCastOperationData(b, model.sourceSectionOperation, "i"); + b.startCase().tree(createOperationConstant(model.sourceSectionPrefixOperation)).end(); + b.startBlock(); + emitCastOperationData(b, model.sourceSectionPrefixOperation, "i"); b.startStatement().startCall("doEmitSourceInfo"); b.string("operationData.sourceIndex"); b.string("0"); @@ -7201,9 +7291,22 @@ private CodeExecutableElement createDoEmitRoot() { b.string("operationData.start"); b.string("operationData.length"); b.end(2); + b.statement("return"); + b.end(); - b.statement("break"); - b.end(); // case epilog + b.startCase().tree(createOperationConstant(model.sourceSectionSuffixOperation)).end(); + b.startBlock(); + emitCastOperationData(b, model.sourceSectionPrefixOperation, "i"); + b.statement("operationData.nodeId = nodeId"); + b.startAssign("operationData.start").startCall("doEmitSourceInfo"); + b.string("operationData.sourceIndex"); + b.string("0"); + b.string("bci"); + b.string("operationData.start"); + b.string("PATCH_NODE_SOURCE"); + b.end(2); + b.statement("return"); + b.end(); b.end(); // switch }); @@ -7417,9 +7520,9 @@ private void emitUnwindBeforeEarlyExit(CodeTreeBuilder b, OperationKind operatio b.statement("break"); b.end(); // case trycatch - b.startCase().tree(createOperationConstant(model.sourceSectionOperation)).end(); + b.startCase().tree(createOperationConstant(model.sourceSectionPrefixOperation)).end(); b.startBlock(); - emitCastOperationData(b, model.sourceSectionOperation, "i"); + emitCastOperationData(b, model.sourceSectionPrefixOperation, "i"); b.startStatement().startCall("doEmitSourceInfo"); b.string("operationData.sourceIndex"); b.string("operationData.startBci"); @@ -7431,6 +7534,20 @@ private void emitUnwindBeforeEarlyExit(CodeTreeBuilder b, OperationKind operatio b.statement("break"); b.end(); // case source section + b.startCase().tree(createOperationConstant(model.sourceSectionSuffixOperation)).end(); + b.startBlock(); + emitCastOperationData(b, model.sourceSectionSuffixOperation, "i"); + b.startAssign("operationData.start").startCall("doEmitSourceInfo"); + b.string("operationData.sourceIndex"); + b.string("operationData.startBci"); + b.string("bci"); + b.string("operationData.start"); + b.string("PATCH_CURRENT_SOURCE"); + b.end(2); + b.statement("needsRewind = true"); + b.statement("break"); + b.end(); + if (model.enableBlockScoping) { b.startCase().tree(createOperationConstant(model.blockOperation)).end(); b.startBlock(); @@ -7502,9 +7619,16 @@ private void emitRewindBeforeEarlyExit(CodeTreeBuilder b, OperationKind operatio b.statement("break"); b.end(); // case trycatch - b.startCase().tree(createOperationConstant(model.sourceSectionOperation)).end(); + b.startCase().tree(createOperationConstant(model.sourceSectionPrefixOperation)).end(); b.startBlock(); - emitCastOperationData(b, model.sourceSectionOperation, "i"); + emitCastOperationData(b, model.sourceSectionPrefixOperation, "i"); + b.statement("operationData.startBci = bci"); + b.statement("break"); + b.end(); // case source section + + b.startCase().tree(createOperationConstant(model.sourceSectionSuffixOperation)).end(); + b.startBlock(); + emitCastOperationData(b, model.sourceSectionSuffixOperation, "i"); b.statement("operationData.startBci = bci"); b.statement("break"); b.end(); // case source section @@ -7709,8 +7833,9 @@ private CodeTypeElement createDataClass(OperationModel operation) { fields = List.of(// field(type(int.class), "sourceIndex").asFinal(), field(type(int.class), "startBci"), - field(type(int.class), "start").asFinal(), - field(type(int.class), "length").asFinal(), + field(type(int.class), "start"), + field(type(int.class), "length"), + field(type(int.class), "nodeId").withInitializer("-1"), field(type(boolean.class), "producedValue").withInitializer("false"), field(type(int.class), "childBci").withInitializer(UNINIT)); break; @@ -11821,30 +11946,20 @@ private CodeExecutableElement createGetSourceSection() { CodeTreeBuilder b = ex.createBuilder(); b.declaration(arrayOf(type(int.class)), "info", "this.sourceInfo"); - b.startIf().string("info == null").end().startBlock(); + b.startIf().string("info == null || info.length == 0").end().startBlock(); b.startReturn().string("null").end(); b.end(); - b.lineComment("The source table encodes a preorder traversal of a logical tree of source sections (with entries in reverse)."); - b.lineComment("The most specific source section corresponds to the \"lowest\" node in the tree that covers the whole bytecode range."); - b.lineComment("We find this node by iterating the entries from the root until we hit a node that does not cover the bytecode range."); - b.declaration(type(int.class), "mostSpecific", "-1"); - - b.startFor().string("int i = info.length - SOURCE_INFO_LENGTH; i >= 0; i -= SOURCE_INFO_LENGTH").end().startBlock(); + b.declaration(type(int.class), "lastEntry", "info.length - SOURCE_INFO_LENGTH"); b.startIf(); - b.string("info[i + SOURCE_INFO_OFFSET_START_BCI] != 0 ||").startIndention().newLine(); - b.string("info[i + SOURCE_INFO_OFFSET_END_BCI] != bytecodes.length").end(); + b.string("info[lastEntry + SOURCE_INFO_OFFSET_START_BCI] == 0 &&").startIndention().newLine(); + b.string("info[lastEntry + SOURCE_INFO_OFFSET_END_BCI] == bytecodes.length").end(); b.end().startBlock(); - b.statement("break"); - b.end(); // if - b.statement("mostSpecific = i"); // best so far - b.end(); // for - - b.startIf().string("mostSpecific != -1").end().startBlock(); b.startReturn(); - b.string("createSourceSection(sources, info, mostSpecific)"); - b.end(); + b.string("createSourceSection(sources, info, lastEntry)"); b.end(); + b.end(); // if + b.startReturn().string("null").end(); return ex; } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java index 73ed87e357c0..b1500f3aadbf 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLBuiltins.java @@ -292,12 +292,15 @@ Conditional implements a conditional expression (e.g., {@code condition ? thens .setVariadic(true, 0) // .setOperationBeginArguments(new OperationArgument(types.Source, Encoding.OBJECT, "source", "the source object to associate with the enclosed operations")) // .setDynamicOperands(transparentOperationChild()); - m.sourceSectionOperation = m.operation(OperationKind.SOURCE_SECTION, "SourceSection", - """ - SourceSection associates the children in its {@code body} with the source section with the given character {@code index} and {@code length}. - To specify an {@link Source#createUnavailableSection() unavailable source section}, provide {@code -1} for both arguments. - This operation must be (directly or indirectly) enclosed within a Source operation. - """) // + + String sourceDoc = """ + SourceSection associates the children in its {@code body} with the source section with the given character {@code index} and {@code length}. + To specify an {@link Source#createUnavailableSection() unavailable source section}, provide {@code -1} for both arguments. + This operation must be (directly or indirectly) enclosed within a Source operation. + """; + + m.sourceSectionPrefixOperation = m.operation(OperationKind.SOURCE_SECTION, "SourceSectionPrefix", + sourceDoc, "SourceSection") // .setTransparent(true) // .setVariadic(true, 0) // .setOperationBeginArguments( @@ -307,6 +310,17 @@ This operation must be (directly or indirectly) enclosed within a Source operati "the length (in characters) of the source section, or -1 if the section is unavailable")) // .setDynamicOperands(transparentOperationChild()); + m.sourceSectionSuffixOperation = m.operation(OperationKind.SOURCE_SECTION, "SourceSectionSuffix", + sourceDoc, "SourceSection") // + .setTransparent(true) // + .setVariadic(true, 0) // + .setOperationEndArguments( + new OperationArgument(context.getType(int.class), Encoding.INTEGER, "index", + "the starting character index of the source section, or -1 if the section is unavailable"), + new OperationArgument(context.getType(int.class), Encoding.INTEGER, "length", + "the length (in characters) of the source section, or -1 if the section is unavailable")) // + .setDynamicOperands(transparentOperationChild()); + if (m.enableTagInstrumentation) { m.tagEnterInstruction = m.instruction(InstructionKind.TAG_ENTER, "tag.enter", m.signature(void.class)); m.tagEnterInstruction.addImmediate(ImmediateKind.TAG_NODE, "tag"); diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java index 89d571645cd9..354f9fb4298a 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/BytecodeDSLModel.java @@ -157,7 +157,8 @@ public BytecodeDSLModel(ProcessorContext context, TypeElement templateType, Anno public OperationModel ifThenOperation; public OperationModel ifThenElseOperation; public OperationModel returnOperation; - public OperationModel sourceSectionOperation; + public OperationModel sourceSectionPrefixOperation; + public OperationModel sourceSectionSuffixOperation; public OperationModel sourceOperation; public CustomOperationModel prolog = null; public CustomOperationModel epilogReturn = null; @@ -279,11 +280,15 @@ public InstructionModel[] getInvalidateInstructions() { } public OperationModel operation(OperationKind kind, String name, String javadoc) { + return operation(kind, name, javadoc, name); + } + + public OperationModel operation(OperationKind kind, String name, String javadoc, String builderName) { if (operations.containsKey(name)) { addError("Multiple operations declared with name %s. Operation names must be distinct.", name); return null; } - OperationModel op = new OperationModel(this, operationId++, kind, name, javadoc); + OperationModel op = new OperationModel(this, operationId++, kind, name, builderName, javadoc); operations.put(name, op); return op; } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java index 37df223856c5..2d8de307d769 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/bytecode/model/OperationModel.java @@ -138,6 +138,10 @@ public boolean hasConstantOperands() { public final int id; public final OperationKind kind; public final String name; + /* + * Name used to generate builder methods. + */ + public final String builderName; public final String javadoc; /** @@ -179,11 +183,12 @@ public boolean hasConstantOperands() { // A unique identifier for instrumentation instructions. public int instrumentationIndex; - public OperationModel(BytecodeDSLModel parent, int id, OperationKind kind, String name, String javadoc) { + public OperationModel(BytecodeDSLModel parent, int id, OperationKind kind, String name, String builderName, String javadoc) { this.parent = parent; this.id = id; this.kind = kind; this.name = name; + this.builderName = builderName; this.javadoc = javadoc; } @@ -326,4 +331,10 @@ public boolean requiresStackBalancing() { public String getConstantName() { return name.toUpperCase(); } + + @Override + public String toString() { + return "OperationModel [id=" + id + ", kind=" + kind + ", name=" + name + "]"; + } + }