# HG changeset patch # User attila # Date 1391109269 -3600 # Node ID 11b83c913cca4d88b8646af551a0ee48def31ede # Parent c59fb10cb0b586c27b88be2c5cdc19c10ec37cf0 8032681: Issues with Nashorn Reviewed-by: ahgross, jlaskey, sundar diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/internal/dynalink/linker/GuardedTypeConversion.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java Thu Jan 30 20:14:29 2014 +0100 @@ -0,0 +1,102 @@ +/* + * 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. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file, and Oracle licenses the original version of this file under the BSD + * license: + */ +/* + Copyright 2009-2013 Attila Szegedi + + Licensed under both the Apache License, Version 2.0 (the "Apache License") + and the BSD License (the "BSD License"), with licensee being free to + choose either of the two at their discretion. + + You may not use this file except in compliance with either the Apache + License or the BSD License. + + If you choose to use this file in compliance with the Apache License, the + following notice applies to you: + + You may obtain a copy of the Apache License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. See the License for the specific language governing + permissions and limitations under the License. + + If you choose to use this file in compliance with the BSD License, the + following notice applies to you: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holder nor the names of + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package jdk.internal.dynalink.linker; + +public class GuardedTypeConversion { + private final GuardedInvocation conversionInvocation; + private final boolean cacheable; + + public GuardedTypeConversion(final GuardedInvocation conversionInvocation, final boolean cacheable) { + this.conversionInvocation = conversionInvocation; + this.cacheable = cacheable; + } + + public GuardedInvocation getConversionInvocation() { + return conversionInvocation; + } + + public boolean isCacheable() { + return cacheable; + } +} diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java --- a/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java Thu Jan 30 20:14:29 2014 +0100 @@ -96,19 +96,19 @@ */ public interface GuardingTypeConverterFactory { /** - * Returns a guarded invocation that receives an Object of the specified source type and returns an Object converted - * to the specified target type. The type of the invocation is targetType(sourceType), while the type of the guard - * is boolean(sourceType). Note that this will never be invoked for type conversions allowed by the JLS 5.3 "Method - * Invocation Conversion", see {@link TypeUtilities#isMethodInvocationConvertible(Class, Class)} for details. An - * implementation can assume it is never requested to produce a converter for these conversions. + * Returns a guarded type conversion that receives an Object of the specified source type and returns an Object + * converted to the specified target type. The type of the invocation is targetType(sourceType), while the type of + * the guard is boolean(sourceType). Note that this will never be invoked for type conversions allowed by the JLS + * 5.3 "Method Invocation Conversion", see {@link TypeUtilities#isMethodInvocationConvertible(Class, Class)} for + * details. An implementation can assume it is never requested to produce a converter for these conversions. * * @param sourceType source type * @param targetType the target type. - * @return a guarded invocation that can take an object (if it passes guard) and returns another object that is its - * representation coerced into the target type. In case the factory is certain it is unable to handle a conversion, - * it can return null. In case the factory is certain that it can always handle the conversion, it can return an - * unconditional invocation (one whose guard is null). + * @return a guarded type conversion that contains a guarded invocation that can take an object (if it passes guard) + * and return another object that is its representation coerced into the target type. In case the factory is certain + * it is unable to handle a conversion, it can return null. In case the factory is certain that it can always handle + * the conversion, it can return an unconditional invocation (one whose guard is null). * @throws Exception if there was an error during creation of the converter */ - public GuardedInvocation convertToType(Class sourceType, Class targetType) throws Exception; + public GuardedTypeConversion convertToType(Class sourceType, Class targetType) throws Exception; } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/internal/dynalink/support/LinkerServicesImpl.java --- a/src/jdk/internal/dynalink/support/LinkerServicesImpl.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/internal/dynalink/support/LinkerServicesImpl.java Thu Jan 30 20:14:29 2014 +0100 @@ -98,6 +98,9 @@ */ public class LinkerServicesImpl implements LinkerServices { + private static final RuntimePermission GET_CURRENT_LINK_REQUEST = new RuntimePermission("dynalink.getCurrentLinkRequest"); + private static final ThreadLocal threadLinkRequest = new ThreadLocal<>(); + private final TypeConverterFactory typeConverterFactory; private final GuardingDynamicLinker topLevelLinker; @@ -135,6 +138,26 @@ @Override public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest) throws Exception { - return topLevelLinker.getGuardedInvocation(linkRequest, this); + final LinkRequest prevLinkRequest = threadLinkRequest.get(); + threadLinkRequest.set(linkRequest); + try { + return topLevelLinker.getGuardedInvocation(linkRequest, this); + } finally { + threadLinkRequest.set(prevLinkRequest); + } + } + + /** + * Returns the currently processed link request, or null if the method is invoked outside of the linking process. + * @return the currently processed link request, or null. + * @throws SecurityException if the calling code doesn't have the {@code "dynalink.getCurrentLinkRequest"} runtime + * permission. + */ + public static LinkRequest getCurrentLinkRequest() { + SecurityManager sm = System.getSecurityManager(); + if(sm != null) { + sm.checkPermission(GET_CURRENT_LINK_REQUEST); + } + return threadLinkRequest.get(); } } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/internal/dynalink/support/TypeConverterFactory.java --- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/internal/dynalink/support/TypeConverterFactory.java Thu Jan 30 20:14:29 2014 +0100 @@ -94,6 +94,7 @@ import jdk.internal.dynalink.linker.ConversionComparator; import jdk.internal.dynalink.linker.ConversionComparator.Comparison; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.GuardedTypeConversion; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkerServices; @@ -134,8 +135,8 @@ @Override protected MethodHandle computeValue(Class targetType) { if(!canAutoConvert(sourceType, targetType)) { - final MethodHandle converter = getTypeConverterNull(sourceType, targetType); - if(converter != null) { + final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType); + if(converter != IDENTITY_CONVERSION) { return converter; } } @@ -145,6 +146,24 @@ } }; + private final ClassValue> canConvert = new ClassValue>() { + @Override + protected ClassMap computeValue(final Class sourceType) { + return new ClassMap(getClassLoader(sourceType)) { + @Override + protected Boolean computeValue(Class targetType) { + try { + return getTypeConverterNull(sourceType, targetType) != null; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } + }; + private static final ClassLoader getClassLoader(final Class clazz) { return AccessController.doPrivileged(new PrivilegedAction() { @Override @@ -253,7 +272,7 @@ * @return true if there can be a conversion, false if there can not. */ public boolean canConvert(final Class from, final Class to) { - return canAutoConvert(from, to) || getTypeConverterNull(from, to) != null; + return canAutoConvert(from, to) || canConvert.get(from).get(to).booleanValue(); } /** @@ -294,9 +313,21 @@ return TypeUtilities.isMethodInvocationConvertible(fromType, toType); } + /*private*/ MethodHandle getCacheableTypeConverterNull(Class sourceType, Class targetType) { + final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType); + return converter == IDENTITY_CONVERSION ? null : converter; + } + /*private*/ MethodHandle getTypeConverterNull(Class sourceType, Class targetType) { - final MethodHandle converter = converterMap.get(sourceType).get(targetType); - return converter == IDENTITY_CONVERSION ? null : converter; + try { + return getCacheableTypeConverterNull(sourceType, targetType); + } catch(NotCacheableConverter e) { + return e.converter; + } + } + + /*private*/ MethodHandle getCacheableTypeConverter(Class sourceType, Class targetType) { + return converterMap.get(sourceType).get(targetType); } /** @@ -309,22 +340,44 @@ * @return a method handle performing the conversion. */ public MethodHandle getTypeConverter(Class sourceType, Class targetType) { - return converterIdentityMap.get(sourceType).get(targetType); + try { + return converterIdentityMap.get(sourceType).get(targetType); + } catch(NotCacheableConverter e) { + return e.converter; + } } /*private*/ MethodHandle createConverter(Class sourceType, Class targetType) throws Exception { final MethodType type = MethodType.methodType(targetType, sourceType); final MethodHandle identity = IDENTITY_CONVERSION.asType(type); MethodHandle last = identity; + boolean cacheable = true; for(int i = factories.length; i-- > 0;) { - final GuardedInvocation next = factories[i].convertToType(sourceType, targetType); + final GuardedTypeConversion next = factories[i].convertToType(sourceType, targetType); if(next != null) { - next.assertType(type); - last = next.compose(last); + cacheable = cacheable && next.isCacheable(); + final GuardedInvocation conversionInvocation = next.getConversionInvocation(); + conversionInvocation.assertType(type); + last = conversionInvocation.compose(last); } } - return last == identity ? IDENTITY_CONVERSION : last; + if(last == identity) { + return IDENTITY_CONVERSION; + } + if(cacheable) { + return last; + } + throw new NotCacheableConverter(last); } /*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class); + + private static class NotCacheableConverter extends RuntimeException { + final MethodHandle converter; + + NotCacheableConverter(final MethodHandle converter) { + super("", null, false, false); + this.converter = converter; + } + } } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/api/scripting/NashornScriptEngine.java --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Thu Jan 30 20:14:29 2014 +0100 @@ -32,6 +32,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; @@ -104,7 +105,7 @@ private volatile Property contextProperty; // default options passed to Nashorn Options object - private static final String[] DEFAULT_OPTIONS = new String[] { "-scripting", "-doe" }; + private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" }; // Nashorn script engine error message management private static final String MESSAGES_RESOURCE = "jdk.nashorn.api.scripting.resources.Messages"; @@ -355,7 +356,8 @@ if (! isInterfaceImplemented(clazz, realSelf)) { return null; } - return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz).invoke(realSelf)); + return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz, + MethodHandles.publicLookup()).invoke(realSelf)); } finally { if (globalChanged) { Context.setGlobal(oldGlobal); diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/objects/NativeJava.java --- a/src/jdk/nashorn/internal/objects/NativeJava.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/objects/NativeJava.java Thu Jan 30 20:14:29 2014 +0100 @@ -28,6 +28,7 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.util.Collection; import java.util.Deque; @@ -463,12 +464,14 @@ * * We can see several important concepts in the above example: *
    - *
  • Every specified list of Java types will have exactly one extender subclass in Nashorn - repeated invocations - * of {@code extend} for the same list of types will yield the same extender type. It's a generic adapter that - * delegates to whatever JavaScript functions its implementation object has on a per-instance basis.
  • + *
  • Every specified list of Java types will have one extender subclass in Nashorn per caller protection domain - + * repeated invocations of {@code extend} for the same list of types for scripts same protection domain will yield + * the same extender type. It's a generic adapter that delegates to whatever JavaScript functions its implementation + * object has on a per-instance basis.
  • *
  • If the Java method is overloaded (as in the above example {@code List.add()}), then your JavaScript adapter * must be prepared to deal with all overloads.
  • - *
  • You can't invoke {@code super.*()} from adapters for now.
  • + *
  • To invoke super methods from adapters, call them on the adapter instance prefixing them with {@code super$}, + * or use the special {@link #_super(Object, Object) super-adapter}.
  • *
  • It is also possible to specify an ordinary JavaScript object as the last argument to {@code extend}. In that * case, it is treated as a class-level override. {@code extend} will return an extender class where all instances * will have the methods implemented by functions on that object, just as if that object were passed as the last @@ -486,15 +489,18 @@ * t.join() * * As you can see, you don't have to pass any object when you create a new instance of {@code R1} as its - * {@code run()} function was defined already when extending the class. Of course, you can still provide - * instance-level overrides on these objects. The order of precedence is instance-level method, class-level method, - * superclass method, or {@code UnsupportedOperationException} if the superclass method is abstract. If we continue - * our previous example: + * {@code run()} function was defined already when extending the class. If you also want to add instance-level + * overrides on these objects, you will have to repeatedly use {@code extend()} to subclass the class-level adapter. + * For such adapters, the order of precedence is instance-level method, class-level method, superclass method, or + * {@code UnsupportedOperationException} if the superclass method is abstract. If we continue our previous example: *
    -     * var r2 = new R1(function() { print("r2.run() invoked!") })
    +     * var R2 = Java.extend(R1);
    +     * var r2 = new R2(function() { print("r2.run() invoked!") })
          * r2.run()
          * 
    * We'll see it'll print {@code "r2.run() invoked!"}, thus overriding on instance-level the class-level behavior. + * Note that you must use {@code Java.extend} to explicitly create an instance-override adapter class from a + * class-override adapter class, as the class-override adapter class is no longer abstract. *
  • *
* @param self not used @@ -541,7 +547,18 @@ } catch(final ClassCastException e) { throw typeError("extend.expects.java.types"); } - return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides); + // Note that while the public API documentation claims self is not used, we actually use it. + // ScriptFunction.findCallMethod will bind the lookup object into it, and we can then use that lookup when + // requesting the adapter class. Note that if Java.extend is invoked with no lookup object, it'll pass the + // public lookup which'll result in generation of a no-permissions adapter. A typical situation this can happen + // is when the extend function is bound. + final MethodHandles.Lookup lookup; + if(self instanceof MethodHandles.Lookup) { + lookup = (MethodHandles.Lookup)self; + } else { + lookup = MethodHandles.publicLookup(); + } + return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides, lookup); } /** diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/objects/NativeJavaImporter.java --- a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Thu Jan 30 20:14:29 2014 +0100 @@ -33,6 +33,7 @@ import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.NativeJavaPackage; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptObject; @@ -156,8 +157,9 @@ } else if (obj instanceof NativeJavaPackage) { final String pkgName = ((NativeJavaPackage)obj).getName(); final String fullName = pkgName.isEmpty() ? name : (pkgName + "." + name); + final Context context = Global.instance().getContext(); try { - return StaticClass.forClass(Class.forName(fullName)); + return StaticClass.forClass(context.findClass(fullName)); } catch (final ClassNotFoundException e) { // IGNORE } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/Context.java --- a/src/jdk/nashorn/internal/runtime/Context.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/Context.java Thu Jan 30 20:14:29 2014 +0100 @@ -647,6 +647,19 @@ } /** + * Checks that the given package name can be accessed from no permissions context. + * + * @param pkgName package name + * @throw SecurityException if not accessible + */ + public static void checkPackageAccess(final String pkgName) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + checkPackageAccess(sm, pkgName.endsWith(".")? pkgName : pkgName + "."); + } + } + + /** * Checks that the given package can be accessed from no permissions context. * * @param sm current security manager instance diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/NativeJavaPackage.java --- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java Thu Jan 30 20:14:29 2014 +0100 @@ -85,6 +85,8 @@ */ public NativeJavaPackage(final String name, final ScriptObject proto) { super(proto, null); + // defense-in-path, check here for sensitive packages + Context.checkPackageAccess(name); this.name = name; } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/ScriptFunction.java --- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java Thu Jan 30 20:14:29 2014 +0100 @@ -26,14 +26,13 @@ package jdk.nashorn.internal.runtime; import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; +import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; -import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; - import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; @@ -524,7 +523,11 @@ } } else { final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments()); - if (scopeCall) { + if (data.isBuiltin() && "extend".equals(data.getName())) { + // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the + // current lookup as its "this" so it can do security-sensitive creation of adapter classes. + boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, Object.class, Object.class); + } else if (scopeCall) { // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined // (this, args...) => (args...) boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java --- a/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java Thu Jan 30 20:14:29 2014 +0100 @@ -47,7 +47,8 @@ ERROR_NON_PUBLIC_CLASS, ERROR_NO_ACCESSIBLE_CONSTRUCTOR, ERROR_MULTIPLE_SUPERCLASSES, - ERROR_NO_COMMON_LOADER + ERROR_NO_COMMON_LOADER, + ERROR_FINAL_FINALIZER } static final AdaptationResult SUCCESSFUL_RESULT = new AdaptationResult(Outcome.SUCCESS, ""); diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Thu Jan 30 20:14:29 2014 +0100 @@ -32,6 +32,7 @@ import java.util.Map; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.GuardedTypeConversion; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; @@ -79,7 +80,7 @@ } @Override - public GuardedInvocation convertToType(final Class sourceType, final Class targetType) throws Exception { + public GuardedTypeConversion convertToType(final Class sourceType, final Class targetType) throws Exception { final boolean sourceIsAlwaysJSObject = JSObject.class.isAssignableFrom(sourceType); if(!sourceIsAlwaysJSObject && !sourceType.isAssignableFrom(JSObject.class)) { return null; @@ -90,7 +91,7 @@ return null; } - return new GuardedInvocation(converter, sourceIsAlwaysJSObject ? null : IS_JSOBJECT_GUARD).asType(MethodType.methodType(targetType, sourceType)); + return new GuardedTypeConversion(new GuardedInvocation(converter, sourceIsAlwaysJSObject ? null : IS_JSOBJECT_GUARD).asType(MethodType.methodType(targetType, sourceType)), true); } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Thu Jan 30 20:14:29 2014 +0100 @@ -59,6 +59,7 @@ import java.util.List; import java.util.Set; import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.Label; import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.org.objectweb.asm.Type; @@ -66,21 +67,23 @@ import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome; import sun.reflect.CallerSensitive; /** * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}. *

