Skip to content

Commit

Permalink
Kill unused method invoker logic and related classes.
Browse files Browse the repository at this point in the history
We have largely committed to JRuby 9k being Java 7+, and to
simplify the process of binding jitted method bodies we are using
MethodHandle exclusively. Because of this, and because MHs work
anywhere reflection works, we are removing the bytecode-generate
CompiledMethod and the Reflected*Method.

There's also a bit of peripheral cleanup for code that is defunct
with these classes removed.
headius committed Jun 30, 2015
1 parent 9d2d570 commit 3f44a10
Showing 11 changed files with 2 additions and 2,166 deletions.

This file was deleted.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -76,123 +76,6 @@ public InvokeDynamicMethodFactory(ClassLoader classLoader) {
super(classLoader);
}

/**
* Use code generation to provide a method handle for a compiled Ruby method.
*
* @see org.jruby.runtime.MethodFactory#getCompiledMethod
*/
@Override
public DynamicMethod getCompiledMethodLazily(
RubyModule implementationClass,
String rubyName,
String javaName,
Visibility visibility,
StaticScope scope,
Object scriptObject,
CallConfiguration callConfig,
ISourcePosition position,
String parameterDesc,
MethodNodes methodNodes) {

return getCompiledMethod(implementationClass, rubyName, javaName, visibility, scope, scriptObject, callConfig, position, parameterDesc, methodNodes);
}

/**
* Use JSR292 to provide a method handle for a compiled Ruby method.
*
* @see org.jruby.runtime.MethodFactory#getCompiledMethod
*/
@Override
public DynamicMethod getCompiledMethod(
RubyModule implementationClass,
String rubyName,
String javaName,
Visibility visibility,
StaticScope scope,
Object scriptObject,
CallConfiguration callConfig,
ISourcePosition position,
String parameterDesc,
MethodNodes methodNodes) {
Class scriptClass = scriptObject.getClass();

try {
MethodHandle[] targets = new MethodHandle[5];
SmartHandle directCall;
int specificArity = -1;

// acquire handle to the actual method body
// FIXME: This passes in Arity but then gets info from static scope?
if (!safeFixedSignature(scope.getSignature())) {
// variable arity method (has optional, rest, or more args than we can splat)
directCall = SmartBinder
.from(VARIABLE_ARITY_SIGNATURE.prependArg("script", scriptClass))
.invokeStaticQuiet(LOOKUP, scriptClass, javaName)
.bindTo(scriptObject);
} else {
// specific arity method (less than 4 required args only)
specificArity = scope.getSignature().required();

directCall = SmartBinder
.from(SPECIFIC_ARITY_SIGNATURES[specificArity].prependArg("script", scriptClass))
.invokeStaticQuiet(LOOKUP, scriptClass, javaName)
.bindTo(scriptObject);
}

// wrap with framing logic if needed
if (!callConfig.isNoop()) {
directCall = SmartHandle
.from(directCall.signature(), InvocationLinker.wrapWithFraming(directCall.signature(), callConfig, implementationClass, rubyName, directCall.handle(), scope));
}

// provide a variable-arity path for specific-arity target
SmartHandle variableCall;
if (specificArity >= 0) {
SmartHandle arityCheck = SmartBinder
.from(ARITY_CHECK_FOLD)
.append(new String[]{"min", "max"}, new Class[]{int.class, int.class}, specificArity, specificArity)
.cast(ARITY_CHECK_SIGNATURE)
.invokeStaticQuiet(LOOKUP, Arity.class, "checkArgumentCount");

variableCall = SmartBinder
.from(VARIABLE_ARITY_SIGNATURE)
.foldVoid(arityCheck)
.permute("script", "context", "self", "block", "args")
.spread("arg", specificArity)
.permute("script", "context", "self", "arg*", "block")
.invoke(directCall);
} else {
variableCall = directCall;
}

// TODO: tracing

// pre-call trace
if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
}

if (specificArity >= 0) {
targets[specificArity] = directCall.handle();
targets[4] = variableCall.handle();
} else {
targets[4] = directCall.handle();
}

return new HandleMethod(implementationClass, visibility, callConfig, targets, parameterDesc);

} catch(Exception e) {
throw new RuntimeException(e);
}
}

@Override
public byte[] getCompiledMethodOffline(
String rubyName, String javaName, String className, String invokerPath,
StaticScope scope, CallConfiguration callConfig, String filename, int line,
MethodNodes methodNodes) {
throw new RuntimeException("no offline support for invokedynamic handles");
}

@Override
public DynamicMethod getAnnotatedMethod(RubyModule implementationClass, List<JavaMethodDescriptor> descs) {
JavaMethodDescriptor desc1 = descs.get(0);
@@ -441,8 +324,4 @@ public DynamicMethod getAnnotatedMethod(RubyModule implementationClass, JavaMeth
.permute("context", "self", "arg*", "block");
}
}

