Skip to content

Commit 54ad99c

Browse files
authored
Merge pull request jruby#8531 from enebo/single
getSingletonClass() replaced with singleonClass(context)
2 parents a85dd6d + ce30020 commit 54ad99c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+286
-223
lines changed

core/src/main/java/org/jruby/MetaClass.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,13 @@ public RubyClass makeMetaClass(RubyClass superClass) {
100100

101101
private RubyClass getSuperSingletonMetaClass() {
102102
if (attached instanceof RubyClass att) {
103-
RubyClass superClass = att.getSuperClass();
103+
RubyClass superClass = att.superClass();
104104
if (superClass != null) superClass = superClass.getRealClass();
105105
// #<Class:BasicObject>'s singleton class == Class.singleton_class
106-
if (superClass == null) return runtime.getClassClass().getSingletonClass();
107-
return superClass.getMetaClass().getSingletonClass();
106+
// Context should be safe here as we never make a metaclass from a metaclass before first TC is made.
107+
var context = getRuntime().getCurrentContext();
108+
if (superClass == null) return runtime.getClassClass().singletonClass(context);
109+
return superClass.getMetaClass().singletonClass(context);
108110
}
109111

110112
return getSuperClass().getRealClass().getMetaClass(); // NOTE: is this correct?

core/src/main/java/org/jruby/RubyBasicObject.java

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
package org.jruby;
3030

3131
import org.jcodings.Encoding;
32+
import org.jruby.api.JRubyAPI;
3233
import org.jruby.ast.util.ArgsUtil;
3334
import org.jruby.ir.interpreter.Interpreter;
3435
import org.jruby.java.proxies.JavaProxy;
@@ -75,6 +76,7 @@
7576
import static org.jruby.api.Error.typeError;
7677
import static org.jruby.api.Warn.warn;
7778
import static org.jruby.ir.runtime.IRRuntimeHelpers.dupIfKeywordRestAtCallsite;
79+
import static org.jruby.ir.runtime.IRRuntimeHelpers.getCurrentClassBase;
7880
import static org.jruby.runtime.Helpers.invokeChecked;
7981
import static org.jruby.runtime.ThreadContext.*;
8082
import static org.jruby.runtime.Visibility.*;
@@ -460,16 +462,22 @@ public static RubyClass getMetaClass(IRubyObject arg) {
460462
return ((RubyBasicObject) arg).metaClass;
461463
}
462464

463-
/** rb_singleton_class
464-
*
465-
* Note: this method is specialized for RubyFixnum, RubySymbol,
466-
* RubyNil and RubyBoolean
467-
*
468-
* Will either return the existing singleton class for this
469-
* object, or create a new one and return that.
470-
*/
471465
@Override
472466
public RubyClass getSingletonClass() {
467+
return singletonClass(getCurrentContext());
468+
}
469+
470+
/**
471+
* Will either return the existing singleton class for this object, or create a new one and return that.
472+
* For a few types a singleton class is not possible so it will throw an error.
473+
*
474+
* @param context the current thread context
475+
* @return the singleton of this type
476+
*/
477+
478+
// MRI: rb_singleton_class
479+
@JRubyAPI
480+
public RubyClass singletonClass(ThreadContext context) {
473481
RubyClass klass = metaClass.toSingletonClass(this);
474482

475483
if (isFrozen()) klass.setFrozen(true);
@@ -1874,12 +1882,15 @@ public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObje
18741882
return evalUnder(context, mod, evalStr, file, line, evalType);
18751883
}
18761884

1885+
@Deprecated(since = "10.0")
18771886
protected RubyModule getInstanceEvalClass() {
1878-
if (isImmediate()) {
1879-
// Ruby uses Qnil here, we use "dummy" because we need a class
1880-
return getRuntime().getDummy();
1881-
}
1882-
return getSingletonClass();
1887+
return getInstanceEvalClass(getCurrentContext());
1888+
}
1889+
1890+
protected RubyModule getInstanceEvalClass(ThreadContext context) {
1891+
return isImmediate() ?
1892+
context.runtime.getDummy() : // MRI uses Qnil here, we use "dummy" because we need a class
1893+
singletonClass(context);
18831894
}
18841895

18851896
/**
@@ -2535,25 +2546,25 @@ public RubyArray to_a(ThreadContext context) {
25352546
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
25362547
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
25372548
public IRubyObject instance_eval(ThreadContext context, Block block) {
2538-
return specificEval(context, getInstanceEvalClass(), block, EvalType.INSTANCE_EVAL);
2549+
return specificEval(context, getInstanceEvalClass(context), block, EvalType.INSTANCE_EVAL);
25392550
}
25402551
@JRubyMethod(name = "instance_eval",
25412552
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
25422553
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
25432554
public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, Block block) {
2544-
return specificEval(context, getInstanceEvalClass(), arg0, block, EvalType.INSTANCE_EVAL);
2555+
return specificEval(context, getInstanceEvalClass(context), arg0, block, EvalType.INSTANCE_EVAL);
25452556
}
25462557
@JRubyMethod(name = "instance_eval",
25472558
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
25482559
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
25492560
public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
2550-
return specificEval(context, getInstanceEvalClass(), arg0, arg1, block, EvalType.INSTANCE_EVAL);
2561+
return specificEval(context, getInstanceEvalClass(context), arg0, arg1, block, EvalType.INSTANCE_EVAL);
25512562
}
25522563
@JRubyMethod(name = "instance_eval",
25532564
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
25542565
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
25552566
public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
2556-
return specificEval(context, getInstanceEvalClass(), arg0, arg1, arg2, block, EvalType.INSTANCE_EVAL);
2567+
return specificEval(context, getInstanceEvalClass(context), arg0, arg1, arg2, block, EvalType.INSTANCE_EVAL);
25572568
}
25582569

25592570
// This is callable and will work but the rest = true is put so we can match the expected arity error message
@@ -2593,19 +2604,9 @@ public IRubyObject instance_eval(ThreadContext context, IRubyObject[] args, Bloc
25932604
reads = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE},
25942605
writes = {LASTLINE, BACKREF, VISIBILITY, BLOCK, SELF, METHODNAME, LINE, CLASS, FILENAME, SCOPE})
25952606
public IRubyObject instance_exec(ThreadContext context, IRubyObject[] args, Block block) {
2596-
if (!block.isGiven()) {
2597-
throw context.runtime.newLocalJumpErrorNoBlock();
2598-
}
2599-
2600-
RubyModule klazz;
2601-
if (isImmediate()) {
2602-
// Ruby uses Qnil here, we use "dummy" because we need a class
2603-
klazz = context.runtime.getDummy();
2604-
} else {
2605-
klazz = getSingletonClass();
2606-
}
2607+
if (!block.isGiven()) throw context.runtime.newLocalJumpErrorNoBlock();
26072608

2608-
return yieldUnder(context, klazz, args, block, EvalType.INSTANCE_EVAL);
2609+
return yieldUnder(context, getInstanceEvalClass(context), args, block, EvalType.INSTANCE_EVAL);
26092610
}
26102611

26112612
/** rb_obj_extend

core/src/main/java/org/jruby/RubyBignum.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,11 @@ public BigInteger getBigIntegerValue() {
157157
}
158158

159159
@Override
160-
public RubyClass getSingletonClass() {
161-
throw typeError(getRuntime().getCurrentContext(), "can't define singleton");
160+
public RubyClass singletonClass(ThreadContext context) {
161+
throw typeError(context, "can't define singleton");
162162
}
163163

164+
164165
/** Getter for property value.
165166
* @return Value of property value.
166167
*/

core/src/main/java/org/jruby/RubyBinding.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static RubyClass createBindingClass(ThreadContext context, RubyClass Obje
7575
reifiedClass(RubyBinding.class).
7676
classIndex(ClassIndex.BINDING).
7777
defineMethods(context, RubyBinding.class).
78-
tap(c -> c.getSingletonClass().undefMethods(context, "new"));
78+
tap(c -> c.singletonClass(context).undefMethods(context, "new"));
7979
}
8080

8181
public Binding getBinding() {

core/src/main/java/org/jruby/RubyBoolean.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ public boolean isImmediate() {
8888
return true;
8989
}
9090

91-
@Override
92-
public RubyClass getSingletonClass() {
91+
public RubyClass singletonClass(ThreadContext context) {
9392
return metaClass;
9493
}
9594

core/src/main/java/org/jruby/RubyClass.java

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ public static RubyClass newClass(Ruby runtime, RubyClass superClass, String name
536536
}
537537

538538
/**
539-
* @see #getSingletonClass()
539+
* @see #singletonClass(ThreadContext)
540540
*/
541541
RubyClass toSingletonClass(RubyBasicObject target) {
542542
// replaced after makeMetaClass with MetaClass's toSingletonClass
@@ -2216,6 +2216,7 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) {
22162216

22172217
// now create proxy class
22182218
m.getstatic(javaPath, RUBY_FIELD, ci(Ruby.class));
2219+
m.invokevirtual("org/jruby/Ruby", "getCurrentContext", "()Lorg/jruby/runtime/ThreadContext;");
22192220
m.getstatic(javaPath, RUBY_CLASS_FIELD, ci(RubyClass.class));
22202221
m.ldc(org.objectweb.asm.Type.getType("L" + javaPath + ";"));
22212222
// if (simpleAlloc) // if simple, don't init, if complex, do init
@@ -2224,7 +2225,7 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) {
22242225
m.iconst_1(); // true (as int)
22252226

22262227
m.invokestatic(p(JavaProxyClass.class), "setProxyClassReified",
2227-
sig(JavaProxyClass.class, Ruby.class, RubyClass.class, Class.class, boolean.class));
2228+
sig(JavaProxyClass.class, ThreadContext.class, RubyClass.class, Class.class, boolean.class));
22282229
m.dup();
22292230
m.putstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class));
22302231

@@ -2893,31 +2894,27 @@ public IRubyObject smartLoadNewUser(IRubyObject target, IRubyObject data) {
28932894
*/
28942895
public IRubyObject smartLoadOldUser(IRubyObject data) {
28952896
ThreadContext context = runtime.getCurrentContext();
2896-
CacheEntry cache;
2897-
if ((cache = getSingletonClass().cachedLoad).token == getSingletonClass().generation) {
2898-
return cache.method.call(context, this, cache.sourceModule, "_load", data);
2899-
} else {
2900-
cache = getSingletonClass().searchWithCache("respond_to?");
2901-
DynamicMethod method = cache.method;
2902-
if (!method.equals(runtime.getRespondToMethod()) && !method.isUndefined()) {
2903-
2904-
// custom respond_to?, cache nothing and use slow path
2905-
if (method.call(context, this, cache.sourceModule, "respond_to?", asSymbol(context, "_load")).isTrue()) {
2906-
return callMethod(context, "_load", data);
2907-
} else {
2908-
throw typeError(context, "class ", this, " needs to have method `_load'");
2909-
}
2910-
2911-
} else if (!(cache = getSingletonClass().searchWithCache("_load")).method.isUndefined()) {
2912-
2913-
// real _load defined, cache and call it
2914-
getSingletonClass().cachedLoad = cache;
2915-
return cache.method.call(context, this, cache.sourceModule, "_load", data);
2916-
2897+
var singleton = singletonClass(context);
2898+
CacheEntry cache = singleton.cachedLoad;
2899+
if (cache.token == singleton.generation) return cache.method.call(context, this, cache.sourceModule, "_load", data);
2900+
2901+
cache = singleton.searchWithCache("respond_to?");
2902+
DynamicMethod method = cache.method;
2903+
if (!method.equals(runtime.getRespondToMethod()) && !method.isUndefined()) {
2904+
// custom respond_to?, cache nothing and use slow path
2905+
if (method.call(context, this, cache.sourceModule, "respond_to?", asSymbol(context, "_load")).isTrue()) {
2906+
return callMethod(context, "_load", data);
29172907
} else {
2918-
// provide an error, since it doesn't exist
29192908
throw typeError(context, "class ", this, " needs to have method `_load'");
29202909
}
2910+
} else if (!(cache = singleton.searchWithCache("_load")).method.isUndefined()) {
2911+
// real _load defined, cache and call it
2912+
singleton.cachedLoad = cache;
2913+
return cache.method.call(context, this, cache.sourceModule, "_load", data);
2914+
2915+
} else {
2916+
// provide an error, since it doesn't exist
2917+
throw typeError(context, "class ", this, " needs to have method `_load'");
29212918
}
29222919
}
29232920

core/src/main/java/org/jruby/RubyComplex.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static RubyClass createComplexClass(ThreadContext context, RubyClass Nume
7979
defineMethods(context, RubyComplex.class).
8080
undefMethods(context, "<", "<=", ">", ">=", "between?", "clamp", "%", "div", "divmod", "floor", "ceil",
8181
"modulo", "remainder", "round", "step", "truncate", "positive?", "negative?").
82-
tap(c -> c.getSingletonClass().undefMethods(context, "allocate", "new")).
82+
tap(c -> c.singletonClass(context).undefMethods(context, "allocate", "new")).
8383
tap(c -> c.defineConstant("I", RubyComplex.convert(context, c, asFixnum(context, 0), asFixnum(context, 1))));
8484
}
8585

core/src/main/java/org/jruby/RubyContinuation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public static RubyClass createContinuation(ThreadContext context, RubyClass obje
6262
return defineClass(context, "Continuation", objectClass, objectClass.getAllocator()).
6363
reifiedClass(RubyContinuation.class).
6464
classIndex(ClassIndex.CONTINUATION).
65-
tap(c -> c.getSingletonClass().undefMethods(context, "new"));
65+
tap(c -> c.singletonClass(context).undefMethods(context, "new"));
6666
}
6767

6868
@Deprecated

core/src/main/java/org/jruby/RubyEncoding.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static RubyClass createEncodingClass(ThreadContext context, RubyClass Obj
7979
kindOf(new RubyModule.JavaClassKindOf(RubyEncoding.class)).
8080
classIndex(ClassIndex.ENCODING).
8181
defineMethods(context, RubyEncoding.class).
82-
tap(c -> c.getSingletonClass().undefMethods(context, "allocate"));
82+
tap(c -> c.singletonClass(context).undefMethods(context, "allocate"));
8383
}
8484

8585
private Encoding encoding;

core/src/main/java/org/jruby/RubyFile.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public static RubyClass createFileClass(ThreadContext context, RubyClass IO) {
121121
defineConstant(context, "PATH_SEPARATOR", pathSeparator);
122122

123123
// For JRUBY-5276, physically define FileTest methods on File's singleton
124-
File.getSingletonClass().defineMethods(context, RubyFileTest.FileTestFileMethods.class);
124+
File.singletonClass(context).defineMethods(context, RubyFileTest.FileTestFileMethods.class);
125125

126126
var FileConstants = File.defineModuleUnder(context, "Constants").
127127
defineConstant(context, "RDONLY", asFixnum(context, OpenFlags.O_RDONLY.intValue())).
@@ -205,7 +205,7 @@ public static RubyClass createFileClass(ThreadContext context, RubyClass IO) {
205205
if (!Platform.IS_BSD) {
206206
// lchmod appears to be mostly a BSD-ism, not supported on Linux.
207207
// See https://github.com/jruby/jruby/issues/5547
208-
File.getSingletonClass().searchMethod("lchmod").setNotImplemented(true);
208+
File.singletonClass(context).searchMethod("lchmod").setNotImplemented(true);
209209
}
210210

211211
return File;

0 commit comments

Comments
 (0)