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}