Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b97e35bf4606^
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 6df7e042f450
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Apr 4, 2016

  1. Copy the full SHA
    b97e35b View commit details
  2. [ji] refactor Ruby class reify to generate more accurate (arity match…

    …ing) signatures
    
    previously all method arities ended up as var-args : `foo(IRubyObject[] args)`
    
    now for fixed arities we'll have correct number of args e.g. `foo(IRubyObject arg1)`
    ... we still generate var-args: `foo(IRubyObject[] args)` for all non-fixed arity sizes!
    
    resolves #3206 and likely #449 also #3366 (java_signature is now honored)
    kares committed Apr 4, 2016
    Copy the full SHA
    eb7c236 View commit details
  3. Copy the full SHA
    6df7e04 View commit details
217 changes: 126 additions & 91 deletions core/src/main/java/org/jruby/RubyClass.java
Original file line number Diff line number Diff line change
@@ -46,6 +46,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -810,23 +811,22 @@ public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,

private void dumpReifiedClass(String dumpDir, String javaPath, byte[] classBytes) {
if (dumpDir != null) {
if (dumpDir.equals("")) {
dumpDir = ".";
}
if (dumpDir.length() == 0) dumpDir = ".";

java.io.FileOutputStream classStream = null;
try {
java.io.File classFile = new java.io.File(dumpDir, javaPath + ".class");
classFile.getParentFile().mkdirs();
classStream = new java.io.FileOutputStream(classFile);
classStream.write(classBytes);
} catch (IOException io) {
}
catch (IOException io) {
getRuntime().getWarnings().warn("unable to dump class file: " + io.getMessage());
} finally {
}
finally {
if (classStream != null) {
try {
classStream.close();
} catch (IOException ignored) {
}
try { classStream.close(); }
catch (IOException ignored) { /* no-op */ }
}
}
}
@@ -1289,44 +1289,27 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) {
// re-check reifiable in case another reify call has jumped in ahead of us
if (!isReifiable()) return;

Class reifiedParent = RubyObject.class;

// calculate an appropriate name, using "Anonymous####" if none is present
String name;
if (getBaseName() == null) {
name = "AnonymousRubyClass__" + id;
} else {
name = getName();
}
final String name = getBaseName() != null ? getName() : ("AnonymousRubyClass__" + id);

String javaName = "rubyobj." + name.replaceAll("::", ".");
String javaPath = "rubyobj/" + name.replaceAll("::", "/");
ClassDefiningClassLoader parentCL;
Class parentReified = superClass.getRealClass().getReifiedClass();
final String javaName = "rubyobj." + name.replaceAll("::", ".");
final String javaPath = "rubyobj/" + name.replaceAll("::", "/");

final Class parentReified = superClass.getRealClass().getReifiedClass();
if (parentReified == null) {
throw getClassRuntime().newTypeError("class " + getName() + " parent class is not yet reified");
}

if (parentReified.getClassLoader() instanceof OneShotClassLoader) {
parentCL = (OneShotClassLoader)superClass.getRealClass().getReifiedClass().getClassLoader();
} else {
if (useChildLoader) {
parentCL = new OneShotClassLoader(runtime.getJRubyClassLoader());
} else {
parentCL = runtime.getJRubyClassLoader();
}
}
Class reifiedParent = RubyObject.class;

if (superClass.reifiedClass != null) {
reifiedParent = superClass.reifiedClass;
}

Class[] interfaces = Java.getInterfacesFromRubyClass(this);
String[] interfaceNames = new String[interfaces.length + 1];

final Class[] interfaces = Java.getInterfacesFromRubyClass(this);
final String[] interfaceNames = new String[interfaces.length + 1];
// mark this as a Reified class
interfaceNames[0] = p(Reified.class);

// add the other user-specified interfaces
for (int i = 0; i < interfaces.length; i++) {
interfaceNames[i + 1] = p(interfaces[i]);
@@ -1387,9 +1370,7 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) {

FieldVisitor fieldVisitor = cw.visitField(ACC_PUBLIC, fieldName, ci(type), null, null);

if (fieldAnnos == null) {
continue;
}
if (fieldAnnos == null) continue;

for (Map.Entry<Class, Map<String, Object>> fieldAnno : fieldAnnos.entrySet()) {
Class annoType = fieldAnno.getKey();
@@ -1400,11 +1381,11 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) {
}

// gather a list of instance methods, so we don't accidentally make static ones that conflict
Set<String> instanceMethods = new HashSet<String>();
final Set<String> instanceMethods = new HashSet<String>(getMethods().size());

// define instance methods
for (Map.Entry<String,DynamicMethod> methodEntry : getMethods().entrySet()) {
String methodName = methodEntry.getKey();
final String methodName = methodEntry.getKey();

if (!JavaNameMangler.willMethodMangleOk(methodName)) continue;

@@ -1414,48 +1395,81 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) {
List<Map<Class,Map<String,Object>>> parameterAnnos = getParameterAnnotations().get(methodName);
Class[] methodSignature = getMethodSignatures().get(methodName);

String signature;
if (methodSignature == null) {
// non-signature signature with just IRubyObject
switch (methodEntry.getValue().getArity().getValue()) {
final String signature;
if (methodSignature == null) { // non-signature signature with just IRubyObject
final Arity arity = methodEntry.getValue().getArity();
switch (arity.getValue()) {
case 0:
signature = sig(IRubyObject.class);
signature = sig(IRubyObject.class); // return IRubyObject foo()
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);

m.aload(0);
m.ldc(methodName);
m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class));
break;
default:
signature = sig(IRubyObject.class, IRubyObject[].class);
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS, javaMethodName, signature, null, null);
case 1:
signature = sig(IRubyObject.class, IRubyObject.class); // return IRubyObject foo(IRubyObject arg1)
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);

m.aload(0);
m.ldc(methodName);
m.aload(1);
m.aload(1); // IRubyObject arg1
m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class, IRubyObject.class));
break;
// currently we only have :
// callMethod(context, name)
// callMethod(context, name, arg1)
// so for other arities use generic:
// callMethod(context, name, args...)
default:
if ( arity.isFixed() ) {
final int paramCount = arity.getValue();
Class[] params = new Class[paramCount]; Arrays.fill(params, IRubyObject.class);
signature = sig(IRubyObject.class, params);
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);

