blob: d517d3979f73b2d542ec19e32b4391c3190760d3 [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Objects;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyListeners;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.events.RuntimeEvent;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
/**
* Nashorn specific debug utils. This is meant for Nashorn developers.
* The interface is subject to change without notice!!
*
*/
@ScriptClass("Debug")
public final class NativeDebug extends ScriptObject {
// initialized by nasgen
@SuppressWarnings("unused")
private static PropertyMap $nasgenmap$;
private NativeDebug() {
// don't create me!
throw new UnsupportedOperationException();
}
@Override
public String getClassName() {
return "Debug";
}
/**
* Return the ArrayData class for this ScriptObject
* @param self self
* @param obj script object to check
* @return ArrayData class, or undefined if no ArrayData is present
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getArrayDataClass(final Object self, final Object obj) {
try {
return ((ScriptObject)obj).getArray().getClass();
} catch (final ClassCastException e) {
return ScriptRuntime.UNDEFINED;
}
}
/**
* Return the ArrayData for this ScriptObject
* @param self self
* @param obj script object to check
* @return ArrayData, ArrayDatas have toString methods, return Undefined if data missing
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getArrayData(final Object self, final Object obj) {
try {
return ((ScriptObject)obj).getArray();
} catch (final ClassCastException e) {
return ScriptRuntime.UNDEFINED;
}
}
/**
* Nashorn extension: get context, context utility
*
* @param self self reference
* @return context
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getContext(final Object self) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission(Context.NASHORN_GET_CONTEXT));
}
return Global.getThisContext();
}
/**
* Nashorn extension: get map from {@link ScriptObject}
*
* @param self self reference
* @param obj script object
* @return the map for the current ScriptObject
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object map(final Object self, final Object obj) {
if (obj instanceof ScriptObject) {
return ((ScriptObject)obj).getMap();
}
return UNDEFINED;
}
/**
* Check object identity comparison regardless of type
*
* @param self self reference
* @param obj1 first object in comparison
* @param obj2 second object in comparison
* @return true if reference identity
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean identical(final Object self, final Object obj1, final Object obj2) {
return obj1 == obj2;
}
/**
* Returns true if if the two objects are both property maps, and they have identical properties in the same order,
* but allows the properties to differ in their types.
* @param self self
* @param m1 first property map
* @param m2 second property map
* @return true if they have identical properties in same order, with possibly different types.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object equalWithoutType(final Object self, final Object m1, final Object m2) {
return ((PropertyMap)m1).equalsWithoutType((PropertyMap)m2);
}
/**
* Returns a diagnostic string representing the difference of two property maps.
* @param self self
* @param m1 first property map
* @param m2 second property map
* @return a diagnostic string representing the difference of two property maps.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object diffPropertyMaps(final Object self, final Object m1, final Object m2) {
return PropertyMap.diff((PropertyMap)m1, (PropertyMap)m2);
}
/**
* Object util - getClass
*
* @param self self reference
* @param obj object
* @return class of {@code obj}
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getClass(final Object self, final Object obj) {
if (obj != null) {
return obj.getClass();
}
return UNDEFINED;
}
/**
* Object util - equals
*
* @param self self reference
* @param obj1 first object in comparison
* @param obj2 second object in comparison
* @return return {@link Object#equals(Object)} for objects.
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static boolean equals(final Object self, final Object obj1, final Object obj2) {
return Objects.equals(obj1, obj2);
}
/**
* Object util - toJavaString
*
* @param self self reference
* @param obj object to represent as a string
* @return Java string representation of {@code obj}
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static String toJavaString(final Object self, final Object obj) {
return Objects.toString(obj);
}
/**
* Do not call overridden toString -- use default toString impl
*
* @param self self reference
* @param obj object to represent as a string
* @return string representation
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static String toIdentString(final Object self, final Object obj) {
if (obj == null) {
return "null";
}
final int hash = System.identityHashCode(obj);
return obj.getClass() + "@" + Integer.toHexString(hash);
}
/**
* Returns the property listener count for a script object
*
* @param self self reference
* @param obj script object whose listener count is returned
* @return listener count
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static int getListenerCount(final Object self, final Object obj) {
return (obj instanceof ScriptObject) ? PropertyListeners.getListenerCount((ScriptObject) obj) : 0;
}
/**
* Dump all Nashorn debug mode counters. Calling this may be better if
* you want to print all counters. This way you can avoid too many callsites
* due to counter access itself!!
* @param self self reference
* @return undefined
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object dumpCounters(final Object self) {
final PrintWriter out = Context.getCurrentErr();
out.println("ScriptObject count " + ScriptObject.getCount());
out.println("Scope count " + Scope.getScopeCount());
out.println("ScriptObject listeners added " + PropertyListeners.getListenersAdded());
out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved());
out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount());
out.println("ScriptFunction invokes " + ScriptFunction.getInvokes());
out.println("ScriptFunction allocations " + ScriptFunction.getAllocations());
out.println("PropertyMap count " + PropertyMap.getCount());
out.println("PropertyMap cloned " + PropertyMap.getClonedCount());
out.println("PropertyMap history hit " + PropertyMap.getHistoryHit());
out.println("PropertyMap proto invalidations " + PropertyMap.getProtoInvalidations());
out.println("PropertyMap proto history hit " + PropertyMap.getProtoHistoryHit());
out.println("PropertyMap setProtoNewMapCount " + PropertyMap.getSetProtoNewMapCount());
out.println("Callsite count " + LinkerCallSite.getCount());
out.println("Callsite misses " + LinkerCallSite.getMissCount());
out.println("Callsite misses by site at " + LinkerCallSite.getMissSamplingPercentage() + "%");
LinkerCallSite.getMissCounts(out);
return UNDEFINED;
}
/*
* Framework for logging runtime events
*/
private static final String EVENT_QUEUE = "__eventQueue__";
private static final String EVENT_QUEUE_CAPACITY = "__eventQueueCapacity__";
/**
* Get the capacity of the event queue
* @param self self reference
* @return capacity of event queue as an integer
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getEventQueueCapacity(final Object self) {
final ScriptObject sobj = (ScriptObject)self;
Integer cap;
if (sobj.has(EVENT_QUEUE_CAPACITY)) {
cap = JSType.toInt32(sobj.get(EVENT_QUEUE_CAPACITY));
} else {
setEventQueueCapacity(self, cap = RuntimeEvent.RUNTIME_EVENT_QUEUE_SIZE);
}
return cap;
}
/**
* Set the event queue capacity
* @param self an event queue
* @param newCapacity new capacity
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static void setEventQueueCapacity(final Object self, final Object newCapacity) {
((ScriptObject)self).set(EVENT_QUEUE_CAPACITY, newCapacity, NashornCallSiteDescriptor.CALLSITE_STRICT);
}
/**
* Add a runtime event to the runtime event queue. The queue has a fixed
* size {@link RuntimeEvent#RUNTIME_EVENT_QUEUE_SIZE} and the oldest
* entry will be thrown out of the queue is about to overflow
* @param self self reference
* @param event event to add
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static void addRuntimeEvent(final Object self, final Object event) {
final LinkedList<RuntimeEvent<?>> q = getEventQueue(self);
final int cap = (Integer)getEventQueueCapacity(self);
while (q.size() >= cap) {
q.removeFirst();
}
q.addLast(getEvent(event));
}
/**
* Expands the event queue capacity, or truncates if capacity is lower than
* current capacity. Then only the newest entries are kept
* @param self self reference
* @param newCapacity new capacity
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static void expandEventQueueCapacity(final Object self, final Object newCapacity) {
final LinkedList<RuntimeEvent<?>> q = getEventQueue(self);
final int nc = JSType.toInt32(newCapacity);
while (q.size() > nc) {
q.removeFirst();
}
setEventQueueCapacity(self, nc);
}
/**
* Clear the runtime event queue
* @param self self reference
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static void clearRuntimeEvents(final Object self) {
final LinkedList<RuntimeEvent<?>> q = getEventQueue(self);
q.clear();
}
/**
* Remove a specific runtime event from the event queue
* @param self self reference
* @param event event to remove
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static void removeRuntimeEvent(final Object self, final Object event) {
final LinkedList<RuntimeEvent<?>> q = getEventQueue(self);
final RuntimeEvent<?> re = getEvent(event);
if (!q.remove(re)) {
throw new IllegalStateException("runtime event " + re + " was not in event queue");
}
}
/**
* Return all runtime events in the queue as an array
* @param self self reference
* @return array of events
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getRuntimeEvents(final Object self) {
final LinkedList<RuntimeEvent<?>> q = getEventQueue(self);
return q.toArray(new RuntimeEvent<?>[q.size()]);
}
/**
* Return the last runtime event in the queue
* @param self self reference
* @return the freshest event, null if queue is empty
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getLastRuntimeEvent(final Object self) {
final LinkedList<RuntimeEvent<?>> q = getEventQueue(self);
return q.isEmpty() ? null : q.getLast();
}
@SuppressWarnings("unchecked")
private static LinkedList<RuntimeEvent<?>> getEventQueue(final Object self) {
final ScriptObject sobj = (ScriptObject)self;
LinkedList<RuntimeEvent<?>> q;
if (sobj.has(EVENT_QUEUE)) {
q = (LinkedList<RuntimeEvent<?>>)((ScriptObject)self).get(EVENT_QUEUE);
} else {
((ScriptObject)self).set(EVENT_QUEUE, q = new LinkedList<>(), NashornCallSiteDescriptor.CALLSITE_STRICT);
}
return q;
}
private static RuntimeEvent<?> getEvent(final Object event) {
return (RuntimeEvent<?>)event;
}
}