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: e5caa8a8ec35
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: b43d6d065f8b
Choose a head ref
  • 15 commits
  • 13 files changed
  • 1 contributor

Commits on Feb 22, 2016

  1. Copy the full SHA
    4f62879 View commit details
  2. Copy the full SHA
    ea1a785 View commit details
  3. Copy the full SHA
    ba5ac0d View commit details
  4. 2
    Copy the full SHA
    1db32d2 View commit details
  5. Copy the full SHA
    e7f5d89 View commit details
  6. Copy the full SHA
    50164bf View commit details
  7. Copy the full SHA
    fc5b409 View commit details
  8. Copy the full SHA
    ec53aad View commit details
  9. Copy the full SHA
    3a59a23 View commit details
  10. Copy the full SHA
    47b4049 View commit details
  11. Copy the full SHA
    b34759b View commit details
  12. Copy the full SHA
    c0486e1 View commit details
  13. Copy the full SHA
    96cb699 View commit details
  14. Copy the full SHA
    452ce79 View commit details
  15. Copy the full SHA
    b43d6d0 View commit details
18 changes: 14 additions & 4 deletions truffle/src/main/java/org/jruby/truffle/core/CoreLibrary.java
Original file line number Diff line number Diff line change
@@ -745,7 +745,7 @@ private DynamicObject defineModule(String name) {

private DynamicObject defineModule(DynamicObject lexicalParent, String name) {
assert RubyGuards.isRubyModule(lexicalParent);
return ModuleNodes.createRubyModule(context, moduleClass, lexicalParent, name, node);
return ModuleNodes.createModule(context, moduleClass, lexicalParent, name, node);
}

public void initializeAfterBasicMethodsAdded() {
@@ -1039,6 +1039,11 @@ public DynamicObject typeErrorCantCreateInstanceOfSingletonClass(Node currentNod
return typeError("can't create instance of singleton class", currentNode, null);
}

@TruffleBoundary
public DynamicObject superclassMismatch(String name, Node currentNode) {
return typeError("superclass mismatch for class " + name, currentNode);
}

@TruffleBoundary
public DynamicObject typeError(String message, Node currentNode) {
return typeError(message, currentNode, null);
@@ -1055,8 +1060,8 @@ public DynamicObject typeErrorAllocatorUndefinedFor(DynamicObject rubyClass, Nod
return typeError(String.format("allocator undefined for %s", className), currentNode);
}

@TruffleBoundary
public DynamicObject typeErrorCantDefineSingleton(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return typeError("can't define singleton", currentNode);
}

@@ -1077,8 +1082,13 @@ public DynamicObject typeErrorCantConvertInto(Object from, String toClass, Node
return typeError(String.format("can't convert %s into %s", Layouts.MODULE.getFields(getLogicalClass(from)).getName(), toClass), currentNode);
}

@TruffleBoundary
public DynamicObject typeErrorIsNotA(Object value, String expectedType, Node currentNode) {
return typeErrorIsNotA(value.toString(), expectedType, currentNode);
}

@TruffleBoundary
public DynamicObject typeErrorIsNotA(String value, String expectedType, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return typeError(String.format("%s is not a %s", value, expectedType), currentNode);
}

@@ -1146,8 +1156,8 @@ public DynamicObject nameErrorUninitializedClassVariable(DynamicObject module, S
return nameError(String.format("uninitialized class variable %s in %s", name, Layouts.MODULE.getFields(module).getName()), name, currentNode);
}

@TruffleBoundary
public DynamicObject nameErrorPrivateConstant(DynamicObject module, String name, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
assert RubyGuards.isRubyModule(module);
return nameError(String.format("private constant %s::%s referenced", Layouts.MODULE.getFields(module).getName(), name), name, currentNode);
}
Original file line number Diff line number Diff line change
@@ -1571,11 +1571,7 @@ public boolean require(DynamicObject featureString) {

// Pysch loads either the jar or the so - we need to intercept
if (feature.equals("psych.so") && callerIs("psych.rb")) {
try {
getContext().getFeatureLoader().require("truffle/psych.rb", this);
} catch (IOException e) {
throw new RuntimeException(e);
}
getContext().getFeatureLoader().require("truffle/psych.rb", this);
return true;
}

@@ -1584,11 +1580,7 @@ public boolean require(DynamicObject featureString) {
throw new RaiseException(coreLibrary().loadErrorCannotLoad(feature, this));
}

try {
return getContext().getFeatureLoader().require(feature, this);
} catch (IOException e) {
throw new RuntimeException(e);
}
return getContext().getFeatureLoader().require(feature, this);
}

private boolean callerIs(String caller) {
@@ -1626,11 +1618,7 @@ public boolean requireRelative(DynamicObject feature) {
featurePath = dirname(sourcePath) + "/" + featureString;
}

try {
featureLoader.require(featurePath, this);
} catch (IOException e) {
throw new RuntimeException(e);
}
featureLoader.require(featurePath, this);

return true;
}
Original file line number Diff line number Diff line change
@@ -172,6 +172,7 @@ public static DynamicObject ensureSingletonConsistency(RubyContext context, Dyna
return rubyClass;
}

@TruffleBoundary
public static DynamicObject getSingletonClass(RubyContext context, DynamicObject rubyClass) {
// We also need to create the singleton class of a singleton class for proper lookup and consistency.
// See rb_singleton_class() documentation in MRI.
Original file line number Diff line number Diff line change
@@ -103,7 +103,8 @@
@CoreClass(name = "Module")
public abstract class ModuleNodes {

public static DynamicObject createRubyModule(RubyContext context, DynamicObject selfClass, DynamicObject lexicalParent, String name, Node currentNode) {
@TruffleBoundary
public static DynamicObject createModule(RubyContext context, DynamicObject selfClass, DynamicObject lexicalParent, String name, Node currentNode) {
final ModuleFields model = new ModuleFields(context, lexicalParent, name);
final DynamicObject module = Layouts.MODULE.createModule(Layouts.CLASS.getInstanceFactory(selfClass), model);
model.rubyModuleObject = module;
@@ -2041,7 +2042,7 @@ public AllocateNode(RubyContext context, SourceSection sourceSection) {

@Specialization
public DynamicObject allocate(DynamicObject rubyClass) {
return createRubyModule(getContext(), rubyClass, null, null, this);
return createModule(getContext(), rubyClass, null, null, this);
}

}
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
import org.jruby.truffle.RubyLanguage;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.RubyRootNode;
import org.jruby.truffle.language.objects.OpenModuleNode;
import org.jruby.truffle.language.objects.RunModuleDefinitionNode;

public class RubyDefaultASTProber implements NodeVisitor, ASTProber {

@@ -42,7 +42,7 @@ public boolean visit(Node node) {
probe.tagAs(RubySyntaxTag.LINE, null);
}

if (rubyNode instanceof OpenModuleNode) {
if (rubyNode instanceof RunModuleDefinitionNode) {
final Probe probe = instrumenter.probe(rubyNode);
probe.tagAs(RubySyntaxTag.CLASS, null);
}
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ private enum RequireResult {
}
}

public boolean require(String feature, Node currentNode) throws IOException {
public boolean require(String feature, Node currentNode) {
final RubyConstant dataConstantBefore = ModuleOperations.lookupConstant(context, context.getCoreLibrary().getObjectClass(), "DATA");

if (feature.startsWith("./")) {
@@ -88,6 +88,8 @@ public boolean require(String feature, Node currentNode) throws IOException {
}

throw new RaiseException(context.getCoreLibrary().loadErrorCannotLoad(feature, currentNode));
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (dataConstantBefore == null) {
Layouts.MODULE.getFields(context.getCoreLibrary().getObjectClass()).removeConstant(context, currentNode, "DATA");
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2013, 2016 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.language.objects;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.kernel.KernelNodes;
import org.jruby.truffle.core.kernel.KernelNodesFactory;
import org.jruby.truffle.core.klass.ClassNodes;
import org.jruby.truffle.language.LexicalScope;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;

public class DefineClassNode extends RubyNode {

protected final String name;

@Child private RubyNode superClass;
@Child private CallDispatchHeadNode inheritedNode;
@Child private RubyNode lexicalParentModule;

private final ConditionProfile needToDefineProfile = ConditionProfile.createBinaryProfile();
private final BranchProfile errorProfile = BranchProfile.create();

public DefineClassNode(RubyContext context, SourceSection sourceSection, String name,
RubyNode lexicalParent, RubyNode superClass) {
super(context, sourceSection);
this.name = name;
this.lexicalParentModule = lexicalParent;
this.superClass = superClass;
}

@Override
public Object execute(VirtualFrame frame) {
final Object lexicalParentObject = lexicalParentModule.execute(frame);;

if (!RubyGuards.isRubyModule(lexicalParentObject)) {
errorProfile.enter();
throw new RaiseException(coreLibrary().typeErrorIsNotA(lexicalParentObject, "module", this));
}

DynamicObject lexicalParentModule = (DynamicObject) lexicalParentObject;

final RubyConstant constant = DefineModuleNode.lookupForExistingModule(
getContext(), name, lexicalParentModule, this);

final DynamicObject definingClass;
final Object superClassObject = superClass.execute(frame);

if (!RubyGuards.isRubyClass(superClassObject)) {
errorProfile.enter();
throw new RaiseException(coreLibrary().typeError("superclass must be a Class", this));
}

final DynamicObject superClassModule = (DynamicObject) superClassObject;

if (Layouts.CLASS.getIsSingleton(superClassModule)) {
errorProfile.enter();
throw new RaiseException(coreLibrary().typeError("can't make subclass of virtual class", this));
}

if (needToDefineProfile.profile(constant == null)) {
definingClass = ClassNodes.createRubyClass(getContext(), lexicalParentModule, superClassModule, name);

if (inheritedNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
inheritedNode = insert(DispatchHeadNodeFactory.createMethodCallOnSelf(getContext()));
}

inheritedNode.call(frame, superClassModule, "inherited", null, definingClass);
} else {
if (!RubyGuards.isRubyClass(constant.getValue())) {
errorProfile.enter();
throw new RaiseException(coreLibrary().typeErrorIsNotA(constant.getValue(), "class", this));
}

definingClass = (DynamicObject) constant.getValue();

if (!isBlankOrRootClass(superClassModule) && !isBlankOrRootClass(definingClass)
&& ClassNodes.getSuperClass(definingClass) != superClassModule) {
errorProfile.enter();

throw new RaiseException(coreLibrary().superclassMismatch(
Layouts.MODULE.getFields(definingClass).getName(), this));
}
}

return definingClass;
}

private boolean isBlankOrRootClass(DynamicObject rubyClass) {
return rubyClass == coreLibrary().getBasicObjectClass() || rubyClass == coreLibrary().getObjectClass();
}

}
Original file line number Diff line number Diff line change
@@ -9,107 +9,100 @@
*/
package org.jruby.truffle.language.objects;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.kernel.KernelNodes;
import org.jruby.truffle.core.kernel.KernelNodesFactory;
import org.jruby.truffle.core.module.ModuleNodes;
import org.jruby.truffle.language.LexicalScope;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.control.RaiseException;

/**
* Define a new module, or get the existing one of the same name.
*/
public class DefineOrGetModuleNode extends RubyNode {
public class DefineModuleNode extends RubyNode {

private final String name;

protected final String name;
@Child private RubyNode lexicalParentModule;
@Child private KernelNodes.RequireNode requireNode;

public DefineOrGetModuleNode(RubyContext context, SourceSection sourceSection, String name, RubyNode lexicalParent) {
private final ConditionProfile needToDefineProfile = ConditionProfile.createBinaryProfile();
private final BranchProfile errorProfile = BranchProfile.create();

public DefineModuleNode(RubyContext context, SourceSection sourceSection, String name, RubyNode lexicalParent) {
super(context, sourceSection);
this.name = name;
this.lexicalParentModule = lexicalParent;
}

@Override
public Object execute(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();
final Object lexicalParentObject = lexicalParentModule.execute(frame);;

// Look for a current definition of the module, or create a new one
if (!RubyGuards.isRubyModule(lexicalParentObject)) {
errorProfile.enter();
throw new RaiseException(coreLibrary().typeErrorIsNotA(lexicalParentObject, "module", this));
}

DynamicObject lexicalParent = getLexicalParentModule(frame);
final RubyConstant constant = lookupForExistingModule(lexicalParent);
final DynamicObject lexicalParentModule = (DynamicObject) lexicalParentObject;
final RubyConstant constant = lookupForExistingModule(getContext(), name, lexicalParentModule, this);

DynamicObject definingModule;
final DynamicObject definingModule;

if (constant == null) {
definingModule = ModuleNodes.createRubyModule(getContext(), coreLibrary().getModuleClass(), lexicalParent, name, this);
if (needToDefineProfile.profile(constant == null)) {
definingModule = ModuleNodes.createModule(getContext(), coreLibrary().getModuleClass(),
lexicalParentModule, name, this);
} else {
Object module = constant.getValue();
if (!RubyGuards.isRubyModule(module) || RubyGuards.isRubyClass(module)) {
final Object constantValue = constant.getValue();

if (!RubyGuards.isRubyModule(constantValue) || RubyGuards.isRubyClass(constantValue)) {
errorProfile.enter();
throw new RaiseException(coreLibrary().typeErrorIsNotA(name, "module", this));
}
definingModule = (DynamicObject) module;
}

return definingModule;
}

protected DynamicObject getLexicalParentModule(VirtualFrame frame) {
final Object lexicalParent = lexicalParentModule.execute(frame);;

if (!RubyGuards.isRubyModule(lexicalParent)) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(coreLibrary().typeErrorIsNotA(lexicalParent.toString(), "module", this));
definingModule = (DynamicObject) constantValue;
}

return (DynamicObject) lexicalParent;
return definingModule;
}

@TruffleBoundary
protected RubyConstant lookupForExistingModule(DynamicObject lexicalParent) {
public static RubyConstant lookupForExistingModule(RubyContext context, String name,
DynamicObject lexicalParent, RubyNode node) {
RubyConstant constant = Layouts.MODULE.getFields(lexicalParent).getConstant(name);

final DynamicObject objectClass = coreLibrary().getObjectClass();
final DynamicObject objectClass = context.getCoreLibrary().getObjectClass();

if (constant == null && lexicalParent == objectClass) {
for (DynamicObject included : Layouts.MODULE.getFields(objectClass).prependedAndIncludedModules()) {
constant = Layouts.MODULE.getFields(included).getConstant(name);

if (constant != null) {
break;
}
}
}

if (constant != null && !constant.isVisibleTo(getContext(), LexicalScope.NONE, lexicalParent)) {
throw new RaiseException(coreLibrary().nameErrorPrivateConstant(lexicalParent, name, this));
if (constant != null && !constant.isVisibleTo(context, LexicalScope.NONE, lexicalParent)) {
throw new RaiseException(context.getCoreLibrary().nameErrorPrivateConstant(lexicalParent, name, node));
}

// If a constant already exists with this class/module name and it's an autoload module, we have to trigger
// the autoload behavior before proceeding.

if ((constant != null) && constant.isAutoload()) {
if (requireNode == null) {
CompilerDirectives.transferToInterpreter();
requireNode = insert(KernelNodesFactory.RequireNodeFactory.create(getContext(), getSourceSection(), null));
}

// We know that we're redefining this constant as we're defining a class/module with that name. We remove
// the constant here rather than just overwrite it in order to prevent autoload loops in either the require
// call or the recursive execute call.
Layouts.MODULE.getFields(lexicalParent).removeConstant(getContext(), this, name);

requireNode.require((DynamicObject) constant.getValue());

return lookupForExistingModule(lexicalParent);
Layouts.MODULE.getFields(lexicalParent).removeConstant(context, node, name);
context.getFeatureLoader().require(constant.getValue().toString(), node);
return lookupForExistingModule(context, name, lexicalParent, node);
}

return constant;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -31,13 +31,13 @@

public abstract class ObjectGraph {

public static Set<DynamicObject> stopAndGetAllObjects(
Node currentNode, final RubyContext context) {
public static Set<DynamicObject> stopAndGetAllObjects(Node currentNode, final RubyContext context) {
final Set<DynamicObject> visited = new HashSet<>();

final Thread stoppingThread = Thread.currentThread();

context.getSafepointManager().pauseAllThreadsAndExecute(currentNode, false, new Function2<Void, DynamicObject, Node>() {
context.getSafepointManager().pauseAllThreadsAndExecute(currentNode, false,
new Function2<Void, DynamicObject, Node>() {

@Override
public Void apply(DynamicObject thread, Node currentNode) {
@@ -51,16 +51,21 @@ public Void apply(DynamicObject thread, Node currentNode) {
}

final FrameInstance currentFrame = Truffle.getRuntime().getCurrentFrame();

if (currentFrame != null) {
stack.addAll(getObjectsInFrame(currentFrame.getFrame(FrameInstance.FrameAccess.READ_ONLY, true)));
stack.addAll(getObjectsInFrame(currentFrame.getFrame(
FrameInstance.FrameAccess.READ_ONLY, true)));
}

Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<Object>() {

@Override
public Object visitFrame(FrameInstance frameInstance) {
stack.addAll(getObjectsInFrame(frameInstance.getFrame(FrameInstance.FrameAccess.READ_ONLY, true)));
stack.addAll(getObjectsInFrame(frameInstance.getFrame(
FrameInstance.FrameAccess.READ_ONLY, true)));
return null;
}

});

while (!stack.isEmpty()) {
@@ -84,7 +89,9 @@ public static Set<DynamicObject> stopAndGetRootObjects(Node currentNode, final R

final Thread stoppingThread = Thread.currentThread();

context.getSafepointManager().pauseAllThreadsAndExecute(currentNode, false, new Function2<Void, DynamicObject, Node>() {
context.getSafepointManager().pauseAllThreadsAndExecute(currentNode, false,
new Function2<Void, DynamicObject, Node>() {

@Override
public Void apply(DynamicObject thread, Node currentNode) {
objects.add(thread);
@@ -95,6 +102,7 @@ public Void apply(DynamicObject thread, Node currentNode) {

return null;
}

});

return objects;
@@ -103,7 +111,6 @@ public Void apply(DynamicObject thread, Node currentNode) {
public static void visitContextRoots(RubyContext context, Collection<DynamicObject> stack) {
// We do not want to expose the global object
stack.addAll(ObjectGraph.getAdjacentObjects(context.getCoreLibrary().getGlobalVariablesObject()));

stack.addAll(context.getAtExitManager().getHandlers());
stack.addAll(context.getObjectSpaceManager().getFinalizerHandlers());
}
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
*/
package org.jruby.truffle.language.objects;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.IndirectCallNode;
@@ -24,16 +24,16 @@
import org.jruby.truffle.language.methods.InternalMethod;
import org.jruby.truffle.language.methods.ModuleBodyDefinitionNode;

public class OpenModuleNode extends RubyNode {
public class RunModuleDefinitionNode extends RubyNode {

final protected LexicalScope lexicalScope;

@Child private RubyNode definingModule;
@Child private ModuleBodyDefinitionNode definitionMethod;
@Child private IndirectCallNode callModuleDefinitionNode;

public OpenModuleNode(RubyContext context, SourceSection sourceSection, LexicalScope lexicalScope,
ModuleBodyDefinitionNode definition, RubyNode definingModule) {
public RunModuleDefinitionNode(RubyContext context, SourceSection sourceSection, LexicalScope lexicalScope,
ModuleBodyDefinitionNode definition, RubyNode definingModule) {
super(context, sourceSection);
this.definingModule = definingModule;
this.definitionMethod = definition;
@@ -43,17 +43,18 @@ public OpenModuleNode(RubyContext context, SourceSection sourceSection, LexicalS

@Override
public Object execute(VirtualFrame frame) {
CompilerDirectives.transferToInterpreter();

final DynamicObject module = (DynamicObject) definingModule.execute(frame);

lexicalScope.unsafeSetLiveModule(module);
Layouts.MODULE.getFields(lexicalScope.getParent().getLiveModule()).addLexicalDependent(module);

final InternalMethod definition = definitionMethod.executeMethod(frame).withDeclaringModule(module);
final InternalMethod definition = prepareLexicalScope(module, definitionMethod.executeMethod(frame));

return callModuleDefinitionNode.call(frame, definition.getCallTarget(), RubyArguments.pack(
null, null, definition, DeclarationContext.MODULE, null, module, null, new Object[]{}));
}

@TruffleBoundary
private InternalMethod prepareLexicalScope(DynamicObject module, InternalMethod definition) {
lexicalScope.unsafeSetLiveModule(module);
Layouts.MODULE.getFields(lexicalScope.getParent().getLiveModule()).addLexicalDependent(module);
return definition.withDeclaringModule(module);
}

}
Original file line number Diff line number Diff line change
@@ -9,11 +9,12 @@
*/
package org.jruby.truffle.language.objects;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
@@ -22,14 +23,11 @@
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.control.RaiseException;

/**
* Reads the singleton (meta, eigen) class of an object.
*/
@NodeChild(value = "value", type = RubyNode.class)
public abstract class SingletonClassNode extends RubyNode {

@Child IsFrozenNode isFrozenNode;
@Child FreezeNode freezeNode;
@Child private IsFrozenNode isFrozenNode;
@Child private FreezeNode freezeNode;

public SingletonClassNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -77,61 +75,107 @@ protected DynamicObject singletonClassSymbol(DynamicObject value) {
return noSingletonClass();
}

@Specialization(guards = "isRubyClass(rubyClass)")
protected DynamicObject singletonClassClass(DynamicObject rubyClass) {
return ClassNodes.getSingletonClass(getContext(), rubyClass);
@Specialization(
guards = {
"isRubyClass(rubyClass)",
"rubyClass.getShape() == cachedShape"
},
limit = "getCacheLimit()"
)
protected DynamicObject singletonClassClassCached(
DynamicObject rubyClass,
@Cached("rubyClass.getShape()") Shape cachedShape,
@Cached("getSingletonClassForClass(rubyClass)") DynamicObject cachedSingletonClass) {
return cachedSingletonClass;
}

@Specialization(guards = { "!isNil(object)", "!isRubyBignum(object)", "!isRubySymbol(object)", "!isRubyClass(object)" })
protected DynamicObject singletonClass(DynamicObject object) {
return getNormalObjectSingletonClass(object);
@Specialization(
guards = "isRubyClass(rubyClass)",
contains = "singletonClassClassCached"
)
protected DynamicObject singletonClassClassUncached(DynamicObject rubyClass) {
return getSingletonClassForClass(rubyClass);
}

public DynamicObject getNormalObjectSingletonClass(DynamicObject object) {
CompilerAsserts.neverPartOfCompilation();
@Specialization(
guards = {
"!isNil(object)",
"!isRubyBignum(object)",
"!isRubySymbol(object)",
"!isRubyClass(object)",
"object == cachedObject",
"object.getShape() == cachedShape"
},
limit = "getCacheLimit()")
protected DynamicObject singletonClassInstanceCached(
DynamicObject object,
@Cached("object") DynamicObject cachedObject,
@Cached("object.getShape()") Shape cachedShape,
@Cached("getSingletonClassForInstance(object)") DynamicObject cachedSingletonClass) {
return cachedSingletonClass;
}

if (RubyGuards.isRubyClass(object)) { // For the direct caller
return ClassNodes.getSingletonClass(getContext(), object);
}
@Specialization(
guards = {
"!isNil(object)",
"!isRubyBignum(object)",
"!isRubySymbol(object)",
"!isRubyClass(object)"
},
contains = "singletonClassInstanceCached"
)
protected DynamicObject singletonClassInstanceUncached(DynamicObject object) {
return getSingletonClassForInstance(object);
}

private DynamicObject noSingletonClass() {
throw new RaiseException(coreLibrary().typeErrorCantDefineSingleton(this));
}

@TruffleBoundary
protected DynamicObject getSingletonClassForClass(DynamicObject rubyClass) {
return ClassNodes.getSingletonClass(getContext(), rubyClass);
}

@TruffleBoundary
protected DynamicObject getSingletonClassForInstance(DynamicObject object) {
if (Layouts.CLASS.getIsSingleton(Layouts.BASIC_OBJECT.getMetaClass(object))) {
return Layouts.BASIC_OBJECT.getMetaClass(object);
}

CompilerDirectives.transferToInterpreter();
final DynamicObject logicalClass = Layouts.BASIC_OBJECT.getLogicalClass(object);

DynamicObject attached = null;

if (RubyGuards.isRubyModule(object)) {
attached = object;
}

final String name = String.format("#<Class:#<%s:0x%x>>", Layouts.MODULE.getFields(logicalClass).getName(), ObjectIDOperations.verySlowGetObjectID(getContext(), object));
final DynamicObject singletonClass = ClassNodes.createSingletonClassOfObject(getContext(), logicalClass, attached, name);
propagateFrozen(object, singletonClass);
final String name = String.format("#<Class:#<%s:0x%x>>", Layouts.MODULE.getFields(logicalClass).getName(),
ObjectIDOperations.verySlowGetObjectID(getContext(), object));

Layouts.BASIC_OBJECT.setMetaClass(object, singletonClass);

return singletonClass;
}

private void propagateFrozen(Object object, DynamicObject singletonClass) {
assert RubyGuards.isRubyClass(singletonClass);
final DynamicObject singletonClass = ClassNodes.createSingletonClassOfObject(
getContext(), logicalClass, attached, name);

if (isFrozenNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
isFrozenNode = insert(IsFrozenNodeGen.create(getContext(), getSourceSection(), null));
freezeNode = insert(FreezeNodeGen.create(getContext(), getSourceSection(), null));
}

if (isFrozenNode.executeIsFrozen(object)) {
if (freezeNode == null) {
freezeNode = insert(FreezeNodeGen.create(getContext(), getSourceSection(), null));
}

freezeNode.executeFreeze(singletonClass);
}

Layouts.BASIC_OBJECT.setMetaClass(object, singletonClass);

return singletonClass;
}

private DynamicObject noSingletonClass() {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(coreLibrary().typeErrorCantDefineSingleton(this));
protected int getCacheLimit() {
return getContext().getOptions().CLASS_CACHE;
}

}
Original file line number Diff line number Diff line change
@@ -147,10 +147,10 @@
import org.jruby.truffle.language.methods.MethodDefinitionNode;
import org.jruby.truffle.language.methods.ModuleBodyDefinitionNode;
import org.jruby.truffle.language.methods.SharedMethodInfo;
import org.jruby.truffle.language.objects.DefineOrGetClassNode;
import org.jruby.truffle.language.objects.DefineOrGetModuleNode;
import org.jruby.truffle.language.objects.DefineClassNode;
import org.jruby.truffle.language.objects.DefineModuleNode;
import org.jruby.truffle.language.objects.LexicalScopeNode;
import org.jruby.truffle.language.objects.OpenModuleNode;
import org.jruby.truffle.language.objects.RunModuleDefinitionNode;
import org.jruby.truffle.language.objects.ReadClassVariableNode;
import org.jruby.truffle.language.objects.ReadInstanceVariableNode;
import org.jruby.truffle.language.objects.SelfNode;
@@ -935,7 +935,7 @@ private RubyNode openModule(SourceSection sourceSection, RubyNode defineOrGetNod

final ModuleBodyDefinitionNode definition = moduleTranslator.compileClassNode(sourceSection, name, bodyNode, sclass);

return new OpenModuleNode(context, sourceSection, newLexicalScope, definition, defineOrGetNode);
return new RunModuleDefinitionNode(context, sourceSection, newLexicalScope, definition, defineOrGetNode);
} finally {
environment.popLexicalScope();
}
@@ -992,7 +992,7 @@ public RubyNode visitClassNode(org.jruby.ast.ClassNode node) {
superClass = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getObjectClass());
}

final DefineOrGetClassNode defineOrGetClass = new DefineOrGetClassNode(context, sourceSection, name, lexicalParent, superClass);
final DefineClassNode defineOrGetClass = new DefineClassNode(context, sourceSection, name, lexicalParent, superClass);

final RubyNode ret = openModule(sourceSection, defineOrGetClass, name, node.getBodyNode(), false);
return addNewlineIfNeeded(node, ret);
@@ -2105,7 +2105,7 @@ public RubyNode visitModuleNode(org.jruby.ast.ModuleNode node) {

RubyNode lexicalParent = translateCPath(sourceSection, node.getCPath());

final DefineOrGetModuleNode defineModuleNode = new DefineOrGetModuleNode(context, sourceSection, name, lexicalParent);
final DefineModuleNode defineModuleNode = new DefineModuleNode(context, sourceSection, name, lexicalParent);

final RubyNode ret = openModule(sourceSection, defineModuleNode, name, node.getBodyNode(), false);
return addNewlineIfNeeded(node, ret);