m.aload(0);
m.ldc(methodName);

// generate an IRubyObject[] for the method arguments :
m.pushInt(paramCount);
m.anewarray(p(IRubyObject.class)); // new IRubyObject[size]
for ( int i = 1; i <= paramCount; i++ ) {
m.dup();
m.pushInt(i - 1); // array index e.g. iconst_0
m.aload(i); // IRubyObject arg1, arg2 e.g. aload_1
m.aastore(); // arr[ i - 1 ] = arg_i
}
}
else { // (generic) variable arity e.g. method(*args)
// NOTE: maybe improve to match fixed part for < -1 e.g. (IRubObject, IRubyObject, IRubyObject...)
signature = sig(IRubyObject.class, IRubyObject[].class);
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);

m.aload(0);
m.ldc(methodName);
m.aload(1); // IRubyObject[] arg1
}
m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class));
}
m.areturn();
} else {
// generate a real method signature for the method, with to/from coercions
}
else { // generate a real method signature for the method, with to/from coercions

// indices for temp values
Class[] params = new Class[methodSignature.length - 1];
System.arraycopy(methodSignature, 1, params, 0, params.length);
int baseIndex = 1;
for (Class paramType : params) {
if (paramType == double.class || paramType == long.class) {
baseIndex += 2;
} else {
baseIndex += 1;
}
}
int rubyIndex = baseIndex;
final int baseIndex = RealClassGenerator.calcBaseIndex(params, 1);
final int rubyIndex = baseIndex;

signature = sig(methodSignature[0], params);
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null);
int mod = ACC_PUBLIC;
if ( isVarArgsSignature(methodName, methodSignature) ) mod |= ACC_VARARGS;
m = new SkinnyMethodAdapter(cw, mod, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);