private static final SmartHandle HANDLE_GETTER = SmartBinder
.from(Signature.returning(MethodHandle.class).appendArg("targets", MethodHandle[].class).appendArg("arity", int.class))
.arrayGet();
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

3 changes: 0 additions & 3 deletions core/src/main/java/org/jruby/runtime/DynamicScope.arities.erb

This file was deleted.

372 changes: 0 additions & 372 deletions core/src/main/java/org/jruby/runtime/DynamicScope.erb

This file was deleted.

89 changes: 0 additions & 89 deletions core/src/main/java/org/jruby/runtime/Helpers.java
Original file line number Diff line number Diff line change
@@ -1835,95 +1835,6 @@ private static void callSingletonMethodHook(IRubyObject receiver, ThreadContext
receiver.callMethod(context, "singleton_method_added", name);
}

private static DynamicMethod constructNormalMethod(
MethodFactory factory,
String javaName,
String name,
RubyModule containingClass,
ISourcePosition position,
int arity,
StaticScope scope,
Visibility visibility,
Object scriptObject,
CallConfiguration callConfig,
String parameterDesc,
MethodNodes methodNodes) {

DynamicMethod method;
final Ruby runtime = containingClass.getRuntime();

if (name.equals("initialize") || name.equals("initialize_copy") || name.equals("initialize_clone") || name.equals("initialize_dup") || name.equals("respond_to_missing?") || visibility == Visibility.MODULE_FUNCTION) {
visibility = Visibility.PRIVATE;
}

if (RubyInstanceConfig.LAZYHANDLES_COMPILE) {
method = factory.getCompiledMethodLazily(
containingClass,
name,
javaName,
visibility,
scope,
scriptObject,
callConfig,
position,
parameterDesc,
methodNodes);
} else {
method = factory.getCompiledMethod(
containingClass,
name,
javaName,
visibility,
scope,
scriptObject,
callConfig,
position,
parameterDesc,
methodNodes);
}

return method;
}

private static DynamicMethod constructSingletonMethod(
MethodFactory factory,
String rubyName,
String javaName,
RubyClass rubyClass,
ISourcePosition position,
StaticScope scope,
Object scriptObject,
CallConfiguration callConfig,
String parameterDesc,
MethodNodes methodNodes) {

if (RubyInstanceConfig.LAZYHANDLES_COMPILE) {
return factory.getCompiledMethodLazily(
rubyClass,
rubyName,
javaName,
Visibility.PUBLIC,
scope,
scriptObject,
callConfig,
position,
parameterDesc,
methodNodes);
} else {
return factory.getCompiledMethod(
rubyClass,
rubyName,
javaName,
Visibility.PUBLIC,
scope,
scriptObject,
callConfig,
position,
parameterDesc,
methodNodes);
}
}

