src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1721
bfc671539e50
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

Merge

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.runtime.linker;
    28 import static jdk.nashorn.internal.lookup.Lookup.MH;
    30 import java.lang.invoke.MethodHandle;
    31 import java.lang.invoke.MethodHandles;
    32 import jdk.internal.dynalink.linker.ConversionComparator;
    33 import jdk.internal.dynalink.linker.GuardedInvocation;
    34 import jdk.internal.dynalink.linker.GuardedTypeConversion;
    35 import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
    36 import jdk.internal.dynalink.linker.LinkRequest;
    37 import jdk.internal.dynalink.linker.LinkerServices;
    38 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
    39 import jdk.internal.dynalink.support.TypeUtilities;
    40 import jdk.nashorn.internal.objects.Global;
    41 import jdk.nashorn.internal.runtime.ConsString;
    42 import jdk.nashorn.internal.runtime.JSType;
    43 import jdk.nashorn.internal.runtime.ScriptRuntime;
    45 /**
    46  * Internal linker for String, Boolean, and Number objects, only ever used by Nashorn engine and not exposed to other
    47  * engines. It is used for treatment of strings, boolean, and numbers as JavaScript primitives. Also provides ECMAScript
    48  * primitive type conversions for these types when linking to Java methods.
    49  */
    50 final class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator {
    51     private static final GuardedTypeConversion VOID_TO_OBJECT = new GuardedTypeConversion(
    52             new GuardedInvocation(MethodHandles.constant(Object.class, ScriptRuntime.UNDEFINED)), true);
    54     @Override
    55     public boolean canLinkType(final Class<?> type) {
    56         return canLinkTypeStatic(type);
    57     }
    59     private static boolean canLinkTypeStatic(final Class<?> type) {
    60         return type == String.class || type == Boolean.class || type == ConsString.class || type == Integer.class
    61                 || type == Double.class || type == Float.class || type == Short.class || type == Byte.class;
    62     }
    64     @Override
    65     public GuardedInvocation getGuardedInvocation(final LinkRequest origRequest, final LinkerServices linkerServices)
    66             throws Exception {
    67         final LinkRequest request = origRequest.withoutRuntimeContext(); // Nashorn has no runtime context
    69         final Object self = request.getReceiver();
    70         final NashornCallSiteDescriptor desc = (NashornCallSiteDescriptor) request.getCallSiteDescriptor();
    72         return Bootstrap.asTypeSafeReturn(Global.primitiveLookup(request, self), linkerServices, desc);
    73     }
    75     /**
    76      * This implementation of type converter factory will pretty much allow implicit conversions of anything to anything
    77      * else that's allowed among JavaScript primitive types (string to number, boolean to string, etc.)
    78      * @param sourceType the type to convert from
    79      * @param targetType the type to convert to
    80      * @return a conditional converter from source to target type
    81      */
    82     @Override
    83     public GuardedTypeConversion convertToType(final Class<?> sourceType, final Class<?> targetType) {
    84         final MethodHandle mh = JavaArgumentConverters.getConverter(targetType);
    85         if (mh == null) {
    86             if(targetType == Object.class && sourceType == void.class) {
    87                 return VOID_TO_OBJECT;
    88             }
    89             return null;
    90         }
    92         return new GuardedTypeConversion(new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : GUARD_PRIMITIVE).asType(mh.type().changeParameterType(0, sourceType)), true);
    93     }
    95     /**
    96      * Implements the somewhat involved prioritization of JavaScript primitive types conversions. Instead of explaining
    97      * it here in prose, just follow the source code comments.
    98      * @param sourceType the source type to convert from
    99      * @param targetType1 one candidate target type
   100      * @param targetType2 another candidate target type
   101      * @return one of {@link jdk.internal.dynalink.linker.ConversionComparator.Comparison} values signifying which
   102      * target type should be favored for conversion.
   103      */
   104     @Override
   105     public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
   106         final Class<?> wrapper1 = getWrapperTypeOrSelf(targetType1);
   107         if (sourceType == wrapper1) {
   108             // Source type exactly matches target 1
   109             return Comparison.TYPE_1_BETTER;
   110         }
   111         final Class<?> wrapper2 = getWrapperTypeOrSelf(targetType2);
   112         if (sourceType == wrapper2) {
   113             // Source type exactly matches target 2
   114             return Comparison.TYPE_2_BETTER;
   115         }
   117         if (Number.class.isAssignableFrom(sourceType)) {
   118             // If exactly one of the targets is a number, pick it.
   119             if (Number.class.isAssignableFrom(wrapper1)) {
   120                 if (!Number.class.isAssignableFrom(wrapper2)) {
   121                     return Comparison.TYPE_1_BETTER;
   122                 }
   123             } else if (Number.class.isAssignableFrom(wrapper2)) {
   124                 return Comparison.TYPE_2_BETTER;
   125             }
   127             // If exactly one of the targets is a character, pick it. Numbers can be reasonably converted to chars using
   128             // the UTF-16 values.
   129             if (Character.class == wrapper1) {
   130                 return Comparison.TYPE_1_BETTER;
   131             } else if (Character.class == wrapper2) {
   132                 return Comparison.TYPE_2_BETTER;
   133             }
   135             // For all other cases, we fall through to the next if statement - not that we repeat the condition in it
   136             // too so if we entered this branch, we'll enter the below if statement too.
   137         }
   139         if (sourceType == String.class || sourceType == Boolean.class || Number.class.isAssignableFrom(sourceType)) {
   140             // Treat wrappers as primitives.
   141             final Class<?> primitiveType1 = getPrimitiveTypeOrSelf(targetType1);
   142             final Class<?> primitiveType2 = getPrimitiveTypeOrSelf(targetType2);
   143             // Basically, choose the widest possible primitive type. (First "if" returning TYPE_2_BETTER is correct;
   144             // when faced with a choice between double and int, choose double).
   145             if (TypeUtilities.isMethodInvocationConvertible(primitiveType1, primitiveType2)) {
   146                 return Comparison.TYPE_2_BETTER;
   147             } else if (TypeUtilities.isMethodInvocationConvertible(primitiveType2, primitiveType1)) {
   148                 return Comparison.TYPE_1_BETTER;
   149             }
   150             // Ok, at this point we're out of possible number conversions, so try strings. A String can represent any
   151             // value without loss, so if one of the potential targets is string, go for it.
   152             if (targetType1 == String.class) {
   153                 return Comparison.TYPE_1_BETTER;
   154             }
   155             if (targetType2 == String.class) {
   156                 return Comparison.TYPE_2_BETTER;
   157             }
   158         }
   160         return Comparison.INDETERMINATE;
   161     }
   163     private static Class<?> getPrimitiveTypeOrSelf(final Class<?> type) {
   164         final Class<?> primitive = TypeUtilities.getPrimitiveType(type);
   165         return primitive == null ? type : primitive;
   166     }
   168     private static Class<?> getWrapperTypeOrSelf(final Class<?> type) {
   169         final Class<?> wrapper = TypeUtilities.getWrapperType(type);
   170         return wrapper == null ? type : wrapper;
   171     }
   173     @SuppressWarnings("unused")
   174     private static boolean isJavaScriptPrimitive(final Object o) {
   175         return JSType.isString(o) || o instanceof Boolean || JSType.isNumber(o) || o == null;
   176     }
   178     private static final MethodHandle GUARD_PRIMITIVE = findOwnMH("isJavaScriptPrimitive", boolean.class, Object.class);
   180     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
   181         return MH.findStatic(MethodHandles.lookup(), NashornPrimitiveLinker.class, name, MH.type(rtype, types));
   182     }
   183 }

mercurial