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