m.getstatic(javaPath, "ruby", ci(Ruby.class));
@@ -1490,17 +1504,18 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) {

String signature;
if (methodSignature == null) {
final Arity arity = methodEntry.getValue().getArity();
// non-signature signature with just IRubyObject
switch (methodEntry.getValue().getArity().getValue()) {
switch (arity.getValue()) {
case 0:
signature = sig(IRubyObject.class);
if (instanceMethods.contains(javaMethodName + signature)) continue;
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS | ACC_STATIC, javaMethodName, signature, null, null);
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);

m.getstatic(javaPath, "rubyClass", ci(RubyClass.class));
//m.invokevirtual("org/jruby/RubyClass", "getMetaClass", sig(RubyClass.class) );
m.ldc(methodName); // Method name
m.ldc(methodName);
m.invokevirtual("org/jruby/RubyClass", "callMethod", sig(IRubyObject.class, String.class) );
break;
default:
@@ -1510,25 +1525,18 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) {
generateMethodAnnotations(methodAnnos, m, parameterAnnos);

m.getstatic(javaPath, "rubyClass", ci(RubyClass.class));
m.ldc(methodName); // Method name
m.ldc(methodName);
m.aload(0);
m.invokevirtual("org/jruby/RubyClass", "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class) );
}
m.areturn();
} else {
// generate a real method signature for the method, with to/from coercions
}
else { // generate a real method signature for the method, with to/from coercions

// indices for temp values
Class[] params = new Class[methodSignature.length - 1];
System.arraycopy(methodSignature, 1, params, 0, params.length);
int baseIndex = 0;
for (Class paramType : params) {
if (paramType == double.class || paramType == long.class) {
baseIndex += 2;
} else {
baseIndex += 1;
}
}
final int baseIndex = RealClassGenerator.calcBaseIndex(params, 0);
int rubyIndex = baseIndex;

signature = sig(methodSignature[0], params);
@@ -1555,45 +1563,71 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) {


cw.visitEnd();
byte[] classBytes = cw.toByteArray();



final ClassDefiningClassLoader parentCL;
if (parentReified.getClassLoader() instanceof OneShotClassLoader) {
parentCL = (OneShotClassLoader) parentReified.getClassLoader();
} else {
if (useChildLoader) {
parentCL = new OneShotClassLoader(runtime.getJRubyClassLoader());
} else {
parentCL = runtime.getJRubyClassLoader();
}
}
// Attempt to load the name we plan to use; skip reification if it exists already (see #1229).
Throwable failure = null;
try {
final byte[] classBytes = cw.toByteArray();
Class result = parentCL.defineClass(javaName, classBytes);
dumpReifiedClass(classDumpDir, javaPath, classBytes);

@SuppressWarnings("unchecked")
java.lang.reflect.Method clinit = result.getDeclaredMethod("clinit", Ruby.class, RubyClass.class);
clinit.invoke(null, runtime, this);

setClassAllocator(result);
reifiedClass = result;

return; // success
} catch (LinkageError le) {
// fall through to failure path
failure = le;
} catch (Exception e) {
failure = e;
}
catch (LinkageError error) { // fall through to failure path
final String msg = error.getMessage();
if ( msg != null && msg.contains("duplicate class definition for name") ) {
logReifyException(error, false);
}
else {
logReifyException(error, true);
}
}
catch (Exception ex) {
logReifyException(ex, true);
}

// If we get here, there's some other class in this classloader hierarchy with the same name. In order to
// avoid a naming conflict, we set reified class to parent and skip reification.
if (RubyInstanceConfig.REIFY_LOG_ERRORS) {
LOG.error("failed to reify class " + getName() + " due to:");
LOG.error(failure);
}

if (superClass.reifiedClass != null) {
reifiedClass = superClass.reifiedClass;
allocator = superClass.allocator;
}
}

public void setReifiedClass(Class<? extends IRubyObject> newReifiedClass) {
this.reifiedClass = newReifiedClass;
private boolean isVarArgsSignature(final String method, final Class[] methodSignature) {
// TODO we should simply detect "java.lang.Object m1(java.lang.Object... args)"
// var-args distinguished from "java.lang.Object m2(java.lang.Object[] args)"
return methodSignature.length > 1 && // methodSignature[0] is return value
methodSignature[ methodSignature.length - 1 ].isArray() ;
}

private void logReifyException(final Throwable failure, final boolean error) {
if (RubyInstanceConfig.REIFY_LOG_ERRORS) {
final String msg = "failed to reify class " + getName() + " due to: ";
if ( error ) LOG.error(msg, failure);
else LOG.info(msg, failure);
}
}

public void setReifiedClass(Class<? extends IRubyObject> reifiedClass) {
this.reifiedClass = reifiedClass;
}

public Class<? extends IRubyObject> getReifiedClass() {
@@ -1964,7 +1998,7 @@ public IRubyObject smartLoadOldUser(IRubyObject data) {
public enum CS_NAMES {
INITIALIZE("initialize");

private CS_NAMES(String id) {
CS_NAMES(String id) {
this.id = id;
}

@@ -1979,7 +2013,8 @@ public static CS_NAMES fromOrdinal(int ordinal) {
}

public final String id;
};
}

private final CallSite[] baseCallSites = new CallSite[CS_NAMES.length];
{
for(int i = 0; i < baseCallSites.length; i++) {
Loading