public static String encodeScope(StaticScope scope) {
StringBuilder namesBuilder = new StringBuilder(scope.getType().name()); // 0

149 changes: 2 additions & 147 deletions core/src/main/java/org/jruby/runtime/MethodFactory.java
Original file line number Diff line number Diff line change
@@ -28,80 +28,24 @@
***** END LICENSE BLOCK *****/
package org.jruby.runtime;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;

import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.anno.JavaMethodDescriptor;
import org.jruby.internal.runtime.methods.CallConfiguration;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.internal.runtime.methods.ReflectionMethodFactory;
import org.jruby.internal.runtime.methods.InvocationMethodFactory;
import org.jruby.internal.runtime.methods.InvokeDynamicMethodFactory;
import org.jruby.internal.runtime.methods.MethodNodes;
import org.jruby.lexer.yacc.ISourcePosition;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ClassDefiningJRubyClassLoader;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

import java.util.List;

/**
* MethodFactory is used to generate "invokers" or "method handles" given a target
* class, method name, and other characteristics. In order to bind methods into
* Ruby's reified class hierarchy, we need a way to treat individual methods as
* objects. Implementers of this class provide that functionality.
*/
public abstract class MethodFactory {
private static final Logger LOG = LoggerFactory.getLogger("MethodFactory");

/**
* A Class[] representing the signature of compiled Ruby method.
*/
public final static Class[] COMPILED_METHOD_PARAMS = new Class[] {ThreadContext.class, IRubyObject.class, IRubyObject[].class, Block.class};

/**
* A test to see if we can load bytecode dynamically, so we know whether InvocationMethodFactory will work.
*/
public final static boolean CAN_LOAD_BYTECODE;
static {
// any exception or error will cause us to consider bytecode-loading impossible
boolean can = false;
try {
InputStream unloaderStream = Ruby.getClassLoader().getResourceAsStream("org/jruby/util/JDBCDriverUnloader.class");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int bytesRead;
while ((bytesRead = unloaderStream.read(buf)) != -1) {
baos.write(buf, 0, bytesRead);
}

ClassDefiningJRubyClassLoader oscl = new ClassDefiningJRubyClassLoader(Ruby.getClassLoader());
Class<?> unloaderClass = oscl.defineClass("org.jruby.util.JDBCDriverUnloader", baos.toByteArray());
unloaderClass.newInstance();
can = true;
oscl.close();
} catch (Throwable t) {
LOG.debug("MethodFactory: failed to load bytecode at runtime, falling back on reflection", t);
}
CAN_LOAD_BYTECODE = can;
}

/**
* For batched method construction, the logic necessary to bind resulting
* method objects into a target module/class must be provided as a callback.
* This interface should be implemented by code calling any batched methods
* on this MethodFactory.
*/
@Deprecated
public interface MethodDefiningCallback {
public void define(RubyModule targetMetaClass, JavaMethodDescriptor desc, DynamicMethod dynamicMethod);
}

/**
* Based on optional properties, create a new MethodFactory. By default,
* this will create a code-generation-based InvocationMethodFactory. If
@@ -114,9 +58,6 @@ public interface MethodDefiningCallback {
* @return A new MethodFactory.
*/
public static MethodFactory createFactory(ClassLoader classLoader) {
// if reflection is forced or we've determined that we can't load bytecode, use reflection
if (reflection || !CAN_LOAD_BYTECODE) return new ReflectionMethodFactory();

// otherwise, generate invokers at runtime
if (Options.COMPILE_INVOKEDYNAMIC.load() && Options.INVOKEDYNAMIC_HANDLES.load()) {
return new InvokeDynamicMethodFactory(classLoader);
@@ -125,75 +66,6 @@ public static MethodFactory createFactory(ClassLoader classLoader) {
}
}

/**
* Get a new method handle based on the target JRuby-compiled method.
* Because compiled Ruby methods have additional requirements and
* characteristics not typically found in Java-based methods, this is
* provided as a separate way to define such method handles.
*
* @param implementationClass The class to which the method will be bound.
* @param rubyName The Ruby method name to which the method will bind
* @param javaName The name of the method
* @param visibility The method's visibility on the target type.
* @param scope The methods static scoping information.
* @param scriptObject An instace of the target compiled method class.
* @param callConfig The call configuration to use for this method.
* @param position The position to use when generating traceable handles.
* @return A new method handle for the target compiled method.
*/
public abstract DynamicMethod getCompiledMethod(
RubyModule implementationClass, String rubyName, String javaName,
Visibility visibility, StaticScope scope,
Object scriptObject, CallConfiguration callConfig,
ISourcePosition position, String parameterDesc,
MethodNodes methodNodes);

/**
* Like getCompiledMethod, but produces the actual bytes for the compiled
* method handle rather than loading and constructing it. This can be used
* to generate all the handles ahead of time, as when doing a full system
* precompile.
*
* @param rubyName The Ruby method name to which the method will bind
* @param javaName The name of the method
* @param classPath The path-like (with / instead of .) name of the class
* @param invokerPath The path-line name of the invoker to generate
* @param scope The methods static scoping information.
* @param callConfig The call configuration to use for this method.
* @param filename The position to use when generating traceable handles.
* @param line The position to use when generating traceable handles.
* @return
*/
public byte[] getCompiledMethodOffline(
String rubyName, String javaName, String classPath, String invokerPath,
StaticScope scope,
CallConfiguration callConfig, String filename, int line,
MethodNodes methodNodes) {
return null;
}

/**
* Like getCompiledMethod, but postpones any heavy lifting involved in
* creating the method until first invocation. This helps reduce the cost
* of starting up AOT-compiled code, by spreading out the heavy lifting
* across the run rather than causing all method handles to be immediately
* instantiated.
*
* @param implementationClass The class to which the method will be bound.
* @param rubyName The Ruby method name to which the method will bind
* @param javaName The name of the method
* @param visibility The method's visibility on the target type.
* @param scope The methods static scoping information.
* @param scriptObject An instace of the target compiled method class.
* @param callConfig The call configuration to use for this method.
* @return A new method handle for the target compiled method.
*/
public abstract DynamicMethod getCompiledMethodLazily(
RubyModule implementationClass, String rubyName, String javaName,
Visibility visibility, StaticScope scope,
Object scriptObject, CallConfiguration callConfig,
ISourcePosition position, String parameterDesc, MethodNodes methodNodes);

/**
* Based on a list of annotated Java methods, generate a method handle using
* the annotation and the target signatures. The annotation and signatures
@@ -219,21 +91,4 @@ public abstract DynamicMethod getCompiledMethodLazily(
* @return A method handle for the target object.
*/
public abstract DynamicMethod getAnnotatedMethod(RubyModule implementationClass, JavaMethodDescriptor desc);

/**
* Use the reflection-based factory.
*/
private static final boolean reflection;

static {
boolean reflection_ = false, dumping_ = false;
String dumpingPath_ = null;
// initialize the static settings to determine which factory to use
if (Ruby.isSecurityRestricted()) {
reflection_ = true;
} else {
reflection_ = RubyInstanceConfig.REFLECTED_HANDLES;
}
reflection = reflection_;
}
}

0 comments on commit 3f44a10

Please sign in to comment.