提交 1e5a0a50 编写于 作者: A attila

8023630: Implement Java.super() as the preferred way to call super methods

Reviewed-by: jlaskey, lagergren, sundar
上级 02818322
......@@ -32,7 +32,6 @@ import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Deque;
import java.util.List;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.support.TypeUtilities;
import jdk.nashorn.internal.objects.annotations.Attribute;
......@@ -44,6 +43,7 @@ import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ListAdapter;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
/**
......@@ -539,4 +539,25 @@ public final class NativeJava {
}
return JavaAdapterFactory.getAdapterClassFor(stypes, classOverrides);
}
/**
* When given an object created using {@code Java.extend()} or equivalent mechanism (that is, any JavaScript-to-Java
* adapter), returns an object that can be used to invoke superclass methods on that object. E.g.:
* <pre>
* var cw = new FilterWriterAdapter(sw) {
* write: function(s, off, len) {
* s = capitalize(s, off, len)
* cw_super.write(s, 0, s.length())
* }
* }
* var cw_super = Java.super(cw)
* </pre>
* @param self the {@code Java} object itself - not used.
* @param adapter the original Java adapter instance for which the super adapter is created.
* @return a super adapter for the original adapter
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR, name="super")
public static Object _super(final Object self, final Object adapter) {
return Bootstrap.createSuperAdapter(adapter);
}
}
......@@ -30,11 +30,10 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import java.lang.invoke.MethodHandles;
import java.util.Locale;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
* Representation for ECMAScript types - this maps directly to the ECMA script standard
......@@ -148,22 +147,10 @@ public enum JSType {
return JSType.STRING;
}
if (obj instanceof ScriptObject) {
return (obj instanceof ScriptFunction) ? JSType.FUNCTION : JSType.OBJECT;
}
if (obj instanceof StaticClass) {
if (Bootstrap.isCallable(obj)) {
return JSType.FUNCTION;
}
if (BeansLinker.isDynamicMethod(obj)) {
return JSType.FUNCTION;
}
if (obj instanceof ScriptObjectMirror) {
return ((ScriptObjectMirror)obj).isFunction()? JSType.FUNCTION : JSType.OBJECT;
}
return JSType.OBJECT;
}
......
......@@ -36,6 +36,7 @@ import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.DynamicLinker;
import jdk.internal.dynalink.DynamicLinkerFactory;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
......@@ -61,7 +62,7 @@ public final class Bootstrap {
static {
final DynamicLinkerFactory factory = new DynamicLinkerFactory();
factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(),
new BoundDynamicMethodLinker(), new JSObjectLinker(), new ReflectionCheckLinker());
new BoundDynamicMethodLinker(), new JavaSuperAdapterLinker(), new JSObjectLinker(), new ReflectionCheckLinker());
factory.setFallbackLinkers(new BeansLinker(), new NashornBottomLinker());
factory.setSyncOnRelink(true);
final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1);
......@@ -88,7 +89,8 @@ public final class Bootstrap {
return obj instanceof ScriptFunction ||
((obj instanceof ScriptObjectMirror) && ((ScriptObjectMirror)obj).isFunction()) ||
isDynamicMethod(obj) ||
isFunctionalInterfaceObject(obj);
isFunctionalInterfaceObject(obj) ||
obj instanceof StaticClass;
}
/**
......@@ -261,6 +263,16 @@ public final class Bootstrap {
return new BoundDynamicMethod(dynamicMethod, boundThis);
}
/**
* Creates a super-adapter for an adapter, that is, an adapter to the adapter that allows invocation of superclass
* methods on it.
* @param adapter the original adapter
* @return a new adapter that can be used to invoke super methods on the original adapter.
*/
public static Object createSuperAdapter(final Object adapter) {
return new JavaSuperAdapter(adapter);
}
/**
* If the given class is a reflection-specific class (anything in {@code java.lang.reflect} and
* {@code java.lang.invoke} package, as well a {@link Class} and any subclass of {@link ClassLoader}) and there is
......
......@@ -67,11 +67,12 @@ final class BoundDynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
// BeansLinker.
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
final MethodType type = descriptor.getMethodType();
final Class<?> dynamicMethodClass = dynamicMethod.getClass();
final CallSiteDescriptor newDescriptor = descriptor.changeMethodType(
type.changeParameterType(0, dynamicMethod.getClass()).changeParameterType(1, boundThis.getClass()));
type.changeParameterType(0, dynamicMethodClass).changeParameterType(1, boundThis.getClass()));
// Delegate to BeansLinker
final GuardedInvocation inv = BeansLinker.getLinkerForClass(dynamicMethod.getClass()).getGuardedInvocation(
final GuardedInvocation inv = BeansLinker.getLinkerForClass(dynamicMethodClass).getGuardedInvocation(
linkRequest.replaceArguments(newDescriptor, args), linkerServices);
if(inv == null) {
return null;
......
......@@ -174,7 +174,7 @@ final class JavaAdapterBytecodeGenerator {
private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal";
// Method name prefix for invoking super-methods
private static final String SUPER_PREFIX = "super$";
static final String SUPER_PREFIX = "super$";
/**
* Collection of methods we never override: Object.clone(), Object.finalize().
......
......@@ -34,7 +34,6 @@ import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.security.SecureClassLoader;
import jdk.internal.dynalink.beans.StaticClass;
/**
......
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime.linker;
/**
* Represents a an adapter for invoking superclass methods on an adapter instance generated by
* {@link JavaAdapterBytecodeGenerator}. Note that objects of this class are just wrappers around the adapter instances,
* without any behavior. All the behavior is defined in the {@code JavaSuperAdapterLinker}.
*/
class JavaSuperAdapter {
private final Object adapter;
JavaSuperAdapter(final Object adapter) {
adapter.getClass(); // NPE check
this.adapter = adapter;
}
public Object getAdapter() {
return adapter;
}
}
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.lookup.Lookup.EMPTY_GETTER;
import static jdk.nashorn.internal.runtime.linker.JavaAdapterBytecodeGenerator.SUPER_PREFIX;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.beans.BeansLinker;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.linker.LinkerServices;
import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.internal.dynalink.support.Lookup;
import jdk.nashorn.internal.runtime.ScriptRuntime;
/**
* A linker for instances of {@link JavaSuperAdapter}. Only links {@code getMethod} calls, by forwarding them to the
* bean linker for the adapter class and prepending {@code super$} to method names.
*
*/
final class JavaSuperAdapterLinker implements TypeBasedGuardingDynamicLinker {
private static final String GET_METHOD = "getMethod";
private static final String DYN_GET_METHOD = "dyn:" + GET_METHOD;
private static final String DYN_GET_METHOD_FIXED = DYN_GET_METHOD + ":" + SUPER_PREFIX;
private static final MethodHandle ADD_PREFIX_TO_METHOD_NAME;
private static final MethodHandle BIND_DYNAMIC_METHOD;
private static final MethodHandle GET_ADAPTER;
private static final MethodHandle IS_ADAPTER_OF_CLASS;
static {
final Lookup lookup = new Lookup(MethodHandles.lookup());
ADD_PREFIX_TO_METHOD_NAME = lookup.findOwnStatic("addPrefixToMethodName", Object.class, Object.class);
BIND_DYNAMIC_METHOD = lookup.findOwnStatic("bindDynamicMethod", Object.class, Object.class, Object.class);
GET_ADAPTER = lookup.findVirtual(JavaSuperAdapter.class, "getAdapter", MethodType.methodType(Object.class));
IS_ADAPTER_OF_CLASS = lookup.findOwnStatic("isAdapterOfClass", boolean.class, Class.class, Object.class);
}
@Override
public boolean canLinkType(final Class<?> type) {
return type == JavaSuperAdapter.class;
}
@Override
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
throws Exception {
final Object objSuperAdapter = linkRequest.getReceiver();
if(!(objSuperAdapter instanceof JavaSuperAdapter)) {
return null;
}
final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
if(!CallSiteDescriptorFactory.tokenizeOperators(descriptor).contains(GET_METHOD)) {
// We only handle getMethod
return null;
}
final Object adapter = ((JavaSuperAdapter)objSuperAdapter).getAdapter();
// Replace argument (javaSuperAdapter, ...) => (adapter, ...) when delegating to BeansLinker
final Object[] args = linkRequest.getArguments();
args[0] = adapter;
// Use R(T0, ...) => R(adapter.class, ...) call site type when delegating to BeansLinker.
final MethodType type = descriptor.getMethodType();
final Class<?> adapterClass = adapter.getClass();
final boolean hasFixedName = descriptor.getNameTokenCount() > 2;
final String opName = hasFixedName ? (DYN_GET_METHOD_FIXED + descriptor.getNameToken(
CallSiteDescriptor.NAME_OPERAND)) : DYN_GET_METHOD;
final CallSiteDescriptor newDescriptor = NashornCallSiteDescriptor.get(descriptor.getLookup(), opName,
type.changeParameterType(0, adapterClass), 0);
// Delegate to BeansLinker
final GuardedInvocation guardedInv = BeansLinker.getLinkerForClass(adapterClass).getGuardedInvocation(
linkRequest.replaceArguments(newDescriptor, args), linkerServices);
final MethodHandle guard = IS_ADAPTER_OF_CLASS.bindTo(adapterClass);
if(guardedInv == null) {
// Short circuit the lookup here for non-existent methods by linking an empty getter. If we just returned
// null instead, BeansLinker would find final methods on the JavaSuperAdapter instead: getClass() and
// wait().
return new GuardedInvocation(MethodHandles.dropArguments(EMPTY_GETTER, 1,type.parameterList().subList(1,
type.parameterCount())), guard).asType(descriptor);
}
final MethodHandle invocation = guardedInv.getInvocation();
final MethodType invType = invocation.type();
// For invocation typed R(T0, ...) create a dynamic method binder of type R(R, T0)
final MethodHandle typedBinder = BIND_DYNAMIC_METHOD.asType(MethodType.methodType(invType.returnType(),
invType.returnType(), invType.parameterType(0)));
// For invocation typed R(T0, T1, ...) create a dynamic method binder of type R(R, T0, T1, ...)
final MethodHandle droppingBinder = MethodHandles.dropArguments(typedBinder, 2,
invType.parameterList().subList(1, invType.parameterCount()));
// Finally, fold the invocation into the binder to produce a method handle that will bind every returned
// DynamicMethod object from dyn:getMethod calls to the actual receiver
// R(R(T0, T1, ...), T0, T1, ...)
final MethodHandle bindingInvocation = MethodHandles.foldArguments(droppingBinder, invocation);
final MethodHandle typedGetAdapter = asFilterType(GET_ADAPTER, 0, invType, type);
final MethodHandle adaptedInvocation;
if(hasFixedName) {
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter);
} else {
// Add a filter that'll prepend "super$" to each name passed to the variable-name "dyn:getMethod".
final MethodHandle typedAddPrefix = asFilterType(ADD_PREFIX_TO_METHOD_NAME, 1, invType, type);
adaptedInvocation = MethodHandles.filterArguments(bindingInvocation, 0, typedGetAdapter, typedAddPrefix);
}
return guardedInv.replaceMethods(adaptedInvocation, guard).asType(descriptor);
}
/**
* Adapts the type of a method handle used as a filter in a position from a source method type to a target method type.
* @param filter the filter method handle
* @param pos the position in the argument list that it's filtering
* @param targetType the target method type for filtering
* @param sourceType the source method type for filtering
* @return a type adapted filter
*/
private static MethodHandle asFilterType(final MethodHandle filter, int pos, MethodType targetType, MethodType sourceType) {
return filter.asType(MethodType.methodType(targetType.parameterType(pos), sourceType.parameterType(pos)));
}
@SuppressWarnings("unused")
private static Object addPrefixToMethodName(final Object name) {
return SUPER_PREFIX.concat(String.valueOf(name));
}
/**
* Used to transform the return value of getMethod; transform a {@code DynamicMethod} into a
* {@code BoundDynamicMethod} while also accounting for the possibility of a non-existent method.
* @param dynamicMethod the dynamic method to bind
* @param boundThis the adapter underlying a super adapter, to which the dynamic method is bound.
* @return a dynamic method bound to the adapter instance.
*/
@SuppressWarnings("unused")
private static Object bindDynamicMethod(final Object dynamicMethod, final Object boundThis) {
return dynamicMethod == null ? ScriptRuntime.UNDEFINED : Bootstrap.bindDynamicMethod(dynamicMethod, boundThis);
}
/**
* Used as the guard of linkages, as the receiver is not guaranteed to be a JavaSuperAdapter.
* @param clazz the class the receiver's adapter is tested against.
* @param obj receiver
* @return true if the receiver is a super adapter, and its underlying adapter is of the specified class
*/
@SuppressWarnings("unused")
private static boolean isAdapterOfClass(Class<?> clazz, Object obj) {
return obj instanceof JavaSuperAdapter && clazz == (((JavaSuperAdapter)obj).getAdapter()).getClass();
}
}
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8023630: Implement Java.super() as the preferred way to call super methods
*
* @test
* @run
*/
var CharArray = Java.type("char[]")
var jString = Java.type("java.lang.String")
var Character = Java.type("java.lang.Character")
function capitalize(s) {
if(s instanceof CharArray) {
return new jString(s).toUpperCase()
}
if(s instanceof jString) {
return s.toUpperCase()
}
return Character.toUpperCase(s) // must be int
}
var sw = new (Java.type("java.io.StringWriter"))
var FilterWriterAdapter = Java.extend(Java.type("java.io.FilterWriter"))
var cw = new FilterWriterAdapter(sw) {
write: function(s, off, len) {
s = capitalize(s)
// Must handle overloads by arity
if(off === undefined) {
cw_super.write(s, 0, s.length())
} else if (typeof s === "string") {
cw_super.write(s, off, len)
}
}
}
var cw_super = Java.super(cw)
cw.write("abcd")
cw.write("e".charAt(0))
cw.write("fgh".toCharArray())
cw.write("**ijk**", 2, 3)
cw.write("***lmno**".toCharArray(), 3, 4)
cw.flush()
print(sw)
// Can invoke super for Object methods
print("cw_super has hashCode(): " + (typeof cw_super.hashCode === "function"))
print("cw_super has super equals(): " + (typeof cw_super.equals === "function"))
// Can't invoke super for final methods
print("cw_super has no getClass(): " + (typeof cw_super.getClass === "undefined"))
print("cw_super has no wait(): " + (typeof cw_super.wait === "undefined"))
var r = new (Java.type("java.lang.Runnable"))(function() {})
var r_super = Java.super(r)
// Can't invoke super for abstract methods
print("r_super has no run(): " + (typeof r_super.run === "undefined"))
// Interfaces can also invoke super Object methods
print("r_super has hashCode(): " + (typeof r_super.hashCode === "function"))
print("r_super has equals(): " + (typeof r_super.equals === "function"))
// But still can't invoke final methods
print("r_super has no getClass(): " + (typeof r_super.getClass === "undefined"))
print("r_super has no wait(): " + (typeof r_super.wait === "undefined"))
var name = "write"
print("cw_super can access write through [] getter: " + (typeof cw_super[name] === "function"))
var name = "hashCode"
print("cw_super can access hashCode through [] getter: " + (typeof cw_super[name] === "function"))
var name = "getClass"
print("cw_super can not access getClass through [] getter: " + (typeof cw_super[name] === "undefined"))
ABCDEFGHIJKLMNO
cw_super has hashCode(): true
cw_super has super equals(): true
cw_super has no getClass(): true
cw_super has no wait(): true
r_super has no run(): true
r_super has hashCode(): true
r_super has equals(): true
r_super has no getClass(): true
r_super has no wait(): true
cw_super can access write through [] getter: true
cw_super can access hashCode through [] getter: true
cw_super can not access getClass through [] getter: true
......@@ -35,7 +35,10 @@ if (typeof (5).x !== 'number') {
fail("typeof(5).x is not 'number'");
}
if (typeof (java.lang.System.out) != 'object') {
// It is function because PrintStream implements Closeable, which is
// marked with @FunctionalInterface. Yes, this means calling a stream
// like "stream()" closes it.
if (typeof (java.lang.System.out) != 'function') {
fail("typeof java.lang.System.out is not 'object'");
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册