Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * To change this template, choose Tools | Templates
   * and open the template in the editor.
   */
  package org.jruby.java.codegen;
  
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.jruby.Ruby;
 import static org.jruby.util.CodegenUtils.*;
 import  org.objectweb.asm.ClassWriter;
 import  org.objectweb.asm.Type;
 import static org.objectweb.asm.Opcodes.*;

Author(s):
headius
 
 public class RealClassGenerator {
     private static final boolean DEBUG = false;
 
     private static Map<StringList<Method>> buildSimpleToAllMap(Class[] interfacesString[] superTypeNamesthrows SecurityException {
         Map<StringList<Method>> simpleToAll = new HashMap<StringList<Method>>();
         for (int i = 0; i < interfaces.lengthi++) {
             superTypeNames[i] = p(interfaces[i]);
             for (Method method : interfaces[i].getMethods()) {
                 List<Methodmethods = simpleToAll.get(method.getName());
                 if (methods == null) {
                     simpleToAll.put(method.getName(), methods = new ArrayList<Method>());
                 }
                 methods.add(method);
             }
         }
         return simpleToAll;
     }
 
     public static Class createOldStyleImplClass(Class[] superTypesRubyClass rubyClassRuby rubyString nameClassDefiningClassLoader classLoader) {
         String[] superTypeNames = new String[superTypes.length];
         Map<StringList<Method>> simpleToAll = buildSimpleToAllMap(superTypessuperTypeNames);
         
         Class newClass = defineOldStyleImplClass(rubynamesuperTypeNamessimpleToAllclassLoader);
         
         return newClass;
     }
 
     public static Class createRealImplClass(Class superClassClass[] interfacesRubyClass rubyClassRuby rubyString name) {
         String[] superTypeNames = new String[interfaces.length];
         Map<StringList<Method>> simpleToAll = buildSimpleToAllMap(interfacessuperTypeNames);
 
         Class newClass = defineRealImplClass(rubynamesuperClasssuperTypeNamessimpleToAll);
 
         return newClass;
     }
    
    
This variation on defineImplClass uses all the classic type coercion logic for passing args and returning results.

Parameters:
ruby
name
superTypeNames
simpleToAll
Returns:
 
     public static Class defineOldStyleImplClass(Ruby rubyString nameString[] superTypeNamesMap<StringList<Method>> simpleToAllClassDefiningClassLoader classLoader) {
         Class newClass;
         byte[] bytes;
 
         synchronized (classLoader) {
             // try to load the specified name; only if that fails, try to define the class
             try {
                 newClass = classLoader.loadClass(name);
             } catch (ClassNotFoundException cnfe) {
                 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                 String pathName = name.replace('.''/');
 
                 // construct the class, implementing all supertypes
                cw.visit(V1_5, ACC_PUBLIC | ACC_SUPER, pathNamenullp(Object.class), superTypeNames);
                cw.visitSource(pathName + ".gen"null);
                // fields needed for dispatch and such
                cw.visitField(ACC_STATIC | ACC_FINAL | ACC_PRIVATE, "$runtimeCache"ci(RuntimeCache.class), nullnull).visitEnd();
                cw.visitField(ACC_PRIVATE | ACC_FINAL, "$self"ci(IRubyObject.class), nullnull).visitEnd();
                // create static init
                SkinnyMethodAdapter clinitMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "<clinit>"sig(void.class), nullnull);
                // create constructor
                SkinnyMethodAdapter initMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>"sig(void.classIRubyObject.class), nullnull);
                initMethod.aload(0);
                initMethod.invokespecial(p(Object.class), "<init>"sig(void.class));
                // store the wrapper
                initMethod.aload(0);
                initMethod.aload(1);
                initMethod.putfield(pathName"$self"ci(IRubyObject.class));
                // end constructor
                initMethod.voidreturn();
                initMethod.end();
                int cacheSize = 0;
                // for each simple method name, implement the complex methods, calling the simple version
                for (Map.Entry<StringList<Method>> entry : simpleToAll.entrySet()) {
                    String simpleName = entry.getKey();
                    Set<StringnameSet = JavaUtil.getRubyNamesForJavaName(simpleNameentry.getValue());
                    Set<StringimplementedNames = 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 selfIndex = baseIndex;
                        int rubyIndex = selfIndex + 1;
                        SkinnyMethodAdapter mv = new SkinnyMethodAdapter(
                                cw, ACC_PUBLIC, simpleNamesig(returnTypeparamTypes), nullnull);
                        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 == .) {
                            mv.line(2);
                            mv.aload(0);
                            mv.aload(1);
                            mv.invokespecial(p(Object.class), "equals"sig(.params(Object.class)));
                            mv.ireturn();
                        } else if (simpleName.equals("hashCode") && paramTypes.length == 0 && returnType == .) {
                            mv.line(3);
                            mv.aload(0);
                            mv.invokespecial(p(Object.class), "hashCode"sig(.));
                            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 if (simpleName.equals("__ruby_object") && paramTypes.length == 0 && returnType == IRubyObject.class) {
                            mv.aload(0);
                            mv.getfield(pathName"$self"ci(IRubyObject.class));
                            mv.areturn();
                        } else {
                            mv.line(5);
                            int cacheIndex = cacheSize++;
                            // prepare temp locals
                            mv.aload(0);
                            mv.getfield(pathName"$self"ci(IRubyObject.class));
                            mv.astore(selfIndex);
                            mv.aload(selfIndex);
                            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(selfIndex);
                            mv.ldc(cacheIndex);
                            for (String eachName : nameSet) {
                                mv.ldc(eachName);
                            }
                            mv.invokevirtual(p(RuntimeCache.class), "searchWithCache",
                                    sig(DynamicMethod.classparams(IRubyObject.classint.classString.classnameSet.size())));
                            // get current context
                            mv.aload(rubyIndex);
                            mv.invokevirtual(p(Ruby.class), "getCurrentContext"sig(ThreadContext.class));
                            // load self, class, and name
                            mv.aloadMany(selfIndexselfIndex);
                            mv.invokeinterface(p(IRubyObject.class), "getMetaClass"sig(RubyClass.class));
                            mv.ldc(simpleName);
                            // coerce arguments
                            coerceArgumentsToRuby(mvparamTypesrubyIndex);
                            // 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.classThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject[].classBlock.class));
                            coerceResultAndReturn(mvreturnType);
                        }
                        mv.end();
                    }
                }
                // end setup method
                clinitMethod.newobj(p(RuntimeCache.class));
                clinitMethod.dup();
                clinitMethod.invokespecial(p(RuntimeCache.class), "<init>"sig(void.class));
                clinitMethod.dup();
                clinitMethod.ldc(cacheSize);
                clinitMethod.invokevirtual(p(RuntimeCache.class), "initMethodCache"sig(void.classint.class));
                clinitMethod.putstatic(pathName"$runtimeCache"ci(RuntimeCache.class));
                clinitMethod.voidreturn();
                clinitMethod.end();
                // end class
                cw.visitEnd();
                // create the class
                bytes = cw.toByteArray();
                newClass = classLoader.defineClass(namecw.toByteArray());
            }
        }
        if () {
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(name + ".class");
                fos.write(bytes);
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                try {fos.close();} catch (Exception e) {}
            }
        }
        
        return newClass;
    }

    
