001/* 002 * Copyright (C) 2009-2011 Mathias Doenitz 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.parboiled.transform; 018 019import org.parboiled.common.ImmutableList; 020import org.objectweb.asm.ClassWriter; 021 022import java.util.List; 023 024import static org.parboiled.common.Preconditions.checkArgNotNull; 025import static org.parboiled.transform.AsmUtils.defineClass; 026import static org.parboiled.transform.AsmUtils.getExtendedParserClassName; 027 028public class ParserTransformer { 029 030 private ParserTransformer() {} 031 032 @SuppressWarnings({"unchecked"}) 033 public static synchronized <T> Class<? extends T> transformParser(Class<T> parserClass) throws Exception { 034 checkArgNotNull(parserClass, "parserClass"); 035 // first check whether we did not already create and load the extension of the given parser class 036 Class<?> extendedClass = AsmUtils.loadClass( 037 AsmUtils.getExtendedParserClassName(parserClass.getName()), parserClass 038 ); 039 return (Class<? extends T>) 040 (extendedClass != null ? extendedClass : extendParserClass(parserClass).getExtendedClass()); 041 } 042 043 static ParserClassNode extendParserClass(Class<?> parserClass) throws Exception { 044 ParserClassNode classNode = new ParserClassNode(parserClass); 045 new ClassNodeInitializer().process(classNode); 046 runMethodTransformers(classNode); 047 new ConstructorGenerator().process(classNode); 048 defineExtendedParserClass(classNode); 049 return classNode; 050 } 051 052 @SuppressWarnings({"unchecked"}) 053 private static void runMethodTransformers(ParserClassNode classNode) throws Exception { 054 List<RuleMethodProcessor> methodProcessors = createRuleMethodProcessors(); 055 056 // iterate through all rule methods 057 // since the ruleMethods map on the classnode is a treemap we get the methods sorted by name which puts 058 // all super methods first (since they are prefixed with one or more '$') 059 for (RuleMethod ruleMethod : classNode.getRuleMethods().values()) { 060 if (!ruleMethod.hasDontExtend()) { 061 for (RuleMethodProcessor methodProcessor : methodProcessors) { 062 if (methodProcessor.appliesTo(classNode, ruleMethod)) { 063 methodProcessor.process(classNode, ruleMethod); 064 } 065 } 066 } 067 } 068 069 for (RuleMethod ruleMethod : classNode.getRuleMethods().values()) { 070 if (!ruleMethod.isGenerationSkipped()) { 071 classNode.methods.add(ruleMethod); 072 } 073 } 074 } 075 076 static List<RuleMethodProcessor> createRuleMethodProcessors() { 077 return ImmutableList.of( 078 new UnusedLabelsRemover(), 079 new ReturnInstructionUnifier(), 080 new InstructionGraphCreator(), 081 new ImplicitActionsConverter(), 082 new InstructionGroupCreator(), 083 new InstructionGroupPreparer(), 084 new ActionClassGenerator(false), 085 new VarInitClassGenerator(false), 086 087 new RuleMethodRewriter(), 088 new SuperCallRewriter(), 089 new BodyWithSuperCallReplacer(), 090 new VarFramingGenerator(), 091 new LabellingGenerator(), 092 new FlagMarkingGenerator(), 093 new CachingGenerator() 094 ); 095 } 096 097 private static void defineExtendedParserClass(final ParserClassNode classNode) { 098 ClassWriter classWriter = new ClassWriter(ASMSettings.FRAMES) { 099 @Override 100 protected ClassLoader getClassLoader() { 101 return classNode.getParentClass().getClassLoader(); 102 } 103 }; 104 classNode.accept(classWriter); 105 classNode.setClassCode(classWriter.toByteArray()); 106 classNode.setExtendedClass(AsmUtils.defineClass( 107 classNode.name.replace('/', '.'), 108 classNode.getClassCode(), 109 classNode.getParentClass() 110 )); 111 } 112 113}