diff options
Diffstat (limited to 'src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java')
-rw-r--r-- | src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java | 120 |
1 files changed, 111 insertions, 9 deletions
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java index e2db2b11..d5133cb3 100644 --- a/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java @@ -35,22 +35,52 @@ import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.support.Lookup; +import jdk.nashorn.api.scripting.ScriptUtils; +import jdk.nashorn.internal.objects.NativeArray; import jdk.nashorn.internal.runtime.ConsString; +import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.options.Options; /** * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified * {@code asType} method that will ensure that we never pass internal engine objects that should not be externally - * observable (currently only ConsString) to Java APIs, but rather that we flatten it into a String. We can't just add + * observable (currently ConsString and ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add * this functionality as custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when * the target method handle parameter signature is {@code Object}. */ public class NashornBeansLinker implements GuardingDynamicLinker { - private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class); + // System property to control whether to wrap ScriptObject->ScriptObjectMirror for + // Object type arguments of Java method calls, field set and array set. + private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true); + + private static final MethodHandle EXPORT_ARGUMENT; + private static final MethodHandle EXPORT_NATIVE_ARRAY; + private static final MethodHandle EXPORT_SCRIPT_OBJECT; + private static final MethodHandle IMPORT_RESULT; + private static final MethodHandle FILTER_CONSSTRING; + + static { + final Lookup lookup = new Lookup(MethodHandles.lookup()); + EXPORT_ARGUMENT = lookup.findOwnStatic("exportArgument", Object.class, Object.class); + EXPORT_NATIVE_ARRAY = lookup.findOwnStatic("exportNativeArray", Object.class, NativeArray.class); + EXPORT_SCRIPT_OBJECT = lookup.findOwnStatic("exportScriptObject", Object.class, ScriptObject.class); + IMPORT_RESULT = lookup.findOwnStatic("importResult", Object.class, Object.class); + FILTER_CONSSTRING = lookup.findOwnStatic("consStringFilter", Object.class, Object.class); + } private final BeansLinker beansLinker = new BeansLinker(); @Override public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + if (linkRequest.getReceiver() instanceof ConsString) { + // In order to treat ConsString like a java.lang.String we need a link request with a string receiver. + final Object[] arguments = linkRequest.getArguments(); + arguments[0] = ""; + final LinkRequest forgedLinkRequest = linkRequest.replaceArguments(linkRequest.getCallSiteDescriptor(), arguments); + final GuardedInvocation invocation = getGuardedInvocation(beansLinker, forgedLinkRequest, linkerServices); + // If an invocation is found we add a filter that makes it work for both Strings and ConsStrings. + return invocation == null ? null : invocation.filterArguments(0, FILTER_CONSSTRING); + } return getGuardedInvocation(beansLinker, linkRequest, linkerServices); } @@ -69,6 +99,41 @@ public class NashornBeansLinker implements GuardingDynamicLinker { @SuppressWarnings("unused") private static Object exportArgument(final Object arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + @SuppressWarnings("unused") + private static Object exportNativeArray(final NativeArray arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + @SuppressWarnings("unused") + private static Object exportScriptObject(final ScriptObject arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + @SuppressWarnings("unused") + private static Object exportScriptArray(final NativeArray arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + static Object exportArgument(final Object arg, final boolean mirrorAlways) { + if (arg instanceof ConsString) { + return arg.toString(); + } else if (mirrorAlways && arg instanceof ScriptObject) { + return ScriptUtils.wrap((ScriptObject)arg); + } else { + return arg; + } + } + + @SuppressWarnings("unused") + private static Object importResult(final Object arg) { + return ScriptUtils.unwrap(arg); + } + + @SuppressWarnings("unused") + private static Object consStringFilter(final Object arg) { return arg instanceof ConsString ? arg.toString() : arg; } @@ -81,30 +146,58 @@ public class NashornBeansLinker implements GuardingDynamicLinker { @Override public MethodHandle asType(final MethodHandle handle, final MethodType fromType) { - final MethodHandle typed = linkerServices.asType(handle, fromType); - final MethodType handleType = handle.type(); final int paramCount = handleType.parameterCount(); assert fromType.parameterCount() == handleType.parameterCount(); + MethodType newFromType = fromType; MethodHandle[] filters = null; for(int i = 0; i < paramCount; ++i) { - if(shouldConvert(handleType.parameterType(i), fromType.parameterType(i))) { - if(filters == null) { + final MethodHandle filter = argConversionFilter(handleType.parameterType(i), fromType.parameterType(i)); + if (filter != null) { + if (filters == null) { filters = new MethodHandle[paramCount]; } - filters[i] = EXPORT_ARGUMENT; + // "erase" specific type with Object type or else we'll get filter mismatch + newFromType = newFromType.changeParameterType(i, Object.class); + filters[i] = filter; } } - return filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed; + final MethodHandle typed = linkerServices.asType(handle, newFromType); + MethodHandle result = filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed; + // Filter Object typed return value for possible ScriptObjectMirror. We convert + // ScriptObjectMirror as ScriptObject (if it is mirror from current global). + if (MIRROR_ALWAYS && areBothObjects(handleType.returnType(), fromType.returnType())) { + result = MethodHandles.filterReturnValue(result, IMPORT_RESULT); + } + + return result; } - private static boolean shouldConvert(final Class<?> handleType, final Class<?> fromType) { + private static MethodHandle argConversionFilter(final Class<?> handleType, final Class<?> fromType) { + if (handleType == Object.class) { + if (fromType == Object.class) { + return EXPORT_ARGUMENT; + } else if (fromType == NativeArray.class) { + return EXPORT_NATIVE_ARRAY; + } else if (fromType == ScriptObject.class) { + return EXPORT_SCRIPT_OBJECT; + } + } + return null; + } + + private static boolean areBothObjects(final Class<?> handleType, final Class<?> fromType) { return handleType == Object.class && fromType == Object.class; } @Override + public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) { + return Implementation.asTypeLosslessReturn(this, handle, fromType); + } + + @Override public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) { return linkerServices.getTypeConverter(sourceType, targetType); } @@ -121,6 +214,15 @@ public class NashornBeansLinker implements GuardingDynamicLinker { @Override public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) { + if (sourceType == ConsString.class) { + if (String.class == targetType1 || CharSequence.class == targetType1) { + return Comparison.TYPE_1_BETTER; + } + + if (String.class == targetType2 || CharSequence.class == targetType2) { + return Comparison.TYPE_2_BETTER; + } + } return linkerServices.compareConversion(sourceType, targetType1, targetType2); } } |