Skip to content

Commit 3e18abe

Browse files
committed
Always use ModuleSuper and rewrite define_method
The changes here attempt to get more super calls using a static name, so we can eliminate dependency on a call frame to get that name. * Super in a block in any method will start out as a ModuleSuper using the name from the method. * define_method clones and rewrites the block to retarget any super calls to the newly defined name. Remaining cases that use UnresolvedSuper may only be the ones where super is invalid, such as a block at top-level or within a class or module body.
1 parent a151aad commit 3e18abe

File tree

3 files changed

+86
-18
lines changed

3 files changed

+86
-18
lines changed

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

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,16 @@
8989
import org.jruby.internal.runtime.methods.UndefinedMethod;
9090
import org.jruby.ir.IRClosure;
9191
import org.jruby.ir.IRMethod;
92+
import org.jruby.ir.instructions.CallBase;
93+
import org.jruby.ir.instructions.ClassSuperInstr;
94+
import org.jruby.ir.instructions.InstanceSuperInstr;
95+
import org.jruby.ir.instructions.Instr;
96+
import org.jruby.ir.instructions.ModuleSuperInstr;
97+
import org.jruby.ir.instructions.UnresolvedSuperInstr;
98+
import org.jruby.ir.interpreter.InterpreterContext;
99+
import org.jruby.ir.representations.BasicBlock;
92100
import org.jruby.ir.targets.indy.Bootstrap;
101+
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
93102
import org.jruby.javasupport.JavaClass;
94103
import org.jruby.javasupport.binding.MethodGatherer;
95104
import org.jruby.parser.StaticScope;
@@ -102,6 +111,7 @@
102111
import org.jruby.runtime.IRBlockBody;
103112
import org.jruby.runtime.MethodFactory;
104113
import org.jruby.runtime.MethodIndex;
114+
import org.jruby.runtime.MixedModeIRBlockBody;
105115
import org.jruby.runtime.ObjectAllocator;
106116
import org.jruby.runtime.ThreadContext;
107117
import org.jruby.runtime.Visibility;
@@ -1616,7 +1626,7 @@ private CacheEntry refinedMethodOriginalMethodEntry(Map<RubyModule, RubyModule>
16161626
* failed to return a result. Cache superclass definitions in this class.
16171627
*
16181628
* MRI: method_entry_get_without_cache
1619-
*
1629+
*
16201630
* @param id The name of the method to search for
16211631
* @param cacheUndef Flag for caching UndefinedMethod. This should normally be true.
16221632
* @return The method, or UndefinedMethod if not found
@@ -2386,19 +2396,57 @@ public IRubyObject defineMethodFromBlock(ThreadContext context, IRubyObject arg0
23862396

23872397
// If we know it comes from IR we can convert this directly to a method and
23882398
// avoid overhead of invoking it as a block
2389-
if (block.getBody() instanceof IRBlockBody &&
2390-
runtime.getInstanceConfig().getCompileMode().shouldJIT()) { // FIXME: Once Interp and Mixed Methods are one class we can fix this to work in interp mode too.
2399+
if (block.getBody() instanceof IRBlockBody) {
23912400
IRBlockBody body = (IRBlockBody) block.getBody();
23922401
IRClosure closure = body.getScope();
23932402

2394-
// closure may be null from AOT scripts
2395-
if (closure != null) {
2396-
// Ask closure to give us a method equivalent.
2397-
IRMethod method = closure.convertToMethod(name.getBytes());
2398-
if (method != null) {
2399-
newMethod = new DefineMethodMethod(method, visibility, this, context.getFrameBlock());
2400-
Helpers.addInstanceMethod(this, name, newMethod, visibility, context, runtime);
2401-
return name;
2403+
// clone for rewriting and optimization specific to define_method
2404+
closure = closure.cloneForInlining(new SimpleCloneInfo(closure, false));
2405+
body = new MixedModeIRBlockBody(closure, body.getSignature());
2406+
block = new Block(body, block.getBinding(), block.type);
2407+
2408+
InterpreterContext ic = closure.prepareFullBuild();
2409+
for (BasicBlock bb : ic.getCFG().getBasicBlocks()) {
2410+
ArrayList<Instr> newList = new ArrayList<>();
2411+
for (Instr i : bb.getInstrs()) {
2412+
if (i instanceof CallBase) {
2413+
CallBase cb = (CallBase) i;
2414+
2415+
if (cb instanceof UnresolvedSuperInstr) {
2416+
newList.add(
2417+
new ModuleSuperInstr(
2418+
closure,
2419+
cb.getResult(),
2420+
name,
2421+
cb.getReceiver(),
2422+
cb.getCallArgs(),
2423+
cb.getClosureArg(),
2424+
cb.isPotentiallyRefined()));
2425+
continue;
2426+
}
2427+
2428+
if (cb instanceof InstanceSuperInstr ||
2429+
cb instanceof ClassSuperInstr ||
2430+
cb instanceof ModuleSuperInstr) {
2431+
cb.setName(name);
2432+
}
2433+
}
2434+
2435+
newList.add(i);
2436+
}
2437+
}
2438+
2439+
if (runtime.getInstanceConfig().getCompileMode().shouldJIT()) { // FIXME: Once Interp and Mixed Methods are one class we can fix this to work in interp mode too.
2440+
2441+
// closure may be null from AOT scripts
2442+
if (closure != null) {
2443+
// Ask closure to give us a method equivalent.
2444+
IRMethod method = closure.convertToMethod(name.getBytes());
2445+
if (method != null) {
2446+
newMethod = new DefineMethodMethod(method, visibility, this, context.getFrameBlock());
2447+
Helpers.addInstanceMethod(this, name, newMethod, visibility, context, runtime);
2448+
return name;
2449+
}
24022450
}
24032451
}
24042452
}

core/src/main/java/org/jruby/ir/IRBuilder.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4077,21 +4077,37 @@ public Operand buildStrRaw(StrNode strNode) {
40774077
private Operand buildSuperInstr(Operand block, Operand[] args) {
40784078
CallInstr superInstr;
40794079
Variable ret = createTemporaryVariable();
4080-
if (scope instanceof IRMethod && scope.getLexicalParent() instanceof IRClassBody) {
4081-
if (((IRMethod) scope).isInstanceMethod) {
4082-
superInstr = new InstanceSuperInstr(scope, ret, getCurrentModuleVariable(), getName(), args, block, scope.maybeUsingRefinements());
4080+
IRMethod nearestMethod = scope.getNearestMethod();
4081+
4082+
if (nearestMethod != null) {
4083+
IRScope lexicalParent;
4084+
RubySymbol superName;
4085+
4086+
if (scope instanceof IRMethod) {
4087+
lexicalParent = scope.getLexicalParent();
4088+
superName = getName();
40834089
} else {
4084-
superInstr = new ClassSuperInstr(scope, ret, getCurrentModuleVariable(), getName(), args, block, scope.maybeUsingRefinements());
4090+
lexicalParent = nearestMethod.getLexicalParent();
4091+
superName = nearestMethod.getName();
4092+
}
4093+
4094+
if (lexicalParent instanceof IRClassBody) {
4095+
if (nearestMethod.isInstanceMethod) {
4096+
superInstr = new InstanceSuperInstr(scope, ret, getCurrentModuleVariable(), superName, args, block, scope.maybeUsingRefinements());
4097+
} else {
4098+
superInstr = new ClassSuperInstr(scope, ret, getCurrentModuleVariable(), superName, args, block, scope.maybeUsingRefinements());
4099+
}
4100+
} else {
4101+
superInstr = new ModuleSuperInstr(scope, ret, superName, buildSelf(), args, block, scope.maybeUsingRefinements());
40854102
}
4086-
} else if (scope instanceof IRMethod && scope.getLexicalParent() instanceof IRModuleBody) {
4087-
superInstr = new ModuleSuperInstr(scope, ret, getName(), buildSelf(), args, block, scope.maybeUsingRefinements());
40884103
} else {
40894104
// We dont always know the method name we are going to be invoking if the super occurs in a closure.
40904105
// This is because the super can be part of a block that will be used by 'define_method' to define
40914106
// a new method. In that case, the method called by super will be determined by the 'name' argument
40924107
// to 'define_method'.
40934108
superInstr = new UnresolvedSuperInstr(scope, ret, buildSelf(), args, block, scope.maybeUsingRefinements());
40944109
}
4110+
40954111
receiveBreakException(block, superInstr);
40964112
return ret;
40974113
}

core/src/main/java/org/jruby/ir/instructions/CallBase.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public abstract class CallBase extends NOperandInstr implements ClosureAccepting
3030

3131
public transient long callSiteId;
3232
private final CallType callType;
33-
protected final RubySymbol name;
33+
protected RubySymbol name;
3434
protected final transient CallSite callSite;
3535
protected final transient int argsCount;
3636
protected final transient boolean hasClosure;
@@ -104,6 +104,10 @@ public String getId() {
104104
return name.idString();
105105
}
106106

107+
public void setName(RubySymbol name) {
108+
this.name = name;
109+
}
110+
107111
public long getCallSiteId() {
108112
return callSiteId;
109113
}

0 commit comments

Comments
 (0)