Skip to content

Commit a9e11af

Browse files
committed
[GR-58572] When searching for a nearest node, we take unavailable source sections into account.
PullRequest: graal/21012
2 parents 40d7936 + d4eb908 commit a9e11af

File tree

3 files changed

+116
-7
lines changed

3 files changed

+116
-7
lines changed

truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/AbstractInstrumentationTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,12 @@ protected final com.oracle.truffle.api.source.SourceSection getSectionImpl(Sourc
182182

183183
protected final SourceSection createSection(Source source, int charIndex, int length) {
184184
com.oracle.truffle.api.source.Source sourceImpl = getSourceImpl(source);
185-
com.oracle.truffle.api.source.SourceSection sectionImpl = sourceImpl.createSection(charIndex, length);
185+
com.oracle.truffle.api.source.SourceSection sectionImpl;
186+
if (charIndex < 0 && length < 0) {
187+
sectionImpl = sourceImpl.createUnavailableSection();
188+
} else {
189+
sectionImpl = sourceImpl.createSection(charIndex, length);
190+
}
186191
return (SourceSection) TestAccessor.ACCESSOR.engineAccess().createPolyglotSourceSection(getPolyglotEngine(), source, sectionImpl);
187192
}
188193

truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/NearestSectionFilterTest.java

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,39 @@
4040
*/
4141
package com.oracle.truffle.api.instrumentation.test;
4242

43-
import com.oracle.truffle.api.CompilerDirectives;
44-
import com.oracle.truffle.api.frame.VirtualFrame;
4543
import java.io.IOException;
4644
import java.util.ArrayList;
4745
import java.util.List;
4846

47+
import com.oracle.truffle.api.CallTarget;
48+
import com.oracle.truffle.api.CompilerDirectives;
49+
import com.oracle.truffle.api.TruffleLanguage;
50+
import com.oracle.truffle.api.frame.VirtualFrame;
4951
import com.oracle.truffle.api.instrumentation.EventBinding;
5052
import com.oracle.truffle.api.instrumentation.EventContext;
5153
import com.oracle.truffle.api.instrumentation.ExecutionEventNode;
5254
import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
55+
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
56+
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
5357
import com.oracle.truffle.api.instrumentation.LoadSourceSectionEvent;
5458
import com.oracle.truffle.api.instrumentation.LoadSourceSectionListener;
5559
import com.oracle.truffle.api.instrumentation.NearestSectionFilter;
60+
import com.oracle.truffle.api.instrumentation.ProbeNode;
5661
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
62+
import com.oracle.truffle.api.instrumentation.StandardTags;
5763
import com.oracle.truffle.api.instrumentation.StandardTags.ExpressionTag;
5864
import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag;
65+
import com.oracle.truffle.api.instrumentation.Tag;
66+
import com.oracle.truffle.api.nodes.Node;
67+
import com.oracle.truffle.api.nodes.RootNode;
68+
import com.oracle.truffle.api.test.polyglot.ProxyLanguage;
5969

6070
import static org.junit.Assert.assertEquals;
6171
import static org.junit.Assert.assertNotEquals;
6272
import org.junit.Test;
6373

74+
import org.graalvm.polyglot.Context;
75+
import org.graalvm.polyglot.Source;
6476
import org.graalvm.polyglot.SourceSection;
6577
import org.graalvm.polyglot.Value;
6678

@@ -218,6 +230,75 @@ private void entered() {
218230
assertEquals(executedEvents.toString(), 0, executedEvents.size());
219231
}
220232

233+
@Test
234+
public void testUnavailableSection() {
235+
setupEnv(Context.create(), new ProxyLanguage() {
236+
237+
@Override
238+
protected CallTarget parse(TruffleLanguage.ParsingRequest request) throws Exception {
239+
com.oracle.truffle.api.source.Source source = request.getSource();
240+
return new RootNode(languageInstance) {
241+
@Node.Child private NearestStatementTestNode child = new NearestStatementTestNode(source.createSection(1), StandardTags.RootTag.class);
242+
243+
@Override
244+
public Object execute(VirtualFrame frame) {
245+
return child.execute(frame);
246+
}
247+
248+
@Override
249+
public com.oracle.truffle.api.source.SourceSection getSourceSection() {
250+
return source.createUnavailableSection();
251+
}
252+
}.getCallTarget();
253+
}
254+
});
255+
Source source = Source.create(ProxyLanguage.ID, "a");
256+
context.eval(source);
257+
checkLoadEvents(NearestSectionFilter.newBuilder(1, 0).build(), SourceSectionFilter.ANY, createSection(source, -1, -1));
258+
SourceSection textSection = createSection(source, 0, 1);
259+
checkLoadEvents(NearestSectionFilter.newBuilder(1, 0).tagIs(ExpressionTag.class).build(), SourceSectionFilter.ANY, textSection);
260+
checkLoadEvents(NearestSectionFilter.newBuilder(1, 1).tagIs(ExpressionTag.class).build(), SourceSectionFilter.ANY, textSection);
261+
checkLoadEvents(NearestSectionFilter.newBuilder(2, 0).tagIs(ExpressionTag.class).build(), SourceSectionFilter.ANY, textSection);
262+
}
263+
264+
@GenerateWrapper
265+
static class NearestStatementTestNode extends Node implements InstrumentableNode {
266+
267+
private final com.oracle.truffle.api.source.SourceSection sourceSection;
268+
private final Class<?> tag;
269+
@Node.Child private NearestStatementTestNode child;
270+
271+
NearestStatementTestNode(com.oracle.truffle.api.source.SourceSection sourceSection, Class<?> tag) {
272+
this.sourceSection = StandardTags.RootTag.class.equals(tag) ? sourceSection.getSource().createUnavailableSection() : sourceSection;
273+
this.tag = tag;
274+
this.child = StandardTags.RootTag.class.equals(tag) ? new NearestStatementTestNode(sourceSection, StandardTags.ExpressionTag.class) : null;
275+
}
276+
277+
@Override
278+
public boolean isInstrumentable() {
279+
return true;
280+
}
281+
282+
@Override
283+
public InstrumentableNode.WrapperNode createWrapper(ProbeNode probe) {
284+
return new NearestStatementTestNodeWrapper(sourceSection, tag, this, probe);
285+
}
286+
287+
public Object execute(@SuppressWarnings("unused") VirtualFrame frame) {
288+
return child != null ? child.execute(frame) : true;
289+
}
290+
291+
@Override
292+
public boolean hasTag(Class<? extends Tag> t) {
293+
return this.tag.equals(t);
294+
}
295+
296+
@Override
297+
public com.oracle.truffle.api.source.SourceSection getSourceSection() {
298+
return sourceSection;
299+
}
300+
}
301+
221302
private void checkLoadEvents(NearestSectionFilter nearestFilter, SourceSectionFilter baseFilter, SourceSection... expectedSections) {
222303
final List<LoadSourceSectionEvent> visitedEvents = new ArrayList<>(expectedSections.length);
223304
LoadSourceSectionListener listener = new LoadSourceSectionListener() {

truffle/src/com.oracle.truffle.api.instrumentation/src/com/oracle/truffle/api/instrumentation/NearestNodesCollector.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@
6060
* The {@link #loadedSection(Node, SourceSection, SourceSection)} is called with the visited
6161
* {@link Node} and {@link SourceSection}. Every call updates data about the nearest node/section.
6262
* These data are stored in four categories: <code>exact</code>, <code>contains</code>,
63-
* <code>previous</code> and <code>next</code>. After the AST traversal is done, the
64-
* {@link #getNearest(Set)} is called from post-visit. It uses the collected data to find a
65-
* <code>contextNode</code>. If the exact node was matched, it is returned. When not,
63+
* <code>previous</code> and <code>next</code>. The comparisons between source sections and code
64+
* positions are not totally ordered when some information is missing (section is unavailable, line
65+
* is set but not a column, etc.) After the AST traversal is done, the {@link #getNearest(Set)} is
66+
* called from post-visit. It uses the collected data to find a <code>contextNode</code>. If the
67+
* exact node was matched, it is returned. When not,
6668
* {@link InstrumentableNode#findNearestNodeAt(int, int, Set)} is called on the
6769
* <code>contextNode</code> to provide the final nearest node.
6870
* <p>
@@ -520,7 +522,6 @@ static final class Position {
520522
this.line = line;
521523
this.column = column;
522524
this.offset = offset;
523-
assert offset >= 0 || line >= 1 : toString();
524525
}
525526

526527
static Position startOf(SourceSection section) {
@@ -547,6 +548,10 @@ static Position endOf(SourceSection section) {
547548
}
548549

549550
boolean isIn(SourceSection section) {
551+
if (!isAvailable() || !section.isAvailable()) {
552+
// when either is not available, it can be in
553+
return true;
554+
}
550555
if (offset >= 0 && section.hasCharIndex()) {
551556
return section.getCharIndex() <= offset && offset < section.getCharEndIndex();
552557
}
@@ -604,7 +609,18 @@ public boolean equals(Object obj) {
604609
}
605610
}
606611

612+
boolean isAvailable() {
613+
return offset >= 0 || line >= 1;
614+
}
615+
607616
boolean isLessThan(Position other) {
617+
if (!isAvailable()) {
618+
// when not available, we're less than any other available
619+
return other.isAvailable();
620+
} else if (!other.isAvailable()) {
621+
// when the other is not available, we can not be less than that
622+
return false;
623+
}
608624
if (this.offset >= 0 && other.offset >= 0) {
609625
return this.offset < other.offset;
610626
}
@@ -615,6 +631,13 @@ boolean isLessThan(Position other) {
615631
}
616632

617633
boolean isLessThanOrEqual(Position other) {
634+
if (!isAvailable()) {
635+
// when not available, we're either less than any other available, or equal
636+
return true;
637+
} else if (!other.isAvailable()) {
638+
// when the other is not available and we are, we are not less than that
639+
return false;
640+
}
618641
if (this.offset >= 0 && other.offset >= 0) {
619642
return this.offset <= other.offset;
620643
}

0 commit comments

Comments
 (0)