/* * Copyright (C) 2009-2011 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 com.github.fge.grappa.transform.process; import com.github.fge.grappa.transform.hash.InstructionGroupHasher; import org.objectweb.asm.Type; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.VarInsnNode; import com.github.fge.grappa.transform.base.InstructionGraphNode; import com.github.fge.grappa.transform.base.InstructionGroup; import com.github.fge.grappa.transform.base.ParserClassNode; import com.github.fge.grappa.transform.base.RuleMethod; import javax.annotation.Nonnull; import java.util.List; import java.util.Objects; public final class InstructionGroupPreparer implements RuleMethodProcessor { private RuleMethod method; @Override public boolean appliesTo(@Nonnull final ParserClassNode classNode, @Nonnull final RuleMethod method) { Objects.requireNonNull(classNode, "classNode"); Objects.requireNonNull(method, "method"); return method.containsExplicitActions() || method.containsVars(); } @Override public void process(@Nonnull final ParserClassNode classNode, @Nonnull final RuleMethod method) { this.method = Objects.requireNonNull(method, "method"); // prepare groups for later stages for (final InstructionGroup group: method.getGroups()) { extractInstructions(group); extractFields(group); InstructionGroupHasher.hash(group, classNode.name); } } // move all group instructions except for the root from the underlying // method into the groups Insnlist private void extractInstructions(final InstructionGroup group) { AbstractInsnNode insn; for (final InstructionGraphNode node: group.getNodes()) { if (node == group.getRoot()) continue; insn = node.getInstruction(); method.instructions.remove(insn); group.getInstructions().add(insn); } } // create FieldNodes for all xLoad instructions private static void extractFields(final InstructionGroup group) { final List<FieldNode> fields = group.getFields(); VarInsnNode insn; for (final InstructionGraphNode node : group.getNodes()) { if (!node.isXLoad()) continue; insn = (VarInsnNode) node.getInstruction(); // check whether we already have a field for the var with this index int index; for (index = 0; index < fields.size(); index++) if (fields.get(index).access == insn.var) break; // if we don't, create a new field for the var if (index == fields.size()) { /* * TODO: fix hack below * * CAUTION, HACK!: for brevity we reuse the access field and * the value field of the FieldNode for keeping track of the * original var index as well as the FieldNodes Type * (respectively) so we need to make sure that we correct * for this when the field is actually written */ final Type type = node.getResultValue().getType(); fields.add(new FieldNode(insn.var, "field$" + index, type.getDescriptor(), null, type)); } // normalize the instruction so instruction groups that are // identical except for the variable indices are still mapped to // the same group class (name) insn.var = index; } } }