| /* |
| * 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.ClassHelper; |
| import org.codehaus.groovy.ast.ClassNode; |
| import org.codehaus.groovy.ast.ConstructorNode; |
| import org.codehaus.groovy.ast.InnerClassNode; |
| import org.codehaus.groovy.ast.InterfaceHelperClassNode; |
| import org.codehaus.groovy.ast.MethodNode; |
| import org.codehaus.groovy.classgen.AsmClassGenerator; |
| import org.codehaus.groovy.classgen.GeneratorContext; |
| import org.codehaus.groovy.classgen.asm.indy.IndyBinHelper; |
| import org.codehaus.groovy.classgen.asm.indy.IndyCallSiteWriter; |
| import org.codehaus.groovy.classgen.asm.indy.InvokeDynamicWriter; |
| import org.codehaus.groovy.classgen.asm.util.LoggableClassVisitor; |
| import org.codehaus.groovy.control.CompilerConfiguration; |
| import org.codehaus.groovy.control.SourceUnit; |
| import org.objectweb.asm.ClassVisitor; |
| import org.objectweb.asm.MethodVisitor; |
| import org.objectweb.asm.Opcodes; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import static org.apache.groovy.util.SystemUtil.getBooleanSafe; |
| |
| public class WriterController { |
| private static final String GROOVY_LOG_CLASSGEN = "groovy.log.classgen"; |
| private final boolean LOG_CLASSGEN = getBooleanSafe(GROOVY_LOG_CLASSGEN); |
| private AsmClassGenerator acg; |
| private MethodVisitor methodVisitor; |
| private CompileStack compileStack; |
| private OperandStack operandStack; |
| private ClassNode classNode; |
| private CallSiteWriter callSiteWriter; |
| private ClassVisitor cv; |
| private ClosureWriter closureWriter; |
| private LambdaWriter lambdaWriter; |
| private String internalClassName; |
| private InvocationWriter invocationWriter; |
| private BinaryExpressionHelper binaryExpHelper, fastPathBinaryExpHelper; |
| private UnaryExpressionHelper unaryExpressionHelper, fastPathUnaryExpressionHelper; |
| private AssertionWriter assertionWriter; |
| private String internalBaseClassName; |
| private ClassNode outermostClass; |
| private MethodNode methodNode; |
| private SourceUnit sourceUnit; |
| private ConstructorNode constructorNode; |
| private GeneratorContext context; |
| private InterfaceHelperClassNode interfaceClassLoadingClass; |
| public boolean optimizeForInt = true; |
| private StatementWriter statementWriter; |
| private boolean fastPath = false; |
| private TypeChooser typeChooser; |
| private int bytecodeVersion = Opcodes.V1_8; |
| private int lineNumber = -1; |
| private int helperMethodIndex = 0; |
| private List<String> superMethodNames = new ArrayList<String>(); |
| private MethodPointerExpressionWriter methodPointerExpressionWriter; |
| private MethodReferenceExpressionWriter methodReferenceExpressionWriter; |
| |
| public void init(AsmClassGenerator asmClassGenerator, GeneratorContext gcon, ClassVisitor cv, ClassNode cn) { |
| CompilerConfiguration config = cn.getCompileUnit().getConfig(); |
| Map<String,Boolean> optOptions = config.getOptimizationOptions(); |
| boolean invokedynamic=false; |
| if (optOptions.isEmpty()) { |
| // IGNORE |
| } else if (Boolean.FALSE.equals(optOptions.get("all"))) { |
| optimizeForInt=false; |
| // set other optimizations options to false here |
| } else { |
| if (Boolean.TRUE.equals(optOptions.get(CompilerConfiguration.INVOKEDYNAMIC))) invokedynamic=true; |
| if (Boolean.FALSE.equals(optOptions.get("int"))) optimizeForInt=false; |
| if (invokedynamic) optimizeForInt=false; |
| // set other optimizations options to false here |
| } |
| this.classNode = cn; |
| this.outermostClass = null; |
| this.internalClassName = BytecodeHelper.getClassInternalName(classNode); |
| |
| bytecodeVersion = chooseBytecodeVersion(invokedynamic, config.isPreviewFeatures(), config.getTargetBytecode()); |
| |
| if (invokedynamic) { |
| this.invocationWriter = new InvokeDynamicWriter(this); |
| this.callSiteWriter = new IndyCallSiteWriter(this); |
| this.binaryExpHelper = new IndyBinHelper(this); |
| } else { |
| this.callSiteWriter = new CallSiteWriter(this); |
| this.invocationWriter = new InvocationWriter(this); |
| this.binaryExpHelper = new BinaryExpressionHelper(this); |
| } |
| |
| this.unaryExpressionHelper = new UnaryExpressionHelper(this); |
| if (optimizeForInt) { |
| this.fastPathBinaryExpHelper = new BinaryExpressionMultiTypeDispatcher(this); |
| // todo: replace with a real fast path unary expression helper when available |
| this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this); |
| } else { |
| this.fastPathBinaryExpHelper = this.binaryExpHelper; |
| this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this); |
| } |
| |
| this.operandStack = new OperandStack(this); |
| this.assertionWriter = new AssertionWriter(this); |
| this.closureWriter = new ClosureWriter(this); |
| this.lambdaWriter = new LambdaWriter(this); |
| this.methodPointerExpressionWriter = new MethodPointerExpressionWriter(this); |
| this.methodReferenceExpressionWriter = new MethodReferenceExpressionWriter(this); |
| this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass()); |
| this.acg = asmClassGenerator; |
| this.sourceUnit = acg.getSourceUnit(); |
| this.context = gcon; |
| this.compileStack = new CompileStack(this); |
| this.cv = this.createClassVisitor(cv); |
| if (optimizeForInt) { |
| this.statementWriter = new OptimizingStatementWriter(this); |
| } else { |
| this.statementWriter = new StatementWriter(this); |
| } |
| this.typeChooser = new StatementMetaTypeChooser(); |
| } |
| |
| private ClassVisitor createClassVisitor(ClassVisitor cv) { |
| if (!LOG_CLASSGEN) { |
| return cv; |
| } |
| if (cv instanceof LoggableClassVisitor) { |
| return cv; |
| } |
| return new LoggableClassVisitor(cv); |
| } |
| |
| private static int chooseBytecodeVersion(final boolean invokedynamic, final boolean previewFeatures, final String targetBytecode) { |
| Integer bytecodeVersion = CompilerConfiguration.JDK_TO_BYTECODE_VERSION_MAP.get(targetBytecode); |
| |
| if (invokedynamic && bytecodeVersion < Opcodes.V1_8) { |
| return Opcodes.V1_8; |
| } else { |
| if (null != bytecodeVersion) { |
| return previewFeatures ? bytecodeVersion | Opcodes.V_PREVIEW : bytecodeVersion; |
| } |
| } |
| |
| throw new GroovyBugError("Bytecode version ["+targetBytecode+"] is not supported by the compiler"); |
| } |
| |
| public AsmClassGenerator getAcg() { |
| return acg; |
| } |
| |
| public void setMethodVisitor(MethodVisitor methodVisitor) { |
| this.methodVisitor = methodVisitor; |
| } |
| |
| public MethodVisitor getMethodVisitor() { |
| return methodVisitor; |
| } |
| |
| public CompileStack getCompileStack() { |
| return compileStack; |
| } |
| |
| public OperandStack getOperandStack() { |
| return operandStack; |
| } |
| |
| public ClassNode getClassNode() { |
| return classNode; |
| } |
| |
| public CallSiteWriter getCallSiteWriter() { |
| return callSiteWriter; |
| } |
| |
| public ClassVisitor getClassVisitor() { |
| return cv; |
| } |
| |
| public ClosureWriter getClosureWriter() { |
| return closureWriter; |
| } |
| |
| public LambdaWriter getLambdaWriter() { |
| return lambdaWriter; |
| } |
| |
| public ClassVisitor getCv() { |
| return cv; |
| } |
| |
| public String getInternalClassName() { |
| return internalClassName; |
| } |
| |
| public InvocationWriter getInvocationWriter() { |
| return invocationWriter; |
| } |
| |
| public BinaryExpressionHelper getBinaryExpressionHelper() { |
| if (fastPath) { |
| return fastPathBinaryExpHelper; |
| } else { |
| return binaryExpHelper; |
| } |
| } |
| |
| public MethodPointerExpressionWriter getMethodPointerExpressionWriter() { |
| return methodPointerExpressionWriter; |
| } |
| |
| public MethodReferenceExpressionWriter getMethodReferenceExpressionWriter() { |
| return methodReferenceExpressionWriter; |
| } |
| |
| public UnaryExpressionHelper getUnaryExpressionHelper() { |
| if (fastPath) { |
| return fastPathUnaryExpressionHelper; |
| } else { |
| return unaryExpressionHelper; |
| } |
| } |
| |
| public AssertionWriter getAssertionWriter() { |
| return assertionWriter; |
| } |
| |
| public TypeChooser getTypeChooser() { |
| return typeChooser; |
| } |
| |
| public String getInternalBaseClassName() { |
| return internalBaseClassName; |
| } |
| |
| public MethodNode getMethodNode() { |
| return methodNode; |
| } |
| |
| public void setMethodNode(MethodNode mn) { |
| methodNode = mn; |
| constructorNode = null; |
| } |
| |
| public ConstructorNode getConstructorNode(){ |
| return constructorNode; |
| } |
| |
| public void setConstructorNode(ConstructorNode cn) { |
| constructorNode = cn; |
| methodNode = null; |
| } |
| |
| public boolean isNotClinit() { |
| return methodNode == null || !methodNode.getName().equals("<clinit>"); |
| } |
| |
| public SourceUnit getSourceUnit() { |
| return sourceUnit; |
| } |
| |
| public boolean isStaticContext() { |
| if (compileStack!=null && compileStack.getScope()!=null) { |
| return compileStack.getScope().isInStaticContext(); |
| } |
| if (!isInClosure()) return false; |
| if (constructorNode != null) return false; |
| return classNode.isStaticClass() || methodNode.isStatic(); |
| } |
| |
| public boolean isInClosure() { |
| return classNode.getOuterClass() != null |
| && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE; |
| } |
| |
| public boolean isInClosureConstructor() { |
| return constructorNode != null |
| && classNode.getOuterClass() != null |
| && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE; |
| } |
| |
| public boolean isNotExplicitThisInClosure(boolean implicitThis) { |
| return implicitThis || !isInClosure(); |
| } |
| |
| |
| public boolean isStaticMethod() { |
| return methodNode != null && methodNode.isStatic(); |
| } |
| |
| public ClassNode getReturnType() { |
| if (methodNode != null) { |
| return methodNode.getReturnType(); |
| } else if (constructorNode != null) { |
| return constructorNode.getReturnType(); |
| } else { |
| throw new GroovyBugError("I spotted a return that is neither in a method nor in a constructor... I can not handle that"); |
| } |
| } |
| |
| public boolean isStaticConstructor() { |
| return methodNode != null && methodNode.getName().equals("<clinit>"); |
| } |
| |
| public boolean isConstructor() { |
| return constructorNode!=null; |
| } |
| |
| /** |
| * @return true if we are in a script body, where all variables declared are no longer |
| * local variables but are properties |
| */ |
| public boolean isInScriptBody() { |
| if (classNode.isScriptBody()) { |
| return true; |
| } else { |
| return classNode.isScript() && methodNode != null && methodNode.getName().equals("run"); |
| } |
| } |
| |
| public String getClassName() { |
| String className; |
| if (!classNode.isInterface() || interfaceClassLoadingClass == null) { |
| className = internalClassName; |
| } else { |
| className = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass); |
| } |
| return className; |
| } |
| |
| public ClassNode getOutermostClass() { |
| if (outermostClass == null) { |
| outermostClass = classNode; |
| while (outermostClass instanceof InnerClassNode) { |
| outermostClass = outermostClass.getOuterClass(); |
| } |
| } |
| return outermostClass; |
| } |
| |
| public GeneratorContext getContext() { |
| return context; |
| } |
| |
| public void setInterfaceClassLoadingClass(InterfaceHelperClassNode ihc) { |
| interfaceClassLoadingClass = ihc; |
| } |
| |
| public InterfaceHelperClassNode getInterfaceClassLoadingClass() { |
| return interfaceClassLoadingClass; |
| } |
| |
| public boolean shouldOptimizeForInt() { |
| return optimizeForInt; |
| } |
| |
| public StatementWriter getStatementWriter() { |
| return statementWriter; |
| } |
| |
| public void switchToFastPath() { |
| fastPath = true; |
| resetLineNumber(); |
| } |
| |
| public void switchToSlowPath() { |
| fastPath = false; |
| resetLineNumber(); |
| } |
| |
| public boolean isFastPath() { |
| return fastPath; |
| } |
| |
| public int getBytecodeVersion() { |
| return bytecodeVersion; |
| } |
| |
| public int getLineNumber() { |
| return lineNumber; |
| } |
| |
| public void setLineNumber(int n) { |
| lineNumber = n; |
| } |
| |
| public void resetLineNumber() { |
| setLineNumber(-1); |
| } |
| |
| public int getNextHelperMethodIndex() { |
| return helperMethodIndex++; |
| } |
| |
| public List<String> getSuperMethodNames() { |
| return superMethodNames; |
| } |
| |
| } |