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

changeset 1053
a35c8136c045
parent 1017
e83ceda86582
child 1088
b49b6786afad
     1.1 --- a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java	Mon Oct 13 20:10:14 2014 +0200
     1.2 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java	Tue Oct 14 16:16:12 2014 +0530
     1.3 @@ -35,17 +35,28 @@
     1.4  import jdk.internal.dynalink.linker.LinkRequest;
     1.5  import jdk.internal.dynalink.linker.LinkerServices;
     1.6  import jdk.internal.dynalink.support.Lookup;
     1.7 +import jdk.nashorn.api.scripting.ScriptUtils;
     1.8 +import jdk.nashorn.internal.objects.NativeArray;
     1.9  import jdk.nashorn.internal.runtime.ConsString;
    1.10 +import jdk.nashorn.internal.runtime.ScriptObject;
    1.11 +import jdk.nashorn.internal.runtime.options.Options;
    1.12  
    1.13  /**
    1.14   * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified
    1.15   * {@code asType} method that will ensure that we never pass internal engine objects that should not be externally
    1.16 - * observable (currently only ConsString) to Java APIs, but rather that we flatten it into a String. We can't just add
    1.17 + * observable (currently ConsString and ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add
    1.18   * this functionality as custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when
    1.19   * the target method handle parameter signature is {@code Object}.
    1.20   */
    1.21  public class NashornBeansLinker implements GuardingDynamicLinker {
    1.22 +    // System property to control whether to wrap ScriptObject->ScriptObjectMirror for
    1.23 +    // Object type arguments of Java method calls, field set and array set.
    1.24 +    private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true);
    1.25 +
    1.26      private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class);
    1.27 +    private static final MethodHandle EXPORT_NATIVE_ARRAY = new Lookup(MethodHandles.lookup()).findOwnStatic("exportNativeArray", Object.class, NativeArray.class);
    1.28 +    private static final MethodHandle EXPORT_SCRIPT_OBJECT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportScriptObject", Object.class, ScriptObject.class);
    1.29 +    private static final MethodHandle IMPORT_RESULT = new Lookup(MethodHandles.lookup()).findOwnStatic("importResult", Object.class, Object.class);
    1.30  
    1.31      private final BeansLinker beansLinker = new BeansLinker();
    1.32  
    1.33 @@ -67,8 +78,39 @@
    1.34          return delegateLinker.getGuardedInvocation(linkRequest, new NashornBeansLinkerServices(linkerServices));
    1.35      }
    1.36  
    1.37 -    static Object exportArgument(final Object arg) {
    1.38 -        return arg instanceof ConsString ? arg.toString() : arg;
    1.39 +    @SuppressWarnings("unused")
    1.40 +    private static Object exportArgument(final Object arg) {
    1.41 +        return exportArgument(arg, MIRROR_ALWAYS);
    1.42 +    }
    1.43 +
    1.44 +    @SuppressWarnings("unused")
    1.45 +    private static Object exportNativeArray(final NativeArray arg) {
    1.46 +        return exportArgument(arg, MIRROR_ALWAYS);
    1.47 +    }
    1.48 +
    1.49 +    @SuppressWarnings("unused")
    1.50 +    private static Object exportScriptObject(final ScriptObject arg) {
    1.51 +        return exportArgument(arg, MIRROR_ALWAYS);
    1.52 +    }
    1.53 +
    1.54 +    @SuppressWarnings("unused")
    1.55 +    private static Object exportScriptArray(final NativeArray arg) {
    1.56 +        return exportArgument(arg, MIRROR_ALWAYS);
    1.57 +    }
    1.58 +
    1.59 +    static Object exportArgument(final Object arg, final boolean mirrorAlways) {
    1.60 +        if (arg instanceof ConsString) {
    1.61 +            return arg.toString();
    1.62 +        } else if (mirrorAlways && arg instanceof ScriptObject) {
    1.63 +            return ScriptUtils.wrap((ScriptObject)arg);
    1.64 +        } else {
    1.65 +            return arg;
    1.66 +        }
    1.67 +    }
    1.68 +
    1.69 +    @SuppressWarnings("unused")
    1.70 +    private static Object importResult(final Object arg) {
    1.71 +        return ScriptUtils.unwrap(arg);
    1.72      }
    1.73  
    1.74      private static class NashornBeansLinkerServices implements LinkerServices {
    1.75 @@ -80,23 +122,50 @@
    1.76  
    1.77          @Override
    1.78          public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
    1.79 -            final MethodHandle typed = linkerServices.asType(handle, fromType);
    1.80 -
    1.81              final MethodType handleType = handle.type();
    1.82              final int paramCount = handleType.parameterCount();
    1.83              assert fromType.parameterCount() == handleType.parameterCount();
    1.84  
    1.85 +            MethodType newFromType = fromType;
    1.86              MethodHandle[] filters = null;
    1.87              for(int i = 0; i < paramCount; ++i) {
    1.88 -                if(shouldConvert(handleType.parameterType(i), fromType.parameterType(i))) {
    1.89 -                    if(filters == null) {
    1.90 +                final MethodHandle filter = argConversionFilter(handleType.parameterType(i), fromType.parameterType(i));
    1.91 +                if (filter != null) {
    1.92 +                    if (filters == null) {
    1.93                          filters = new MethodHandle[paramCount];
    1.94                      }
    1.95 -                    filters[i] = EXPORT_ARGUMENT;
    1.96 +                    // "erase" specific type with Object type or else we'll get filter mismatch
    1.97 +                    newFromType = newFromType.changeParameterType(i, Object.class);
    1.98 +                    filters[i] = filter;
    1.99                  }
   1.100              }
   1.101  
   1.102 -            return filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed;
   1.103 +            final MethodHandle typed = linkerServices.asType(handle, newFromType);
   1.104 +            MethodHandle result = filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed;
   1.105 +            // Filter Object typed return value for possible ScriptObjectMirror. We convert
   1.106 +            // ScriptObjectMirror as ScriptObject (if it is mirror from current global).
   1.107 +            if (MIRROR_ALWAYS && areBothObjects(handleType.returnType(), fromType.returnType())) {
   1.108 +                result = MethodHandles.filterReturnValue(result, IMPORT_RESULT);
   1.109 +            }
   1.110 +
   1.111 +            return result;
   1.112 +        }
   1.113 +
   1.114 +        private static MethodHandle argConversionFilter(final Class<?> handleType, final Class<?> fromType) {
   1.115 +            if (handleType == Object.class) {
   1.116 +                if (fromType == Object.class) {
   1.117 +                    return EXPORT_ARGUMENT;
   1.118 +                } else if (fromType == NativeArray.class) {
   1.119 +                    return EXPORT_NATIVE_ARRAY;
   1.120 +                } else if (fromType == ScriptObject.class) {
   1.121 +                    return EXPORT_SCRIPT_OBJECT;
   1.122 +                }
   1.123 +            }
   1.124 +            return null;
   1.125 +        }
   1.126 +
   1.127 +        private static boolean areBothObjects(final Class<?> handleType, final Class<?> fromType) {
   1.128 +            return handleType == Object.class && fromType == Object.class;
   1.129          }
   1.130  
   1.131          @Override
   1.132 @@ -104,10 +173,6 @@
   1.133              return Implementation.asTypeLosslessReturn(this, handle, fromType);
   1.134          }
   1.135  
   1.136 -        private static boolean shouldConvert(final Class<?> handleType, final Class<?> fromType) {
   1.137 -            return handleType == Object.class && fromType == Object.class;
   1.138 -        }
   1.139 -
   1.140          @Override
   1.141          public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
   1.142              return linkerServices.getTypeConverter(sourceType, targetType);

mercurial