Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 364d745f7020
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8e680d9f8e82
Choose a head ref
  • 5 commits
  • 13 files changed
  • 1 contributor

Commits on Jun 25, 2015

  1. [Truffle] Pass a Method to #install_rubinius_primitive.

    * Receiver + method name in one argument.
    eregon committed Jun 25, 2015
    Copy the full SHA
    06cf7d1 View commit details
  2. [Truffle] The rubinius primitive map should be a ConcurrentMap now.

    * Since we can add primitives at runtime.
    eregon committed Jun 25, 2015
    Copy the full SHA
    3ae7f62 View commit details
  3. Copy the full SHA
    008fd1a View commit details
  4. [Truffle] Call the Ruby Method directly for rubinius primitives.

    * Extract logic from BodyTranslator into
      RubiniusPrimitiveConstructor implementations.
    eregon committed Jun 25, 2015
    Copy the full SHA
    2519521 View commit details
  5. Copy the full SHA
    8e680d9 View commit details
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.arguments;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;

public class NodeArrayToObjectArrayNode extends RubyNode {

@Children final RubyNode[] nodes;

public NodeArrayToObjectArrayNode(RubyContext context, SourceSection sourceSection, RubyNode[] nodes) {
super(context, sourceSection);
this.nodes = nodes;
}

@Override
@ExplodeLoop
public Object[] execute(VirtualFrame frame) {
final Object[] values = new Object[nodes.length];

for (int i = 0; i < nodes.length; i++) {
values[i] = nodes[i].execute(frame);
}

return values;
}

}
Original file line number Diff line number Diff line change
@@ -13,7 +13,9 @@
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.NotProvided;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyProc;

@@ -24,23 +26,26 @@ public ProcOrNullNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public abstract RubyProc executeProcOrNull(Object proc);

@Specialization
public RubyProc doNotProvided(NotProvided proc) {
return null;
}

