# HG changeset patch # User attila # Date 1383316593 -3600 # Node ID 98bab0cdd7bff6ea1eda72b31b1a2c16be038098 # Parent ae5f2130c3b991bfff59aab114ce6f169f618873 8027236: Ensure ScriptObject and ConsString aren't visible to Java Reviewed-by: lagergren, sundar diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/api/scripting/ScriptObjectMirror.java --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Fri Nov 01 15:36:33 2013 +0100 @@ -41,6 +41,7 @@ import java.util.Set; import java.util.concurrent.Callable; import javax.script.Bindings; +import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.JSType; @@ -594,14 +595,20 @@ } /** - * Make a script object mirror on given object if needed. + * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings. * - * @param obj object to be wrapped - * @param homeGlobal global to which this object belongs - * @return wrapped object + * @param obj object to be wrapped/converted + * @param homeGlobal global to which this object belongs. Not used for ConsStrings. + * @return wrapped/converted object */ public static Object wrap(final Object obj, final ScriptObject homeGlobal) { - return (obj instanceof ScriptObject && homeGlobal != null) ? new ScriptObjectMirror((ScriptObject)obj, homeGlobal) : obj; + if(obj instanceof ScriptObject) { + return homeGlobal != null ? new ScriptObjectMirror((ScriptObject)obj, homeGlobal) : obj; + } + if(obj instanceof ConsString) { + return obj.toString(); + } + return obj; } /** diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/objects/Global.java --- a/src/jdk/nashorn/internal/objects/Global.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/objects/Global.java Fri Nov 01 15:36:33 2013 +0100 @@ -53,19 +53,19 @@ import jdk.nashorn.internal.runtime.GlobalObject; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.NativeJavaPackage; +import jdk.nashorn.internal.runtime.PropertyDescriptor; import jdk.nashorn.internal.runtime.PropertyMap; +import jdk.nashorn.internal.runtime.Scope; import jdk.nashorn.internal.runtime.ScriptEnvironment; -import jdk.nashorn.internal.runtime.PropertyDescriptor; -import jdk.nashorn.internal.runtime.arrays.ArrayData; -import jdk.nashorn.internal.runtime.regexp.RegExpResult; -import jdk.nashorn.internal.runtime.Scope; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.ScriptingFunctions; import jdk.nashorn.internal.runtime.Source; +import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; +import jdk.nashorn.internal.runtime.regexp.RegExpResult; import jdk.nashorn.internal.scripts.JO; /** diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/objects/NativeObject.java --- a/src/jdk/nashorn/internal/objects/NativeObject.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/objects/NativeObject.java Fri Nov 01 15:36:33 2013 +0100 @@ -60,6 +60,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.linker.Bootstrap; import jdk.nashorn.internal.runtime.linker.InvokeByName; +import jdk.nashorn.internal.runtime.linker.NashornBeansLinker; /** * ECMA 15.2 Object objects @@ -729,8 +730,7 @@ final MethodType methodType, final Object source) { final GuardedInvocation inv; try { - inv = linker.getGuardedInvocation(createLinkRequest(operation, methodType, source), - Bootstrap.getLinkerServices()); + inv = NashornBeansLinker.getGuardedInvocation(linker, createLinkRequest(operation, methodType, source), Bootstrap.getLinkerServices()); assert passesGuard(source, inv.getGuard()); } catch(RuntimeException|Error e) { throw e; diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/runtime/ConsString.java --- a/src/jdk/nashorn/internal/runtime/ConsString.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/ConsString.java Fri Nov 01 15:36:33 2013 +0100 @@ -57,10 +57,7 @@ @Override public String toString() { - if (!flat) { - flatten(); - } - return (String) left; + return (String) flattened(); } @Override @@ -70,18 +67,19 @@ @Override public char charAt(final int index) { - if (!flat) { - flatten(); - } - return left.charAt(index); + return flattened().charAt(index); } @Override public CharSequence subSequence(final int start, final int end) { + return flattened().subSequence(start, end); + } + + private CharSequence flattened() { if (!flat) { flatten(); } - return left.subSequence(start, end); + return left; } private void flatten() { diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/runtime/JSType.java --- a/src/jdk/nashorn/internal/runtime/JSType.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Fri Nov 01 15:36:33 2013 +0100 @@ -883,7 +883,7 @@ */ public static Object toJavaArray(final Object obj, final Class componentType) { if (obj instanceof ScriptObject) { - return convertArray(((ScriptObject)obj).getArray().asObjectArray(), componentType); + return ((ScriptObject)obj).getArray().asArrayOfType(componentType); } else if (obj instanceof JSObject) { final ArrayLikeIterator itr = ArrayLikeIterator.arrayLikeIterator(obj); final int len = (int) itr.getLength(); @@ -908,6 +908,15 @@ * @return converted Java array */ public static Object convertArray(final Object[] src, final Class componentType) { + if(componentType == Object.class) { + for(int i = 0; i < src.length; ++i) { + final Object e = src[i]; + if(e instanceof ConsString) { + src[i] = e.toString(); + } + } + } + final int l = src.length; final Object dst = Array.newInstance(componentType, l); final MethodHandle converter = Bootstrap.getLinkerServices().getTypeConverter(Object.class, componentType); diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/runtime/linker/Bootstrap.java --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java Fri Nov 01 15:36:33 2013 +0100 @@ -63,7 +63,7 @@ final DynamicLinkerFactory factory = new DynamicLinkerFactory(); factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(), new BoundDynamicMethodLinker(), new JavaSuperAdapterLinker(), new JSObjectLinker(), new ReflectionCheckLinker()); - factory.setFallbackLinkers(new BeansLinker(), new NashornBottomLinker()); + factory.setFallbackLinkers(new NashornBeansLinker(), new NashornBottomLinker()); factory.setSyncOnRelink(true); final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1); if (relinkThreshold > -1) { diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java Fri Nov 01 15:36:33 2013 +0100 @@ -72,7 +72,7 @@ type.changeParameterType(0, dynamicMethodClass).changeParameterType(1, boundThis.getClass())); // Delegate to BeansLinker - final GuardedInvocation inv = BeansLinker.getLinkerForClass(dynamicMethodClass).getGuardedInvocation( + final GuardedInvocation inv = NashornBeansLinker.getGuardedInvocation(BeansLinker.getLinkerForClass(dynamicMethodClass), linkRequest.replaceArguments(newDescriptor, args), linkerServices); if(inv == null) { return null; diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java Fri Nov 01 15:36:33 2013 +0100 @@ -100,8 +100,9 @@ type.changeParameterType(0, adapterClass), 0); // Delegate to BeansLinker - final GuardedInvocation guardedInv = BeansLinker.getLinkerForClass(adapterClass).getGuardedInvocation( - linkRequest.replaceArguments(newDescriptor, args), linkerServices); + final GuardedInvocation guardedInv = NashornBeansLinker.getGuardedInvocation( + BeansLinker.getLinkerForClass(adapterClass), linkRequest.replaceArguments(newDescriptor, args), + linkerServices); final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass); if(guardedInv == null) { diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java Fri Nov 01 15:36:33 2013 +0100 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.internal.runtime.linker; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import jdk.internal.dynalink.beans.BeansLinker; +import jdk.internal.dynalink.linker.ConversionComparator.Comparison; +import jdk.internal.dynalink.linker.GuardedInvocation; +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.internal.runtime.ConsString; + +/** + * 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 + * 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); + + private final BeansLinker beansLinker = new BeansLinker(); + + @Override + public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + return getGuardedInvocation(beansLinker, linkRequest, linkerServices); + } + + /** + * Delegates to the specified linker but injects its linker services wrapper so that it will apply all special + * conversions that this class does. + * @param delegateLinker the linker to which the actual work is delegated to. + * @param linkRequest the delegated link request + * @param linkerServices the original link services that will be augmented with special conversions + * @return the guarded invocation from the delegate, possibly augmented with special conversions + * @throws Exception if the delegate throws an exception + */ + public static GuardedInvocation getGuardedInvocation(final GuardingDynamicLinker delegateLinker, final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception { + return delegateLinker.getGuardedInvocation(linkRequest, new NashornBeansLinkerServices(linkerServices)); + } + + @SuppressWarnings("unused") + private static Object exportArgument(final Object arg) { + return arg instanceof ConsString ? arg.toString() : arg; + } + + private static class NashornBeansLinkerServices implements LinkerServices { + private final LinkerServices linkerServices; + + NashornBeansLinkerServices(final LinkerServices linkerServices) { + this.linkerServices = linkerServices; + } + + @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(); + + MethodHandle[] filters = null; + for(int i = 0; i < paramCount; ++i) { + if(shouldConvert(handleType.parameterType(i), fromType.parameterType(i))) { + if(filters == null) { + filters = new MethodHandle[paramCount]; + } + filters[i] = EXPORT_ARGUMENT; + } + } + + return filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed; + } + + private static boolean shouldConvert(final Class handleType, final Class fromType) { + return handleType == Object.class && fromType == Object.class; + } + + @Override + public MethodHandle getTypeConverter(final Class sourceType, final Class targetType) { + return linkerServices.getTypeConverter(sourceType, targetType); + } + + @Override + public boolean canConvert(final Class from, final Class to) { + return linkerServices.canConvert(from, to); + } + + @Override + public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest) throws Exception { + return linkerServices.getGuardedInvocation(linkRequest); + } + + @Override + public Comparison compareConversion(final Class sourceType, final Class targetType1, final Class targetType2) { + return linkerServices.compareConversion(sourceType, targetType1, targetType2); + } + } +} diff -r ae5f2130c3b9 -r 98bab0cdd7bf src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Fri Nov 01 19:54:48 2013 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Fri Nov 01 15:36:33 2013 +0100 @@ -93,7 +93,7 @@ } private static GuardedInvocation delegate(LinkerServices linkerServices, final LinkRequest request) throws Exception { - return staticClassLinker.getGuardedInvocation(request, linkerServices); + return NashornBeansLinker.getGuardedInvocation(staticClassLinker, request, linkerServices); } private static GuardedInvocation checkNullConstructor(final GuardedInvocation ctorInvocation, final Class receiverClass) { diff -r ae5f2130c3b9 -r 98bab0cdd7bf test/script/basic/JDK-8027236.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8027236.js Fri Nov 01 15:36:33 2013 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8027236: Ensure ScriptObject and ConsString aren't visible to Java + * + * @test + * @run + */ + +// Check that ConsString is flattened +var m = new java.util.HashMap() +var x = "f" +x += "oo" +m.put(x, "bar") +print(m.get("foo")) +// Note: many more tests are run by the JavaExportImportTest TestNG class. diff -r ae5f2130c3b9 -r 98bab0cdd7bf test/script/basic/JDK-8027236.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/script/basic/JDK-8027236.js.EXPECTED Fri Nov 01 15:36:33 2013 +0100 @@ -0,0 +1,1 @@ +bar diff -r ae5f2130c3b9 -r 98bab0cdd7bf test/src/jdk/nashorn/api/javaaccess/ConsStringTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/api/javaaccess/ConsStringTest.java Fri Nov 01 15:36:33 2013 +0100 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.nashorn.api.javaaccess; + +import static org.testng.AssertJUnit.assertEquals; + +import java.util.HashMap; +import java.util.Map; +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import jdk.nashorn.api.scripting.JSObject; +import org.testng.TestNG; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ConsStringTest { + private static ScriptEngine e = null; + + public static void main(final String[] args) { + TestNG.main(args); + } + + @BeforeClass + public static void setUpClass() throws ScriptException { + e = new ScriptEngineManager().getEngineByName("nashorn"); + } + + @AfterClass + public static void tearDownClass() { + e = null; + } + + @Test + public void testConsStringFlattening() throws ScriptException { + final Bindings b = e.getBindings(ScriptContext.ENGINE_SCOPE); + final Map m = new HashMap<>(); + b.put("m", m); + e.eval("var x = 'f'; x += 'oo'; var y = 'b'; y += 'ar'; m.put(x, y)"); + assertEquals("bar", m.get("foo")); + } + + @Test + public void testConsStringFromMirror() throws ScriptException { + final Bindings b = e.getBindings(ScriptContext.ENGINE_SCOPE); + final Map m = new HashMap<>(); + e.eval("var x = 'f'; x += 'oo'; var obj = {x: x};"); + assertEquals("foo", ((JSObject)b.get("obj")).getMember("x")); + } + + @Test + public void testArrayConsString() throws ScriptException { + final Bindings b = e.getBindings(ScriptContext.ENGINE_SCOPE); + final ArrayHolder h = new ArrayHolder(); + b.put("h", h); + e.eval("var x = 'f'; x += 'oo'; h.array = [x];"); + assertEquals(1, h.array.length); + assertEquals("foo", h.array[0]); + } + + + public static class ArrayHolder { + private Object[] array; + + public void setArray(Object[] array) { + this.array = array; + } + + public Object[] getArray() { + return array; + } + } +}