• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 org.mockito.cglib.core;
17 
18 import java.io.*;
19 import java.util.*;
20 
21 import org.mockito.asm.*;
22 
23 /**
24  * @author Juozas Baliuka, Chris Nokleberg
25  */
26 public class CodeEmitter extends LocalVariablesSorter {
27     private static final Signature BOOLEAN_VALUE =
28       TypeUtils.parseSignature("boolean booleanValue()");
29     private static final Signature CHAR_VALUE =
30       TypeUtils.parseSignature("char charValue()");
31     private static final Signature LONG_VALUE =
32       TypeUtils.parseSignature("long longValue()");
33     private static final Signature DOUBLE_VALUE =
34       TypeUtils.parseSignature("double doubleValue()");
35     private static final Signature FLOAT_VALUE =
36       TypeUtils.parseSignature("float floatValue()");
37     private static final Signature INT_VALUE =
38       TypeUtils.parseSignature("int intValue()");
39     private static final Signature CSTRUCT_NULL =
40       TypeUtils.parseConstructor("");
41     private static final Signature CSTRUCT_STRING =
42       TypeUtils.parseConstructor("String");
43 
44     public static final int ADD = Constants.IADD;
45     public static final int MUL = Constants.IMUL;
46     public static final int XOR = Constants.IXOR;
47     public static final int USHR = Constants.IUSHR;
48     public static final int SUB = Constants.ISUB;
49     public static final int DIV = Constants.IDIV;
50     public static final int NEG = Constants.INEG;
51     public static final int REM = Constants.IREM;
52     public static final int AND = Constants.IAND;
53     public static final int OR = Constants.IOR;
54 
55     public static final int GT = Constants.IFGT;
56     public static final int LT = Constants.IFLT;
57     public static final int GE = Constants.IFGE;
58     public static final int LE = Constants.IFLE;
59     public static final int NE = Constants.IFNE;
60     public static final int EQ = Constants.IFEQ;
61 
62     private ClassEmitter ce;
63     private State state;
64 
65     private static class State
66     extends MethodInfo
67     {
68         ClassInfo classInfo;
69         int access;
70         Signature sig;
71         Type[] argumentTypes;
72         int localOffset;
73         Type[] exceptionTypes;
74 
State(ClassInfo classInfo, int access, Signature sig, Type[] exceptionTypes)75         State(ClassInfo classInfo, int access, Signature sig, Type[] exceptionTypes) {
76             this.classInfo = classInfo;
77             this.access = access;
78             this.sig = sig;
79             this.exceptionTypes = exceptionTypes;
80             localOffset = TypeUtils.isStatic(access) ? 0 : 1;
81             argumentTypes = sig.getArgumentTypes();
82         }
83 
getClassInfo()84         public ClassInfo getClassInfo() {
85             return classInfo;
86         }
87 
getModifiers()88         public int getModifiers() {
89             return access;
90         }
91 
getSignature()92         public Signature getSignature() {
93             return sig;
94         }
95 
getExceptionTypes()96         public Type[] getExceptionTypes() {
97             return exceptionTypes;
98         }
99 
getAttribute()100         public Attribute getAttribute() {
101             // TODO
102             return null;
103         }
104     }
105 
CodeEmitter(ClassEmitter ce, MethodVisitor mv, int access, Signature sig, Type[] exceptionTypes)106     CodeEmitter(ClassEmitter ce, MethodVisitor mv, int access, Signature sig, Type[] exceptionTypes) {
107         super(access, sig.getDescriptor(), mv);
108         this.ce = ce;
109         state = new State(ce.getClassInfo(), access, sig, exceptionTypes);
110     }
111 
CodeEmitter(CodeEmitter wrap)112     public CodeEmitter(CodeEmitter wrap) {
113         super(wrap);
114         this.ce = wrap.ce;
115         this.state = wrap.state;
116     }
117 
isStaticHook()118     public boolean isStaticHook() {
119         return false;
120     }
121 
getSignature()122     public Signature getSignature() {
123         return state.sig;
124     }
125 
getReturnType()126     public Type getReturnType() {
127         return state.sig.getReturnType();
128     }
129 
getMethodInfo()130     public MethodInfo getMethodInfo() {
131         return state;
132     }
133 
getClassEmitter()134     public ClassEmitter getClassEmitter() {
135         return ce;
136     }
137 
end_method()138     public void end_method() {
139         visitMaxs(0, 0);
140     }
141 
begin_block()142     public Block begin_block() {
143         return new Block(this);
144     }
145 
catch_exception(Block block, Type exception)146     public void catch_exception(Block block, Type exception) {
147         if (block.getEnd() == null) {
148             throw new IllegalStateException("end of block is unset");
149         }
150         mv.visitTryCatchBlock(block.getStart(),
151                               block.getEnd(),
152                               mark(),
153                               exception.getInternalName());
154     }
155 
goTo(Label label)156     public void goTo(Label label) { mv.visitJumpInsn(Constants.GOTO, label); }
ifnull(Label label)157     public void ifnull(Label label) { mv.visitJumpInsn(Constants.IFNULL, label); }
ifnonnull(Label label)158     public void ifnonnull(Label label) { mv.visitJumpInsn(Constants.IFNONNULL, label); }
159 
if_jump(int mode, Label label)160     public void if_jump(int mode, Label label) {
161         mv.visitJumpInsn(mode, label);
162     }
163 
if_icmp(int mode, Label label)164     public void if_icmp(int mode, Label label) {
165         if_cmp(Type.INT_TYPE, mode, label);
166     }
167 
if_cmp(Type type, int mode, Label label)168     public void if_cmp(Type type, int mode, Label label) {
169         int intOp = -1;
170         int jumpmode = mode;
171         switch (mode) {
172         case GE: jumpmode = LT; break;
173         case LE: jumpmode = GT; break;
174         }
175         switch (type.getSort()) {
176         case Type.LONG:
177             mv.visitInsn(Constants.LCMP);
178             break;
179         case Type.DOUBLE:
180             mv.visitInsn(Constants.DCMPG);
181             break;
182         case Type.FLOAT:
183             mv.visitInsn(Constants.FCMPG);
184             break;
185         case Type.ARRAY:
186         case Type.OBJECT:
187             switch (mode) {
188             case EQ:
189                 mv.visitJumpInsn(Constants.IF_ACMPEQ, label);
190                 return;
191             case NE:
192                 mv.visitJumpInsn(Constants.IF_ACMPNE, label);
193                 return;
194             }
195             throw new IllegalArgumentException("Bad comparison for type " + type);
196         default:
197             switch (mode) {
198             case EQ: intOp = Constants.IF_ICMPEQ; break;
199             case NE: intOp = Constants.IF_ICMPNE; break;
200             case GE: swap(); /* fall through */
201             case LT: intOp = Constants.IF_ICMPLT; break;
202             case LE: swap(); /* fall through */
203             case GT: intOp = Constants.IF_ICMPGT; break;
204             }
205             mv.visitJumpInsn(intOp, label);
206             return;
207         }
208         if_jump(jumpmode, label);
209     }
210 
pop()211     public void pop() { mv.visitInsn(Constants.POP); }
pop2()212     public void pop2() { mv.visitInsn(Constants.POP2); }
dup()213     public void dup() { mv.visitInsn(Constants.DUP); }
dup2()214     public void dup2() { mv.visitInsn(Constants.DUP2); }
dup_x1()215     public void dup_x1() { mv.visitInsn(Constants.DUP_X1); }
dup_x2()216     public void dup_x2() { mv.visitInsn(Constants.DUP_X2); }
dup2_x1()217     public void dup2_x1() { mv.visitInsn(Constants.DUP2_X1); }
dup2_x2()218     public void dup2_x2() { mv.visitInsn(Constants.DUP2_X2); }
swap()219     public void swap() { mv.visitInsn(Constants.SWAP); }
aconst_null()220     public void aconst_null() { mv.visitInsn(Constants.ACONST_NULL); }
221 
swap(Type prev, Type type)222     public void swap(Type prev, Type type) {
223         if (type.getSize() == 1) {
224             if (prev.getSize() == 1) {
225                 swap(); // same as dup_x1(), pop();
226             } else {
227                 dup_x2();
228                 pop();
229             }
230         } else {
231             if (prev.getSize() == 1) {
232                 dup2_x1();
233                 pop2();
234             } else {
235                 dup2_x2();
236                 pop2();
237             }
238         }
239     }
240 
monitorenter()241     public void monitorenter() { mv.visitInsn(Constants.MONITORENTER); }
monitorexit()242     public void monitorexit() { mv.visitInsn(Constants.MONITOREXIT); }
243 
math(int op, Type type)244     public void math(int op, Type type) { mv.visitInsn(type.getOpcode(op)); }
245 
array_load(Type type)246     public void array_load(Type type) { mv.visitInsn(type.getOpcode(Constants.IALOAD)); }
array_store(Type type)247     public void array_store(Type type) { mv.visitInsn(type.getOpcode(Constants.IASTORE)); }
248 
249     /**
250      * Casts from one primitive numeric type to another
251      */
cast_numeric(Type from, Type to)252     public void cast_numeric(Type from, Type to) {
253         if (from != to) {
254             if (from == Type.DOUBLE_TYPE) {
255                 if (to == Type.FLOAT_TYPE) {
256                     mv.visitInsn(Constants.D2F);
257                 } else if (to == Type.LONG_TYPE) {
258                     mv.visitInsn(Constants.D2L);
259                 } else {
260                     mv.visitInsn(Constants.D2I);
261                     cast_numeric(Type.INT_TYPE, to);
262                 }
263             } else if (from == Type.FLOAT_TYPE) {
264                 if (to == Type.DOUBLE_TYPE) {
265                     mv.visitInsn(Constants.F2D);
266                 } else if (to == Type.LONG_TYPE) {
267                     mv.visitInsn(Constants.F2L);
268                 } else {
269                     mv.visitInsn(Constants.F2I);
270                     cast_numeric(Type.INT_TYPE, to);
271                 }
272             } else if (from == Type.LONG_TYPE) {
273                 if (to == Type.DOUBLE_TYPE) {
274                     mv.visitInsn(Constants.L2D);
275                 } else if (to == Type.FLOAT_TYPE) {
276                     mv.visitInsn(Constants.L2F);
277                 } else {
278                     mv.visitInsn(Constants.L2I);
279                     cast_numeric(Type.INT_TYPE, to);
280                 }
281             } else {
282                 if (to == Type.BYTE_TYPE) {
283                     mv.visitInsn(Constants.I2B);
284                 } else if (to == Type.CHAR_TYPE) {
285                     mv.visitInsn(Constants.I2C);
286                 } else if (to == Type.DOUBLE_TYPE) {
287                     mv.visitInsn(Constants.I2D);
288                 } else if (to == Type.FLOAT_TYPE) {
289                     mv.visitInsn(Constants.I2F);
290                 } else if (to == Type.LONG_TYPE) {
291                     mv.visitInsn(Constants.I2L);
292                 } else if (to == Type.SHORT_TYPE) {
293                     mv.visitInsn(Constants.I2S);
294                 }
295             }
296         }
297     }
298 
push(int i)299     public void push(int i) {
300         if (i < -1) {
301             mv.visitLdcInsn(new Integer(i));
302         } else if (i <= 5) {
303             mv.visitInsn(TypeUtils.ICONST(i));
304         } else if (i <= Byte.MAX_VALUE) {
305             mv.visitIntInsn(Constants.BIPUSH, i);
306         } else if (i <= Short.MAX_VALUE) {
307             mv.visitIntInsn(Constants.SIPUSH, i);
308         } else {
309             mv.visitLdcInsn(new Integer(i));
310         }
311     }
312 
push(long value)313     public void push(long value) {
314         if (value == 0L || value == 1L) {
315             mv.visitInsn(TypeUtils.LCONST(value));
316         } else {
317             mv.visitLdcInsn(new Long(value));
318         }
319     }
320 
push(float value)321     public void push(float value) {
322         if (value == 0f || value == 1f || value == 2f) {
323             mv.visitInsn(TypeUtils.FCONST(value));
324         } else {
325             mv.visitLdcInsn(new Float(value));
326         }
327     }
push(double value)328     public void push(double value) {
329         if (value == 0d || value == 1d) {
330             mv.visitInsn(TypeUtils.DCONST(value));
331         } else {
332             mv.visitLdcInsn(new Double(value));
333         }
334     }
335 
push(String value)336     public void push(String value) {
337         mv.visitLdcInsn(value);
338     }
339 
newarray()340     public void newarray() {
341         newarray(Constants.TYPE_OBJECT);
342     }
343 
newarray(Type type)344     public void newarray(Type type) {
345         if (TypeUtils.isPrimitive(type)) {
346             mv.visitIntInsn(Constants.NEWARRAY, TypeUtils.NEWARRAY(type));
347         } else {
348             emit_type(Constants.ANEWARRAY, type);
349         }
350     }
351 
arraylength()352     public void arraylength() {
353         mv.visitInsn(Constants.ARRAYLENGTH);
354     }
355 
load_this()356     public void load_this() {
357         if (TypeUtils.isStatic(state.access)) {
358             throw new IllegalStateException("no 'this' pointer within static method");
359         }
360         mv.visitVarInsn(Constants.ALOAD, 0);
361     }
362 
363     /**
364      * Pushes all of the arguments of the current method onto the stack.
365      */
load_args()366     public void load_args() {
367         load_args(0, state.argumentTypes.length);
368     }
369 
370     /**
371      * Pushes the specified argument of the current method onto the stack.
372      * @param index the zero-based index into the argument list
373      */
load_arg(int index)374     public void load_arg(int index) {
375         load_local(state.argumentTypes[index],
376                    state.localOffset + skipArgs(index));
377     }
378 
379     // zero-based (see load_this)
load_args(int fromArg, int count)380     public void load_args(int fromArg, int count) {
381         int pos = state.localOffset + skipArgs(fromArg);
382         for (int i = 0; i < count; i++) {
383             Type t = state.argumentTypes[fromArg + i];
384             load_local(t, pos);
385             pos += t.getSize();
386         }
387     }
388 
skipArgs(int numArgs)389     private int skipArgs(int numArgs) {
390         int amount = 0;
391         for (int i = 0; i < numArgs; i++) {
392             amount += state.argumentTypes[i].getSize();
393         }
394         return amount;
395     }
396 
load_local(Type t, int pos)397     private void load_local(Type t, int pos) {
398         // TODO: make t == null ok?
399         mv.visitVarInsn(t.getOpcode(Constants.ILOAD), pos);
400     }
401 
store_local(Type t, int pos)402     private void store_local(Type t, int pos) {
403         // TODO: make t == null ok?
404         mv.visitVarInsn(t.getOpcode(Constants.ISTORE), pos);
405     }
406 
iinc(Local local, int amount)407     public void iinc(Local local, int amount) {
408         mv.visitIincInsn(local.getIndex(), amount);
409     }
410 
store_local(Local local)411     public void store_local(Local local) {
412         store_local(local.getType(), local.getIndex());
413     }
414 
load_local(Local local)415     public void load_local(Local local) {
416         load_local(local.getType(), local.getIndex());
417     }
418 
return_value()419     public void return_value() {
420         mv.visitInsn(state.sig.getReturnType().getOpcode(Constants.IRETURN));
421     }
422 
getfield(String name)423     public void getfield(String name) {
424         ClassEmitter.FieldInfo info = ce.getFieldInfo(name);
425         int opcode = TypeUtils.isStatic(info.access) ? Constants.GETSTATIC : Constants.GETFIELD;
426         emit_field(opcode, ce.getClassType(), name, info.type);
427     }
428 
putfield(String name)429     public void putfield(String name) {
430         ClassEmitter.FieldInfo info = ce.getFieldInfo(name);
431         int opcode = TypeUtils.isStatic(info.access) ? Constants.PUTSTATIC : Constants.PUTFIELD;
432         emit_field(opcode, ce.getClassType(), name, info.type);
433     }
434 
super_getfield(String name, Type type)435     public void super_getfield(String name, Type type) {
436         emit_field(Constants.GETFIELD, ce.getSuperType(), name, type);
437     }
438 
super_putfield(String name, Type type)439     public void super_putfield(String name, Type type) {
440         emit_field(Constants.PUTFIELD, ce.getSuperType(), name, type);
441     }
442 
super_getstatic(String name, Type type)443     public void super_getstatic(String name, Type type) {
444         emit_field(Constants.GETSTATIC, ce.getSuperType(), name, type);
445     }
446 
super_putstatic(String name, Type type)447     public void super_putstatic(String name, Type type) {
448         emit_field(Constants.PUTSTATIC, ce.getSuperType(), name, type);
449     }
450 
getfield(Type owner, String name, Type type)451     public void getfield(Type owner, String name, Type type) {
452         emit_field(Constants.GETFIELD, owner, name, type);
453     }
454 
putfield(Type owner, String name, Type type)455     public void putfield(Type owner, String name, Type type) {
456         emit_field(Constants.PUTFIELD, owner, name, type);
457     }
458 
getstatic(Type owner, String name, Type type)459     public void getstatic(Type owner, String name, Type type) {
460         emit_field(Constants.GETSTATIC, owner, name, type);
461     }
462 
putstatic(Type owner, String name, Type type)463     public void putstatic(Type owner, String name, Type type) {
464         emit_field(Constants.PUTSTATIC, owner, name, type);
465     }
466 
467     // package-protected for EmitUtils, try to fix
emit_field(int opcode, Type ctype, String name, Type ftype)468     void emit_field(int opcode, Type ctype, String name, Type ftype) {
469         mv.visitFieldInsn(opcode,
470                           ctype.getInternalName(),
471                           name,
472                           ftype.getDescriptor());
473     }
474 
super_invoke()475     public void super_invoke() {
476         super_invoke(state.sig);
477     }
478 
super_invoke(Signature sig)479     public void super_invoke(Signature sig) {
480         emit_invoke(Constants.INVOKESPECIAL, ce.getSuperType(), sig);
481     }
482 
invoke_constructor(Type type)483     public void invoke_constructor(Type type) {
484         invoke_constructor(type, CSTRUCT_NULL);
485     }
486 
super_invoke_constructor()487     public void super_invoke_constructor() {
488         invoke_constructor(ce.getSuperType());
489     }
490 
invoke_constructor_this()491     public void invoke_constructor_this() {
492         invoke_constructor(ce.getClassType());
493     }
494 
emit_invoke(int opcode, Type type, Signature sig)495     private void emit_invoke(int opcode, Type type, Signature sig) {
496         if (sig.getName().equals(Constants.CONSTRUCTOR_NAME) &&
497             ((opcode == Constants.INVOKEVIRTUAL) ||
498              (opcode == Constants.INVOKESTATIC))) {
499             // TODO: error
500         }
501         mv.visitMethodInsn(opcode,
502                            type.getInternalName(),
503                            sig.getName(),
504                            sig.getDescriptor());
505     }
506 
invoke_interface(Type owner, Signature sig)507     public void invoke_interface(Type owner, Signature sig) {
508         emit_invoke(Constants.INVOKEINTERFACE, owner, sig);
509     }
510 
invoke_virtual(Type owner, Signature sig)511     public void invoke_virtual(Type owner, Signature sig) {
512         emit_invoke(Constants.INVOKEVIRTUAL, owner, sig);
513     }
514 
invoke_static(Type owner, Signature sig)515     public void invoke_static(Type owner, Signature sig) {
516         emit_invoke(Constants.INVOKESTATIC, owner, sig);
517     }
518 
invoke_virtual_this(Signature sig)519     public void invoke_virtual_this(Signature sig) {
520         invoke_virtual(ce.getClassType(), sig);
521     }
522 
invoke_static_this(Signature sig)523     public void invoke_static_this(Signature sig) {
524         invoke_static(ce.getClassType(), sig);
525     }
526 
invoke_constructor(Type type, Signature sig)527     public void invoke_constructor(Type type, Signature sig) {
528         emit_invoke(Constants.INVOKESPECIAL, type, sig);
529     }
530 
invoke_constructor_this(Signature sig)531     public void invoke_constructor_this(Signature sig) {
532         invoke_constructor(ce.getClassType(), sig);
533     }
534 
super_invoke_constructor(Signature sig)535     public void super_invoke_constructor(Signature sig) {
536         invoke_constructor(ce.getSuperType(), sig);
537     }
538 
new_instance_this()539     public void new_instance_this() {
540         new_instance(ce.getClassType());
541     }
542 
new_instance(Type type)543     public void new_instance(Type type) {
544         emit_type(Constants.NEW, type);
545     }
546 
emit_type(int opcode, Type type)547     private void emit_type(int opcode, Type type) {
548         String desc;
549         if (TypeUtils.isArray(type)) {
550             desc = type.getDescriptor();
551         } else {
552             desc = type.getInternalName();
553         }
554         mv.visitTypeInsn(opcode, desc);
555     }
556 
aaload(int index)557     public void aaload(int index) {
558         push(index);
559         aaload();
560     }
561 
aaload()562     public void aaload() { mv.visitInsn(Constants.AALOAD); }
aastore()563     public void aastore() { mv.visitInsn(Constants.AASTORE); }
athrow()564     public void athrow() { mv.visitInsn(Constants.ATHROW); }
565 
make_label()566     public Label make_label() {
567         return new Label();
568     }
569 
make_local()570     public Local make_local() {
571         return make_local(Constants.TYPE_OBJECT);
572     }
573 
make_local(Type type)574     public Local make_local(Type type) {
575         return new Local(newLocal(type.getSize()), type);
576     }
577 
checkcast_this()578     public void checkcast_this() {
579         checkcast(ce.getClassType());
580     }
581 
checkcast(Type type)582     public void checkcast(Type type) {
583         if (!type.equals(Constants.TYPE_OBJECT)) {
584             emit_type(Constants.CHECKCAST, type);
585         }
586     }
587 
instance_of(Type type)588     public void instance_of(Type type) {
589         emit_type(Constants.INSTANCEOF, type);
590     }
591 
instance_of_this()592     public void instance_of_this() {
593         instance_of(ce.getClassType());
594     }
595 
process_switch(int[] keys, ProcessSwitchCallback callback)596     public void process_switch(int[] keys, ProcessSwitchCallback callback) {
597         float density;
598         if (keys.length == 0) {
599             density = 0;
600         } else {
601             density = (float)keys.length / (keys[keys.length - 1] - keys[0] + 1);
602         }
603         process_switch(keys, callback, density >= 0.5f);
604     }
605 
process_switch(int[] keys, ProcessSwitchCallback callback, boolean useTable)606     public void process_switch(int[] keys, ProcessSwitchCallback callback, boolean useTable) {
607         if (!isSorted(keys))
608             throw new IllegalArgumentException("keys to switch must be sorted ascending");
609         Label def = make_label();
610         Label end = make_label();
611 
612         try {
613             if (keys.length > 0) {
614                 int len = keys.length;
615                 int min = keys[0];
616                 int max = keys[len - 1];
617                 int range = max - min + 1;
618 
619                 if (useTable) {
620                     Label[] labels = new Label[range];
621                     Arrays.fill(labels, def);
622                     for (int i = 0; i < len; i++) {
623                         labels[keys[i] - min] = make_label();
624                     }
625                     mv.visitTableSwitchInsn(min, max, def, labels);
626                     for (int i = 0; i < range; i++) {
627                         Label label = labels[i];
628                         if (label != def) {
629                             mark(label);
630                             callback.processCase(i + min, end);
631                         }
632                     }
633                 } else {
634                     Label[] labels = new Label[len];
635                     for (int i = 0; i < len; i++) {
636                         labels[i] = make_label();
637                     }
638                     mv.visitLookupSwitchInsn(def, keys, labels);
639                     for (int i = 0; i < len; i++) {
640                         mark(labels[i]);
641                         callback.processCase(keys[i], end);
642                     }
643                 }
644             }
645 
646             mark(def);
647             callback.processDefault();
648             mark(end);
649 
650         } catch (RuntimeException e) {
651             throw e;
652         } catch (Error e) {
653             throw e;
654         } catch (Exception e) {
655             throw new CodeGenerationException(e);
656         }
657     }
658 
isSorted(int[] keys)659     private static boolean isSorted(int[] keys) {
660         for (int i = 1; i < keys.length; i++) {
661             if (keys[i] < keys[i - 1])
662                 return false;
663         }
664         return true;
665     }
666 
mark(Label label)667     public void mark(Label label) {
668         mv.visitLabel(label);
669     }
670 
mark()671     Label mark() {
672         Label label = make_label();
673         mv.visitLabel(label);
674         return label;
675     }
676 
push(boolean value)677     public void push(boolean value) {
678         push(value ? 1 : 0);
679     }
680 
681     /**
682      * Toggles the integer on the top of the stack from 1 to 0 or vice versa
683      */
not()684     public void not() {
685         push(1);
686         math(XOR, Type.INT_TYPE);
687     }
688 
throw_exception(Type type, String msg)689     public void throw_exception(Type type, String msg) {
690         new_instance(type);
691         dup();
692         push(msg);
693         invoke_constructor(type, CSTRUCT_STRING);
694         athrow();
695     }
696 
697     /**
698      * If the argument is a primitive class, replaces the primitive value
699      * on the top of the stack with the wrapped (Object) equivalent. For
700      * example, char -> Character.
701      * If the class is Void, a null is pushed onto the stack instead.
702      * @param type the class indicating the current type of the top stack value
703      */
box(Type type)704     public void box(Type type) {
705         if (TypeUtils.isPrimitive(type)) {
706             if (type == Type.VOID_TYPE) {
707                 aconst_null();
708             } else {
709                 Type boxed = TypeUtils.getBoxedType(type);
710                 new_instance(boxed);
711                 if (type.getSize() == 2) {
712                     // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
713                     dup_x2();
714                     dup_x2();
715                     pop();
716                 } else {
717                     // p -> po -> opo -> oop -> o
718                     dup_x1();
719                     swap();
720                 }
721                 invoke_constructor(boxed, new Signature(Constants.CONSTRUCTOR_NAME, Type.VOID_TYPE, new Type[]{ type }));
722             }
723         }
724     }
725 
726     /**
727      * If the argument is a primitive class, replaces the object
728      * on the top of the stack with the unwrapped (primitive)
729      * equivalent. For example, Character -> char.
730      * @param type the class indicating the desired type of the top stack value
731      * @return true if the value was unboxed
732      */
unbox(Type type)733     public void unbox(Type type) {
734         Type t = Constants.TYPE_NUMBER;
735         Signature sig = null;
736         switch (type.getSort()) {
737         case Type.VOID:
738             return;
739         case Type.CHAR:
740             t = Constants.TYPE_CHARACTER;
741             sig = CHAR_VALUE;
742             break;
743         case Type.BOOLEAN:
744             t = Constants.TYPE_BOOLEAN;
745             sig = BOOLEAN_VALUE;
746             break;
747         case Type.DOUBLE:
748             sig = DOUBLE_VALUE;
749             break;
750         case Type.FLOAT:
751             sig = FLOAT_VALUE;
752             break;
753         case Type.LONG:
754             sig = LONG_VALUE;
755             break;
756         case Type.INT:
757         case Type.SHORT:
758         case Type.BYTE:
759             sig = INT_VALUE;
760         }
761 
762         if (sig == null) {
763             checkcast(type);
764         } else {
765             checkcast(t);
766             invoke_virtual(t, sig);
767         }
768     }
769 
770     /**
771      * Allocates and fills an Object[] array with the arguments to the
772      * current method. Primitive values are inserted as their boxed
773      * (Object) equivalents.
774      */
create_arg_array()775     public void create_arg_array() {
776         /* generates:
777            Object[] args = new Object[]{ arg1, new Integer(arg2) };
778          */
779 
780         push(state.argumentTypes.length);
781         newarray();
782         for (int i = 0; i < state.argumentTypes.length; i++) {
783             dup();
784             push(i);
785             load_arg(i);
786             box(state.argumentTypes[i]);
787             aastore();
788         }
789     }
790 
791 
792     /**
793      * Pushes a zero onto the stack if the argument is a primitive class, or a null otherwise.
794      */
zero_or_null(Type type)795     public void zero_or_null(Type type) {
796         if (TypeUtils.isPrimitive(type)) {
797             switch (type.getSort()) {
798             case Type.DOUBLE:
799                 push(0d);
800                 break;
801             case Type.LONG:
802                 push(0L);
803                 break;
804             case Type.FLOAT:
805                 push(0f);
806                 break;
807             case Type.VOID:
808                 aconst_null();
809             default:
810                 push(0);
811             }
812         } else {
813             aconst_null();
814         }
815     }
816 
817     /**
818      * Unboxes the object on the top of the stack. If the object is null, the
819      * unboxed primitive value becomes zero.
820      */
unbox_or_zero(Type type)821     public void unbox_or_zero(Type type) {
822         if (TypeUtils.isPrimitive(type)) {
823             if (type != Type.VOID_TYPE) {
824                 Label nonNull = make_label();
825                 Label end = make_label();
826                 dup();
827                 ifnonnull(nonNull);
828                 pop();
829                 zero_or_null(type);
830                 goTo(end);
831                 mark(nonNull);
832                 unbox(type);
833                 mark(end);
834             }
835         } else {
836             checkcast(type);
837         }
838     }
839 
visitMaxs(int maxStack, int maxLocals)840     public void visitMaxs(int maxStack, int maxLocals) {
841         if (!TypeUtils.isAbstract(state.access)) {
842             mv.visitMaxs(0, 0);
843         }
844     }
845 
invoke(MethodInfo method, Type virtualType)846     public void invoke(MethodInfo method, Type virtualType) {
847         ClassInfo classInfo = method.getClassInfo();
848         Type type = classInfo.getType();
849         Signature sig = method.getSignature();
850         if (sig.getName().equals(Constants.CONSTRUCTOR_NAME)) {
851             invoke_constructor(type, sig);
852         } else if (TypeUtils.isInterface(classInfo.getModifiers())) {
853             invoke_interface(type, sig);
854         } else if (TypeUtils.isStatic(method.getModifiers())) {
855             invoke_static(type, sig);
856         } else {
857             invoke_virtual(virtualType, sig);
858         }
859     }
860 
invoke(MethodInfo method)861     public void invoke(MethodInfo method) {
862         invoke(method, method.getClassInfo().getType());
863     }
864 }
865