Example usage for org.objectweb.asm.tree LabelNode clone

List of usage examples for org.objectweb.asm.tree LabelNode clone

Introduction

In this page you can find the example usage for org.objectweb.asm.tree LabelNode clone.

Prototype

@Override
    public AbstractInsnNode clone(final Map<LabelNode, LabelNode> clonedLabels) 

Source Link

Usage

From source file:jaspex.speculation.newspec.FlowFrame.java

License:Open Source License

/** Mtodo que implementa a deteco e correco do problema dos mltiplos fluxos de controlo.
  * Retorna true se alterou o mtodo, e false cc.
  **//*  www  . ja  v a 2 s . c  o  m*/
private static boolean computeControlFlowGraph(String owner, String superOwner, MethodNode mn,
        LabelMap labelMap, boolean ranBefore) {
    //Log.debug("Visiting: {}.{}", owner, mn.name);

    FlowAnalyzer analyzer = new FlowAnalyzer(new FutureVerifier(owner, superOwner));

    try {
        analyzer.analyze(owner, mn);
        //if (ranBefore) printCode(mn, Arrays.asList(analyzer.getFrames()), null, null);
        FrameInjector.injectFrames(mn, analyzer.getFrames());
        return false;
    } catch (AnalyzerException e) {
        /*e.printStackTrace();*/ }

    // Se chegmos aqui, o Analyzer detectou problemas, e vamos ter que fazer correces no mtodo

    // Detectar o primeiro LabelNode onde aparece um MergedUninitializedValue
    LabelNode problemLabelNode = null;
    FlowFrame problemFrame = null;
    int problemPosition = -1;
    boolean problemInStack = false;

    List<FlowFrame> frames = listGenericCast(Arrays.asList(analyzer.getFrames()));

    outer: for (int i = 0; i < frames.size(); i++) {
        FlowFrame f = frames.get(i);
        if (f == null)
            continue;

        int localsStartPos = 0;
        int stackStartPos = 0;
        // Identificar se alguma entrada nos locals/stack da frame deve ser escolhida como
        // problemtica
        // Isto  um loop porque algumas das entradas encontradas podem ser backwards edges,
        // e no estamos interessadas nessas
        while (true) {
            for (int j = localsStartPos; j < f.getLocals(); j++) {
                if (f.getLocal(j) instanceof MergedUninitializedValue) {
                    problemPosition = j;
                    problemInStack = false;
                    localsStartPos = j + 1;
                }
            }
            for (int j = stackStartPos; j < f.getStackSize() && (problemPosition < 0); j++) {
                if (f.getStack(j) instanceof MergedUninitializedValue) {
                    problemPosition = j;
                    problemInStack = true;
                    stackStartPos = j + 1;
                }
            }

            // J processamos completamente a frame e no encontramos nada, ou s
            // encontramos backwards edges, passar  prxima
            if (problemPosition < 0)
                continue outer;

            if ((f.predecessors().size() > 0)
                    && (getPredecessorWithFuture(f, problemInStack, problemPosition) == null)) {
                // Isto pode acontecer quando instruco que encontrmos  o target de
                // um "backwards edge". Nesse caso, no  esta a instruco que queremos
                // processar, mas queremos uma que tenha como predecessor um Future,
                // no um MergedUninitializedValue
                /*Log.debug("Found result of backwards edge at " +
                   (problemInStack ? "stack" : "locals") + " position " + i +
                   ", continuing");*/
                problemPosition = -1;
                continue;
            }

            if (problemInStack && !(mn.instructions.get(i) instanceof LabelNode)
                    && (mn.instructions.get(i - 1) instanceof VarInsnNode)) {
                // Caso especial: Uma instruco de load colocou um
                // MergedUninitializedValue na stack, e como no estava l na instruco
                // anterior o getPredecessorWithFuture no detecta este caso, mas no
                // estamos interessados nesta frame, estamos interessadas na que originou
                // o MUV que estava nos locals
                problemPosition = -1;
                continue;
            }

            // Frame e posio escolhidas so consideradas problemticas
            break;
        }

        AbstractInsnNode insn = mn.instructions.get(i);
        // First node with problematic frame should be a LabelNode
        if (!(insn instanceof LabelNode))
            throw new AssertionError();
        problemLabelNode = (LabelNode) insn;
        problemFrame = f;

        printCode(mn, frames, problemFrame.predecessors(), f);
        Log.trace("First problematic frame is " + f + "\n\t\tPredecessors: " + f.predecessors());
        break;
    }

    if (problemLabelNode == null) {
        Log.warn("Errors found during analysis, bytecode possibly invalid, bailing out");
        throw new AssertionError(); // Causar revert de todas as alteraes no mtodo
        //return false;
    }

    // Duplicar cdigo problemtico, para depois o alterar com o DelayGetFutureMethodVisitor
    InsnList il = new InsnList();

    // Label que marca o inicio do cdigo a copiar
    LabelNode copiedBlockStartLabel = new LabelNode();

    // Criar mapa para passar ao AbstractInsnNode (ver javadoc ASM)
    //LabelMap labelMap = new LabelMap();
    labelMap.put(problemLabelNode, copiedBlockStartLabel);

    // Adiciona copiedBlockStartLabel  nova il
    il.add(problemLabelNode.clone(labelMap));

    // Usado para manter a ltima (inclusiv) instruco do bloco copiado
    AbstractInsnNode lastInsn = null;

    // Simular execuo das frames durante a cpia
    // O objectivo disto  resolver problemas como o NewSpecExample17, onde cdigo copiado deve
    // fazer branch para:
    // -- Cdigo copiado numa iterao anterior ("cdigo novo") se a frame continuar a conter um
    //    futuro, porque o cdigo novo  o que  suposto lidar com a existncia do futuro
    // -- Cdigo existente do mtodo ("cdigo antigo") se a frame j no contm um futuro, o que
    //    significa que a concretizao se faz durante o bloco actual
    FlowFrame currentFrame = analyzer.newFrame(problemFrame);
    if (problemInStack) {
        currentFrame.setStack(problemPosition,
                ((MergedUninitializedValue) currentFrame.getStack(problemPosition)).getFuture());
    } else {
        currentFrame.setLocal(problemPosition,
                ((MergedUninitializedValue) currentFrame.getLocal(problemPosition)).getFuture());
    }

    for (AbstractInsnNode n = problemLabelNode.getNext(); n != null; n = n.getNext()) {
        if (n instanceof LabelNode) {
            LabelNode labelNode = (LabelNode) n;
            if (getNextIgnoreLabelLineNop(labelNode) instanceof FrameNode) {
                // Se label se refere a uma frame, o bloco terminou
                // FIXME: Tem que se saltar sempre para labels novas, se existirem?
                il.add(new JumpInsnNode(GOTO, labelMap.get(labelNode)));
                break;
            } else {
                // Caso contrrio, substituimos por uma nova label, para permitir
                // que os LineNumberNodes continuem a existir no novo cdigo.
                labelMap.put(labelNode, new LabelNode());
            }
        }

        // Detectar, no caso de um salto, qual a label que se deve utilizar (ver comentrios acima)
        // FIXME: Ser que no caso do switch/case algumas das labels tm que apontar para o cdigo
        //     novo, e outras para o antigo?
        if (n instanceof JumpInsnNode || n instanceof LookupSwitchInsnNode
                || n instanceof TableSwitchInsnNode) {
            // Se ProblemPosition ainda tem um Futuro, saltar para cdigo novo
            if ((problemInStack && isFuture(currentFrame.getStack(problemPosition)))
                    || (!problemInStack && isFuture(currentFrame.getLocal(problemPosition)))) {
                il.add(n.clone(labelMap));
            } else { // Deixou de ter um Futuro, saltar para cdigo antigo
                il.add(n.clone(new LabelMap()));
            }
        } else {
            il.add(n.clone(labelMap));
        }

        lastInsn = n;

        // Se chegamos ao fim do bloco (GOTO, ATHROW ou *RETURN) tambm paramos a cpia
        if (n.getOpcode() == GOTO || n.getOpcode() == ATHROW || returnOpcodes.contains(n.getOpcode()))
            break;

        // Actualizar currentFrame -- simular execuo da instruco actual
        try {
            currentFrame = analyzer.computeNextFrame(currentFrame, n);
        } catch (AnalyzerException e) {
            // Ocorreu um erro, continuamos com a ltima frame vlida
            //Log.debug("WARNING: AnalyzerException during computeNextFrame");
        }
        //Log.debug("CurrentFrame: " + currentFrame + " (isFuture? " +
        //   (isFuture(currentFrame.getLocal(problemPosition)) ? "yes" : "no") + ")");
    }

    LabelNode copiedBlockEndLabel = new LabelNode();
    il.add(copiedBlockEndLabel);

    mn.instructions.add(il);
    il = mn.instructions;

    // Detectar qual dos seus predecessores originou o Futuro que ficou no MergedUninitializedValue

    if (problemFrame.predecessors().isEmpty()) { // ProblemFrame  o inicio de um exception handler
        // Popular predecessors da problemFrame com control flows de exceptions
        analyzer.populateExceptionPredecessors(problemFrame);

        //printCode(mn, frames, problemFrame.predecessors());

        // Adicionar um novo tryCatchBlock com:
        // - Range [Primeira instruco com Future,
        //      ltima instruco que tem future *E* faz parte da lista de predecessors]
        //   Razo: Lista de predecessors --> handler ainda est activo
        //          Tem future --> future pode ser substituido mais tarde
        //             (por exemplo { i = doA(); j = doB(); i = 0 })
        // - Target: Novo bloco copiado -- copiedBlockStartLabel

        AbstractInsnNode startBlockInsn = null;
        AbstractInsnNode endBlockInsn = null;

        for (FlowFrame f : problemFrame.predecessors()) {
            BasicValue v = problemInStack ? f.getStack(problemPosition) : f.getLocal(problemPosition);
            if (isFuture(v)) {
                AbstractInsnNode insn = insnForFrame(il, frames, f);

                if (startBlockInsn == null) {
                    startBlockInsn = insn;
                }

                if (endBlockInsn != null) {
                    // Detectar se o bloco actual terminou
                    if (getTryCatchBlock(mn, startBlockInsn, insn) == null)
                        break;
                }

                endBlockInsn = insn;
            } else if (startBlockInsn != null) {
                break;
            }
        }

        // Provavelmente o problema do NewSpecExample20, ver comentrios no ficheiro
        if (startBlockInsn == null || endBlockInsn == null) {
            throw new AssertionError("KNOWN BUG: Probably picked the wrong code to copy");
        }

        //Log.debug("PredecessorInsn [Exception]: First " + startBlockInsn + " Last " + endBlockInsn);

        LabelNode startBlockLabel = labelBefore(il, startBlockInsn);
        LabelNode endBlockLabel = labelAfter(il, endBlockInsn);

        TryCatchBlockNode originalBlock = getTryCatchBlock(mn, startBlockInsn, endBlockInsn);
        assert (originalBlock != null);

        mn.tryCatchBlocks.add(0, new SafeTryCatchBlockNode(startBlockLabel, endBlockLabel,
                copiedBlockStartLabel, originalBlock.type));

        if (originalBlock.start.equals(startBlockLabel) && originalBlock.end.equals(endBlockLabel)) {
            // Novo bloco substitui completamente o antigo
            mn.tryCatchBlocks.remove(originalBlock);
        } else {
            // Como o novo try/catch substitui o antigo, para que o verificador da JVM e do ASM
            // lidem melhor com a coisa (embora segundo os specs no seria necessrio), vamos
            // alterar o inicio e/ou o fim do bloco original para deixar de conter as instruces
            // que esto cobertas pelo novo bloco
            if (originalBlock.start.equals(startBlockLabel)) {
                originalBlock.start = endBlockLabel;
            } else if (originalBlock.end.equals(endBlockLabel)) {
                originalBlock.end = startBlockLabel;
            } else {
                Log.debug("FIXME: Original (old) try catch block should be adjusted");
            }
        }
    } else { // Existem predecessores, problemFrame  um bloco de cdigo normal
        FlowFrame predecessorWithFuture = getPredecessorWithFuture(problemFrame, problemInStack,
                problemPosition);

        if (predecessorWithFuture == null)
            throw new AssertionError();

        AbstractInsnNode predecessorInsn = insnForFrame(il, frames, predecessorWithFuture);
        //Log.debug("PredecessorInsn: " + predecessorInsn);

        // Detectar como vai ser feito o salto para a nova seco do cdigo

        // Casos possveis:
        // - Predecessor  instruco imediatamente antes da labelnode (e no  um salto)
        //   -> No alteramos, adicionamos goto
        //   -> Se for um salto, podemos ou no ter que alterar, dependendo de ser ou no
        //      um salto para a labelNode que marca o incio da seco problemtica
        // - Predecessor  jump / table|lookup switch
        //   -> Temos que alterar
        // Fazendo clone com o labelmap obtemos um n que tem o salto para a nova label trocado
        // pelo salto para a antiga.
        if (directlyPrecedes(predecessorInsn, problemLabelNode)
                && !hasLabelAsTarget(predecessorInsn, problemLabelNode)) {
            // No temos que alterar n, saltamos directamente para novo bloco
            il.insert(predecessorInsn, new JumpInsnNode(GOTO, copiedBlockStartLabel));
        } else {
            if (!((predecessorInsn instanceof LookupSwitchInsnNode)
                    || (predecessorInsn instanceof TableSwitchInsnNode)
                    || (predecessorInsn instanceof JumpInsnNode))) {
                throw new AssertionError(); // Instruco tem que ser salto
            }
            // N tem que ser alterado
            AbstractInsnNode replacementNode = predecessorInsn.clone(labelMap);
            il.set(predecessorInsn, replacementNode);
            if (lastInsn == predecessorInsn)
                lastInsn = replacementNode;
        }
    }

    // Corrigir exception handlers do mtodo

    // Como blocos de cdigo so copiados, temos tambm que copiar os exception handlers,
    // para que os try/catch continuem a funcionar correctamente
    List<AbstractInsnNode> copiedCodeRange = getRange(problemLabelNode, lastInsn);
    List<SafeTryCatchBlockNode> newTryCatchBlocks = new ArrayList<SafeTryCatchBlockNode>();
    for (TryCatchBlockNode tryCatchBlock : mn.tryCatchBlocks) {
        List<AbstractInsnNode> blockRange = getRange(tryCatchBlock.start, tryCatchBlock.end);
        blockRange.retainAll(copiedCodeRange);
        if (blockRange.isEmpty())
            continue;
        // Corner case: Supostamente um try/catch block cobre [start, end[, enquanto
        // que o getRange devolve [start, end]
        if (blockRange.size() == 1 && problemLabelNode == tryCatchBlock.end)
            continue;

        //Log.debug("Exception handler table needs fixup");

        // Determinar a nova label de inicio
        LabelNode newStart;
        if (copiedCodeRange.contains(tryCatchBlock.start)) {
            // loco excepo comea j dentro do copiedCodeRange
            newStart = labelMap.getMapping(tryCatchBlock.start);
        } else {
            // Bloco excepo comea fora do copiedCodeRange
            newStart = copiedBlockStartLabel;
        }

        // Determinar a nova label de fim
        LabelNode newEnd;
        if (copiedCodeRange.contains(tryCatchBlock.end)) {
            // Bloco excepo comea dentro do copiedCodeRange
            newEnd = labelMap.getMapping(tryCatchBlock.end);
        } else {
            // Bloco excepo acaba fora do copiedCodeRange
            newEnd = copiedBlockEndLabel;
        }

        newTryCatchBlocks
                .add(new SafeTryCatchBlockNode(newStart, newEnd, tryCatchBlock.handler, tryCatchBlock.type));
    }
    mn.tryCatchBlocks.addAll(newTryCatchBlocks);

    return true;
}