Skip to content

Commit c3e0eb8

Browse files
committed
[GR-7505] Implement parse(InlineParsingRequest request).
PullRequest: truffleruby/800
2 parents 2118ee6 + 61f0d9d commit c3e0eb8

File tree

8 files changed

+208
-17
lines changed

8 files changed

+208
-17
lines changed

.rubocop.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ AllCops:
99
- 'lib/truffle/socket/mri.rb' # taken from MRI
1010
- 'lib/truffle/ffi/library.rb' # taken from FFI gem
1111
- 'src/test/ruby/types.rb' # deliberately strange code for debugging purposes
12+
- 'src/test/ruby/lexical-context.rb' # deliberately strange code for debugging purposes
1213

1314
# Type 'Layout' (166):
1415
# Supports --auto-correct

src/main/java/org/truffleruby/RubyLanguage.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
import com.oracle.truffle.api.TruffleLogger;
1616
import com.oracle.truffle.api.instrumentation.ProvidedTags;
1717
import com.oracle.truffle.api.instrumentation.StandardTags;
18+
import com.oracle.truffle.api.nodes.ExecutableNode;
1819
import com.oracle.truffle.api.object.DynamicObject;
1920
import com.oracle.truffle.api.source.SourceSection;
2021
import org.graalvm.options.OptionDescriptor;
2122
import org.graalvm.options.OptionDescriptors;
2223
import org.truffleruby.cext.ValueWrapper;
2324
import org.truffleruby.core.kernel.TraceManager;
24-
import org.truffleruby.language.LazyRubyRootNode;
25+
import org.truffleruby.language.RubyInlineParsingRequestNode;
26+
import org.truffleruby.language.RubyParsingRequestNode;
2527
import org.truffleruby.language.NotProvided;
2628
import org.truffleruby.language.RubyGuards;
2729
import org.truffleruby.shared.BuildInformationImpl;
@@ -121,8 +123,13 @@ public static ContextReference<RubyContext> getCurrentContextReference() {
121123
}
122124

