# HG changeset patch # User attila # Date 1377085149 -7200 # Node ID 310246d552b736e0b13ad011fbaed3d2cb0fb83e # Parent 8b2ee6bf9c628e07ad05fc412c20d544e006dc77 8023373: allow super invocation for adapters Reviewed-by: lagergren, sundar diff -r 8b2ee6bf9c62 -r 310246d552b7 nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java --- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Mon Aug 19 17:16:54 2013 +0530 +++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java Wed Aug 21 13:39:09 2013 +0200 @@ -173,6 +173,9 @@ private static final String CLASS_INIT = ""; private static final String STATIC_GLOBAL_FIELD_NAME = "staticGlobal"; + // Method name prefix for invoking super-methods + private static final String SUPER_PREFIX = "super$"; + /** * Collection of methods we never override: Object.clone(), Object.finalize(). */ @@ -240,6 +243,7 @@ } generateConstructors(); generateMethods(); + generateSuperMethods(); // } cw.visitEnd(); } @@ -507,6 +511,10 @@ private static void endInitMethod(final InstructionAdapter mv) { mv.visitInsn(RETURN); + endMethod(mv); + } + + private static void endMethod(final InstructionAdapter mv) { mv.visitMaxs(0, 0); mv.visitEnd(); } @@ -603,13 +611,8 @@ */ private void generateMethod(final MethodInfo mi) { final Method method = mi.method; - final int mod = method.getModifiers(); - final int access = ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0); final Class[] exceptions = method.getExceptionTypes(); - final String[] exceptionNames = new String[exceptions.length]; - for (int i = 0; i < exceptions.length; ++i) { - exceptionNames[i] = Type.getInternalName(exceptions[i]); - } + final String[] exceptionNames = getExceptionNames(exceptions); final MethodType type = mi.type; final String methodDesc = type.toMethodDescriptorString(); final String name = mi.getName(); @@ -617,14 +620,8 @@ final Type asmType = Type.getMethodType(methodDesc); final Type[] asmArgTypes = asmType.getArgumentTypes(); - // Determine the first index for a local variable - int nextLocalVar = 1; // this - for(final Type t: asmArgTypes) { - nextLocalVar += t.getSize(); - } - - final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(access, name, methodDesc, null, - exceptionNames)); + final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name, + methodDesc, null, exceptionNames)); mv.visitCode(); final Label instanceHandleDefined = new Label(); @@ -646,7 +643,7 @@ } // No handle is available, fall back to default behavior - if(Modifier.isAbstract(mod)) { + if(Modifier.isAbstract(method.getModifiers())) { // If the super method is abstract, throw an exception mv.anew(UNSUPPORTED_OPERATION_TYPE); mv.dup(); @@ -654,14 +651,7 @@ mv.athrow(); } else { // If the super method is not abstract, delegate to it. - mv.visitVarInsn(ALOAD, 0); - int nextParam = 1; - for(final Type t: asmArgTypes) { - mv.load(nextParam, t); - nextParam += t.getSize(); - } - mv.invokespecial(superClassName, name, methodDesc); - mv.areturn(asmReturnType); + emitSuperCall(mv, name, methodDesc); } final Label setupGlobal = new Label(); @@ -685,6 +675,12 @@ // stack: [creatingGlobal, someHandle] mv.visitLabel(setupGlobal); + // Determine the first index for a local variable + int nextLocalVar = 1; // "this" is at 0 + for(final Type t: asmArgTypes) { + nextLocalVar += t.getSize(); + } + // Set our local variable indices final int currentGlobalVar = nextLocalVar++; final int globalsDifferVar = nextLocalVar++; @@ -775,8 +771,7 @@ } mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME); } - mv.visitMaxs(0, 0); - mv.visitEnd(); + endMethod(mv); } /** @@ -817,6 +812,53 @@ return false; } + private void generateSuperMethods() { + for(final MethodInfo mi: methodInfos) { + if(!Modifier.isAbstract(mi.method.getModifiers())) { + generateSuperMethod(mi); + } + } + } + + private void generateSuperMethod(MethodInfo mi) { + final Method method = mi.method; + + final String methodDesc = mi.type.toMethodDescriptorString(); + final String name = mi.getName(); + + final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), + SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes()))); + mv.visitCode(); + + emitSuperCall(mv, name, methodDesc); + + endMethod(mv); + } + + private void emitSuperCall(final InstructionAdapter mv, final String name, final String methodDesc) { + mv.visitVarInsn(ALOAD, 0); + int nextParam = 1; + final Type methodType = Type.getMethodType(methodDesc); + for(final Type t: methodType.getArgumentTypes()) { + mv.load(nextParam, t); + nextParam += t.getSize(); + } + mv.invokespecial(superClassName, name, methodDesc); + mv.areturn(methodType.getReturnType()); + } + + private static String[] getExceptionNames(final Class[] exceptions) { + final String[] exceptionNames = new String[exceptions.length]; + for (int i = 0; i < exceptions.length; ++i) { + exceptionNames[i] = Type.getInternalName(exceptions[i]); + } + return exceptionNames; + } + + private static int getAccessModifiers(final Method method) { + return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0); + } + /** * Gathers methods that can be implemented or overridden from the specified type into this factory's * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from diff -r 8b2ee6bf9c62 -r 310246d552b7 nashorn/test/script/basic/JDK-8023373.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8023373.js Wed Aug 21 13:39:09 2013 +0200 @@ -0,0 +1,84 @@ +/* + * 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-8023373: allow super invocation for adapters + * + * @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) + } + } +} + +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 has super hashCode(): " + (typeof cw.super$hashCode === "function")) +print("cw has super equals(): " + (typeof cw.super$equals === "function")) +// Can't invoke super for final methods +print("cw has no super getClass(): " + (typeof cw.super$getClass === "undefined")) +print("cw has no super wait(): " + (typeof cw.super$wait === "undefined")) + +var r = new (Java.type("java.lang.Runnable"))(function() {}) +// Can't invoke super for abstract methods +print("r has no super run(): " + (typeof r.super$run === "undefined")) +// Interfaces can also invoke super Object methods +print("r has super hashCode(): " + (typeof r.super$hashCode === "function")) +print("r has super equals(): " + (typeof r.super$equals === "function")) +// But still can't invoke final methods +print("r has no super getClass(): " + (typeof r.super$getClass === "undefined")) +print("r has no super wait(): " + (typeof r.super$wait === "undefined")) diff -r 8b2ee6bf9c62 -r 310246d552b7 nashorn/test/script/basic/JDK-8023373.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/basic/JDK-8023373.js.EXPECTED Wed Aug 21 13:39:09 2013 +0200 @@ -0,0 +1,10 @@ +ABCDEFGHIJKLMNO +cw has super hashCode(): true +cw has super equals(): true +cw has no super getClass(): true +cw has no super wait(): true +r has no super run(): true +r has super hashCode(): true +r has super equals(): true +r has no super getClass(): true +r has no super wait(): true