- * For every protected or public constructor in the extended class, the adapter class will have between one to three + * For every protected or public constructor in the extended class, the adapter class will have either one or two * public constructors (visibility of protected constructors in the extended class is promoted to public). - *

    - *
  • In every case, a constructor taking a trailing ScriptObject argument preceded by original constructor arguments - * is always created on the adapter class. When such a constructor is invoked, the passed ScriptObject's member - * functions are used to implement and/or override methods on the original class, dispatched by name. A single - * JavaScript function will act as the implementation for all overloaded methods of the same name. When methods on an - * adapter instance are invoked, the functions are invoked having the ScriptObject passed in the instance constructor as - * their "this". Subsequent changes to the ScriptObject (reassignment or removal of its functions) are not reflected in - * the adapter instance; the method implementations are bound to functions at constructor invocation time. + *
  • + *
  • For adapter classes with instance-level overrides, a constructor taking a trailing ScriptObject argument preceded + * by original constructor arguments is always created on the adapter class. When such a constructor is invoked, the + * passed ScriptObject's member functions are used to implement and/or override methods on the original class, + * dispatched by name. A single JavaScript function will act as the implementation for all overloaded methods of the + * same name. When methods on an adapter instance are invoked, the functions are invoked having the ScriptObject passed + * in the instance constructor as their "this". Subsequent changes to the ScriptObject (reassignment or removal of its + * functions) are not reflected in the adapter instance; the method implementations are bound to functions at + * constructor invocation time. * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The * only restriction is that since every JavaScript object already has a {@code toString} function through the * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a @@ -89,16 +92,17 @@ *
  • *
  • * If the original types collectively have only one abstract method, or have several of them, but all share the - * same name, an additional constructor is provided for every original constructor; this one takes a ScriptFunction as - * its last argument preceded by original constructor arguments. This constructor will use the passed function as the - * implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method - * name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is - * invoked with global or UNDEFINED as its "this" depending whether the function is non-strict or not. + * same name, an additional constructor for instance-level override adapter is provided for every original constructor; + * this one takes a ScriptFunction as its last argument preceded by original constructor arguments. This constructor + * will use the passed function as the implementation for all abstract methods. For consistency, any concrete methods + * sharing the single abstract method name will also be overridden by the function. When methods on the adapter instance + * are invoked, the ScriptFunction is invoked with UNDEFINED or Global as its "this" depending whether the function is + * strict or not. *
  • *
  • * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass - * constructors are also created. These constructors simply delegate to the superclass constructor. They are used to - * create instances of the adapter class with no instance-level overrides. + * constructors are created. These constructors simply delegate to the superclass constructor. They are simply used to + * create instances of the adapter class, with no instance-level overrides, as they don't have them. *
  • *
