Skip to content

Commit 2d0e094

Browse files
committed
[Truffle] Initial implementation of Module#autoload.
1 parent a64434d commit 2d0e094

File tree

6 files changed

+94
-28
lines changed

6 files changed

+94
-28
lines changed
Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,18 @@
11
fails:Module#autoload? returns the name of the file that will be autoloaded
22
fails:Module#autoload? returns nil if no file has been registered for a constant
33
fails:Module#autoload registers a file to load the first time the named constant is accessed
4-
fails:Module#autoload sets the autoload constant in the constants table
5-
fails:Module#autoload loads the registered constant when it is accessed
6-
fails:Module#autoload loads the registered constant into a dynamically created class
7-
fails:Module#autoload loads the registered constant into a dynamically created module
84
fails:Module#autoload loads the registered constant when it is opened as a class
95
fails:Module#autoload loads the registered constant when it is opened as a module
10-
fails:Module#autoload loads the registered constant when it is inherited from
11-
fails:Module#autoload loads the registered constant when it is included
12-
fails:Module#autoload does not load the file when the constant is already set
13-
fails:Module#autoload loads a file with .rb extension when passed the name without the extension
146
fails:Module#autoload does not load the file if the file is manually required
157
fails:Module#autoload ignores the autoload request if the file is already loaded
168
fails:Module#autoload retains the autoload even if the request to require fails
17-
fails:Module#autoload allows multiple autoload constants for a single file
18-
fails:Module#autoload runs for an exception condition class and doesn't trample the exception
19-
fails:Module#autoload does not load the file when refering to the constant in defined?
209
fails:Module#autoload does not remove the constant from the constant table if load fails
2110
fails:Module#autoload does not remove the constant from the constant table if the loaded files does not define it
22-
fails:Module#autoload returns 'constant' on refering the constant with defined?()
23-
fails:Module#autoload does not load the file when removing an autoload constant
24-
fails:Module#autoload does not load the file when accessing the constants table of the module
2511
fails:Module#autoload loads the file when opening a module that is the autoloaded constant
26-
fails:Module#autoload loads the file that defines subclass XX::YY < YY and YY is a top level constant
27-
fails:Module#autoload looks up the constant in the scope where it is referred
2812
fails:Module#autoload looks up the constant when in a meta class scope
2913
fails:Module#autoload does NOT raise a NameError when the autoload file did not define the constant and a module is opened with the same name
3014
fails:Module#autoload calls #to_path on non-string filenames
31-
fails:Module#autoload raises an ArgumentError when an empty filename is given
3215
fails:Module#autoload shares the autoload request across dup'ed copies of modules
33-
fails:Module#autoload raises a TypeError if opening a class with a different superclass than the class defined in the autoload file
34-
fails:Module#autoload raises a TypeError if not passed a String or object respodning to #to_path for the filename
3516
fails:Module#autoload calls #to_path on non-String filename arguments
36-
fails:Module#autoload on a frozen module raises a RuntimeError before setting the name
3717
fails:Module#autoload (concurrently) blocks a second thread while a first is doing the autoload
38-
fails:Module#autoload when changing $LOAD_PATH does not reload a file due to a different load path
3918
fails:Module#autoload (concurrently) blocks others threads while doing an autoload
40-
fails:Module#autoload does not call Kernel#require or Kernel#load dynamically

truffle/src/main/java/org/jruby/truffle/nodes/core/ModuleNodes.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@
1212
import com.oracle.truffle.api.CallTarget;
1313
import com.oracle.truffle.api.CompilerDirectives;
1414
import com.oracle.truffle.api.Truffle;
15+
import com.oracle.truffle.api.dsl.CreateCast;
16+
import com.oracle.truffle.api.dsl.NodeChild;
17+
import com.oracle.truffle.api.dsl.NodeChildren;
1518
import com.oracle.truffle.api.dsl.Specialization;
1619
import com.oracle.truffle.api.frame.VirtualFrame;
1720
import com.oracle.truffle.api.nodes.Node.Child;
1821
import com.oracle.truffle.api.source.Source;
1922
import com.oracle.truffle.api.source.SourceSection;
2023

24+
import com.oracle.truffle.api.utilities.ConditionProfile;
2125
import org.jcodings.Encoding;
2226
import org.jruby.runtime.Visibility;
2327
import org.jruby.truffle.nodes.RubyNode;
@@ -384,6 +388,59 @@ public static void attrAccessor(RubyNode currentNode, RubyContext context, Sourc
384388

385389
}
386390

391+
@CoreMethod(names = "autoload", required = 2)
392+
@NodeChildren({
393+
@NodeChild(value = "module"),
394+
@NodeChild(value = "name"),
395+
@NodeChild(value = "filename")
396+
})
397+
public abstract static class AutoloadNode extends RubyNode {
398+
399+
@Child private StringNodes.EmptyNode emptyNode;
400+
private final ConditionProfile invalidConstantName = ConditionProfile.createBinaryProfile();
401+
private final ConditionProfile emptyFilename = ConditionProfile.createBinaryProfile();
402+
403+
public AutoloadNode(RubyContext context, SourceSection sourceSection) {
404+
super(context, sourceSection);
405+
emptyNode = StringNodesFactory.EmptyNodeFactory.create(context, sourceSection, new RubyNode[]{});
406+
}
407+
408+
public AutoloadNode(AutoloadNode prev) {
409+
super(prev);
410+
emptyNode = prev.emptyNode;
411+
}
412+
413+
@CreateCast("filename") public RubyNode coerceFilenameToString(RubyNode filename) {
414+
return ToStrNodeFactory.create(getContext(), getSourceSection(), filename);
415+
}
416+
417+
@Specialization
418+
public RubyNilClass autoload(RubyModule module, RubySymbol name, RubyString filename) {
419+
return autoload(module, name.toString(), filename);
420+
}
421+
422+
@Specialization
423+
public RubyNilClass autoload(RubyModule module, RubyString name, RubyString filename) {
424+
return autoload(module, name.toString(), filename);
425+
}
426+
427+
private RubyNilClass autoload(RubyModule module, String name, RubyString filename) {
428+
if (invalidConstantName.profile(!IdUtil.isConstant(name))) {
429+
CompilerDirectives.transferToInterpreter();
430+
throw new RaiseException(getContext().getCoreLibrary().nameError(String.format("autoload must be constant name: %s", name), this));
431+
}
432+
433+
if (emptyFilename.profile(emptyNode.empty(filename))) {
434+
CompilerDirectives.transferToInterpreter();
435+
throw new RaiseException(getContext().getCoreLibrary().argumentError("empty file name", this));
436+
}
437+
438+
module.setAutoloadConstant(this, name.toString(), filename);
439+
440+
return getContext().getCoreLibrary().getNilObject();
441+
}
442+
}
443+
387444
@CoreMethod(names = {"class_eval","module_eval"}, optional = 3, needsBlock = true)
388445
public abstract static class ClassEvalNode extends CoreMethodNode {
389446

truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UnresolvedDispatchNode.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@
1212
import com.oracle.truffle.api.Assumption;
1313
import com.oracle.truffle.api.CompilerDirectives;
1414
import com.oracle.truffle.api.frame.VirtualFrame;
15+
import org.jruby.truffle.nodes.RubyNode;
16+
import org.jruby.truffle.nodes.core.KernelNodes;
17+
import org.jruby.truffle.nodes.core.KernelNodesFactory;
1518
import org.jruby.truffle.runtime.RubyArguments;
1619
import org.jruby.truffle.runtime.RubyConstant;
1720
import org.jruby.truffle.runtime.RubyContext;
1821
import org.jruby.truffle.runtime.control.RaiseException;
1922
import org.jruby.truffle.runtime.core.RubyBasicObject;
2023
import org.jruby.truffle.runtime.core.RubyClass;
2124
import org.jruby.truffle.runtime.core.RubyModule;
25+
import org.jruby.truffle.runtime.core.RubyString;
2226
import org.jruby.truffle.runtime.core.RubySymbol;
2327
import org.jruby.truffle.runtime.methods.InternalMethod;
2428
import org.jruby.util.cli.Options;
@@ -31,6 +35,8 @@ public final class UnresolvedDispatchNode extends DispatchNode {
3135
private final boolean indirect;
3236
private final MissingBehavior missingBehavior;
3337

38+
@Child private KernelNodes.RequireNode requireNode;
39+
3440
public UnresolvedDispatchNode(
3541
RubyContext context,
3642
boolean ignoreVisibility,
@@ -41,6 +47,7 @@ public UnresolvedDispatchNode(
4147
this.ignoreVisibility = ignoreVisibility;
4248
this.indirect = indirect;
4349
this.missingBehavior = missingBehavior;
50+
requireNode = KernelNodesFactory.RequireNodeFactory.create(context, getSourceSection(), new RubyNode[]{});
4451
}
4552

4653
@Override
@@ -195,6 +202,14 @@ private Object doRubyBasicObject(
195202
methodName, blockObject, argumentsObjects);
196203
}
197204

205+
if (constant.isAutoload()) {
206+
module.removeConstant(this, (String) methodName);
207+
208+
requireNode.require((RubyString) constant.getValue());
209+
210+
return doRubyBasicObject(frame, first, receiverObject, methodName, blockObject, argumentsObjects);
211+
}
212+
198213
// The module, the "receiver" is an instance of its singleton class.
199214
// But we want to check the module assumption, not its singleton class assumption.
200215
final DispatchNode newDispatch = new CachedBoxedDispatchNode(getContext(), methodName, first,

truffle/src/main/java/org/jruby/truffle/runtime/RubyConstant.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ public class RubyConstant {
1818
private final RubyModule declaringModule;
1919
private final Object value;
2020
private boolean isPrivate;
21+
private boolean autoload;
2122

22-
public RubyConstant(RubyModule declaringModule, Object value, boolean isPrivate) {
23+
public RubyConstant(RubyModule declaringModule, Object value, boolean isPrivate, boolean autoload) {
2324
this.declaringModule = declaringModule;
2425
this.value = value;
2526
this.isPrivate = isPrivate;
27+
this.autoload = autoload;
2628
}
2729

2830
public Object getValue() {
@@ -72,4 +74,8 @@ public boolean isVisibleTo(RubyContext context, LexicalScope lexicalScope, RubyM
7274
return false;
7375
}
7476

77+
public boolean isAutoload() {
78+
return autoload;
79+
}
80+
7581
}

truffle/src/main/java/org/jruby/truffle/runtime/core/RubyModule.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ protected RubyModule(RubyContext context, RubyClass selfClass, RubyModule lexica
124124
}
125125

126126
public void getAdoptedByLexicalParent(RubyModule lexicalParent, String name, RubyNode currentNode) {
127-
lexicalParent.setConstantInternal(currentNode, name, this);
127+
lexicalParent.setConstantInternal(currentNode, name, this, false);
128128
lexicalParent.addLexicalDependent(this);
129129

130130
if (this.name == null) {
@@ -192,21 +192,26 @@ public void setConstant(RubyNode currentNode, String name, Object value) {
192192
if (value instanceof RubyModule) {
193193
((RubyModule) value).getAdoptedByLexicalParent(this, name, currentNode);
194194
} else {
195-
setConstantInternal(currentNode, name, value);
195+
setConstantInternal(currentNode, name, value, false);
196196
}
197197
}
198198

199-
private void setConstantInternal(RubyNode currentNode, String name, Object value) {
199+
@TruffleBoundary
200+
public void setAutoloadConstant(RubyNode currentNode, String name, RubyString filename) {
201+
setConstantInternal(currentNode, name, filename, true);
202+
}
203+
204+
private void setConstantInternal(RubyNode currentNode, String name, Object value, boolean autoload) {
200205
RubyNode.notDesignedForCompilation();
201206

202207
checkFrozen(currentNode);
203208

204209
RubyConstant previous = getConstants().get(name);
205210
if (previous == null) {
206-
getConstants().put(name, new RubyConstant(this, value, false));
211+
getConstants().put(name, new RubyConstant(this, value, false, autoload));
207212
} else {
208213
// TODO(CS): warn when redefining a constant
209-
getConstants().put(name, new RubyConstant(this, value, previous.isPrivate()));
214+
getConstants().put(name, new RubyConstant(this, value, previous.isPrivate(), autoload));
210215
}
211216

212217
newLexicalVersion();

truffle/src/main/ruby/core/rubinius/common/kernel.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ def StringValue(obj)
9292
end
9393
module_function :StringValue
9494

95+
def autoload(name, file)
96+
Object.autoload(name, file)
97+
end
98+
private :autoload
99+
95100
def define_singleton_method(*args, &block)
96101
singleton_class.send(:define_method, *args, &block)
97102
end

0 commit comments

Comments
 (0)