8032681: Issues with Nashorn
Reviewed-by: ahgross, jlaskey, sundar
This commit is contained in:
parent
ea24aa581b
commit
281b87b796
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
@ -161,8 +162,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
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ import java.io.PrintWriter;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.AccessControlContext;
|
||||
@ -48,7 +47,7 @@ import java.security.Permissions;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
|
||||
import jdk.nashorn.api.scripting.ScriptObjectMirror;
|
||||
@ -651,6 +650,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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,6 +59,7 @@ import java.util.Iterator;
|
||||
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.internal.org.objectweb.asm.commons.InstructionAdapter;
|
||||
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}.
|
||||
* </p><p>
|
||||
* 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).
|
||||
* <ul>
|
||||
* <li>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.
|
||||
* <li>
|
||||
* <li>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 @@ import sun.reflect.CallerSensitive;
|
||||
* </li>
|
||||
* <li>
|
||||
* 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.
|
||||
* </li>
|
||||
* <li>
|
||||
* 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.
|
||||
* </li>
|
||||
* </ul>
|
||||
* </p><p>
|
||||
@ -111,16 +115,20 @@ import sun.reflect.CallerSensitive;
|
||||
* source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses
|
||||
* to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>.
|
||||
* </p><p>
|
||||
* 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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255;
|
||||
|
||||
private static final String CLASS_INIT = "<clinit>";
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED);
|
||||
private final Set<MethodInfo> methodInfos = new HashSet<>();
|
||||
private boolean autoConvertibleFromFunction = false;
|
||||
private boolean hasExplicitFinalizer = false;
|
||||
|
||||
private final ClassWriter cw;
|
||||
|
||||
@ -207,8 +215,8 @@ final class JavaAdapterBytecodeGenerator {
|
||||
* @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<Class<?>> interfaces,
|
||||
@ -230,8 +238,7 @@ final class JavaAdapterBytecodeGenerator {
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
}
|
||||
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
} 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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
mv.aconst(mi.getName());
|
||||
mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
|
||||
mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR, false);
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
// 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);
|
||||
}
|
||||
} else {
|
||||
// Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
|
||||
// beginning of its parameter list.
|
||||
generateOverridingConstructor(ctor, false);
|
||||
|
||||
// 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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString()));
|
||||
mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor, false);
|
||||
}
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
return getName().hashCode() ^ type.hashCode();
|
||||
}
|
||||
|
||||
void setIsCanonical(final Set<String> usedFieldNames, boolean classOverride) {
|
||||
methodHandleInstanceFieldName = nextName(usedFieldNames);
|
||||
if(classOverride) {
|
||||
methodHandleClassFieldName = nextName(usedFieldNames);
|
||||
}
|
||||
void setIsCanonical(final JavaAdapterBytecodeGenerator self) {
|
||||
methodHandleFieldName = self.nextName(getName());
|
||||
}
|
||||
}
|
||||
|
||||
String nextName(final Set<String> 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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
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 @@ final class JavaAdapterBytecodeGenerator {
|
||||
emitSuperCall(mv, method.getDeclaringClass(), 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 class JavaAdapterBytecodeGenerator {
|
||||
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;
|
||||
@ -835,7 +822,7 @@ final class JavaAdapterBytecodeGenerator {
|
||||
endMethod(mv);
|
||||
}
|
||||
|
||||
private void emitSuperCall(final InstructionAdapter mv, final Class owner, final String name, final String methodDesc) {
|
||||
private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) {
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
int nextParam = 1;
|
||||
final Type methodType = Type.getMethodType(methodDesc);
|
||||
@ -853,6 +840,42 @@ final class JavaAdapterBytecodeGenerator {
|
||||
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) {
|
||||
@ -873,16 +896,32 @@ final class JavaAdapterBytecodeGenerator {
|
||||
* 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);
|
||||
@ -890,7 +929,7 @@ final class JavaAdapterBytecodeGenerator {
|
||||
if (Modifier.isAbstract(m)) {
|
||||
abstractMethodNames.add(mi.getName());
|
||||
}
|
||||
mi.setIsCanonical(usedFieldNames, classOverride);
|
||||
mi.setIsCanonical(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -911,7 +950,7 @@ final class JavaAdapterBytecodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void gatherMethods(final List<Class<?>> classes) {
|
||||
private void gatherMethods(final List<Class<?>> classes) throws AdaptationException {
|
||||
for(final Class<?> c: classes) {
|
||||
gatherMethods(c);
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
print("(new (Java.extend(ExtendsList)){}).size() = " + (new (Java.extend(ExtendsList)){}).size())
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user