Skip to content

Commit 8dc0f4f

Browse files
committed
Fix reassignment in static LexicalScope when loading the same file multiple times
* Fixes #2408
1 parent afa87db commit 8dc0f4f

File tree

3 files changed

+31
-1
lines changed

3 files changed

+31
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Bug fixes:
1212
* Fix `Dir.mkdir` error handling for `Pathname` paths (#2397).
1313
* `BasicSocket#*_nonblock(exception: false)` now only return `:wait_readable/:wait_writable` for `EAGAIN`/`EWOULDBLOCK` like MRI (#2400).
1414
* Fix issue with `strspn` used in the `date` C extension compiled as a macro on older glibc and then missing the `__strspn_c1` symbol on newer glibc (#2406).
15+
* Fix constant lookup when loading the same file multiple times (#2408).
1516

1617
Compatibility:
1718

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
package org.truffleruby.language;
1111

12+
import com.oracle.truffle.api.CompilerDirectives;
1213
import org.truffleruby.core.module.RubyModule;
1314

1415
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
@@ -44,7 +45,20 @@ public RubyModule getLiveModule() {
4445
}
4546

4647
public void unsafeSetLiveModule(RubyModule liveModule) {
47-
this.liveModule = liveModule;
48+
final RubyModule previous = this.liveModule;
49+
if (previous == null) {
50+
this.liveModule = liveModule;
51+
} else if (previous == liveModule) {
52+
// Can be the same module for code like 2.times { module M } (at the toplevel)
53+
} else {
54+
throw CompilerDirectives.shouldNotReachHere(
55+
"Overriding module for static LexicalScope, old=" + moduleToDebugString(previous) +
56+
", new=" + moduleToDebugString(liveModule));
57+
}
58+
}
59+
60+
private static String moduleToDebugString(RubyModule module) {
61+
return module + "@" + Integer.toHexString(module.hashCode());
4862
}
4963

5064
@Override

src/main/java/org/truffleruby/language/loader/CodeLoader.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,16 @@
3434
import com.oracle.truffle.api.nodes.IndirectCallNode;
3535
import com.oracle.truffle.api.nodes.Node;
3636

37+
import java.util.Set;
38+
import java.util.concurrent.ConcurrentHashMap;
39+
3740
public class CodeLoader {
3841

3942
private final RubyLanguage language;
4043
private final RubyContext context;
4144

45+
private final Set<String> alreadyLoadedInContext = ConcurrentHashMap.newKeySet();
46+
4247
public CodeLoader(RubyLanguage language, RubyContext context) {
4348
this.language = language;
4449
this.context = context;
@@ -49,6 +54,16 @@ public RootCallTarget parseTopLevelWithCache(Pair<Source, Rope> sourceRopePair,
4954
final Source source = sourceRopePair.getLeft();
5055
final Rope rope = sourceRopePair.getRight();
5156

57+
final String path = RubyLanguage.getPath(source);
58+
if (language.singleContext && !alreadyLoadedInContext.add(language.getPathRelativeToHome(path))) {
59+
/* Duplicate load of the same file in the same context, we cannot use the cache because it would re-assign
60+
* the live modules of static LexicalScopes and we cannot/do not want to invalidate static LexicalScopes, so
61+
* there the static lexical scope and its module are constants and need no checks in single context (e.g.,
62+
* in LookupConstantWithLexicalScopeNode). */
63+
final RubySource rubySource = new RubySource(source, path, rope);
64+
return parse(rubySource, ParserContext.TOP_LEVEL, null, context.getRootLexicalScope(), true, currentNode);
65+
}
66+
5267
language.parsingRequestParams.set(new ParsingParameters(currentNode, rope, source));
5368
try {
5469
return (RootCallTarget) context.getEnv().parseInternal(source);

0 commit comments

Comments
 (0)