name = getName();
}
String javaName = "rubyobj." + name.replaceAll("::", ".");
String javaPath = "rubyobj/" + name.replaceAll("::", "/");
ClassDefiningClassLoader parentCL;
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();
}
}
if (superClass.reifiedClass != null) {
reifiedParent = superClass.reifiedClass;
}
Class[] interfaces = Java.getInterfacesFromRubyClass(this);
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]);
}
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, javaPath, null, p(reifiedParent),
interfaceNames);
if (classAnnotations != null && !classAnnotations.isEmpty()) {
for (Map.Entry<Class,Map<String,Object>> entry : classAnnotations.entrySet()) {
Class annoType = entry.getKey();
Map<String,Object> fields = entry.getValue();
AnnotationVisitor av = cw.visitAnnotation(ci(annoType), true);
CodegenUtils.visitAnnotationFields(av, fields);
av.visitEnd();
}
}
// fields to hold Ruby and RubyClass references
cw.visitField(ACC_STATIC | ACC_PRIVATE, "ruby", ci(Ruby.class), null, null);
cw.visitField(ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null);
// static initializing method
SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "clinit", sig(void.class, Ruby.class, RubyClass.class), null, null);
m.start();
m.aload(0);
m.putstatic(javaPath, "ruby", ci(Ruby.class));
m.aload(1);
m.putstatic(javaPath, "rubyClass", ci(RubyClass.class));
m.voidreturn();
m.end();
// standard constructor that accepts Ruby, RubyClass
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", sig(void.class, Ruby.class, RubyClass.class), null, null);
m.aload(0);
m.aload(1);
m.aload(2);
m.invokespecial(p(reifiedParent), "<init>", sig(void.class, Ruby.class, RubyClass.class));
m.voidreturn();
m.end();
// no-arg constructor using static references to Ruby and RubyClass
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", CodegenUtils.sig(void.class), null, null);
m.aload(0);
m.getstatic(javaPath, "ruby", ci(Ruby.class));
m.getstatic(javaPath, "rubyClass", ci(RubyClass.class));
m.invokespecial(p(reifiedParent), "<init>", sig(void.class, Ruby.class, RubyClass.class));
m.voidreturn();
m.end();
// define fields
for (Map.Entry<String, Class> fieldSignature : getFieldSignatures().entrySet()) {
String fieldName = fieldSignature.getKey();
Class type = fieldSignature.getValue();
Map<Class, Map<String, Object>> fieldAnnos = getFieldAnnotations().get(fieldName);
FieldVisitor fieldVisitor = cw.visitField(ACC_PUBLIC, fieldName, ci(type), null, null);
if (fieldAnnos == null) {
continue;
}
for (Map.Entry<Class, Map<String, Object>> fieldAnno : fieldAnnos.entrySet()) {
Class annoType = fieldAnno.getKey();
AnnotationVisitor av = fieldVisitor.visitAnnotation(ci(annoType), true);
CodegenUtils.visitAnnotationFields(av, fieldAnno.getValue());
}
fieldVisitor.visitEnd();
}
// gather a list of instance methods, so we don't accidentally make static ones that conflict
Set<String> instanceMethods = new HashSet<String>();
// define instance methods
for (Map.Entry<String,DynamicMethod> methodEntry : getMethods().entrySet()) {
String methodName = methodEntry.getKey();
if (!JavaNameMangler.willMethodMangleOk(methodName)) continue;
String javaMethodName = JavaNameMangler.mangleMethodName(methodName);
Map<Class,Map<String,Object>> methodAnnos = getMethodAnnotations().get(methodName);
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()) {
case 0:
signature = sig(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.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);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);
m.aload(0);
m.ldc(methodName);
m.aload(1);
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
// 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;
signature = sig(methodSignature[0], params);
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);
m.getstatic(javaPath, "ruby", ci(Ruby.class));
m.astore(rubyIndex);
m.aload(0); // self
m.ldc(methodName); // method name
RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex);
m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class));
RealClassGenerator.coerceResultAndReturn(m, methodSignature[0]);
}
if (DEBUG_REIFY) LOG.debug("defining {}#{} as {}#{}", getName(), methodName, javaName, javaMethodName + signature);
instanceMethods.add(javaMethodName + signature);
m.end();
}
// define class/static methods
for (Map.Entry<String,DynamicMethod> methodEntry : getMetaClass().getMethods().entrySet()) {
String methodName = methodEntry.getKey();
if (!JavaNameMangler.willMethodMangleOk(methodName)) continue;
String javaMethodName = JavaNameMangler.mangleMethodName(methodName);
Map<Class,Map<String,Object>> methodAnnos = getMetaClass().getMethodAnnotations().get(methodName);
List<Map<Class,Map<String,Object>>> parameterAnnos = getMetaClass().getParameterAnnotations().get(methodName);
Class[] methodSignature = getMetaClass().getMethodSignatures().get(methodName);
String signature;
if (methodSignature == null) {
// non-signature signature with just IRubyObject
switch (methodEntry.getValue().getArity().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);
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.invokevirtual("org/jruby/RubyClass", "callMethod", sig(IRubyObject.class, String.class) );
break;
default:
signature = sig(IRubyObject.class, IRubyObject[].class);
if (instanceMethods.contains(javaMethodName + signature)) continue;
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS | ACC_STATIC, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);
m.getstatic(javaPath, "rubyClass", ci(RubyClass.class));
m.ldc(methodName); // Method name
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
// 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;
}
}
int rubyIndex = baseIndex;
signature = sig(methodSignature[0], params);
if (instanceMethods.contains(javaMethodName + signature)) continue;
m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS | ACC_STATIC, javaMethodName, signature, null, null);
generateMethodAnnotations(methodAnnos, m, parameterAnnos);
m.getstatic(javaPath, "ruby", ci(Ruby.class));
m.astore(rubyIndex);
m.getstatic(javaPath, "rubyClass", ci(RubyClass.class));
m.ldc(methodName); // method name
RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex);
m.invokevirtual("org/jruby/RubyClass", "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class));
RealClassGenerator.coerceResultAndReturn(m, methodSignature[0]);
}
if (DEBUG_REIFY) LOG.debug("defining {}.{} as {}.{}", getName(), methodName, javaName, javaMethodName + signature);
m.end();
}
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
// Attempt to load the name we plan to use; skip reification if it exists already (see #1229).
Throwable failure = null;
try {
Class result = parentCL.defineClass(javaName, classBytes);
dumpReifiedClass(classDumpDir, javaPath, classBytes);
java.lang.reflect.Method clinit = result.getDeclaredMethod("clinit", Ruby.class, RubyClass.class);
clinit.invoke(null, runtime, this);