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

Fri, 05 Jun 2015 14:46:52 +0530

author
sundar
date
Fri, 05 Jun 2015 14:46:52 +0530
changeset 1397
19263eb2ff0c
parent 1239
e1146c9cc758
child 1521
eed10d5bf2f4
permissions
-rw-r--r--

8081809: Missing final modifier in method parameters (nashorn code convention)
Reviewed-by: attila, lagergren

     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;
    29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    31 import java.lang.invoke.MethodHandle;
    32 import java.lang.invoke.MethodHandles;
    33 import java.lang.invoke.MethodType;
    34 import java.lang.reflect.Method;
    35 import java.lang.reflect.Modifier;
    36 import jdk.internal.dynalink.CallSiteDescriptor;
    37 import jdk.internal.dynalink.beans.BeansLinker;
    38 import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
    39 import jdk.internal.dynalink.linker.GuardedInvocation;
    40 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
    41 import jdk.internal.dynalink.linker.LinkRequest;
    42 import jdk.internal.dynalink.linker.LinkerServices;
    43 import jdk.internal.dynalink.linker.MethodHandleTransformer;
    44 import jdk.internal.dynalink.support.DefaultInternalObjectFilter;
    45 import jdk.internal.dynalink.support.Guards;
    46 import jdk.internal.dynalink.support.Lookup;
    47 import jdk.nashorn.api.scripting.ScriptUtils;
    48 import jdk.nashorn.internal.runtime.ConsString;
    49 import jdk.nashorn.internal.runtime.Context;
    50 import jdk.nashorn.internal.runtime.ScriptObject;
    51 import jdk.nashorn.internal.runtime.ScriptRuntime;
    52 import jdk.nashorn.internal.runtime.options.Options;
    54 /**
    55  * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified
    56  * {@code compareConversion} method that favors conversion of {@link ConsString} to either {@link String} or
    57  * {@link CharSequence}. It also provides a {@link #createHiddenObjectFilter()} method for use with bootstrap that will
    58  * ensure that we never pass internal engine objects that should not be externally observable (currently ConsString and
    59  * ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add this functionality as
    60  * custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when
    61  * the target method handle parameter signature is {@code Object}. This linker also makes sure that primitive
    62  * {@link String} operations can be invoked on a {@link ConsString}, and allows invocation of objects implementing
    63  * the {@link FunctionalInterface} attribute.
    64  */
    65 public class NashornBeansLinker implements GuardingDynamicLinker {
    66     // System property to control whether to wrap ScriptObject->ScriptObjectMirror for
    67     // Object type arguments of Java method calls, field set and array set.
    68     private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
    70     private static final MethodHandle EXPORT_ARGUMENT;
    71     private static final MethodHandle IMPORT_RESULT;
    72     private static final MethodHandle FILTER_CONSSTRING;
    74     static {
    75         final Lookup lookup  = new Lookup(MethodHandles.lookup());
    76         EXPORT_ARGUMENT      = lookup.findOwnStatic("exportArgument", Object.class, Object.class);
    77         IMPORT_RESULT        = lookup.findOwnStatic("importResult", Object.class, Object.class);
    78         FILTER_CONSSTRING    = lookup.findOwnStatic("consStringFilter", Object.class, Object.class);
    79     }
    81     // cache of @FunctionalInterface method of implementor classes
    82     private static final ClassValue<Method> FUNCTIONAL_IFACE_METHOD = new ClassValue<Method>() {
    83         @Override
    84         protected Method computeValue(final Class<?> type) {
    85             return findFunctionalInterfaceMethod(type);
    86         }
    87     };
    89     private final BeansLinker beansLinker = new BeansLinker();
    91     @Override
    92     public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
    93         final Object self = linkRequest.getReceiver();
    94         final CallSiteDescriptor desc = linkRequest.getCallSiteDescriptor();
    95         if (self instanceof ConsString) {
    96             // In order to treat ConsString like a java.lang.String we need a link request with a string receiver.
    97             final Object[] arguments = linkRequest.getArguments();
    98             arguments[0] = "";
    99             final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(desc, arguments);
   100             final GuardedInvocation invocation = getGuardedInvocation(beansLinker, forgedLinkRequest, linkerServices);
   101             // If an invocation is found we add a filter that makes it work for both Strings and ConsStrings.
   102             return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING);
   103         }
   105         if (self != null && "call".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
   106             // Support dyn:call on any object that supports some @FunctionalInterface
   107             // annotated interface. This way Java method, constructor references or
   108             // implementations of java.util.function.* interfaces can be called as though
   109             // those are script functions.
   110             final Method m = getFunctionalInterfaceMethod(self.getClass());
   111             if (m != null) {
   112                 final MethodType callType = desc.getMethodType();
   113                 // 'callee' and 'thiz' passed from script + actual arguments
   114                 if (callType.parameterCount() != m.getParameterCount() + 2) {
   115                     throw typeError("no.method.matches.args", ScriptRuntime.safeToString(self));
   116                 }
   117                 return new GuardedInvocation(
   118                         // drop 'thiz' passed from the script.
   119                         MH.dropArguments(linkerServices.filterInternalObjects(desc.getLookup().unreflect(m)), 1,
   120                                 callType.parameterType(1)), Guards.getInstanceOfGuard(
   121                                         m.getDeclaringClass())).asTypeSafeReturn(
   122                                                 new NashornBeansLinkerServices(linkerServices), callType);
   123             }
   124         }
   125         return getGuardedInvocation(beansLinker, linkRequest, linkerServices);
   126     }
   128     /**
   129      * Delegates to the specified linker but injects its linker services wrapper so that it will apply all special
   130      * conversions that this class does.
   131      * @param delegateLinker the linker to which the actual work is delegated to.
   132      * @param linkRequest the delegated link request
   133      * @param linkerServices the original link services that will be augmented with special conversions
   134      * @return the guarded invocation from the delegate, possibly augmented with special conversions
   135      * @throws Exception if the delegate throws an exception
   136      */
   137     public static GuardedInvocation getGuardedInvocation(final GuardingDynamicLinker delegateLinker, final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
   138         return delegateLinker.getGuardedInvocation(linkRequest, new NashornBeansLinkerServices(linkerServices));
   139     }
   141     @SuppressWarnings("unused")
   142     private static Object exportArgument(final Object arg) {
   143         return exportArgument(arg, MIRROR_ALWAYS);
   144     }
   146     static Object exportArgument(final Object arg, final boolean mirrorAlways) {
   147         if (arg instanceof ConsString) {
   148             return arg.toString();
   149         } else if (mirrorAlways && arg instanceof ScriptObject) {
   150             return ScriptUtils.wrap((ScriptObject)arg);
   151         } else {
   152             return arg;
   153         }
   154     }
   156     @SuppressWarnings("unused")
   157     private static Object importResult(final Object arg) {
   158         return ScriptUtils.unwrap(arg);
   159     }
   161     @SuppressWarnings("unused")
   162     private static Object consStringFilter(final Object arg) {
   163         return arg instanceof ConsString ? arg.toString() : arg;
   164     }
   166     private static Method findFunctionalInterfaceMethod(final Class<?> clazz) {
   167         if (clazz == null) {
   168             return null;
   169         }
   171         for (final Class<?> iface : clazz.getInterfaces()) {
   172             // check accessiblity up-front
   173             if (! Context.isAccessibleClass(iface)) {
   174                 continue;
   175             }
   177             // check for @FunctionalInterface
   178             if (iface.isAnnotationPresent(FunctionalInterface.class)) {
   179                 // return the first abstract method
   180                 for (final Method m : iface.getMethods()) {
   181                     if (Modifier.isAbstract(m.getModifiers())) {
   182                         return m;
   183                     }
   184                 }
   185             }
   186         }
   188         // did not find here, try super class
   189         return findFunctionalInterfaceMethod(clazz.getSuperclass());
   190     }
   192     // Returns @FunctionalInterface annotated interface's single abstract
   193     // method. If not found, returns null.
   194     static Method getFunctionalInterfaceMethod(final Class<?> clazz) {
   195         return FUNCTIONAL_IFACE_METHOD.get(clazz);
   196     }
   198     static MethodHandleTransformer createHiddenObjectFilter() {
   199         return new DefaultInternalObjectFilter(EXPORT_ARGUMENT, MIRROR_ALWAYS ? IMPORT_RESULT : null);
   200     }
   202     private static class NashornBeansLinkerServices implements LinkerServices {
   203         private final LinkerServices linkerServices;
   205         NashornBeansLinkerServices(final LinkerServices linkerServices) {
   206             this.linkerServices = linkerServices;
   207         }
   209         @Override
   210         public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
   211             return linkerServices.asType(handle, fromType);
   212         }
   214         @Override
   215         public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) {
   216             return Implementation.asTypeLosslessReturn(this, handle, fromType);
   217         }
   219         @Override
   220         public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
   221             return linkerServices.getTypeConverter(sourceType, targetType);
   222         }
   224         @Override
   225         public boolean canConvert(final Class<?> from, final Class<?> to) {
   226             return linkerServices.canConvert(from, to);
   227         }
   229         @Override
   230         public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest) throws Exception {
   231             return linkerServices.getGuardedInvocation(linkRequest);
   232         }
   234         @Override
   235         public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
   236             if (sourceType == ConsString.class) {
   237                 if (String.class == targetType1 || CharSequence.class == targetType1) {
   238                     return Comparison.TYPE_1_BETTER;
   239                 }
   241                 if (String.class == targetType2 || CharSequence.class == targetType2) {
   242                     return Comparison.TYPE_2_BETTER;
   243                 }
   244             }
   245             return linkerServices.compareConversion(sourceType, targetType1, targetType2);
   246         }
   248         @Override
   249         public MethodHandle filterInternalObjects(final MethodHandle target) {
   250             return linkerServices.filterInternalObjects(target);
   251         }
   252     }
   253 }

mercurial