@Specialization(guards = "isNil(nil)")
public Object doNil(Object nil) {
public RubyProc doNil(Object nil) {
return null;
}

@Specialization
public Object doProc(RubyProc proc) {
public RubyProc doProc(RubyProc proc) {
return proc;
}

@Override
public final RubyProc executeRubyProc(VirtualFrame frame) {
final Object proc = execute(frame);

assert proc == null || proc instanceof RubyProc;

return (RubyProc) proc;
return (RubyProc) execute(frame);
}

}
Original file line number Diff line number Diff line change
@@ -1000,7 +1000,7 @@ protected RubyClass getMetaClass(VirtualFrame frame, Object object) {
return metaClassNode.executeMetaClass(frame, object);
}

protected int getCacheLimit() {
protected static int getCacheLimit() {
return DispatchNode.DISPATCH_POLYMORPHIC_MAX;
}
}
44 changes: 33 additions & 11 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/MethodNodes.java
Original file line number Diff line number Diff line change
@@ -9,20 +9,30 @@
*/
package org.jruby.truffle.nodes.core;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CreateCast;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.object.*;
import com.oracle.truffle.api.source.NullSourceSection;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.ast.ArgsNode;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.Helpers;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.ProcOrNullNode;
import org.jruby.truffle.nodes.cast.ProcOrNullNodeGen;
import org.jruby.truffle.nodes.coerce.NameToJavaStringNodeGen;
import org.jruby.truffle.nodes.core.BasicObjectNodes.ReferenceEqualNode;
import org.jruby.truffle.nodes.core.array.ArrayNodes;
import org.jruby.truffle.nodes.dispatch.DispatchNode;
import org.jruby.truffle.nodes.objects.ClassNode;
import org.jruby.truffle.nodes.objects.ClassNodeGen;
import org.jruby.truffle.runtime.NotProvided;
@@ -124,31 +134,43 @@ public int arity(RubyBasicObject method) {
@CoreMethod(names = "call", needsBlock = true, argumentsAsArray = true)
public abstract static class CallNode extends CoreMethodArrayArgumentsNode {

@Child private IndirectCallNode callNode;
@Child ProcOrNullNode procOrNullNode;

public CallNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
callNode = Truffle.getRuntime().createIndirectCallNode();
procOrNullNode = ProcOrNullNodeGen.create(context, sourceSection, null);
}

@Specialization
public Object call(VirtualFrame frame, RubyBasicObject method, Object[] arguments, NotProvided block) {
return doCall(frame, method, arguments, null);
@Specialization(guards = "getCalltarget(method) == cachedCallTarget", limit = "getCacheLimit()")
public Object callCached(VirtualFrame frame, RubyBasicObject method, Object[] arguments, Object block,
@Cached("getCalltarget(method)") CallTarget cachedCallTarget,
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
return callNode.call(frame, packArguments(method, arguments, block));
}

@Specialization
public Object doCall(VirtualFrame frame, RubyBasicObject method, Object[] arguments, RubyProc block) {
// TODO(CS 11-Jan-15) should use a cache and DirectCallNode here so that we can inline - but it's
// incompatible with our current dispatch chain.
protected Object doCall(VirtualFrame frame, RubyBasicObject method, Object[] arguments, Object block,
@Cached("create()") IndirectCallNode callNode) {
return callNode.call(frame, getCalltarget(method), packArguments(method, arguments, block));
}

private Object[] packArguments(RubyBasicObject method, Object[] arguments, Object block) {
final InternalMethod internalMethod = getMethod(method);

return callNode.call(frame, getMethod(method).getCallTarget(), RubyArguments.pack(
return RubyArguments.pack(
internalMethod,
internalMethod.getDeclarationFrame(),
getReceiver(method),
block,
arguments));
procOrNullNode.executeProcOrNull(block),
arguments);
}

protected CallTarget getCalltarget(RubyBasicObject method) {
return getMethod(method).getCallTarget();
}

protected static int getCacheLimit() {
return DispatchNode.DISPATCH_POLYMORPHIC_MAX;
}

}
Original file line number Diff line number Diff line change
@@ -473,17 +473,17 @@ public Object atExit(boolean always, RubyProc block) {
}
}

@CoreMethod(names = "install_rubinius_primitive", isModuleFunction = true, required = 2)
@CoreMethod(names = "install_rubinius_primitive", isModuleFunction = true, required = 1)
public abstract static class InstallRubiniusPrimitiveNode extends CoreMethodArrayArgumentsNode {

public InstallRubiniusPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@TruffleBoundary
@Specialization(guards = "isRubySymbol(name)")
public Object installRubiniusPrimitive(RubyModule module, RubyBasicObject name) {
getContext().getRubiniusPrimitiveManager().installPrimitive(module, SymbolNodes.getString(name));
@Specialization(guards = "isRubyMethod(rubyMethod)")
public Object installRubiniusPrimitive(RubyBasicObject rubyMethod) {
String name = MethodNodes.getMethod(rubyMethod).getName();
getContext().getRubiniusPrimitiveManager().installPrimitive(name, rubyMethod);
return nil();
}
}
Original file line number Diff line number Diff line change
@@ -9,23 +9,50 @@
*/
package org.jruby.truffle.nodes.rubinius;

import org.jruby.truffle.runtime.core.RubyModule;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.arguments.NodeArrayToObjectArrayNode;
import org.jruby.truffle.nodes.arguments.ReadAllArgumentsNode;
import org.jruby.truffle.nodes.arguments.ReadBlockNode;
import org.jruby.truffle.nodes.core.MethodNodes;
import org.jruby.truffle.nodes.core.MethodNodesFactory.CallNodeFactory;
import org.jruby.truffle.nodes.literal.LiteralNode;
import org.jruby.truffle.runtime.NotProvided;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyBasicObject;

import com.oracle.truffle.api.source.SourceSection;

public class RubiniusPrimitiveCallConstructor implements RubiniusPrimitiveConstructor {

private final RubyModule module;
private final String method;
private final RubyBasicObject method;

public RubiniusPrimitiveCallConstructor(RubyModule module, String method) {
this.module = module;
public RubiniusPrimitiveCallConstructor(RubyBasicObject method) {
assert RubyGuards.isRubyMethod(method);
this.method = method;
}

public RubyModule getModule() {
return module;
@Override
public int getPrimitiveArity() {
return MethodNodes.getMethod(method).getSharedMethodInfo().getArity().getRequired();
}

@Override
public RubyNode createCallPrimitiveNode(RubyContext context, SourceSection sourceSection, long returnID) {
return CallNodeFactory.create(context, sourceSection, new RubyNode[] {
new LiteralNode(context, sourceSection, method),
new ReadAllArgumentsNode(context, sourceSection),
new ReadBlockNode(context, sourceSection, NotProvided.INSTANCE)
});
}

public String getMethod() {
return method;
@Override
public RubyNode createInvokePrimitiveNode(RubyContext context, SourceSection sourceSection, RubyNode[] arguments) {
return CallNodeFactory.create(context, sourceSection, new RubyNode[] {
new LiteralNode(context, sourceSection, method),
new NodeArrayToObjectArrayNode(context, sourceSection, arguments),
new ReadBlockNode(context, sourceSection, NotProvided.INSTANCE)
});
}

}
Original file line number Diff line number Diff line change
@@ -9,6 +9,15 @@
*/
package org.jruby.truffle.nodes.rubinius;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

import com.oracle.truffle.api.source.SourceSection;

public interface RubiniusPrimitiveConstructor {
public int getPrimitiveArity();

public RubyNode createCallPrimitiveNode(RubyContext context, SourceSection sourceSection, long returnID);

public RubyNode createInvokePrimitiveNode(RubyContext context, SourceSection sourceSection, RubyNode[] arguments);
}
Original file line number Diff line number Diff line change
@@ -9,25 +9,28 @@
*/
package org.jruby.truffle.nodes.rubinius;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.GeneratedBy;
import com.oracle.truffle.api.dsl.NodeFactory;

import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyModule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
* Manages the available Rubinius primitive calls.
*/
public class RubiniusPrimitiveManager {

private final Map<String, RubiniusPrimitiveConstructor> primitives; // Read-only
private final ConcurrentMap<String, RubiniusPrimitiveConstructor> primitives = new ConcurrentHashMap<String, RubiniusPrimitiveConstructor>();

private RubiniusPrimitiveManager(Map<String, RubiniusPrimitiveConstructor> primitives) {
this.primitives = primitives;
public RubiniusPrimitiveManager() {
}

public RubiniusPrimitiveConstructor getPrimitive(String name) {
@@ -40,7 +43,11 @@ public RubiniusPrimitiveConstructor getPrimitive(String name) {
return constructor;
}

public static RubiniusPrimitiveManager create() {
private void addPrimitive(String name, RubiniusPrimitiveConstructor constructor) {
primitives.putIfAbsent(name, constructor);
}

public void addAnnotatedPrimitives() {
final List<NodeFactory<? extends RubyNode>> nodeFactories = new ArrayList<>();

nodeFactories.addAll(VMPrimitiveNodesFactory.getFactories());
@@ -68,19 +75,17 @@ public static RubiniusPrimitiveManager create() {
// This comes last as a catch-all
nodeFactories.addAll(UndefinedPrimitiveNodesFactory.getFactories());

final Map<String, RubiniusPrimitiveConstructor> primitives = new HashMap<>();

for (NodeFactory<? extends RubyNode> nodeFactory : nodeFactories) {
final GeneratedBy generatedBy = nodeFactory.getClass().getAnnotation(GeneratedBy.class);
final Class<?> nodeClass = generatedBy.value();
final RubiniusPrimitive annotation = nodeClass.getAnnotation(RubiniusPrimitive.class);
primitives.put(annotation.name(), new RubiniusPrimitiveNodeConstructor(annotation, nodeFactory));
addPrimitive(annotation.name(), new RubiniusPrimitiveNodeConstructor(annotation, nodeFactory));
}

return new RubiniusPrimitiveManager(primitives);
}

public void installPrimitive(RubyModule module, String method) {
primitives.put(method, new RubiniusPrimitiveCallConstructor(module, method));
@TruffleBoundary
public void installPrimitive(String name, RubyBasicObject method) {
assert RubyGuards.isRubyMethod(method);
addPrimitive(name, new RubiniusPrimitiveCallConstructor(method));
}
}
Original file line number Diff line number Diff line change
@@ -9,8 +9,19 @@
*/
package org.jruby.truffle.nodes.rubinius;

import java.util.ArrayList;
import java.util.List;

import com.oracle.truffle.api.dsl.NodeFactory;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.arguments.MissingArgumentBehaviour;
import org.jruby.truffle.nodes.arguments.ReadPreArgumentNode;
import org.jruby.truffle.nodes.core.fixnum.FixnumLowerNode;
import org.jruby.truffle.nodes.objects.SelfNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.array.ArrayUtils;

public class RubiniusPrimitiveNodeConstructor implements RubiniusPrimitiveConstructor {

@@ -22,12 +33,46 @@ public RubiniusPrimitiveNodeConstructor(RubiniusPrimitive annotation, NodeFactor
this.factory = factory;
}

public RubiniusPrimitive getAnnotation() {
return annotation;
@Override
public int getPrimitiveArity() {
return factory.getExecutionSignature().size();
}

public NodeFactory<? extends RubyNode> getFactory() {
return factory;
@Override
public RubyNode createCallPrimitiveNode(RubyContext context, SourceSection sourceSection, long returnID) {
int argumentsCount = getPrimitiveArity();
final List<RubyNode> arguments = new ArrayList<>(argumentsCount);

if (annotation.needsSelf()) {
arguments.add(new SelfNode(context, sourceSection));
argumentsCount--;
}

for (int n = 0; n < argumentsCount; n++) {
RubyNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, MissingArgumentBehaviour.UNDEFINED);
arguments.add(transformArgument(readArgumentNode, n));
}

return new CallRubiniusPrimitiveNode(context, sourceSection,
factory.createNode(context, sourceSection, arguments.toArray(new RubyNode[arguments.size()])), returnID);
}

public RubyNode createInvokePrimitiveNode(RubyContext context, SourceSection sourceSection, RubyNode[] arguments) {
for (int n = 1; n < arguments.length; n++) {
arguments[n] = transformArgument(arguments[n], n);
}

return new InvokeRubiniusPrimitiveNode(context, sourceSection,
factory.createNode(context, sourceSection, arguments));
}

private RubyNode transformArgument(RubyNode argument, int n) {
if (ArrayUtils.contains(annotation.lowerFixnumParameters(), n)) {
return new FixnumLowerNode(argument);
} else {
return argument;
}
}


}
Original file line number Diff line number Diff line change
@@ -162,7 +162,8 @@ public RubyContext(Ruby runtime) {
threadManager = new ThreadManager(this);
threadManager.initialize();

rubiniusPrimitiveManager = RubiniusPrimitiveManager.create();
rubiniusPrimitiveManager = new RubiniusPrimitiveManager();
rubiniusPrimitiveManager.addAnnotatedPrimitives();

if (INSTRUMENTATION_SERVER_PORT != 0) {
instrumentationServerManager = new InstrumentationServerManager(this, INSTRUMENTATION_SERVER_PORT);
Original file line number Diff line number Diff line change
@@ -9,7 +9,10 @@
*/
package org.jruby.truffle.runtime.core;

import java.util.Arrays;

import com.oracle.truffle.api.nodes.Node;

import org.jruby.truffle.nodes.core.StringNodes;
import org.jruby.truffle.nodes.core.array.ArrayNodes;
import org.jruby.truffle.nodes.objects.Allocator;
@@ -66,6 +69,12 @@ public RubyBasicObject asRubyStringArray() {
return ArrayNodes.fromObjects(getContext().getCoreLibrary().getArrayClass(), array);
}

@Override
public String toString() {
return message + " : " + super.toString() + "\n" +
Arrays.toString(Backtrace.EXCEPTION_FORMATTER.format(getContext(), this, backtrace));
}

public static class ExceptionAllocator implements Allocator {

@Override
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
import org.jruby.truffle.nodes.arguments.IsRubiniusUndefinedNode;
import org.jruby.truffle.nodes.arguments.MissingArgumentBehaviour;
import org.jruby.truffle.nodes.arguments.ReadAllArgumentsNode;
import org.jruby.truffle.nodes.arguments.ReadBlockNode;
import org.jruby.truffle.nodes.arguments.ReadPreArgumentNode;
import org.jruby.truffle.nodes.cast.*;
import org.jruby.truffle.nodes.cast.LambdaNode;
@@ -68,6 +69,7 @@
import org.jruby.truffle.nodes.rubinius.*;
import org.jruby.truffle.nodes.yield.YieldNode;
import org.jruby.truffle.runtime.LexicalScope;
import org.jruby.truffle.runtime.NotProvided;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.array.ArrayUtils;
import org.jruby.truffle.runtime.control.RaiseException;
@@ -464,56 +466,8 @@ private RubyNode translateRubiniusPrimitive(SourceSection sourceSection, CallNod
final String primitiveName = ((org.jruby.ast.SymbolNode) node.getArgsNode().childNodes().get(0)).getName();

final RubiniusPrimitiveConstructor primitive = context.getRubiniusPrimitiveManager().getPrimitive(primitiveName);

if (primitive instanceof RubiniusPrimitiveNodeConstructor) {
final RubiniusPrimitiveNodeConstructor nodeConstructor = (RubiniusPrimitiveNodeConstructor) primitive;

final List<RubyNode> arguments = new ArrayList<>();

int argumentsCount = nodeConstructor.getFactory().getExecutionSignature().size();

if (nodeConstructor.getAnnotation().needsSelf()) {
arguments.add(new SelfNode(context, sourceSection));
argumentsCount--;
}

for (int n = 0; n < argumentsCount; n++) {
RubyNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, MissingArgumentBehaviour.UNDEFINED);

if (ArrayUtils.contains(nodeConstructor.getAnnotation().lowerFixnumParameters(), n)) {
readArgumentNode = new FixnumLowerNode(readArgumentNode);
}

arguments.add(readArgumentNode);
}

return new CallRubiniusPrimitiveNode(context, sourceSection,
nodeConstructor.getFactory().createNode(context, sourceSection, arguments.toArray(new RubyNode[arguments.size()])),
environment.getReturnID());
} else if (primitive instanceof RubiniusPrimitiveCallConstructor) {
final RubiniusPrimitiveCallConstructor callConstructor = (RubiniusPrimitiveCallConstructor) primitive;

final List<RubyNode> arguments = new ArrayList<>();

final InternalMethod method = callConstructor.getModule().getMethods().get(callConstructor.getMethod());
final int argumentsCount = method.getSharedMethodInfo().getArity().getRequired();

for (int n = 0; n < argumentsCount; n++) {
RubyNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, MissingArgumentBehaviour.UNDEFINED);
arguments.add(readArgumentNode);
}

return new RubyCallNode(
context,
sourceSection,
callConstructor.getMethod(),
new LiteralNode(context, sourceSection, callConstructor.getModule()),
null,
false,
arguments.toArray(new RubyNode[arguments.size()]));
} else {
throw new UnsupportedOperationException();
}
final long returnID = environment.getReturnID();
return primitive.createCallPrimitiveNode(context, sourceSection, returnID);
}

private RubyNode translateRubiniusInvokePrimitive(SourceSection sourceSection, CallNode node) {
@@ -547,31 +501,7 @@ private RubyNode translateRubiniusInvokePrimitive(SourceSection sourceSection, C
arguments.add(readArgumentNode);
}

if (primitive instanceof RubiniusPrimitiveNodeConstructor) {
final RubiniusPrimitiveNodeConstructor nodeConstructor = (RubiniusPrimitiveNodeConstructor) primitive;

for (int n = 1; n < arguments.size(); n++) {
if (ArrayUtils.contains(nodeConstructor.getAnnotation().lowerFixnumParameters(), n)) {
arguments.set(n, new FixnumLowerNode(arguments.get(n)));
}
}

return new InvokeRubiniusPrimitiveNode(context, sourceSection,
nodeConstructor.getFactory().createNode(context, sourceSection, arguments.toArray(new RubyNode[arguments.size()])));
} else if (primitive instanceof RubiniusPrimitiveCallConstructor) {
final RubiniusPrimitiveCallConstructor callConstructor = (RubiniusPrimitiveCallConstructor) primitive;

return new RubyCallNode(
context,
sourceSection,
callConstructor.getMethod(),
new LiteralNode(context, sourceSection, callConstructor.getModule()),
null,
false,
arguments.toArray(new RubyNode[arguments.size()]));
} else {
throw new UnsupportedOperationException();
}
return primitive.createInvokePrimitiveNode(context, sourceSection, arguments.toArray(new RubyNode[arguments.size()]));
}

private RubyNode translateRubiniusPrivately(SourceSection sourceSection, CallNode node) {
2 changes: 1 addition & 1 deletion truffle/src/main/ruby/core/rubinius/primitives.rb
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ def self.module_mirror(obj)
end
end

Truffle::Primitive.install_rubinius_primitive self, :module_mirror
Truffle::Primitive.install_rubinius_primitive method(:module_mirror)

end
end