Package

Source Code of RT$BinaryOps

/***
* Copyright (c) 2000-2011 INRIA, France Telecom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
*    contributors may be used to endorse or promote products derived from
*    this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.MethodHandles.Lookup;
import java.util.HashMap;

/**
* There are 3 bootstrap methods: - one for the constant that are initialized
* once the first time the bootstrap method is called and after always reuse the
* same constant. It's almost equivalent to an LDC but here the constant are
* stored in boxed form e.g a java.lang.Integer containing 0 instead of an int
* containing 0.
*
* - one for the unary operation 'not' and 'asBoolean', here the semantics is
* hard coded, all primitive value are transformed as object by applying this
* operation: (v == 0)? false: true
*
* - one for the binary operation 'add', 'mul' and 'gt', here the semantics can
* be changed by adding more static methods in {@link BinaryOps}. This bootstrap
* method is a little more complex because it creates an inlining cache to avoid
* to recompute the binary method to call if the type of the two arguments
* doesn't change. Also, if the expression is used a lot and trigger the JIT, it
* will be able to inline the code of the operation directly at callsite.
*
* @author Remi Forax
*/
public class RT {
    /**
     * bootstrap method for constant
     */
    public static CallSite cst(
        Lookup lookup,
        String name,
        MethodType type,
        Object constant)
    {
        return new ConstantCallSite(MethodHandles.constant(Object.class,
                constant));
    }

    /**
     * bootstrap method for unary operation 'asBoolean' and 'not'
     */
    public static CallSite unary(Lookup lookup, String name, MethodType type) {
        MethodHandle target;
        if (name.equals("asBoolean")) {
            target = MethodHandles.explicitCastArguments(MethodHandles.identity(Object.class),
                    MethodType.methodType(boolean.class, Object.class));
        } else { // "not"
            target = MethodHandles.explicitCastArguments(NOT,
                    MethodType.methodType(Object.class, Object.class));
        }
        return new ConstantCallSite(target);
    }

    /**
     * bootstrap method for binary operation 'add', 'mul' and 'gt'
     *
     * This bootstrap method doesn't install the target method handle directly,
     * because we want to install an inlining cache and we can't create an
     * inlining cache without knowing the class of the arguments. So this method
     * first installs a method handle that will call
     * {@link BinaryOpCallSite#fallback(Object, Object)} and the fallback method
     * will be called with the arguments and thus can install the inlining
     * cache. Also, the fallback has to be bound to a specific callsite to be
     * able to change its target after the first call, this part is done in the
     * constructor of {@link BinaryOpCallSite}.
     */
    public static CallSite binary(Lookup lookup, String name, MethodType type) {
        BinaryOpCallSite callSite = new BinaryOpCallSite(name, type);
        callSite.setTarget(callSite.fallback);
        return callSite;
    }

    /**
     * Garbage class containing the method used to apply 'not' on a boolean. See
     * {@link RT#unary(Lookup, String, MethodType)}
     */
    public static class UnayOps {
       
        public static Object not(boolean b) {
            return !b;
        }
    }

    private static final MethodHandle NOT;
   
    static {
        try {
            NOT = MethodHandles.publicLookup().findStatic(UnayOps.class,
                    "not",
                    MethodType.methodType(Object.class, boolean.class));
        } catch (ReflectiveOperationException e) {
            throw new LinkageError(e.getMessage(), e);
        }
    }

    /**
     * A specific callsite that will install an 'inlining cache'. Because we
     * don't know until runtime which method handle to call, the lookup
     * depending on the dynamic type of the argument will be done at runtime
     * when the method {@link #fallback(Object, Object)} is called. To avoid to
     * do this dynamic lookup at each call, the fallback install two guards in
     * front of dispatch call that will check if the arguments class change or
     * not. If the arguments class don't change, the previously computed method
     * handle will be called again. Otherwise, a new method handle will be
     * computed and two new guards will be installed.
     */
    static class BinaryOpCallSite extends MutableCallSite {
       
        private final String opName;
       
        final MethodHandle fallback;

        public BinaryOpCallSite(String opName, MethodType type) {
            super(type);
            this.opName = opName;
            this.fallback = FALLBACK.bindTo(this);
        }

        Object fallback(Object v1, Object v2) throws Throwable {
            // when you debug with this message
            // don't forget that && and || are lazy !!
            // System.out.println("fallback called with "+opName+'('+v1.getClass()+','+v2.getClass()+')');

            Class< ? extends Object> class1 = v1.getClass();
            Class< ? extends Object> class2 = v2.getClass();
            MethodHandle op = lookupBinaryOp(opName, class1, class2);

            // convert arguments
            MethodType type = type();
            MethodType opType = op.type();
            if (opType.parameterType(0) == String.class) {
                if (opType.parameterType(1) == String.class) {
                    op = MethodHandles.filterArguments(op,
                            0,
                            TO_STRING,
                            TO_STRING);
                } else {
                    op = MethodHandles.filterArguments(op, 0, TO_STRING);
                    op = MethodHandles.explicitCastArguments(op, type);
                }
            } else {
                if (opType.parameterType(1) == String.class) {
                    op = MethodHandles.filterArguments(op, 1, TO_STRING);
                }
                op = MethodHandles.explicitCastArguments(op, type);
            }

            // prepare guard
            MethodHandle guard = MethodHandles.guardWithTest(TEST1.bindTo(class1),
                    MethodHandles.guardWithTest(TEST2.bindTo(class2),
                            op,
                            fallback),
                    fallback);

            // install the inlining cache
            setTarget(guard);
            return op.invokeWithArguments(v1, v2);
        }

