Thu, 22 Aug 2013 13:51:24 -0300
8023228: Debugger information gather is too slow.
Reviewed-by: sundar, lagergren
Contributed-by: james.laskey@oracle.com
src/jdk/nashorn/internal/runtime/Context.java | file | annotate | diff | comparison | revisions | |
src/jdk/nashorn/internal/runtime/DebuggerSupport.java | file | annotate | diff | comparison | revisions |
1.1 --- a/src/jdk/nashorn/internal/runtime/Context.java Thu Aug 22 17:23:50 2013 +0200 1.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java Thu Aug 22 13:51:24 2013 -0300 1.3 @@ -91,6 +91,11 @@ 1.4 */ 1.5 public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; 1.6 1.7 + /* Force DebuggerSupport to be loaded. */ 1.8 + static { 1.9 + DebuggerSupport.FORCELOAD = true; 1.10 + } 1.11 + 1.12 /** 1.13 * ContextCodeInstaller that has the privilege of installing classes in the Context. 1.14 * Can only be instantiated from inside the context and is opaque to other classes
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/jdk/nashorn/internal/runtime/DebuggerSupport.java Thu Aug 22 13:51:24 2013 -0300 2.3 @@ -0,0 +1,310 @@ 2.4 +/* 2.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General License version 2 only, as 2.10 + * published by the Free Software Foundation. Oracle designates this 2.11 + * particular file as subject to the "Classpath" exception as provided 2.12 + * by Oracle in the LICENSE file that accompanied this code. 2.13 + * 2.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License 2.17 + * version 2 for more details (a copy is included in the LICENSE file that 2.18 + * accompanied this code). 2.19 + * 2.20 + * You should have received a copy of the GNU General License version 2.21 + * 2 along with this work; if not, write to the Free Software Foundation, 2.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.23 + * 2.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.25 + * or visit www.oracle.com if you need additional information or have any 2.26 + * questions. 2.27 + */ 2.28 + 2.29 +package jdk.nashorn.internal.runtime; 2.30 + 2.31 +import java.util.HashSet; 2.32 +import java.util.Set; 2.33 + 2.34 +/** 2.35 + * This class provides support for external debuggers. Its primary purpose is 2.36 + * is to simplify the debugger tasks and provide better performance. 2.37 + */ 2.38 +final class DebuggerSupport { 2.39 + /** 2.40 + * Hook to force the loading of the DebuggerSupport class so that it is 2.41 + * available to external debuggers. 2.42 + */ 2.43 + static boolean FORCELOAD = true; 2.44 + 2.45 + static { 2.46 + /** 2.47 + * Hook to force the loading of the DebuggerValueDesc class so that it is 2.48 + * available to external debuggers. 2.49 + */ 2.50 + DebuggerValueDesc forceLoad = new DebuggerValueDesc(null, false, null, null); 2.51 + } 2.52 + 2.53 + /** This class is used to send a bulk description of a value. */ 2.54 + static class DebuggerValueDesc { 2.55 + /** Property key (or index) or field name. */ 2.56 + final String key; 2.57 + 2.58 + /** If the value is expandable. */ 2.59 + final boolean expandable; 2.60 + 2.61 + /** Property or field value as object. */ 2.62 + final Object valueAsObject; 2.63 + 2.64 + /** Property or field value as string. */ 2.65 + final String valueAsString; 2.66 + 2.67 + DebuggerValueDesc(final String key, final boolean expandable, final Object valueAsObject, final String valueAsString) { 2.68 + this.key = key; 2.69 + this.expandable = expandable; 2.70 + this.valueAsObject = valueAsObject; 2.71 + this.valueAsString = valueAsString; 2.72 + } 2.73 + } 2.74 + 2.75 + /** 2.76 + * Return the current context global. 2.77 + * @return context global. 2.78 + */ 2.79 + static Object getGlobal() { 2.80 + return Context.getGlobalTrusted(); 2.81 + } 2.82 + 2.83 + /** 2.84 + * This method returns a bulk description of an object's properties. 2.85 + * @param object Script object to be displayed by the debugger. 2.86 + * @param all true if to include non-enumerable values. 2.87 + * @return An array of DebuggerValueDesc. 2.88 + */ 2.89 + static DebuggerValueDesc[] valueInfos(final Object object, final boolean all) { 2.90 + assert object instanceof ScriptObject; 2.91 + return getDebuggerValueDescs((ScriptObject)object, all, new HashSet<>()); 2.92 + } 2.93 + 2.94 + /** 2.95 + * This method returns a debugger description of the value. 2.96 + * @param name Name of value (property name). 2.97 + * @param value Data value. 2.98 + * @param all true if to include non-enumerable values. 2.99 + * @return A DebuggerValueDesc. 2.100 + */ 2.101 + static DebuggerValueDesc valueInfo(final String name, final Object value, final boolean all) { 2.102 + return valueInfo(name, value, all, new HashSet<>()); 2.103 + } 2.104 + 2.105 + /** 2.106 + * This method returns a debugger description of the value. 2.107 + * @param name Name of value (property name). 2.108 + * @param value Data value. 2.109 + * @param all true if to include non-enumerable values. 2.110 + * @param duplicates Duplication set to avoid cycles. 2.111 + * @return A DebuggerValueDesc. 2.112 + */ 2.113 + private static DebuggerValueDesc valueInfo(final String name, final Object value, final boolean all, final Set<Object> duplicates) { 2.114 + if (value instanceof ScriptObject && !(value instanceof ScriptFunction)) { 2.115 + final ScriptObject object = (ScriptObject)value; 2.116 + return new DebuggerValueDesc(name, !object.isEmpty(), value, objectAsString(object, all, duplicates)); 2.117 + } else { 2.118 + return new DebuggerValueDesc(name, false, value, valueAsString(value)); 2.119 + } 2.120 + } 2.121 + 2.122 + /** 2.123 + * Generate the descriptions for an object's properties. 2.124 + * @param object Object to introspect. 2.125 + * @param all true if to include non-enumerable values. 2.126 + * @param duplicates Duplication set to avoid cycles. 2.127 + * @return An array of DebuggerValueDesc. 2.128 + */ 2.129 + private static DebuggerValueDesc[] getDebuggerValueDescs(final ScriptObject object, final boolean all, final Set<Object> duplicates) { 2.130 + if (duplicates.contains(object)) { 2.131 + return null; 2.132 + } 2.133 + 2.134 + duplicates.add(object); 2.135 + 2.136 + final String[] keys = object.getOwnKeys(all); 2.137 + final DebuggerValueDesc[] descs = new DebuggerValueDesc[keys.length]; 2.138 + 2.139 + for (int i = 0; i < keys.length; i++) { 2.140 + final String key = keys[i]; 2.141 + descs[i] = valueInfo(key, object.get(key), true, duplicates); 2.142 + } 2.143 + 2.144 + duplicates.remove(object); 2.145 + 2.146 + return descs; 2.147 + } 2.148 + 2.149 + /** 2.150 + * Generate a string representation of a Script object. 2.151 + * @param object Script object to represent. 2.152 + * @param all true if to include non-enumerable values. 2.153 + * @param duplicates Duplication set to avoid cycles. 2.154 + * @return String representation. 2.155 + */ 2.156 + private static String objectAsString(final ScriptObject object, final boolean all, final Set<Object> duplicates) { 2.157 + final StringBuilder sb = new StringBuilder(); 2.158 + 2.159 + if (ScriptObject.isArray(object)) { 2.160 + sb.append('['); 2.161 + final long length = object.getLong("length"); 2.162 + 2.163 + for (long i = 0; i < length; i++) { 2.164 + if (object.has(i)) { 2.165 + final Object valueAsObject = object.get(i); 2.166 + final boolean isUndefined = JSType.of(valueAsObject) == JSType.UNDEFINED; 2.167 + 2.168 + if (isUndefined) { 2.169 + if (i != 0) { 2.170 + sb.append(","); 2.171 + } 2.172 + } else { 2.173 + if (i != 0) { 2.174 + sb.append(", "); 2.175 + } 2.176 + 2.177 + if (valueAsObject instanceof ScriptObject && !(valueAsObject instanceof ScriptFunction)) { 2.178 + final String objectString = objectAsString((ScriptObject)valueAsObject, true, duplicates); 2.179 + sb.append(objectString != null ? objectString : "{...}"); 2.180 + } else { 2.181 + sb.append(valueAsString(valueAsObject)); 2.182 + } 2.183 + } 2.184 + } else { 2.185 + if (i != 0) { 2.186 + sb.append(','); 2.187 + } 2.188 + } 2.189 + } 2.190 + 2.191 + sb.append(']'); 2.192 + } else { 2.193 + sb.append('{'); 2.194 + final DebuggerValueDesc[] descs = getDebuggerValueDescs(object, all, duplicates); 2.195 + 2.196 + if (descs != null) { 2.197 + for (int i = 0; i < descs.length; i++) { 2.198 + if (i != 0) { 2.199 + sb.append(", "); 2.200 + } 2.201 + 2.202 + final String valueAsString = descs[i].valueAsString; 2.203 + sb.append(descs[i].key); 2.204 + sb.append(": "); 2.205 + sb.append(descs[i].valueAsString); 2.206 + } 2.207 + } 2.208 + 2.209 + sb.append('}'); 2.210 + } 2.211 + 2.212 + return sb.toString(); 2.213 + } 2.214 + 2.215 + /** 2.216 + * This method returns a string representation of a value. 2.217 + * @param value Arbitrary value to be displayed by the debugger. 2.218 + * @return A string representation of the value or an array of DebuggerValueDesc. 2.219 + */ 2.220 + private static String valueAsString(final Object value) { 2.221 + final JSType type = JSType.of(value); 2.222 + 2.223 + switch (type) { 2.224 + case BOOLEAN: 2.225 + return value.toString(); 2.226 + 2.227 + case STRING: 2.228 + return escape((String)value); 2.229 + 2.230 + case NUMBER: 2.231 + return JSType.toString(((Number)value).doubleValue()); 2.232 + 2.233 + case NULL: 2.234 + return "null"; 2.235 + 2.236 + case UNDEFINED: 2.237 + return "undefined"; 2.238 + 2.239 + case OBJECT: 2.240 + return ScriptRuntime.safeToString(value); 2.241 + 2.242 + case FUNCTION: 2.243 + if (value instanceof ScriptFunction) { 2.244 + return ((ScriptFunction)value).toSource(); 2.245 + } else { 2.246 + return value.toString(); 2.247 + } 2.248 + 2.249 + default: 2.250 + return value.toString(); 2.251 + } 2.252 + } 2.253 + 2.254 + /** 2.255 + * Escape a string into a form that can be parsed by JavaScript. 2.256 + * @param value String to be escaped. 2.257 + * @return Escaped string. 2.258 + */ 2.259 + private static String escape(final String value) { 2.260 + final StringBuilder sb = new StringBuilder(); 2.261 + 2.262 + sb.append("\""); 2.263 + 2.264 + for (final char ch : value.toCharArray()) { 2.265 + switch (ch) { 2.266 + case '\\': 2.267 + sb.append("\\\\"); 2.268 + break; 2.269 + case '"': 2.270 + sb.append("\\\""); 2.271 + break; 2.272 + case '\'': 2.273 + sb.append("\\\'"); 2.274 + break; 2.275 + case '\b': 2.276 + sb.append("\\b"); 2.277 + break; 2.278 + case '\f': 2.279 + sb.append("\\f"); 2.280 + break; 2.281 + case '\n': 2.282 + sb.append("\\n"); 2.283 + break; 2.284 + case '\r': 2.285 + sb.append("\\r"); 2.286 + break; 2.287 + case '\t': 2.288 + sb.append("\\t"); 2.289 + break; 2.290 + default: 2.291 + if (ch < ' ' || ch >= 0xFF) { 2.292 + sb.append("\\u"); 2.293 + 2.294 + final String hex = Integer.toHexString(ch); 2.295 + for (int i = hex.length(); i < 4; i++) { 2.296 + sb.append('0'); 2.297 + } 2.298 + sb.append(hex); 2.299 + } else { 2.300 + sb.append(ch); 2.301 + } 2.302 + 2.303 + break; 2.304 + } 2.305 + } 2.306 + 2.307 + sb.append("\""); 2.308 + 2.309 + return sb.toString(); 2.310 + } 2.311 +} 2.312 + 2.313 +