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 LocalVariable getThis(TypeDesc type) {
30 return new LocalVariable(0, type);
33 public void loadConstant(boolean value) {
35 methodVisitor.visitInsn(Opcodes.ICONST_1);
37 methodVisitor.visitInsn(Opcodes.ICONST_0);
40 public void loadConstant(int value) {
41 if (value >= -1 && value <= 5) {
42 methodVisitor.visitInsn(Opcodes.ICONST_0 + value);
43 } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
44 methodVisitor.visitIntInsn(Opcodes.BIPUSH, value);
45 } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
46 methodVisitor.visitIntInsn(Opcodes.SIPUSH, value);
48 methodVisitor.visitLdcInsn(value);
52 public void loadConstant(long value) {
54 methodVisitor.visitInsn(Opcodes.LCONST_0);
56 methodVisitor.visitInsn(Opcodes.LCONST_1);
58 methodVisitor.visitLdcInsn(value);
61 public void loadConstant(float value) {
63 methodVisitor.visitInsn(Opcodes.FCONST_0);
64 else if(value == 1.0f)
65 methodVisitor.visitInsn(Opcodes.FCONST_1);
67 methodVisitor.visitLdcInsn(value);
70 public void loadConstant(double value) {
72 methodVisitor.visitInsn(Opcodes.DCONST_0);
74 methodVisitor.visitInsn(Opcodes.DCONST_1);
76 methodVisitor.visitLdcInsn(value);
79 public void loadConstant(String value) {
80 methodVisitor.visitLdcInsn(value);
83 public void loadConstant(TypeDesc value) {
84 if(value.isPrimitive())
85 loadStaticField(getClassName(value.toObjectType()), "TYPE", Constants.CLASS);
87 methodVisitor.visitLdcInsn(Type.getType(value.getDescriptor()));
91 methodVisitor.visitInsn(Opcodes.DUP);
95 methodVisitor.visitInsn(Opcodes.DUP2);
99 methodVisitor.visitInsn(Opcodes.DUP_X1);
103 methodVisitor.visitInsn(Opcodes.SWAP);
107 methodVisitor.visitInsn(Opcodes.POP);
111 methodVisitor.visitInsn(Opcodes.POP2);
114 public void convert(TypeDesc fromType, TypeDesc toType) {
115 switch(fromType.getTypeCode() + 16*toType.getTypeCode()) {
116 case TypeDesc.OBJECT_CODE + 16*TypeDesc.BOOLEAN_CODE:
117 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false);
119 case TypeDesc.OBJECT_CODE + 16*TypeDesc.INT_CODE:
120 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false);
122 case TypeDesc.OBJECT_CODE + 16*TypeDesc.FLOAT_CODE:
123 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false);
125 case TypeDesc.OBJECT_CODE + 16*TypeDesc.DOUBLE_CODE:
126 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false);
128 case TypeDesc.OBJECT_CODE + 16*TypeDesc.CHAR_CODE:
129 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false);
131 case TypeDesc.OBJECT_CODE + 16*TypeDesc.LONG_CODE:
132 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false);
134 case TypeDesc.OBJECT_CODE + 16*TypeDesc.SHORT_CODE:
135 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false);
137 case TypeDesc.OBJECT_CODE + 16*TypeDesc.BYTE_CODE:
138 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false);
140 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.FLOAT_CODE:
141 methodVisitor.visitInsn(Opcodes.D2F);
143 case TypeDesc.FLOAT_CODE + 16*TypeDesc.DOUBLE_CODE:
144 methodVisitor.visitInsn(Opcodes.F2D);
146 case TypeDesc.INT_CODE + 16*TypeDesc.DOUBLE_CODE:
147 methodVisitor.visitInsn(Opcodes.I2D);
149 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.INT_CODE:
150 methodVisitor.visitInsn(Opcodes.D2I);
152 case TypeDesc.INT_CODE + 16*TypeDesc.FLOAT_CODE:
153 methodVisitor.visitInsn(Opcodes.I2F);
155 case TypeDesc.FLOAT_CODE + 16*TypeDesc.INT_CODE:
156 methodVisitor.visitInsn(Opcodes.F2I);
158 case TypeDesc.LONG_CODE + 16*TypeDesc.FLOAT_CODE:
159 methodVisitor.visitInsn(Opcodes.L2F);
161 case TypeDesc.FLOAT_CODE + 16*TypeDesc.LONG_CODE:
162 methodVisitor.visitInsn(Opcodes.F2L);
164 case TypeDesc.LONG_CODE + 16*TypeDesc.DOUBLE_CODE:
165 methodVisitor.visitInsn(Opcodes.L2D);
167 case TypeDesc.DOUBLE_CODE + 16*TypeDesc.LONG_CODE:
168 methodVisitor.visitInsn(Opcodes.D2L);
170 case TypeDesc.INT_CODE + 16*TypeDesc.LONG_CODE:
171 methodVisitor.visitInsn(Opcodes.I2L);
173 case TypeDesc.LONG_CODE + 16*TypeDesc.INT_CODE:
174 methodVisitor.visitInsn(Opcodes.L2I);
176 case TypeDesc.INT_CODE + 16*TypeDesc.SHORT_CODE:
177 methodVisitor.visitInsn(Opcodes.I2S);
179 case TypeDesc.SHORT_CODE + 16*TypeDesc.INT_CODE:
180 case TypeDesc.BOOLEAN_CODE + 16*TypeDesc.INT_CODE:
183 case TypeDesc.INT_CODE + 16*TypeDesc.BYTE_CODE:
184 methodVisitor.visitInsn(Opcodes.I2B);
186 case TypeDesc.BYTE_CODE + 16*TypeDesc.INT_CODE:
189 case TypeDesc.INT_CODE + 16*TypeDesc.CHAR_CODE:
190 methodVisitor.visitInsn(Opcodes.I2C);
192 case TypeDesc.CHAR_CODE + 16*TypeDesc.INT_CODE:
196 System.out.println("convert: " + fromType + " -> " + toType);
200 public void convert(TypeDesc fromType, TypeDesc toType,
201 int convertFpNormal) {
202 convert(fromType, toType);
205 public LocalVariable createLocalVariable(String name, TypeDesc type) {
206 int index = localVariableCount;
207 localVariableCount += type == TypeDesc.DOUBLE || type == TypeDesc.LONG ? 2 : 1;
208 return new LocalVariable(index, type);
211 public void storeLocal(LocalVariable local) {
212 switch(local.type.getTypeCode()) {
213 case TypeDesc.BOOLEAN_CODE:
214 case TypeDesc.BYTE_CODE:
215 case TypeDesc.CHAR_CODE:
216 case TypeDesc.SHORT_CODE:
217 case TypeDesc.INT_CODE:
218 methodVisitor.visitVarInsn(Opcodes.ISTORE, local.index);
220 case TypeDesc.FLOAT_CODE:
221 methodVisitor.visitVarInsn(Opcodes.FSTORE, local.index);
223 case TypeDesc.DOUBLE_CODE:
224 methodVisitor.visitVarInsn(Opcodes.DSTORE, local.index);
226 case TypeDesc.LONG_CODE:
227 methodVisitor.visitVarInsn(Opcodes.LSTORE, local.index);
229 case TypeDesc.OBJECT_CODE:
230 methodVisitor.visitVarInsn(Opcodes.ASTORE, local.index);
232 default: throw new IllegalArgumentException();
236 public void ifZeroComparisonBranch(Label location, String choice) {
238 if(choice.equals("!="))
239 opcode = Opcodes.IFNE;
240 else if(choice.equals("=="))
241 opcode = Opcodes.IFEQ;
242 else if(choice.equals("<"))
243 opcode = Opcodes.IFLT;
244 else if(choice.equals(">"))
245 opcode = Opcodes.IFGT;
246 else if(choice.equals("<="))
247 opcode = Opcodes.IFLE;
248 else if(choice.equals(">="))
249 opcode = Opcodes.IFGE;
251 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
252 methodVisitor.visitJumpInsn(opcode, location);
255 public void ifComparisonBranch(Label location, String choice, TypeDesc type) {
256 switch(type.getTypeCode()) {
257 case TypeDesc.BOOLEAN_CODE:
258 case TypeDesc.BYTE_CODE:
259 case TypeDesc.CHAR_CODE:
260 case TypeDesc.SHORT_CODE:
261 case TypeDesc.INT_CODE: {
263 if(choice.equals("!="))
264 opcode = Opcodes.IF_ICMPNE;
265 else if(choice.equals("=="))
266 opcode = Opcodes.IF_ICMPEQ;
267 else if(choice.equals("<"))
268 opcode = Opcodes.IF_ICMPLT;
269 else if(choice.equals(">"))
270 opcode = Opcodes.IF_ICMPGT;
271 else if(choice.equals("<="))
272 opcode = Opcodes.IF_ICMPLE;
273 else if(choice.equals(">="))
274 opcode = Opcodes.IF_ICMPGE;
276 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
277 methodVisitor.visitJumpInsn(opcode, location);
279 case TypeDesc.FLOAT_CODE:
280 methodVisitor.visitInsn(Opcodes.FCMPL);
281 ifZeroComparisonBranch(location, choice);
283 case TypeDesc.DOUBLE_CODE:
284 methodVisitor.visitInsn(Opcodes.DCMPL);
285 ifZeroComparisonBranch(location, choice);
287 case TypeDesc.LONG_CODE:
288 methodVisitor.visitInsn(Opcodes.LCMP);
289 ifZeroComparisonBranch(location, choice);
291 case TypeDesc.OBJECT_CODE: {
293 if(choice.equals("!="))
294 opcode = Opcodes.IF_ACMPNE;
295 else if(choice.equals("=="))
296 opcode = Opcodes.IF_ACMPEQ;
298 throw new IllegalArgumentException("Invalid choice \"" + choice + "\".");
299 methodVisitor.visitJumpInsn(opcode, location);
301 default: throw new IllegalArgumentException();
305 public void ifNullBranch(Label location, boolean choice) {
306 methodVisitor.visitJumpInsn(choice ? Opcodes.IFNULL : Opcodes.IFNONNULL, location);
309 public void branch(Label location) {
310 methodVisitor.visitJumpInsn(Opcodes.GOTO, location);
313 public Label createLabel() {
317 public void setLocation(Label label) {
318 methodVisitor.visitLabel(label);
321 public void loadLocal(LocalVariable local) {
322 switch(local.type.getTypeCode()) {
323 case TypeDesc.BOOLEAN_CODE:
324 case TypeDesc.BYTE_CODE:
325 case TypeDesc.CHAR_CODE:
326 case TypeDesc.SHORT_CODE:
327 case TypeDesc.INT_CODE:
328 methodVisitor.visitVarInsn(Opcodes.ILOAD, local.index);
330 case TypeDesc.FLOAT_CODE:
331 methodVisitor.visitVarInsn(Opcodes.FLOAD, local.index);
333 case TypeDesc.DOUBLE_CODE:
334 methodVisitor.visitVarInsn(Opcodes.DLOAD, local.index);
336 case TypeDesc.LONG_CODE:
337 methodVisitor.visitVarInsn(Opcodes.LLOAD, local.index);
339 case TypeDesc.OBJECT_CODE:
340 methodVisitor.visitVarInsn(Opcodes.ALOAD, local.index);
342 default: throw new IllegalArgumentException();
346 public void loadNull() {
347 methodVisitor.visitInsn(Opcodes.ACONST_NULL);
350 public void loadFromArray(TypeDesc type) {
351 switch(type.getTypeCode()) {
352 case TypeDesc.BOOLEAN_CODE:
353 methodVisitor.visitInsn(Opcodes.BALOAD);
355 case TypeDesc.BYTE_CODE:
356 methodVisitor.visitInsn(Opcodes.BALOAD);
358 case TypeDesc.CHAR_CODE:
359 methodVisitor.visitInsn(Opcodes.CALOAD);
361 case TypeDesc.SHORT_CODE:
362 methodVisitor.visitInsn(Opcodes.SALOAD);
364 case TypeDesc.INT_CODE:
365 methodVisitor.visitInsn(Opcodes.IALOAD);
367 case TypeDesc.FLOAT_CODE:
368 methodVisitor.visitInsn(Opcodes.FALOAD);
370 case TypeDesc.DOUBLE_CODE:
371 methodVisitor.visitInsn(Opcodes.DALOAD);
373 case TypeDesc.LONG_CODE:
374 methodVisitor.visitInsn(Opcodes.LALOAD);
376 case TypeDesc.OBJECT_CODE:
377 methodVisitor.visitInsn(Opcodes.AALOAD);
379 default: throw new IllegalArgumentException();
383 public void invokeInterface(String className, String methodName,
384 TypeDesc ret, TypeDesc[] params) {
385 checkClassName(className);
386 checkParameters(params);
387 methodVisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, className, methodName,
388 MethodDesc.forArguments(ret, params).getDescriptor(), true);
391 public void invokeInterface(TypeDesc className, String methodName,
392 TypeDesc ret, TypeDesc[] params) {
393 invokeInterface(getClassName(className), methodName, ret, params);
396 public void invokeVirtual(String className, String methodName,
397 TypeDesc ret, TypeDesc[] params) {
398 checkClassName(className);
399 checkParameters(params);
400 methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, methodName,
401 MethodDesc.forArguments(ret, params).getDescriptor(), false);
404 public void invokeVirtual(TypeDesc className, String methodName, TypeDesc ret,
406 invokeVirtual(getClassName(className), methodName, ret, params);
409 public void invokeConstructor(String className, TypeDesc[] params) {
410 checkClassName(className);
411 checkParameters(params);
412 methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, className, "<init>",
413 MethodDesc.forArguments(TypeDesc.VOID, params).getDescriptor(), false);
416 public void invokeConstructor(TypeDesc className, TypeDesc[] params) {
417 invokeConstructor(getClassName(className), params);
420 public void invokeSuperConstructor(TypeDesc[] params) {
421 invokeConstructor(classBuilder.getSuperClassName(), params);
424 public void invokeStatic(String className, String methodName, TypeDesc ret,
426 checkClassName(className);
427 checkParameters(params);
428 methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, className, methodName,
429 MethodDesc.forArguments(ret, params).getDescriptor(), false);
432 public void invokeStatic(TypeDesc className, String methodName, TypeDesc ret,
434 invokeStatic(getClassName(className), methodName, ret, params);
437 public void newObject(TypeDesc type) {
438 methodVisitor.visitTypeInsn(Opcodes.NEW, getClassName(type));
441 public void newObject(String type) {
442 methodVisitor.visitTypeInsn(Opcodes.NEW, type);
445 public void newObject(TypeDesc type, int dimensions) {
446 methodVisitor.visitMultiANewArrayInsn(type.getDescriptor(), dimensions);
449 public void loadStaticField(String className, String fieldName, TypeDesc type) {
450 checkClassName(className);
451 methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, className, fieldName, type.getDescriptor());
454 public void loadStaticField(TypeDesc className, String fieldName,
456 loadStaticField(getClassName(className), fieldName, type);
459 public void loadField(String className, String fieldName, TypeDesc type) {
460 checkClassName(className);
461 methodVisitor.visitFieldInsn(Opcodes.GETFIELD, className, fieldName, type.getDescriptor());
464 public void storeStaticField(String className, String fieldName,
466 checkClassName(className);
467 methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC, className, fieldName, type.getDescriptor());
470 public void storeField(String className, String fieldName, TypeDesc type) {
471 checkClassName(className);
472 methodVisitor.visitFieldInsn(Opcodes.PUTFIELD, className, fieldName, type.getDescriptor());
475 public void storeToArray(TypeDesc type) {
476 switch(type.getTypeCode()) {
477 case TypeDesc.BOOLEAN_CODE:
478 methodVisitor.visitInsn(Opcodes.BASTORE);
480 case TypeDesc.BYTE_CODE:
481 methodVisitor.visitInsn(Opcodes.BASTORE);
483 case TypeDesc.CHAR_CODE:
484 methodVisitor.visitInsn(Opcodes.CASTORE);
486 case TypeDesc.SHORT_CODE:
487 methodVisitor.visitInsn(Opcodes.SASTORE);
489 case TypeDesc.INT_CODE:
490 methodVisitor.visitInsn(Opcodes.IASTORE);
492 case TypeDesc.FLOAT_CODE:
493 methodVisitor.visitInsn(Opcodes.FASTORE);
495 case TypeDesc.DOUBLE_CODE:
496 methodVisitor.visitInsn(Opcodes.DASTORE);
498 case TypeDesc.LONG_CODE:
499 methodVisitor.visitInsn(Opcodes.LASTORE);
501 case TypeDesc.OBJECT_CODE:
502 methodVisitor.visitInsn(Opcodes.AASTORE);
504 default: throw new IllegalArgumentException();
508 public void math(int opcode) {
509 methodVisitor.visitInsn(opcode);
512 public void throwObject() {
513 methodVisitor.visitInsn(Opcodes.ATHROW);
516 public void checkCast(TypeDesc type) {
517 methodVisitor.visitTypeInsn(Opcodes.CHECKCAST, getClassName(type));
520 public void instanceOf(TypeDesc type) {
521 methodVisitor.visitTypeInsn(Opcodes.INSTANCEOF, getClassName(type));
524 public void arrayLength() {
525 methodVisitor.visitInsn(Opcodes.ARRAYLENGTH);
528 public void loadThis() {
529 methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
532 public LocalVariable getParameter(int index) {
533 return parameters[index];
536 public void returnVoid() {
537 methodVisitor.visitInsn(Opcodes.RETURN);
540 public void returnValue(TypeDesc type) {
541 switch(type.getTypeCode()) {
542 case TypeDesc.VOID_CODE:
543 methodVisitor.visitInsn(Opcodes.RETURN);
545 case TypeDesc.BOOLEAN_CODE:
546 case TypeDesc.BYTE_CODE:
547 case TypeDesc.CHAR_CODE:
548 case TypeDesc.SHORT_CODE:
549 case TypeDesc.INT_CODE:
550 methodVisitor.visitInsn(Opcodes.IRETURN);
552 case TypeDesc.FLOAT_CODE:
553 methodVisitor.visitInsn(Opcodes.FRETURN);
555 case TypeDesc.DOUBLE_CODE:
556 methodVisitor.visitInsn(Opcodes.DRETURN);
558 case TypeDesc.LONG_CODE:
559 methodVisitor.visitInsn(Opcodes.LRETURN);
561 case TypeDesc.OBJECT_CODE:
562 methodVisitor.visitInsn(Opcodes.ARETURN);
564 default: throw new IllegalArgumentException();
568 public void finish() {
569 methodVisitor.visitMaxs(0, 0);
570 methodVisitor.visitEnd();
573 public String getClassName() {
574 return classBuilder.className;
577 public static String getClassName(TypeDesc typeDesc) {
578 if(typeDesc.isArray())
579 return typeDesc.getDescriptor();
581 return typeDesc.getFullName().replace('.', '/');
584 private static void checkClassName(String className) {
585 ClassBuilder.checkClassName(className);
588 public static String getClassName(Class<?> clazz) {
589 return clazz.getName().replace('.', '/');
592 public void switch_(int[] values, Label[] labels, Label defaultLabel) {
594 int hi = values[values.length-1];
595 long table_space_cost = 4 + ((long) hi - lo + 1); // words
596 long table_time_cost = 3; // comparisons
597 long lookup_space_cost = 3 + 2 * (long) values.length;
598 long lookup_time_cost = values.length;
599 if(values.length > 0 &&
600 table_space_cost + 3 * table_time_cost <=
601 lookup_space_cost + 3 * lookup_time_cost) {
602 Label[] table = new Label[hi - lo + 1];
603 for(int i=0,j=0;i<table.length;++i) {
605 if(values[j] == id) {
606 table[i] = labels[j];
610 table[i] = defaultLabel;
612 methodVisitor.visitTableSwitchInsn(lo, hi, defaultLabel, table);
615 methodVisitor.visitLookupSwitchInsn(defaultLabel, values, labels);
618 private static void checkParameters(TypeDesc[] params) {
619 for(TypeDesc param : params)
620 if(param.equals(TypeDesc.VOID))
621 throw new IllegalArgumentException();