        public static boolean test1(Class< ? > v1Class, Object v1, Object v2) {
            return v1.getClass() == v1Class;
        }

        public static boolean test2(Class< ? > v2Class, Object v1, Object v2) {
            return v2.getClass() == v2Class;
        }

        private static final MethodHandle TO_STRING;
       
        private static final MethodHandle TEST1;
       
        private static final MethodHandle TEST2;
       
        private static final MethodHandle FALLBACK;
       
        static {
            Lookup lookup = MethodHandles.lookup();
            try {
                TO_STRING = lookup.findVirtual(Object.class,
                        "toString",
                        MethodType.methodType(String.class));
                MethodType testType = MethodType.methodType(boolean.class,
                        Class.class,
                        Object.class,
                        Object.class);
                TEST1 = lookup.findStatic(BinaryOpCallSite.class,
                        "test1",
                        testType);
                TEST2 = lookup.findStatic(BinaryOpCallSite.class,
                        "test2",
                        testType);
                FALLBACK = lookup.findVirtual(BinaryOpCallSite.class,
                        "fallback",
                        MethodType.genericMethodType(2));
            } catch (ReflectiveOperationException e) {
                throw new LinkageError(e.getMessage(), e);
            }
        }
    }

    /**
     * Garbage class that contains the raw operations used for binary
     * operations. All methods must be static returns an Object and takes the
     * same type for the two parameter types.
     *
     * See {@link RT#lookupBinaryOp(String, Class, Class)} for more info.
     */
    public static class BinaryOps {
       
        public static Object add(int v1, int v2) {
            return v1 + v2;
        }

        public static Object add(double v1, double v2) {
            return v1 + v2;
        }

        public static Object add(String v1, String v2) {
            return v1 + v2;
        }

        public static Object mul(int v1, int v2) {
            return v1 * v2;
        }

        public static Object mul(double v1, double v2) {
            return v1 * v2;
        }

        public static Object gt(int v1, int v2) {
            return v1 > v2;
        }

        public static Object gt(double v1, double v2) {
            return v1 > v2;
        }

        public static Object gt(String v1, String v2) {
            return v1.compareTo(v2) > 0;
        }
    }

    /**
     * Select a most specific method among the ones defined in
     * {@link RT.BinaryOps}. The algorithm first find the most specific subtype
     * between class1 and class2. The order of the types is defined in
     * {@link RT#RANK_MAP}: Boolean < Byte < Short < Character < Integer < Long
     * < Float < Double < String then the algorithm lookup in
     * {@link RT.BinaryOps} to find a method with the name opName taking as
     * argument the primitive corresponding to the most specific subtype. If no
     * such method exist, the algorithm retry but looking for a method with a
     * more specific type (using the same order). The result of the lookup is
     * cached in {@link RT#BINARY_CACHE} to avoid to avoid to do a lookup (a
     * reflective call) on the same method twice.
     */
    static MethodHandle lookupBinaryOp(
        String opName,
        Class< ? > class1,
        Class< ? > class2)
    {
        int rank = Math.max(RANK_MAP.get(class1), RANK_MAP.get(class2));
        String mangledName = opName + rank;
        MethodHandle mh = BINARY_CACHE.get(mangledName);
        if (mh != null) {
            return mh;
        }

        for (; rank < PRIMITIVE_ARRAY.length;) {
            Class< ? > primitive = PRIMITIVE_ARRAY[rank];
            try {
                mh = MethodHandles.publicLookup().findStatic(BinaryOps.class,
                        opName,
                        MethodType.methodType(Object.class,
                                primitive,
                                primitive));
            } catch (NoSuchMethodException e) {
                rank = rank + 1;
                continue;
            } catch (IllegalAccessException e) {
                throw new LinkageError(e.getMessage(), e);
            }

            BINARY_CACHE.put(mangledName, mh);
            return mh;
        }
        throw new LinkageError("unknown operation " + opName + " ("
                + class1.getName() + ',' + class2.getName() + ')');
    }

    private static final HashMap<Class< ? >, Integer> RANK_MAP;
   
    private static final Class< ? >[] PRIMITIVE_ARRAY;
   
    private static final HashMap<String, MethodHandle> BINARY_CACHE;
   
    static {
        Class< ? >[] primitives = new Class< ? >[] {
            boolean.class,
            byte.class,
            short.class,
            char.class,
            int.class,
            long.class,
            float.class,
            double.class,
            String.class };
        Class< ? >[] wrappers = new Class< ? >[] {
            Boolean.class,
            Byte.class,
            Short.class,
            Character.class,
            Integer.class,
            Long.class,
            Float.class,
            Double.class,
            String.class };
        HashMap<Class< ? >, Integer> rankMap = new HashMap<Class< ? >, Integer>();
        for (int i = 0; i < wrappers.length; i++) {
            rankMap.put(wrappers[i], i);
        }

        RANK_MAP = rankMap;
        PRIMITIVE_ARRAY = primitives;
        BINARY_CACHE = new HashMap<String, MethodHandle>();
    }
}
TOP

Related Classes of RT$BinaryOps

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.