org.elasticsearch.painless.lookup.PainlessMethod.java Source code

Java tutorial

Introduction

Here is the source code for org.elasticsearch.painless.lookup.PainlessMethod.java

Source

/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.elasticsearch.painless.lookup;

import org.elasticsearch.painless.MethodWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;

public class PainlessMethod {
    public final String name;
    public final Class<?> target;
    public final Class<?> augmentation;
    public final Class<?> rtn;
    public final List<Class<?>> arguments;
    public final org.objectweb.asm.commons.Method method;
    public final int modifiers;
    public final MethodHandle handle;

    public PainlessMethod(String name, Class<?> target, Class<?> augmentation, Class<?> rtn,
            List<Class<?>> arguments, org.objectweb.asm.commons.Method method, int modifiers, MethodHandle handle) {
        this.name = name;
        this.augmentation = augmentation;
        this.target = target;
        this.rtn = rtn;
        this.arguments = Collections.unmodifiableList(arguments);
        this.method = method;
        this.modifiers = modifiers;
        this.handle = handle;
    }

    /**
     * Returns MethodType for this method.
     * <p>
     * This works even for user-defined Methods (where the MethodHandle is null).
     */
    public MethodType getMethodType() {
        // we have a methodhandle already (e.g. whitelisted class)
        // just return its type
        if (handle != null) {
            return handle.type();
        }
        // otherwise compute it
        final Class<?> params[];
        final Class<?> returnValue;
        if (augmentation != null) {
            // static method disguised as virtual/interface method
            params = new Class<?>[1 + arguments.size()];
            params[0] = augmentation;
            for (int i = 0; i < arguments.size(); i++) {
                params[i + 1] = PainlessLookupUtility.painlessDefTypeToJavaObjectType(arguments.get(i));
            }
            returnValue = PainlessLookupUtility.painlessDefTypeToJavaObjectType(rtn);
        } else if (Modifier.isStatic(modifiers)) {
            // static method: straightforward copy
            params = new Class<?>[arguments.size()];
            for (int i = 0; i < arguments.size(); i++) {
                params[i] = PainlessLookupUtility.painlessDefTypeToJavaObjectType(arguments.get(i));
            }
            returnValue = PainlessLookupUtility.painlessDefTypeToJavaObjectType(rtn);
        } else if ("<init>".equals(name)) {
            // constructor: returns the owner class
            params = new Class<?>[arguments.size()];
            for (int i = 0; i < arguments.size(); i++) {
                params[i] = PainlessLookupUtility.painlessDefTypeToJavaObjectType(arguments.get(i));
            }
            returnValue = target;
        } else {
            // virtual/interface method: add receiver class
            params = new Class<?>[1 + arguments.size()];
            params[0] = target;
            for (int i = 0; i < arguments.size(); i++) {
                params[i + 1] = PainlessLookupUtility.painlessDefTypeToJavaObjectType(arguments.get(i));
            }
            returnValue = PainlessLookupUtility.painlessDefTypeToJavaObjectType(rtn);
        }
        return MethodType.methodType(returnValue, params);
    }

    public void write(MethodWriter writer) {
        final org.objectweb.asm.Type type;
        final Class<?> clazz;
        if (augmentation != null) {
            assert Modifier.isStatic(modifiers);
            clazz = augmentation;
            type = org.objectweb.asm.Type.getType(augmentation);
        } else {
            clazz = target;
            type = Type.getType(target);
        }

        if (Modifier.isStatic(modifiers)) {
            // invokeStatic assumes that the owner class is not an interface, so this is a
            // special case for interfaces where the interface method boolean needs to be set to
            // true to reference the appropriate class constant when calling a static interface
            // method since java 8 did not check, but java 9 and 10 do
            if (Modifier.isInterface(clazz.getModifiers())) {
                writer.visitMethodInsn(Opcodes.INVOKESTATIC, type.getInternalName(), name,
                        getMethodType().toMethodDescriptorString(), true);
            } else {
                writer.invokeStatic(type, method);
            }
        } else if (Modifier.isInterface(clazz.getModifiers())) {
            writer.invokeInterface(type, method);
        } else {
            writer.invokeVirtual(type, method);
        }
    }
}