提交 b5299681 编写于 作者: A attila

8032681: Issues with Nashorn

Reviewed-by: ahgross, jlaskey, sundar
上级 269c7739
/*
* 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;
}
}
......@@ -96,19 +96,19 @@ import jdk.internal.dynalink.support.TypeUtilities;
*/
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;
}
......@@ -98,6 +98,9 @@ import jdk.internal.dynalink.linker.LinkerServices;
*/
public class LinkerServicesImpl implements LinkerServices {
private static final RuntimePermission GET_CURRENT_LINK_REQUEST = new RuntimePermission("dynalink.getCurrentLinkRequest");
private static final ThreadLocal<LinkRequest> threadLinkRequest = new ThreadLocal<>();
private final TypeConverterFactory typeConverterFactory;
private final GuardingDynamicLinker topLevelLinker;
......@@ -135,6 +138,26 @@ public class LinkerServicesImpl implements LinkerServices {
@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();
}
}
......@@ -94,6 +94,7 @@ import java.util.List;
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 @@ public class TypeConverterFactory {
@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 @@ public class TypeConverterFactory {
}
};
private final ClassValue<ClassMap<Boolean>> canConvert = new ClassValue<ClassMap<Boolean>>() {
@Override
protected ClassMap<Boolean> computeValue(final Class<?> sourceType) {
return new ClassMap<Boolean>(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<ClassLoader>() {
@Override
......@@ -253,7 +272,7 @@ public class TypeConverterFactory {
* @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,11 +313,23 @@ public class TypeConverterFactory {
return TypeUtilities.isMethodInvocationConvertible(fromType, toType);
}
/*private*/ MethodHandle getTypeConverterNull(Class<?> sourceType, Class<?> targetType) {
final MethodHandle converter = converterMap.get(sourceType).get(targetType);
/*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) {
try {
return getCacheableTypeConverterNull(sourceType, targetType);
} catch(NotCacheableConverter e) {
return e.converter;
}
}
/*private*/ MethodHandle getCacheableTypeConverter(Class<?> sourceType, Class<?> targetType) {
return converterMap.get(sourceType).get(targetType);
}
/**
* Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
* case it will return an identity conversion (that might fail for some values at runtime). You can use this method
......@@ -309,22 +340,44 @@ public class TypeConverterFactory {
* @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;
}
}
}
......@@ -32,6 +32,7 @@ import java.io.IOException;
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 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
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 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
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);
......
......@@ -28,6 +28,7 @@ package jdk.nashorn.internal.objects;
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 @@ public final class NativeJava {
* </pre>
* We can see several important concepts in the above example:
* <ul>
* <li>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.</li>
* <li>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.</li>
* <li>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.</li>
* <li>You can't invoke {@code super.*()} from adapters for now.</li>
* <li>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}.</li>
* <li>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 @@ public final class NativeJava {
* t.join()
* </pre>
* 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:
* <pre>
* 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()
* </pre>
* 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.
* </li>
* </ul>
* @param self not used
......@@ -541,7 +547,18 @@ public final class NativeJava {
} 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);
}
/**
......
......@@ -33,6 +33,7 @@ import jdk.nashorn.internal.objects.annotations.Attribute;
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 @@ public final class NativeJavaImporter extends ScriptObject {
} 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
}
......
......@@ -646,6 +646,19 @@ public final class Context {
}
}
/**
* 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.
*
......
......@@ -85,6 +85,8 @@ public final class NativeJavaPackage extends ScriptObject {
*/
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;
}
......
......@@ -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 @@ public abstract class ScriptFunction extends ScriptObject {
}
} 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);
......
......@@ -47,7 +47,8 @@ final class AdaptationResult {
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, "");
......
......@@ -32,6 +32,7 @@ import java.util.HashMap;
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 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
}
@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 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy
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);
}
......
......@@ -27,10 +27,6 @@ package jdk.nashorn.internal.runtime.linker;
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,35 +41,29 @@ import jdk.internal.dynalink.beans.StaticClass;
*/
@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('/', '.');
this.classBytes = classBytes;
}
/**
* 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<StaticClass>() {
@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 @@ final class JavaAdapterClassLoader {
// 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 @@ final class JavaAdapterClassLoader {
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);
}
}
......@@ -29,17 +29,23 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
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 @@ import jdk.nashorn.internal.runtime.ScriptObject;
@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 @@ public final class JavaAdapterFactory {
* @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 @@ public final class JavaAdapterFactory {
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<ProtectionDomain>() {
@Override
public ProtectionDomain run() {
return clazz.getProtectionDomain();
}
});
}
/**
......@@ -129,10 +160,10 @@ public final class JavaAdapterFactory {
* @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 @@ public final class JavaAdapterFactory {
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<CodeSource, StaticClass> instanceAdapters = new ConcurrentHashMap<>();
final boolean autoConvertibleFromFunction;
final AdaptationResult adaptationResult;
......@@ -231,11 +262,8 @@ public final class JavaAdapterFactory {
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 @@ public final class JavaAdapterFactory {
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 @@ public final class JavaAdapterFactory {
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);
}
}
......@@ -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 @@ import jdk.nashorn.internal.runtime.Undefined;
*/
public final class JavaAdapterServices {
private static final ThreadLocal<ScriptObject> classOverrides = new ThreadLocal<>();
private static final MethodHandle NO_PERMISSIONS_INVOKER = createNoPermissionsInvoker();
private JavaAdapterServices() {
}
......@@ -55,7 +74,7 @@ public final class JavaAdapterServices {
*/
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 @@ public final class JavaAdapterServices {
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 @@ public final class JavaAdapterServices {
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<ClassLoader>() {
@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);
}
}
}
......@@ -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.Map;
import java.util.HashMap;
import java.util.Map;
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 @@ final class NashornBottomLinker implements GuardingDynamicLinker, GuardingTypeCo
}
@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);
}
/**
......
......@@ -29,7 +29,10 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
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 javax.script.Bindings;
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 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
}
@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 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
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 class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
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<LinkRequest>() {
@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.
......
......@@ -31,6 +31,7 @@ import java.lang.invoke.MethodHandle;
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 @@ final class NashornPrimitiveLinker implements TypeBasedGuardingDynamicLinker, Gu
* @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);
}
/**
......
......@@ -76,7 +76,8 @@ final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker {
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.
......
......@@ -130,6 +130,7 @@ type.error.extend.ERROR_NON_PUBLIC_CLASS=Can not extend/implement non-public cla
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.
......
......@@ -32,9 +32,10 @@ var RunnableImpl1 = Java.extend(java.lang.Runnable, function() { print("I'm runn
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))
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
......@@ -46,7 +46,8 @@ var R2 = Java.extend(java.lang.Runnable, {
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(r2)
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 @@ function checkAbstract(r) {
// 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())
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
......@@ -51,6 +51,21 @@ try {
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)
......
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
......
......@@ -33,8 +33,8 @@ import java.lang.reflect.Proxy;
import java.util.Objects;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.testng.annotations.Test;
/**
......@@ -130,6 +130,23 @@ public class ScriptEngineSecurityTest {
}
}
@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) {
......
/*
* 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() {
}
}
/*
* 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 {
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册