Skip to content

Commit cb4c8a9

Browse files
committed
Fix aliasing global variables
PullRequest: truffleruby/720
2 parents 06425b6 + f571154 commit cb4c8a9

21 files changed

+216
-191
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Bug fixes:
1818
* The `shell` standard library can now be `require`-d.
1919
* Fixed a bug where `for` could result in a `NullPointerException` when trying
2020
to assign the iteration variable.
21+
* Existing global variables can now become aliases of other global variables (#1590).
2122

2223
# 1.0 RC 14
2324

ci.jsonnet

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ local composition_environment = utils.add_inclusion_tracking(part_definitions, "
542542

543543
"ruby-test-specs-linux": linux_gate + $.run.test_unit_tck_specs + { timelimit: "35:00" },
544544
"ruby-test-fast-linux": linux_gate + $.run.test_fast + { timelimit: "30:00" }, # To catch missing slow tags
545-
"ruby-test-mri-linux": linux_gate + $.run.test_mri + { timelimit: "25:00" },
545+
"ruby-test-mri-linux": linux_gate + $.run.test_mri + { timelimit: "30:00" },
546546
"ruby-test-integration": linux_gate + $.run.test_integration,
547547
"ruby-test-cexts-linux": linux_gate + $.use.gem_test_pack + $.run.test_cexts,
548548
"ruby-test-gems": linux_gate + $.use.gem_test_pack + $.run.test_gems,
@@ -556,7 +556,7 @@ local composition_environment = utils.add_inclusion_tracking(part_definitions, "
556556
{
557557
local darwin_gate = $.platform.darwin + $.cap.gate + $.jdk.labsjdk8 + $.use.common + $.use.build + { timelimit: "01:00:00" },
558558

559-
"ruby-test-specs-darwin": darwin_gate + $.run.test_unit_tck_specs + { timelimit: "35:00" },
559+
"ruby-test-specs-darwin": darwin_gate + $.run.test_unit_tck_specs + { timelimit: "45:00" },
560560
"ruby-test-mri-darwin": darwin_gate + $.run.test_mri,
561561
"ruby-test-cexts-darwin": darwin_gate + $.use.gem_test_pack + $.run.test_cexts,
562562
} +

spec/ruby/language/alias_spec.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,15 @@ def test_with_check(*args)
244244
}
245245
end
246246
end
247+
248+
describe "The alias keyword" do
249+
it "can create a new global variable, synonym of the original" do
250+
code = '$a = 1; alias $b $a; p [$a, $b]; $b = 2; p [$a, $b]'
251+
ruby_exe(code).should == "[1, 1]\n[2, 2]\n"
252+
end
253+
254+
it "can override an existing global variable and make them synonyms" do
255+
code = '$a = 1; $b = 2; alias $b $a; p [$a, $b]; $b = 3; p [$a, $b]'
256+
ruby_exe(code).should == "[1, 1]\n[3, 3]\n"
257+
end
258+
end

spec/tags/language/alias_tags.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
slow:The alias keyword on top level defines the alias on Object
2+
slow:The alias keyword can create a new global variable, synonym of the original
3+
slow:The alias keyword can override an existing global variable and make them synonyms

src/main/java/org/truffleruby/core/CoreLibrary.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
import org.truffleruby.language.control.JavaException;
5858
import org.truffleruby.language.control.RaiseException;
5959
import org.truffleruby.language.control.TruffleFatalException;
60-
import org.truffleruby.language.globals.GlobalVariableStorage;
60+
import org.truffleruby.language.globals.GlobalVariableReader;
6161
import org.truffleruby.language.globals.GlobalVariables;
6262
import org.truffleruby.language.loader.CodeLoader;
6363
import org.truffleruby.language.loader.FileLoader;
@@ -217,12 +217,12 @@ public class CoreLibrary {
217217
@CompilationFinal private SharedMethodInfo kernelPublicSendInfo;
218218
@CompilationFinal private SharedMethodInfo truffleBootMainInfo;
219219

220-
@CompilationFinal private GlobalVariableStorage loadPathStorage;
221-
@CompilationFinal private GlobalVariableStorage loadedFeaturesStorage;
222-
@CompilationFinal private GlobalVariableStorage debugStorage;
223-
@CompilationFinal private GlobalVariableStorage verboseStorage;
224-
@CompilationFinal private GlobalVariableStorage stdinStorage;
225-
@CompilationFinal private GlobalVariableStorage stderrStorage;
220+
@CompilationFinal private GlobalVariableReader loadPathReader;
221+
@CompilationFinal private GlobalVariableReader loadedFeaturesReader;
222+
@CompilationFinal private GlobalVariableReader debugReader;
223+
@CompilationFinal private GlobalVariableReader verboseReader;
224+
@CompilationFinal private GlobalVariableReader stdinReader;
225+
@CompilationFinal private GlobalVariableReader stderrReader;
226226

227227
private final ConcurrentMap<String, Boolean> patchFiles;
228228

@@ -672,13 +672,12 @@ private Object verbosityOption() {
672672
}
673673

674674
private void findGlobalVariableStorage() {
675-
GlobalVariables globals = globalVariables;
676-
loadPathStorage = globals.getStorage("$LOAD_PATH");
677-
loadedFeaturesStorage = globals.getStorage("$LOADED_FEATURES");
678-
debugStorage = globals.getStorage("$DEBUG");
679-
verboseStorage = globals.getStorage("$VERBOSE");
680-
stdinStorage = globals.getStorage("$stdin");
681-
stderrStorage = globals.getStorage("$stderr");
675+
loadPathReader = globalVariables.getReader("$LOAD_PATH");
676+
loadedFeaturesReader = globalVariables.getReader("$LOADED_FEATURES");
677+
debugReader = globalVariables.getReader("$DEBUG");
678+
verboseReader = globalVariables.getReader("$VERBOSE");
679+
stdinReader = globalVariables.getReader("$stdin");
680+
stderrReader = globalVariables.getReader("$stderr");
682681
}
683682

684683
private void initializeConstants() {
@@ -1094,24 +1093,24 @@ public GlobalVariables getGlobalVariables() {
10941093
}
10951094

10961095
public DynamicObject getLoadPath() {
1097-
return (DynamicObject) loadPathStorage.getValue();
1096+
return (DynamicObject) loadPathReader.getValue(globalVariables);
10981097
}
10991098

11001099
public DynamicObject getLoadedFeatures() {
1101-
return (DynamicObject) loadedFeaturesStorage.getValue();
1100+
return (DynamicObject) loadedFeaturesReader.getValue(globalVariables);
11021101
}
11031102

11041103
public Object getDebug() {
1105-
if (debugStorage != null) {
1106-
return debugStorage.getValue();
1104+
if (debugReader != null) {
1105+
return debugReader.getValue(globalVariables);
11071106
} else {
11081107
return context.getOptions().DEBUG;
11091108
}
11101109
}
11111110

11121111
private Object verbosity() {
1113-
if (verboseStorage != null) {
1114-
return verboseStorage.getValue();
1112+
if (verboseReader != null) {
1113+
return verboseReader.getValue(globalVariables);
11151114
} else {
11161115
return verbosityOption();
11171116
}
@@ -1128,11 +1127,11 @@ public boolean isVerbose() {
11281127
}
11291128

11301129
public Object getStdin() {
1131-
return stdinStorage.getValue();
1130+
return stdinReader.getValue(globalVariables);
11321131
}
11331132

11341133
public Object getStderr() {
1135-
return stderrStorage.getValue();
1134+
return stderrReader.getValue(globalVariables);
11361135
}
11371136

11381137
public DynamicObject getMainObject() {

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -698,16 +698,6 @@ public DynamicObject zeroDivisionError(Node currentNode, ArithmeticException exc
698698
return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, currentNode, exception);
699699
}
700700

701-
// NotImplementedError
702-
703-
@TruffleBoundary
704-
public DynamicObject notImplementedError(String message, Node currentNode) {
705-
DynamicObject exceptionClass = context.getCoreLibrary().getNotImplementedErrorClass();
706-
DynamicObject errorMessage = StringOperations.createString(context, StringOperations.encodeRope(StringUtils.format("Method %s not implemented", message),
707-
UTF8Encoding.INSTANCE));
708-
return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, currentNode, null);
709-
}
710-
711701
// SyntaxError
712702

713703
@TruffleBoundary

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@
144144
import java.nio.file.Paths;
145145
import java.util.ArrayList;
146146
import java.util.Arrays;
147-
import java.util.Collection;
148147
import java.util.List;
149148
import java.util.concurrent.TimeUnit;
150149

@@ -1769,20 +1768,18 @@ protected boolean isDebug(VirtualFrame frame) {
17691768

17701769
}
17711770

1772-
@Primitive(name = "kernel_global_variables", needsSelf = false)
1773-
public abstract static class KernelGlobalVariablesPrimitiveNode extends PrimitiveArrayArgumentsNode {
1771+
@CoreMethod(names = "global_variables", isModuleFunction = true)
1772+
public abstract static class KernelGlobalVariablesNode extends CoreMethodArrayArgumentsNode {
17741773

17751774
@TruffleBoundary
17761775
@Specialization
17771776
public DynamicObject globalVariables() {
1778-
final Collection<String> keys = coreLibrary().getGlobalVariables().keys();
1779-
final Object[] store = new Object[keys.size()];
1780-
int i = 0;
1781-
for (String key : keys) {
1782-
store[i] = getSymbol(key);
1783-
i++;
1777+
final String[] keys = coreLibrary().getGlobalVariables().keys();
1778+
final Object[] store = new Object[keys.length];
1779+
for (int i = 0; i < keys.length; i++) {
1780+
store[i] = getSymbol(keys[i]);
17841781
}
1785-
return createArray(store, store.length);
1782+
return createArray(store);
17861783
}
17871784

17881785
}

src/main/java/org/truffleruby/core/kernel/TruffleKernelNodes.java

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,25 @@
1111

1212
import java.io.IOException;
1313

14+
import com.oracle.truffle.api.dsl.ImportStatic;
1415
import com.oracle.truffle.api.profiles.ConditionProfile;
1516

1617
import org.truffleruby.Layouts;
1718
import org.truffleruby.builtins.CoreClass;
1819
import org.truffleruby.builtins.CoreMethod;
1920
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
2021
import org.truffleruby.builtins.CoreMethodNode;
22+
import org.truffleruby.builtins.Primitive;
2123
import org.truffleruby.core.cast.BooleanCastWithDefaultNodeGen;
2224
import org.truffleruby.core.string.StringOperations;
2325
import org.truffleruby.language.RubyNode;
2426
import org.truffleruby.language.RubyRootNode;
2527
import org.truffleruby.language.control.RaiseException;
26-
import org.truffleruby.language.globals.GlobalVariableStorage;
2728
import org.truffleruby.language.globals.ReadSimpleGlobalVariableNode;
2829
import org.truffleruby.language.globals.WriteSimpleGlobalVariableNode;
2930
import org.truffleruby.language.loader.CodeLoader;
3031
import org.truffleruby.language.loader.FileLoader;
3132
import org.truffleruby.language.methods.DeclarationContext;
32-
import org.truffleruby.language.objects.shared.WriteBarrierNode;
3333
import org.truffleruby.language.threadlocal.FindThreadAndFrameLocalStorageNode;
3434
import org.truffleruby.language.threadlocal.FindThreadAndFrameLocalStorageNodeGen;
3535
import org.truffleruby.parser.ParserContext;
@@ -104,50 +104,30 @@ private RubySource loadFile(String feature) throws IOException {
104104

105105
}
106106

107-
@CoreMethod(names = "global_variable_set", isModuleFunction = true, required = 2)
107+
// Only used internally with a constant literal name, does not trigger hooks
108+
@Primitive(name = "global_variable_set")
109+
@ImportStatic(Layouts.class)
108110
public abstract static class WriteGlobalVariableNode extends CoreMethodArrayArgumentsNode {
111+
109112
@Specialization(guards = { "isRubySymbol(cachedName)", "name == cachedName" }, limit = "1")
110113
public Object write(DynamicObject name, Object value,
111114
@Cached("name") DynamicObject cachedName,
112-
@Cached("create(getStorage(cachedName))") WriteSimpleGlobalVariableNode writeNode) {
115+
@Cached("create(SYMBOL.getString(cachedName))") WriteSimpleGlobalVariableNode writeNode) {
113116
return writeNode.execute(value);
114117
}
115-
116-
@Specialization(guards = "isRubySymbol(name)")
117-
@TruffleBoundary
118-
public Object writeGeneric(DynamicObject name, Object value,
119-
@Cached("create()") WriteBarrierNode writeBarrierNode) {
120-
GlobalVariableStorage storage = getStorage(name);
121-
if (getContext().getSharedObjects().isSharing()) {
122-
writeBarrierNode.executeWriteBarrier(value);
123-
}
124-
storage.setValueInternal(value);
125-
return value;
126-
}
127-
128-
protected GlobalVariableStorage getStorage(DynamicObject name) {
129-
return getContext().getCoreLibrary().getGlobalVariables().getStorage(Layouts.SYMBOL.getString(name));
130-
}
131118
}
132119

133-
@CoreMethod(names = "global_variable_get", isModuleFunction = true, required = 1)
120+
// Only used internally with a constant literal name, does not trigger hooks
121+
@Primitive(name = "global_variable_get")
122+
@ImportStatic(Layouts.class)
134123
public abstract static class ReadGlobalVariableNode extends CoreMethodArrayArgumentsNode {
124+
135125
@Specialization(guards = { "isRubySymbol(cachedName)", "name == cachedName" }, limit = "1")
136126
public Object read(DynamicObject name,
137127
@Cached("name") DynamicObject cachedName,
138-
@Cached("create(getStorage(cachedName))") ReadSimpleGlobalVariableNode readNode) {
128+
@Cached("create(SYMBOL.getString(cachedName))") ReadSimpleGlobalVariableNode readNode) {
139129
return readNode.execute();
140130
}
141-
142-
@TruffleBoundary
143-
@Specialization(guards = "isRubySymbol(name)", replaces = "read")
144-
public Object readGeneric(DynamicObject name) {
145-
return getStorage(name).getValue();
146-
}
147-
148-
protected GlobalVariableStorage getStorage(DynamicObject name) {
149-
return getContext().getCoreLibrary().getGlobalVariables().getStorage(Layouts.SYMBOL.getString(name));
150-
}
151131
}
152132

153133
@CoreMethod(names = "define_hooked_variable_with_is_defined", isModuleFunction = true, required = 4)
@@ -156,7 +136,7 @@ public abstract static class DefineHookedVariableInnerNode extends CoreMethodArr
156136
@TruffleBoundary
157137
@Specialization(guards = { "isRubySymbol(name)", "isRubyProc(getter)", "isRubyProc(setter)" })
158138
public DynamicObject defineHookedVariableInnerNode(DynamicObject name, DynamicObject getter, DynamicObject setter, DynamicObject isDefined) {
159-
getContext().getCoreLibrary().getGlobalVariables().put(Layouts.SYMBOL.getString(name), getter, setter, isDefined);
139+
getContext().getCoreLibrary().getGlobalVariables().define(Layouts.SYMBOL.getString(name), getter, setter, isDefined);
160140
return nil();
161141
}
162142

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,14 @@ private void setArgvGlobals(StringNodes.MakeStringNode makeStringNode) {
165165
String key = global_values[i];
166166
String value = global_values[i + 1];
167167

168-
getContext().getCoreLibrary().getGlobalVariables().put(
168+
getContext().getCoreLibrary().getGlobalVariables().define(
169169
"$" + key,
170170
makeStringNode.executeMake(value, UTF8Encoding.INSTANCE, CodeRange.CR_UNKNOWN));
171171
}
172172

173173
String[] global_flags = getContext().getOptions().ARGV_GLOBAL_FLAGS;
174174
for (String flag : global_flags) {
175-
getContext().getCoreLibrary().getGlobalVariables().put("$" + flag, true);
175+
getContext().getCoreLibrary().getGlobalVariables().define("$" + flag, true);
176176
}
177177
}
178178
}

src/main/java/org/truffleruby/language/globals/AliasGlobalVarNode.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99
*/
1010
package org.truffleruby.language.globals;
1111

12-
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1312
import com.oracle.truffle.api.frame.VirtualFrame;
1413
import org.truffleruby.language.RubyNode;
15-
import org.truffleruby.language.control.RaiseException;
1614

1715
public class AliasGlobalVarNode extends RubyNode {
1816

@@ -26,17 +24,8 @@ public AliasGlobalVarNode(String oldName, String newName) {
2624

2725
@Override
2826
public Object execute(VirtualFrame frame) {
29-
checkExisting();
3027
getContext().getCoreLibrary().getGlobalVariables().alias(oldName, newName);
3128
return nil();
3229
}
3330

34-
@TruffleBoundary
35-
private void checkExisting() {
36-
if (getContext().getCoreLibrary().getGlobalVariables().contains(newName)) {
37-
// TODO CS 4-Apr-18 This exception is non-standard
38-
throw new RaiseException(getContext(), coreExceptions().notImplementedError(String.format("%s is already a global", newName), this), true);
39-
}
40-
}
41-
4231
}

0 commit comments

Comments
 (0)