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 }