
org.parboiled.transform.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 org.parboiled.transform;
import com.github.parboiled1.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 org.parboiled.BaseParser;
import org.parboiled.support.Var;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.CACHED;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.DONT_EXTEND;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.DONT_LABEL;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.DONT_SKIP_ACTIONS_IN_PREDICATES;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.EXPLICIT_ACTIONS_ONLY;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.MEMO_MISMATCHES;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.SKIP_ACTIONS_IN_PREDICATES;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.SKIP_NODE;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.SUPPRESS_NODE;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.SUPPRESS_SUBNODES;
import static com.github.parboiled1.grappa.transform.ParserAnnotation.moveTo;
import static com.github.parboiled1.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 org.parboiled.transform.AsmUtils.getClassForType;
import static org.parboiled.transform.AsmUtils.isActionRoot;
import static org.parboiled.transform.AsmUtils.isAssignableTo;
import static org.parboiled.transform.AsmUtils.isBooleanValueOfZ;
import static org.parboiled.transform.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 hasSuppressNodeAnnotation()
{
return annotations.contains(SUPPRESS_NODE);
}
public boolean hasSuppressSubnodesAnnotation()
{
return annotations.contains(SUPPRESS_SUBNODES);
}
public boolean hasSkipActionsInPredicatesAnnotation()
{
return annotations.contains(SKIP_ACTIONS_IN_PREDICATES);
}
public boolean hasSkipNodeAnnotation()
{
return annotations.contains(SKIP_NODE);
}
public boolean hasMemoMismatchesAnnotation()
{
return annotations.contains(MEMO_MISMATCHES);
}
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 org.parboiled.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)
{
Preconditions.checkNotNull(method);
moveTo(annotations, method.annotations);
}
public boolean isGenerationSkipped()
{
return skipGeneration;
}
public void dontSkipGeneration()
{
skipGeneration = false;
}
public void suppressNode()
{
annotations.add(SUPPRESS_NODE);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy