1/*
2 * Copyright (c) 2010, 2016, 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
26package jdk.nashorn.internal.objects;
27
28import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
29
30import java.io.PrintWriter;
31import java.util.LinkedList;
32import java.util.Objects;
33import jdk.nashorn.internal.objects.annotations.Attribute;
34import jdk.nashorn.internal.objects.annotations.Function;
35import jdk.nashorn.internal.objects.annotations.ScriptClass;
36import jdk.nashorn.internal.objects.annotations.Where;
37import jdk.nashorn.internal.runtime.Context;
38import jdk.nashorn.internal.runtime.JSType;
39import jdk.nashorn.internal.runtime.PropertyListeners;
40import jdk.nashorn.internal.runtime.PropertyMap;
41import jdk.nashorn.internal.runtime.Scope;
42import jdk.nashorn.internal.runtime.ScriptFunction;
43import jdk.nashorn.internal.runtime.ScriptObject;
44import jdk.nashorn.internal.runtime.ScriptRuntime;
45import jdk.nashorn.internal.runtime.events.RuntimeEvent;
46import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
47import 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")
55public 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) ? PropertyListeners.getListenerCount((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("ScriptObject listeners added " + PropertyListeners.getListenersAdded());
264        out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved());
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}
412