package org.jruby.internal.runtime.methods;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JavaMethodDescriptor;
import org.jruby.anno.TypePopulator;
import org.jruby.compiler.impl.SkinnyMethodAdapter;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.MethodFactory;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.ClassDefiningJRubyClassLoader;
import org.jruby.util.CodegenUtils;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.CheckClassAdapter;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
import static org.jruby.util.CodegenUtils.ci;
import static org.jruby.util.CodegenUtils.p;
import static org.jruby.util.CodegenUtils.params;
import static org.jruby.util.CodegenUtils.sig;
public class InvocationMethodFactory extends MethodFactory implements Opcodes {
private static final Logger LOG = LoggerFactory.getLogger(InvocationMethodFactory.class);
private static final boolean DEBUG = false;
private final static String COMPILED_CALL_SIG = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject[].class));
private final static String COMPILED_CALL_SIG_BLOCK = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject[].class, Block.class));
private final static String COMPILED_CALL_SIG_ZERO_BLOCK = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, Block.class));
private final static String COMPILED_CALL_SIG_ZERO = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class));
private final static String COMPILED_CALL_SIG_ONE_BLOCK = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, Block.class));
private final static String COMPILED_CALL_SIG_ONE = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class));
private final static String COMPILED_CALL_SIG_TWO_BLOCK = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class, Block.class));
private final static String COMPILED_CALL_SIG_TWO = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class));
private final static String COMPILED_CALL_SIG_THREE_BLOCK = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class, IRubyObject.class, Block.class));
private final static String COMPILED_CALL_SIG_THREE = sig(IRubyObject.class,
params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject.class, IRubyObject.class, IRubyObject.class));
private final static String JAVA_SUPER_SIG = sig(Void.TYPE, params(RubyModule.class, Visibility.class, String.class));
public static final int THIS_INDEX = 0;
public static final int THREADCONTEXT_INDEX = 1;
public static final int RECEIVER_INDEX = 2;
public static final int CLASS_INDEX = 3;
public static final int NAME_INDEX = 4;
public static final int ARGS_INDEX = 5;
public static final int BLOCK_INDEX = 6;
protected final ClassDefiningJRubyClassLoader classLoader;
protected final Object syncObject;
private boolean seenUndefinedClasses = false;
private boolean haveWarnedUser = false;
public InvocationMethodFactory(ClassLoader classLoader) {
this.syncObject = classLoader;
if (classLoader instanceof ClassDefiningJRubyClassLoader) {
this.classLoader = (ClassDefiningJRubyClassLoader) classLoader;
} else {
this.classLoader = new ClassDefiningJRubyClassLoader(classLoader);
}
}
private static final Class[] RubyModule_and_Visibility_and_Name = new Class[]{ RubyModule.class, Visibility.class, String.class };
public DynamicMethod getAnnotatedMethod(RubyModule implementationClass, List<JavaMethodDescriptor> descs, String name) {
JavaMethodDescriptor desc1 = descs.get(0);
final String javaMethodName = desc1.name;
if (DEBUG) LOG.debug("Binding multiple: " + desc1.declaringClassName + '.' + javaMethodName);
try {
Class c = getAnnotatedMethodClass(descs);
DescriptorInfo info = new DescriptorInfo(descs);
if (DEBUG) LOG.debug(" min: " + info.getMin() + ", max: " + info.getMax());
JavaMethod ic = constructJavaMethod(implementationClass, desc1, name, c);
TypePopulator.populateMethod(
ic,
Arity.optional().getValue(),
javaMethodName,
desc1.isStatic,
desc1.anno.notImplemented(),
desc1.declaringClass,
desc1.name,
desc1.returnClass,
desc1.parameters);
return ic;
} catch(Exception e) {
LOG.error(e);
throw implementationClass.getRuntime().newLoadError(e.getMessage());
}
}
public Class getAnnotatedMethodClass(List<JavaMethodDescriptor> descs) {
JavaMethodDescriptor desc1 = descs.get(0);
if (!Modifier.isPublic(desc1.declaringClass.getModifiers())) {
LOG.warn("binding non-public class {} reflected handles won't work", desc1.declaringClassName);
}
String javaMethodName = desc1.name;
if (DEBUG) {
if (descs.size() > 1) {
LOG.debug("Binding multiple: " + desc1.declaringClassName + '.' + javaMethodName);
} else {
LOG.debug("Binding single: " + desc1.declaringClassName + '.' + javaMethodName);
}
}
String generatedClassName = CodegenUtils.getAnnotatedBindingClassName(javaMethodName, desc1.declaringClassName, desc1.isStatic, desc1.actualRequired, desc1.optional, descs.size() > 1, desc1.anno.frame());
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
generatedClassName += "_DBG";
}
String generatedClassPath = generatedClassName.replace('.', '/');
DescriptorInfo info = new DescriptorInfo(descs);
Class superclass = determineSuperclass(info);
Class c = tryClass(generatedClassName, desc1.declaringClass, superclass);
if (c == null) {
synchronized (syncObject) {
c = tryClass(generatedClassName, desc1.declaringClass, superclass);
if (c == null) {
if (DEBUG) LOG.debug("Generating " + generatedClassName + ", min: " + info.getMin() + ", max: " + info.getMax() + ", hasBlock: " + info.isBlock() + ", rest: " + info.isRest());
String superClassString = p(superclass);
ClassWriter cw = createJavaMethodCtor(generatedClassPath, superClassString, info.getParameterDesc());
addAnnotatedMethodInvoker(cw, superClassString, descs);
c = endClass(cw, generatedClassName);
}
}
}
return c;
}
private static Class determineSuperclass(DescriptorInfo info) {
Class superClass;
if (info.getMin() == -1) {
if (info.isBlock()) {
superClass = JavaMethod.JavaMethodNBlock.class;
} else {
superClass = JavaMethod.JavaMethodN.class;
}
} else {
if (info.isRest()) {
if (info.isBlock()) {
superClass = JavaMethod.BLOCK_REST_METHODS[info.getMin()][info.getMax()];
} else {
superClass = JavaMethod.REST_METHODS[info.getMin()][info.getMax()];
}
} else {
if (info.isBlock()) {
superClass = JavaMethod.BLOCK_METHODS[info.getMin()][info.getMax()];
} else {
superClass = JavaMethod.METHODS[info.getMin()][info.getMax()];
}
}
}
if (superClass == null) throw new RuntimeException("invalid multi combination");
return superClass;
}
public DynamicMethod getAnnotatedMethod(RubyModule implementationClass, JavaMethodDescriptor desc, String name) {
String javaMethodName = desc.name;
try {
Class c = getAnnotatedMethodClass(Collections.singletonList(desc));
JavaMethod ic = constructJavaMethod(implementationClass, desc, name, c);
TypePopulator.populateMethod(
ic,
Arity.fromAnnotation(desc.anno, desc.actualRequired).getValue(),
javaMethodName,
desc.isStatic,
desc.anno.notImplemented(),
desc.declaringClass,
desc.name,
desc.returnClass,
desc.parameters);
return ic;
} catch(Exception e) {
LOG.error(e);
throw implementationClass.getRuntime().newLoadError(e.getMessage());
}
}
@SuppressWarnings("deprecation")
public JavaMethod constructJavaMethod(RubyModule implementationClass, JavaMethodDescriptor desc, String name, Class c) throws InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException, NoSuchMethodException {
DynamicMethod.Version version = (DynamicMethod.Version) c.getAnnotation(DynamicMethod.Version.class);
JavaMethod ic;
if (version == null) {
JavaMethod.NAME_PASSER.set(name);
try {
ic = (JavaMethod) c.getConstructor(RubyModule_and_Visibility).newInstance(implementationClass, desc.anno.visibility());
} finally {
JavaMethod.NAME_PASSER.remove();
}
} else {
ic = (JavaMethod) c.getConstructor(RubyModule_and_Visibility_and_Name).newInstance(implementationClass, desc.anno.visibility(), name);
}
return ic;
}
private static void checkArity(JRubyMethod jrubyMethod, SkinnyMethodAdapter method, int specificArity) {
switch (specificArity) {
case 0:
case 1:
case 2:
case 3:
return;
default:
final Label arityError = new Label();
final Label noArityError = new Label();
boolean checkArity = false;
if (jrubyMethod.rest()) {
if (jrubyMethod.required() > 0) {
method.aload(ARGS_INDEX);
method.arraylength();
method.ldc(jrubyMethod.required());
method.if_icmplt(arityError);
checkArity = true;
}
} else if (jrubyMethod.optional() > 0) {
if (jrubyMethod.required() > 0) {
method.aload(ARGS_INDEX);
method.arraylength();
method.ldc(jrubyMethod.required());
method.if_icmplt(arityError);
}
method.aload(ARGS_INDEX);
method.arraylength();
method.ldc(jrubyMethod.required() + jrubyMethod.optional());
method.if_icmpgt(arityError);
checkArity = true;
} else {
method.aload(ARGS_INDEX);
method.arraylength();
method.ldc(jrubyMethod.required());
method.if_icmpne(arityError);
checkArity = true;
}
if (checkArity) {
method.go_to(noArityError);
method.label(arityError);
method.aload(THREADCONTEXT_INDEX);
method.invokevirtual(p(ThreadContext.class), "getRuntime", sig(Ruby.class));
method.aload(ARGS_INDEX);
method.ldc(jrubyMethod.required());
method.ldc(jrubyMethod.required() + jrubyMethod.optional());
method.invokestatic(p(Arity.class), "checkArgumentCount", sig(int.class, Ruby.class, IRubyObject[].class, int.class, int.class));
method.pop();
method.label(noArityError);
}
}
}
private static ClassWriter createJavaMethodCtor(String namePath, String sup, String parameterDesc) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
String sourceFile = namePath.substring(namePath.lastIndexOf('/') + 1) + ".gen";
cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, namePath, null, sup, null);
cw.visitSource(sourceFile, null);
AnnotationVisitor av = cw.visitAnnotation(ci(DynamicMethod.Version.class), true);
av.visit("version", 0);
av.visitEnd();
SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", JAVA_SUPER_SIG, null, null);
mv.start();
mv.aloadMany(0, 1, 2, 3);
mv.invokespecial(sup, "<init>", JAVA_SUPER_SIG);
mv.aload(0);
mv.ldc(parameterDesc.toString());
mv.invokevirtual(p(JavaMethod.class), "setParameterDesc", sig(void.class, String.class));
mv.voidreturn();
mv.end();
return cw;
}
private void invokeCallConfigPre(SkinnyMethodAdapter mv, String superClass, int specificArity, boolean block, CallConfiguration callConfig) {
if (callConfig.isNoop()) return;
prepareForPre(mv, specificArity, block, callConfig);
mv.invokevirtual(superClass, getPreMethod(callConfig), getPreSignature(callConfig));
}
private void invokeCallConfigPost(SkinnyMethodAdapter mv, String superClass, CallConfiguration callConfig) {
if (callConfig.isNoop()) return;
mv.aload(1);
mv.invokestatic(superClass, getPostMethod(callConfig), sig(void.class, params(ThreadContext.class)));
}
private static void prepareForPre(SkinnyMethodAdapter mv, int specificArity, boolean block, CallConfiguration callConfig) {
if (callConfig.isNoop()) return;
mv.aloadMany(0, THREADCONTEXT_INDEX);
switch (callConfig.framing()) {
case Full:
mv.aloadMany(RECEIVER_INDEX, CLASS_INDEX, NAME_INDEX);
loadBlockForPre(mv, specificArity, block);
break;
case Backtrace:
mv.aload(NAME_INDEX);
break;
case None:
break;
default: throw new RuntimeException("Unknown call configuration");
}
}
private static String getPreMethod(CallConfiguration callConfig) {
switch (callConfig) {
case FrameFullScopeFull: return "preFrameAndScope";
case FrameFullScopeDummy: return "preFrameAndDummyScope";
case FrameFullScopeNone: return "preFrameOnly";
case FrameBacktraceScopeFull: return "preBacktraceAndScope";
case FrameBacktraceScopeDummy: return "preBacktraceDummyScope";
case FrameBacktraceScopeNone: return "preBacktraceOnly";
case FrameNoneScopeFull: return "preScopeOnly";
case FrameNoneScopeDummy: return "preNoFrameDummyScope";
case FrameNoneScopeNone: return "preNoop";
default: throw new RuntimeException("Unknown call configuration");
}
}
private static String getPreSignature(CallConfiguration callConfig) {
switch (callConfig) {
case FrameFullScopeFull: return sig(void.class, params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, Block.class));
case FrameFullScopeDummy: return sig(void.class, params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, Block.class));
case FrameFullScopeNone: return sig(void.class, params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, Block.class));
case FrameBacktraceScopeFull: return sig(void.class, params(ThreadContext.class, String.class));
case FrameBacktraceScopeDummy: return sig(void.class, params(ThreadContext.class, String.class));
case FrameBacktraceScopeNone: return sig(void.class, params(ThreadContext.class, String.class));
case FrameNoneScopeFull: return sig(void.class, params(ThreadContext.class));
case FrameNoneScopeDummy: return sig(void.class, params(ThreadContext.class));
case FrameNoneScopeNone: return sig(void.class);
default: throw new RuntimeException("Unknown call configuration");
}
}
public static String getPostMethod(CallConfiguration callConfig) {
switch (callConfig) {
case FrameFullScopeFull: return "postFrameAndScope";
case FrameFullScopeDummy: return "postFrameAndScope";
case FrameFullScopeNone: return "postFrameOnly";
case FrameBacktraceScopeFull: return "postBacktraceAndScope";
case FrameBacktraceScopeDummy: return "postBacktraceDummyScope";
case FrameBacktraceScopeNone: return "postBacktraceOnly";
case FrameNoneScopeFull: return "postScopeOnly";
case FrameNoneScopeDummy: return "postNoFrameDummyScope";
case FrameNoneScopeNone: return "postNoop";
default: throw new RuntimeException("Unknown call configuration");
}
}
private static void loadArguments(SkinnyMethodAdapter mv, JavaMethodDescriptor desc, int specificArity) {
Class[] argumentTypes;
switch (specificArity) {
default:
case -1:
mv.aload(ARGS_INDEX);
break;
case 0:
break;
case 1:
argumentTypes = desc.getArgumentTypes();
loadArgumentWithCast(mv, 1, argumentTypes[0]);
break;
case 2:
argumentTypes = desc.getArgumentTypes();
loadArgumentWithCast(mv, 1, argumentTypes[0]);
loadArgumentWithCast(mv, 2, argumentTypes[1]);
break;
case 3:
argumentTypes = desc.getArgumentTypes();
loadArgumentWithCast(mv, 1, argumentTypes[0]);
loadArgumentWithCast(mv, 2, argumentTypes[1]);
loadArgumentWithCast(mv, 3, argumentTypes[2]);
break;
}
}
private static void loadArgumentWithCast(SkinnyMethodAdapter mv, int argNumber, Class coerceType) {
mv.aload(ARGS_INDEX + (argNumber - 1));
if (coerceType != IRubyObject.class && coerceType != IRubyObject[].class) {
if (coerceType == RubyString.class) {
mv.invokeinterface(p(IRubyObject.class), "convertToString", sig(RubyString.class));
} else {
throw new RuntimeException("Unknown coercion target: " + coerceType);
}
}
}
private static void loadBlockForPre(SkinnyMethodAdapter mv, int specificArity, boolean getsBlock) {
if (!getsBlock) {
mv.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class));
return;
}
loadBlock(mv, specificArity, getsBlock);
}
private static void loadBlock(SkinnyMethodAdapter mv, int specificArity, boolean getsBlock) {
if (!getsBlock) return;
switch (specificArity) {
case 0: case 1: case 2: case 3:
mv.aload(BLOCK_INDEX - 1 + specificArity);
break;
default: case -1:
mv.aload(BLOCK_INDEX);
break;
}
}
private static void loadReceiver(String typePath, JavaMethodDescriptor desc, SkinnyMethodAdapter mv) {
if (Modifier.isStatic(desc.modifiers)) {
if (desc.hasContext) {
mv.aload(THREADCONTEXT_INDEX);
}
mv.aload(RECEIVER_INDEX);
} else {
mv.aload(RECEIVER_INDEX);
mv.checkcast(typePath);
if (desc.hasContext) {
mv.aload(THREADCONTEXT_INDEX);
}
}
}
private Class tryClass(String name, Class targetClass, Class expectedSuperclass) {
final Class c;
try {
if (classLoader == null) {
c = Class.forName(name, true, classLoader);
} else {
c = classLoader.loadClass(name);
}
} catch (ClassNotFoundException e) {
if (DEBUG) LOG.debug(e);
seenUndefinedClasses = true;
return null;
} catch (Exception e) {
if (DEBUG) LOG.warn(e);
seenUndefinedClasses = true;
return null;
}
if (c.getSuperclass() == expectedSuperclass) {
if (seenUndefinedClasses && !haveWarnedUser) {
haveWarnedUser = true;
System.err.println("WARNING: while creating new bindings for " + targetClass + ",\n" +
"found an existing binding; you may want to run a clean build.");
}
return c;
} else {
seenUndefinedClasses = true;
return null;
}
}
protected Class endClass(ClassWriter cw, String name) {
cw.visitEnd();
final byte[] code = cw.toByteArray();
if (DEBUG) CheckClassAdapter.verify(new ClassReader(code), classLoader, false, new PrintWriter(System.err));
return classLoader.defineClass(name, code);
}
private SkinnyMethodAdapter beginMethod(ClassWriter cw, String methodName, int specificArity, boolean block) {
switch (specificArity) {
default:
case -1:
if (block) {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_BLOCK, null, null);
} else {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG, null, null);
}
case 0:
if (block) {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ZERO_BLOCK, null, null);
} else {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ZERO, null, null);
}
case 1:
if (block) {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ONE_BLOCK, null, null);
} else {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_ONE, null, null);
}
case 2:
if (block) {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_TWO_BLOCK, null, null);
} else {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_TWO, null, null);
}
case 3:
if (block) {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_THREE_BLOCK, null, null);
} else {
return new SkinnyMethodAdapter(cw, ACC_PUBLIC, methodName, COMPILED_CALL_SIG_THREE, null, null);
}
}
}
private void addAnnotatedMethodInvoker(ClassWriter cw, String superClass, List<JavaMethodDescriptor> descs) {
for (JavaMethodDescriptor desc: descs) {
int specificArity = desc.calculateSpecificCallArity();
SkinnyMethodAdapter mv = beginMethod(cw, "call", specificArity, desc.hasBlock);
mv.visitCode();
createAnnotatedMethodInvocation(desc, mv, superClass, specificArity, desc.hasBlock);
mv.end();
}
}
private void createAnnotatedMethodInvocation(JavaMethodDescriptor desc, SkinnyMethodAdapter method, String superClass, int specificArity, boolean block) {
String typePath = desc.declaringClassPath;
String javaMethodName = desc.name;
checkArity(desc.anno, method, specificArity);
CallConfiguration callConfig = CallConfiguration.getCallConfigByAnno(desc.anno);
if (!callConfig.isNoop()) {
invokeCallConfigPre(method, superClass, specificArity, block, callConfig);
}
final boolean FULL_TRACE_ENABLED = RubyInstanceConfig.FULL_TRACE_ENABLED;
int traceBoolIndex = -1;
if (FULL_TRACE_ENABLED) {
switch (specificArity) {
case -1:
traceBoolIndex = ARGS_INDEX + (block ? 1 : 0) + 1;
break;
case 0:
traceBoolIndex = ARGS_INDEX + (block ? 1 : 0);
break;
default:
traceBoolIndex = ARGS_INDEX + specificArity + (block ? 1 : 0) + 1;
}
method.aload(1);
method.invokevirtual(p(ThreadContext.class), "getRuntime", sig(Ruby.class));
method.invokevirtual(p(Ruby.class), "hasEventHooks", sig(boolean.class));
method.istore(traceBoolIndex);
invokeCCallTrace(method, traceBoolIndex);
}
Label tryBegin = new Label();
Label tryEnd = new Label();
Label doFinally = new Label();
if (!callConfig.isNoop()) {
method.trycatch(tryBegin, tryEnd, doFinally, null);
}
method.label(tryBegin);
{
loadReceiver(typePath, desc, method);
loadArguments(method, desc, specificArity);
loadBlock(method, specificArity, block);
if (Modifier.isStatic(desc.modifiers)) {
method.invokestatic(typePath, javaMethodName, sig(desc.returnClass, desc.parameters));
} else {
method.invokevirtual(typePath, javaMethodName, sig(desc.returnClass, desc.parameters));
}
if (desc.returnClass == void.class) {
method.aload(THREADCONTEXT_INDEX);
method.getfield(p(ThreadContext.class), "nil", ci(IRubyObject.class));
}
}
method.label(tryEnd);
{
if (FULL_TRACE_ENABLED) {
invokeCReturnTrace(method, traceBoolIndex);
}
if (!callConfig.isNoop()) {
invokeCallConfigPost(method, superClass, callConfig);
}
method.visitInsn(ARETURN);
}
if (!callConfig.isNoop()) {
{
method.label(doFinally);
if (FULL_TRACE_ENABLED) {
invokeCReturnTrace(method, traceBoolIndex);
}
if (!callConfig.isNoop()) {
invokeCallConfigPost(method, superClass, callConfig);
}
method.athrow();
}
}
}
private static void invokeCCallTrace(SkinnyMethodAdapter method, int traceBoolIndex) {
method.aloadMany(0, 1);
method.iload(traceBoolIndex);
method.aload(4);
method.invokevirtual(p(JavaMethod.class), "callTrace", sig(void.class, ThreadContext.class, boolean.class, String.class));
}
private static void invokeCReturnTrace(SkinnyMethodAdapter method, int traceBoolIndex) {
method.aloadMany(0, 1);
method.iload(traceBoolIndex);
method.aload(4);
method.invokevirtual(p(JavaMethod.class), "returnTrace", sig(void.class, ThreadContext.class, boolean.class, String.class));
}
@Deprecated
private static final Class[] RubyModule_and_Visibility = new Class[]{ RubyModule.class, Visibility.class };
}