org.parboiled.transform.InstructionGroupPreparer Maven / Gradle / Ivy
The newest version!
/*
* 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 org.parboiled.transform;
import static org.parboiled.common.Preconditions.*;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.parboiled.common.Base64;
import org.parboiled.common.StringUtils;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import static org.objectweb.asm.Opcodes.ALOAD;
class InstructionGroupPreparer implements RuleMethodProcessor {
private static final Base64 CUSTOM_BASE64 =
new Base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789zzzz");
private RuleMethod method;
public boolean appliesTo(ParserClassNode classNode, RuleMethod method) {
checkArgNotNull(classNode, "classNode");
checkArgNotNull(method, "method");
return method.containsExplicitActions() || method.containsVars();
}
public void process(ParserClassNode classNode, RuleMethod method) {
this.method = checkArgNotNull(method, "method");
// prepare groups for later stages
for (InstructionGroup group : method.getGroups()) {
extractInstructions(group);
extractFields(group);
name(group, classNode);
}
}
// move all group instructions except for the root from the underlying method into the groups Insnlist
private void extractInstructions(InstructionGroup group) {
for (InstructionGraphNode node : group.getNodes()) {
if (node != group.getRoot()) {
AbstractInsnNode insn = node.getInstruction();
method.instructions.remove(insn);
group.getInstructions().add(insn);
}
}
}
// create FieldNodes for all xLoad instructions
private void extractFields(InstructionGroup group) {
List fields = group.getFields();
for (InstructionGraphNode node : group.getNodes()) {
if (node.isXLoad()) {
VarInsnNode 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()) {
// 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
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
// indexes are still mapped to the same group class (name)
insn.var = index;
}
}
}
// set a group name base on the hash across all group instructions and fields
private synchronized void name(InstructionGroup group, ParserClassNode classNode) {
// generate an MD5 hash across the buffer, use only the first 96 bit
MD5Digester digester = new MD5Digester(classNode.name);
group.getInstructions().accept(digester);
for (FieldNode field: group.getFields()) digester.visitField(field);
byte[] hash = digester.getMD5Hash();
byte[] hash96 = new byte[12];
System.arraycopy(hash, 0, hash96, 0, 12);
// generate a name for the group based on the hash
String name = group.getRoot().isActionRoot() ? "Action$" : "VarInit$";
name += CUSTOM_BASE64.encodeToString(hash96, false);
group.setName(name);
}
private static class MD5Digester extends MethodVisitor {
private static MessageDigest digest;
private static ByteBuffer buffer;
private final List