123125
@Override
124-
protected RootCallTarget parse(ParsingRequest request) throws Exception {
125-
return Truffle.getRuntime().createCallTarget(new LazyRubyRootNode(this, null, null, request.getSource(), request.getArgumentNames()));
126+
protected RootCallTarget parse(ParsingRequest request) {
127+
return Truffle.getRuntime().createCallTarget(new RubyParsingRequestNode(this, request.getSource(), request.getArgumentNames().toArray(new String[]{})));
128+
}
129+
130+
@Override
131+
protected ExecutableNode parse(InlineParsingRequest request) {
132+
return new RubyInlineParsingRequestNode(this, request.getSource(), request.getFrame());
126133
}
127134

128135
@SuppressWarnings("deprecation")
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 1.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.language;
11+
12+
import com.oracle.truffle.api.CompilerDirectives;
13+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
14+
import com.oracle.truffle.api.RootCallTarget;
15+
import com.oracle.truffle.api.Truffle;
16+
import com.oracle.truffle.api.TruffleLanguage;
17+
import com.oracle.truffle.api.frame.MaterializedFrame;
18+
import com.oracle.truffle.api.frame.VirtualFrame;
19+
import com.oracle.truffle.api.nodes.DirectCallNode;
20+
import com.oracle.truffle.api.nodes.ExecutableNode;
21+
import com.oracle.truffle.api.source.Source;
22+
23+
import org.truffleruby.RubyContext;
24+
import org.truffleruby.RubyLanguage;
25+
import org.truffleruby.language.arguments.RubyArguments;
26+
import org.truffleruby.language.methods.DeclarationContext;
27+
import org.truffleruby.language.methods.InternalMethod;
28+
import org.truffleruby.language.methods.SharedMethodInfo;
29+
import org.truffleruby.language.objects.shared.SharedObjects;
30+
import org.truffleruby.parser.ParserContext;
31+
import org.truffleruby.parser.RubySource;
32+
import org.truffleruby.parser.TranslatorDriver;
33+
34+
public class RubyInlineParsingRequestNode extends ExecutableNode {
35+
36+
private final TruffleLanguage.ContextReference<RubyContext> contextReference;
37+
private final Source source;
38+
private final MaterializedFrame currentFrame;
39+
40+
@CompilationFinal private RubyContext cachedContext;
41+
@CompilationFinal private InternalMethod method;
42+
43+
@Child private DirectCallNode callNode;
44+
45+
public RubyInlineParsingRequestNode(RubyLanguage language, Source source, MaterializedFrame currentFrame) {
46+
super(language);
47+
contextReference = language.getContextReference();
48+
this.source = source;
49+
this.currentFrame = currentFrame;
50+
}
51+
52+
@Override
53+
public Object execute(VirtualFrame frame) {
54+
final RubyContext context = contextReference.get();
55+
56+
if (cachedContext == null) {
57+
CompilerDirectives.transferToInterpreterAndInvalidate();
58+
cachedContext = context;
59+
}
60+
61+
if (callNode == null || context != cachedContext) {
62+
CompilerDirectives.transferToInterpreterAndInvalidate();
63+
64+
final TranslatorDriver translator = new TranslatorDriver(context);
65+
66+
// We use the current frame as the lexical scope to parse, but then we may run with a new frame in the future
67+
68+
final RubyRootNode rootNode = translator.parse(new RubySource(source), ParserContext.INLINE, null, currentFrame, false, null);
69+
70+
final RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
71+
72+
callNode = insert(Truffle.getRuntime().createDirectCallNode(callTarget));
73+
callNode.forceInlining();
74+
75+
final SharedMethodInfo sharedMethodInfo = rootNode.getSharedMethodInfo();
76+
method = new InternalMethod(context, sharedMethodInfo, sharedMethodInfo.getLexicalScope(), DeclarationContext.topLevel(context),
77+
sharedMethodInfo.getName(), context.getCoreLibrary().getObjectClass(), Visibility.PUBLIC, callTarget);
78+
}
79+
80+
// We run the Ruby code as if it was written in a block
81+
82+
final Object[] arguments = RubyArguments.pack(
83+
frame.materialize(),
84+
null,
85+
method,
86+
null,
87+
RubyArguments.getSelf(frame),
88+
RubyArguments.getBlock(frame),
89+
new Object[]{});
90+
91+
final Object value = callNode.call(arguments);
92+
93+
// The return value will be leaked to Java, share it.
94+
if (context.getOptions().SHARED_OBJECTS_ENABLED) {
95+
SharedObjects.writeBarrier(context, value);
96+
}
97+
98+
return value;
99+
}
100+
101+
}

src/main/java/org/truffleruby/language/LazyRubyRootNode.java renamed to src/main/java/org/truffleruby/language/RubyParsingRequestNode.java

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved. This
2+
* Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved. This
33
* code is released under a tri EPL/GPL/LGPL license. You can use it,
44
* redistribute it and/or modify it under the terms of the:
55
*
@@ -15,13 +15,10 @@
1515
import com.oracle.truffle.api.RootCallTarget;
1616
import com.oracle.truffle.api.Truffle;
1717
import com.oracle.truffle.api.TruffleLanguage;
18-
import com.oracle.truffle.api.frame.FrameDescriptor;
1918
import com.oracle.truffle.api.frame.VirtualFrame;
2019
import com.oracle.truffle.api.nodes.DirectCallNode;
2120
import com.oracle.truffle.api.object.DynamicObject;
2221
import com.oracle.truffle.api.source.Source;
23-
import com.oracle.truffle.api.source.SourceSection;
24-
2522
import org.truffleruby.RubyContext;
2623
import org.truffleruby.RubyLanguage;
2724
import org.truffleruby.language.arguments.RubyArguments;
@@ -30,28 +27,25 @@
3027
import org.truffleruby.language.methods.InternalMethod;
3128
import org.truffleruby.language.methods.SharedMethodInfo;
3229
import org.truffleruby.language.objects.shared.SharedObjects;
33-
import org.truffleruby.shared.Metrics;
3430
import org.truffleruby.parser.ParserContext;
3531
import org.truffleruby.parser.RubySource;
3632
import org.truffleruby.parser.TranslatorDriver;
33+
import org.truffleruby.shared.Metrics;
3734

38-
import java.util.List;
39-
40-
public class LazyRubyRootNode extends RubyBaseRootNode implements InternalRootNode {
35+
public class RubyParsingRequestNode extends RubyBaseRootNode implements InternalRootNode {
4136

4237
private final TruffleLanguage.ContextReference<RubyContext> contextReference;
4338
private final Source source;
44-
private final List<String> argumentNames;
39+
private final String[] argumentNames;
4540

4641
@CompilationFinal private RubyContext cachedContext;
4742
@CompilationFinal private DynamicObject mainObject;
4843
@CompilationFinal private InternalMethod method;
4944

5045
@Child private DirectCallNode callNode;
5146

52-
public LazyRubyRootNode(RubyLanguage language, SourceSection sourceSection, FrameDescriptor frameDescriptor, Source source,
53-
List<String> argumentNames) {
54-
super(language, frameDescriptor, sourceSection);
47+
public RubyParsingRequestNode(RubyLanguage language, Source source, String[] argumentNames) {
48+
super(language, null, null);
5549
contextReference = language.getContextReference();
5650
this.source = source;
5751
this.argumentNames = argumentNames;
@@ -72,8 +66,7 @@ public Object execute(VirtualFrame frame) {
7266

7367
final TranslatorDriver translator = new TranslatorDriver(context);
7468

75-
final String[] argumentsArray = argumentNames.toArray(new String[argumentNames.size()]);
76-
final RubyRootNode rootNode = translator.parse(new RubySource(source), ParserContext.TOP_LEVEL, argumentsArray, null, true, null);
69+
final RubyRootNode rootNode = translator.parse(new RubySource(source), ParserContext.TOP_LEVEL, argumentNames, null, true, null);
7770

7871
final RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
7972

src/test/java/org/truffleruby/RubyDebugTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,26 @@ public void testEvalThrow() throws Throwable {
217217
assertExecutedOK("OK");
218218
}
219219

220+
@Test
221+
public void testInlineModifiesFrame() throws Throwable {
222+
Source source = getSource("src/test/ruby/modify.rb");
223+
run.addLast(() -> {
224+
assertNull(suspendedEvent);
225+
assertNotNull(debuggerSession);
226+
debuggerSession.suspendNextExecution();
227+
});
228+
stepOver(2);
229+
run.addLast(() -> {
230+
assertNotNull(suspendedEvent);
231+
suspendedEvent.getTopStackFrame().eval("a = 22");
232+
run.removeFirst().run();
233+
});
234+
continueExecution();
235+
performWork();
236+
Assert.assertEquals(22 + 2 + 3, context.eval(source).asInt());
237+
assertExecutedOK("OK");
238+
}
239+
220240
@Ignore
221241
@Test
222242
public void testProperties() throws Throwable {

src/test/java/org/truffleruby/RubyTCKLanguageProvider.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010
package org.truffleruby;
1111

1212
import org.graalvm.polyglot.Context;
13+
import org.graalvm.polyglot.PolyglotException;
1314
import org.graalvm.polyglot.Source;
1415
import org.graalvm.polyglot.Value;
16+
import org.graalvm.polyglot.tck.InlineSnippet;
1517
import org.graalvm.polyglot.tck.LanguageProvider;
1618
import org.graalvm.polyglot.tck.ResultVerifier;
1719
import org.graalvm.polyglot.tck.Snippet;
1820
import org.graalvm.polyglot.tck.TypeDescriptor;
1921
import org.junit.Assert;
22+
import org.truffleruby.language.control.RaiseException;
2023
import org.truffleruby.shared.TruffleRuby;
2124

2225
import java.io.File;
@@ -142,6 +145,39 @@ public Collection<? extends Snippet> createScripts(Context context) {
142145
return Collections.unmodifiableList(res);
143146
}
144147

148+
@Override
149+
public Collection<? extends InlineSnippet> createInlineScripts(Context context) {
150+
List<InlineSnippet> res = new ArrayList<>();
151+
res.add(createInlineSnippet(
152+
context,
153+
getSource("src/test/ruby/lexical-context.rb"),
154+
16,
155+
"a + b + c",
156+
14 + 2 + 6));
157+
res.add(createInlineSnippet(
158+
context,
159+
getSource("src/test/ruby/lexical-context.rb"),
160+
16,
161+
"binding.local_variable_get(:a) + binding.local_variable_get(:b) + binding.local_variable_get(:c)",
162+
14 + 2 + 6));
163+
return Collections.unmodifiableList(res);
164+
}
165+
166+
private InlineSnippet createInlineSnippet(Context context, Source mainSource, int line, String inlineSource, int expected) {
167+
final Snippet mainSnippet = Snippet.newBuilder(mainSource.getName(), context.eval(mainSource), TypeDescriptor.ANY).build();
168+
169+
return InlineSnippet.newBuilder(mainSnippet, inlineSource)
170+
.locationPredicate(sourceSection ->
171+
sourceSection.getSource().getName().endsWith(mainSource.getName()) && sourceSection.getStartLine() == line)
172+
.resultVerifier(snippetRun -> {
173+
final PolyglotException exception = snippetRun.getException();
174+
if (exception != null) {
175+
throw exception;
176+
}
177+
Assert.assertEquals(expected, snippetRun.getResult().asInt());
178+
}).build();
179+
}
180+
145181
private Snippet createValueConstructor(Context context, String value, TypeDescriptor type) {
146182
return Snippet.newBuilder(value, context.eval(getId(), String.format("-> { %s }", value)), type).build();
147183
}

src/test/ruby/lexical-context.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. This
2+
# code is released under a tri EPL/GPL/LGPL license. You can use it,
3+
# redistribute it and/or modify it under the terms of the:
4+
#
5+
# Eclipse Public License version 1.0, or
6+
# GNU General Public License version 2, or
7+
# GNU Lesser General Public License version 2.1.
8+
9+
# Beware, RubyTCKLanguageProvider use hard-coded line numbers from this file!
10+
11+
-> {
12+
a = 14
13+
3.times do
14+
b = 2
15+
[6].each do |c|
16+
x = c
17+
end
18+
end
19+
}

src/test/ruby/modify.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. This
2+
# code is released under a tri EPL/GPL/LGPL license. You can use it,
3+
# redistribute it and/or modify it under the terms of the:
4+
#
5+
# Eclipse Public License version 1.0, or
6+
# GNU General Public License version 2, or
7+
# GNU Lesser General Public License version 2.1.
8+
9+
# Beware, RubyDebugTest use hard-coded line numbers from this file!
10+
11+
a = 14
12+
b = 2
13+
c = 3
14+
a + b + c

0 commit comments

Comments
 (0)