com.github.fge.grappa.transform.base.RuleMethod.java Source code

Java tutorial

Introduction

Here is the source code for com.github.fge.grappa.transform.base.RuleMethod.java

Source

/*
 * Copyright (c) 2009-2010 Ken Wenzel and Mathias Doenitz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.github.fge.grappa.transform.base;

import com.github.fge.grappa.parsers.BaseParser;
import com.github.fge.grappa.support.Var;
import com.github.fge.grappa.transform.ParserAnnotation;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import nars.util.data.list.FasterList;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.analysis.BasicValue;

import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static com.github.fge.grappa.misc.AsmUtils.*;
import static com.github.fge.grappa.transform.ParserAnnotation.*;
import static org.objectweb.asm.Opcodes.*;

public class RuleMethod extends MethodNode {
    private static final Set<ParserAnnotation> COPY_FROM_CLASS = EnumSet.of(DONT_LABEL, EXPLICIT_ACTIONS_ONLY,
            SKIP_ACTIONS_IN_PREDICATES);

    private final List<InstructionGroup> groups = new FasterList<>();
    private final List<LabelNode> usedLabels = new FasterList<>();
    private final Set<ParserAnnotation> annotations = EnumSet.noneOf(ParserAnnotation.class);

    private final Class<?> ownerClass;
    private final int parameterCount;
    private boolean containsImplicitActions;
    // calls to Boolean.valueOf(boolean)
    private boolean containsExplicitActions;
    // calls to BaseParser.ACTION(boolean)
    private boolean containsVars; // calls to Var.<init>(T)
    private boolean containsPotentialSuperCalls;
    private int numberOfReturns;
    private InstructionGraphNode returnInstructionNode;
    private List<InstructionGraphNode> graphNodes;
    private final List<LocalVariableNode> localVarVariables = new FasterList();
    private boolean bodyRewritten;
    private boolean skipGeneration;

    public RuleMethod(Class<?> ownerClass, int access, String name, String desc, String signature,
            String[] exceptions, Set<ParserAnnotation> classAnnotations) {
        super(Opcodes.ASM5, access, name, desc, signature, exceptions);
        this.ownerClass = ownerClass;
        parameterCount = Type.getArgumentTypes(desc).length;

        if (parameterCount == 0)
            annotations.add(CACHED);
        Set<ParserAnnotation> set = EnumSet.copyOf(classAnnotations);
        set.retainAll(COPY_FROM_CLASS);
        annotations.addAll(set);
        skipGeneration = isSuperMethod();
    }

    public List<InstructionGroup> getGroups() {
        return groups;
    }

    public List<LabelNode> getUsedLabels() {
        return usedLabels;
    }

    public Class<?> getOwnerClass() {
        return ownerClass;
    }

    public boolean hasDontExtend() {
        return annotations.contains(DONT_EXTEND);
    }

    public int getParameterCount() {
        return parameterCount;
    }

    public boolean containsImplicitActions() {
        return containsImplicitActions;
    }

    public void setContainsImplicitActions(boolean containsImplicitActions) {
        this.containsImplicitActions = containsImplicitActions;
    }

    public boolean containsExplicitActions() {
        return containsExplicitActions;
    }

    public void setContainsExplicitActions(boolean containsExplicitActions) {
        this.containsExplicitActions = containsExplicitActions;
    }

    public boolean containsVars() {
        return containsVars;
    }

    public boolean containsPotentialSuperCalls() {
        return containsPotentialSuperCalls;
    }

    public boolean hasCachedAnnotation() {
        return annotations.contains(CACHED);
    }

    public boolean hasDontLabelAnnotation() {
        return annotations.contains(DONT_LABEL);
    }

    public boolean hasSkipActionsInPredicatesAnnotation() {
        return annotations.contains(SKIP_ACTIONS_IN_PREDICATES);
    }

    public int getNumberOfReturns() {
        return numberOfReturns;
    }

    public InstructionGraphNode getReturnInstructionNode() {
        return returnInstructionNode;
    }

    public void setReturnInstructionNode(InstructionGraphNode returnInstructionNode) {
        this.returnInstructionNode = returnInstructionNode;
    }

    public List<InstructionGraphNode> getGraphNodes() {
        return graphNodes;
    }

    public List<LocalVariableNode> getLocalVarVariables() {
        return localVarVariables;
    }

    public boolean isBodyRewritten() {
        return bodyRewritten;
    }

    public void setBodyRewritten() {
        bodyRewritten = true;
    }

    public boolean isSuperMethod() {
        Preconditions.checkState(!name.isEmpty());
        return name.charAt(0) == '$';
    }

    public InstructionGraphNode setGraphNode(AbstractInsnNode insn, BasicValue resultValue,
            BasicValue[] predecessors) {
        if (graphNodes == null) {
            // initialize with a list of null values
            graphNodes = Lists.newArrayList(new InstructionGraphNode[instructions.size()]);
        }
        int index = instructions.indexOf(insn);
        InstructionGraphNode node = graphNodes.get(index);
        if (node == null) {
            node = new InstructionGraphNode(insn, resultValue);
            graphNodes.set(index, node);
        }
        node.addPredecessors(predecessors);
        return node;
    }

    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
        boolean recorded = recordAnnotation(annotations, desc);
        // FIXME...
        if (annotations.contains(DONT_SKIP_ACTIONS_IN_PREDICATES))
            annotations.remove(SKIP_ACTIONS_IN_PREDICATES);
        if (recorded)
            return null;
        // only keep visible annotations
        return visible ? super.visitAnnotation(desc, true) : null;
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
        switch (opcode) {
        case INVOKESTATIC:
            if (!annotations.contains(EXPLICIT_ACTIONS_ONLY) && isBooleanValueOfZ(owner, name, desc)) {
                containsImplicitActions = true;
            } else if (isActionRoot(owner, name)) {
                containsExplicitActions = true;
            }
            break;

        case INVOKESPECIAL:
            if ("<init>".equals(name)) {
                if (isVarRoot(owner, name, desc)) {
                    containsVars = true;
                }
            } else if (isAssignableTo(owner, BaseParser.class)) {
                containsPotentialSuperCalls = true;
            }
            break;
        }
        super.visitMethodInsn(opcode, owner, name, desc, itf);
    }

    @Override
    public void visitInsn(int opcode) {
        if (opcode == ARETURN)
            numberOfReturns++;
        super.visitInsn(opcode);
    }

    @Override
    public void visitJumpInsn(int opcode, Label label) {
        usedLabels.add(getLabelNode(label));
        super.visitJumpInsn(opcode, label);
    }

    @Override
    public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
        usedLabels.add(getLabelNode(dflt));
        for (Label label : labels)
            usedLabels.add(getLabelNode(label));
        super.visitTableSwitchInsn(min, max, dflt, labels);
    }

    @Override
    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
        usedLabels.add(getLabelNode(dflt));
        for (Label label : labels)
            usedLabels.add(getLabelNode(label));
        super.visitLookupSwitchInsn(dflt, keys, labels);
    }

    @Override
    public void visitLineNumber(int line, Label start) {
        // do not record line numbers
    }

    @Override
    public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
        // only remember the local variables of Type com.github.fge.grappa.support.Var that are not parameters
        Type type = Type.getType(desc);
        if (index > parameterCount && Var.class.isAssignableFrom(getClassForType(type)))
            localVarVariables.add(new LocalVariableNode(name, desc, null, null, null, index));
    }

    @Override
    public String toString() {
        return name;
    }

    public void moveFlagsTo(RuleMethod method) {
        Objects.requireNonNull(method);
        moveTo(annotations, method.annotations);
    }

    public boolean isGenerationSkipped() {
        return skipGeneration;
    }

    public void dontSkipGeneration() {
        skipGeneration = false;
    }
}