
org.parboiled.transform.process.GroupClassGenerator Maven / Gradle / Ivy
/*
* 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.process;
import com.github.parboiled1.grappa.transform.CodeBlock;
import com.google.common.base.Preconditions;
import me.qmx.jitescript.util.CodegenUtils;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.parboiled.Context;
import org.parboiled.ContextAware;
import org.parboiled.transform.AsmUtils;
import org.parboiled.transform.InstructionGraphNode;
import org.parboiled.transform.InstructionGroup;
import org.parboiled.transform.ParserClassNode;
import org.parboiled.transform.RuleMethod;
import javax.annotation.Nonnull;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.DUP;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.RETURN;
public abstract class GroupClassGenerator
implements RuleMethodProcessor
{
private final boolean forceCodeBuilding;
protected ParserClassNode classNode;
protected RuleMethod method;
protected GroupClassGenerator(final boolean forceCodeBuilding)
{
this.forceCodeBuilding = forceCodeBuilding;
}
@Override
public final void process(@Nonnull final ParserClassNode classNode,
@Nonnull final RuleMethod method)
{
this.classNode = Preconditions.checkNotNull(classNode, "classNode");
this.method = Preconditions.checkNotNull(method, "method");
for (final InstructionGroup group: method.getGroups())
if (appliesTo(group.getRoot()))
loadGroupClass(group);
}
protected abstract boolean appliesTo(InstructionGraphNode group);
private void loadGroupClass(final InstructionGroup group)
{
createGroupClassType(group);
final String className = group.getGroupClassType().getClassName();
final ClassLoader classLoader
= classNode.getParentClass().getClassLoader();
final Class> groupClass;
synchronized (AsmUtils.class) {
groupClass = AsmUtils.findLoadedClass(className, classLoader);
if (groupClass == null || forceCodeBuilding) {
final byte[] groupClassCode = generateGroupClassCode(group);
group.setGroupClassCode(groupClassCode);
if (groupClass == null)
AsmUtils.loadClass(className, groupClassCode, classLoader);
}
}
}
private void createGroupClassType(final InstructionGroup group)
{
final String s = classNode.name;
/*
* If the parser has no package, the group will be an embedded class
* to the parser class
*/
final int lastSlash = classNode.name.lastIndexOf('/');
final String groupName = group.getName();
final String pkg = lastSlash >= 0 ? s.substring(0, lastSlash) : s;
final String groupClassInternalName = pkg + '/' + groupName;
group.setGroupClassType(Type.getObjectType(groupClassInternalName));
}
protected final byte[] generateGroupClassCode(final InstructionGroup group)
{
final ClassWriter classWriter
= new ClassWriter(ClassWriter.COMPUTE_MAXS);
generateClassBasics(group, classWriter);
generateFields(group, classWriter);
generateConstructor(classWriter);
generateMethod(group, classWriter);
return classWriter.toByteArray();
}
private void generateClassBasics(final InstructionGroup group,
final ClassWriter cw)
{
cw.visit(Opcodes.V1_6, ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
group.getGroupClassType().getInternalName(), null,
getBaseType().getInternalName(), null);
cw.visitSource(classNode.sourceFile, null);
}
protected abstract Type getBaseType();
private static void generateFields(final InstructionGroup group,
final ClassWriter cw)
{
// TODO: fix the below comment; those "two members" should be split
// CAUTION: the FieldNode has illegal access flags and an illegal
// value field since these two members are reused for other
// purposes, so we need to write out the field "manually" here
// rather than just call "field.accept(cw)"
for (final FieldNode field: group.getFields())
cw.visitField(ACC_PUBLIC + ACC_SYNTHETIC, field.name, field.desc,
null, null);
}
private void generateConstructor(final ClassWriter cw)
{
final MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "",
CodegenUtils.sig(void.class, String.class), null, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESPECIAL, getBaseType().getInternalName(),
"", CodegenUtils.sig(void.class, String.class), false);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0); // trigger automatic computing
}
protected abstract void generateMethod(InstructionGroup group,
ClassWriter cw);
protected static void insertSetContextCalls(final InstructionGroup group,
int localVarIx)
{
final InsnList instructions = group.getInstructions();
final CodeBlock block = CodeBlock.newCodeBlock();
for (final InstructionGraphNode node: group.getNodes()) {
if (!node.isCallOnContextAware())
continue;
final AbstractInsnNode insn = node.getInstruction();
if (node.getPredecessors().size() > 1) {
// store the target of the call in a new local variable
final AbstractInsnNode loadTarget = node.getPredecessors()
.get(0).getInstruction();
block.clear().dup().astore(++localVarIx);
instructions.insert(loadTarget, block.getInstructionList());
// immediately before the call get the target from the local var
// and set the context on it
instructions.insertBefore(insn, new VarInsnNode(ALOAD,
localVarIx));
} else {
// if we have only one predecessor the call does not take any
// parameters and we can skip the storing and loading of the
// invocation target
instructions.insertBefore(insn, new InsnNode(DUP));
}
block.clear()
.aload(1)
.invokeinterface(CodegenUtils.p(ContextAware.class),
"setContext", CodegenUtils.sig(void.class, Context.class));
instructions.insertBefore(insn, block.getInstructionList());
}
}
protected static void convertXLoads(final InstructionGroup group)
{
final String owner = group.getGroupClassType().getInternalName();
InsnList insnList;
for (final InstructionGraphNode node : group.getNodes()) {
if (!node.isXLoad())
continue;
final VarInsnNode insn = (VarInsnNode) node.getInstruction();
final FieldNode field = group.getFields().get(insn.var);
final FieldInsnNode fieldNode = new FieldInsnNode(GETFIELD, owner,
field.name, field.desc);
insnList = group.getInstructions();
// insert the correct GETFIELD after the xLoad
insnList.insert(insn, fieldNode);
// change the load to ALOAD 0
insnList.set(insn, new VarInsnNode(ALOAD, 0));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy