Skip to content

Commit

Permalink
[Truffle] Fixed class variable lookup from singleton classes by attac…
Browse files Browse the repository at this point in the history
…hing their companion class to them.
  • Loading branch information
nirvdrum committed Mar 9, 2015
1 parent 2ffaeb6 commit 86815ef
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 11 deletions.
Expand Up @@ -243,6 +243,30 @@ public static Object lookupClassVariable(RubyModule module, String name) {
}
}

// If singleton class, check attached module as well.
if (module instanceof RubyClass) {
RubyClass klass = (RubyClass) module;

if (klass.isSingleton() && klass.getAttached() != null) {

// Look in the attached module.
value = klass.getAttached().getClassVariables().get(name);

if (value != null) {
return value;
}

// Look in the attached module's ancestors.
for (RubyModule ancestor : klass.getAttached().parentAncestors()) {
value = ancestor.getClassVariables().get(name);

if (value != null) {
return value;
}
}
}
}

// Nothing found

return null;
Expand Down
Expand Up @@ -151,7 +151,7 @@ public CoreLibrary(RubyContext context) {

// Create the cyclic classes and modules

classClass = new RubyClass(context, null, null, null, "Class", false);
classClass = new RubyClass(context, null, null, null, "Class", false, null);
classClass.setAllocator(new RubyClass.ClassAllocator());

basicObjectClass = RubyClass.createBootClass(context, classClass, "BasicObject");
Expand All @@ -160,7 +160,7 @@ public CoreLibrary(RubyContext context) {
objectClass = RubyClass.createBootClass(context, classClass, "Object");
objectClass.setAllocator(basicObjectClass.getAllocator());

moduleClass = new RubyClass(context, classClass, null, null, "Module", false);
moduleClass = new RubyClass(context, classClass, null, null, "Module", false, null);
moduleClass.setAllocator(new RubyModule.ModuleAllocator());

// Close the cycles
Expand Down
Expand Up @@ -104,8 +104,13 @@ public RubyClass getSingletonClass(Node currentNode) {

final RubyClass logicalClass = metaClass;

metaClass = RubyClass.createSingletonClassOfObject(getContext(), logicalClass,
String.format("#<Class:#<%s:0x%x>>", logicalClass.getName(), getObjectID()));
if (this instanceof RubyModule) {
metaClass = RubyClass.createSingletonClassOfObject(getContext(), logicalClass, (RubyModule) this,
String.format("#<Class:#<%s:0x%x>>", logicalClass.getName(), getObjectID()));
} else {
metaClass = RubyClass.createSingletonClassOfObject(getContext(), logicalClass, null,
String.format("#<Class:#<%s:0x%x>>", logicalClass.getName(), getObjectID()));
}

if (DebugOperations.verySlowIsFrozen(this)) {
DebugOperations.verySlowFreeze(metaClass);
Expand Down
Expand Up @@ -33,30 +33,35 @@ public class RubyClass extends RubyModule {

private final boolean isSingleton;
private final Set<RubyClass> subClasses = Collections.newSetFromMap(new WeakHashMap<RubyClass, Boolean>());
private final RubyModule attached;

/**
* This constructor supports initialization and solves boot-order problems and should not
* normally be used from outside this class.
*/
public static RubyClass createBootClass(RubyContext context, RubyClass classClass, String name) {
return new RubyClass(context, classClass, null, null, name, false);
return new RubyClass(context, classClass, null, null, name, false, null);
}

public RubyClass(RubyContext context, RubyModule lexicalParent, RubyClass superclass, String name) {
this(context, superclass.getLogicalClass(), lexicalParent, superclass, name, false);
this(context, superclass.getLogicalClass(), lexicalParent, superclass, name, false, null);
// Always create a class singleton class for normal classes for consistency.
ensureSingletonConsistency();
}

protected static RubyClass createSingletonClassOfObject(RubyContext context, RubyClass superclass, String name) {
protected static RubyClass createSingletonClassOfObject(RubyContext context, RubyClass superclass, RubyModule attached, String name) {
// We also need to create the singleton class of a singleton class for proper lookup and consistency.
// See rb_singleton_class() documentation in MRI.
return new RubyClass(context, superclass.getLogicalClass(), null, superclass, name, true).ensureSingletonConsistency();
return new RubyClass(context, superclass.getLogicalClass(), null, superclass, name, true, attached).ensureSingletonConsistency();
}

protected RubyClass(RubyContext context, RubyClass classClass, RubyModule lexicalParent, RubyClass superclass, String name, boolean isSingleton) {
protected RubyClass(RubyContext context, RubyClass classClass, RubyModule lexicalParent, RubyClass superclass, String name, boolean isSingleton, RubyModule attached) {
super(context, classClass, lexicalParent, name, null);

assert isSingleton || attached == null;

this.isSingleton = isSingleton;
this.attached = attached;

if (superclass != null) {
unsafeSetSuperclass(superclass);
Expand Down Expand Up @@ -124,7 +129,7 @@ private RubyClass createOneSingletonClass() {
}

metaClass = new RubyClass(getContext(),
getLogicalClass(), null, singletonSuperclass, String.format("#<Class:%s>", getName()), true);
getLogicalClass(), null, singletonSuperclass, String.format("#<Class:%s>", getName()), true, this);

return metaClass;
}
Expand All @@ -137,6 +142,10 @@ public boolean isSingleton() {
return isSingleton;
}

public RubyModule getAttached() {
return attached;
}

public RubyClass getSuperClass() {
CompilerAsserts.neverPartOfCompilation();

Expand All @@ -157,7 +166,7 @@ public static class ClassAllocator implements Allocator {

@Override
public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, Node currentNode) {
return new RubyClass(context, context.getCoreLibrary().getClassClass(), null, null, null, false);
return new RubyClass(context, context.getCoreLibrary().getClassClass(), null, null, null, false, null);
}

}
Expand Down

0 comments on commit 86815ef

Please sign in to comment.