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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy