Save This Page
Home » cglib-src-2.2 » net.sf.cglib.core » [javadoc | source]
    1   /*
    2    * Copyright 2003,2004 The Apache Software Foundation
    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 net.sf.cglib.core;
   17   
   18   import java.math.BigDecimal;
   19   import java.math.BigInteger;
   20   import java.util;
   21   import org.objectweb.asm.Label;
   22   import org.objectweb.asm.Type;
   23   
   24   public class EmitUtils {
   25       private static final Signature CSTRUCT_NULL =
   26         TypeUtils.parseConstructor("");
   27       private static final Signature CSTRUCT_THROWABLE =
   28         TypeUtils.parseConstructor("Throwable");
   29   
   30       private static final Signature GET_NAME =
   31         TypeUtils.parseSignature("String getName()");
   32       private static final Signature HASH_CODE =
   33         TypeUtils.parseSignature("int hashCode()");
   34       private static final Signature EQUALS =
   35         TypeUtils.parseSignature("boolean equals(Object)");
   36       private static final Signature STRING_LENGTH =
   37         TypeUtils.parseSignature("int length()");
   38       private static final Signature STRING_CHAR_AT =
   39         TypeUtils.parseSignature("char charAt(int)");
   40       private static final Signature FOR_NAME =
   41         TypeUtils.parseSignature("Class forName(String)");
   42       private static final Signature DOUBLE_TO_LONG_BITS =
   43         TypeUtils.parseSignature("long doubleToLongBits(double)");
   44       private static final Signature FLOAT_TO_INT_BITS =
   45         TypeUtils.parseSignature("int floatToIntBits(float)");
   46       private static final Signature TO_STRING =
   47         TypeUtils.parseSignature("String toString()");
   48       private static final Signature APPEND_STRING =
   49         TypeUtils.parseSignature("StringBuffer append(String)");
   50       private static final Signature APPEND_INT =
   51         TypeUtils.parseSignature("StringBuffer append(int)");
   52       private static final Signature APPEND_DOUBLE =
   53         TypeUtils.parseSignature("StringBuffer append(double)");
   54       private static final Signature APPEND_FLOAT =
   55         TypeUtils.parseSignature("StringBuffer append(float)");
   56       private static final Signature APPEND_CHAR =
   57         TypeUtils.parseSignature("StringBuffer append(char)");
   58       private static final Signature APPEND_LONG =
   59         TypeUtils.parseSignature("StringBuffer append(long)");
   60       private static final Signature APPEND_BOOLEAN =
   61         TypeUtils.parseSignature("StringBuffer append(boolean)");
   62       private static final Signature LENGTH =
   63         TypeUtils.parseSignature("int length()");
   64       private static final Signature SET_LENGTH =
   65         TypeUtils.parseSignature("void setLength(int)");
   66       private static final Signature GET_DECLARED_METHOD =
   67         TypeUtils.parseSignature("java.lang.reflect.Method getDeclaredMethod(String, Class[])");
   68        
   69       
   70   
   71       public static final ArrayDelimiters DEFAULT_DELIMITERS = new ArrayDelimiters("{", ", ", "}");
   72   
   73       private EmitUtils() {
   74       }
   75   
   76       public static void factory_method(ClassEmitter ce, Signature sig) {
   77           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, sig, null);
   78           e.new_instance_this();
   79           e.dup();
   80           e.load_args();
   81           e.invoke_constructor_this(TypeUtils.parseConstructor(sig.getArgumentTypes()));
   82           e.return_value();
   83           e.end_method();
   84       }
   85   
   86       public static void null_constructor(ClassEmitter ce) {
   87           CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_NULL, null);
   88           e.load_this();
   89           e.super_invoke_constructor();
   90           e.return_value();
   91           e.end_method();
   92       }
   93       
   94       /**
   95        * Process an array on the stack. Assumes the top item on the stack
   96        * is an array of the specified type. For each element in the array,
   97        * puts the element on the stack and triggers the callback.
   98        * @param type the type of the array (type.isArray() must be true)
   99        * @param callback the callback triggered for each element
  100        */
  101       public static void process_array(CodeEmitter e, Type type, ProcessArrayCallback callback) {
  102           Type componentType = TypeUtils.getComponentType(type);
  103           Local array = e.make_local();
  104           Local loopvar = e.make_local(Type.INT_TYPE);
  105           Label loopbody = e.make_label();
  106           Label checkloop = e.make_label();
  107           e.store_local(array);
  108           e.push(0);
  109           e.store_local(loopvar);
  110           e.goTo(checkloop);
  111           
  112           e.mark(loopbody);
  113           e.load_local(array);
  114           e.load_local(loopvar);
  115           e.array_load(componentType);
  116           callback.processElement(componentType);
  117           e.iinc(loopvar, 1);
  118           
  119           e.mark(checkloop);
  120           e.load_local(loopvar);
  121           e.load_local(array);
  122           e.arraylength();
  123           e.if_icmp(e.LT, loopbody);
  124       }
  125       
  126       /**
  127        * Process two arrays on the stack in parallel. Assumes the top two items on the stack
  128        * are arrays of the specified class. The arrays must be the same length. For each pair
  129        * of elements in the arrays, puts the pair on the stack and triggers the callback.
  130        * @param type the type of the arrays (type.isArray() must be true)
  131        * @param callback the callback triggered for each pair of elements
  132        */
  133       public static void process_arrays(CodeEmitter e, Type type, ProcessArrayCallback callback) {
  134           Type componentType = TypeUtils.getComponentType(type);
  135           Local array1 = e.make_local();
  136           Local array2 = e.make_local();
  137           Local loopvar = e.make_local(Type.INT_TYPE);
  138           Label loopbody = e.make_label();
  139           Label checkloop = e.make_label();
  140           e.store_local(array1);
  141           e.store_local(array2);
  142           e.push(0);
  143           e.store_local(loopvar);
  144           e.goTo(checkloop);
  145           
  146           e.mark(loopbody);
  147           e.load_local(array1);
  148           e.load_local(loopvar);
  149           e.array_load(componentType);
  150           e.load_local(array2);
  151           e.load_local(loopvar);
  152           e.array_load(componentType);
  153           callback.processElement(componentType);
  154           e.iinc(loopvar, 1);
  155           
  156           e.mark(checkloop);
  157           e.load_local(loopvar);
  158           e.load_local(array1);
  159           e.arraylength();
  160           e.if_icmp(e.LT, loopbody);
  161       }
  162       
  163       public static void string_switch(CodeEmitter e, String[] strings, int switchStyle, ObjectSwitchCallback callback) {
  164           try {
  165               switch (switchStyle) {
  166               case Constants.SWITCH_STYLE_TRIE:
  167                   string_switch_trie(e, strings, callback);
  168                   break;
  169               case Constants.SWITCH_STYLE_HASH:
  170                   string_switch_hash(e, strings, callback, false);
  171                   break;
  172               case Constants.SWITCH_STYLE_HASHONLY:
  173                   string_switch_hash(e, strings, callback, true);
  174                   break;
  175               default:
  176                   throw new IllegalArgumentException("unknown switch style " + switchStyle);
  177               }
  178           } catch (RuntimeException ex) {
  179               throw ex;
  180           } catch (Error ex) {
  181               throw ex;
  182           } catch (Exception ex) {
  183               throw new CodeGenerationException(ex);
  184           }
  185       }
  186   
  187       private static void string_switch_trie(final CodeEmitter e,
  188                                              String[] strings,
  189                                              final ObjectSwitchCallback callback) throws Exception {
  190           final Label def = e.make_label();
  191           final Label end = e.make_label();
  192           final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
  193               public Object transform(Object value) {
  194                   return new Integer(((String)value).length());
  195               }
  196           });
  197           e.dup();
  198           e.invoke_virtual(Constants.TYPE_STRING, STRING_LENGTH);
  199           e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
  200                   public void processCase(int key, Label ignore_end) throws Exception {
  201                       List bucket = (List)buckets.get(new Integer(key));
  202                       stringSwitchHelper(e, bucket, callback, def, end, 0);
  203                   }
  204                   public void processDefault() {
  205                       e.goTo(def);
  206                   }
  207               });
  208           e.mark(def);
  209           e.pop();
  210           callback.processDefault();
  211           e.mark(end);
  212       }
  213   
  214       private static void stringSwitchHelper(final CodeEmitter e,
  215                                              List strings,
  216                                              final ObjectSwitchCallback callback,
  217                                              final Label def,
  218                                              final Label end,
  219                                              final int index) throws Exception {
  220           final int len = ((String)strings.get(0)).length();
  221           final Map buckets = CollectionUtils.bucket(strings, new Transformer() {
  222               public Object transform(Object value) {
  223                   return new Integer(((String)value).charAt(index));
  224               }
  225           });
  226           e.dup();
  227           e.push(index);
  228           e.invoke_virtual(Constants.TYPE_STRING, STRING_CHAR_AT);
  229           e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
  230                   public void processCase(int key, Label ignore_end) throws Exception {
  231                       List bucket = (List)buckets.get(new Integer(key));
  232                       if (index + 1 == len) {
  233                           e.pop();
  234                           callback.processCase(bucket.get(0), end);
  235                       } else {
  236                           stringSwitchHelper(e, bucket, callback, def, end, index + 1);
  237                       }
  238                   }
  239                   public void processDefault() {
  240                       e.goTo(def);
  241                   }
  242               });
  243       }        
  244   
  245       static int[] getSwitchKeys(Map buckets) {
  246           int[] keys = new int[buckets.size()];
  247           int index = 0;
  248           for (Iterator it = buckets.keySet().iterator(); it.hasNext();) {
  249               keys[index++] = ((Integer)it.next()).intValue();
  250           }
  251           Arrays.sort(keys);
  252           return keys;
  253       }
  254   
  255       private static void string_switch_hash(final CodeEmitter e,
  256                                              final String[] strings,
  257                                              final ObjectSwitchCallback callback,
  258                                              final boolean skipEquals) throws Exception {
  259           final Map buckets = CollectionUtils.bucket(Arrays.asList(strings), new Transformer() {
  260               public Object transform(Object value) {
  261                   return new Integer(value.hashCode());
  262               }
  263           });
  264           final Label def = e.make_label();
  265           final Label end = e.make_label();
  266           e.dup();
  267           e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
  268           e.process_switch(getSwitchKeys(buckets), new ProcessSwitchCallback() {
  269               public void processCase(int key, Label ignore_end) throws Exception {
  270                   List bucket = (List)buckets.get(new Integer(key));
  271                   Label next = null;
  272                   if (skipEquals && bucket.size() == 1) {
  273                       if (skipEquals)
  274                           e.pop();
  275                       callback.processCase((String)bucket.get(0), end);
  276                   } else {
  277                       for (Iterator it = bucket.iterator(); it.hasNext();) {
  278                           String string = (String)it.next();
  279                           if (next != null) {
  280                               e.mark(next);
  281                           }
  282                           if (it.hasNext()) {
  283                               e.dup();
  284                           }
  285                           e.push(string);
  286                           e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
  287                           if (it.hasNext()) {
  288                               e.if_jump(e.EQ, next = e.make_label());
  289                               e.pop();
  290                           } else {
  291                               e.if_jump(e.EQ, def);
  292                           }
  293                           callback.processCase(string, end);
  294                       }
  295                   }
  296               }
  297               public void processDefault() {
  298                   e.pop();
  299               }
  300           });
  301           e.mark(def);
  302           callback.processDefault();
  303           e.mark(end);
  304       }
  305   
  306       public static void load_class_this(CodeEmitter e) {
  307           load_class_helper(e, e.getClassEmitter().getClassType());
  308       }
  309       
  310       public static void load_class(CodeEmitter e, Type type) {
  311           if (TypeUtils.isPrimitive(type)) {
  312               if (type == Type.VOID_TYPE) {
  313                   throw new IllegalArgumentException("cannot load void type");
  314               }
  315               e.getstatic(TypeUtils.getBoxedType(type), "TYPE", Constants.TYPE_CLASS);
  316           } else {
  317               load_class_helper(e, type);
  318           }
  319       }
  320   
  321       private static void load_class_helper(CodeEmitter e, final Type type) {
  322           if (e.isStaticHook()) {
  323               // have to fall back on non-optimized load
  324               e.push(TypeUtils.emulateClassGetName(type));
  325               e.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
  326           } else {
  327               ClassEmitter ce = e.getClassEmitter();
  328               String typeName = TypeUtils.emulateClassGetName(type);
  329   
  330               // TODO: can end up with duplicated field names when using chained transformers; incorporate static hook # somehow
  331               String fieldName = "CGLIB$load_class$" + TypeUtils.escapeType(typeName);
  332               if (!ce.isFieldDeclared(fieldName)) {
  333                   ce.declare_field(Constants.PRIVATE_FINAL_STATIC, fieldName, Constants.TYPE_CLASS, null);
  334                   CodeEmitter hook = ce.getStaticHook();
  335                   hook.push(typeName);
  336                   hook.invoke_static(Constants.TYPE_CLASS, FOR_NAME);
  337                   hook.putstatic(ce.getClassType(), fieldName, Constants.TYPE_CLASS);
  338               }
  339               e.getfield(fieldName);
  340           }
  341       }
  342   
  343       public static void push_array(CodeEmitter e, Object[] array) {
  344           e.push(array.length);
  345           e.newarray(Type.getType(remapComponentType(array.getClass().getComponentType())));
  346           for (int i = 0; i < array.length; i++) {
  347               e.dup();
  348               e.push(i);
  349               push_object(e, array[i]);
  350               e.aastore();
  351           }
  352       }
  353   
  354       private static Class remapComponentType(Class componentType) {
  355           if (componentType.equals(Type.class))
  356               return Class.class;
  357           return componentType;
  358       }
  359       
  360       public static void push_object(CodeEmitter e, Object obj) {
  361           if (obj == null) {
  362               e.aconst_null();
  363           } else {
  364               Class type = obj.getClass();
  365               if (type.isArray()) {
  366                   push_array(e, (Object[])obj);
  367               } else if (obj instanceof String) {
  368                   e.push((String)obj);
  369               } else if (obj instanceof Type) {
  370                   load_class(e, (Type)obj);
  371               } else if (obj instanceof Class) {
  372                   load_class(e, Type.getType((Class)obj));
  373               } else if (obj instanceof BigInteger) {
  374                   e.new_instance(Constants.TYPE_BIG_INTEGER);
  375                   e.dup();
  376                   e.push(obj.toString());
  377                   e.invoke_constructor(Constants.TYPE_BIG_INTEGER);
  378               } else if (obj instanceof BigDecimal) {
  379                   e.new_instance(Constants.TYPE_BIG_DECIMAL);
  380                   e.dup();
  381                   e.push(obj.toString());
  382                   e.invoke_constructor(Constants.TYPE_BIG_DECIMAL);
  383               } else {
  384                   throw new IllegalArgumentException("unknown type: " + obj.getClass());
  385               }
  386           }
  387       }
  388   
  389       public static void hash_code(CodeEmitter e, Type type, int multiplier, Customizer customizer) {
  390           if (TypeUtils.isArray(type)) {
  391               hash_array(e, type, multiplier, customizer);
  392           } else {
  393               e.swap(Type.INT_TYPE, type);
  394               e.push(multiplier);
  395               e.math(e.MUL, Type.INT_TYPE);
  396               e.swap(type, Type.INT_TYPE);
  397               if (TypeUtils.isPrimitive(type)) {
  398                   hash_primitive(e, type);
  399               } else {
  400                   hash_object(e, type, customizer);
  401               }
  402               e.math(e.ADD, Type.INT_TYPE);
  403           }
  404       }
  405   
  406       private static void hash_array(final CodeEmitter e, Type type, final int multiplier, final Customizer customizer) {
  407           Label skip = e.make_label();
  408           Label end = e.make_label();
  409           e.dup();
  410           e.ifnull(skip);
  411           EmitUtils.process_array(e, type, new ProcessArrayCallback() {
  412               public void processElement(Type type) {
  413                   hash_code(e, type, multiplier, customizer);
  414               }
  415           });
  416           e.goTo(end);
  417           e.mark(skip);
  418           e.pop();
  419           e.mark(end);
  420       }
  421   
  422       private static void hash_object(CodeEmitter e, Type type, Customizer customizer) {
  423           // (f == null) ? 0 : f.hashCode();
  424           Label skip = e.make_label();
  425           Label end = e.make_label();
  426           e.dup();
  427           e.ifnull(skip);
  428           if (customizer != null) {
  429               customizer.customize(e, type);
  430           }
  431           e.invoke_virtual(Constants.TYPE_OBJECT, HASH_CODE);
  432           e.goTo(end);
  433           e.mark(skip);
  434           e.pop();
  435           e.push(0);
  436           e.mark(end);
  437       }
  438   
  439       private static void hash_primitive(CodeEmitter e, Type type) {
  440           switch (type.getSort()) {
  441           case Type.BOOLEAN:
  442               // f ? 0 : 1
  443               e.push(1);
  444               e.math(e.XOR, Type.INT_TYPE);
  445               break;
  446           case Type.FLOAT:
  447               // Float.floatToIntBits(f)
  448               e.invoke_static(Constants.TYPE_FLOAT, FLOAT_TO_INT_BITS);
  449               break;
  450           case Type.DOUBLE:
  451               // Double.doubleToLongBits(f), hash_code(Long.TYPE)
  452               e.invoke_static(Constants.TYPE_DOUBLE, DOUBLE_TO_LONG_BITS);
  453               // fall through
  454           case Type.LONG:
  455               hash_long(e);
  456           }
  457       }
  458   
  459       private static void hash_long(CodeEmitter e) {
  460           // (int)(f ^ (f >>> 32))
  461           e.dup2();
  462           e.push(32);
  463           e.math(e.USHR, Type.LONG_TYPE);
  464           e.math(e.XOR, Type.LONG_TYPE);
  465           e.cast_numeric(Type.LONG_TYPE, Type.INT_TYPE);
  466       }
  467   
  468   //     public static void not_equals(CodeEmitter e, Type type, Label notEquals) {
  469   //         not_equals(e, type, notEquals, null);
  470   //     }
  471       
  472       /**
  473        * Branches to the specified label if the top two items on the stack
  474        * are not equal. The items must both be of the specified
  475        * class. Equality is determined by comparing primitive values
  476        * directly and by invoking the <code>equals</code> method for
  477        * Objects. Arrays are recursively processed in the same manner.
  478        */
  479       public static void not_equals(final CodeEmitter e, Type type, final Label notEquals, final Customizer customizer) {
  480           (new ProcessArrayCallback() {
  481               public void processElement(Type type) {
  482                   not_equals_helper(e, type, notEquals, customizer, this);
  483               }
  484           }).processElement(type);
  485       }
  486       
  487       private static void not_equals_helper(CodeEmitter e,
  488                                             Type type,
  489                                             Label notEquals,
  490                                             Customizer customizer,
  491                                             ProcessArrayCallback callback) {
  492           if (TypeUtils.isPrimitive(type)) {
  493               e.if_cmp(type, e.NE, notEquals);
  494           } else {
  495               Label end = e.make_label();
  496               nullcmp(e, notEquals, end);
  497               if (TypeUtils.isArray(type)) {
  498                   Label checkContents = e.make_label();
  499                   e.dup2();
  500                   e.arraylength();
  501                   e.swap();
  502                   e.arraylength();
  503                   e.if_icmp(e.EQ, checkContents);
  504                   e.pop2();
  505                   e.goTo(notEquals);
  506                   e.mark(checkContents);
  507                   EmitUtils.process_arrays(e, type, callback);
  508               } else {
  509                   if (customizer != null) {
  510                       customizer.customize(e, type);
  511                       e.swap();
  512                       customizer.customize(e, type);
  513                   }
  514                   e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
  515                   e.if_jump(e.EQ, notEquals);
  516               }
  517               e.mark(end);
  518           }
  519       }
  520   
  521       /**
  522        * If both objects on the top of the stack are non-null, does nothing.
  523        * If one is null, or both are null, both are popped off and execution
  524        * branches to the respective label.
  525        * @param oneNull label to branch to if only one of the objects is null
  526        * @param bothNull label to branch to if both of the objects are null
  527        */
  528       private static void nullcmp(CodeEmitter e, Label oneNull, Label bothNull) {
  529           e.dup2();
  530           Label nonNull = e.make_label();
  531           Label oneNullHelper = e.make_label();
  532           Label end = e.make_label();
  533           e.ifnonnull(nonNull);
  534           e.ifnonnull(oneNullHelper);
  535           e.pop2();
  536           e.goTo(bothNull);
  537           
  538           e.mark(nonNull);
  539           e.ifnull(oneNullHelper);
  540           e.goTo(end);
  541           
  542           e.mark(oneNullHelper);
  543           e.pop2();
  544           e.goTo(oneNull);
  545           
  546           e.mark(end);
  547       }
  548   
  549       /*
  550       public static void to_string(CodeEmitter e,
  551                                    Type type,
  552                                    ArrayDelimiters delims,
  553                                    Customizer customizer) {
  554           e.new_instance(Constants.TYPE_STRING_BUFFER);
  555           e.dup();
  556           e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
  557           e.swap();
  558           append_string(e, type, delims, customizer);
  559           e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
  560       }
  561       */
  562   
  563       public static void append_string(final CodeEmitter e,
  564                                        Type type,
  565                                        final ArrayDelimiters delims,
  566                                        final Customizer customizer) {
  567           final ArrayDelimiters d = (delims != null) ? delims : DEFAULT_DELIMITERS;
  568           ProcessArrayCallback callback = new ProcessArrayCallback() {
  569               public void processElement(Type type) {
  570                   append_string_helper(e, type, d, customizer, this);
  571                   e.push(d.inside);
  572                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
  573               }
  574           };
  575           append_string_helper(e, type, d, customizer, callback);
  576       }
  577   
  578       private static void append_string_helper(CodeEmitter e,
  579                                                Type type,
  580                                                ArrayDelimiters delims,
  581                                                Customizer customizer,
  582                                                ProcessArrayCallback callback) {
  583           Label skip = e.make_label();
  584           Label end = e.make_label();
  585           if (TypeUtils.isPrimitive(type)) {
  586               switch (type.getSort()) {
  587               case Type.INT:
  588               case Type.SHORT:
  589               case Type.BYTE:
  590                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_INT);
  591                   break;
  592               case Type.DOUBLE:
  593                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_DOUBLE);
  594                   break;
  595               case Type.FLOAT:
  596                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_FLOAT);
  597                   break;
  598               case Type.LONG:
  599                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_LONG);
  600                   break;
  601               case Type.BOOLEAN:
  602                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_BOOLEAN);
  603                   break;
  604               case Type.CHAR:
  605                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_CHAR);
  606                   break;
  607               }
  608           } else if (TypeUtils.isArray(type)) {
  609               e.dup();
  610               e.ifnull(skip);
  611               e.swap();
  612               if (delims != null && delims.before != null && !"".equals(delims.before)) {
  613                   e.push(delims.before);
  614                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
  615                   e.swap();
  616               }
  617               EmitUtils.process_array(e, type, callback);
  618               shrinkStringBuffer(e, 2);
  619               if (delims != null && delims.after != null && !"".equals(delims.after)) {
  620                   e.push(delims.after);
  621                   e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
  622               }
  623           } else {
  624               e.dup();
  625               e.ifnull(skip);
  626               if (customizer != null) {
  627                   customizer.customize(e, type);
  628               }
  629               e.invoke_virtual(Constants.TYPE_OBJECT, TO_STRING);
  630               e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
  631           }
  632           e.goTo(end);
  633           e.mark(skip);
  634           e.pop();
  635           e.push("null");
  636           e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
  637           e.mark(end);
  638       }
  639   
  640       private static void shrinkStringBuffer(CodeEmitter e, int amt) {
  641           e.dup();
  642           e.dup();
  643           e.invoke_virtual(Constants.TYPE_STRING_BUFFER, LENGTH);
  644           e.push(amt);
  645           e.math(e.SUB, Type.INT_TYPE);
  646           e.invoke_virtual(Constants.TYPE_STRING_BUFFER, SET_LENGTH);
  647       }
  648   
  649       public static class ArrayDelimiters {
  650           private String before;
  651           private String inside;
  652           private String after;
  653               
  654           public ArrayDelimiters(String before, String inside, String after) {
  655               this.before = before;
  656               this.inside = inside;
  657               this.after = after;
  658           }
  659       }
  660   
  661       public static void load_method(CodeEmitter e, MethodInfo method) {
  662           load_class(e, method.getClassInfo().getType());
  663           e.push(method.getSignature().getName());
  664           push_object(e, method.getSignature().getArgumentTypes());
  665           e.invoke_virtual(Constants.TYPE_CLASS, GET_DECLARED_METHOD);
  666       }
  667   
  668       private interface ParameterTyper {
  669           Type[] getParameterTypes(MethodInfo member);
  670       }
  671   
  672       public static void method_switch(CodeEmitter e,
  673                                        List methods,
  674                                        ObjectSwitchCallback callback) {
  675           member_switch_helper(e, methods, callback, true);
  676       }
  677   
  678       public static void constructor_switch(CodeEmitter e,
  679                                             List constructors,
  680                                             ObjectSwitchCallback callback) {
  681           member_switch_helper(e, constructors, callback, false);
  682       }
  683   
  684       private static void member_switch_helper(final CodeEmitter e,
  685                                                List members,
  686                                                final ObjectSwitchCallback callback,
  687                                                boolean useName) {
  688           try {
  689               final Map cache = new HashMap();
  690               final ParameterTyper cached = new ParameterTyper() {
  691                       public Type[] getParameterTypes(MethodInfo member) {
  692                           Type[] types = (Type[])cache.get(member);
  693                           if (types == null) {
  694                               cache.put(member, types = member.getSignature().getArgumentTypes());
  695                           }
  696                           return types;
  697                       }
  698                   };
  699               final Label def = e.make_label();
  700               final Label end = e.make_label();
  701               if (useName) {
  702                   e.swap();
  703                   final Map buckets = CollectionUtils.bucket(members, new Transformer() {
  704                           public Object transform(Object value) {
  705                               return ((MethodInfo)value).getSignature().getName();
  706                           }
  707                       });
  708                   String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
  709                   EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
  710                           public void processCase(Object key, Label dontUseEnd) throws Exception {
  711                               member_helper_size(e, (List)buckets.get(key), callback, cached, def, end);
  712                           }
  713                           public void processDefault() throws Exception {
  714                               e.goTo(def);
  715                           }
  716                       });
  717               } else {
  718                   member_helper_size(e, members, callback, cached, def, end);
  719               }
  720               e.mark(def);
  721               e.pop();
  722               callback.processDefault();
  723               e.mark(end);
  724           } catch (RuntimeException ex) {
  725               throw ex;
  726           } catch (Error ex) {
  727               throw ex;
  728           } catch (Exception ex) {
  729               throw new CodeGenerationException(ex);
  730           }
  731       }
  732   
  733       private static void member_helper_size(final CodeEmitter e,
  734                                              List members,
  735                                              final ObjectSwitchCallback callback,
  736                                              final ParameterTyper typer,
  737                                              final Label def,
  738                                              final Label end) throws Exception {
  739           final Map buckets = CollectionUtils.bucket(members, new Transformer() {
  740               public Object transform(Object value) {
  741                   return new Integer(typer.getParameterTypes((MethodInfo)value).length);
  742               }
  743           });
  744           e.dup();
  745           e.arraylength();
  746           e.process_switch(EmitUtils.getSwitchKeys(buckets), new ProcessSwitchCallback() {
  747               public void processCase(int key, Label dontUseEnd) throws Exception {
  748                   List bucket = (List)buckets.get(new Integer(key));
  749                   member_helper_type(e, bucket, callback, typer, def, end, new BitSet());
  750               }
  751               public void processDefault() throws Exception {
  752                   e.goTo(def);
  753               }
  754           });
  755       }
  756   
  757       private static void member_helper_type(final CodeEmitter e,
  758                                              List members,
  759                                              final ObjectSwitchCallback callback,
  760                                              final ParameterTyper typer,
  761                                              final Label def,
  762                                              final Label end,
  763                                              final BitSet checked) throws Exception {
  764           if (members.size() == 1) {
  765               MethodInfo member = (MethodInfo)members.get(0);
  766               Type[] types = typer.getParameterTypes(member);
  767               // need to check classes that have not already been checked via switches
  768               for (int i = 0; i < types.length; i++) {
  769                   if (checked == null || !checked.get(i)) {
  770                       e.dup();
  771                       e.aaload(i);
  772                       e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
  773                       e.push(TypeUtils.emulateClassGetName(types[i]));
  774                       e.invoke_virtual(Constants.TYPE_OBJECT, EQUALS);
  775                       e.if_jump(e.EQ, def);
  776                   }
  777               }
  778               e.pop();
  779               callback.processCase(member, end);
  780           } else {
  781               // choose the index that has the best chance of uniquely identifying member
  782               Type[] example = typer.getParameterTypes((MethodInfo)members.get(0));
  783               Map buckets = null;
  784               int index = -1;
  785               for (int i = 0; i < example.length; i++) {
  786                   final int j = i;
  787                   Map test = CollectionUtils.bucket(members, new Transformer() {
  788                       public Object transform(Object value) {
  789                           return TypeUtils.emulateClassGetName(typer.getParameterTypes((MethodInfo)value)[j]);
  790                       }
  791                   });
  792                   if (buckets == null || test.size() > buckets.size()) {
  793                       buckets = test;
  794                       index = i;
  795                   }
  796               }
  797               if (buckets == null || buckets.size() == 1) {
  798                   // TODO: switch by returnType
  799                   // must have two methods with same name, types, and different return types
  800                   e.goTo(def);
  801               } else {
  802                   checked.set(index);
  803   
  804                   e.dup();
  805                   e.aaload(index);
  806                   e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
  807   
  808                   final Map fbuckets = buckets;
  809                   String[] names = (String[])buckets.keySet().toArray(new String[buckets.size()]);
  810                   EmitUtils.string_switch(e, names, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
  811                       public void processCase(Object key, Label dontUseEnd) throws Exception {
  812                           member_helper_type(e, (List)fbuckets.get(key), callback, typer, def, end, checked);
  813                       }
  814                       public void processDefault() throws Exception {
  815                           e.goTo(def);
  816                       }
  817                   });
  818               }
  819           }
  820       }
  821   
  822       public static void wrap_throwable(Block block, Type wrapper) {
  823           CodeEmitter e = block.getCodeEmitter();
  824           e.catch_exception(block, Constants.TYPE_THROWABLE);
  825           e.new_instance(wrapper);
  826           e.dup_x1();
  827           e.swap();
  828           e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
  829           e.athrow();
  830       }
  831   
  832       public static void add_properties(ClassEmitter ce, String[] names, Type[] types) {
  833           for (int i = 0; i < names.length; i++) {
  834               String fieldName = "$cglib_prop_" + names[i];
  835               ce.declare_field(Constants.ACC_PRIVATE, fieldName, types[i], null);
  836               EmitUtils.add_property(ce, names[i], types[i], fieldName);
  837           }
  838       }
  839   
  840       public static void add_property(ClassEmitter ce, String name, Type type, String fieldName) {
  841           String property = TypeUtils.upperFirst(name);
  842           CodeEmitter e;
  843           e = ce.begin_method(Constants.ACC_PUBLIC,
  844                               new Signature("get" + property,
  845                                             type,
  846                                             Constants.TYPES_EMPTY),
  847                               null);
  848           e.load_this();
  849           e.getfield(fieldName);
  850           e.return_value();
  851           e.end_method();
  852   
  853           e = ce.begin_method(Constants.ACC_PUBLIC,
  854                               new Signature("set" + property,
  855                                             Type.VOID_TYPE,
  856                                             new Type[]{ type }),
  857                               null);
  858           e.load_this();
  859           e.load_arg(0);
  860           e.putfield(fieldName);
  861           e.return_value();
  862           e.end_method();
  863       }
  864   
  865       /* generates:
  866          } catch (RuntimeException e) {
  867            throw e;
  868          } catch (Error e) {
  869            throw e;
  870          } catch (<DeclaredException> e) {
  871            throw e;
  872          } catch (Throwable e) {
  873            throw new <Wrapper>(e);
  874          }
  875       */
  876       public static void wrap_undeclared_throwable(CodeEmitter e, Block handler, Type[] exceptions, Type wrapper) {
  877           Set set = (exceptions == null) ? Collections.EMPTY_SET : new HashSet(Arrays.asList(exceptions));
  878   
  879           if (set.contains(Constants.TYPE_THROWABLE))
  880               return;
  881   
  882           boolean needThrow = exceptions != null;
  883           if (!set.contains(Constants.TYPE_RUNTIME_EXCEPTION)) {
  884               e.catch_exception(handler, Constants.TYPE_RUNTIME_EXCEPTION);
  885               needThrow = true;
  886           }
  887           if (!set.contains(Constants.TYPE_ERROR)) {
  888               e.catch_exception(handler, Constants.TYPE_ERROR);
  889               needThrow = true;
  890           }
  891           if (exceptions != null) {
  892               for (int i = 0; i < exceptions.length; i++) {
  893                   e.catch_exception(handler, exceptions[i]);
  894               }
  895           }
  896           if (needThrow) {
  897               e.athrow();
  898           }
  899           // e -> eo -> oeo -> ooe -> o
  900           e.catch_exception(handler, Constants.TYPE_THROWABLE);
  901           e.new_instance(wrapper);
  902           e.dup_x1();
  903           e.swap();
  904           e.invoke_constructor(wrapper, CSTRUCT_THROWABLE);
  905           e.athrow();
  906       }
  907   
  908       public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method) {
  909           return begin_method(e, method, method.getModifiers());
  910       }
  911   
  912       public static CodeEmitter begin_method(ClassEmitter e, MethodInfo method, int access) {
  913           return e.begin_method(access,
  914                                 method.getSignature(),
  915                                 method.getExceptionTypes());
  916       }
  917   }

Save This Page
Home » cglib-src-2.2 » net.sf.cglib.core » [javadoc | source]