# HG changeset patch # User sundar # Date 1402457015 -19800 # Node ID 67cf2d94e00a84b7fef9e9ec8722c00b06d3dc86 # Parent cac5f6a0a40ca146b13b9007b5ad2b5d0e9bcbf1 8044798: API for debugging Nashorn Reviewed-by: jlaskey, hannesw diff -r cac5f6a0a40c -r 67cf2d94e00a nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java --- a/nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java Mon Jun 09 16:00:06 2014 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java Wed Jun 11 08:53:35 2014 +0530 @@ -25,14 +25,21 @@ package jdk.nashorn.internal.runtime; +import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import java.net.URL; import java.util.HashSet; import java.util.Set; +import jdk.nashorn.internal.scripts.JS; /** * This class provides support for external debuggers. Its primary purpose is * is to simplify the debugger tasks and provide better performance. + * Even though the methods are not public, there are still part of the + * external debugger interface. */ final class DebuggerSupport { /** @@ -49,6 +56,11 @@ @SuppressWarnings("unused") final DebuggerValueDesc forceLoad = new DebuggerValueDesc(null, false, null, null); + + // Hook to force the loading of the SourceInfo class + @SuppressWarnings("unused") + final + SourceInfo srcInfo = new SourceInfo(null, 0, null, null); } /** This class is used to send a bulk description of a value. */ @@ -73,6 +85,54 @@ } } + static class SourceInfo { + final String name; + final URL url; + final int hash; + final char[] content; + + SourceInfo(final String name, final int hash, final URL url, final char[] content) { + this.name = name; + this.hash = hash; + this.url = url; + this.content = content; + } + } + + /** + * Hook that is called just before invoking method handle + * from ScriptFunctionData via invoke, constructor method calls. + * + * @param mh script class method about to be invoked. + */ + static void notifyInvoke(final MethodHandle mh) { + // Do nothing here. This is placeholder method on which a + // debugger can place a breakpoint so that it can access the + // (script class) method handle that is about to be invoked. + // See ScriptFunctionData.invoke and ScriptFunctionData.construct. + } + + /** + * Return the script source info for the given script class. + * + * @param clazz compiled script class + * @return SourceInfo + */ + static SourceInfo getSourceInfo(final Class clazz) { + if (JS.class.isAssignableFrom(clazz)) { + try { + final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); + sourceField.setAccessible(true); + final Source src = (Source) sourceField.get(null); + return src.getSourceInfo(); + } catch (final IllegalAccessException | NoSuchFieldException ignored) { + return null; + } + } + + return null; + } + /** * Return the current context global. * @return context global. @@ -87,7 +147,7 @@ * @param self Receiver to use. * @param string String to evaluate. * @param returnException true if exceptions are to be returned. - * @return Result of eval as string, or, an exception or null depending on returnException. + * @return Result of eval, or, an exception or null depending on returnException. */ static Object eval(final ScriptObject scope, final Object self, final String string, final boolean returnException) { final ScriptObject global = Context.getGlobal(); @@ -238,7 +298,7 @@ * @param value Arbitrary value to be displayed by the debugger. * @return A string representation of the value or an array of DebuggerValueDesc. */ - private static String valueAsString(final Object value) { + static String valueAsString(final Object value) { final JSType type = JSType.of(value); switch (type) { diff -r cac5f6a0a40c -r 67cf2d94e00a nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java --- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Mon Jun 09 16:00:06 2014 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Wed Jun 11 08:53:35 2014 +0530 @@ -551,6 +551,8 @@ final Object selfObj = convertThisObject(self); final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; + DebuggerSupport.notifyInvoke(mh); + if (isVarArg(mh)) { if (needsCallee(mh)) { return mh.invokeExact(fn, selfObj, args); @@ -604,6 +606,8 @@ final MethodHandle mh = getGenericConstructor(fn.getScope()); final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; + DebuggerSupport.notifyInvoke(mh); + if (isVarArg(mh)) { if (needsCallee(mh)) { return mh.invokeExact(fn, args); diff -r cac5f6a0a40c -r 67cf2d94e00a nashorn/src/jdk/nashorn/internal/runtime/Source.java --- a/nashorn/src/jdk/nashorn/internal/runtime/Source.java Mon Jun 09 16:00:06 2014 +0200 +++ b/nashorn/src/jdk/nashorn/internal/runtime/Source.java Wed Jun 11 08:53:35 2014 +0530 @@ -126,6 +126,11 @@ } } + /* package-private */ + DebuggerSupport.SourceInfo getSourceInfo() { + return new DebuggerSupport.SourceInfo(getName(), data.hashCode(), data.url(), data.array()); + } + // Wrapper to manage lazy loading private static interface Data { diff -r cac5f6a0a40c -r 67cf2d94e00a nashorn/test/script/nosecurity/JDK-8044798.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/JDK-8044798.js Wed Jun 11 08:53:35 2014 +0530 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014, 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-8044798: API for debugging Nashorn + * + * @test + * @run + */ + +// basic API exercise checks + +var Arrays = Java.type("java.util.Arrays"); +var CharArray = Java.type("char[]"); +var DebuggerSupport = Java.type("jdk.nashorn.internal.runtime.DebuggerSupport"); +var DebuggerValueDesc = Java.type("jdk.nashorn.internal.runtime.DebuggerSupport.DebuggerValueDesc"); + +var valueDescFields = DebuggerValueDesc.class.declaredFields; +Arrays.sort(valueDescFields, function(f1, f2) f1.name.compareTo(f2.name)); +for each (var f in valueDescFields) { + f.accessible = true; +} + +var debuggerSupportMethods = DebuggerSupport.class.declaredMethods; + +// methods of DebuggerSupport that we use +var evalMethod, valueInfoMethod, valueInfosMethod; +var getSourceInfoMethod, valueAsStringMethod; + +for each (var m in debuggerSupportMethods) { + m.accessible = true; + switch (m.name) { + case "eval": + evalMethod = m; + break; + case "valueInfo": + if (m.parameterCount == 3) { + valueInfoMethod = m; + } + break; + case "valueInfos": + valueInfosMethod = m; + break; + case "valueAsString": + valueAsStringMethod = m; + break; + case "getSourceInfo": + getSourceInfoMethod = m; + break; + } +} + +// eval +var value = evalMethod.invoke(null, null, null, "33 + 55", false); +print(value); + +// valueInfo +var info = valueInfoMethod.invoke(null, "apply", Function, true); +for each (var f in valueDescFields) { + print(f.name, "=", f.get(info)); +} + +// valueInfo - user defined object +var info = valueInfoMethod.invoke(null, "foo", { foo: 343 }, true); +for each (var f in valueDescFields) { + print(f.name, "=", f.get(info)); +} + +// valueInfos +var infos = valueInfosMethod.invoke(null, Object, true); +for each (var info in infos) { + for each (var f in valueDescFields) { + print(f.name, "=", f.get(info)); + } +} + +// valueInfos - user defined object +var infos = valueInfosMethod.invoke(null, { foo: 34, bar: "hello" }, true); +for each (var info in infos) { + for each (var f in valueDescFields) { + print(f.name, "=", f.get(info)); + } +} + +// valueAsString +function printValue(value) { + print(valueAsStringMethod.invoke(null, value)); +} + +printValue(undefined); +printValue(null); +printValue("hello"); +printValue(Math.PI); +printValue(this); + +// The below are not part of DebuggerSupport. But we need these to +// test DebuggerSupport.getSourceInfo etc. which need compiled script class + +var Source = Java.type("jdk.nashorn.internal.runtime.Source"); +var Context = Java.type("jdk.nashorn.internal.runtime.Context"); +var sourceCls = Source.class; +var errorMgrCls = Java.type("jdk.nashorn.internal.runtime.ErrorManager").class; +var booleanCls = Java.type("java.lang.Boolean").TYPE; + +// private compile method of Context class +var compileMethod = Context.class.getDeclaredMethod("compile", + sourceCls, errorMgrCls, booleanCls); +compileMethod.accessible = true; + +var scriptCls = compileMethod.invoke(Context.context, + Source.sourceFor("test", "print('hello')"), + new Context.ThrowErrorManager(), false); + +var SCRIPT_CLASS_NAME_PREFIX = "jdk.nashorn.internal.scripts.Script$"; +print("script class name pattern satisfied? " + + scriptCls.name.startsWith(SCRIPT_CLASS_NAME_PREFIX)); + +var srcInfo = getSourceInfoMethod.invoke(null, scriptCls); +var srcInfoFields = srcInfo.class.declaredFields; +Arrays.sort(srcInfoFields, function(f1, f2) f1.name.compareTo(f2.name)); + +print("Source info"); +for each (var f in srcInfoFields) { + f.accessible = true; + var fieldValue = f.get(srcInfo); + if (fieldValue instanceof CharArray) { + fieldValue = new java.lang.String(fieldValue); + } + + print(f.name, "=", fieldValue); +} diff -r cac5f6a0a40c -r 67cf2d94e00a nashorn/test/script/nosecurity/JDK-8044798.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/JDK-8044798.js.EXPECTED Wed Jun 11 08:53:35 2014 +0530 @@ -0,0 +1,104 @@ +88 +expandable = false +key = apply +valueAsObject = function Function() { [native code] } +valueAsString = function Function() { [native code] } +expandable = true +key = foo +valueAsObject = [object Object] +valueAsString = {foo: 343} +expandable = false +key = setIndexedPropertiesToExternalArrayData +valueAsObject = function setIndexedPropertiesToExternalArrayData() { [native code] } +valueAsString = function setIndexedPropertiesToExternalArrayData() { [native code] } +expandable = false +key = getPrototypeOf +valueAsObject = function getPrototypeOf() { [native code] } +valueAsString = function getPrototypeOf() { [native code] } +expandable = false +key = setPrototypeOf +valueAsObject = function setPrototypeOf() { [native code] } +valueAsString = function setPrototypeOf() { [native code] } +expandable = false +key = getOwnPropertyDescriptor +valueAsObject = function getOwnPropertyDescriptor() { [native code] } +valueAsString = function getOwnPropertyDescriptor() { [native code] } +expandable = false +key = getOwnPropertyNames +valueAsObject = function getOwnPropertyNames() { [native code] } +valueAsString = function getOwnPropertyNames() { [native code] } +expandable = false +key = create +valueAsObject = function create() { [native code] } +valueAsString = function create() { [native code] } +expandable = false +key = defineProperty +valueAsObject = function defineProperty() { [native code] } +valueAsString = function defineProperty() { [native code] } +expandable = false +key = defineProperties +valueAsObject = function defineProperties() { [native code] } +valueAsString = function defineProperties() { [native code] } +expandable = false +key = seal +valueAsObject = function seal() { [native code] } +valueAsString = function seal() { [native code] } +expandable = false +key = freeze +valueAsObject = function freeze() { [native code] } +valueAsString = function freeze() { [native code] } +expandable = false +key = preventExtensions +valueAsObject = function preventExtensions() { [native code] } +valueAsString = function preventExtensions() { [native code] } +expandable = false +key = isSealed +valueAsObject = function isSealed() { [native code] } +valueAsString = function isSealed() { [native code] } +expandable = false +key = isFrozen +valueAsObject = function isFrozen() { [native code] } +valueAsString = function isFrozen() { [native code] } +expandable = false +key = isExtensible +valueAsObject = function isExtensible() { [native code] } +valueAsString = function isExtensible() { [native code] } +expandable = false +key = keys +valueAsObject = function keys() { [native code] } +valueAsString = function keys() { [native code] } +expandable = false +key = bindProperties +valueAsObject = function bindProperties() { [native code] } +valueAsString = function bindProperties() { [native code] } +expandable = false +key = prototype +valueAsObject = [object Object] +valueAsString = {toString: function toString() { [native code] }, toLocaleString: function toLocaleString() { [native code] }, valueOf: function valueOf() { [native code] }, hasOwnProperty: function hasOwnProperty() { [native code] }, isPrototypeOf: function isPrototypeOf() { [native code] }, propertyIsEnumerable: function propertyIsEnumerable() { [native code] }, constructor: function Object() { [native code] }, __proto__: null} +expandable = false +key = length +valueAsObject = 1 +valueAsString = 1 +expandable = false +key = name +valueAsObject = Object +valueAsString = "Object" +expandable = false +key = foo +valueAsObject = 34 +valueAsString = 34 +expandable = false +key = bar +valueAsObject = hello +valueAsString = "hello" +undefined +null +"hello" +3.141592653589793 +[object global] +script class name pattern satisfied? true +Source info +content = print('hello') +hash = 1655359881 +name = test +url = null diff -r cac5f6a0a40c -r 67cf2d94e00a nashorn/test/script/nosecurity/debuggersupportapi.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/debuggersupportapi.js Wed Jun 11 08:53:35 2014 +0530 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2014, 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-8044798: API for debugging Nashorn + * + * @test + * @run + */ + +// Basic API class, method, field existence checks. + +// The following classes and the associated methods and fields are used as +// private debugger interface. Though private/implementation defined, nashorn +// code should not be changed to remove these classes, fields and methods. +// The test takes signatures of debugger interface and stores in .EXPECTED file. +// If any incompatible change is made to nashorn to break any of these, this +// test will fail. + +var Arrays = Java.type("java.util.Arrays"); +var DebuggerSupport = Java.type("jdk.nashorn.internal.runtime.DebuggerSupport"); + +print(DebuggerSupport.class); +print(); +var methods = DebuggerSupport.class.declaredMethods; +Arrays.sort(methods, function(m1, m2) m1.name.compareTo(m2.name)); +for each (var mth in methods) { + switch (mth.name) { + case "eval": + case "notifyInvoke": + case "getSourceInfo": + case "valueAsString": + case "valueInfos": + print(mth); + break; + case "valueInfo": + if (mth.parameterCount == 3) { + print(mth); + } + break; + } +} +print(); + +var DebuggerValueDesc = Java.type("jdk.nashorn.internal.runtime.DebuggerSupport.DebuggerValueDesc"); +print(DebuggerValueDesc.class); +print(); +var fields = DebuggerValueDesc.class.declaredFields; +Arrays.sort(fields, function(f1, f2) f1.name.compareTo(f2.name)); +for each (var fld in fields) { + switch (fld.name) { + case "key": + case "expandable": + case "valueAsObject": + case "valueAsString": + print(fld); + } +} +print(); + +var SourceInfo = Java.type("jdk.nashorn.internal.runtime.DebuggerSupport.SourceInfo"); +print(SourceInfo.class); +print(); +var fields = SourceInfo.class.declaredFields; +Arrays.sort(fields, function(f1, f2) f1.name.compareTo(f2.name)); +for each (var fld in fields) { + switch (fld.name) { + case "name": + case "hash": + case "url": + case "content": + print(fld); + } +} diff -r cac5f6a0a40c -r 67cf2d94e00a nashorn/test/script/nosecurity/debuggersupportapi.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/debuggersupportapi.js.EXPECTED Wed Jun 11 08:53:35 2014 +0530 @@ -0,0 +1,22 @@ +class jdk.nashorn.internal.runtime.DebuggerSupport + +static java.lang.Object jdk.nashorn.internal.runtime.DebuggerSupport.eval(jdk.nashorn.internal.runtime.ScriptObject,java.lang.Object,java.lang.String,boolean) +static jdk.nashorn.internal.runtime.DebuggerSupport$SourceInfo jdk.nashorn.internal.runtime.DebuggerSupport.getSourceInfo(java.lang.Class) +static void jdk.nashorn.internal.runtime.DebuggerSupport.notifyInvoke(java.lang.invoke.MethodHandle) +static java.lang.String jdk.nashorn.internal.runtime.DebuggerSupport.valueAsString(java.lang.Object) +static jdk.nashorn.internal.runtime.DebuggerSupport$DebuggerValueDesc jdk.nashorn.internal.runtime.DebuggerSupport.valueInfo(java.lang.String,java.lang.Object,boolean) +static jdk.nashorn.internal.runtime.DebuggerSupport$DebuggerValueDesc[] jdk.nashorn.internal.runtime.DebuggerSupport.valueInfos(java.lang.Object,boolean) + +class jdk.nashorn.internal.runtime.DebuggerSupport$DebuggerValueDesc + +final boolean jdk.nashorn.internal.runtime.DebuggerSupport$DebuggerValueDesc.expandable +final java.lang.String jdk.nashorn.internal.runtime.DebuggerSupport$DebuggerValueDesc.key +final java.lang.Object jdk.nashorn.internal.runtime.DebuggerSupport$DebuggerValueDesc.valueAsObject +final java.lang.String jdk.nashorn.internal.runtime.DebuggerSupport$DebuggerValueDesc.valueAsString + +class jdk.nashorn.internal.runtime.DebuggerSupport$SourceInfo + +final char[] jdk.nashorn.internal.runtime.DebuggerSupport$SourceInfo.content +final int jdk.nashorn.internal.runtime.DebuggerSupport$SourceInfo.hash +final java.lang.String jdk.nashorn.internal.runtime.DebuggerSupport$SourceInfo.name +final java.net.URL jdk.nashorn.internal.runtime.DebuggerSupport$SourceInfo.url