Skip to content

Commit

Permalink
[Truffle] Finally fix where top level methods are defined.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed Dec 6, 2014
1 parent e17ec06 commit 54dd9a0
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 29 deletions.
73 changes: 57 additions & 16 deletions core/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Expand Up @@ -1238,6 +1238,33 @@ public ObjectIDNode(ObjectIDNode prev) {

}

/*
* Kernel#pretty_inspect is normally part of stdlib, in pp.rb, but we aren't able to execute
* that file yet. Instead we implement a very simple version here, which is the solution
* suggested by RubySpec.
*/

@CoreMethod(names = "pretty_inspect")
public abstract static class PrettyInspectNode extends CoreMethodNode {

@Child protected DispatchHeadNode inspectNode;

public PrettyInspectNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
inspectNode = DispatchHeadNode.onSelf(context);
}

public PrettyInspectNode(PrettyInspectNode prev) {
super(prev);
inspectNode = prev.inspectNode;
}

@Specialization
public Object prettyInspect(VirtualFrame frame, Object self) {
return inspectNode.call(frame, self, "inspect", null);
}
}

@CoreMethod(names = "print", isModuleFunction = true, argumentsAsArray = true)
public abstract static class PrintNode extends CoreMethodNode {

Expand Down Expand Up @@ -1311,31 +1338,45 @@ public RubyNilClass printf(Object[] args) {
}
}

