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

org.codehaus.groovy.classgen.asm.OptimizingStatementWriter Maven / Gradle / Ivy

The newest version!
/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.codehaus.groovy.classgen.asm;

import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.VariableScope;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.PostfixExpression;
import org.codehaus.groovy.ast.expr.PrefixExpression;
import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.UnaryMinusExpression;
import org.codehaus.groovy.ast.expr.UnaryPlusExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.DoWhileStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.ForStatement;
import org.codehaus.groovy.ast.stmt.IfStatement;
import org.codehaus.groovy.ast.stmt.ReturnStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.codehaus.groovy.ast.stmt.WhileStatement;
import org.codehaus.groovy.ast.tools.ParameterUtils;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.classgen.Verifier;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.runtime.BytecodeInterface8;
import org.codehaus.groovy.syntax.Types;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import java.util.LinkedList;
import java.util.List;

import static org.codehaus.groovy.ast.ClassHelper.BigDecimal_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.GROOVY_INTERCEPTABLE_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.boolean_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.int_TYPE;
import static org.codehaus.groovy.ast.ClassHelper.isPrimitiveType;
import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
import static org.codehaus.groovy.ast.tools.WideningCategories.isBigDecCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isDoubleCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isFloatingCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isIntCategory;
import static org.codehaus.groovy.ast.tools.WideningCategories.isLongCategory;
import static org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher.typeMap;
import static org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher.typeMapKeyNames;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFNE;
import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;

/**
 * A class to write out the optimized statements
 */
public class OptimizingStatementWriter extends StatementWriter {

    private static class FastPathData {
        private Label pathStart = new Label();
        private Label afterPath = new Label();
    }

    public static class ClassNodeSkip{}

