All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.fge.grappa.transform.base.RuleMethod Maven / Gradle / Ivy

/*
 * 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.transform.ParserAnnotation;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
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 com.github.fge.grappa.support.Var;

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

import static com.github.fge.grappa.transform.ParserAnnotation.CACHED;
import static com.github.fge.grappa.transform.ParserAnnotation.DONT_EXTEND;
import static com.github.fge.grappa.transform.ParserAnnotation.DONT_LABEL;
import static com.github.fge.grappa.transform.ParserAnnotation
    .DONT_SKIP_ACTIONS_IN_PREDICATES;
import static com.github.fge.grappa.transform.ParserAnnotation
    .EXPLICIT_ACTIONS_ONLY;
import static com.github.fge.grappa.transform.ParserAnnotation
    .SKIP_ACTIONS_IN_PREDICATES;
import static com.github.fge.grappa.transform.ParserAnnotation.moveTo;
import static com.github.fge.grappa.transform.ParserAnnotation.recordAnnotation;
import static org.objectweb.asm.Opcodes.ARETURN;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static com.github.fge.grappa.misc.AsmUtils.getClassForType;
import static com.github.fge.grappa.misc.AsmUtils.isActionRoot;
import static com.github.fge.grappa.misc.AsmUtils.isAssignableTo;
import static com.github.fge.grappa.misc.AsmUtils.isBooleanValueOfZ;
import static com.github.fge.grappa.misc.AsmUtils.isVarRoot;

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


    private final List groups
        = new ArrayList<>();
    private final List usedLabels = new ArrayList<>();
    private final Set 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.(T)
    private boolean containsPotentialSuperCalls;
    private int numberOfReturns;
    private InstructionGraphNode returnInstructionNode;
    private List graphNodes;
    private final List localVarVariables
        = Lists.newArrayList();
    private boolean bodyRewritten;
    private boolean skipGeneration;

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

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

    public List getGroups()
    {
        return groups;
    }

    public List 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(
        final boolean containsImplicitActions)
    {
        this.containsImplicitActions = containsImplicitActions;
    }

    public boolean containsExplicitActions()
    {
        return containsExplicitActions;
    }

    public void setContainsExplicitActions(
        final 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(
        final InstructionGraphNode returnInstructionNode)
    {
        this.returnInstructionNode = returnInstructionNode;
    }

    public List getGraphNodes()
    {
        return graphNodes;
    }

    public List 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(final AbstractInsnNode insn,
        final BasicValue resultValue, final List predecessors)
    {
        if (graphNodes == null) {
            // initialize with a list of null values
            graphNodes = Lists
                .newArrayList(new InstructionGraphNode[instructions.size()]);
        }
        final 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(final String desc,
        final boolean visible)
    {
        final  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(final int opcode, final String owner,
        final String name, final String desc, final 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 ("".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(final int opcode)
    {
        if (opcode == ARETURN)
            numberOfReturns++;
        super.visitInsn(opcode);
    }

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

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

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

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

    @Override
    public void visitLocalVariable(final String name, final String desc,
        final String signature, final Label start, final Label end,
        final int index)
    {
        // only remember the local variables of Type com.github.fge.grappa.support.Var that are not parameters
        final 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(final RuleMethod method)
    {
        Objects.requireNonNull(method);
        moveTo(annotations, method.annotations);
    }

    public boolean isGenerationSkipped()
    {
        return skipGeneration;
    }

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy