Save This Page
Home » Groovy-1.7.0 » org.codehaus » groovy » runtime » callsite » [javadoc | source]
    1   /*
    2    * Copyright 2003-2009 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   package org.codehaus.groovy.runtime.callsite;
   17   
   18   import org.codehaus.groovy.ast.ClassHelper;
   19   import org.codehaus.groovy.classgen.BytecodeHelper;
   20   import org.codehaus.groovy.reflection.CachedClass;
   21   import org.codehaus.groovy.reflection.CachedMethod;
   22   import org.objectweb.asm.ClassWriter;
   23   import org.objectweb.asm.Label;
   24   import org.objectweb.asm.MethodVisitor;
   25   import org.objectweb.asm.Opcodes;
   26   
   27   import groovy.lang.GroovyRuntimeException;
   28   
   29   import java.lang.reflect.Constructor;
   30   import java.lang.reflect.Method;
   31   import java.lang.reflect.Modifier;
   32   
   33   public class CallSiteGenerator {
   34   
   35       private static final String GRE = BytecodeHelper.getClassInternalName(ClassHelper.make(GroovyRuntimeException.class));
   36       
   37       private CallSiteGenerator () {}
   38       
   39       private static MethodVisitor writeMethod(ClassWriter cw, String name, int argumentCount, final String superClass, CachedMethod cachedMethod, String receiverType, String parameterDescription, boolean useArray) {
   40           MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "call" + name, "(L" + receiverType + ";" + parameterDescription + ")Ljava/lang/Object;", null, null);
   41           mv.visitCode();
   42           
   43           final Label tryStart = new Label();
   44           mv.visitLabel(tryStart);
   45           
   46           // call for checking if method is still valid
   47           for (int i = 0; i < argumentCount; ++i) mv.visitVarInsn(Opcodes.ALOAD, i);
   48           mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, superClass, "checkCall", "(Ljava/lang/Object;" + parameterDescription + ")Z");
   49           Label l0 = new Label();
   50           mv.visitJumpInsn(Opcodes.IFEQ, l0);
   51           
   52           // valid method branch
   53           BytecodeHelper helper = new BytecodeHelper(mv);
   54   
   55           Class callClass = cachedMethod.getDeclaringClass().getTheClass();
   56           boolean useInterface = callClass.isInterface();
   57   
   58           String type = BytecodeHelper.getClassInternalName(callClass.getName());
   59           String descriptor = BytecodeHelper.getMethodDescriptor(cachedMethod.getReturnType(), cachedMethod.getNativeParameterTypes());
   60           
   61           // prepare call
   62           int invokeMethodCode = Opcodes.INVOKEVIRTUAL;
   63           if (cachedMethod.isStatic()) {
   64               invokeMethodCode = Opcodes.INVOKESTATIC;
   65           } else {
   66               mv.visitVarInsn(Opcodes.ALOAD, 1);
   67               helper.doCast(callClass);
   68               if (useInterface) invokeMethodCode = Opcodes.INVOKEINTERFACE;
   69           }
   70           
   71           Method method = cachedMethod.setAccessible();
   72           Class<?>[] parameters = method.getParameterTypes();
   73           int size = parameters.length;
   74           for (int i = 0; i < size; i++) {
   75               if (useArray) {
   76                   // unpack argument from Object[]
   77                   mv.visitVarInsn(Opcodes.ALOAD, 2);
   78                   helper.pushConstant(i);
   79                   mv.visitInsn(Opcodes.AALOAD);
   80               } else {
   81                   mv.visitVarInsn(Opcodes.ALOAD, i+2);
   82               }
   83   
   84               // cast argument to parameter class, inclusive unboxing
   85               // for methods with primitive types
   86               Class parameterType = parameters[i];
   87               if (parameterType.isPrimitive()) {
   88                   helper.unbox(parameterType);
   89               } else {
   90                   helper.doCast(parameterType);
   91               }
   92           }        
   93           
   94           // make call
   95           mv.visitMethodInsn(invokeMethodCode, type, cachedMethod.getName(), descriptor);
   96   
   97           // produce result
   98           helper.box(cachedMethod.getReturnType());
   99           if (cachedMethod.getReturnType() == void.class) {
  100               mv.visitInsn(Opcodes.ACONST_NULL);
  101           }
  102   
  103           // return
  104           mv.visitInsn(Opcodes.ARETURN);
  105           
  106           // fall back after method change
  107           mv.visitLabel(l0);
  108           for (int i = 0; i < argumentCount; ++i) mv.visitVarInsn(Opcodes.ALOAD, i);
  109           if (!useArray) {
  110               mv.visitMethodInsn(Opcodes.INVOKESTATIC, "org/codehaus/groovy/runtime/ArrayUtil", "createArray", "(" + parameterDescription + ")[Ljava/lang/Object;");
  111           }
  112           mv.visitMethodInsn(Opcodes.INVOKESTATIC, "org/codehaus/groovy/runtime/callsite/CallSiteArray", "defaultCall" + name, "(Lorg/codehaus/groovy/runtime/callsite/CallSite;L" + receiverType + ";[Ljava/lang/Object;)Ljava/lang/Object;");
  113           mv.visitInsn(Opcodes.ARETURN);
  114           
  115           // exception unwrapping for stackless exceptions
  116           final Label tryEnd = new Label();
  117           mv.visitLabel(tryEnd);
  118           final Label catchStart = new Label();
  119           mv.visitLabel(catchStart);
  120           mv.visitMethodInsn(Opcodes.INVOKESTATIC, "org/codehaus/groovy/runtime/ScriptBytecodeAdapter", "unwrap", "(Lgroovy/lang/GroovyRuntimeException;)Ljava/lang/Throwable;");
  121           mv.visitInsn(Opcodes.ATHROW);        
  122           mv.visitTryCatchBlock(tryStart, tryEnd, catchStart, GRE);
  123           
  124           mv.visitMaxs(0, 0);
  125           mv.visitEnd();
  126           return mv;
  127       }
  128       
  129   
  130       public static void genCallWithFixedParams(ClassWriter cw, String name, final String superClass, CachedMethod cachedMethod, String receiverType ) {
  131           if (cachedMethod.getParamsCount() > 4) return;
  132           
  133           StringBuilder pdescb = new StringBuilder();
  134           final int pc = cachedMethod.getParamsCount();
  135           for (int i = 0; i != pc; ++i) pdescb.append("Ljava/lang/Object;");
  136           
  137           writeMethod(cw,name,pc+2,superClass,cachedMethod,receiverType,pdescb.toString(),false);
  138       }
  139   
  140       public static void genCallXxxWithArray(ClassWriter cw, final String name, final String superClass, CachedMethod cachedMethod, String receiverType) {
  141           writeMethod(cw,name,3,superClass,cachedMethod,receiverType,"[Ljava/lang/Object;",true);
  142       }
  143   
  144       private static void genConstructor(ClassWriter cw, final String superClass) {
  145           MethodVisitor mv;
  146           mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(Lorg/codehaus/groovy/runtime/callsite/CallSite;Lgroovy/lang/MetaClassImpl;Lgroovy/lang/MetaMethod;[Ljava/lang/Class;)V", null, null);
  147           mv.visitCode();
  148           mv.visitVarInsn(Opcodes.ALOAD, 0);
  149           mv.visitVarInsn(Opcodes.ALOAD, 1);
  150           mv.visitVarInsn(Opcodes.ALOAD, 2);
  151           mv.visitVarInsn(Opcodes.ALOAD, 3);
  152           mv.visitVarInsn(Opcodes.ALOAD, 4);
  153           mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superClass, "<init>", "(Lorg/codehaus/groovy/runtime/callsite/CallSite;Lgroovy/lang/MetaClassImpl;Lgroovy/lang/MetaMethod;[Ljava/lang/Class;)V");
  154           mv.visitInsn(Opcodes.RETURN);
  155           mv.visitMaxs(0, 0);
  156           mv.visitEnd();
  157       }
  158   
  159       public static byte[] genPogoMetaMethodSite(CachedMethod cachedMethod, ClassWriter cw, String name) {
  160           MethodVisitor mv;
  161           cw.visit(Opcodes.V1_4, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name.replace('.','/'), null, "org/codehaus/groovy/runtime/callsite/PogoMetaMethodSite", null);
  162   
  163           genConstructor(cw, "org/codehaus/groovy/runtime/callsite/PogoMetaMethodSite");
  164   
  165           genCallXxxWithArray(cw, "Current", "org/codehaus/groovy/runtime/callsite/PogoMetaMethodSite", cachedMethod, "groovy/lang/GroovyObject");
  166           genCallXxxWithArray(cw, "", "org/codehaus/groovy/runtime/callsite/PogoMetaMethodSite", cachedMethod, "java/lang/Object");
  167   
  168           genCallWithFixedParams(cw, "Current", "org/codehaus/groovy/runtime/callsite/PogoMetaMethodSite", cachedMethod, "groovy/lang/GroovyObject");
  169           genCallWithFixedParams(cw, "", "org/codehaus/groovy/runtime/callsite/PogoMetaMethodSite", cachedMethod, "java/lang/Object");
  170   
  171   
  172           cw.visitEnd();
  173   
  174           return cw.toByteArray();
  175       }
  176   
  177       public static byte[] genPojoMetaMethodSite(CachedMethod cachedMethod, ClassWriter cw, String name) {
  178           MethodVisitor mv;
  179           cw.visit(Opcodes.V1_4, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name.replace('.','/'), null, "org/codehaus/groovy/runtime/callsite/PojoMetaMethodSite", null);
  180   
  181           genConstructor(cw, "org/codehaus/groovy/runtime/callsite/PojoMetaMethodSite");
  182   
  183           genCallXxxWithArray(cw, "", "org/codehaus/groovy/runtime/callsite/PojoMetaMethodSite", cachedMethod, "java/lang/Object");
  184           genCallWithFixedParams(cw, "", "org/codehaus/groovy/runtime/callsite/PojoMetaMethodSite", cachedMethod, "java/lang/Object");
  185   
  186           cw.visitEnd();
  187   
  188           return cw.toByteArray();
  189       }
  190   
  191       public static byte[] genStaticMetaMethodSite(CachedMethod cachedMethod, ClassWriter cw, String name) {
  192           cw.visit(Opcodes.V1_4, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name.replace('.','/'), null, "org/codehaus/groovy/runtime/callsite/StaticMetaMethodSite", null);
  193   
  194           genConstructor(cw, "org/codehaus/groovy/runtime/callsite/StaticMetaMethodSite");
  195   
  196           genCallXxxWithArray(cw, "", "org/codehaus/groovy/runtime/callsite/StaticMetaMethodSite", cachedMethod, "java/lang/Object");
  197           genCallXxxWithArray(cw, "Static", "org/codehaus/groovy/runtime/callsite/StaticMetaMethodSite", cachedMethod, "java/lang/Class");
  198           genCallWithFixedParams(cw, "", "org/codehaus/groovy/runtime/callsite/StaticMetaMethodSite", cachedMethod, "java/lang/Object");
  199           genCallWithFixedParams(cw, "Static", "org/codehaus/groovy/runtime/callsite/StaticMetaMethodSite", cachedMethod, "java/lang/Class");
  200   
  201           cw.visitEnd();
  202   
  203           return cw.toByteArray();
  204       }
  205   
  206       public static Constructor compilePogoMethod(CachedMethod cachedMethod) {
  207           ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  208   
  209           final CachedClass declClass = cachedMethod.getDeclaringClass();
  210           final CallSiteClassLoader callSiteLoader = declClass.getCallSiteLoader();
  211           final String name = callSiteLoader.createClassName(cachedMethod.setAccessible());
  212   
  213           final byte[] bytes = genPogoMetaMethodSite(cachedMethod, cw, name);
  214           
  215           return callSiteLoader.defineClassAndGetConstructor(name, bytes);
  216       }
  217   
  218       public static Constructor compilePojoMethod(CachedMethod cachedMethod) {
  219           ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  220   
  221           final CachedClass declClass = cachedMethod.getDeclaringClass();
  222           final CallSiteClassLoader callSiteLoader = declClass.getCallSiteLoader();
  223           final String name = callSiteLoader.createClassName(cachedMethod.setAccessible());
  224   
  225           final byte[] bytes = genPojoMetaMethodSite(cachedMethod, cw, name);
  226           
  227           return callSiteLoader.defineClassAndGetConstructor(name, bytes);
  228       }
  229   
  230       public static Constructor compileStaticMethod(CachedMethod cachedMethod) {
  231           ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  232   
  233           final CachedClass declClass = cachedMethod.getDeclaringClass();
  234           final CallSiteClassLoader callSiteLoader = declClass.getCallSiteLoader();
  235           final String name = callSiteLoader.createClassName(cachedMethod.setAccessible());
  236   
  237           final byte[] bytes = genStaticMetaMethodSite(cachedMethod, cw, name);
  238           
  239           return callSiteLoader.defineClassAndGetConstructor(name, bytes);
  240       }
  241   
  242       public static boolean isCompilable (CachedMethod method) {
  243           return GroovySunClassLoader.sunVM != null || Modifier.isPublic(method.cachedClass.getModifiers()) && method.isPublic() && publicParams(method);
  244       }
  245   
  246       private static boolean publicParams(CachedMethod method) {
  247           for (Class nativeParamType : method.getNativeParameterTypes()) {
  248               if (!Modifier.isPublic(nativeParamType.getModifiers()))
  249                   return false;
  250           }
  251           return true;
  252       }
  253   
  254   }

Save This Page
Home » Groovy-1.7.0 » org.codehaus » groovy » runtime » callsite » [javadoc | source]