    public static class StatementMeta {
        private boolean optimize=false;
        protected MethodNode target;
        protected ClassNode type;
        protected VariableExpression declaredVariableExpression;
        protected boolean[] involvedTypes = new boolean[typeMapKeyNames.length];
        public void chainInvolvedTypes(OptimizeFlagsCollector opt) {
            for (int i=0; i can do fast path
        if (meta==null || meta.optimize==false) return false;
        // fastPathBlocked -> slow path
        if (fastPathBlocked) return false;
        // controller.isFastPath() -> fastPath
        return !controller.isFastPath();
    }

    @Override
    public void writeReturn(ReturnStatement statement) {
        if (controller.isFastPath()) {
            super.writeReturn(statement);
        } else {
            StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
            if (isNewPathFork(meta) && writeDeclarationExtraction(statement)) {
                if (meta.declaredVariableExpression != null) {
                    // declaration was replaced by assignment so we need to define the variable
                    controller.getCompileStack().defineVariable(meta.declaredVariableExpression, false);
                }
                FastPathData fastPathData = writeGuards(meta, statement);

                boolean oldFastPathBlock = fastPathBlocked;
                fastPathBlocked = true;
                super.writeReturn(statement);
                fastPathBlocked = oldFastPathBlock;

                if (fastPathData==null) return;
                writeFastPathPrelude(fastPathData);
                super.writeReturn(statement);
                writeFastPathEpilogue(fastPathData);
            } else {
                super.writeReturn(statement);
            }
        }
    }

    @Override
    public void writeExpressionStatement(ExpressionStatement statement) {
        if (controller.isFastPath()) {
            super.writeExpressionStatement(statement);
        } else {
            StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
            // we have to have handle DelcarationExpressions special, since their 
            // entry should be outside the optimization path, we have to do that of
            // course only if we are actually going to do two different paths, 
            // otherwise it is not needed
            //
            // there are several cases to be considered now.
            // (1) no fast path possible, so just do super
            // (2) fast path possible, and at path split point (meaning not in 
            //     fast path and not in slow path). Here we have to extract the 
            //     Declaration and replace by an assignment
            // (3) fast path possible and in slow or fastPath. Nothing to do here.
            //
            // the only case we need to handle is then (2).

            if (isNewPathFork(meta) && writeDeclarationExtraction(statement)) {
                if (meta.declaredVariableExpression != null) {
                    // declaration was replaced by assignment so we need to define the variable
                    controller.getCompileStack().defineVariable(meta.declaredVariableExpression, false);
                }
                FastPathData fastPathData = writeGuards(meta, statement);

                boolean oldFastPathBlock = fastPathBlocked;
                fastPathBlocked = true;
                super.writeExpressionStatement(statement);
                fastPathBlocked = oldFastPathBlock;

                if (fastPathData==null) return;
                writeFastPathPrelude(fastPathData);
                super.writeExpressionStatement(statement);
                writeFastPathEpilogue(fastPathData);
            } else {
                super.writeExpressionStatement(statement);
            }
        }
    }

    private boolean writeDeclarationExtraction(Statement statement) {
        Expression ex = null;
        if (statement instanceof ReturnStatement) {
            ReturnStatement rs = (ReturnStatement) statement;
            ex = rs.getExpression();
        } else if (statement instanceof ExpressionStatement) {
            ExpressionStatement es = (ExpressionStatement) statement;
            ex = es.getExpression();
        } else {
            throw new GroovyBugError("unknown statement type :"+statement.getClass());
        }
        if (!(ex instanceof DeclarationExpression)) return true;
        DeclarationExpression declaration = (DeclarationExpression) ex;
        ex = declaration.getLeftExpression();
        if (ex instanceof TupleExpression) return false;

        // stash declared variable in case we do subsequent visits after we
        // change to assignment only
        StatementMeta meta = statement.getNodeMetaData(StatementMeta.class);
        if (meta != null) {
            meta.declaredVariableExpression = declaration.getVariableExpression();
        }

        // change statement to do assignment only
        BinaryExpression assignment = new BinaryExpression(
                declaration.getLeftExpression(),
                declaration.getOperation(),
                declaration.getRightExpression());
        assignment.setSourcePosition(declaration);
        assignment.copyNodeMetaData(declaration);
        // replace statement code
        if (statement instanceof ReturnStatement) {
            ReturnStatement rs = (ReturnStatement) statement;
            rs.setExpression(assignment);
        } else if (statement instanceof ExpressionStatement) {
            ExpressionStatement es = (ExpressionStatement) statement;
            es.setExpression(assignment);
        } else {
            throw new GroovyBugError("unknown statement type :"+statement.getClass());
        }
        return true;
    }

    public static void setNodeMeta(TypeChooser chooser, ClassNode classNode) {
        if (classNode.getNodeMetaData(ClassNodeSkip.class)!=null) return;
        new OptVisitor(chooser).visitClass(classNode);
    }

    private static StatementMeta addMeta(ASTNode node) {
        StatementMeta metaOld = node.getNodeMetaData(StatementMeta.class);
        StatementMeta meta = metaOld;
        if (meta==null) meta = new StatementMeta();
        meta.optimize = true;
        if (metaOld==null) node.setNodeMetaData(StatementMeta.class, meta);
        return meta;
    }

    private static StatementMeta addMeta(ASTNode node, OptimizeFlagsCollector opt) {
        StatementMeta meta = addMeta(node);
        meta.chainInvolvedTypes(opt);
        return meta;
    }

    private static class OptimizeFlagsCollector {
        private static class OptimizeFlagsEntry {
            private boolean canOptimize = false;
            private boolean shouldOptimize = false;
            private boolean[] involvedTypes = new boolean[typeMapKeyNames.length];
        }
        private OptimizeFlagsEntry current = new OptimizeFlagsEntry();
        private LinkedList olderEntries = new LinkedList();
        public void push() {
            olderEntries.addLast(current);
            current = new OptimizeFlagsEntry();
        }
        public void pop(boolean propagateFlags){
            OptimizeFlagsEntry old = current;
            current = olderEntries.removeLast();
            if (propagateFlags) {
                chainCanOptimize(old.canOptimize);
                chainShouldOptimize(old.shouldOptimize);
                for (int i=0; i", call.getArguments(), false);
        }

        private void setMethodTarget(Expression expression, String name, Expression callArgs, boolean isMethod) {
            if (name==null) return;
            if (!optimizeMethodCall) return;
            if (AsmClassGenerator.containsSpreadExpression(callArgs)) return;
            // find method call target
            Parameter[] paraTypes = null;
            if (callArgs instanceof ArgumentListExpression) {
                ArgumentListExpression args = (ArgumentListExpression) callArgs;
                int size = args.getExpressions().size();
                paraTypes = new Parameter[size];
                int i=0;
                for (Expression exp: args.getExpressions()) {
                    ClassNode type = typeChooser.resolveType(exp, node);
                    if (!validTypeForCall(type)) return;
                    paraTypes[i] = new Parameter(type,"");
                    i++;
                }
            } else {
                ClassNode type = typeChooser.resolveType(callArgs, node);
                if (!validTypeForCall(type)) return;
                paraTypes = new Parameter[]{new Parameter(type,"")};
            }

            MethodNode target;
            ClassNode type;
            if (isMethod) {
                target = node.getMethod(name, paraTypes);
                if (target==null) return;
                if (!target.getDeclaringClass().equals(node)) return;
                if (scope.isInStaticContext() && !target.isStatic()) return;
                type = target.getReturnType().redirect();
            } else {
                type = expression.getType();
                target = selectConstructor(type, paraTypes);
                if (target==null) return;
            }

            StatementMeta meta = addMeta(expression);
            meta.target = target;
            meta.type = type;
            opt.chainShouldOptimize(true);
        }

        private static MethodNode selectConstructor(ClassNode node, Parameter[] paraTypes) {
            List cl = node.getDeclaredConstructors();
            MethodNode res = null;
            for (ConstructorNode cn : cl) {
                if (ParameterUtils.parametersEqual(cn.getParameters(), paraTypes)) {
                    res = cn;
                    break;
                }
            }
            if (res !=null && res.isPublic()) return res;
            return null;
        }

        private static boolean validTypeForCall(ClassNode type) {
            // do call only for final classes and primitive types
            if (isPrimitiveType(type)) return true;
            return (type.getModifiers() & ACC_FINAL) > 0;
        }

        @Override
        public void visitClosureExpression(ClosureExpression expression) {
            return;
        }

        @Override
        public void visitForLoop(ForStatement statement) {
            opt.push();
            super.visitForLoop(statement);
            if (opt.shouldOptimize()) addMeta(statement,opt);
            opt.pop(opt.shouldOptimize());
        }
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy