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

org.codehaus.groovy.classgen.asm.indy.InvokeDynamicWriter Maven / Gradle / Ivy

There is a newer version: 3.0.21
Show newest version
/*
 * Copyright 2003-2010 the original author or authors.
 *
 * Licensed 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.indy;

import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;

import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.CastExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.classgen.asm.CompileStack;
import org.codehaus.groovy.classgen.asm.InvocationWriter;
import org.codehaus.groovy.classgen.asm.MethodCallerMultiAdapter;
import org.codehaus.groovy.classgen.asm.OperandStack;
import org.codehaus.groovy.classgen.asm.WriterController;
import org.codehaus.groovy.runtime.wrappers.Wrapper;
import org.codehaus.groovy.vmplugin.v7.IndyInterface;
import org.objectweb.asm.Handle;

import static org.objectweb.asm.Opcodes.*;
import static org.codehaus.groovy.classgen.asm.BytecodeHelper.*;

/**
 * This Writer is used to generate the call invocation byte codes
 * for usage by invokedynamic.
 * 
 * @author Jochen "blackdrag" Theodorou
 */
public class InvokeDynamicWriter extends InvocationWriter {
    
    
    private static final String INDY_INTERFACE_NAME = IndyInterface.class.getName().replace('.', '/');
    private static final String BSM_METHOD_TYPE_DESCRIPTOR = 
        MethodType.methodType(
                CallSite.class, Lookup.class, String.class, MethodType.class
        ).toMethodDescriptorString();
    
    private static final Handle BSM = 
        new Handle(
                H_INVOKESTATIC,
                INDY_INTERFACE_NAME,
                "bootstrap",
                BSM_METHOD_TYPE_DESCRIPTOR);
    private static final Handle BSM_SAFE = 
        new Handle(
                H_INVOKESTATIC,
                INDY_INTERFACE_NAME,
                "bootstrapSafe",
                BSM_METHOD_TYPE_DESCRIPTOR);
    private static final Handle BSM_CURRENT = 
        new Handle(
                H_INVOKESTATIC,
                INDY_INTERFACE_NAME,
                "bootstrapCurrent",
                BSM_METHOD_TYPE_DESCRIPTOR);
    private static final Handle BSM_CURRENT_SAFE = 
        new Handle(
                H_INVOKESTATIC,
                INDY_INTERFACE_NAME,
                "bootstrapCurrentSafe",
                BSM_METHOD_TYPE_DESCRIPTOR);
    
    private WriterController controller;

    public InvokeDynamicWriter(WriterController wc) {
        super(wc);
        this.controller = wc;
    }

