org.codehaus.groovy.classgen.asm.OptimizingStatementWriter Maven / Gradle / Ivy
/*
* 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