Skip to content

Commit 5c9f431

Browse files
committed
Call Truffle::Boot.INTERACTIVE_BINDING.eval(code) for interactive sources
* Fixes the ignored test and problems with Truffle-level caching. * The caching is done correctly in Binding#eval, updating and checking the Binding's FrameDescriptor. * Significantly simpler and easier to understand.
1 parent 005ac87 commit 5c9f431

File tree

3 files changed

+27
-23
lines changed

3 files changed

+27
-23
lines changed

src/main/java/org/truffleruby/language/RubyParsingRequestNode.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
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.MaterializedFrame;
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;
22+
import org.jcodings.specific.UTF8Encoding;
2323
import org.truffleruby.Layouts;
2424
import org.truffleruby.RubyContext;
2525
import org.truffleruby.RubyLanguage;
26+
import org.truffleruby.core.rope.Rope;
27+
import org.truffleruby.core.string.StringOperations;
2628
import org.truffleruby.language.arguments.RubyArguments;
2729
import org.truffleruby.language.backtrace.InternalRootNode;
2830
import org.truffleruby.language.methods.DeclarationContext;
@@ -38,19 +40,21 @@ public class RubyParsingRequestNode extends RubyBaseRootNode implements Internal
3840

3941
private final TruffleLanguage.ContextReference<RubyContext> contextReference;
4042
private final Source source;
43+
private final boolean interactive;
4144
private final String[] argumentNames;
4245

46+
@CompilationFinal private Rope sourceRope;
4347
@CompilationFinal private RubyContext cachedContext;
4448
@CompilationFinal private DynamicObject mainObject;
4549
@CompilationFinal private InternalMethod method;
46-
@CompilationFinal private MaterializedFrame declarationFrame;
4750

4851
@Child private DirectCallNode callNode;
4952

5053
public RubyParsingRequestNode(RubyLanguage language, Source source, String[] argumentNames) {
5154
super(language, null, null);
52-
contextReference = language.getContextReference();
55+
this.contextReference = language.getContextReference();
5356
this.source = source;
57+
this.interactive = source.isInteractive();
5458
this.argumentNames = argumentNames;
5559
}
5660

@@ -59,6 +63,19 @@ public Object execute(VirtualFrame frame) {
5963
printTimeMetric("before-script");
6064
final RubyContext context = contextReference.get();
6165

66+
if (interactive) {
67+
if (sourceRope == null) {
68+
CompilerDirectives.transferToInterpreterAndInvalidate();
69+
sourceRope = StringOperations.encodeRope(source.getCharacters().toString(), UTF8Encoding.INSTANCE);
70+
}
71+
72+
// Just do Truffle::Boot.INTERACTIVE_BINDING.eval(code) for interactive sources.
73+
// It's the semantics we want and takes care of caching correctly based on the Binding's FrameDescriptor.
74+
final Object interactiveBinding = Layouts.MODULE.getFields(context.getCoreLibrary().getTruffleBootModule())
75+
.getConstant("INTERACTIVE_BINDING").getValue();
76+
return context.send(interactiveBinding, "eval", StringOperations.createString(context, sourceRope));
77+
}
78+
6279
if (cachedContext == null) {
6380
CompilerDirectives.transferToInterpreterAndInvalidate();
6481
cachedContext = context;
@@ -69,20 +86,12 @@ public Object execute(VirtualFrame frame) {
6986

7087
final TranslatorDriver translator = new TranslatorDriver(context);
7188

72-
final boolean interactive = source.isInteractive();
73-
74-
if (interactive) {
75-
declarationFrame = Layouts.BINDING.getFrame(
76-
(DynamicObject) Layouts.MODULE.getFields(context.getCoreLibrary().getTruffleBootModule())
77-
.getConstant("INTERACTIVE_BINDING").getValue());
78-
}
79-
8089
final RubyRootNode rootNode = translator.parse(
8190
new RubySource(source),
82-
interactive ? ParserContext.EVAL : ParserContext.TOP_LEVEL,
91+
ParserContext.TOP_LEVEL,
8392
argumentNames,
84-
declarationFrame,
85-
!interactive,
93+
null,
94+
true,
8695
null);
8796

8897
final RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
@@ -98,7 +107,7 @@ public Object execute(VirtualFrame frame) {
98107
}
99108

100109
final Object value = callNode.call(RubyArguments.pack(
101-
declarationFrame,
110+
null,
102111
null,
103112
method,
104113
null,

src/main/ruby/truffleruby/core/truffle/boot.rb

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,10 @@
1212
# as the TOPLEVEL_BINDING should be empty until the main script is executed.
1313
TOPLEVEL_BINDING = binding
1414

15-
module Truffle::Boot
16-
17-
def self.create_interactive_binding
18-
binding
19-
end
15+
# The Binding used for sharing top-level locals of interactive Sources
16+
Truffle::Boot::INTERACTIVE_BINDING = binding
2017

21-
INTERACTIVE_BINDING = create_interactive_binding
18+
module Truffle::Boot
2219

2320
def self.check_syntax(source_or_file)
2421
inner_check_syntax source_or_file

src/test/java/org/truffleruby/PolyglotInteropTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.graalvm.polyglot.Context;
1313
import org.graalvm.polyglot.Source;
1414
import org.graalvm.polyglot.Value;
15-
import org.junit.Ignore;
1615
import org.junit.Test;
1716
import org.truffleruby.fixtures.FluidForce;
1817
import org.truffleruby.shared.TruffleRuby;
@@ -175,7 +174,6 @@ public void testLocalVariablesSharedBetweenInteractiveEval() {
175174
}
176175
}
177176

178-
@Ignore
179177
@Test
180178
public void testLocalVariablesSharedBetweenInteractiveEvalChangesParsing() {
181179
try (Context polyglot = Context.newBuilder()

0 commit comments

Comments
 (0)