    @Override
    public void makeCall(
            Expression origin,  Expression receiver,
            Expression message, Expression arguments,
            MethodCallerMultiAdapter adapter, 
            boolean safe, boolean spreadSafe, boolean implicitThis 
    ) {
        // direct method call
        boolean containsSpreadExpression = AsmClassGenerator.containsSpreadExpression(arguments);
        if (containsSpreadExpression) {
            super.makeCall(origin, receiver, message, arguments, adapter, safe, spreadSafe, implicitThis);
            return;
        }
        
        if (origin instanceof MethodCallExpression) {
            MethodCallExpression mce = (MethodCallExpression) origin;
            MethodNode target = mce.getMethodTarget();
            if (writeDirectMethodCall(target, implicitThis, receiver, makeArgumentList(arguments))) return;
        }

        // no direct method call, dynamic call site        
        ClassNode cn = controller.getClassNode();
        if (controller.isInClosure() && !implicitThis && AsmClassGenerator.isThisExpression(receiver)) cn=cn.getOuterClass();
        ClassExpression sender = new ClassExpression(cn);

        OperandStack operandStack = controller.getOperandStack();
        CompileStack compileStack = controller.getCompileStack();
        AsmClassGenerator acg = controller.getAcg();
        
        // fixed number of arguments && name is a real String and no GString
        if ((adapter == invokeMethod || adapter == invokeMethodOnCurrent || adapter == invokeStaticMethod) && !spreadSafe) {
            String methodName = getMethodName(message);
            
            if (methodName != null) {
                makeIndyCall(adapter, receiver, implicitThis, safe, methodName, arguments);
                return;
            }
        }

        // variable number of arguments wrapped in a Object[]
        
        
        // ensure VariableArguments are read, not stored
        compileStack.pushLHS(false);

        // sender only for call sites
        if (adapter == AsmClassGenerator.setProperty) {
            ConstantExpression.NULL.visit(acg);
        } else {
            sender.visit(acg);
        }
        
        // receiver
        compileStack.pushImplicitThis(implicitThis);
        receiver.visit(acg);
        operandStack.box();
        compileStack.popImplicitThis();
        
        
        int operandsToRemove = 2;
        // message
        if (message != null) {
            message.visit(acg);
            operandStack.box();
            operandsToRemove++;
        }

        // arguments
        int numberOfArguments = containsSpreadExpression ? -1 : AsmClassGenerator.argumentSize(arguments);
        if (numberOfArguments > MethodCallerMultiAdapter.MAX_ARGS || containsSpreadExpression) {
            ArgumentListExpression ae = makeArgumentList(arguments);
            if (containsSpreadExpression) {
                acg.despreadList(ae.getExpressions(), true);
            } else {
                ae.visit(acg);
            }
        } else if (numberOfArguments > 0) {
            operandsToRemove += numberOfArguments;
            TupleExpression te = (TupleExpression) arguments;
            for (int i = 0; i < numberOfArguments; i++) {
                Expression argument = te.getExpression(i);
                argument.visit(acg);
                operandStack.box();
                if (argument instanceof CastExpression) acg.loadWrapper(argument);
            }
        }

        adapter.call(controller.getMethodVisitor(), numberOfArguments, safe, spreadSafe);

        compileStack.popLHS();
        operandStack.replace(ClassHelper.OBJECT_TYPE,operandsToRemove);
    }
    
    private void makeIndyCall(MethodCallerMultiAdapter adapter, Expression receiver, boolean implicitThis, boolean safe, String methodName, Expression arguments) {
        CompileStack compileStack = controller.getCompileStack();
        OperandStack operandStack = controller.getOperandStack();
        
        compileStack.pushLHS(false);

        String sig = "(";
        
        // load normal receiver as first argument
        compileStack.pushImplicitThis(implicitThis);
        receiver.visit(controller.getAcg());
        compileStack.popImplicitThis();
        sig += getTypeDescription(operandStack.getTopOperand());
        int numberOfArguments = 1;
        
        ArgumentListExpression ae = makeArgumentList(arguments);
        for (Expression arg : ae.getExpressions()) {
            arg.visit(controller.getAcg());
            if (arg instanceof CastExpression) {
                operandStack.box();
                controller.getAcg().loadWrapper(arg);
                sig += getTypeDescription(Wrapper.class);
            } else {
                sig += getTypeDescription(operandStack.getTopOperand());
            }
            numberOfArguments++;
        }
        sig += ")Ljava/lang/Object;";
        
        Handle bsmHandle = getBsmHandle(adapter, safe);
        controller.getMethodVisitor().visitInvokeDynamicInsn(methodName, sig, bsmHandle);
        
        operandStack.replace(ClassHelper.OBJECT_TYPE, numberOfArguments);
        compileStack.popLHS();
    }
    
    private Handle getBsmHandle(MethodCallerMultiAdapter adapter, boolean safe) {
        if (adapter==invokeMethodOnCurrent) {
            if (safe) return BSM_CURRENT_SAFE;
            return BSM_CURRENT;
        } else {
            if (safe) return BSM_SAFE;
            return BSM;
        }
    }

    @Override
    public void makeSingleArgumentCall(Expression receiver, String message, Expression arguments) {
        makeIndyCall(null, receiver, false, false, message, arguments);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy