8023630: Implement Java.super() as the preferred way to call super methods
authorattila
Fri, 23 Aug 2013 13:10:45 +0200
changeset 19627 90d910ec15a3
parent 19626 9e799e96c1ec
child 19628 6d484513c9fc
child 19630 99f53f31008e
8023630: Implement Java.super() as the preferred way to call super methods Reviewed-by: jlaskey, lagergren, sundar
nashorn/src/jdk/nashorn/internal/objects/NativeJava.java
nashorn/src/jdk/nashorn/internal/runtime/JSType.java
nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java
nashorn/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapter.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java
nashorn/test/script/basic/JDK-8023630.js
nashorn/test/script/basic/JDK-8023630.js.EXPECTED
nashorn/test/script/basic/NASHORN-397.js
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java	Fri Aug 23 12:44:01 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJava.java	Fri Aug 23 13:10:45 2013 +0200
@@ -32,7 +32,6 @@
 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.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 @@
         }
         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);
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Fri Aug 23 12:44:01 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Fri Aug 23 13:10:45 2013 +0200
@@ -30,11 +30,10 @@
 
 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 @@
             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;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Fri Aug 23 12:44:01 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Fri Aug 23 13:10:45 2013 +0200
@@ -36,6 +36,7 @@
 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 @@
     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 @@
         return obj instanceof ScriptFunction ||
             ((obj instanceof ScriptObjectMirror) && ((ScriptObjectMirror)obj).isFunction()) ||
             isDynamicMethod(obj) ||
-            isFunctionalInterfaceObject(obj);
+            isFunctionalInterfaceObject(obj) ||
+            obj instanceof StaticClass;
     }
 
     /**
@@ -262,6 +264,16 @@
     }
 
     /**
+     * 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
      * a security manager in the system, then it checks the {@code nashorn.JavaReflection} {@code RuntimePermission}.
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java	Fri Aug 23 12:44:01 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java	Fri Aug 23 13:10:45 2013 +0200
@@ -67,11 +67,12 @@
         // 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;
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java	Fri Aug 23 12:44:01 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java	Fri Aug 23 13:10:45 2013 +0200
@@ -174,7 +174,7 @@
     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().
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java	Fri Aug 23 12:44:01 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java	Fri Aug 23 13:10:45 2013 +0200
@@ -34,7 +34,6 @@
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.security.SecureClassLoader;
-
 import jdk.internal.dynalink.beans.StaticClass;
 
 /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapter.java	Fri Aug 23 13:10:45 2013 +0200
@@ -0,0 +1,44 @@
+/*
+ * 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaSuperAdapterLinker.java	Fri Aug 23 13:10:45 2013 +0200
@@ -0,0 +1,180 @@
+/*
+ * 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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8023630.js	Fri Aug 23 13:10:45 2013 +0200
@@ -0,0 +1,94 @@
+/*
+ * 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"))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8023630.js.EXPECTED	Fri Aug 23 13:10:45 2013 +0200
@@ -0,0 +1,13 @@
+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
--- a/nashorn/test/script/basic/NASHORN-397.js	Fri Aug 23 12:44:01 2013 +0200
+++ b/nashorn/test/script/basic/NASHORN-397.js	Fri Aug 23 13:10:45 2013 +0200
@@ -35,7 +35,10 @@
     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'");
 }