Skip to content

Commit cb27869

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. * all super instructions get rewritten to the appropriate type for the target class, instance/class/module 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 cb27869

File tree

9 files changed

+188
-48
lines changed

9 files changed

+188
-48
lines changed

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

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,17 @@
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.SuperInstr;
98+
import org.jruby.ir.instructions.UnresolvedSuperInstr;
99+
import org.jruby.ir.interpreter.InterpreterContext;
100+
import org.jruby.ir.representations.BasicBlock;
92101
import org.jruby.ir.targets.indy.Bootstrap;
102+
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
93103
import org.jruby.javasupport.JavaClass;
94104
import org.jruby.javasupport.binding.MethodGatherer;
95105
import org.jruby.parser.StaticScope;
@@ -102,6 +112,7 @@
102112
import org.jruby.runtime.IRBlockBody;
103113
import org.jruby.runtime.MethodFactory;
104114
import org.jruby.runtime.MethodIndex;
115+
import org.jruby.runtime.MixedModeIRBlockBody;
105116
import org.jruby.runtime.ObjectAllocator;
106117
import org.jruby.runtime.ThreadContext;
107118
import org.jruby.runtime.Visibility;
@@ -1616,7 +1627,7 @@ private CacheEntry refinedMethodOriginalMethodEntry(Map<RubyModule, RubyModule>
16161627
* failed to return a result. Cache superclass definitions in this class.
16171628
*
16181629
* MRI: method_entry_get_without_cache
1619-
*
1630+
*
16201631
* @param id The name of the method to search for
16211632
* @param cacheUndef Flag for caching UndefinedMethod. This should normally be true.
16221633
* @return The method, or UndefinedMethod if not found
@@ -2386,19 +2397,67 @@ public IRubyObject defineMethodFromBlock(ThreadContext context, IRubyObject arg0
23862397

23872398
// If we know it comes from IR we can convert this directly to a method and
23882399
// 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.
2400+
if (block.getBody() instanceof IRBlockBody) {
23912401
IRBlockBody body = (IRBlockBody) block.getBody();
23922402
IRClosure closure = body.getScope();
23932403

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;
2404+
// clone for rewriting and optimization specific to define_method
2405+
closure = closure.cloneForInlining(new SimpleCloneInfo(closure, false));
2406+
body = new MixedModeIRBlockBody(closure, body.getSignature());
2407+
block = new Block(body, block.getBinding(), block.type);
2408+
2409+
InterpreterContext ic = closure.prepareFullBuild();
2410+
for (BasicBlock bb : ic.getCFG().getBasicBlocks()) {
2411+
ArrayList<Instr> newList = new ArrayList<>();
2412+
for (Instr instr : bb.getInstrs()) {
2413+
if (instr instanceof SuperInstr) {
2414+
SuperInstr superInstr = (SuperInstr) instr;
2415+
2416+
if (this.isSingleton()) {
2417+
instr = new ClassSuperInstr(
2418+
closure,
2419+
superInstr.getResult(),
2420+
superInstr.getDefiningModule(),
2421+
name,
2422+
superInstr.getCallArgs(),
2423+
superInstr.getClosureArg(),
2424+
superInstr.isPotentiallyRefined());
2425+
} else if (this.isModule()) {
2426+
instr = new ModuleSuperInstr(
2427+
closure,
2428+
superInstr.getResult(),
2429+
name,
2430+
superInstr.getReceiver(),
2431+
superInstr.getCallArgs(),
2432+
superInstr.getClosureArg(),
2433+
superInstr.isPotentiallyRefined());
2434+
} else {
2435+
instr = new InstanceSuperInstr(
2436+
closure,
2437+
superInstr.getResult(),
2438+
superInstr.getDefiningModule(),
2439+
name,
2440+
superInstr.getCallArgs(),
2441+
superInstr.getClosureArg(),
2442+
superInstr.isPotentiallyRefined());
2443+
}
2444+
}
2445+
2446+
newList.add(instr);
2447+
}
2448+
}
2449+
2450+
if (runtime.getInstanceConfig().getCompileMode().shouldJIT()) { // FIXME: Once Interp and Mixed Methods are one class we can fix this to work in interp mode too.
2451+
2452+
// closure may be null from AOT scripts
2453+
if (closure != null) {
2454+
// Ask closure to give us a method equivalent.
2455+
IRMethod method = closure.convertToMethod(name.getBytes());
2456+
if (method != null) {
2457+
newMethod = new DefineMethodMethod(method, visibility, this, context.getFrameBlock());
2458+
Helpers.addInstanceMethod(this, name, newMethod, visibility, context, runtime);
2459+
return name;
2460+
}
24022461
}
24032462
}
24042463
}

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

Lines changed: 24 additions & 8 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'.
4093-
superInstr = new UnresolvedSuperInstr(scope, ret, buildSelf(), args, block, scope.maybeUsingRefinements());
4108+
superInstr = new UnresolvedSuperInstr(scope, getCurrentModuleVariable(), ret, buildSelf(), args, block, scope.maybeUsingRefinements());
40944109
}
4110+
40954111
receiveBreakException(block, superInstr);
40964112
return ret;
40974113
}
@@ -4254,7 +4270,7 @@ private Operand buildZSuperIfNest(final Operand block) {
42544270
Variable zsuperResult = createTemporaryVariable();
42554271
if (superScope instanceof IRMethod && !defineMethod) {
42564272
Operand[] args = adjustVariableDepth(getCallArgs(superScope, superBuilder), depthFromSuper);
4257-
addInstr(new ZSuperInstr(scope, zsuperResult, buildSelf(), args, block, scope.maybeUsingRefinements()));
4273+
addInstr(new ZSuperInstr(scope, zsuperResult, getCurrentModuleVariable(), buildSelf(), args, block, scope.maybeUsingRefinements()));
42584274
} else {
42594275
// We will not have a zsuper show up since we won't emit it but we still need to toggle it.
42604276
// define_method optimization will try and create a method from a closure but it should not in this case.

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
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,21 @@
1818

1919
import java.util.EnumSet;
2020

21-
public class ClassSuperInstr extends CallInstr {
21+
public class ClassSuperInstr extends SuperInstr {
2222
private final boolean isLiteralBlock;
2323

2424
// clone constructor
2525
protected ClassSuperInstr(IRScope scope, Variable result, Operand receiver, RubySymbol name, Operand[] args,
2626
Operand closure, boolean potentiallyRefined, CallSite callSite, long callSiteId) {
27-
super(scope, Operation.CLASS_SUPER, CallType.SUPER, result, name, receiver, args, closure, potentiallyRefined, callSite, callSiteId);
27+
super(scope, Operation.CLASS_SUPER, result, receiver, name, args, closure, potentiallyRefined, callSite, callSiteId);
2828

2929
isLiteralBlock = closure instanceof WrappedIRClosure;
3030
}
3131

3232
// normal constructor
3333
public ClassSuperInstr(IRScope scope, Variable result, Operand definingModule, RubySymbol name, Operand[] args, Operand closure,
3434
boolean isPotentiallyRefined) {
35-
super(scope, Operation.CLASS_SUPER, CallType.SUPER, result, name, definingModule, args, closure, isPotentiallyRefined);
35+
super(scope, Operation.CLASS_SUPER, result, definingModule, name, args, closure, isPotentiallyRefined);
3636

3737
isLiteralBlock = closure instanceof WrappedIRClosure;
3838
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@
4949

5050
import java.util.EnumSet;
5151

52-
public class InstanceSuperInstr extends CallInstr {
52+
public class InstanceSuperInstr extends SuperInstr {
5353
private final boolean isLiteralBlock;
5454

5555
// clone constructor
5656
protected InstanceSuperInstr(IRScope scope, Variable result, Operand definingModule, RubySymbol name, Operand[] args,
5757
Operand closure, boolean isPotentiallyRefined, CallSite callSite, long callSiteId) {
58-
super(scope, Operation.INSTANCE_SUPER, CallType.SUPER, result, name, definingModule, args, closure,
58+
super(scope, Operation.INSTANCE_SUPER, result, definingModule, name, args, closure,
5959
isPotentiallyRefined, callSite, callSiteId);
6060

6161
isLiteralBlock = closure instanceof WrappedIRClosure;
@@ -64,7 +64,7 @@ protected InstanceSuperInstr(IRScope scope, Variable result, Operand definingMod
6464
// normal constructor
6565
public InstanceSuperInstr(IRScope scope, Variable result, Operand definingModule, RubySymbol name, Operand[] args,
6666
Operand closure, boolean isPotentiallyRefined) {
67-
super(scope, Operation.INSTANCE_SUPER, CallType.SUPER, result, name, definingModule, args, closure, isPotentiallyRefined);
67+
super(scope, Operation.INSTANCE_SUPER, result, definingModule, name, args, closure, isPotentiallyRefined);
6868

6969
isLiteralBlock = closure instanceof WrappedIRClosure;
7070
}

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,21 @@
2626

2727
// SSS FIXME: receiver is never used -- being passed in only to meet requirements of CallInstr
2828

29-
public class ModuleSuperInstr extends CallInstr {
29+
public class ModuleSuperInstr extends SuperInstr {
3030
private final boolean isLiteralBlock;
3131

3232
// clone constructor
3333
public ModuleSuperInstr(IRScope scope, Operation op, Variable result, RubySymbol name, Operand receiver, Operand[] args,
3434
Operand closure, boolean isPotentiallyRefined, CallSite callSite, long callSiteId) {
35-
super(scope, op, CallType.SUPER, result, name,
36-
receiver, args, closure, isPotentiallyRefined, callSite, callSiteId);
35+
super(scope, op, result, receiver, name, args, closure, isPotentiallyRefined, callSite, callSiteId);
3736

3837
isLiteralBlock = closure instanceof WrappedIRClosure;
3938
}
4039

4140
// normal constructor
4241
public ModuleSuperInstr(IRScope scope, Operation op, Variable result, RubySymbol name, Operand receiver, Operand[] args, Operand closure,
4342
boolean isPotentiallyRefined) {
44-
super(scope, op, CallType.SUPER, result, name,
45-
receiver, args, closure, isPotentiallyRefined);
43+
super(scope, op, result, receiver, name, args, closure, isPotentiallyRefined);
4644

4745
isLiteralBlock = closure instanceof WrappedIRClosure;
4846
}
@@ -53,6 +51,11 @@ public ModuleSuperInstr(IRScope scope, Variable result, RubySymbol name, Operand
5351
this(scope, Operation.MODULE_SUPER, result, name, receiver, args, closure, isPotentiallyRefined);
5452
}
5553

54+
@Override
55+
public Operand getDefiningModule() {
56+
return getReceiver();
57+
}
58+
5659
@Override
5760
public boolean computeScopeFlags(IRScope scope, EnumSet<IRFlags> flags) {
5861
super.computeScopeFlags(scope, flags);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.jruby.ir.instructions;
2+
3+
import org.jruby.RubyInstanceConfig;
4+
import org.jruby.RubySymbol;
5+
import org.jruby.ir.IRFlags;
6+
import org.jruby.ir.IRScope;
7+
import org.jruby.ir.IRVisitor;
8+
import org.jruby.ir.Operation;
9+
import org.jruby.ir.operands.Operand;
10+
import org.jruby.ir.operands.Variable;
11+
import org.jruby.ir.operands.WrappedIRClosure;
12+
import org.jruby.ir.persistence.IRReaderDecoder;
13+
import org.jruby.ir.runtime.IRRuntimeHelpers;
14+
import org.jruby.ir.transformations.inlining.CloneInfo;
15+
import org.jruby.parser.StaticScope;
16+
import org.jruby.runtime.Block;
17+
import org.jruby.runtime.CallSite;
18+
import org.jruby.runtime.CallType;
19+
import org.jruby.runtime.DynamicScope;
20+
import org.jruby.runtime.ThreadContext;
21+
import org.jruby.runtime.builtin.IRubyObject;
22+
23+
import java.util.EnumSet;
24+
25+
public abstract class SuperInstr extends CallInstr {
26+
private final boolean isLiteralBlock;
27+
28+
// clone constructor
29+
protected SuperInstr(IRScope scope, Operation op, Variable result, Operand receiver, RubySymbol name, Operand[] args,
30+
Operand closure, boolean potentiallyRefined, CallSite callSite, long callSiteId) {
31+
super(scope, op, CallType.SUPER, result, name, receiver, args, closure, potentiallyRefined, callSite, callSiteId);
32+
33+
isLiteralBlock = closure instanceof WrappedIRClosure;
34+
}
35+
36+
// normal constructor
37+
public SuperInstr(IRScope scope, Operation op, Variable result, Operand definingModule, RubySymbol name, Operand[] args, Operand closure,
38+
boolean isPotentiallyRefined) {
39+
super(scope, op, CallType.SUPER, result, name, definingModule, args, closure, isPotentiallyRefined);
40+
41+
isLiteralBlock = closure instanceof WrappedIRClosure;
42+
}
43+
44+
public abstract Operand getDefiningModule();
45+
}

0 commit comments

Comments
 (0)