// fields needed for dispatch and such
cw.visitField(ACC_STATIC | ACC_FINAL | ACC_PRIVATE, "$runtimeCache", ci(RuntimeCache.class), null, null).visitEnd();
// create static init
SkinnyMethodAdapter clinitMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "<clinit>", sig(void.class), null, null);
// create constructor
SkinnyMethodAdapter initMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", sig(void.class, Ruby.class, RubyClass.class), null, null);
if (isRubyHierarchy) {
// superclass is in the Ruby object hierarchy; invoke typical Ruby superclass constructor
initMethod.aloadMany(0, 1, 2);
initMethod.invokespecial(p(superClass), "<init>", sig(void.class, Ruby.class, RubyClass.class));
} else {
// superclass is not in Ruby hierarchy; store objects and call no-arg super constructor
cw.visitField(ACC_FINAL | ACC_PRIVATE, "$ruby", ci(Ruby.class), null, null).visitEnd();
cw.visitField(ACC_FINAL | ACC_PRIVATE, "$rubyClass", ci(RubyClass.class), null, null).visitEnd();
initMethod.aloadMany(0, 1);
initMethod.putfield(pathName, "$ruby", ci(Ruby.class));
initMethod.aloadMany(0, 2);
initMethod.putfield(pathName, "$rubyClass", ci(RubyClass.class));
// only no-arg super constructor supported right now
initMethod.aload(0);
initMethod.invokespecial(p(superClass), "<init>", sig(void.class));
}
initMethod.voidreturn();
initMethod.end();
if (isRubyHierarchy) {
// override toJava
SkinnyMethodAdapter toJavaMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "toJava", sig(Object.class, Class.class), null, null);
toJavaMethod.aload(0);
toJavaMethod.areturn();
toJavaMethod.end();
} else {
// decorate with stubbed IRubyObject methods
BasicObjectStubGenerator.addBasicObjectStubsToClass(cw);
// add getRuntime and getMetaClass impls based on captured fields
SkinnyMethodAdapter getRuntimeMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getRuntime", sig(Ruby.class), null, null);
getRuntimeMethod.aload(0);
getRuntimeMethod.getfield(pathName, "$ruby", ci(Ruby.class));
getRuntimeMethod.areturn();
getRuntimeMethod.end();
SkinnyMethodAdapter getMetaClassMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "getMetaClass", sig(RubyClass.class), null, null);
getMetaClassMethod.aload(0);
getMetaClassMethod.getfield(pathName, "$rubyClass", ci(RubyClass.class));
getMetaClassMethod.areturn();
getMetaClassMethod.end();
}
int cacheSize = 0;
// for each simple method name, implement the complex methods, calling the simple version
for (Map.Entry<String, List<Method>> entry : simpleToAll.entrySet()) {
String simpleName = entry.getKey();
Set<String> nameSet = JavaUtil.getRubyNamesForJavaName(simpleName, entry.getValue());
Set<String> implementedNames = new HashSet<String>();
for (Method method : entry.getValue()) {
Class[] paramTypes = method.getParameterTypes();
Class returnType = method.getReturnType();
String fullName = simpleName + prettyParams(paramTypes);
if (implementedNames.contains(fullName)) continue;
implementedNames.add(fullName);
// indices for temp values
int baseIndex = 1;
for (Class paramType : paramTypes) {
if (paramType == double.class || paramType == long.class) {
baseIndex += 2;
} else {
baseIndex += 1;
}
}
int rubyIndex = baseIndex + 1;
SkinnyMethodAdapter mv = new SkinnyMethodAdapter(
cw, ACC_PUBLIC, simpleName, sig(returnType, paramTypes), null, null);
mv.start();
mv.line(1);
// TODO: this code should really check if a Ruby equals method is implemented or not.
if(simpleName.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class && returnType == Boolean.TYPE) {
mv.line(2);
mv.aload(0);
mv.aload(1);
mv.invokespecial(p(Object.class), "equals", sig(Boolean.TYPE, params(Object.class)));
mv.ireturn();
} else if(simpleName.equals("hashCode") && paramTypes.length == 0 && returnType == Integer.TYPE) {
mv.line(3);
mv.aload(0);
mv.invokespecial(p(Object.class), "hashCode", sig(Integer.TYPE));
mv.ireturn();
} else if(simpleName.equals("toString") && paramTypes.length == 0 && returnType == String.class) {
mv.line(4);
mv.aload(0);
mv.invokespecial(p(Object.class), "toString", sig(String.class));
mv.areturn();
} else {
mv.line(5);
int cacheIndex = cacheSize++;
// prepare temp locals
mv.aload(0);
mv.invokeinterface(p(IRubyObject.class), "getRuntime", sig(Ruby.class));
mv.astore(rubyIndex);
// get method from cache
mv.getstatic(pathName, "$runtimeCache", ci(RuntimeCache.class));
mv.aload(0);
mv.ldc(cacheIndex);
for (String eachName : nameSet) {
mv.ldc(eachName);
}
mv.invokevirtual(p(RuntimeCache.class), "searchWithCache",
sig(DynamicMethod.class, params(IRubyObject.class, int.class, String.class, nameSet.size())));
// get current context
mv.aload(rubyIndex);
mv.invokevirtual(p(Ruby.class), "getCurrentContext", sig(ThreadContext.class));
// load self, class, and name
mv.aloadMany(0, 0);
mv.invokeinterface(p(IRubyObject.class), "getMetaClass", sig(RubyClass.class));
mv.ldc(simpleName);
// coerce arguments
coerceArgumentsToRuby(mv, paramTypes, rubyIndex);
// load null block
mv.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class));
// invoke method
mv.line(13);
mv.invokevirtual(p(DynamicMethod.class), "call", sig(IRubyObject.class, ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject[].class, Block.class));
coerceResultAndReturn(mv, returnType);
}
mv.end();
}
}
// end setup method
clinitMethod.newobj(p(RuntimeCache.class));