aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorattila <none@none>2014-01-30 20:14:29 +0100
committerattila <none@none>2014-01-30 20:14:29 +0100
commit858b836f66666648a09720711712e5d5b2ba8c5a (patch)
tree2e7b80695bd3e999984e452d371656c8aaa1c336
parentf49f5e1db8fb56d4fb78dcca0115cdfce77d2831 (diff)
downloadnashorn-858b836f66666648a09720711712e5d5b2ba8c5a.tar.gz
8032681: Issues with Nashorn
Reviewed-by: ahgross, jlaskey, sundar
-rw-r--r--src/jdk/internal/dynalink/linker/GuardedTypeConversion.java102
-rw-r--r--src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java20
-rw-r--r--src/jdk/internal/dynalink/support/LinkerServicesImpl.java25
-rw-r--r--src/jdk/internal/dynalink/support/TypeConverterFactory.java73
-rw-r--r--src/jdk/nashorn/api/scripting/NashornScriptEngine.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJava.java37
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJavaImporter.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java13
-rw-r--r--src/jdk/nashorn/internal/runtime/NativeJavaPackage.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunction.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java275
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java35
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java94
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java83
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornLinker.java38
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java5
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Messages.properties1
-rw-r--r--test/script/basic/JDK-8014647.js7
-rw-r--r--test/script/basic/JDK-8014647.js.EXPECTED4
-rw-r--r--test/script/basic/javaclassoverrides.js13
-rw-r--r--test/script/basic/javaclassoverrides.js.EXPECTED6
-rw-r--r--test/script/sandbox/javaextend.js15
-rw-r--r--test/script/sandbox/javaextend.js.EXPECTED2
-rw-r--r--test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java19
-rw-r--r--test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java31
-rw-r--r--test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java29
30 files changed, 726 insertions, 242 deletions
diff --git a/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java
new file mode 100644
index 00000000..9baed7c0
--- /dev/null
+++ b/src/jdk/internal/dynalink/linker/GuardedTypeConversion.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file, and Oracle licenses the original version of this file under the BSD
+ * license:
+ */
+/*
+ Copyright 2009-2013 Attila Szegedi
+
+ Licensed under both the Apache License, Version 2.0 (the "Apache License")
+ and the BSD License (the "BSD License"), with licensee being free to
+ choose either of the two at their discretion.
+
+ You may not use this file except in compliance with either the Apache
+ License or the BSD License.
+
+ If you choose to use this file in compliance with the Apache License, the
+ following notice applies to you:
+
+ You may obtain a copy of the Apache License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+
+ If you choose to use this file in compliance with the BSD License, the
+ following notice applies to you:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the names of
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package jdk.internal.dynalink.linker;
+
+public class GuardedTypeConversion {
+ private final GuardedInvocation conversionInvocation;
+ private final boolean cacheable;
+
+ public GuardedTypeConversion(final GuardedInvocation conversionInvocation, final boolean cacheable) {
+ this.conversionInvocation = conversionInvocation;
+ this.cacheable = cacheable;
+ }
+
+ public GuardedInvocation getConversionInvocation() {
+ return conversionInvocation;
+ }
+
+ public boolean isCacheable() {
+ return cacheable;
+ }
+}
diff --git a/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java b/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java
index 30ab9467..5f66e8a4 100644
--- a/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java
+++ b/src/jdk/internal/dynalink/linker/GuardingTypeConverterFactory.java
@@ -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;
}
diff --git a/src/jdk/internal/dynalink/support/LinkerServicesImpl.java b/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
index 4eb0ca9d..3b8e7b46 100644
--- a/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
+++ b/src/jdk/internal/dynalink/support/LinkerServicesImpl.java
@@ -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();
}
}
diff --git a/src/jdk/internal/dynalink/support/TypeConverterFactory.java b/src/jdk/internal/dynalink/support/TypeConverterFactory.java
index 5ab541f3..436acad7 100644
--- a/src/jdk/internal/dynalink/support/TypeConverterFactory.java
+++ b/src/jdk/internal/dynalink/support/TypeConverterFactory.java
@@ -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;
+ }
+ }
}
diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
index d0fb0c31..83b0bee8 100644
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
@@ -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);
diff --git a/src/jdk/nashorn/internal/objects/NativeJava.java b/src/jdk/nashorn/internal/objects/NativeJava.java
index 5e9ac83d..b863e24f 100644
--- a/src/jdk/nashorn/internal/objects/NativeJava.java
+++ b/src/jdk/nashorn/internal/objects/NativeJava.java
@@ -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);
}
/**
diff --git a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
index c2d2bd10..3e46a2c7 100644
--- a/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
+++ b/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
@@ -33,6 +33,7 @@ import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
+import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -156,8 +157,9 @@ public final class NativeJavaImporter extends ScriptObject {
} else if (obj instanceof NativeJavaPackage) {
final String pkgName = ((NativeJavaPackage)obj).getName();
final String fullName = pkgName.isEmpty() ? name : (pkgName + "." + name);
+ final Context context = Global.instance().getContext();
try {
- return StaticClass.forClass(Class.forName(fullName));
+ return StaticClass.forClass(context.findClass(fullName));
} catch (final ClassNotFoundException e) {
// IGNORE
}
diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java
index c9deee35..86b5abd8 100644
--- a/src/jdk/nashorn/internal/runtime/Context.java
+++ b/src/jdk/nashorn/internal/runtime/Context.java
@@ -647,6 +647,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.
*
* @param sm current security manager instance
diff --git a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
index 99ba9e19..534d495c 100644
--- a/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
+++ b/src/jdk/nashorn/internal/runtime/NativeJavaPackage.java
@@ -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;
}
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
index ae59855c..d0669d70 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
@@ -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);
diff --git a/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java b/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java
index de1cfccc..52511e5d 100644
--- a/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java
+++ b/src/jdk/nashorn/internal/runtime/linker/AdaptationResult.java
@@ -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, "");
diff --git a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
index f0d45317..f3c8284b 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
@@ -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);
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
index 5b8d4cfe..96819df0 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
@@ -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);
- 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);
- }
-
- // 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;
+ } else {
+ // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
+ // beginning of its parameter list.
+ generateOverridingConstructor(ctor, false);
+
+ if (samName != null) {
+ if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) {
+ // If the original type only has a single abstract method name, as well as a default ctor, then it can
+ // be automatically converted from JS function.
+ autoConvertibleFromFunction = true;
+ }
+ // If 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);
}
- 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, 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;
@@ -847,6 +834,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) {
@@ -867,16 +890,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);
@@ -884,7 +923,7 @@ final class JavaAdapterBytecodeGenerator {
if (Modifier.isAbstract(m)) {
abstractMethodNames.add(mi.getName());
}
- mi.setIsCanonical(usedFieldNames, classOverride);
+ mi.setIsCanonical(this);
}
}
}
@@ -905,7 +944,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);
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
index 0062cd5f..fa162d88 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
@@ -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,11 +41,10 @@ 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('/', '.');
@@ -57,23 +52,18 @@ final class JavaAdapterClassLoader {
}
/**
- * 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);
- }
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
index ccb2c879..5e0b890a 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterFactory.java
@@ -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);
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
index 06ae1b11..1188c6b6 100644
--- a/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
+++ b/src/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java
@@ -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);
+ }
}
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
index c94df15b..88ccf5a4 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornBottomLinker.java
@@ -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);
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
index 27e4573f..0ac5f9a5 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornLinker.java
@@ -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.
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
index ccd95fda..5cc1cd2c 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornPrimitiveLinker.java
@@ -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);
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java
index 21e2d14e..272b4ec0 100644
--- a/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java
+++ b/src/jdk/nashorn/internal/runtime/linker/NashornStaticClassLinker.java
@@ -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.
diff --git a/src/jdk/nashorn/internal/runtime/resources/Messages.properties b/src/jdk/nashorn/internal/runtime/resources/Messages.properties
index 1a37ba7b..95993c9f 100644
--- a/src/jdk/nashorn/internal/runtime/resources/Messages.properties
+++ b/src/jdk/nashorn/internal/runtime/resources/Messages.properties
@@ -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.
diff --git a/test/script/basic/JDK-8014647.js b/test/script/basic/JDK-8014647.js
index 8ecc2110..8d06848f 100644
--- a/test/script/basic/JDK-8014647.js
+++ b/test/script/basic/JDK-8014647.js
@@ -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))
diff --git a/test/script/basic/JDK-8014647.js.EXPECTED b/test/script/basic/JDK-8014647.js.EXPECTED
index 641a13b1..f4f51dcc 100644
--- a/test/script/basic/JDK-8014647.js.EXPECTED
+++ b/test/script/basic/JDK-8014647.js.EXPECTED
@@ -1,5 +1,5 @@
I'm runnable 1!
I'm runnable 2!
I'm runnable 3!
-r1.class === r2.class: false
-r2.class === r3.class: true
+r1.class !== r2.class: true
+r2.class !== r3.class: true
diff --git a/test/script/basic/javaclassoverrides.js b/test/script/basic/javaclassoverrides.js
index e7ad61d8..2fa7a857 100644
--- a/test/script/basic/javaclassoverrides.js
+++ b/test/script/basic/javaclassoverrides.js
@@ -46,7 +46,8 @@ var R2 = Java.extend(java.lang.Runnable, {
var r1 = new R1
var r2 = new R2
// Create one with an instance-override too
-var r3 = new R2(function() { print("r3.run() invoked") })
+var R3 = Java.extend(R2)
+var r3 = new R3({ run: function() { print("r3.run() invoked") }})
// Run 'em - we're passing them through a Thread to make sure they indeed
// are full-blown Runnables
@@ -60,9 +61,9 @@ runInThread(r2)
runInThread(r3)
// Two class-override classes differ
-print("r1.class != r2.class: " + (r1.class != r2.class))
-// However, adding instance-overrides doesn't change the class
-print("r2.class == r3.class: " + (r2.class == r3.class))
+print("r1.class !== r2.class: " + (r1.class !== r2.class))
+// instance-override class also differs
+print("r2.class !== r3.class: " + (r2.class !== r3.class))
function checkAbstract(r) {
try {
@@ -77,10 +78,10 @@ function checkAbstract(r) {
// overrides nor instance overrides are present
var RAbstract = Java.extend(java.lang.Runnable, {})
checkAbstract(new RAbstract()) // class override (empty)
-checkAbstract(new RAbstract() {}) // class+instance override (empty)
+checkAbstract(new (Java.extend(RAbstract))() {}) // class+instance override (empty)
// Check we delegate to superclass if neither class
// overrides nor instance overrides are present
var ExtendsList = Java.extend(java.util.ArrayList, {})
print("(new ExtendsList).size() = " + (new ExtendsList).size())
-print("(new ExtendsList(){}).size() = " + (new ExtendsList(){}).size()) \ No newline at end of file
+print("(new (Java.extend(ExtendsList)){}).size() = " + (new (Java.extend(ExtendsList)){}).size())
diff --git a/test/script/basic/javaclassoverrides.js.EXPECTED b/test/script/basic/javaclassoverrides.js.EXPECTED
index 6c534302..ceec09cf 100644
--- a/test/script/basic/javaclassoverrides.js.EXPECTED
+++ b/test/script/basic/javaclassoverrides.js.EXPECTED
@@ -1,9 +1,9 @@
R1.run() invoked
R2.run() invoked
r3.run() invoked
-r1.class != r2.class: true
-r2.class == r3.class: true
+r1.class !== r2.class: true
+r2.class !== r3.class: true
Got exception: java.lang.UnsupportedOperationException
Got exception: java.lang.UnsupportedOperationException
(new ExtendsList).size() = 0
-(new ExtendsList(){}).size() = 0
+(new (Java.extend(ExtendsList)){}).size() = 0
diff --git a/test/script/sandbox/javaextend.js b/test/script/sandbox/javaextend.js
index 33cc6b01..60eab74d 100644
--- a/test/script/sandbox/javaextend.js
+++ b/test/script/sandbox/javaextend.js
@@ -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)
diff --git a/test/script/sandbox/javaextend.js.EXPECTED b/test/script/sandbox/javaextend.js.EXPECTED
index 69c78189..c7277459 100644
--- a/test/script/sandbox/javaextend.js.EXPECTED
+++ b/test/script/sandbox/javaextend.js.EXPECTED
@@ -1,6 +1,8 @@
TypeError: Can not extend final class jdk.nashorn.test.models.FinalClass.
TypeError: Can not extend class jdk.nashorn.test.models.NoAccessibleConstructorClass as it has no public or protected constructors.
TypeError: Can not extend/implement non-public class/interface jdk.nashorn.test.models.NonPublicClass.
+TypeError: Can not extend class because jdk.nashorn.test.models.ClassWithFinalFinalizer has a final finalize method.
+TypeError: Can not extend class because jdk.nashorn.test.models.ClassWithFinalFinalizer has a final finalize method.
TypeError: Can not extend multiple classes java.lang.Number and java.lang.Thread. At most one of the specified types can be a class, the rest must all be interfaces.
abcdabcd
run-object
diff --git a/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java b/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java
index 9cea21ba..6d0d40f6 100644
--- a/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java
+++ b/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java
@@ -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) {
diff --git a/test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java b/test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java
new file mode 100644
index 00000000..ba0d86d8
--- /dev/null
+++ b/test/src/jdk/nashorn/test/models/ClassWithFinalFinalizer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.test.models;
+
+public class ClassWithFinalFinalizer {
+ protected final void finalize() {
+ }
+}
diff --git a/test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java b/test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java
new file mode 100644
index 00000000..80393fbb
--- /dev/null
+++ b/test/src/jdk/nashorn/test/models/ClassWithInheritedFinalFinalizer.java
@@ -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 {
+}