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

Tue, 22 Jan 2013 14:14:37 +0100

author
hannesw
date
Tue, 22 Jan 2013 14:14:37 +0100
changeset 42
935dcec38e9a
parent 23
2a5c2258827b
child 51
f52d7294536f
permissions
-rw-r--r--

8006570: This-value for non-strict functions should be converted to object
Reviewed-by: jlaskey, lagergren, attila

     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.runtime.linker.Lookup.MH;
    30 import java.lang.invoke.MethodHandle;
    31 import java.lang.invoke.MethodHandles;
    33 import jdk.nashorn.internal.runtime.ConsString;
    34 import jdk.nashorn.internal.runtime.Context;
    35 import jdk.nashorn.internal.runtime.GlobalObject;
    36 import jdk.nashorn.internal.runtime.ScriptFunction;
    37 import jdk.nashorn.internal.runtime.ScriptObject;
    38 import jdk.nashorn.internal.runtime.Undefined;
    39 import org.dynalang.dynalink.CallSiteDescriptor;
    40 import org.dynalang.dynalink.linker.ConversionComparator;
    41 import org.dynalang.dynalink.linker.GuardedInvocation;
    42 import org.dynalang.dynalink.linker.GuardingTypeConverterFactory;
    43 import org.dynalang.dynalink.linker.LinkRequest;
    44 import org.dynalang.dynalink.linker.LinkerServices;
    45 import org.dynalang.dynalink.linker.TypeBasedGuardingDynamicLinker;
    46 import org.dynalang.dynalink.support.Guards;
    48 /**
    49  * This is the main dynamic linker for Nashorn. It is used for linking all {@link ScriptObject} and its subclasses (this
    50  * includes {@link ScriptFunction} and its subclasses) as well as {@link Undefined}. This linker is exported to other
    51  * language runtimes by being declared in {@code META-INF/services/org.dynalang.dynalink.linker.GuardingDynamicLinker}
    52  * file of Nashorn's distribution.
    53  */
    54 public class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory, ConversionComparator {
    55     /**
    56      * Returns true if {@code ScriptObject} is assignable from {@code type}, or it is {@code Undefined}.
    57      */
    58     @Override
    59     public boolean canLinkType(final Class<?> type) {
    60         return canLinkTypeStatic(type);
    61     }
    63     static boolean canLinkTypeStatic(final Class<?> type) {
    64         return ScriptObject.class.isAssignableFrom(type) || Undefined.class == type;
    65     }
    67     @Override
    68     public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception {
    69         final LinkRequest requestWithoutContext = request.withoutRuntimeContext(); // Nashorn has no runtime context
    70         final Object self = requestWithoutContext.getReceiver();
    71         final CallSiteDescriptor desc = requestWithoutContext.getCallSiteDescriptor();
    73         if (desc.getNameTokenCount() < 2 || !"dyn".equals(desc.getNameToken(CallSiteDescriptor.SCHEME))) {
    74             // We only support standard "dyn:*[:*]" operations
    75             return null;
    76         }
    78         GuardedInvocation inv;
    79         if (self instanceof ScriptObject) {
    80             inv = ((ScriptObject)self).lookup(desc, request.isCallSiteUnstable());
    82             if (self instanceof ScriptFunction && "call".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
    84                 // Add toObject wrapper for non-object self argument to non-strict functions
    85                 assert request.getArguments().length >= 2 : "No self argument in script function callsite";
    86                 Object thisObject = request.getArguments()[1];
    87                 // Add wrapper filter for primitive this-object on non-strict functions
    88                 if (NashornGuardedInvocation.isNonStrict(inv) && isPrimitive(thisObject)) {
    89                     MethodHandle wrapFilter = ((GlobalObject) Context.getGlobal()).getWrapFilter(thisObject);
    90                     MethodHandle invocation = MH.filterArguments(inv.getInvocation(), 1,
    91                             MH.asType(wrapFilter, wrapFilter.type().changeReturnType(Object.class)));
    92                     inv = new GuardedInvocation(invocation, inv.getSwitchPoint(), inv.getGuard());
    93                 }
    94             }
    95         } else if (self instanceof Undefined) {
    96             inv = Undefined.lookup(desc);
    97         } else {
    98             throw new AssertionError(); // Should never reach here.
    99         }
   101         return Bootstrap.asType(inv, linkerServices, desc);
   102     }
   104     @Override
   105     public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
   106         final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType);
   107         return gi == null ? null : gi.asType(MH.type(targetType, sourceType));
   108     }
   110     /**
   111      * Main part of the implementation of {@link GuardingTypeConverterFactory#convertToType(Class, Class)} that doesn't
   112      * care about adapting the method signature; that's done by the invoking method. Returns either a built-in
   113      * conversion to primitive (or primitive wrapper) Java types or to String, or a just-in-time generated converter to
   114      * a SAM type (if the target type is a SAM type).
   115      * @param sourceType the source type
   116      * @param targetType the target type
   117      * @return a guarded invocation that converts from the source type to the target type.
   118      * @throws Exception if something goes wrong
   119      */
   120     private static GuardedInvocation convertToTypeNoCast(final Class<?> sourceType, final Class<?> targetType) throws Exception {
   121         final MethodHandle mh = JavaArgumentConverters.getConverter(targetType);
   122         if (mh != null) {
   123             return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : IS_NASHORN_OR_UNDEFINED_TYPE);
   124         }
   125         return getSamTypeConverter(sourceType, targetType);
   126     }
   128     /**
   129      * Returns a guarded invocation that converts from a source type that is ScriptFunction, or a subclass or a
   130      * superclass of it) to a SAM type.
   131      * @param sourceType the source type (presumably ScriptFunction or a subclass or a superclass of it)
   132      * @param targetType the target type (presumably a SAM type)
   133      * @return a guarded invocation that converts from the source type to the target SAM type. null is returned if
   134      * either the source type is neither ScriptFunction, nor a subclass, nor a superclass of it, or if the target type
   135      * is not a SAM type.
   136      * @throws Exception if something goes wrong; generally, if there's an issue with creation of the SAM proxy type
   137      * constructor.
   138      */
   139     private static GuardedInvocation getSamTypeConverter(final Class<?> sourceType, final Class<?> targetType) throws Exception {
   140         // If source type is more generic than ScriptFunction class, we'll need to use a guard
   141         final boolean isSourceTypeGeneric = sourceType.isAssignableFrom(ScriptFunction.class);
   143         if ((isSourceTypeGeneric || ScriptFunction.class.isAssignableFrom(sourceType)) && isAutoConvertibleFromFunction(targetType)) {
   144             final MethodHandle ctor = JavaAdapterFactory.getConstructor(ScriptFunction.class, targetType);
   145             assert ctor != null; // if isAutoConvertibleFromFunction() returned true, then ctor must exist.
   146             return new GuardedInvocation(ctor, isSourceTypeGeneric ? IS_SCRIPT_FUNCTION : null);
   147         }
   148         return null;
   149     }
   151     private static boolean isAutoConvertibleFromFunction(final Class<?> clazz) {
   152         return JavaAdapterFactory.isAbstractClass(clazz) && !ScriptObject.class.isAssignableFrom(clazz) &&
   153                 JavaAdapterFactory.isAutoConvertibleFromFunction(clazz);
   154     }
   156     private static boolean isPrimitive(Object obj) {
   157         return obj instanceof Boolean ||
   158                obj instanceof Number ||
   159                obj instanceof String ||
   160                obj instanceof ConsString;
   161     }
   163     @Override
   164     public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
   165         if(ScriptObject.class.isAssignableFrom(sourceType)) {
   166             if(targetType1.isInterface()) {
   167                 if(!targetType2.isInterface()) {
   168                     return Comparison.TYPE_1_BETTER;
   169                 }
   170             } else if(targetType2.isInterface()) {
   171                 return Comparison.TYPE_2_BETTER;
   172             }
   173         }
   174         return Comparison.INDETERMINATE;
   175     }
   177     private static final MethodHandle IS_SCRIPT_FUNCTION = Guards.isInstance(ScriptFunction.class, MH.type(Boolean.TYPE, Object.class));
   179     private static final MethodHandle IS_NASHORN_OR_UNDEFINED_TYPE = findOwnMH("isNashornTypeOrUndefined",
   180             Boolean.TYPE, Object.class);
   182     @SuppressWarnings("unused")
   183     private static boolean isNashornTypeOrUndefined(final Object obj) {
   184         return obj instanceof ScriptObject || obj instanceof Undefined;
   185     }
   187     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
   188         return MH.findStatic(MethodHandles.lookup(), NashornLinker.class, name, MH.type(rtype, types));
   189     }
   190 }

mercurial