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;
15 private int currentLineNumber = -1;
17 public MethodBuilderBase(ClassBuilder classBuilder, boolean isStatic, MethodVisitor methodVisitor, TypeDesc[] parameterTypes) {
18 this.classBuilder = classBuilder;
19 this.methodVisitor = methodVisitor;
20 this.parameters = new LocalVariable[parameterTypes.length];
23 for(int i=0;i<parameterTypes.length;++i) {
24 parameters[i] = createLocalVariable("p" + i, parameterTypes[i]);
25 methodVisitor.visitParameter("p" + i, Opcodes.ACC_PUBLIC);
27 methodVisitor.visitCode();
30 public LocalVariable getThis(TypeDesc type) {
31 return new LocalVariable(0, type);
34 public void loadConstant(boolean value) {
36 methodVisitor.visitInsn(Opcodes.ICONST_1);
38 methodVisitor.visitInsn(Opcodes.ICONST_0);
41 public void loadConstant(int value) {
42 if (value >= -1 && value <= 5) {
43 methodVisitor.visitInsn(Opcodes.ICONST_0 + value);
44 } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
45 methodVisitor.visitIntInsn(Opcodes.BIPUSH, value);
46 } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
47 methodVisitor.visitIntInsn(Opcodes.SIPUSH, value);
49 methodVisitor.visitLdcInsn(value);
53 public void loadConstant(long value) {
55 methodVisitor.visitInsn(Opcodes.LCONST_0);
57 methodVisitor.visitInsn(Opcodes.LCONST_1);
59 methodVisitor.visitLdcInsn(value);
62 public void loadConstant(float value) {
64 methodVisitor.visitInsn(Opcodes.FCONST_0);
65 else if(value == 1.0f)
66 methodVisitor.visitInsn(Opcodes.FCONST_1);
68 methodVisitor.visitLdcInsn(value);
71 public void loadConstant(double value) {
73 methodVisitor.visitInsn(Opcodes.DCONST_0);
75 methodVisitor.visitInsn(Opcodes.DCONST_1);
77 methodVisitor.visitLdcInsn(value);
80 public void loadConstant(String value) {
81 methodVisitor.visitLdcInsn(value);
84 public void loadConstant(TypeDesc value) {
85 if(value.isPrimitive())
86 loadStaticField(getClassName(value.toObjectType()), "TYPE", Constants.CLASS);
88 methodVisitor.visitLdcInsn(Type.getType(value.getDescriptor()));
92 public int lineNumber(int lineNumber) {
93 if(lineNumber != currentLineNumber) {
94 int oldLineNumber = currentLineNumber;
95 Label label = createLabel();
97 methodVisitor.visitLineNumber(lineNumber, label);
98 currentLineNumber = lineNumber;
102 return currentLineNumber;
106 methodVisitor.visitInsn(Opcodes.DUP);
110 methodVisitor.visitInsn(Opcodes.DUP2);
113 public void dupX1() {
114 methodVisitor.visitInsn(Opcodes.DUP_X1);
118 methodVisitor.visitInsn(Opcodes.SWAP);
122 methodVisitor.visitInsn(Opcodes.POP);
126 methodVisitor.visitInsn(Opcodes.POP2);
129 public void convert(TypeDesc fromType, TypeDesc toType) {
130 switch(fromType.getTypeCode() + 16*toType.getTypeCode()) {
131 case TypeDesc.OBJECT_CODE + 16*TypeDesc.BOOLEAN_CODE:
132 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
134 case TypeDesc.OBJECT_CODE + 16*TypeDesc.INT_CODE:
135 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
137 case TypeDesc.OBJECT_CODE + 16*TypeDesc.FLOAT_CODE:
138 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
140 case TypeDesc.OBJECT_CODE + 16*TypeDesc.DOUBLE_CODE:
141 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
143 case TypeDesc.OBJECT_CODE + 16*TypeDesc.CHAR_CODE:
144 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
146 case TypeDesc.OBJECT_CODE + 16*TypeDesc.LONG_CODE:
147 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
149 case TypeDesc.OBJECT_CODE + 16*TypeDesc.SHORT_CODE:
150 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
152 case TypeDesc.OBJECT_CODE + 16*TypeDesc.BYTE_CODE:
153 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
155 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.FLOAT_CODE:
156 methodVisitor.visitInsn(Opcodes.D2F);
158 case TypeDesc.FLOAT_CODE + 16*TypeDesc.DOUBLE_CODE:
159 methodVisitor.visitInsn(Opcodes.F2D);
161 case TypeDesc.INT_CODE + 16*TypeDesc.DOUBLE_CODE:
162 methodVisitor.visitInsn(Opcodes.I2D);
164 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.INT_CODE:
165 methodVisitor.visitInsn(Opcodes.D2I);
167 case TypeDesc.INT_CODE + 16*TypeDesc.FLOAT_CODE:
168 methodVisitor.visitInsn(Opcodes.I2F);
170 case TypeDesc.FLOAT_CODE + 16*TypeDesc.INT_CODE:
171 methodVisitor.visitInsn(Opcodes.F2I);
173 case TypeDesc.LONG_CODE + 16*TypeDesc.FLOAT_CODE:
174 methodVisitor.visitInsn(Opcodes.L2F);
176 case TypeDesc.FLOAT_CODE + 16*TypeDesc.LONG_CODE:
177 methodVisitor.visitInsn(Opcodes.F2L);
179 case TypeDesc.LONG_CODE + 16*TypeDesc.DOUBLE_CODE:
180 methodVisitor.visitInsn(Opcodes.L2D);
182 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.LONG_CODE:
183 methodVisitor.visitInsn(Opcodes.D2L);
185 case TypeDesc.INT_CODE + 16*TypeDesc.LONG_CODE:
186 methodVisitor.visitInsn(Opcodes.I2L);
188 case TypeDesc.LONG_CODE + 16*TypeDesc.INT_CODE:
189 methodVisitor.visitInsn(Opcodes.L2I);
191 case TypeDesc.INT_CODE + 16*TypeDesc.SHORT_CODE:
192 methodVisitor.visitInsn(Opcodes.I2S);
194 case TypeDesc.SHORT_CODE + 16*TypeDesc.INT_CODE:
195 case TypeDesc.BOOLEAN_CODE + 16*TypeDesc.INT_CODE:
198 case TypeDesc.INT_CODE + 16*TypeDesc.BYTE_CODE:
199 methodVisitor.visitInsn(Opcodes.I2B);
201 case TypeDesc.BYTE_CODE + 16*TypeDesc.INT_CODE:
204 case TypeDesc.INT_CODE + 16*TypeDesc.CHAR_CODE:
205 methodVisitor.visitInsn(Opcodes.I2C);
207 case TypeDesc.CHAR_CODE + 16*TypeDesc.INT_CODE:
211 System.out.println("convert: " + fromType + " -> " + toType);
215 public void convert(TypeDesc fromType, TypeDesc toType,
216 int convertFpNormal) {
217 convert(fromType, toType);
220 public LocalVariable createLocalVariable(String name, TypeDesc type) {
221 int index = localVariableCount;
222 localVariableCount += type == TypeDesc.DOUBLE || type == TypeDesc.LONG ? 2 : 1;
223 return new LocalVariable(index, type);
226 public void storeLocal(LocalVariable local) {
227 switch(local.type.getTypeCode()) {
228 case TypeDesc.BOOLEAN_CODE:
229 case TypeDesc.BYTE_CODE:
230 case TypeDesc.CHAR_CODE:
231 case TypeDesc.SHORT_CODE:
232 case TypeDesc.INT_CODE:
233 methodVisitor.visitVarInsn(Opcodes.ISTORE, local.index);
235 case TypeDesc.FLOAT_CODE:
236 methodVisitor.visitVarInsn(Opcodes.FSTORE, local.index);
238 case TypeDesc.DOUBLE_CODE:
239 methodVisitor.visitVarInsn(Opcodes.DSTORE, local.index);
241 case TypeDesc.LONG_CODE:
242 methodVisitor.visitVarInsn(Opcodes.LSTORE, local.index);
244 case TypeDesc.OBJECT_CODE:
245 methodVisitor.visitVarInsn(Opcodes.ASTORE, local.index);
247 default: throw new IllegalArgumentException();
251 public void ifZeroComparisonBranch(Label location, String choice) {
253 if(choice.equals("!="))
254 opcode = Opcodes.IFNE;
255 else if(choice.equals("=="))
256 opcode = Opcodes.IFEQ;
257 else if(choice.equals("<"))
258 opcode = Opcodes.IFLT;
259 else if(choice.equals(">"))
260 opcode = Opcodes.IFGT;
261 else if(choice.equals("<="))
262 opcode = Opcodes.IFLE;
263 else if(choice.equals(">="))
264 opcode = Opcodes.IFGE;
266 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
267 methodVisitor.visitJumpInsn(opcode, location);
270 public void ifComparisonBranch(Label location, String choice, TypeDesc type) {
271 switch(type.getTypeCode()) {
272 case TypeDesc.BOOLEAN_CODE:
273 case TypeDesc.BYTE_CODE:
274 case TypeDesc.CHAR_CODE:
275 case TypeDesc.SHORT_CODE:
276 case TypeDesc.INT_CODE: {
278 if(choice.equals("!="))
279 opcode = Opcodes.IF_ICMPNE;
280 else if(choice.equals("=="))
281 opcode = Opcodes.IF_ICMPEQ;
282 else if(choice.equals("<"))
283 opcode = Opcodes.IF_ICMPLT;
284 else if(choice.equals(">"))
285 opcode = Opcodes.IF_ICMPGT;
286 else if(choice.equals("<="))
287 opcode = Opcodes.IF_ICMPLE;
288 else if(choice.equals(">="))
289 opcode = Opcodes.IF_ICMPGE;
291 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
292 methodVisitor.visitJumpInsn(opcode, location);
294 case TypeDesc.FLOAT_CODE:
295 methodVisitor.visitInsn(Opcodes.FCMPL);
296 ifZeroComparisonBranch(location, choice);
298 case TypeDesc.DOUBLE_CODE:
299 methodVisitor.visitInsn(Opcodes.DCMPL);
300 ifZeroComparisonBranch(location, choice);
302 case TypeDesc.LONG_CODE:
303 methodVisitor.visitInsn(Opcodes.LCMP);
304 ifZeroComparisonBranch(location, choice);
306 case TypeDesc.OBJECT_CODE: {
308 if(choice.equals("!="))
309 opcode = Opcodes.IF_ACMPNE;
310 else if(choice.equals("=="))
311 opcode = Opcodes.IF_ACMPEQ;
313 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
314 methodVisitor.visitJumpInsn(opcode, location);
316 default: throw new IllegalArgumentException();
320 public void ifNullBranch(Label location, boolean choice) {
321 methodVisitor.visitJumpInsn(choice ? Opcodes.IFNULL : Opcodes.IFNONNULL, location);
324 public void branch(Label location) {
325 methodVisitor.visitJumpInsn(Opcodes.GOTO, location);
328 public Label createLabel() {
332 public void setLocation(Label label) {
333 methodVisitor.visitLabel(label);
336 public void loadLocal(LocalVariable local) {
337 switch(local.type.getTypeCode()) {
338 case TypeDesc.BOOLEAN_CODE:
339 case TypeDesc.BYTE_CODE:
340 case TypeDesc.CHAR_CODE:
341 case TypeDesc.SHORT_CODE:
342 case TypeDesc.INT_CODE:
343 methodVisitor.visitVarInsn(Opcodes.ILOAD, local.index);
345 case TypeDesc.FLOAT_CODE:
346 methodVisitor.visitVarInsn(Opcodes.FLOAD, local.index);
348 case TypeDesc.DOUBLE_CODE:
349 methodVisitor.visitVarInsn(Opcodes.DLOAD, local.index);
351 case TypeDesc.LONG_CODE:
352 methodVisitor.visitVarInsn(Opcodes.LLOAD, local.index);
354 case TypeDesc.OBJECT_CODE:
355 methodVisitor.visitVarInsn(Opcodes.ALOAD, local.index);
357 default: throw new IllegalArgumentException();
361 public void loadNull() {
362 methodVisitor.visitInsn(Opcodes.ACONST_NULL);
365 public void loadFromArray(TypeDesc type) {
366 switch(type.getTypeCode()) {
367 case TypeDesc.BOOLEAN_CODE:
368 methodVisitor.visitInsn(Opcodes.BALOAD);
370 case TypeDesc.BYTE_CODE:
371 methodVisitor.visitInsn(Opcodes.BALOAD);
373 case TypeDesc.CHAR_CODE:
374 methodVisitor.visitInsn(Opcodes.CALOAD);
376 case TypeDesc.SHORT_CODE:
377 methodVisitor.visitInsn(Opcodes.SALOAD);
379 case TypeDesc.INT_CODE:
380 methodVisitor.visitInsn(Opcodes.IALOAD);
382 case TypeDesc.FLOAT_CODE:
383 methodVisitor.visitInsn(Opcodes.FALOAD);
385 case TypeDesc.DOUBLE_CODE:
386 methodVisitor.visitInsn(Opcodes.DALOAD);
388 case TypeDesc.LONG_CODE:
389 methodVisitor.visitInsn(Opcodes.LALOAD);
391 case TypeDesc.OBJECT_CODE:
392 methodVisitor.visitInsn(Opcodes.AALOAD);
394 default: throw new IllegalArgumentException();
398 public void invokeInterface(String className, String methodName,
399 TypeDesc ret, TypeDesc[] params) {
400 checkClassName(className);
401 checkParameters(params);
402 methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, className, methodName,
403 MethodDesc.forArguments(ret, params).getDescriptor(), true);
406 public void invokeInterface(TypeDesc className, String methodName,
407 TypeDesc ret, TypeDesc[] params) {
408 invokeInterface(getClassName(className), methodName, ret, params);
411 public void invokeVirtual(String className, String methodName,
412 TypeDesc ret, TypeDesc[] params) {
413 checkClassName(className);
414 checkParameters(params);
415 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, methodName,
416 MethodDesc.forArguments(ret, params).getDescriptor(), false);
419 public void invokeVirtual(TypeDesc className, String methodName, TypeDesc ret,
421 invokeVirtual(getClassName(className), methodName, ret, params);
424 public void invokeConstructor(String className, TypeDesc[] params) {
425 checkClassName(className);
426 checkParameters(params);
427 methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "<init>",
428 MethodDesc.forArguments(TypeDesc.VOID, params).getDescriptor(), false);
431 public void invokeConstructor(TypeDesc className, TypeDesc[] params) {
432 invokeConstructor(getClassName(className), params);
435 public void invokeSuperConstructor(TypeDesc[] params) {
436 invokeConstructor(classBuilder.getSuperClassName(), params);
439 public void invokeStatic(String className, String methodName, TypeDesc ret,
441 checkClassName(className);
442 checkParameters(params);
443 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, className, methodName,
444 MethodDesc.forArguments(ret, params).getDescriptor(), false);
447 public void invokeStatic(TypeDesc className, String methodName, TypeDesc ret,
449 invokeStatic(getClassName(className), methodName, ret, params);
452 public void newObject(TypeDesc type) {
453 methodVisitor.visitTypeInsn(Opcodes.NEW, getClassName(type));
456 public void newObject(String type) {
457 methodVisitor.visitTypeInsn(Opcodes.NEW, type);
460 public void newObject(TypeDesc type, int dimensions) {
461 methodVisitor.visitMultiANewArrayInsn(type.getDescriptor(), dimensions);
464 public void loadStaticField(String className, String fieldName, TypeDesc type) {
465 checkClassName(className);
466 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, fieldName, type.getDescriptor());
469 public void loadStaticField(TypeDesc className, String fieldName,
471 loadStaticField(getClassName(className), fieldName, type);
474 public void loadField(String className, String fieldName, TypeDesc type) {
475 checkClassName(className);
476 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, className, fieldName, type.getDescriptor());
479 public void storeStaticField(String className, String fieldName,
481 checkClassName(className);
482 methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, className, fieldName, type.getDescriptor());
485 public void storeField(String className, String fieldName, TypeDesc type) {
486 checkClassName(className);
487 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, className, fieldName, type.getDescriptor());
490 public void storeToArray(TypeDesc type) {
491 switch(type.getTypeCode()) {
492 case TypeDesc.BOOLEAN_CODE:
493 methodVisitor.visitInsn(Opcodes.BASTORE);
495 case TypeDesc.BYTE_CODE:
496 methodVisitor.visitInsn(Opcodes.BASTORE);
498 case TypeDesc.CHAR_CODE:
499 methodVisitor.visitInsn(Opcodes.CASTORE);
501 case TypeDesc.SHORT_CODE:
502 methodVisitor.visitInsn(Opcodes.SASTORE);
504 case TypeDesc.INT_CODE:
505 methodVisitor.visitInsn(Opcodes.IASTORE);
507 case TypeDesc.FLOAT_CODE:
508 methodVisitor.visitInsn(Opcodes.FASTORE);
510 case TypeDesc.DOUBLE_CODE:
511 methodVisitor.visitInsn(Opcodes.DASTORE);
513 case TypeDesc.LONG_CODE:
514 methodVisitor.visitInsn(Opcodes.LASTORE);
516 case TypeDesc.OBJECT_CODE:
517 methodVisitor.visitInsn(Opcodes.AASTORE);
519 default: throw new IllegalArgumentException();
523 public void math(int opcode) {
524 methodVisitor.visitInsn(opcode);
527 public void throwObject() {
528 methodVisitor.visitInsn(Opcodes.ATHROW);
531 public void checkCast(TypeDesc type) {
532 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, getClassName(type));
535 public void instanceOf(TypeDesc type) {
536 methodVisitor.visitTypeInsn(Opcodes.INSTANCEOF, getClassName(type));
539 public void arrayLength() {
540 methodVisitor.visitInsn(Opcodes.ARRAYLENGTH);
543 public void loadThis() {
544 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
547 public LocalVariable getParameter(int index) {
548 return parameters[index];
551 public void returnVoid() {
552 methodVisitor.visitInsn(Opcodes.RETURN);
555 public void returnValue(TypeDesc type) {
556 switch(type.getTypeCode()) {
557 case TypeDesc.VOID_CODE:
558 methodVisitor.visitInsn(Opcodes.RETURN);
560 case TypeDesc.BOOLEAN_CODE:
561 case TypeDesc.BYTE_CODE:
562 case TypeDesc.CHAR_CODE:
563 case TypeDesc.SHORT_CODE:
564 case TypeDesc.INT_CODE:
565 methodVisitor.visitInsn(Opcodes.IRETURN);
567 case TypeDesc.FLOAT_CODE:
568 methodVisitor.visitInsn(Opcodes.FRETURN);
570 case TypeDesc.DOUBLE_CODE:
571 methodVisitor.visitInsn(Opcodes.DRETURN);
573 case TypeDesc.LONG_CODE:
574 methodVisitor.visitInsn(Opcodes.LRETURN);
576 case TypeDesc.OBJECT_CODE:
577 methodVisitor.visitInsn(Opcodes.ARETURN);
579 default: throw new IllegalArgumentException();
583 public void finish() {
584 methodVisitor.visitMaxs(0, 0);
585 methodVisitor.visitEnd();
588 public String getClassName() {
589 return classBuilder.className;
592 public static String getClassName(TypeDesc typeDesc) {
593 if(typeDesc.isArray())
594 return typeDesc.getDescriptor();
596 return typeDesc.getFullName().replace('.', '/');
599 private static void checkClassName(String className) {
600 ClassBuilder.checkClassName(className);
603 public static String getClassName(Class<?> clazz) {
604 return clazz.getName().replace('.', '/');
607 public void switch_(int[] values, Label[] labels, Label defaultLabel) {
609 int hi = values[values.length-1];
610 long table_space_cost = 4 + ((long) hi - lo + 1); // words
611 long table_time_cost = 3; // comparisons
612 long lookup_space_cost = 3 + 2 * (long) values.length;
613 long lookup_time_cost = values.length;
614 if(values.length > 0 &&
615 table_space_cost + 3 * table_time_cost <=
616 lookup_space_cost + 3 * lookup_time_cost) {
617 Label[] table = new Label[hi - lo + 1];
618 for(int i=0,j=0;i<table.length;++i) {
620 if(values[j] == id) {
621 table[i] = labels[j];
625 table[i] = defaultLabel;
627 methodVisitor.visitTableSwitchInsn(lo, hi, defaultLabel, table);
630 methodVisitor.visitLookupSwitchInsn(defaultLabel, values, labels);
633 private static void checkParameters(TypeDesc[] params) {
634 for(TypeDesc param : params)
635 if(param.equals(TypeDesc.VOID))
636 throw new IllegalArgumentException();