1 package org.simantics.scl.compiler.internal.codegen.utils;
3 import org.cojen.classfile.MethodDesc;
4 import org.cojen.classfile.TypeDesc;
5 import org.objectweb.asm.Label;
6 import org.objectweb.asm.MethodVisitor;
7 import org.objectweb.asm.Opcodes;
8 import org.objectweb.asm.Type;
10 public class MethodBuilderBase {
11 private ClassBuilder classBuilder;
12 private MethodVisitor methodVisitor;
13 private LocalVariable[] parameters;
14 private int localVariableCount = 0;
16 public MethodBuilderBase(ClassBuilder classBuilder, boolean isStatic, MethodVisitor methodVisitor, TypeDesc[] parameterTypes) {
17 this.classBuilder = classBuilder;
18 this.methodVisitor = methodVisitor;
19 this.parameters = new LocalVariable[parameterTypes.length];
22 for(int i=0;i<parameterTypes.length;++i) {
23 parameters[i] = createLocalVariable("p" + i, parameterTypes[i]);
24 methodVisitor.visitParameter("p" + i, Opcodes.ACC_PUBLIC);
26 methodVisitor.visitCode();
29 public void loadConstant(boolean value) {
31 methodVisitor.visitInsn(Opcodes.ICONST_1);
33 methodVisitor.visitInsn(Opcodes.ICONST_0);
36 public void loadConstant(int value) {
37 if (value >= -1 && value <= 5) {
38 methodVisitor.visitInsn(Opcodes.ICONST_0 + value);
39 } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
40 methodVisitor.visitIntInsn(Opcodes.BIPUSH, value);
41 } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
42 methodVisitor.visitIntInsn(Opcodes.SIPUSH, value);
44 methodVisitor.visitLdcInsn(value);
48 public void loadConstant(long value) {
50 methodVisitor.visitInsn(Opcodes.LCONST_0);
52 methodVisitor.visitInsn(Opcodes.LCONST_1);
54 methodVisitor.visitLdcInsn(value);
57 public void loadConstant(float value) {
59 methodVisitor.visitInsn(Opcodes.FCONST_0);
60 else if(value == 1.0f)
61 methodVisitor.visitInsn(Opcodes.FCONST_1);
63 methodVisitor.visitLdcInsn(value);
66 public void loadConstant(double value) {
68 methodVisitor.visitInsn(Opcodes.DCONST_0);
70 methodVisitor.visitInsn(Opcodes.DCONST_1);
72 methodVisitor.visitLdcInsn(value);
75 public void loadConstant(String value) {
76 methodVisitor.visitLdcInsn(value);
79 public void loadConstant(TypeDesc value) {
80 if(value.isPrimitive())
81 loadStaticField(getClassName(value.toObjectType()), "TYPE", Constants.CLASS);
83 methodVisitor.visitLdcInsn(Type.getType(value.getDescriptor()));
87 methodVisitor.visitInsn(Opcodes.DUP);
91 methodVisitor.visitInsn(Opcodes.DUP2);
95 methodVisitor.visitInsn(Opcodes.DUP_X1);
99 methodVisitor.visitInsn(Opcodes.SWAP);
103 methodVisitor.visitInsn(Opcodes.POP);
107 methodVisitor.visitInsn(Opcodes.POP2);
110 public void convert(TypeDesc fromType, TypeDesc toType) {
111 switch(fromType.getTypeCode() + 16*toType.getTypeCode()) {
112 case TypeDesc.OBJECT_CODE + 16*TypeDesc.BOOLEAN_CODE:
113 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
115 case TypeDesc.OBJECT_CODE + 16*TypeDesc.INT_CODE:
116 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
118 case TypeDesc.OBJECT_CODE + 16*TypeDesc.FLOAT_CODE:
119 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
121 case TypeDesc.OBJECT_CODE + 16*TypeDesc.DOUBLE_CODE:
122 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
124 case TypeDesc.OBJECT_CODE + 16*TypeDesc.CHAR_CODE:
125 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
127 case TypeDesc.OBJECT_CODE + 16*TypeDesc.LONG_CODE:
128 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
130 case TypeDesc.OBJECT_CODE + 16*TypeDesc.SHORT_CODE:
131 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
133 case TypeDesc.OBJECT_CODE + 16*TypeDesc.BYTE_CODE:
134 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
136 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.FLOAT_CODE:
137 methodVisitor.visitInsn(Opcodes.D2F);
139 case TypeDesc.FLOAT_CODE + 16*TypeDesc.DOUBLE_CODE:
140 methodVisitor.visitInsn(Opcodes.F2D);
142 case TypeDesc.INT_CODE + 16*TypeDesc.DOUBLE_CODE:
143 methodVisitor.visitInsn(Opcodes.I2D);
145 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.INT_CODE:
146 methodVisitor.visitInsn(Opcodes.D2I);
148 case TypeDesc.INT_CODE + 16*TypeDesc.FLOAT_CODE:
149 methodVisitor.visitInsn(Opcodes.I2F);
151 case TypeDesc.FLOAT_CODE + 16*TypeDesc.INT_CODE:
152 methodVisitor.visitInsn(Opcodes.F2I);
154 case TypeDesc.LONG_CODE + 16*TypeDesc.FLOAT_CODE:
155 methodVisitor.visitInsn(Opcodes.L2F);
157 case TypeDesc.FLOAT_CODE + 16*TypeDesc.LONG_CODE:
158 methodVisitor.visitInsn(Opcodes.F2L);
160 case TypeDesc.LONG_CODE + 16*TypeDesc.DOUBLE_CODE:
161 methodVisitor.visitInsn(Opcodes.L2D);
163 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.LONG_CODE:
164 methodVisitor.visitInsn(Opcodes.D2L);
166 case TypeDesc.INT_CODE + 16*TypeDesc.LONG_CODE:
167 methodVisitor.visitInsn(Opcodes.I2L);
169 case TypeDesc.LONG_CODE + 16*TypeDesc.INT_CODE:
170 methodVisitor.visitInsn(Opcodes.L2I);
172 case TypeDesc.INT_CODE + 16*TypeDesc.SHORT_CODE:
173 methodVisitor.visitInsn(Opcodes.I2S);
175 case TypeDesc.SHORT_CODE + 16*TypeDesc.INT_CODE:
176 case TypeDesc.BOOLEAN_CODE + 16*TypeDesc.INT_CODE:
179 case TypeDesc.INT_CODE + 16*TypeDesc.BYTE_CODE:
180 methodVisitor.visitInsn(Opcodes.I2B);
182 case TypeDesc.BYTE_CODE + 16*TypeDesc.INT_CODE:
185 case TypeDesc.INT_CODE + 16*TypeDesc.CHAR_CODE:
186 methodVisitor.visitInsn(Opcodes.I2C);
188 case TypeDesc.CHAR_CODE + 16*TypeDesc.INT_CODE:
192 System.out.println("convert: " + fromType + " -> " + toType);
196 public void convert(TypeDesc fromType, TypeDesc toType,
197 int convertFpNormal) {
198 convert(fromType, toType);
201 public LocalVariable createLocalVariable(String name, TypeDesc type) {
202 int index = localVariableCount;
203 localVariableCount += type == TypeDesc.DOUBLE || type == TypeDesc.LONG ? 2 : 1;
204 return new LocalVariable(index, type);
207 public void storeLocal(LocalVariable local) {
208 switch(local.type.getTypeCode()) {
209 case TypeDesc.BOOLEAN_CODE:
210 case TypeDesc.BYTE_CODE:
211 case TypeDesc.CHAR_CODE:
212 case TypeDesc.SHORT_CODE:
213 case TypeDesc.INT_CODE:
214 methodVisitor.visitVarInsn(Opcodes.ISTORE, local.index);
216 case TypeDesc.FLOAT_CODE:
217 methodVisitor.visitVarInsn(Opcodes.FSTORE, local.index);
219 case TypeDesc.DOUBLE_CODE:
220 methodVisitor.visitVarInsn(Opcodes.DSTORE, local.index);
222 case TypeDesc.LONG_CODE:
223 methodVisitor.visitVarInsn(Opcodes.LSTORE, local.index);
225 case TypeDesc.OBJECT_CODE:
226 methodVisitor.visitVarInsn(Opcodes.ASTORE, local.index);
228 default: throw new IllegalArgumentException();
232 public void ifZeroComparisonBranch(Label location, String choice) {
234 if(choice.equals("!="))
235 opcode = Opcodes.IFNE;
236 else if(choice.equals("=="))
237 opcode = Opcodes.IFEQ;
238 else if(choice.equals("<"))
239 opcode = Opcodes.IFLT;
240 else if(choice.equals(">"))
241 opcode = Opcodes.IFGT;
242 else if(choice.equals("<="))
243 opcode = Opcodes.IFLE;
244 else if(choice.equals(">="))
245 opcode = Opcodes.IFGE;
247 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
248 methodVisitor.visitJumpInsn(opcode, location);
251 public void ifComparisonBranch(Label location, String choice, TypeDesc type) {
252 switch(type.getTypeCode()) {
253 case TypeDesc.BOOLEAN_CODE:
254 case TypeDesc.BYTE_CODE:
255 case TypeDesc.CHAR_CODE:
256 case TypeDesc.SHORT_CODE:
257 case TypeDesc.INT_CODE: {
259 if(choice.equals("!="))
260 opcode = Opcodes.IF_ICMPNE;
261 else if(choice.equals("=="))
262 opcode = Opcodes.IF_ICMPEQ;
263 else if(choice.equals("<"))
264 opcode = Opcodes.IF_ICMPLT;
265 else if(choice.equals(">"))
266 opcode = Opcodes.IF_ICMPGT;
267 else if(choice.equals("<="))
268 opcode = Opcodes.IF_ICMPLE;
269 else if(choice.equals(">="))
270 opcode = Opcodes.IF_ICMPGE;
272 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
273 methodVisitor.visitJumpInsn(opcode, location);
275 case TypeDesc.FLOAT_CODE:
276 methodVisitor.visitInsn(Opcodes.FCMPL);
277 ifZeroComparisonBranch(location, choice);
279 case TypeDesc.DOUBLE_CODE:
280 methodVisitor.visitInsn(Opcodes.DCMPL);
281 ifZeroComparisonBranch(location, choice);
283 case TypeDesc.LONG_CODE:
284 methodVisitor.visitInsn(Opcodes.LCMP);
285 ifZeroComparisonBranch(location, choice);
287 case TypeDesc.OBJECT_CODE: {
289 if(choice.equals("!="))
290 opcode = Opcodes.IF_ACMPNE;
291 else if(choice.equals("=="))
292 opcode = Opcodes.IF_ACMPEQ;
294 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
295 methodVisitor.visitJumpInsn(opcode, location);
297 default: throw new IllegalArgumentException();
301 public void ifNullBranch(Label location, boolean choice) {
302 methodVisitor.visitJumpInsn(choice ? Opcodes.IFNULL : Opcodes.IFNONNULL, location);
305 public void branch(Label location) {
306 methodVisitor.visitJumpInsn(Opcodes.GOTO, location);
309 public Label createLabel() {
313 public void setLocation(Label label) {
314 methodVisitor.visitLabel(label);
317 public void loadLocal(LocalVariable local) {
318 switch(local.type.getTypeCode()) {
319 case TypeDesc.BOOLEAN_CODE:
320 case TypeDesc.BYTE_CODE:
321 case TypeDesc.CHAR_CODE:
322 case TypeDesc.SHORT_CODE:
323 case TypeDesc.INT_CODE:
324 methodVisitor.visitVarInsn(Opcodes.ILOAD, local.index);
326 case TypeDesc.FLOAT_CODE:
327 methodVisitor.visitVarInsn(Opcodes.FLOAD, local.index);
329 case TypeDesc.DOUBLE_CODE:
330 methodVisitor.visitVarInsn(Opcodes.DLOAD, local.index);
332 case TypeDesc.LONG_CODE:
333 methodVisitor.visitVarInsn(Opcodes.LLOAD, local.index);
335 case TypeDesc.OBJECT_CODE:
336 methodVisitor.visitVarInsn(Opcodes.ALOAD, local.index);
338 default: throw new IllegalArgumentException();
342 public void loadNull() {
343 methodVisitor.visitInsn(Opcodes.ACONST_NULL);
346 public void loadFromArray(TypeDesc type) {
347 switch(type.getTypeCode()) {
348 case TypeDesc.BOOLEAN_CODE:
349 methodVisitor.visitInsn(Opcodes.BALOAD);
351 case TypeDesc.BYTE_CODE:
352 methodVisitor.visitInsn(Opcodes.BALOAD);
354 case TypeDesc.CHAR_CODE:
355 methodVisitor.visitInsn(Opcodes.CALOAD);
357 case TypeDesc.SHORT_CODE:
358 methodVisitor.visitInsn(Opcodes.SALOAD);
360 case TypeDesc.INT_CODE:
361 methodVisitor.visitInsn(Opcodes.IALOAD);
363 case TypeDesc.FLOAT_CODE:
364 methodVisitor.visitInsn(Opcodes.FALOAD);
366 case TypeDesc.DOUBLE_CODE:
367 methodVisitor.visitInsn(Opcodes.DALOAD);
369 case TypeDesc.LONG_CODE:
370 methodVisitor.visitInsn(Opcodes.LALOAD);
372 case TypeDesc.OBJECT_CODE:
373 methodVisitor.visitInsn(Opcodes.AALOAD);
375 default: throw new IllegalArgumentException();
379 public void invokeInterface(String className, String methodName,
380 TypeDesc ret, TypeDesc[] params) {
381 checkClassName(className);
382 checkParameters(params);
383 methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, className, methodName,
384 MethodDesc.forArguments(ret, params).getDescriptor(), true);
387 public void invokeInterface(TypeDesc className, String methodName,
388 TypeDesc ret, TypeDesc[] params) {
389 invokeInterface(getClassName(className), methodName, ret, params);
392 public void invokeVirtual(String className, String methodName,
393 TypeDesc ret, TypeDesc[] params) {
394 checkClassName(className);
395 checkParameters(params);
396 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, methodName,
397 MethodDesc.forArguments(ret, params).getDescriptor(), false);
400 public void invokeVirtual(TypeDesc className, String methodName, TypeDesc ret,
402 invokeVirtual(getClassName(className), methodName, ret, params);
405 public void invokeConstructor(String className, TypeDesc[] params) {
406 checkClassName(className);
407 checkParameters(params);
408 methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "<init>",
409 MethodDesc.forArguments(TypeDesc.VOID, params).getDescriptor(), false);
412 public void invokeConstructor(TypeDesc className, TypeDesc[] params) {
413 invokeConstructor(getClassName(className), params);
416 public void invokeSuperConstructor(TypeDesc[] params) {
417 invokeConstructor(classBuilder.getSuperClassName(), params);
420 public void invokeStatic(String className, String methodName, TypeDesc ret,
422 checkClassName(className);
423 checkParameters(params);
424 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, className, methodName,
425 MethodDesc.forArguments(ret, params).getDescriptor(), false);
428 public void invokeStatic(TypeDesc className, String methodName, TypeDesc ret,
430 invokeStatic(getClassName(className), methodName, ret, params);
433 public void newObject(TypeDesc type) {
434 methodVisitor.visitTypeInsn(Opcodes.NEW, getClassName(type));
437 public void newObject(String type) {
438 methodVisitor.visitTypeInsn(Opcodes.NEW, type);
441 public void newObject(TypeDesc type, int dimensions) {
442 methodVisitor.visitMultiANewArrayInsn(type.getDescriptor(), dimensions);
445 public void loadStaticField(String className, String fieldName, TypeDesc type) {
446 checkClassName(className);
447 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, fieldName, type.getDescriptor());
450 public void loadStaticField(TypeDesc className, String fieldName,
452 loadStaticField(getClassName(className), fieldName, type);
455 public void loadField(String className, String fieldName, TypeDesc type) {
456 checkClassName(className);
457 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, className, fieldName, type.getDescriptor());
460 public void storeStaticField(String className, String fieldName,
462 checkClassName(className);
463 methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, className, fieldName, type.getDescriptor());
466 public void storeField(String className, String fieldName, TypeDesc type) {
467 checkClassName(className);
468 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, className, fieldName, type.getDescriptor());
471 public void storeToArray(TypeDesc type) {
472 switch(type.getTypeCode()) {
473 case TypeDesc.BOOLEAN_CODE:
474 methodVisitor.visitInsn(Opcodes.BASTORE);
476 case TypeDesc.BYTE_CODE:
477 methodVisitor.visitInsn(Opcodes.BASTORE);
479 case TypeDesc.CHAR_CODE:
480 methodVisitor.visitInsn(Opcodes.CASTORE);
482 case TypeDesc.SHORT_CODE:
483 methodVisitor.visitInsn(Opcodes.SASTORE);
485 case TypeDesc.INT_CODE:
486 methodVisitor.visitInsn(Opcodes.IASTORE);
488 case TypeDesc.FLOAT_CODE:
489 methodVisitor.visitInsn(Opcodes.FASTORE);
491 case TypeDesc.DOUBLE_CODE:
492 methodVisitor.visitInsn(Opcodes.DASTORE);
494 case TypeDesc.LONG_CODE:
495 methodVisitor.visitInsn(Opcodes.LASTORE);
497 case TypeDesc.OBJECT_CODE:
498 methodVisitor.visitInsn(Opcodes.AASTORE);
500 default: throw new IllegalArgumentException();
504 public void math(int opcode) {
505 methodVisitor.visitInsn(opcode);
508 public void throwObject() {
509 methodVisitor.visitInsn(Opcodes.ATHROW);
512 public void checkCast(TypeDesc type) {
513 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, getClassName(type));
516 public void instanceOf(TypeDesc type) {
517 methodVisitor.visitTypeInsn(Opcodes.INSTANCEOF, getClassName(type));
520 public void arrayLength() {
521 methodVisitor.visitInsn(Opcodes.ARRAYLENGTH);
524 public void loadThis() {
525 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
528 public LocalVariable getParameter(int index) {
529 return parameters[index];
532 public void returnVoid() {
533 methodVisitor.visitInsn(Opcodes.RETURN);
536 public void returnValue(TypeDesc type) {
537 switch(type.getTypeCode()) {
538 case TypeDesc.VOID_CODE:
539 methodVisitor.visitInsn(Opcodes.RETURN);
541 case TypeDesc.BOOLEAN_CODE:
542 case TypeDesc.BYTE_CODE:
543 case TypeDesc.CHAR_CODE:
544 case TypeDesc.SHORT_CODE:
545 case TypeDesc.INT_CODE:
546 methodVisitor.visitInsn(Opcodes.IRETURN);
548 case TypeDesc.FLOAT_CODE:
549 methodVisitor.visitInsn(Opcodes.FRETURN);
551 case TypeDesc.DOUBLE_CODE:
552 methodVisitor.visitInsn(Opcodes.DRETURN);
554 case TypeDesc.LONG_CODE:
555 methodVisitor.visitInsn(Opcodes.LRETURN);
557 case TypeDesc.OBJECT_CODE:
558 methodVisitor.visitInsn(Opcodes.ARETURN);
560 default: throw new IllegalArgumentException();
564 public void finish() {
565 methodVisitor.visitMaxs(0, 0);
566 methodVisitor.visitEnd();
569 public String getClassName() {
570 return classBuilder.className;
573 public static String getClassName(TypeDesc typeDesc) {
574 if(typeDesc.isArray())
575 return typeDesc.getDescriptor();
577 return typeDesc.getFullName().replace('.', '/');
580 private static void checkClassName(String className) {
581 ClassBuilder.checkClassName(className);
584 public static String getClassName(Class<?> clazz) {
585 return clazz.getName().replace('.', '/');
588 public void switch_(int[] values, Label[] labels, Label defaultLabel) {
590 int hi = values[values.length-1];
591 long table_space_cost = 4 + ((long) hi - lo + 1); // words
592 long table_time_cost = 3; // comparisons
593 long lookup_space_cost = 3 + 2 * (long) values.length;
594 long lookup_time_cost = values.length;
595 if(values.length > 0 &&
596 table_space_cost + 3 * table_time_cost <=
597 lookup_space_cost + 3 * lookup_time_cost) {
598 Label[] table = new Label[hi - lo + 1];
599 for(int i=0,j=0;i<table.length;++i) {
601 if(values[j] == id) {
602 table[i] = labels[j];
606 table[i] = defaultLabel;
608 methodVisitor.visitTableSwitchInsn(lo, hi, defaultLabel, table);
611 methodVisitor.visitLookupSwitchInsn(defaultLabel, values, labels);
614 private static void checkParameters(TypeDesc[] params) {
615 for(TypeDesc param : params)
616 if(param.equals(TypeDesc.VOID))
617 throw new IllegalArgumentException();