/*
* Kernel#pretty_inspect is normally part of stdlib, in pp.rb, but we aren't able to execute
* that file yet. Instead we implement a very simple version here, which is the solution
* suggested by RubySpec.
*/
@CoreMethod(names = "private_methods", optional = 1)
public abstract static class PrivateMethodsNode extends CoreMethodNode {

@CoreMethod(names = "pretty_inspect")
public abstract static class PrettyInspectNode extends CoreMethodNode {

@Child protected DispatchHeadNode inspectNode;

public PrettyInspectNode(RubyContext context, SourceSection sourceSection) {
public PrivateMethodsNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
inspectNode = DispatchHeadNode.onSelf(context);
}

public PrettyInspectNode(PrettyInspectNode prev) {
public PrivateMethodsNode(PrivateMethodsNode prev) {
super(prev);
inspectNode = prev.inspectNode;
}

@Specialization
public Object prettyInspect(VirtualFrame frame, Object self) {
return inspectNode.call(frame, self, "inspect", null);
public RubyArray private_methods(RubyBasicObject self, @SuppressWarnings("unused") UndefinedPlaceholder unused) {
return private_methods(self, true);
}

@Specialization
public RubyArray private_methods(RubyBasicObject self, boolean includeInherited) {
notDesignedForCompilation();

final RubyArray array = new RubyArray(self.getContext().getCoreLibrary().getArrayClass());

Map<String, RubyMethod> methods;

if (includeInherited) {
methods = ModuleOperations.getAllMethods(self.getMetaClass());
} else {
methods = self.getMetaClass().getMethods();
}

for (RubyMethod method : methods.values()) {
if (method.getVisibility() == Visibility.PRIVATE) {
array.slowPush(self.getContext().newSymbol(method.getName()));
}
}

return array;
}

}

@CoreMethod(names = "proc", isModuleFunction = true, needsBlock = true)
Expand Down
Expand Up @@ -22,10 +22,13 @@ public class AddMethodNode extends RubyNode {
@Child protected RubyNode receiver;
@Child protected MethodDefinitionNode method;

public AddMethodNode(RubyContext context, SourceSection section, RubyNode receiver, MethodDefinitionNode method) {
private final boolean topLevel;

public AddMethodNode(RubyContext context, SourceSection section, RubyNode receiver, MethodDefinitionNode method, boolean topLevel) {
super(context, section);
this.receiver = receiver;
this.method = method;
this.topLevel = topLevel;
}

@Override
Expand All @@ -46,11 +49,15 @@ public RubySymbol execute(VirtualFrame frame) {

final RubyMethod methodWithDeclaringModule = methodObject.withDeclaringModule(module);

if (moduleFunctionFlag(frame)) {
if (topLevel) {
module.addMethod(this, methodWithDeclaringModule.withVisibility(Visibility.PRIVATE));
module.getSingletonClass(this).addMethod(this, methodWithDeclaringModule.withVisibility(Visibility.PUBLIC));
} else {
module.addMethod(this, methodWithDeclaringModule);
if (moduleFunctionFlag(frame)) {
module.addMethod(this, methodWithDeclaringModule.withVisibility(Visibility.PRIVATE));
module.getSingletonClass(this).addMethod(this, methodWithDeclaringModule.withVisibility(Visibility.PUBLIC));
} else {
module.addMethod(this, methodWithDeclaringModule);
}
}

return getContext().newSymbol(method.getName());
Expand Down
Expand Up @@ -43,6 +43,7 @@
import org.jruby.truffle.nodes.methods.UndefNode;
import org.jruby.truffle.nodes.methods.locals.*;
import org.jruby.truffle.nodes.objects.*;
import org.jruby.truffle.nodes.objects.ClassNode;
import org.jruby.truffle.nodes.objects.SelfNode;
import org.jruby.truffle.nodes.yield.YieldNode;
import org.jruby.truffle.runtime.*;
Expand All @@ -61,8 +62,8 @@
public class BodyTranslator extends Translator {

protected final BodyTranslator parent;

protected final TranslatorEnvironment environment;
private final boolean topLevel;

public boolean translatingForStatement = false;
public boolean useClassVariablesAsIfInClass = false;
Expand All @@ -81,10 +82,11 @@ public class BodyTranslator extends Translator {
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$~", "$+", "$&", "$`", "$'", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"));
public static final Set<String> THREAD_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_"));

public BodyTranslator(RubyNode currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, Source source) {
public BodyTranslator(RubyNode currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, Source source, boolean topLevel) {
super(currentNode, context, source);
this.parent = parent;
this.environment = environment;
this.topLevel = topLevel;
}

@Override
Expand Down Expand Up @@ -755,7 +757,14 @@ public RubyNode visitDefinedNode(org.jruby.ast.DefinedNode node) {
@Override
public RubyNode visitDefnNode(org.jruby.ast.DefnNode node) {
final SourceSection sourceSection = translate(node.getPosition());
final SelfNode classNode = new SelfNode(context, sourceSection);
final RubyNode classNode;

if (topLevel) {
classNode = ClassNodeFactory.create(context, sourceSection, new SelfNode(context, sourceSection));
} else {
classNode = new SelfNode(context, sourceSection);
}

return translateMethodDefinition(sourceSection, classNode, node.getName(), node, node.getArgsNode(), node.getBodyNode(), false);
}

Expand Down Expand Up @@ -790,7 +799,7 @@ protected RubyNode translateMethodDefinition(SourceSection sourceSection, RubyNo
* http://stackoverflow.com/questions/1761148/where-are-methods-defined-at-the-ruby-top-level
*/

return new AddMethodNode(context, sourceSection, classNode, functionExprNode);
return new AddMethodNode(context, sourceSection, classNode, functionExprNode, topLevel);
}

@Override
Expand Down
Expand Up @@ -36,7 +36,7 @@ class MethodTranslator extends BodyTranslator {
private boolean isBlock;

public MethodTranslator(RubyNode currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, boolean isBlock, boolean isTopLevel, Source source) {
super(currentNode, context, parent, environment, source);
super(currentNode, context, parent, environment, source, false);
this.isBlock = isBlock;
this.isTopLevel = isTopLevel;
}
Expand Down
Expand Up @@ -33,7 +33,7 @@
class ModuleTranslator extends BodyTranslator {

public ModuleTranslator(RubyNode currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, Source source) {
super(currentNode, context, parent, environment, source);
super(currentNode, context, parent, environment, source, false);
useClassVariablesAsIfInClass = true;
}

Expand Down
Expand Up @@ -140,7 +140,7 @@ public RubyRootNode parse(RubyNode currentNode, RubyContext context, Source sour
if (parserContext == TranslatorDriver.ParserContext.MODULE) {
translator = new ModuleTranslator(currentNode, context, null, environment, source);
} else {
translator = new BodyTranslator(currentNode, context, null, environment, source);
translator = new BodyTranslator(currentNode, context, null, environment, source, parserContext == ParserContext.TOP_LEVEL);
}

RubyNode truffleNode;
Expand Down
1 change: 1 addition & 0 deletions spec/truffle/tags/language/def_tags.txt
Expand Up @@ -10,3 +10,4 @@ fails:An instance method with keyword arguments correctly distinguishes between
fails:An instance method with keyword arguments should allow keyword rest arguments
fails:An instance method with keyword arguments when there is a single keyword argument evaluates to the default when a value isn't provided
fails:An instance method with keyword arguments when there is a single keyword argument evaluates to the provided value
fails:A method definition in an eval creates a singleton method
6 changes: 5 additions & 1 deletion spec/truffle/tags/language/predefined_tags.txt
Expand Up @@ -60,4 +60,8 @@ fails:Predefined global $-0 does not call #to_str to convert the object to a Str
fails:Predefined global $-0 raises a TypeError if assigned a Fixnum
fails:Predefined global $-0 raises a TypeError if assigned a boolean
fails:Global variable $0 raises a TypeError when not given an object that can be coerced to a String
fails:Predefined global $~ raises an error if assigned an object not nil or instanceof MatchData
fails:Predefined global $~ raises an error if assigned an object not nil or instanceof MatchData
fails:The predefined global constant STDOUT has nil for the internal encoding
fails:The predefined global constant STDOUT has nil for the internal encoding despite Encoding.default_internal being changed
fails:The predefined global constant STDERR has nil for the internal encoding
fails:The predefined global constant STDERR has nil for the internal encoding despite Encoding.default_internal being changed
1 change: 0 additions & 1 deletion spec/truffle/tags/language/rescue_tags.txt
@@ -1,2 +1 @@
fails:The rescue keyword can rescue a splatted list of exceptions
fails:The rescue keyword will only rescue the specified exceptions when doing a splat rescue

0 comments on commit 54dd9a0

Please sign in to comment.