Package org.parboiled.transform

Source Code of org.parboiled.transform.ImplicitActionsConverter

/*
* Copyright (C) 2009-2010 Mathias Doenitz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.parboiled.transform;

import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.parboiled.transform.AsmUtils.isBooleanValueOfZ;

/**
* Makes all implicit action expressions in a rule method explicit.
*/
class ImplicitActionsConverter implements RuleMethodProcessor, Types, Opcodes {

    private final Set<InstructionGraphNode> covered = new HashSet<InstructionGraphNode>();
    private RuleMethod method;

    public boolean appliesTo(@NotNull ParserClassNode classNode, @NotNull RuleMethod method) {
        return method.containsImplicitActions();
    }

    public void process(@NotNull ParserClassNode classNode, @NotNull RuleMethod method) throws Exception {
        this.method = method;
        covered.clear();
        walkNode(method.getReturnInstructionNode());
        method.setContainsImplicitActions(false);
    }

    private void walkNode(InstructionGraphNode node) {
        if (covered.contains(node)) return;
        covered.add(node);

        if (isImplicitAction(node)) {
            replaceWithActionWrapper(node);
            method.setContainsExplicitActions(true);
            return;
        }
        if (!node.isActionRoot()) {
            for (InstructionGraphNode predecessor : node.getPredecessors()) {
                walkNode(predecessor);
            }
        }
    }

    private void replaceWithActionWrapper(InstructionGraphNode node) {
        MethodInsnNode insn = createActionWrappingInsn();
        method.instructions.set(node.getInstruction(), insn);
        node.setIsActionRoot();
        node.setInstruction(insn);
    }

    private boolean isImplicitAction(InstructionGraphNode node) {
        // an implicit action must be a call to Boolean.valueOf(boolean)
        if (!isBooleanValueOfZ(node.getInstruction())) return false;

        // it must have exactly one other instruction that depends on it
        List<InstructionGraphNode> dependents = getDependents(node);
        if (dependents.size() != 1) return false;

        // this dependent instruction must be rule method call
        InstructionGraphNode dependent = dependents.get(0);
        return isObjectArgumentToRuleCreatingMethodCall(node, dependent) || isStoredIntoObjectArray(dependent);
    }

    private boolean isObjectArgumentToRuleCreatingMethodCall(InstructionGraphNode node,
                                                             InstructionGraphNode dependent) {
        // is the single dependent a method call ?
        AbstractInsnNode insn = dependent.getInstruction();
        if (insn.getType() != AbstractInsnNode.METHOD_INSN) return false;

        // Does this method call return a Rule ?
        MethodInsnNode mi = (MethodInsnNode) insn;
        if (!RULE.equals(Type.getReturnType(mi.desc))) return false;

        // Doesthe result of the Boolean.valueOf(boolean) call correspond to an Object parameter ?
        Type[] argTypes = Type.getArgumentTypes(mi.desc);
        int argIndex = getArgumentIndex(dependent, node);
        Preconditions.checkState(argIndex < argTypes.length);
        return "java/lang/Object".equals(argTypes[argIndex].getInternalName());
    }

    private boolean isStoredIntoObjectArray(InstructionGraphNode dependent) {
        // is the single dependent an AASTORE instruction ?
        AbstractInsnNode insn = dependent.getInstruction();
        if (insn.getOpcode() != AASTORE) return false;

        // Does this instruction store into an array of Object ?
        List<InstructionGraphNode> dependents = getDependents(dependent);
        Preconditions.checkState(dependents.size() == 1); // an AASTORE instruction should have exactly one dependent
        AbstractInsnNode newArrayInsn = dependents.get(0).getInstruction();
        Preconditions.checkState(newArrayInsn.getOpcode() == ANEWARRAY); // which should be a n ANEWARRAY instruction
        return "java/lang/Object".equals(((TypeInsnNode) newArrayInsn).desc);
    }

    private int getArgumentIndex(InstructionGraphNode callNode, InstructionGraphNode predecessor) {
        int startIndex = callNode.getInstruction().getOpcode() == INVOKESTATIC ? 0 : 1;
        for (int i = startIndex; i < callNode.getPredecessors().size(); i++) {
            InstructionGraphNode argumentNode = callNode.getPredecessors().get(i);
            if (predecessor.equals(argumentNode)) {
                return i - startIndex;
            }
        }
        throw new IllegalStateException();
    }

    private List<InstructionGraphNode> getDependents(InstructionGraphNode predecessor) {
        List<InstructionGraphNode> dependents = new ArrayList<InstructionGraphNode>();
        for (InstructionGraphNode node : method.getGraphNodes()) {
            if (node.getPredecessors().contains(predecessor)) {
                dependents.add(node);
            }
        }
        return dependents;
    }

    private MethodInsnNode createActionWrappingInsn() {
        return new MethodInsnNode(INVOKESTATIC, BASE_PARSER.getInternalName(), "ACTION",
                "(Z)" + ACTION_DESC);
    }

}
TOP

Related Classes of org.parboiled.transform.ImplicitActionsConverter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.