*

@@ -111,16 +115,20 @@ * source-level script expression new X(a, b) { ... } (which is a proprietary syntax extension Nashorn uses * to resemble Java anonymous classes) is actually equivalent to new X(a, b, { ... }). *

- * It is possible to create two different classes: those that can have both class-level and instance-level overrides, - * and those that can only have instance-level overrides. When - * {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked with non-null {@code classOverrides} - * parameter, an adapter class is created that can have class-level overrides, and the passed script object will be used - * as the implementations for its methods, just as in the above case of the constructor taking a script object. Note - * that in the case of class-level overrides, a new adapter class is created on every invocation, and the implementation - * object is bound to the class, not to any instance. All created instances will share these functions. Of course, when - * instances of such a class are being created, they can still take another object (or possibly a function) in their - * constructor's trailing position and thus provide further instance-specific overrides. The order of invocation is - * always instance-specified method, then a class-specified method, and finally the superclass method. + * It is possible to create two different adapter classes: those that can have class-level overrides, and those that can + * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked + * with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and + * the passed script object will be used as the implementations for its methods, just as in the above case of the + * constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on + * every invocation, and the implementation object is bound to the class, not to any instance. All created instances + * will share these functions. If it is required to have both class-level overrides and instance-level overrides, the + * class-level override adapter class should be subclassed with an instance-override adapter. Since adapters delegate to + * super class when an overriding method handle is not specified, this will behave as expected. It is not possible to + * have both class-level and instance-level overrides in the same class for security reasons: adapter classes are + * defined with a protection domain of their creator code, and an adapter class that has both class and instance level + * overrides would need to have two potentially different protection domains: one for class-based behavior and one for + * instance-based behavior; since Java classes can only belong to a single protection domain, this could not be + * implemented securely. */ final class JavaAdapterBytecodeGenerator { static final Type CONTEXT_TYPE = Type.getType(Context.class); @@ -171,7 +179,6 @@ private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255; private static final String CLASS_INIT = ""; - private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal"; // Method name prefix for invoking super-methods static final String SUPER_PREFIX = "super$"; @@ -199,6 +206,7 @@ private final Set finalMethods = new HashSet<>(EXCLUDED); private final Set methodInfos = new HashSet<>(); private boolean autoConvertibleFromFunction = false; + private boolean hasExplicitFinalizer = false; private final ClassWriter cw; @@ -207,8 +215,8 @@ * @param superClass the superclass the adapter will extend. * @param interfaces the interfaces the adapter will implement. * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes. - * @param classOverride true to generate the bytecode for the adapter that has both class-level and instance-level - * overrides, false to generate the bytecode for the adapter that only has instance-level overrides. + * @param classOverride true to generate the bytecode for the adapter that has class-level overrides, false to + * generate the bytecode for the adapter that has instance-level overrides. * @throws AdaptationException if the adapter can not be generated for some reason. */ JavaAdapterBytecodeGenerator(final Class superClass, final List> interfaces, @@ -230,8 +238,7 @@ superClassName = Type.getInternalName(superClass); generatedClassName = getGeneratedClassName(superClass, interfaces); - cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces)); - + cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER, generatedClassName, null, superClassName, getInternalTypeNames(interfaces)); generateGlobalFields(); gatherMethods(superClass); @@ -244,17 +251,16 @@ generateConstructors(); generateMethods(); generateSuperMethods(); + if (hasExplicitFinalizer) { + generateFinalizerMethods(); + } // } cw.visitEnd(); } private void generateGlobalFields() { - cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd(); + cw.visitField(ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0), GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd(); usedFieldNames.add(GLOBAL_FIELD_NAME); - if(classOverride) { - cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd(); - usedFieldNames.add(STATIC_GLOBAL_FIELD_NAME); - } } JavaAdapterClassLoader createAdapterClassLoader() { @@ -305,11 +311,9 @@ } private void generateHandleFields() { + final int flags = ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0); for (final MethodInfo mi: methodInfos) { - cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); - if(classOverride) { - cw.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); - } + cw.visitField(flags, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); } } @@ -337,7 +341,7 @@ } else { mv.visitInsn(ACONST_NULL); } - mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); + mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); } initGlobal = new Label(); mv.goTo(initGlobal); @@ -351,15 +355,15 @@ mv.aconst(mi.getName()); mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString())); mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR); - mv.putstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); + mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); } if(initGlobal != null) { mv.visitLabel(initGlobal); } - // Assign "staticGlobal = Context.getGlobal()" + // Assign "global = Context.getGlobal()" invokeGetGlobalWithNullCheck(mv); - mv.putstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); + mv.putstatic(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); endInitMethod(mv); } @@ -390,21 +394,21 @@ // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want // to create instances without further per-instance overrides. generateDelegatingConstructor(ctor); - } - - // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the - // beginning of its parameter list. - generateOverridingConstructor(ctor, false); + } else { + // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the + // beginning of its parameter list. + generateOverridingConstructor(ctor, false); - if (samName != null) { - if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) { - // If the original type only has a single abstract method name, as well as a default ctor, then it can - // be automatically converted from JS function. - autoConvertibleFromFunction = true; + if (samName != null) { + if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) { + // If the original type only has a single abstract method name, as well as a default ctor, then it can + // be automatically converted from JS function. + autoConvertibleFromFunction = true; + } + // If all our abstract methods have a single name, generate an additional constructor, one that takes a + // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. + generateOverridingConstructor(ctor, true); } - // If all our abstract methods have a single name, generate an additional constructor, one that takes a - // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. - generateOverridingConstructor(ctor, true); } } @@ -430,7 +434,7 @@ } /** - * Generates a constructor for the adapter class. This constructor will take the same arguments as the supertype + * Generates a constructor for the instance adapter class. This constructor will take the same arguments as the supertype * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize * all the method handle fields of the adapter instance with functions from the script object (or the script @@ -498,7 +502,7 @@ mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString())); mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor); } - mv.putfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); + mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); } // Assign "this.global = Context.getGlobal()" @@ -536,8 +540,7 @@ private static class MethodInfo { private final Method method; private final MethodType type; - private String methodHandleInstanceFieldName; - private String methodHandleClassFieldName; + private String methodHandleFieldName; private MethodInfo(final Class clazz, final String name, final Class... argTypes) throws NoSuchMethodException { this(clazz.getDeclaredMethod(name, argTypes)); @@ -567,25 +570,20 @@ return getName().hashCode() ^ type.hashCode(); } - void setIsCanonical(final Set usedFieldNames, boolean classOverride) { - methodHandleInstanceFieldName = nextName(usedFieldNames); - if(classOverride) { - methodHandleClassFieldName = nextName(usedFieldNames); - } + void setIsCanonical(final JavaAdapterBytecodeGenerator self) { + methodHandleFieldName = self.nextName(getName()); } + } - String nextName(final Set usedFieldNames) { - int i = 0; - final String name = getName(); - String nextName = name; - while (!usedFieldNames.add(nextName)) { - final String ordinal = String.valueOf(i++); - final int maxNameLen = 255 - ordinal.length(); - nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal); - } - return nextName; + private String nextName(final String name) { + int i = 0; + String nextName = name; + while (!usedFieldNames.add(nextName)) { + final String ordinal = String.valueOf(i++); + final int maxNameLen = 255 - ordinal.length(); + nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal); } - + return nextName; } private void generateMethods() { @@ -624,23 +622,19 @@ methodDesc, null, exceptionNames)); mv.visitCode(); - final Label instanceHandleDefined = new Label(); - final Label classHandleDefined = new Label(); + final Label handleDefined = new Label(); final Type asmReturnType = Type.getType(type.returnType()); - // See if we have instance handle defined - mv.visitVarInsn(ALOAD, 0); - mv.getfield(generatedClassName, mi.methodHandleInstanceFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); - // stack: [instanceHandle] - jumpIfNonNullKeepOperand(mv, instanceHandleDefined); - + // See if we have overriding method handle defined if(classOverride) { - // See if we have the static handle - mv.getstatic(generatedClassName, mi.methodHandleClassFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); - // stack: [classHandle] - jumpIfNonNullKeepOperand(mv, classHandleDefined); + mv.getstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); } + // stack: [handle] + jumpIfNonNullKeepOperand(mv, handleDefined); // No handle is available, fall back to default behavior if(Modifier.isAbstract(method.getModifiers())) { @@ -654,25 +648,17 @@ emitSuperCall(mv, name, methodDesc); } - final Label setupGlobal = new Label(); - + mv.visitLabel(handleDefined); + // Load the creatingGlobal object if(classOverride) { - mv.visitLabel(classHandleDefined); // If class handle is defined, load the static defining global - mv.getstatic(generatedClassName, STATIC_GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); - // stack: [creatingGlobal := classGlobal, classHandle] - mv.goTo(setupGlobal); + mv.getstatic(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); } - - mv.visitLabel(instanceHandleDefined); - // If instance handle is defined, load the instance defining global - mv.visitVarInsn(ALOAD, 0); - mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); - // stack: [creatingGlobal := instanceGlobal, instanceHandle] - - // fallthrough to setupGlobal - - // stack: [creatingGlobal, someHandle] + // stack: [creatingGlobal, handle] + final Label setupGlobal = new Label(); mv.visitLabel(setupGlobal); // Determine the first index for a local variable @@ -685,38 +671,39 @@ final int globalsDifferVar = nextLocalVar++; mv.dup(); - // stack: [creatingGlobal, creatingGlobal, someHandle] + // stack: [creatingGlobal, creatingGlobal, handle] // Emit code for switching to the creating global // ScriptObject currentGlobal = Context.getGlobal(); invokeGetGlobal(mv); mv.dup(); + mv.visitVarInsn(ASTORE, currentGlobalVar); - // stack: [currentGlobal, creatingGlobal, creatingGlobal, someHandle] + // stack: [currentGlobal, creatingGlobal, creatingGlobal, handle] // if(definingGlobal == currentGlobal) { final Label globalsDiffer = new Label(); mv.ifacmpne(globalsDiffer); - // stack: [someGlobal, someHandle] + // stack: [creatingGlobal, handle] // globalsDiffer = false mv.pop(); - // stack: [someHandle] + // stack: [handle] mv.iconst(0); // false - // stack: [false, someHandle] + // stack: [false, handle] final Label invokeHandle = new Label(); mv.goTo(invokeHandle); mv.visitLabel(globalsDiffer); // } else { // Context.setGlobal(definingGlobal); - // stack: [someGlobal, someHandle] + // stack: [creatingGlobal, handle] invokeSetGlobal(mv); - // stack: [someHandle] + // stack: [handle] // globalsDiffer = true mv.iconst(1); - // stack: [true, someHandle] + // stack: [true, handle] mv.visitLabel(invokeHandle); mv.visitVarInsn(ISTORE, globalsDifferVar); - // stack: [someHandle] + // stack: [handle] // Load all parameters back on stack for dynamic invocation. int varOffset = 1; @@ -847,6 +834,42 @@ mv.areturn(methodType.getReturnType()); } + private void generateFinalizerMethods() { + final String finalizerDelegateName = nextName("access$"); + generateFinalizerDelegate(finalizerDelegateName); + generateFinalizerOverride(finalizerDelegateName); + } + + private void generateFinalizerDelegate(final String finalizerDelegateName) { + // Generate a delegate that will be invoked from the no-permission trampoline. Note it can be private, as we'll + // refer to it with a MethodHandle constant pool entry in the overridden finalize() method (see + // generateFinalizerOverride()). + final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PRIVATE | ACC_STATIC, + finalizerDelegateName, Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE), null, null)); + + // Simply invoke super.finalize() + mv.visitVarInsn(ALOAD, 0); + mv.checkcast(Type.getType(generatedClassName)); + mv.invokespecial(superClassName, "finalize", Type.getMethodDescriptor(Type.VOID_TYPE), false); + + mv.visitInsn(RETURN); + endMethod(mv); + } + + private void generateFinalizerOverride(final String finalizerDelegateName) { + final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, "finalize", + VOID_NOARG_METHOD_DESCRIPTOR, null, null)); + // Overridden finalizer will take a MethodHandle to the finalizer delegating method, ... + mv.aconst(new Handle(Opcodes.H_INVOKESTATIC, generatedClassName, finalizerDelegateName, + Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE))); + mv.visitVarInsn(ALOAD, 0); + // ...and invoke it through JavaAdapterServices.invokeNoPermissions + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "invokeNoPermissions", + Type.getMethodDescriptor(METHOD_HANDLE_TYPE, OBJECT_TYPE), false); + mv.visitInsn(RETURN); + endMethod(mv); + } + private static String[] getExceptionNames(final Class[] exceptions) { final String[] exceptionNames = new String[exceptions.length]; for (int i = 0; i < exceptions.length; ++i) { @@ -867,16 +890,32 @@ * class. * @param type the type defining the methods. */ - private void gatherMethods(final Class type) { + private void gatherMethods(final Class type) throws AdaptationException { if (Modifier.isPublic(type.getModifiers())) { final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods(); for (final Method typeMethod: typeMethods) { + final String name = typeMethod.getName(); + if(name.startsWith(SUPER_PREFIX)) { + continue; + } final int m = typeMethod.getModifiers(); if (Modifier.isStatic(m)) { continue; } if (Modifier.isPublic(m) || Modifier.isProtected(m)) { + // Is it a "finalize()"? + if(name.equals("finalize") && typeMethod.getParameterCount() == 0) { + if(type != Object.class) { + hasExplicitFinalizer = true; + if(Modifier.isFinal(m)) { + // Must be able to override an explicit finalizer + throw new AdaptationException(Outcome.ERROR_FINAL_FINALIZER, type.getCanonicalName()); + } + } + continue; + } + final MethodInfo mi = new MethodInfo(typeMethod); if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) { finalMethods.add(mi); @@ -884,7 +923,7 @@ if (Modifier.isAbstract(m)) { abstractMethodNames.add(mi.getName()); } - mi.setIsCanonical(usedFieldNames, classOverride); + mi.setIsCanonical(this); } } } @@ -905,7 +944,7 @@ } } - private void gatherMethods(final List> classes) { + private void gatherMethods(final List> classes) throws AdaptationException { for(final Class c: classes) { gatherMethods(c); } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java Thu Jan 30 20:14:29 2014 +0100 @@ -27,10 +27,6 @@ import java.security.AccessControlContext; import java.security.AccessController; -import java.security.AllPermission; -import java.security.CodeSigner; -import java.security.CodeSource; -import java.security.Permissions; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.security.SecureClassLoader; @@ -45,11 +41,10 @@ */ @SuppressWarnings("javadoc") final class JavaAdapterClassLoader { - private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain(); private static final AccessControlContext CREATE_LOADER_ACC_CTXT = ClassAndLoader.createPermAccCtxt("createClassLoader"); private final String className; - private volatile byte[] classBytes; + private final byte[] classBytes; JavaAdapterClassLoader(String className, byte[] classBytes) { this.className = className.replace('/', '.'); @@ -57,23 +52,18 @@ } /** - * clear classBytes after loading class. - */ - void clearClassBytes() { - this.classBytes = null; - } - - /** * Loads the generated adapter class into the JVM. * @param parentLoader the parent class loader for the generated class loader + * @param protectionDomain the protection domain for the generated class * @return the generated adapter class */ - StaticClass generateClass(final ClassLoader parentLoader) { + StaticClass generateClass(final ClassLoader parentLoader, final ProtectionDomain protectionDomain) { + assert protectionDomain != null; return AccessController.doPrivileged(new PrivilegedAction() { @Override public StaticClass run() { try { - return StaticClass.forClass(Class.forName(className, true, createClassLoader(parentLoader))); + return StaticClass.forClass(Class.forName(className, true, createClassLoader(parentLoader, protectionDomain))); } catch (final ClassNotFoundException e) { throw new AssertionError(e); // cannot happen } @@ -88,7 +78,7 @@ // it even more by separating its invocation into a separate static method on the adapter class, but then someone // with ability to introspect on the class and use setAccessible(true) on it could invoke the method. It's a // security tradeoff... - private ClassLoader createClassLoader(final ClassLoader parentLoader) { + private ClassLoader createClassLoader(final ClassLoader parentLoader, final ProtectionDomain protectionDomain) { return new SecureClassLoader(parentLoader) { private final ClassLoader myLoader = getClass().getClassLoader(); @@ -112,21 +102,10 @@ protected Class findClass(final String name) throws ClassNotFoundException { if(name.equals(className)) { assert classBytes != null : "what? already cleared .class bytes!!"; - return defineClass(name, classBytes, 0, classBytes.length, GENERATED_PROTECTION_DOMAIN); + return defineClass(name, classBytes, 0, classBytes.length, protectionDomain); } throw new ClassNotFoundException(name); } }; } - - private static ProtectionDomain createGeneratedProtectionDomain() { - // Generated classes need to have AllPermission. Since we require the "createClassLoader" RuntimePermission, we - // can create a class loader that'll load new classes with any permissions. Our generated classes are just - // delegating adapters, so having AllPermission can't cause anything wrong; the effective set of permissions for - // the executing script functions will still be limited by the permissions of the caller and the permissions of - // the script. - final Permissions permissions = new Permissions(); - permissions.add(new AllPermission()); - return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions); - } } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java Thu Jan 30 20:14:29 2014 +0100 @@ -29,17 +29,23 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.lang.reflect.Modifier; import java.security.AccessControlContext; import java.security.AccessController; +import java.security.CodeSigner; +import java.security.CodeSource; +import java.security.Permissions; import java.security.PrivilegedAction; +import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.support.LinkRequestImpl; import jdk.nashorn.internal.objects.NativeJava; @@ -70,6 +76,8 @@ @SuppressWarnings("javadoc") public final class JavaAdapterFactory { + private static final ProtectionDomain MINIMAL_PERMISSION_DOMAIN = createMinimalPermissionDomain(); + // context with permissions needs for AdapterInfo creation private static final AccessControlContext CREATE_ADAPTER_INFO_ACC_CTXT = ClassAndLoader.createPermAccCtxt("createClassLoader", "getClassLoader", @@ -99,11 +107,18 @@ * @param classOverrides a JavaScript object with functions serving as the class-level overrides and * implementations. These overrides are defined for all instances of the class, and can be further overridden on a * per-instance basis by passing additional objects in the constructor. + * @param lookup the lookup object identifying the caller class. The generated adapter class will have the + * protection domain of the caller class iff the lookup object is full-strength, otherwise it will be completely + * unprivileged. * @return an adapter class. See this class' documentation for details on the generated adapter class. * @throws ECMAException with a TypeError if the adapter class can not be generated because the original class is * final, non-public, or has no public or protected constructors. */ - public static StaticClass getAdapterClassFor(final Class[] types, ScriptObject classOverrides) { + public static StaticClass getAdapterClassFor(final Class[] types, ScriptObject classOverrides, final MethodHandles.Lookup lookup) { + return getAdapterClassFor(types, classOverrides, getProtectionDomain(lookup)); + } + + private static StaticClass getAdapterClassFor(final Class[] types, ScriptObject classOverrides, final ProtectionDomain protectionDomain) { assert types != null && types.length > 0; final SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -114,7 +129,23 @@ ReflectionCheckLinker.checkReflectionAccess(type, true); } } - return getAdapterInfo(types).getAdapterClassFor(classOverrides); + return getAdapterInfo(types).getAdapterClass(classOverrides, protectionDomain); + } + + private static ProtectionDomain getProtectionDomain(final MethodHandles.Lookup lookup) { + if((lookup.lookupModes() & Lookup.PRIVATE) == 0) { + return MINIMAL_PERMISSION_DOMAIN; + } + return getProtectionDomain(lookup.lookupClass()); + } + + private static ProtectionDomain getProtectionDomain(final Class clazz) { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ProtectionDomain run() { + return clazz.getProtectionDomain(); + } + }); } /** @@ -129,10 +160,10 @@ * @return the constructor method handle. * @throws Exception if anything goes wrong */ - public static MethodHandle getConstructor(final Class sourceType, final Class targetType) throws Exception { - final StaticClass adapterClass = getAdapterClassFor(new Class[] { targetType }, null); + public static MethodHandle getConstructor(final Class sourceType, final Class targetType, final MethodHandles.Lookup lookup) throws Exception { + final StaticClass adapterClass = getAdapterClassFor(new Class[] { targetType }, null, lookup); return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl( - NashornCallSiteDescriptor.get(MethodHandles.publicLookup(), "dyn:new", + NashornCallSiteDescriptor.get(lookup, "dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false, adapterClass, null)).getInvocation(), adapterClass); } @@ -220,10 +251,10 @@ private static final ClassAndLoader SCRIPT_OBJECT_LOADER = new ClassAndLoader(ScriptObject.class, true); private final ClassLoader commonLoader; - private final JavaAdapterClassLoader adapterGenerator; - // Cacheable adapter class that is shared by all adapter instances that don't have class overrides, only - // instance overrides. - final StaticClass instanceAdapterClass; + // TODO: soft reference the JavaAdapterClassLoader objects. They can be recreated when needed. + private final JavaAdapterClassLoader classAdapterGenerator; + private final JavaAdapterClassLoader instanceAdapterGenerator; + private final Map instanceAdapters = new ConcurrentHashMap<>(); final boolean autoConvertibleFromFunction; final AdaptationResult adaptationResult; @@ -231,11 +262,8 @@ this.commonLoader = findCommonLoader(definingLoader); final JavaAdapterBytecodeGenerator gen = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, false); this.autoConvertibleFromFunction = gen.isAutoConvertibleFromFunction(); - final JavaAdapterClassLoader jacl = gen.createAdapterClassLoader(); - this.instanceAdapterClass = jacl.generateClass(commonLoader); - // loaded Class - no need to keep class bytes around - jacl.clearClassBytes(); - this.adapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader(); + instanceAdapterGenerator = gen.createAdapterClassLoader(); + this.classAdapterGenerator = new JavaAdapterBytecodeGenerator(superClass, interfaces, commonLoader, true).createAdapterClassLoader(); this.adaptationResult = AdaptationResult.SUCCESSFUL_RESULT; } @@ -245,22 +273,42 @@ AdapterInfo(final AdaptationResult adaptationResult) { this.commonLoader = null; - this.adapterGenerator = null; - this.instanceAdapterClass = null; + this.classAdapterGenerator = null; + this.instanceAdapterGenerator = null; this.autoConvertibleFromFunction = false; this.adaptationResult = adaptationResult; } - StaticClass getAdapterClassFor(ScriptObject classOverrides) { + StaticClass getAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) { if(adaptationResult.getOutcome() != AdaptationResult.Outcome.SUCCESS) { throw adaptationResult.typeError(); } - if(classOverrides == null) { + return classOverrides == null ? getInstanceAdapterClass(protectionDomain) : + getClassAdapterClass(classOverrides, protectionDomain); + } + + private StaticClass getInstanceAdapterClass(final ProtectionDomain protectionDomain) { + CodeSource codeSource = protectionDomain.getCodeSource(); + if(codeSource == null) { + codeSource = MINIMAL_PERMISSION_DOMAIN.getCodeSource(); + } + StaticClass instanceAdapterClass = instanceAdapters.get(codeSource); + if(instanceAdapterClass != null) { return instanceAdapterClass; } + // Any "unknown source" code source will default to no permission domain. + final ProtectionDomain effectiveDomain = codeSource.equals(MINIMAL_PERMISSION_DOMAIN.getCodeSource()) ? + MINIMAL_PERMISSION_DOMAIN : protectionDomain; + + instanceAdapterClass = instanceAdapterGenerator.generateClass(commonLoader, effectiveDomain); + final StaticClass existing = instanceAdapters.putIfAbsent(codeSource, instanceAdapterClass); + return existing == null ? instanceAdapterClass : existing; + } + + private StaticClass getClassAdapterClass(final ScriptObject classOverrides, final ProtectionDomain protectionDomain) { JavaAdapterServices.setClassOverrides(classOverrides); try { - return adapterGenerator.generateClass(commonLoader); + return classAdapterGenerator.generateClass(commonLoader, protectionDomain); } finally { JavaAdapterServices.setClassOverrides(null); } @@ -285,4 +333,12 @@ throw new AdaptationException(AdaptationResult.Outcome.ERROR_NO_COMMON_LOADER, classAndLoader.getRepresentativeClass().getCanonicalName()); } } + + private static ProtectionDomain createMinimalPermissionDomain() { + // Generated classes need to have at least the permission to access Nashorn runtime and runtime.linker packages. + final Permissions permissions = new Permissions(); + permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime")); + permissions.add(new RuntimePermission("accessClassInPackage.jdk.nashorn.internal.runtime.linker")); + return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions); + } } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java --- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java Thu Jan 30 20:14:29 2014 +0100 @@ -25,10 +25,28 @@ package jdk.nashorn.internal.runtime.linker; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; +import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; +import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; +import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.security.AccessController; +import java.security.CodeSigner; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.security.SecureClassLoader; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.Type; +import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -40,6 +58,7 @@ */ public final class JavaAdapterServices { private static final ThreadLocal classOverrides = new ThreadLocal<>(); + private static final MethodHandle NO_PERMISSIONS_INVOKER = createNoPermissionsInvoker(); private JavaAdapterServices() { } @@ -55,7 +74,7 @@ */ public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type) { // JS "this" will be global object or undefined depending on if 'fn' is strict or not - return adaptHandle(fn.getBoundInvokeHandle(fn.isStrict()? ScriptRuntime.UNDEFINED : Context.getGlobal()), type); + return bindAndAdaptHandle(fn, fn.isStrict()? ScriptRuntime.UNDEFINED : Context.getGlobal(), type); } /** @@ -83,7 +102,7 @@ final Object fnObj = sobj.get(name); if (fnObj instanceof ScriptFunction) { - return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type); + return bindAndAdaptHandle((ScriptFunction)fnObj, sobj, type); } else if(fnObj == null || fnObj instanceof Undefined) { return null; } else { @@ -103,11 +122,67 @@ return overrides; } + /** + * Takes a method handle and an argument to it, and invokes the method handle passing it the argument. Basically + * equivalent to {@code method.invokeExact(arg)}, except that the method handle will be invoked in a protection + * domain with absolutely no permissions. + * @param method the method handle to invoke. The handle must have the exact type of {@code void(Object)}. + * @param arg the argument to pass to the handle. + * @throws Throwable if anything goes wrong. + */ + public static void invokeNoPermissions(final MethodHandle method, final Object arg) throws Throwable { + NO_PERMISSIONS_INVOKER.invokeExact(method, arg); + } + static void setClassOverrides(ScriptObject overrides) { classOverrides.set(overrides); } - private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type) { - return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, false), type); + private static MethodHandle bindAndAdaptHandle(final ScriptFunction fn, final Object self, final MethodType type) { + return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(fn.getBoundInvokeHandle(self), type, false), type); + } + + private static MethodHandle createNoPermissionsInvoker() { + final String className = "NoPermissionsInvoker"; + + final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, className, null, "java/lang/Object", null); + final Type objectType = Type.getType(Object.class); + final Type methodHandleType = Type.getType(MethodHandle.class); + final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invoke", + Type.getMethodDescriptor(Type.VOID_TYPE, methodHandleType, objectType), null, null)); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.invokevirtual(methodHandleType.getInternalName(), "invokeExact", Type.getMethodDescriptor( + Type.VOID_TYPE, objectType), false); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + cw.visitEnd(); + final byte[] bytes = cw.toByteArray(); + + final ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public ClassLoader run() { + return new SecureClassLoader(null) { + @Override + protected Class findClass(String name) throws ClassNotFoundException { + if(name.equals(className)) { + return defineClass(name, bytes, 0, bytes.length, new ProtectionDomain( + new CodeSource(null, (CodeSigner[])null), new Permissions())); + } + throw new ClassNotFoundException(name); + } + }; + } + }); + + try { + return MethodHandles.lookup().findStatic(Class.forName(className, true, loader), "invoke", + MethodType.methodType(void.class, MethodHandle.class, Object.class)); + } catch(ReflectiveOperationException e) { + throw new AssertionError(e.getMessage(), e); + } } } diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java Thu Jan 30 20:14:29 2014 +0100 @@ -25,19 +25,20 @@ package jdk.nashorn.internal.runtime.linker; +import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; -import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.HashMap; import java.util.Map; -import java.util.HashMap; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.beans.BeansLinker; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.GuardedTypeConversion; import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkRequest; @@ -134,9 +135,9 @@ } @Override - public GuardedInvocation convertToType(final Class sourceType, final Class targetType) throws Exception { + public GuardedTypeConversion convertToType(final Class sourceType, final Class targetType) throws Exception { final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); - return gi == null ? null : gi.asType(MH.type(targetType, sourceType)); + return gi == null ? null : new GuardedTypeConversion(gi.asType(MH.type(targetType, sourceType)), true); } /** diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/NashornLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java Thu Jan 30 20:14:29 2014 +0100 @@ -29,7 +29,10 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Deque; import java.util.List; import java.util.Map; @@ -37,16 +40,17 @@ import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.ConversionComparator; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.GuardedTypeConversion; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.internal.dynalink.support.Guards; +import jdk.internal.dynalink.support.LinkerServicesImpl; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.objects.NativeArray; -import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -100,9 +104,16 @@ } @Override - public GuardedInvocation convertToType(final Class sourceType, final Class targetType) throws Exception { - final GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); - return gi == null ? null : gi.asType(MH.type(targetType, sourceType)); + public GuardedTypeConversion convertToType(final Class sourceType, final Class targetType) throws Exception { + GuardedInvocation gi = convertToTypeNoCast(sourceType, targetType); + if(gi != null) { + return new GuardedTypeConversion(gi.asType(MH.type(targetType, sourceType)), true); + } + gi = getSamTypeConverter(sourceType, targetType); + if(gi != null) { + return new GuardedTypeConversion(gi.asType(MH.type(targetType, sourceType)), false); + } + return null; } /** @@ -126,12 +137,7 @@ return arrayConverter; } - final GuardedInvocation mirrorConverter = getMirrorConverter(sourceType, targetType); - if(mirrorConverter != null) { - return mirrorConverter; - } - - return getSamTypeConverter(sourceType, targetType); + return getMirrorConverter(sourceType, targetType); } /** @@ -150,13 +156,23 @@ final boolean isSourceTypeGeneric = sourceType.isAssignableFrom(ScriptFunction.class); if ((isSourceTypeGeneric || ScriptFunction.class.isAssignableFrom(sourceType)) && isAutoConvertibleFromFunction(targetType)) { - final MethodHandle ctor = JavaAdapterFactory.getConstructor(ScriptFunction.class, targetType); + final MethodHandle ctor = JavaAdapterFactory.getConstructor(ScriptFunction.class, targetType, getCurrentLookup()); assert ctor != null; // if isAutoConvertibleFromFunction() returned true, then ctor must exist. return new GuardedInvocation(ctor, isSourceTypeGeneric ? IS_SCRIPT_FUNCTION : null); } return null; } + private static Lookup getCurrentLookup() { + final LinkRequest currentRequest = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public LinkRequest run() { + return LinkerServicesImpl.getCurrentLinkRequest(); + } + }); + return currentRequest == null ? MethodHandles.publicLookup() : currentRequest.getCallSiteDescriptor().getLookup(); + } + /** * Returns a guarded invocation that converts from a source type that is NativeArray to a Java array or List or * Deque type. diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java Thu Jan 30 20:14:29 2014 +0100 @@ -31,6 +31,7 @@ import java.lang.invoke.MethodHandles; import jdk.internal.dynalink.linker.ConversionComparator; import jdk.internal.dynalink.linker.GuardedInvocation; +import jdk.internal.dynalink.linker.GuardedTypeConversion; import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; @@ -75,13 +76,13 @@ * @return a conditional converter from source to target type */ @Override - public GuardedInvocation convertToType(final Class sourceType, final Class targetType) { + public GuardedTypeConversion convertToType(final Class sourceType, final Class targetType) { final MethodHandle mh = JavaArgumentConverters.getConverter(targetType); if (mh == null) { return null; } - return new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : GUARD_PRIMITIVE).asType(mh.type().changeParameterType(0, sourceType)); + return new GuardedTypeConversion(new GuardedInvocation(mh, canLinkTypeStatic(sourceType) ? null : GUARD_PRIMITIVE).asType(mh.type().changeParameterType(0, sourceType)), true); } /** diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java --- a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java Thu Jan 30 20:14:29 2014 +0100 @@ -76,7 +76,8 @@ if (NashornLinker.isAbstractClass(receiverClass)) { // Change this link request into a link request on the adapter class. final Object[] args = request.getArguments(); - args[0] = JavaAdapterFactory.getAdapterClassFor(new Class[] { receiverClass }, null); + args[0] = JavaAdapterFactory.getAdapterClassFor(new Class[] { receiverClass }, null, + linkRequest.getCallSiteDescriptor().getLookup()); final LinkRequest adapterRequest = request.replaceArguments(request.getCallSiteDescriptor(), args); final GuardedInvocation gi = checkNullConstructor(delegate(linkerServices, adapterRequest), receiverClass); // Finally, modify the guard to test for the original abstract class. diff -r c59fb10cb0b5 -r 11b83c913cca src/jdk/nashorn/internal/runtime/resources/Messages.properties --- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Jan 30 19:45:20 2014 +0530 +++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Jan 30 20:14:29 2014 +0100 @@ -130,6 +130,7 @@ type.error.extend.ERROR_NO_ACCESSIBLE_CONSTRUCTOR=Can not extend class {0} as it has no public or protected constructors. type.error.extend.ERROR_MULTIPLE_SUPERCLASSES=Can not extend multiple classes {0}. At most one of the specified types can be a class, the rest must all be interfaces. type.error.extend.ERROR_NO_COMMON_LOADER=Can not find a common class loader for ScriptObject and {0}. +type.error.extend.ERROR_FINAL_FINALIZER=Can not extend class because {0} has a final finalize method. type.error.no.constructor.matches.args=Can not construct {0} with the passed arguments; they do not match any of its constructor signatures. type.error.no.method.matches.args=Can not invoke method {0} with the passed arguments; they do not match any of its method signatures. type.error.method.not.constructor=Java method {0} can't be used as a constructor. diff -r c59fb10cb0b5 -r 11b83c913cca test/script/basic/JDK-8014647.js --- a/test/script/basic/JDK-8014647.js Thu Jan 30 19:45:20 2014 +0530 +++ b/test/script/basic/JDK-8014647.js Thu Jan 30 20:14:29 2014 +0100 @@ -32,9 +32,10 @@ var RunnableImpl2 = Java.extend(java.lang.Runnable, function() { print("I'm runnable 2!") }) var r1 = new RunnableImpl1() var r2 = new RunnableImpl2() -var r3 = new RunnableImpl2(function() { print("I'm runnable 3!") }) +var RunnableImpl3 = Java.extend(RunnableImpl2); +var r3 = new RunnableImpl3({ run: function() { print("I'm runnable 3!") }}) r1.run() r2.run() r3.run() -print("r1.class === r2.class: " + (r1.class === r2.class)) -print("r2.class === r3.class: " + (r2.class === r3.class)) +print("r1.class !== r2.class: " + (r1.class !== r2.class)) +print("r2.class !== r3.class: " + (r2.class !== r3.class)) diff -r c59fb10cb0b5 -r 11b83c913cca test/script/basic/JDK-8014647.js.EXPECTED --- a/test/script/basic/JDK-8014647.js.EXPECTED Thu Jan 30 19:45:20 2014 +0530 +++ b/test/script/basic/JDK-8014647.js.EXPECTED Thu Jan 30 20:14:29 2014 +0100 @@ -1,5 +1,5 @@ I'm runnable 1! I'm runnable 2! I'm runnable 3! -r1.class === r2.class: false -r2.class === r3.class: true +r1.class !== r2.class: true +r2.class !== r3.class: true diff -r c59fb10cb0b5 -r 11b83c913cca test/script/basic/javaclassoverrides.js --- a/test/script/basic/javaclassoverrides.js Thu Jan 30 19:45:20 2014 +0530 +++ b/test/script/basic/javaclassoverrides.js Thu Jan 30 20:14:29 2014 +0100 @@ -46,7 +46,8 @@ var r1 = new R1 var r2 = new R2 // Create one with an instance-override too -var r3 = new R2(function() { print("r3.run() invoked") }) +var R3 = Java.extend(R2) +var r3 = new R3({ run: function() { print("r3.run() invoked") }}) // Run 'em - we're passing them through a Thread to make sure they indeed // are full-blown Runnables @@ -60,9 +61,9 @@ runInThread(r3) // Two class-override classes differ -print("r1.class != r2.class: " + (r1.class != r2.class)) -// However, adding instance-overrides doesn't change the class -print("r2.class == r3.class: " + (r2.class == r3.class)) +print("r1.class !== r2.class: " + (r1.class !== r2.class)) +// instance-override class also differs +print("r2.class !== r3.class: " + (r2.class !== r3.class)) function checkAbstract(r) { try { @@ -77,10 +78,10 @@ // overrides nor instance overrides are present var RAbstract = Java.extend(java.lang.Runnable, {}) checkAbstract(new RAbstract()) // class override (empty) -checkAbstract(new RAbstract() {}) // class+instance override (empty) +checkAbstract(new (Java.extend(RAbstract))() {}) // class+instance override (empty) // Check we delegate to superclass if neither class // overrides nor instance overrides are present var ExtendsList = Java.extend(java.util.ArrayList, {}) print("(new ExtendsList).size() = " + (new ExtendsList).size()) -print("(new ExtendsList(){}).size() = " + (new ExtendsList(){}).size()) \ No newline at end of file +print("(new (Java.extend(ExtendsList)){}).size() = " + (new (Java.extend(ExtendsList)){}).size()) diff -r c59fb10cb0b5 -r 11b83c913cca test/script/basic/javaclassoverrides.js.EXPECTED --- a/test/script/basic/javaclassoverrides.js.EXPECTED Thu Jan 30 19:45:20 2014 +0530 +++ b/test/script/basic/javaclassoverrides.js.EXPECTED Thu Jan 30 20:14:29 2014 +0100 @@ -1,9 +1,9 @@ R1.run() invoked R2.run() invoked r3.run() invoked -r1.class != r2.class: true -r2.class == r3.class: true +r1.class !== r2.class: true +r2.class !== r3.class: true Got exception: java.lang.UnsupportedOperationException Got exception: java.lang.UnsupportedOperationException (new ExtendsList).size() = 0 -(new ExtendsList(){}).size() = 0 +(new (Java.extend(ExtendsList)){}).size() = 0 diff -r c59fb10cb0b5 -r 11b83c913cca test/script/sandbox/javaextend.js --- a/test/script/sandbox/javaextend.js Thu Jan 30 19:45:20 2014 +0530 +++ b/test/script/sandbox/javaextend.js Thu Jan 30 20:14:29 2014 +0100 @@ -51,6 +51,21 @@ print(e) } +// Can't extend a class with explicit non-overridable finalizer +try { + Java.extend(model("ClassWithFinalFinalizer")) +} catch(e) { + print(e) +} + +// Can't extend a class with inherited non-overridable finalizer +try { + Java.extend(model("ClassWithInheritedFinalFinalizer")) +} catch(e) { + print(e) +} + + // Can't extend two classes try { Java.extend(java.lang.Thread,java.lang.Number) diff -r c59fb10cb0b5 -r 11b83c913cca test/script/sandbox/javaextend.js.EXPECTED --- a/test/script/sandbox/javaextend.js.EXPECTED Thu Jan 30 19:45:20 2014 +0530 +++ b/test/script/sandbox/javaextend.js.EXPECTED Thu Jan 30 20:14:29 2014 +0100 @@ -1,6 +1,8 @@ TypeError: Can not extend final class jdk.nashorn.test.models.FinalClass. TypeError: Can not extend class jdk.nashorn.test.models.NoAccessibleConstructorClass as it has no public or protected constructors. TypeError: Can not extend/implement non-public class/interface jdk.nashorn.test.models.NonPublicClass. +TypeError: Can not extend class because jdk.nashorn.test.models.ClassWithFinalFinalizer has a final finalize method. +TypeError: Can not extend class because jdk.nashorn.test.models.ClassWithFinalFinalizer has a final finalize method. TypeError: Can not extend multiple classes java.lang.Number and java.lang.Thread. At most one of the specified types can be a class, the rest must all be interfaces. abcdabcd run-object diff -r c59fb10cb0b5 -r 11b83c913cca test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java --- a/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java Thu Jan 30 19:45:20 2014 +0530 +++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java Thu Jan 30 20:14:29 2014 +0100 @@ -33,8 +33,8 @@ import java.util.Objects; import javax.script.Invocable; import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; import javax.script.ScriptException; -import javax.script.ScriptEngineManager; import org.testng.annotations.Test; /** @@ -130,6 +130,23 @@ } } + + @Test + public void securitySystemExitFromFinalizerThread() throws ScriptException { + if (System.getSecurityManager() == null) { + // pass vacuously + return; + } + + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + e.eval("var o = Java.extend(Java.type('javax.imageio.spi.ServiceRegistry'), { deregisterAll: this.exit.bind(null, 1234)});\n" + + "new o(new java.util.ArrayList().iterator())"); + System.gc(); + System.runFinalization(); + // NOTE: this test just exits the VM if it fails. + } + @Test public void securitySystemLoadLibrary() { if (System.getSecurityManager() == null) { diff -r c59fb10cb0b5 -r 11b83c913cca test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java Thu Jan 30 20:14:29 2014 +0100 @@ -0,0 +1,31 @@ +/* + * 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.test.models; + +public class ClassWithFinalFinalizer { + protected final void finalize() { + } +} diff -r c59fb10cb0b5 -r 11b83c913cca test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java Thu Jan 30 20:14:29 2014 +0100 @@ -0,0 +1,29 @@ +/* + * 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.test.models; + +public class ClassWithInheritedFinalFinalizer extends ClassWithFinalFinalizer { +}