This variation on defineImplClass uses all the classic type coercion logic for passing args and returning results.

Parameters:
ruby
name
superTypeNames
simpleToAll
Returns:
    public static Class defineRealImplClass(Ruby rubyString nameClass superClassString[] superTypeNamesMap<StringList<Method>> simpleToAll) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        String pathName = name.replace('.''/');
        boolean isRubyHierarchy = RubyBasicObject.class.isAssignableFrom(superClass);
        // construct the class, implementing all supertypes
        if (isRubyHierarchy) {
            // Ruby hierarchy...just extend it
            cw.visit(V1_5, ACC_PUBLIC | ACC_SUPER, pathNamenullp(superClass), superTypeNames);
        } else {
            // Non-Ruby hierarchy; add IRubyObject
            String[] plusIRubyObject = new String[superTypeNames.length + 1];
            plusIRubyObject[0] = p(IRubyObject.class);
            System.arraycopy(superTypeNames, 0, plusIRubyObject, 1, superTypeNames.length);
            
            cw.visit(V1_5, ACC_PUBLIC | ACC_SUPER, pathNamenullp(superClass), plusIRubyObject);
        }
        cw.visitSource(pathName + ".gen"null);
        // fields needed for dispatch and such
        cw.visitField(ACC_STATIC | ACC_FINAL | ACC_PRIVATE, "$runtimeCache"ci(RuntimeCache.class), nullnull).visitEnd();
        // create static init
        SkinnyMethodAdapter clinitMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "<clinit>"sig(void.class), nullnull);
        // create constructor
        SkinnyMethodAdapter initMethod = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>"sig(void.classRuby.classRubyClass.class), nullnull);
        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.classRuby.classRubyClass.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), nullnull).visitEnd();
            cw.visitField(ACC_FINAL | ACC_PRIVATE, "$rubyClass"ci(RubyClass.class), nullnull).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.classClass.class), nullnull);
            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), nullnull);
            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), nullnull);
            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<StringList<Method>> entry : simpleToAll.entrySet()) {
            String simpleName = entry.getKey();
            Set<StringnameSet = JavaUtil.getRubyNamesForJavaName(simpleNameentry.getValue());
            Set<StringimplementedNames = 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, simpleNamesig(returnTypeparamTypes), nullnull);
                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 == .) {
                    mv.line(2);
                    mv.aload(0);
                    mv.aload(1);
                    mv.invokespecial(p(Object.class), "equals"sig(.params(Object.class)));
                    mv.ireturn();
                } else if(simpleName.equals("hashCode") && paramTypes.length == 0 && returnType == .) {
                    mv.line(3);
                    mv.aload(0);
                    mv.invokespecial(p(Object.class), "hashCode"sig(.));
                    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.classparams(IRubyObject.classint.classString.classnameSet.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(mvparamTypesrubyIndex);
                    // 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.classThreadContext.classIRubyObject.classRubyModule.classString.classIRubyObject[].classBlock.class));
                    coerceResultAndReturn(mvreturnType);
                }
                mv.end();
            }
        }
        // end setup method
        clinitMethod.newobj(p(RuntimeCache.class));
        clinitMethod.dup();
        clinitMethod.invokespecial(p(RuntimeCache.class), "<init>"sig(void.class));
        clinitMethod.dup();
        clinitMethod.ldc(cacheSize);
        clinitMethod.invokevirtual(p(RuntimeCache.class), "initMethodCache"sig(void.classint.class));
        clinitMethod.putstatic(pathName"$runtimeCache"ci(RuntimeCache.class));
        clinitMethod.voidreturn();
        clinitMethod.end();
        // end class
        cw.visitEnd();
        // create the class
        byte[] bytes = cw.toByteArray();
        Class newClass;
        JRubyClassLoader loader;
        if (superClass.getClassLoader() instanceof JRubyClassLoader) {
            loader = new JRubyClassLoader(superClass.getClassLoader());
        } else {
            loader = new JRubyClassLoader(ruby.getJRubyClassLoader());
        }
        try {
            newClass = loader.loadClass(name);
        } catch (ClassNotFoundException cnfe) {
            newClass = loader.defineClass(namecw.toByteArray());
        }
        if () {
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(name + ".class");
                fos.write(bytes);
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                try {fos.close();} catch (Exception e) {}
            }
        }
        return newClass;
    }
    public static void coerceArgumentsToRuby(SkinnyMethodAdapter mvClass[] paramTypesint rubyIndex) {
        // load arguments into IRubyObject[] for dispatch
        if (paramTypes.length != 0) {
            mv.pushInt(paramTypes.length);
            mv.anewarray(p(IRubyObject.class));
            // TODO: make this do specific-arity calling
            for (int i = 0, argIndex = 1; i < paramTypes.lengthi++) {
                Class paramType = paramTypes[i];
                mv.dup();
                mv.pushInt(i);
                // convert to IRubyObject
                mv.aload(rubyIndex);
                if (paramTypes[i].isPrimitive()) {
                    if (paramType == byte.class || paramType == short.class || paramType == char.class || paramType == int.class) {
                        mv.iload(argIndex++);
                        mv.invokestatic(p(JavaUtil.class), "convertJavaToRuby"sig(IRubyObject.classRuby.classint.class));
                    } else if (paramType == long.class) {
                        mv.lload(argIndex);
                        argIndex += 2; // up two slots, for long's two halves
                        mv.invokestatic(p(JavaUtil.class), "convertJavaToRuby"sig(IRubyObject.classRuby.classlong.class));
                    } else if (paramType == float.class) {
                        mv.fload(argIndex++);
                        mv.invokestatic(p(JavaUtil.class), "convertJavaToRuby"sig(IRubyObject.classRuby.classfloat.class));
                    } else if (paramType == double.class) {
                        mv.dload(argIndex);
                        argIndex += 2; // up two slots, for long's two halves
                        mv.invokestatic(p(JavaUtil.class), "convertJavaToRuby"sig(IRubyObject.classRuby.classdouble.class));
                    } else if (paramType == boolean.class) {
                        mv.iload(argIndex++);
                        mv.invokestatic(p(JavaUtil.class), "convertJavaToRuby"sig(IRubyObject.classRuby.classboolean.class));
                    }
                } else {
                    mv.aload(argIndex++);
                    mv.invokestatic(p(JavaUtil.class), "convertJavaToUsableRubyObject"sig(IRubyObject.classRuby.classObject.class));
                }
                mv.aastore();
            }
        } else {
            mv.getstatic(p(IRubyObject.class), "NULL_ARRAY"ci(IRubyObject[].class));
        }
    }
    public static void coerceResultAndReturn(SkinnyMethodAdapter mvClass returnType) {
        // if we expect a return value, unwrap it
        if (returnType != void.class) {
            // TODO: move the bulk of this logic to utility methods
            if (returnType.isPrimitive()) {
                if (returnType == boolean.class) {
                    mv.getstatic(p(Boolean.class), "TYPE"ci(Class.class));
                    mv.invokeinterface(p(IRubyObject.class), "toJava"sig(Object.classClass.class));
                    mv.checkcast(p(Boolean.class));
                    mv.invokevirtual(p(Boolean.class), "booleanValue"sig(boolean.class));
                    mv.ireturn();
                } else {
                    mv.getstatic(p(getBoxType(returnType)), "TYPE"ci(Class.class));
                    mv.invokeinterface(p(IRubyObject.class), "toJava"sig(Object.classClass.class));
                    if (returnType == byte.class) {
                        mv.checkcast(p(Number.class));
                        mv.invokevirtual(p(Number.class), "byteValue"sig(byte.class));
                        mv.ireturn();
                    } else if (returnType == short.class) {
                        mv.checkcast(p(Number.class));
                        mv.invokevirtual(p(Number.class), "shortValue"sig(short.class));
                        mv.ireturn();
                    } else if (returnType == char.class) {
                        mv.checkcast(p(Character.class));
                        mv.invokevirtual(p(Character.class), "charValue"sig(char.class));
                        mv.ireturn();
                    } else if (returnType == int.class) {
                        mv.checkcast(p(Number.class));
                        mv.invokevirtual(p(Number.class), "intValue"sig(int.class));
                        mv.ireturn();
                    } else if (returnType == long.class) {
                        mv.checkcast(p(Number.class));
                        mv.invokevirtual(p(Number.class), "longValue"sig(long.class));
                        mv.lreturn();
                    } else if (returnType == float.class) {
                        mv.checkcast(p(Number.class));
                        mv.invokevirtual(p(Number.class), "floatValue"sig(float.class));
                        mv.freturn();
                    } else if (returnType == double.class) {
                        mv.checkcast(p(Number.class));
                        mv.invokevirtual(p(Number.class), "doubleValue"sig(double.class));
                        mv.dreturn();
                    }
                }
            } else {
                mv.ldc(Type.getType(returnType));
                mv.invokeinterface(p(IRubyObject.class), "toJava"sig(Object.classClass.class));
                mv.checkcast(p(returnType));
                mv.areturn();
            }
        } else {
            mv.voidreturn();
        }
    }
    public static boolean isCacheOk(CacheEntry entryIRubyObject self) {
        return CacheEntry.typeOk(entryself.getMetaClass()) && entry.method != .;
    }
New to GrepCode? Check out our FAQ X