1 /* 2 * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.objects; 27 28 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 29 30 import java.io.PrintWriter; 31 import java.util.LinkedList; 32 import java.util.Objects; 33 import jdk.nashorn.internal.objects.annotations.Attribute; 34 import jdk.nashorn.internal.objects.annotations.Function; 35 import jdk.nashorn.internal.objects.annotations.ScriptClass; 36 import jdk.nashorn.internal.objects.annotations.Where; 37 import jdk.nashorn.internal.runtime.Context; 38 import jdk.nashorn.internal.runtime.JSType; 39 import jdk.nashorn.internal.runtime.PropertySwitchPoints; 40 import jdk.nashorn.internal.runtime.PropertyMap; 41 import jdk.nashorn.internal.runtime.Scope; 42 import jdk.nashorn.internal.runtime.ScriptFunction; 43 import jdk.nashorn.internal.runtime.ScriptObject; 44 import jdk.nashorn.internal.runtime.ScriptRuntime; 45 import jdk.nashorn.internal.runtime.events.RuntimeEvent; 46 import jdk.nashorn.internal.runtime.linker.LinkerCallSite; 47 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 48 49 /** 50 * Nashorn specific debug utils. This is meant for Nashorn developers. 51 * The interface is subject to change without notice!! 52 * 53 */ 54 @ScriptClass("Debug") 55 public final class NativeDebug extends ScriptObject { 56 57 // initialized by nasgen 58 @SuppressWarnings("unused") 59 private static PropertyMap $nasgenmap$; 60 61 private NativeDebug() { 62 // don't create me! 63 throw new UnsupportedOperationException(); 64 } 65 66 @Override 67 public String getClassName() { 68 return "Debug"; 69 } 70 71 /** 72 * Return the ArrayData class for this ScriptObject 73 * @param self self 74 * @param obj script object to check 75 * @return ArrayData class, or undefined if no ArrayData is present 76 */ 77 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 78 public static Object getArrayDataClass(final Object self, final Object obj) { 79 try { 80 return ((ScriptObject)obj).getArray().getClass(); 81 } catch (final ClassCastException e) { 82 return ScriptRuntime.UNDEFINED; 83 } 84 } 85 86 /** 87 * Return the ArrayData for this ScriptObject 88 * @param self self 89 * @param obj script object to check 90 * @return ArrayData, ArrayDatas have toString methods, return Undefined if data missing 91 */ 92 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 93 public static Object getArrayData(final Object self, final Object obj) { 94 try { 95 return ((ScriptObject)obj).getArray(); 96 } catch (final ClassCastException e) { 97 return ScriptRuntime.UNDEFINED; 98 } 99 } 100 101 /** 102 * Nashorn extension: get context, context utility 103 * 104 * @param self self reference 105 * @return context 106 */ 107 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 108 public static Object getContext(final Object self) { 109 final SecurityManager sm = System.getSecurityManager(); 110 if (sm != null) { 111 sm.checkPermission(new RuntimePermission(Context.NASHORN_GET_CONTEXT)); 112 } 113 return Global.getThisContext(); 114 } 115 116 /** 117 * Nashorn extension: get map from {@link ScriptObject} 118 * 119 * @param self self reference 120 * @param obj script object 121 * @return the map for the current ScriptObject 122 */ 123 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 124 public static Object map(final Object self, final Object obj) { 125 if (obj instanceof ScriptObject) { 126 return ((ScriptObject)obj).getMap(); 127 } 128 return UNDEFINED; 129 } 130 131 /** 132 * Check object identity comparison regardless of type 133 * 134 * @param self self reference 135 * @param obj1 first object in comparison 136 * @param obj2 second object in comparison 137 * @return true if reference identity 138 */ 139 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 140 public static boolean identical(final Object self, final Object obj1, final Object obj2) { 141 return obj1 == obj2; 142 } 143 144 /** 145 * Returns true if if the two objects are both property maps, and they have identical properties in the same order, 146 * but allows the properties to differ in their types. 147 * @param self self 148 * @param m1 first property map 149 * @param m2 second property map 150 * @return true if they have identical properties in same order, with possibly different types. 151 */ 152 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 153 public static Object equalWithoutType(final Object self, final Object m1, final Object m2) { 154 return ((PropertyMap)m1).equalsWithoutType((PropertyMap)m2); 155 } 156 157 /** 158 * Returns a diagnostic string representing the difference of two property maps. 159 * @param self self 160 * @param m1 first property map 161 * @param m2 second property map 162 * @return a diagnostic string representing the difference of two property maps. 163 */ 164 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 165 public static Object diffPropertyMaps(final Object self, final Object m1, final Object m2) { 166 return PropertyMap.diff((PropertyMap)m1, (PropertyMap)m2); 167 } 168 169 /** 170 * Object util - getClass 171 * 172 * @param self self reference 173 * @param obj object 174 * @return class of {@code obj} 175 */ 176 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 177 public static Object getClass(final Object self, final Object obj) { 178 if (obj != null) { 179 return obj.getClass(); 180 } 181 return UNDEFINED; 182 } 183 184 /** 185 * Object util - equals 186 * 187 * @param self self reference 188 * @param obj1 first object in comparison 189 * @param obj2 second object in comparison 190 * @return return {@link Object#equals(Object)} for objects. 191 */ 192 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 193 public static boolean equals(final Object self, final Object obj1, final Object obj2) { 194 return Objects.equals(obj1, obj2); 195 } 196 197 /** 198 * Object util - toJavaString 199 * 200 * @param self self reference 201 * @param obj object to represent as a string 202 * @return Java string representation of {@code obj} 203 */ 204 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 205 public static String toJavaString(final Object self, final Object obj) { 206 return Objects.toString(obj); 207 } 208 209 /** 210 * Do not call overridden toString -- use default toString impl 211 * 212 * @param self self reference 213 * @param obj object to represent as a string 214 * @return string representation 215 */ 216 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 217 public static String toIdentString(final Object self, final Object obj) { 218 if (obj == null) { 219 return "null"; 220 } 221 222 final int hash = System.identityHashCode(obj); 223 return obj.getClass() + "@" + Integer.toHexString(hash); 224 } 225 226 /** 227 * Returns {@code true} if passed object is a function that is fully debuggable (has all vars in scope). 228 * 229 * @param self self reference 230 * @param obj object 231 * @return true {@code obj} is a debuggable function 232 */ 233 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 234 public static Object isDebuggableFunction(final Object self, final Object obj) { 235 return (obj instanceof ScriptFunction && ((ScriptFunction) obj).hasAllVarsInScope()); 236 } 237 238 /** 239 * Returns the property listener count for a script object 240 * 241 * @param self self reference 242 * @param obj script object whose listener count is returned 243 * @return listener count 244 */ 245 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 246 public static int getListenerCount(final Object self, final Object obj) { 247 return (obj instanceof ScriptObject) ? PropertySwitchPoints.getSwitchPointCount((ScriptObject) obj) : 0; 248 } 249 250 /** 251 * Dump all Nashorn debug mode counters. Calling this may be better if 252 * you want to print all counters. This way you can avoid too many callsites 253 * due to counter access itself!! 254 * @param self self reference 255 * @return undefined 256 */ 257 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 258 public static Object dumpCounters(final Object self) { 259 final PrintWriter out = Context.getCurrentErr(); 260 261 out.println("ScriptObject count " + ScriptObject.getCount()); 262 out.println("Scope count " + Scope.getScopeCount()); 263 out.println("Property SwitchPoints added " + PropertySwitchPoints.getSwitchPointsAdded()); 264 out.println("Property SwitchPoints invalidated " + PropertySwitchPoints.getSwitchPointsInvalidated()); 265 out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount()); 266 out.println("ScriptFunction invokes " + ScriptFunction.getInvokes()); 267 out.println("ScriptFunction allocations " + ScriptFunction.getAllocations()); 268 out.println("PropertyMap count " + PropertyMap.getCount()); 269 out.println("PropertyMap cloned " + PropertyMap.getClonedCount()); 270 out.println("PropertyMap history hit " + PropertyMap.getHistoryHit()); 271 out.println("PropertyMap proto invalidations " + PropertyMap.getProtoInvalidations()); 272 out.println("PropertyMap proto history hit " + PropertyMap.getProtoHistoryHit()); 273 out.println("PropertyMap setProtoNewMapCount " + PropertyMap.getSetProtoNewMapCount()); 274 out.println("Callsite count " + LinkerCallSite.getCount()); 275 out.println("Callsite misses " + LinkerCallSite.getMissCount()); 276 out.println("Callsite misses by site at " + LinkerCallSite.getMissSamplingPercentage() + "%"); 277 278 LinkerCallSite.getMissCounts(out); 279 280 return UNDEFINED; 281 } 282 283 /* 284 * Framework for logging runtime events 285 */ 286 287 private static final String EVENT_QUEUE = "__eventQueue__"; 288 private static final String EVENT_QUEUE_CAPACITY = "__eventQueueCapacity__"; 289 290 /** 291 * Get the capacity of the event queue 292 * @param self self reference 293 * @return capacity of event queue as an integer 294 */ 295 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 296 public static Object getEventQueueCapacity(final Object self) { 297 final ScriptObject sobj = (ScriptObject)self; 298 Integer cap; 299 if (sobj.has(EVENT_QUEUE_CAPACITY)) { 300 cap = JSType.toInt32(sobj.get(EVENT_QUEUE_CAPACITY)); 301 } else { 302 setEventQueueCapacity(self, cap = RuntimeEvent.RUNTIME_EVENT_QUEUE_SIZE); 303 } 304 return cap; 305 } 306 307 /** 308 * Set the event queue capacity 309 * @param self an event queue 310 * @param newCapacity new capacity 311 */ 312 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 313 public static void setEventQueueCapacity(final Object self, final Object newCapacity) { 314 ((ScriptObject)self).set(EVENT_QUEUE_CAPACITY, newCapacity, NashornCallSiteDescriptor.CALLSITE_STRICT); 315 } 316 317 /** 318 * Add a runtime event to the runtime event queue. The queue has a fixed 319 * size {@link RuntimeEvent#RUNTIME_EVENT_QUEUE_SIZE} and the oldest 320 * entry will be thrown out of the queue is about to overflow 321 * @param self self reference 322 * @param event event to add 323 */ 324 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 325 public static void addRuntimeEvent(final Object self, final Object event) { 326 final LinkedList<RuntimeEvent<?>> q = getEventQueue(self); 327 final int cap = (Integer)getEventQueueCapacity(self); 328 while (q.size() >= cap) { 329 q.removeFirst(); 330 } 331 q.addLast(getEvent(event)); 332 } 333 334 /** 335 * Expands the event queue capacity, or truncates if capacity is lower than 336 * current capacity. Then only the newest entries are kept 337 * @param self self reference 338 * @param newCapacity new capacity 339 */ 340 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 341 public static void expandEventQueueCapacity(final Object self, final Object newCapacity) { 342 final LinkedList<RuntimeEvent<?>> q = getEventQueue(self); 343 final int nc = JSType.toInt32(newCapacity); 344 while (q.size() > nc) { 345 q.removeFirst(); 346 } 347 setEventQueueCapacity(self, nc); 348 } 349 350 /** 351 * Clear the runtime event queue 352 * @param self self reference 353 */ 354 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 355 public static void clearRuntimeEvents(final Object self) { 356 final LinkedList<RuntimeEvent<?>> q = getEventQueue(self); 357 q.clear(); 358 } 359 360 /** 361 * Remove a specific runtime event from the event queue 362 * @param self self reference 363 * @param event event to remove 364 */ 365 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 366 public static void removeRuntimeEvent(final Object self, final Object event) { 367 final LinkedList<RuntimeEvent<?>> q = getEventQueue(self); 368 final RuntimeEvent<?> re = getEvent(event); 369 if (!q.remove(re)) { 370 throw new IllegalStateException("runtime event " + re + " was not in event queue"); 371 } 372 } 373 374 /** 375 * Return all runtime events in the queue as an array 376 * @param self self reference 377 * @return array of events 378 */ 379 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 380 public static Object getRuntimeEvents(final Object self) { 381 final LinkedList<RuntimeEvent<?>> q = getEventQueue(self); 382 return q.toArray(new RuntimeEvent<?>[0]); 383 } 384 385 /** 386 * Return the last runtime event in the queue 387 * @param self self reference 388 * @return the freshest event, null if queue is empty 389 */ 390 @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) 391 public static Object getLastRuntimeEvent(final Object self) { 392 final LinkedList<RuntimeEvent<?>> q = getEventQueue(self); 393 return q.isEmpty() ? null : q.getLast(); 394 } 395 396 @SuppressWarnings("unchecked") 397 private static LinkedList<RuntimeEvent<?>> getEventQueue(final Object self) { 398 final ScriptObject sobj = (ScriptObject)self; 399 LinkedList<RuntimeEvent<?>> q; 400 if (sobj.has(EVENT_QUEUE)) { 401 q = (LinkedList<RuntimeEvent<?>>)((ScriptObject)self).get(EVENT_QUEUE); 402 } else { 403 ((ScriptObject)self).set(EVENT_QUEUE, q = new LinkedList<>(), NashornCallSiteDescriptor.CALLSITE_STRICT); 404 } 405 return q; 406 } 407 408 private static RuntimeEvent<?> getEvent(final Object event) { 409 return (RuntimeEvent<?>)event; 410 } 411 }