Java tutorial
/* * jaspex-mls: a Java Software Speculative Parallelization Framework * Copyright (C) 2015 Ivo Anjo <ivo.anjo@ist.utl.pt> * * This file is part of jaspex-mls. * * jaspex-mls is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * jaspex-mls is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with jaspex-mls. If not, see <http://www.gnu.org/licenses/>. */ package jaspex.speculation.newspec; import jaspex.speculation.CommonTypes; import jaspex.speculation.InvokedMethod; import java.util.Arrays; import java.util.List; import org.objectweb.asm.*; import org.objectweb.asm.commons.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import util.*; import asmlib.*; import asmlib.Type; import static jaspex.speculation.CommonTypes.FUTURE; import static jaspex.speculation.CommonTypes.TRANSACTION; import static jaspex.speculation.newspec.DelayGetFutureMethodVisitor.extractReturnType; import static jaspex.speculation.newspec.DelayGetFutureMethodVisitor.isFuture; import static org.objectweb.asm.Opcodes.*; /** Classe que simula uma stack usando variveis locais para guardar os elementos. * * Esta classe permite que o fulfillFuturesStack concretize futuros que esto em posies da * stack do Java normalmente inacessveis com os truques normais de swap. A ideia guardar * valores que esto no topo da stack verdadeira na FakeStack, sendo retirados do topo da * stack verdadeira. * * Como "optimizao", depois dos valores serem removidos dos locals, escrito null nas posies * ocupadas, e quando a stack criada, tenta reutilizar posies no final da lista dos locals * que j estejam a null. **/ class FakeLocalsStack { private int _nextPosition; private final UtilList<Object> _elements = new UtilArrayList<Object>(); protected final UtilList<Object> _stack; private final MethodVisitor mv; public FakeLocalsStack(UtilList<Object> locals, UtilList<Object> stack, MethodVisitor mv) { _stack = stack; int pos = locals.size(); for (; pos > 0; pos--) { if (!locals.get(pos - 1).equals(Opcodes.NULL)) break; } _nextPosition = pos; this.mv = mv; } /** Transferir valor do topo da stack verdadeira para a FakeStack **/ // Guarda valor na _nextPosition, actualiza _nextPosition em getNumSlots() e // faz push no _elements, devolve getNumSlots() public int push() { // Determinar tipo no topo da Stack Object realStackTop = _stack.removeLast(); if (realStackTop.equals(TOP)) realStackTop = _stack.removeLast(); int slotsChanged = 0; // Transferir valor da stack para os locals if (realStackTop.equals(NULL)) { // No guardamos null nos locals mv.visitInsn(POP); slotsChanged = 1; } else { Type t = localToType(realStackTop); mv.visitVarInsn(t.getStoreInsn(), _nextPosition); _nextPosition += t.getNumberSlots(); slotsChanged = t.getNumberSlots(); } // Fazer tracking do estado da FakeStack _elements.push(realStackTop); return slotsChanged; } /** Transferir valor do topo da FakeStack para a stack verdadeira **/ // Faz pop no _elements, passa valor para a stack, reduz _nextPosition em // getNumSlots(), e faz set de getNumSlots() a null public int pop() { Object fakeStackTop = _elements.pop(); _stack.add(fakeStackTop); // Repor tipo na representao da stack original if (fakeStackTop.equals(NULL)) { mv.visitInsn(ACONST_NULL); return 1; } else { Type t = localToType(fakeStackTop); _nextPosition -= t.getNumberSlots(); mv.visitVarInsn(t.getLoadInsn(), _nextPosition); // Limpar FakeStack (locals) for (int i = 0; i < t.getNumberSlots(); i++) { mv.visitInsn(ACONST_NULL); mv.visitVarInsn(ASTORE, _nextPosition + i); } if (t.getNumberSlots() > 1) _stack.add(TOP); return t.getNumberSlots(); } } public int size() { return _elements.size(); } private static Type localToType(Object o) { if (o.equals(INTEGER)) return Type.PRIM_INT; if (o.equals(FLOAT)) return Type.PRIM_FLOAT; if (o.equals(LONG)) return Type.PRIM_LONG; if (o.equals(DOUBLE)) return Type.PRIM_DOUBLE; if (o instanceof String) return Type.fromAsm((String) o); throw new AssertionError(); } public UtilList<Object> stackState() { return new UtilArrayList<Object>(_stack); } } /** Verso de FakeLocalsStack que conta Futures com doubles e longs como ocupando dois slots. * Poderia ter feito a alterao directamente na FakeLocalsStack, mas assim consigo mant-la * independente do que o JaSPEx est a fazer. **/ class FakeFutureLocalsStack extends FakeLocalsStack { FakeFutureLocalsStack(UtilList<Object> locals, UtilList<Object> stack, MethodVisitor mv) { super(locals, stack, mv); } @Override public int push() { Object top = _stack.last(); int places = super.push(); if (isFuture(top)) return extractReturnType(top).getNumberSlots(); return places; } @Override public int pop() { int places = super.pop(); Object top = _stack.last(); if (isFuture(top)) return extractReturnType(top).getNumberSlots(); return places; } } /** MethodVisitor que concretiza os Futures inseridos pelo InsertContinuationSpeculationMethodVisitor * imediatamente antes de eles serem necessrios (e no logo que so colocados na stack vindos do * spawnSpeculation). **/ // FIXME: Ideia DelayGetFutureMethodVisitor2: Atrasar ainda mais coisas como IINC ou I2L (etc), // recolhendo-os para serem executados o mais tarde possvel, apenas quando o valor tiver mesmo // que ser consumido (para executar um mtodo ou guardar num slot), em vez de fazer concretizao // demasiado cedo porque algum bytecode simples foi colocado no sitio "errado". public class DelayGetFutureMethodVisitor extends MethodVisitor { @SuppressWarnings("unused") private static final Logger Log = LoggerFactory.getLogger(DelayGetFutureMethodVisitor.class); private AnalyzerAdapter _analyzerAdapter; private final boolean _active; public DelayGetFutureMethodVisitor(int access, String name, String desc, String signature, String[] exceptions, ClassVisitor cv, InfoClass currentClass) { super(Opcodes.ASM4, new AnalyzerAdapter(currentClass.type().asmName(), access, name, desc, cv.visitMethod(access, name, desc, signature, exceptions))); // FIXME: Especulao em constructores disabled por agora (first draft) if (name.endsWith( "$speculative") /*|| (name.equals("<init>") && CommonTypes.SPECULATIVECTORMARKER.equals(m.argumentTypes().peekLast()))*/) { _active = true; } else { _active = false; } _analyzerAdapter = (AnalyzerAdapter) mv; } private UtilList<Object> currentLocals() { return _analyzerAdapter.locals == null ? null : new UtilArrayList<Object>(_analyzerAdapter.locals); } private UtilList<Object> currentStack() { return _analyzerAdapter.stack == null ? null : new UtilArrayList<Object>(_analyzerAdapter.stack); } /** Array com o nmero de slots da stack que consumido durante a execuo do opcode. * (Apenas para opcodes em que estamos interessados em fazer a concretizao) **/ private static final int[][] opcodeSlots = { /* 0 */ {}, /* 1 */ { INEG, FNEG, I2F, I2B, I2C, I2S, I2L, I2D, F2I, F2L, F2D, IRETURN, FRETURN, ARETURN, ARRAYLENGTH, ATHROW, MONITORENTER, MONITOREXIT, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL }, /* 2 */ { IALOAD, FALOAD, AALOAD, BALOAD, CALOAD, SALOAD, LALOAD, DALOAD, IADD, FADD, ISUB, FSUB, IMUL, FMUL, IDIV, FDIV, IREM, FREM, ISHL, ISHR, IUSHR, IAND, IOR, IXOR, FCMPL, FCMPG, LNEG, DNEG, L2I, L2F, L2D, D2I, D2L, D2F, LRETURN, DRETURN, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE }, /* 3 */ { IASTORE, FASTORE, AASTORE, BASTORE, CASTORE, SASTORE, LSHL, LSHR, LUSHR }, /* 4 */ { LADD, DADD, LSUB, DSUB, LMUL, DMUL, LDIV, DDIV, LREM, DREM, LAND, LOR, LXOR, LCMP, DCMPL, DCMPG, LASTORE, DASTORE } }; private void checkOpcode(int opcode) { for (int slots = 1; slots < opcodeSlots.length; slots++) { for (int j = 0; j < opcodeSlots[slots].length; j++) { if (opcodeSlots[slots][j] == opcode) { fulfillFuturesStack(slots); return; } } } } @Override public void visitInsn(int opcode) { if (!_active) { mv.visitInsn(opcode); return; } // Bytecodes que no operam sobre um futuro que esteja na stack, ou que operando // no precisam que este seja concretizado (ex: POP): // NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, // ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, // POP, POP2*, DUP, DUP_X1, DUP_X2, DUP2*, DUP2_X1*, DUP2_X2*, SWAP, RETURN // * - Futuro no tem que ser concretizado, mas tem que se ter muito cuidado // porque um future que representa um long/double s ocupa 1 slot na stack // Bytecodes que precisam que um futuro seja concretizado: // IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, // FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, // IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, // LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, // ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, L2I, // L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG, // DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, ARRAYLENGTH, ATHROW, // MONITORENTER, MONITOREXIT // O array opcodeSlots contm informao do nmero de slots que precisamos de verificar, // para opcodes que esto na lista de opcodes que necessitam concretizao checkOpcode(opcode); // A semntica destes opcodes muda se existir um Future a substituir um long/double, // e temos que corrigir a instruco nesse caso if ((opcode == DUP2 || opcode == DUP2_X1 || opcode == DUP2_X2 || opcode == POP2) && (currentStack() != null)) { Object stackTop = currentStack().last(); if (isFuture(stackTop) && (extractReturnType(stackTop).getNumberSlots() == 2)) { // Temos de fazer substituio de bytecodes switch (opcode) { case DUP2: opcode = DUP; break; case DUP2_X1: opcode = DUP_X1; break; case DUP2_X2: opcode = DUP_X2; break; case POP2: opcode = POP; break; } } } // Mesmo problema do caso acima, mas o Futuro no est no topo da stack if ((opcode == DUP_X2) && (currentStack() != null)) { Object belowTop = currentStack().get(currentStack().size() - 2); if (isFuture(belowTop) && (extractReturnType(belowTop).getNumberSlots() == 2)) { opcode = DUP_X1; } } mv.visitInsn(opcode); } @Override public void visitIntInsn(int opcode, int operand) { if (!_active) { mv.visitIntInsn(opcode, operand); return; } if (opcode == NEWARRAY) { fulfillFuturesStack(1); } mv.visitIntInsn(opcode, operand); } @Override public void visitTypeInsn(int opcode, String type) { if (!_active) { mv.visitTypeInsn(opcode, type); return; } if (opcode == ANEWARRAY || opcode == CHECKCAST || opcode == INSTANCEOF) { fulfillFuturesStack(1); } mv.visitTypeInsn(opcode, type); } // Usado pelo visitMethodInsn para manter estado entre chamadas, quando encontra um // CommonTypes.MARKER_BEFOREINLINEDSTORE private boolean sawInlinedStoreFuture; @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { if (!_active || currentStack() == null) { mv.visitMethodInsn(opcode, owner, name, desc); return; } InvokedMethod m = new InvokedMethod(opcode, Type.fromAsm(owner), name, desc); // Detectar se isto uma escrita num field (verso inlined) // (Passo 1:) if (owner.equals(CommonTypes.MARKER_BEFOREINLINEDSTORE)) { // Detectar se queremos escrever um Futuro no field if (isFuture(currentStack().last())) { // Queremos escrever um future num objecto, mas a referncia // para esse objecto pode tambm ser um future, e portanto // podemos ter que o concretizar, mas sem concretizar o Future // que vai ser escrito. // Para isso, passamos uma representao falsa da stack ao // fullfillFuturesStack, para que faa a concretizao apenas do // Future que queremos. if (name.equals("normalStoreDummy")) { // Apenas para normal stores, no static UtilList<Object> stack = currentStack(); stack.set(stack.size() - 1, NULL); fulfillFuturesStack(2, stack); } assert (!sawInlinedStoreFuture); sawInlinedStoreFuture = true; return; } } // (Passo 2:) if (owner.equals(TRANSACTION.asmName()) && name.startsWith("store") && sawInlinedStoreFuture) { sawInlinedStoreFuture = false; mv.visitMethodInsn(opcode, owner, name.replace("store", "storeFuture"), "(" + (desc.contains(CommonTypes.STATICFIELDBASE.bytecodeName()) ? FUTURE.bytecodeName() + CommonTypes.STATICFIELDBASE.bytecodeName() : Type.OBJECT.bytecodeName() + FUTURE.bytecodeName()) + "J)V"); return; } // Detectar se isto uma escrita num array (j transactificado) if (owner.equals(TRANSACTION.asmName()) && name.startsWith("arrayStore")) { // Detectar se queremos escrever um Futuro no array if (isFuture(currentStack().last())) { // Semelhante ao caso anterior, temos trs coisas na stack que podem ser // Futuros: o array alvo, a posio, e o valor a ser escrito // Apenas o valor a ser escrito pode ser um futuro; os outros dois tm // que ser concretizados obrigatoriamente (mas ser raro o caso onde // sero futuros) // Para isso, passamos uma representao falsa da stack ao // fullfillFuturesStack, para que faa a concretizao apenas dos // Futures que queremos. UtilList<Object> stack = currentStack(); stack.set(stack.size() - 1, NULL); fulfillFuturesStack(3, stack); // Substituir por chamada a storeFutureWhateverArray mv.visitMethodInsn(opcode, owner, name.replace("Store", "StoreFuture"), "(" + m.argumentTypes().first().bytecodeName() + "I" + FUTURE.bytecodeName() + ")V"); return; } } // Obter nmero de stack slots que vo ser consumidos por esta operao int stackSlots = m.isStatic() ? 0 : 1; for (Type t : m.argumentTypes()) stackSlots += t.getNumberSlots(); complexFullfillFuturesStack(stackSlots); mv.visitMethodInsn(opcode, owner, name, desc); } @Override public void visitJumpInsn(int opcode, Label label) { if (!_active) { mv.visitJumpInsn(opcode, label); return; } checkOpcode(opcode); mv.visitJumpInsn(opcode, label); } @Override public void visitIincInsn(int var, int increment) { if (!_active) { mv.visitIincInsn(var, increment); return; } // Caso especial: o IINC incrementa directamente uma var local sem ter que fazer // todo o processo de a ler para a stack, incrementar, e fazer store de novo. // Quando a varivel um future em vez do valor que se estava espera, injectamos // cdigo equivalente ao IINC que concretize tambm o futuro. if ((currentLocals() != null) && isFuture(currentLocals().get(var))) { mv.visitLdcInsn(increment); mv.visitVarInsn(ALOAD, var); // Concretizar future, verso reduzida do fullfillFuturesStack String slotType = (String) currentLocals().get(var); // Isto um tipo nativo de certeza, pode no ser um int (pode ser qualquer coisa // que nos locals se possa guardar como um int: boolean, char, byte ou short) Type returnType = extractReturnType(slotType); mv.visitMethodInsn(INVOKEINTERFACE, slotType /* Future, mas preciso manter o extra info */, "get", "()" + Type.OBJECT.bytecodeName()); mv.visitTypeInsn(CHECKCAST, returnType.toObject().asmName()); mv.visitMethodInsn(INVOKEVIRTUAL, returnType.toObject().asmName(), returnType.primitiveTypeName() + "Value", "()" + returnType.bytecodeName()); mv.visitInsn(IADD); mv.visitVarInsn(ISTORE, var); } else { mv.visitIincInsn(var, increment); } } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { if (!_active) { mv.visitTableSwitchInsn(min, max, dflt, labels); return; } fulfillFuturesStack(1); mv.visitTableSwitchInsn(min, max, dflt, labels); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { if (!_active) { mv.visitLookupSwitchInsn(dflt, keys, labels); return; } fulfillFuturesStack(1); mv.visitLookupSwitchInsn(dflt, keys, labels); } @Override public void visitMultiANewArrayInsn(String desc, int dims) { if (!_active) { mv.visitMultiANewArrayInsn(desc, dims); return; } // Segundo a documentao da JVM, mesmo que o desc indique um array maior, apenas // so consumidos "dims" posies na stack complexFullfillFuturesStack(dims); mv.visitMultiANewArrayInsn(desc, dims); } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { if (!_active) { mv.visitFieldInsn(opcode, owner, name, desc); return; } if (opcode == GETFIELD) { // Acesso transaccional inlined a field fulfillFuturesStack(1); } mv.visitFieldInsn(opcode, owner, name, desc); } /** Opcodes usados para loads em variveis locais **/ static final List<Integer> localLoadOpcodes = Arrays.asList(ILOAD, LLOAD, FLOAD, DLOAD, ALOAD); /** Opcodes usados para stores em variveis locais **/ static final List<Integer> localStoreOpcodes = Arrays.asList(ISTORE, LSTORE, FSTORE, DSTORE, ASTORE); /** Opcodes usados para return **/ static final List<Integer> returnOpcodes = Arrays.asList(IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN); @Override public void visitVarInsn(int opcode, int var) { if (!_active) { mv.visitVarInsn(opcode, var); return; } if (opcode == RET) throw new Error("FIXME"); // Para podemos guardar Futures em variveis locais, em vez de sermos obrigados a // concretiz-los antes de os guardar, trocamos *STORES/*LOADS de tipos que foram // "substituidos" por Futures por ASTORE/ALOAD. if (localLoadOpcodes.contains(opcode) && (currentLocals() != null) && isFuture(currentLocals().get(var))) { //Log.debug("Replaced with ALOAD"); mv.visitVarInsn(ALOAD, var); } else if (localStoreOpcodes.contains(opcode) && (currentStack() != null) && isFuture(currentStack().last())) { //Log.debug("Replaced with ASTORE " + var); mv.visitVarInsn(ASTORE, var); } else { //Log.debug("Not changed"); mv.visitVarInsn(opcode, var); } } @Override // As alteraes para especulao podem alterar o maxStack (converso de IINC, por exemplo) public void visitMaxs(int maxStack, int maxLocals) { mv.visitMaxs(maxStack + 2, maxLocals); } /** Mtodo que verifica se o nmero de slots especificado da stack (mx. 4) contm um ou mais * Futures: se sim, emite cdigo para os concretizar. * * Nota: O nmero de slots o final, depois de concretizao. Ou seja, se estiverem dois futures * com tipo Double na stack, na realidade esto a ocupar 2 slots, mas slots = 4, ou seja, * o seu tamanho-alvo, depois de concretizao. **/ private void fulfillFuturesStack(int slots) { UtilList<Object> stack = currentStack(); if (stack == null) return; fulfillFuturesStack(slots, stack); } private void fulfillFuturesStack(int slots, UtilList<Object> frameStack) { if (slots > 4) throw new AssertionError(); UtilList<Object> stack = frameStack.reversed(); //Log.debug("Checkstack: " + stackToString(stack.reversed())); for (int pos = 0; pos < slots; pos++) { Object slot = stack.get(pos); if (isFuture(slot)) { Type returnType = extractReturnType(slot); // Colocar future a concretizar no topo da stack switch (pos) { case 0: break; // Nada a fazer case 1: mv.visitInsn(SWAP); break; case 2: Util.swap(mv, 2, FUTURE.getNumberSlots()); break; case 3: if (stack.get(1).equals(TOP)) { // Caso muito especial. Estado da stack // [Futuro, Double/Long, Algo size 1> // logo fazer o swap normal iria partir o double/long, // o que no funciona. Neste caso fazemos mais um // swap para transformar no EQUIVALENTE ao caso // [Futuro, Also size 1, Algo size 1, Algo size 1> // e depois temos que desfazer esta transformao // no final Util.swap(mv, 1, 2); } Util.swap(mv, 2, 2); mv.visitInsn(SWAP); break; default: throw new AssertionError(); } // Concretizar future mv.visitMethodInsn(INVOKEINTERFACE, (String) slot /* Future, mas preciso manter o extra info */, "get", "()" + Type.OBJECT.bytecodeName()); if (returnType.isPrimitive()) { mv.visitTypeInsn(CHECKCAST, returnType.toObject().asmName()); mv.visitMethodInsn(INVOKEVIRTUAL, returnType.toObject().asmName(), returnType.primitiveTypeName() + "Value", "()" + returnType.bytecodeName()); } else { mv.visitTypeInsn(CHECKCAST, returnType.asmName()); } // Repor stack como deve ser switch (pos) { case 0: break; // Nada a fazer case 1: // Topo da stack pode agora conter objecto com 1 ou 2 slots // Portanto temos que fazer swap a contar com isso Util.swap(mv, returnType.getNumberSlots(), 1); break; case 2: Util.swap(mv, returnType.getNumberSlots(), 2); break; case 3: mv.visitInsn(SWAP); Util.swap(mv, 2, 2); if (stack.get(1).equals(TOP)) { // Voltar a desfazer a transformao do caso especial // Ver comentrio no case 3 do switch anterior Util.swap(mv, 2, 1); } break; default: throw new AssertionError(); } if (returnType.getNumberSlots() == 2) { // Actualizar estado/tamanho da stack stack.set(pos, TOP); stack.add(pos + 1, LONG); } } } } /** Mtodo que complementa o fullfillFuturesStack, mas para um nmero arbitrrio de slots, * usando a FakeLocalsStack. **/ private void complexFullfillFuturesStack(int slots) { int pos = 0; int deepestFuture = 0; // Encontrar o futuro mais fundo na stack (calculando a sua posio na stack original, // como se no existissem futuros) for (Object slot : currentStack().reversed()) { assert (pos <= slots); if (pos == slots) break; pos++; if (isFuture(slot)) { // Compensar caso seja um futuro com um double/long pos += extractReturnType(slot).getNumberSlots() - 1; deepestFuture = pos; } } FakeLocalsStack fakeStack = new FakeFutureLocalsStack(currentLocals(), currentStack(), mv); // Transferir valores para a FakeLocalsStack while (deepestFuture > 4) { deepestFuture -= fakeStack.push(); } // Futuro j pode ser concretizado com o fulfill normal fulfillFuturesStack(deepestFuture, fakeStack.stackState()); // Retirar valores da FakeLocalsStack, e concretizar os restantes while (fakeStack.size() > 0) { fulfillFuturesStack(fakeStack.pop(), fakeStack.stackState()); } } @SuppressWarnings("unused") private static String stackToString(List<Object> stack) { if (stack == null) return "null"; StringBuffer b = new StringBuffer(); b.append("["); for (Object elem : stack) { String desc = ""; if (elem == TOP) { desc = "TOP"; } else if (elem == INTEGER) { desc = "int"; } else if (elem == FLOAT) { desc = "float"; } else if (elem == LONG) { desc = "long"; } else if (elem == DOUBLE) { desc = "double"; } else if (elem == NULL) { desc = "null"; } else if (elem == UNINITIALIZED_THIS) { desc = "UNINIT_THIS"; } else if (elem instanceof String) { desc = Type.fromAsm((String) elem).commonName(); } else { desc = elem.toString(); } b.append(desc + ", "); } if (b.length() > 1) { b.replace(b.length() - 2, b.length(), ""); } b.append(">"); return b.toString(); } public static boolean isFuture(Object type) { if (type.toString().startsWith("jaspex/HACK/")) { // Quando as frames so criadas pelo FixFutureMultipleControlFlows, em alguns // casos so incluidos os marcadores jaspex/HACK/, mas estes *s* ocorrem quando // o valor no suposto ser usado (por exemplo um MUV que fica nos locals quando // chegamos a um RETURN). Se estamos a tentar fazer isFuture de algum destes hacks, // provavelmente algo est errado. throw new AssertionError("Unexpected type in frame"); } return type.toString().startsWith(FUTURE.asmName() + "$"); } /** Obtem o tipo de retorno a partir de um future em AsmName **/ public static Type extractReturnType(Object future) { return FutureMetadata.fromAsm(future.toString()).returnType(); } }