Skip to content

Commit

Permalink
Use static name for super in a module method
Browse files Browse the repository at this point in the history
Module methods have a dynamic hierarchy, but do not have a dynamic
super target name. This eliminates part of the frame requirement
for super within a module method, on the way to getting __callee__
working.
  • Loading branch information
headius committed Feb 14, 2022
1 parent ced1962 commit a760016
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 0 deletions.
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -4083,6 +4083,8 @@ private Operand buildSuperInstr(Operand block, Operand[] args) {
} else {
superInstr = new ClassSuperInstr(scope, ret, getCurrentModuleVariable(), getName(), args, block, scope.maybeUsingRefinements());
}
} else if (scope instanceof IRMethod && scope.getLexicalParent() instanceof IRModuleBody) {
superInstr = new ModuleSuperInstr(scope, ret, getName(), buildSelf(), args, block, scope.maybeUsingRefinements());
} else {
// We dont always know the method name we are going to be invoking if the super occurs in a closure.
// This is because the super can be part of a block that will be used by 'define_method' to define
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/IRVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ private void error(Object object) {
public void LoadFrameClosure(LoadFrameClosureInstr loadframeclosureinstr) { error(loadframeclosureinstr); }
public void LoadBlockImplicitClosure(LoadBlockImplicitClosureInstr loadblockimplicitclosureinstr) { error(loadblockimplicitclosureinstr); }
public void MatchInstr(MatchInstr matchInstr) { error(matchInstr); }
public void ModuleSuperInstr(ModuleSuperInstr modulesuperinstr) { error(modulesuperinstr); }
public void ModuleVersionGuardInstr(ModuleVersionGuardInstr moduleversionguardinstr) { error(moduleversionguardinstr); }
public void NonlocalReturnInstr(NonlocalReturnInstr nonlocalreturninstr) { error(nonlocalreturninstr); }
public void NopInstr(NopInstr nopinstr) { error(nopinstr); }
Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/Operation.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public enum Operation {
AS_STRING(OpFlags.f_is_call | OpFlags.f_has_side_effect | OpFlags.f_can_raise_exception),
CLASS_SUPER(OpFlags.f_has_side_effect | OpFlags.f_is_call | OpFlags.f_can_raise_exception),
INSTANCE_SUPER(OpFlags.f_has_side_effect | OpFlags.f_is_call | OpFlags.f_can_raise_exception),
MODULE_SUPER(OpFlags.f_has_side_effect | OpFlags.f_is_call | OpFlags.f_can_raise_exception),
UNRESOLVED_SUPER(OpFlags.f_has_side_effect | OpFlags.f_is_call | OpFlags.f_can_raise_exception),
ZSUPER(OpFlags.f_has_side_effect | OpFlags.f_is_call | OpFlags.f_can_raise_exception),

Expand Down
112 changes: 112 additions & 0 deletions core/src/main/java/org/jruby/ir/instructions/ModuleSuperInstr.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package org.jruby.ir.instructions;

import org.jcodings.specific.USASCIIEncoding;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubySymbol;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.operands.WrappedIRClosure;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.ir.transformations.inlining.CloneInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallSite;
import org.jruby.runtime.CallType;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;

import java.util.EnumSet;

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

public class ModuleSuperInstr extends CallInstr {
// clone constructor
public ModuleSuperInstr(IRScope scope, Operation op, Variable result, RubySymbol name, Operand receiver, Operand[] args,
Operand closure, boolean isPotentiallyRefined, CallSite callSite, long callSiteId) {
super(scope, op, CallType.SUPER, result, name,
receiver, args, closure, isPotentiallyRefined, callSite, callSiteId);
}

// normal constructor
public ModuleSuperInstr(IRScope scope, Operation op, Variable result, RubySymbol name, Operand receiver, Operand[] args, Operand closure,
boolean isPotentiallyRefined) {
super(scope, op, CallType.SUPER, result, name,
receiver, args, closure, isPotentiallyRefined);
}

// specific instr constructor
public ModuleSuperInstr(IRScope scope, Variable result, RubySymbol name, Operand receiver, Operand[] args, Operand closure,
boolean isPotentiallyRefined) {
this(scope, Operation.UNRESOLVED_SUPER, result, name, receiver, args, closure, isPotentiallyRefined);
}

@Override
public boolean computeScopeFlags(IRScope scope, EnumSet<IRFlags> flags) {
super.computeScopeFlags(scope, flags);
scope.setUsesSuper();
flags.add(IRFlags.REQUIRES_CLASS); // for current class and method name
flags.add(IRFlags.REQUIRES_METHODNAME); // for current class and method name
return true;
}

@Override
public Instr clone(CloneInfo ii) {
return new ModuleSuperInstr(ii.getScope(), Operation.UNRESOLVED_SUPER, ii.getRenamedVariable(getResult()), name,
getReceiver().cloneForInlining(ii), cloneCallArgs(ii),
getClosureArg() == null ? null : getClosureArg().cloneForInlining(ii),
isPotentiallyRefined(), getCallSite(), getCallSiteId());
}

public static ModuleSuperInstr decode(IRReaderDecoder d) {
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("decoding call");
int callTypeOrdinal = d.decodeInt();
CallType callType = CallType.fromOrdinal(callTypeOrdinal);
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("decoding call, calltype(ord): " + callType);
RubySymbol methAddr = d.decodeSymbol();
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("decoding call, methaddr: " + methAddr);
Operand receiver = d.decodeOperand();
int argsCount = d.decodeInt();
boolean hasClosureArg = argsCount < 0;
int argsLength = hasClosureArg ? (-1 * (argsCount + 1)) : argsCount;
if (RubyInstanceConfig.IR_READING_DEBUG)
System.out.println("ARGS: " + argsLength + ", CLOSURE: " + hasClosureArg);
Operand[] args = new Operand[argsLength];

for (int i = 0; i < argsLength; i++) {
args[i] = d.decodeOperand();
}

Operand closure = hasClosureArg ? d.decodeOperand() : null;
if (RubyInstanceConfig.IR_READING_DEBUG) System.out.println("before result");

return new ModuleSuperInstr(d.getCurrentScope(), d.decodeVariable(), d.decodeSymbol(), receiver, args, closure, d.getCurrentScope().maybeUsingRefinements());
}

/*
// We cannot convert this into a NoCallResultInstr
@Override
public Instr discardResult() {
return this;
}
*/

@Override
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) {
IRubyObject[] args = prepareArguments(context, self, currScope, currDynScope, temp);
Block block = prepareBlock(context, self, currScope, currDynScope, temp);

return IRRuntimeHelpers.moduleSuper(context, self, getId(), args, block);
}

@Override
public void visit(IRVisitor visitor) {
visitor.ModuleSuperInstr(this);
}
}
25 changes: 25 additions & 0 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,11 @@ public static IRubyObject unresolvedSuperSplatArgs(ThreadContext context, IRubyO
return unresolvedSuper(context, self, splatArguments(args, splatMap), block);
}

@JIT
public static IRubyObject moduleSuperSplatArgs(ThreadContext context, IRubyObject self, String id, IRubyObject[] args, Block block, boolean[] splatMap) {
return moduleSuper(context, self, id, splatArguments(args, splatMap), block);
}

@JIT
public static IRubyObject unresolvedSuperIterSplatArgs(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block, boolean[] splatMap) {
return unresolvedSuperIter(context, self, splatArguments(args, splatMap), block);
Expand All @@ -1330,6 +1335,26 @@ public static IRubyObject unresolvedSuper(ThreadContext context, IRubyObject sel
return rVal;
}

@Interp
public static IRubyObject moduleSuper(ThreadContext context, IRubyObject self, String id, IRubyObject[] args, Block block) {
// We have to rely on the frame stack to find the implementation class
RubyModule klazz = context.getFrameKlazz();

Helpers.checkSuperDisabledOrOutOfMethod(context, klazz, id);

RubyClass superClass = searchNormalSuperclass(klazz);
CacheEntry entry = superClass != null ? superClass.searchWithCache(id) : CacheEntry.NULL_CACHE;

IRubyObject rVal;
if (entry.method.isUndefined()) {
rVal = Helpers.callMethodMissing(context, self, entry.method.getVisibility(), id, CallType.SUPER, args, block);
} else {
rVal = entry.method.call(context, self, entry.sourceModule, id, args, block);
}

return rVal;
}

@Interp
public static IRubyObject unresolvedSuperIter(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
try {
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/InvocationCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ public interface InvocationCompiler {
*/
void invokeClassSuper(String file, String name, int arity, boolean hasClosure, boolean literalClosure, boolean[] splatmap);

/**
* Invoke a superclass method from a module context.
*
* Stack required: context, caller, self, arguments[, block]
* @param file the filename of the script making this call
* @param name name of the method to invoke
* @param arity arity of the arguments on the stack
* @param hasClosure whether a block is passed
* @param literalClosure whether the block passed is a literal closure
* @param splatmap a map of arguments to be splatted back into arg list
*/
void invokeModuleSuper(String file, String name, int arity, boolean hasClosure, boolean literalClosure, boolean[] splatmap);

/**
* Invoke a superclass method from an unresolved context.
*
Expand Down
15 changes: 15 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,9 @@ private void superCommon(String name, CallInstr instr, Operand[] args, Operand d
case CLASS_SUPER:
m.getInvocationCompiler().invokeClassSuper(file, name, args.length, hasClosure, literalClosure, splatMap);
break;
case MODULE_SUPER:
m.getInvocationCompiler().invokeModuleSuper(file, name, args.length, hasClosure, literalClosure, splatMap);
break;
case UNRESOLVED_SUPER:
m.getInvocationCompiler().invokeUnresolvedSuper(file, name, args.length, hasClosure, literalClosure, splatMap);
break;
Expand Down Expand Up @@ -1714,6 +1717,18 @@ public void MatchInstr(MatchInstr matchInstr) {
compileCallCommon(jvmMethod(), matchInstr);
}

@Override
public void ModuleSuperInstr(ModuleSuperInstr modulesuperinstr) {
String name = modulesuperinstr.getId();
Operand[] args = modulesuperinstr.getCallArgs();
// this would be getDefiningModule but that is not used for module super
Operand definingModule = UndefinedValue.UNDEFINED;
boolean[] splatMap = modulesuperinstr.splatMap();
Operand closure = modulesuperinstr.getClosureArg(null);

superCommon(name, modulesuperinstr, args, definingModule, splatMap, closure);
}

@Override
public void ModuleVersionGuardInstr(ModuleVersionGuardInstr moduleversionguardinstr) {
// SSS FIXME: Unused at this time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ public void invokeClassSuper(String file, String name, int arity, boolean hasClo
}
}

public void invokeModuleSuper(String file, String name, int arity, boolean hasClosure, boolean literalClosure, boolean[] splatmap) {
if (arity > IRBytecodeAdapter.MAX_ARGUMENTS)
throw new NotCompilableException("call to unresolved super has more than " + IRBytecodeAdapter.MAX_ARGUMENTS + " arguments");

String splatmapString = IRRuntimeHelpers.encodeSplatmap(splatmap);
compiler.adapter.invokedynamic("invokeModuleSuper:" + JavaNameMangler.mangleMethodName(name), sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, RubyClass.class, JVM.OBJECT, arity)), Bootstrap.invokeSuper(), splatmapString, file, compiler.getLastLine());
}

public void invokeUnresolvedSuper(String file, String name, int arity, boolean hasClosure, boolean literalClosure, boolean[] splatmap) {
if (arity > IRBytecodeAdapter.MAX_ARGUMENTS)
throw new NotCompilableException("call to unresolved super has more than " + IRBytecodeAdapter.MAX_ARGUMENTS + " arguments");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.jruby.ir.targets.indy;

import org.jruby.RubyClass;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.invoke.MethodType;

/**
* Created by headius on 10/23/14.
*/
public class ModuleSuperInvokeSite extends SuperInvokeSite {
public ModuleSuperInvokeSite(MethodType type, String name, String splatmapString, String file, int line) {
super(type, name, splatmapString, file, line);
}

public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
// TODO: get rid of caller
// TODO: caching
return IRRuntimeHelpers.moduleSuperSplatArgs(context, self, superName, args, block, splatMap);
}

public IRubyObject fail(ThreadContext context, IRubyObject caller, IRubyObject self, RubyClass definingModule, IRubyObject[] args, Block block) throws Throwable {
return invoke(context, caller, self, definingModule, args, block);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, Metho
case "invokeClassSuperIter":
site = new ClassSuperIterInvokeSite(type, superName, splatmapString, file, line);
break;
case "invokeModuleSuper":
site = new ModuleSuperInvokeSite(type, superName, splatmapString, file, line);
break;
case "invokeUnresolvedSuper":
site = new UnresolvedSuperInvokeSite(type, superName, splatmapString, file, line);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,16 @@ public void invokeClassSuper(String file, String name, int arity, boolean hasClo
performSuper(file, compiler.getLastLine(), name, arity, hasClosure, splatmap, noSplatMethod, splatMethod, false);
}

public void invokeModuleSuper(String file, String name, int arity, boolean hasClosure, boolean literalClosure, boolean[] splatmap) {
if (arity > IRBytecodeAdapter.MAX_ARGUMENTS)
throw new NotCompilableException("call to unresolved super has more than " + IRBytecodeAdapter.MAX_ARGUMENTS + " arguments");

String noSplatMethod = "moduleSuper";
String splatMethod = "moduleSuperSplatArgs";

performSuper(file, compiler.getLastLine(), name, arity, hasClosure, splatmap, noSplatMethod, splatMethod, true);
}

public void invokeUnresolvedSuper(String file, String name, int arity, boolean hasClosure, boolean literalClosure, boolean[] splatmap) {
if (arity > IRBytecodeAdapter.MAX_ARGUMENTS)
throw new NotCompilableException("call to unresolved super has more than " + IRBytecodeAdapter.MAX_ARGUMENTS + " arguments");
Expand Down

0 comments on commit a760016

Please sign in to comment.