--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -6,88 +6,92 @@
"use strict";
const { Cu } = require("chrome");
const { GeneratedLocation } = require("devtools/server/actors/common");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { assert } = DevToolsUtils;
+const protocol = require("devtools/shared/protocol");
+const { objectSpec } = require("devtools/shared/specs/object");
+
loader.lazyRequireGetter(this, "PropertyIteratorActor", "devtools/server/actors/object/property-iterator", true);
loader.lazyRequireGetter(this, "SymbolIteratorActor", "devtools/server/actors/object/symbol-iterator", true);
loader.lazyRequireGetter(this, "previewers", "devtools/server/actors/object/previewers");
loader.lazyRequireGetter(this, "stringify", "devtools/server/actors/object/stringifiers");
const {
getArrayLength,
getPromiseState,
getStorageLength,
isArray,
isStorage,
isTypedArray,
} = require("devtools/server/actors/object/utils");
-/**
- * Creates an actor for the specified object.
- *
- * @param obj Debugger.Object
- * The debuggee object.
- * @param hooks Object
- * A collection of abstract methods that are implemented by the caller.
- * ObjectActor requires the following functions to be implemented by
- * the caller:
- * - createValueGrip
- * Creates a value grip for the given object
- * - sources
- * TabSources getter that manages the sources of a thread
- * - createEnvironmentActor
- * Creates and return an environment actor
- * - getGripDepth
- * An actor's grip depth getter
- * - incrementGripDepth
- * Increment the actor's grip depth
- * - decrementGripDepth
- * Decrement the actor's grip depth
- * - globalDebugObject
- * The Debuggee Global Object as given by the ThreadActor
- */
-function ObjectActor(obj, {
- createValueGrip: createValueGripHook,
- sources,
- createEnvironmentActor,
- getGripDepth,
- incrementGripDepth,
- decrementGripDepth,
- getGlobalDebugObject
-}) {
- assert(!obj.optimizedOut,
- "Should not create object actors for optimized out values!");
- this.obj = obj;
- this.hooks = {
+
+const proto = {
+ /**
+ * Creates an actor for the specified object.
+ *
+ * @param obj Debugger.Object
+ * The debuggee object.
+ * @param Object
+ * A collection of abstract methods that are implemented by the caller.
+ * ObjectActor requires the following functions to be implemented by
+ * the caller:
+ * - createValueGrip
+ * Creates a value grip for the given object
+ * - sources
+ * TabSources getter that manages the sources of a thread
+ * - createEnvironmentActor
+ * Creates and return an environment actor
+ * - getGripDepth
+ * An actor's grip depth getter
+ * - incrementGripDepth
+ * Increment the actor's grip depth
+ * - decrementGripDepth
+ * Decrement the actor's grip depth
+ * - globalDebugObject
+ * The Debuggee Global Object as given by the ThreadActor
+ */
+ initialize(obj, {
createValueGrip: createValueGripHook,
sources,
createEnvironmentActor,
getGripDepth,
incrementGripDepth,
decrementGripDepth,
getGlobalDebugObject
- };
- this.iterators = new Set();
-}
+ }, conn) {
+ assert(!obj.optimizedOut,
+ "Should not create object actors for optimized out values!");
+ protocol.Actor.prototype.initialize.call(this, conn);
-ObjectActor.prototype = {
- actorPrefix: "obj",
+ this.conn = conn;
+ this.obj = obj;
+ this.hooks = {
+ createValueGrip: createValueGripHook,
+ sources,
+ createEnvironmentActor,
+ getGripDepth,
+ incrementGripDepth,
+ decrementGripDepth,
+ getGlobalDebugObject
+ };
+ },
rawValue: function() {
return this.obj.unsafeDereference();
},
/**
* Returns a grip for this actor for returning in a protocol message.
*/
- grip: function() {
+ form: function() {
const g = {
"type": "object",
"actor": this.actorID,
"class": this.obj.class,
};
const unwrapped = DevToolsUtils.unwrap(this.obj);
@@ -190,136 +194,105 @@ ObjectActor.prototype = {
if (state !== "pending") {
promiseState.timeToSettle = this.obj.promiseTimeToResolution;
}
return promiseState;
},
/**
- * Releases this actor from the pool.
- */
- release: function() {
- if (this.registeredPool.objectActors) {
- this.registeredPool.objectActors.delete(this.obj);
- }
- this.iterators.forEach(actor => this.registeredPool.removeActor(actor));
- this.iterators.clear();
- this.registeredPool.removeActor(this);
- },
-
- /**
* Handle a protocol request to provide the definition site of this function
* object.
*/
- onDefinitionSite: function() {
+ definitionSite: function() {
if (this.obj.class != "Function") {
- return {
- from: this.actorID,
- error: "objectNotFunction",
- message: this.actorID + " is not a function."
- };
+ return this.throwError("objectNotFunction", this.actorID + " is not a function.");
}
if (!this.obj.script) {
- return {
- from: this.actorID,
- error: "noScript",
- message: this.actorID + " has no Debugger.Script"
- };
+ return this.throwError("noScript", this.actorID + " has no Debugger.Script");
}
return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
this.hooks.sources().createNonSourceMappedActor(this.obj.script.source),
this.obj.script.startLine,
0 // TODO bug 901138: use Debugger.Script.prototype.startColumn
)).then((originalLocation) => {
return {
- source: originalLocation.originalSourceActor.form(),
+ source: originalLocation.originalSourceActor,
line: originalLocation.originalLine,
column: originalLocation.originalColumn
};
});
},
/**
* Handle a protocol request to provide the names of the properties defined on
* the object and not its prototype.
*/
- onOwnPropertyNames: function() {
+ ownPropertyNames: function() {
let props = [];
if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
try {
props = this.obj.getOwnPropertyNames();
} catch (err) {
// The above can throw when the debuggee does not subsume the object's
// compartment, or for some WrappedNatives like Cu.Sandbox.
}
}
- return { from: this.actorID, ownPropertyNames: props };
+ return { ownPropertyNames: props };
},
/**
* Creates an actor to iterate over an object property names and values.
* See PropertyIteratorActor constructor for more info about options param.
*
- * @param request object
- * The protocol request object.
+ * @param options object
*/
- onEnumProperties: function(request) {
- const actor = new PropertyIteratorActor(this, request.options);
- this.registeredPool.addActor(actor);
- this.iterators.add(actor);
- return { iterator: actor.form() };
+ enumProperties: function(options) {
+ return PropertyIteratorActor(this, options, this.conn);
},
/**
* Creates an actor to iterate over entries of a Map/Set-like object.
*/
- onEnumEntries: function() {
- const actor = new PropertyIteratorActor(this, { enumEntries: true });
- this.registeredPool.addActor(actor);
- this.iterators.add(actor);
- return { iterator: actor.form() };
+ enumEntries: function() {
+ return PropertyIteratorActor(this, { enumEntries: true }, this.conn);
},
/**
* Creates an actor to iterate over an object symbols properties.
*/
- onEnumSymbols: function() {
- const actor = new SymbolIteratorActor(this);
- this.registeredPool.addActor(actor);
- this.iterators.add(actor);
- return { iterator: actor.form() };
+ enumSymbols: function() {
+ return SymbolIteratorActor(this, this.conn);
},
/**
* Handle a protocol request to provide the prototype and own properties of
* the object.
*
* @returns {Object} An object containing the data of this.obj, of the following form:
- * - {string} from: this.obj's actorID.
* - {Object} prototype: The descriptor of this.obj's prototype.
* - {Object} ownProperties: an object where the keys are the names of the
* this.obj's ownProperties, and the values the descriptors of
* the properties.
* - {Array} ownSymbols: An array containing all descriptors of this.obj's
* ownSymbols. Here we have an array, and not an object like for
* ownProperties, because we can have multiple symbols with the same
* name in this.obj, e.g. `{[Symbol()]: "a", [Symbol()]: "b"}`.
* - {Object} safeGetterValues: an object that maps this.obj's property names
* with safe getters descriptors.
*/
- onPrototypeAndProperties: function() {
- let proto = null;
+ prototypeAndProperties: function() {
+ let objProto = null;
let names = [];
let symbols = [];
if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
try {
- proto = this.obj.proto;
+ objProto = this.obj.proto;
names = this.obj.getOwnPropertyNames();
symbols = this.obj.getOwnPropertySymbols();
} catch (err) {
// The above can throw when the debuggee does not subsume the object's
// compartment, or for some WrappedNatives like Cu.Sandbox.
}
}
@@ -332,21 +305,22 @@ ObjectActor.prototype = {
for (const sym of symbols) {
ownSymbols.push({
name: sym.toString(),
descriptor: this._propertyDescriptor(sym)
});
}
- return { from: this.actorID,
- prototype: this.hooks.createValueGrip(proto),
- ownProperties,
- ownSymbols,
- safeGetterValues: this._findSafeGetterValues(names) };
+ return {
+ prototype: this.hooks.createValueGrip(objProto),
+ ownProperties,
+ ownSymbols,
+ safeGetterValues: this._findSafeGetterValues(names)
+ };
},
/**
* Find the safe getter values for the current Debugger.Object, |this.obj|.
*
* @private
* @param array ownProperties
* The array that holds the list of known ownProperties names for
@@ -488,49 +462,46 @@ ObjectActor.prototype = {
object._safeGetters = getters;
return getters;
},
/**
* Handle a protocol request to provide the prototype of the object.
*/
- onPrototype: function() {
- let proto = null;
+ prototype: function() {
+ let objProto = null;
if (DevToolsUtils.isSafeDebuggerObject(this.obj)) {
- proto = this.obj.proto;
+ objProto = this.obj.proto;
}
- return { from: this.actorID,
- prototype: this.hooks.createValueGrip(proto) };
+ return { prototype: this.hooks.createValueGrip(objProto) };
},
/**
* Handle a protocol request to provide the property descriptor of the
* object's specified property.
*
- * @param request object
- * The protocol request object.
+ * @param name string
+ * The property we want the description of.
*/
- onProperty: function(request) {
- if (!request.name) {
+ property: function(name) {
+ if (!name) {
return { error: "missingParameter",
message: "no property name was specified" };
}
- return { from: this.actorID,
- descriptor: this._propertyDescriptor(request.name) };
+ return { descriptor: this._propertyDescriptor(name) };
},
/**
* Handle a protocol request to provide the display string for the object.
*/
- onDisplayString: function() {
+ displayString: function() {
const string = stringify(this.obj);
- return { from: this.actorID,
- displayString: this.hooks.createValueGrip(string) };
+ return { displayString: this.hooks.createValueGrip(string) };
},
/**
* A helper method that creates a property descriptor for the provided object,
* properly formatted for sending in a protocol response.
*
* @private
* @param string name
@@ -595,96 +566,92 @@ ObjectActor.prototype = {
}
}
return retval;
},
/**
* Handle a protocol request to provide the source code of a function.
*
- * @param request object
- * The protocol request object.
+ * @param pretty boolean
*/
- onDecompile: function(request) {
+ decompile: function(pretty) {
if (this.obj.class !== "Function") {
return { error: "objectNotFunction",
message: "decompile request is only valid for object grips " +
"with a 'Function' class." };
}
- return { from: this.actorID,
- decompiledCode: this.obj.decompile(!!request.pretty) };
+ return { decompiledCode: this.obj.decompile(!!pretty) };
},
/**
* Handle a protocol request to provide the parameters of a function.
*/
- onParameterNames: function() {
+ parameterNames: function() {
if (this.obj.class !== "Function") {
return { error: "objectNotFunction",
message: "'parameterNames' request is only valid for object " +
"grips with a 'Function' class." };
}
return { parameterNames: this.obj.parameterNames };
},
/**
- * Handle a protocol request to release a thread-lifetime grip.
- */
- onRelease: function() {
- this.release();
- return {};
- },
-
- /**
* Handle a protocol request to provide the lexical scope of a function.
*/
- onScope: function() {
+ scope: function() {
if (this.obj.class !== "Function") {
- return { error: "objectNotFunction",
- message: "scope request is only valid for object grips with a" +
- " 'Function' class." };
+ return {
+ error: "objectNotFunction",
+ message: "scope request is only valid for object grips with a 'Function' class."
+ };
}
- const envActor = this.hooks.createEnvironmentActor(this.obj.environment,
- this.registeredPool);
+ const { createEnvironmentActor } = this.hooks;
+ const envActor = createEnvironmentActor(this.obj.environment, this.registeredPool);
+
if (!envActor) {
- return { error: "notDebuggee",
- message: "cannot access the environment of this function." };
+ return {
+ error: "notDebuggee",
+ message: "cannot access the environment of this function."
+ };
}
- return { from: this.actorID, scope: envActor.form() };
+ return {
+ scope: envActor
+ };
},
/**
* Handle a protocol request to get the list of dependent promises of a
* promise.
*
* @return object
* Returns an object containing an array of object grips of the
* dependent promises
*/
- onDependentPromises: function() {
+ dependentPromises: function() {
if (this.obj.class != "Promise") {
return { error: "objectNotPromise",
message: "'dependentPromises' request is only valid for " +
"object grips with a 'Promise' class." };
}
const promises = this.obj.promiseDependentPromises
.map(p => this.hooks.createValueGrip(p));
return { promises };
},
/**
* Handle a protocol request to get the allocation stack of a promise.
*/
- onAllocationStack: function() {
+ allocationStack: function() {
if (this.obj.class != "Promise") {
return { error: "objectNotPromise",
message: "'allocationStack' request is only valid for " +
"object grips with a 'Promise' class." };
}
let stack = this.obj.promiseAllocationSite;
const allocationStacks = [];
@@ -695,25 +662,23 @@ ObjectActor.prototype = {
if (source) {
allocationStacks.push(source);
}
}
stack = stack.parent;
}
- return Promise.all(allocationStacks).then(stacks => {
- return { allocationStack: stacks };
- });
+ return Promise.all(allocationStacks);
},
/**
* Handle a protocol request to get the fulfillment stack of a promise.
*/
- onFulfillmentStack: function() {
+ fulfillmentStack: function() {
if (this.obj.class != "Promise") {
return { error: "objectNotPromise",
message: "'fulfillmentStack' request is only valid for " +
"object grips with a 'Promise' class." };
}
let stack = this.obj.promiseResolutionSite;
const fulfillmentStacks = [];
@@ -724,25 +689,23 @@ ObjectActor.prototype = {
if (source) {
fulfillmentStacks.push(source);
}
}
stack = stack.parent;
}
- return Promise.all(fulfillmentStacks).then(stacks => {
- return { fulfillmentStack: stacks };
- });
+ return Promise.all(fulfillmentStacks);
},
/**
* Handle a protocol request to get the rejection stack of a promise.
*/
- onRejectionStack: function() {
+ rejectionStack: function() {
if (this.obj.class != "Promise") {
return { error: "objectNotPromise",
message: "'rejectionStack' request is only valid for " +
"object grips with a 'Promise' class." };
}
let stack = this.obj.promiseResolutionSite;
const rejectionStacks = [];
@@ -753,19 +716,17 @@ ObjectActor.prototype = {
if (source) {
rejectionStacks.push(source);
}
}
stack = stack.parent;
}
- return Promise.all(rejectionStacks).then(stacks => {
- return { rejectionStack: stacks };
- });
+ return Promise.all(rejectionStacks);
},
/**
* Helper function for fetching the source location of a SavedFrame stack.
*
* @param SavedFrame stack
* The promise allocation stack frame
* @return object
@@ -787,38 +748,25 @@ ObjectActor.prototype = {
}
return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
source,
stack.line,
stack.column
)).then((originalLocation) => {
return {
- source: originalLocation.originalSourceActor.form(),
+ source: originalLocation.originalSourceActor,
line: originalLocation.originalLine,
column: originalLocation.originalColumn,
functionDisplayName: stack.functionDisplayName
};
});
- }
+ },
+
+ /**
+ * Release the actor, when it isn't needed anymore.
+ * Protocol.js uses this release method to call the destroy method.
+ */
+ release: function() {}
};
-ObjectActor.prototype.requestTypes = {
- "definitionSite": ObjectActor.prototype.onDefinitionSite,
- "parameterNames": ObjectActor.prototype.onParameterNames,
- "prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
- "enumProperties": ObjectActor.prototype.onEnumProperties,
- "prototype": ObjectActor.prototype.onPrototype,
- "property": ObjectActor.prototype.onProperty,
- "displayString": ObjectActor.prototype.onDisplayString,
- "ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
- "decompile": ObjectActor.prototype.onDecompile,
- "release": ObjectActor.prototype.onRelease,
- "scope": ObjectActor.prototype.onScope,
- "dependentPromises": ObjectActor.prototype.onDependentPromises,
- "allocationStack": ObjectActor.prototype.onAllocationStack,
- "fulfillmentStack": ObjectActor.prototype.onFulfillmentStack,
- "rejectionStack": ObjectActor.prototype.onRejectionStack,
- "enumEntries": ObjectActor.prototype.onEnumEntries,
- "enumSymbols": ObjectActor.prototype.onEnumSymbols,
-};
-
-exports.ObjectActor = ObjectActor;
+exports.ObjectActor = protocol.ActorClassWithSpec(objectSpec, proto);
+exports.ObjectActorProto = proto;
--- a/devtools/server/actors/object/property-iterator.js
+++ b/devtools/server/actors/object/property-iterator.js
@@ -35,18 +35,18 @@ loader.lazyRequireGetter(this, "ObjectUt
* before dispatching them.
* - query String
* If non-empty, will filter the properties by names and values
* containing this query string. The match is not case-sensitive.
* Regarding value filtering it just compare to the stringification
* of the property value.
*/
const PropertyIteratorActor = protocol.ActorClassWithSpec(propertyIteratorSpec, {
- initialize(objectActor, options) {
- protocol.Actor.prototype.initialize.call(this);
+ initialize(objectActor, options, conn) {
+ protocol.Actor.prototype.initialize.call(this, conn);
if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
this.iterator = {
size: 0,
propertyName: index => undefined,
propertyDescription: index => undefined,
};
} else if (options.enumEntries) {
const cls = objectActor.obj.class;
--- a/devtools/server/actors/object/symbol-iterator.js
+++ b/devtools/server/actors/object/symbol-iterator.js
@@ -12,18 +12,18 @@ const DevToolsUtils = require("devtools/
/**
* Creates an actor to iterate over an object's symbols.
*
* @param objectActor ObjectActor
* The object actor.
*/
const SymbolIteratorActor = protocol.ActorClassWithSpec(symbolIteratorSpec, {
- initialize(objectActor) {
- protocol.Actor.prototype.initialize.call(this);
+ initialize(objectActor, conn) {
+ protocol.Actor.prototype.initialize.call(this, conn);
let symbols = [];
if (DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
try {
symbols = objectActor.obj.getOwnPropertySymbols();
} catch (err) {
// The above can throw when the debuggee does not subsume the object's
// compartment, or for some WrappedNatives like Cu.Sandbox.
--- a/devtools/server/actors/pause-scoped.js
+++ b/devtools/server/actors/pause-scoped.js
@@ -1,128 +1,101 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
-const { ObjectActor } = require("devtools/server/actors/object");
-
-/**
- * A base actor for any actors that should only respond receive messages in the
- * paused state. Subclasses may expose a `threadActor` which is used to help
- * determine when we are in a paused state. Subclasses should set their own
- * "constructor" property if they want better error messages. You should never
- * instantiate a PauseScopedActor directly, only through subclasses.
- */
-function PauseScopedActor() {
-}
+const { extend } = require("devtools/shared/extend");
+const { ObjectActorProto } = require("devtools/server/actors/object");
+const protocol = require("devtools/shared/protocol");
+const { ActorClassWithSpec } = protocol;
+const { objectSpec } = require("devtools/shared/specs/object");
/**
- * A function decorator for creating methods to handle protocol messages that
- * should only be received while in the paused state.
- *
- * @param method Function
- * The function we are decorating.
- */
-PauseScopedActor.withPaused = function(method) {
- return function() {
- if (this.isPaused()) {
- return method.apply(this, arguments);
- }
- return this._wrongState();
- };
-};
+ * Protocol.js expects only the prototype object, and does not maintain the prototype
+ * chain when it constructs the ActorClass. For this reason we are using extend to
+ * maintain the properties of ObjectActorProto.
+ **/
+const proto = extend({}, ObjectActorProto);
-PauseScopedActor.prototype = {
-
+Object.assign(proto, {
+ typeName: "pausedobj",
/**
- * Returns true if we are in the paused state.
+ * Creates a pause-scoped actor for the specified object.
+ * @see ObjectActor
*/
+ initialize: function(obj, hooks, conn) {
+ ObjectActorProto.initialize.call(this, obj, hooks, conn);
+ this.hooks.promote = hooks.promote;
+ this.hooks.isThreadLifetimePool = hooks.isThreadLifetimePool;
+ },
+
isPaused: function() {
- // When there is not a ThreadActor available (like in the webconsole) we
- // have to be optimistic and assume that we are paused so that we can
- // respond to requests.
- return this.threadActor ? this.threadActor.state === "paused" : true;
+ return this.threadActor
+ ? this.threadActor.state === "paused"
+ : true;
},
- /**
- * Returns the wrongState response packet for this actor.
- */
- _wrongState: function() {
- return {
- error: "wrongState",
- message: this.constructor.name +
- " actors can only be accessed while the thread is paused."
+ withPaused: function(method) {
+ return function() {
+ if (this.isPaused()) {
+ return method.apply(this, arguments);
+ }
+
+ return {
+ error: "wrongState",
+ message: this.constructor.name +
+ " actors can only be accessed while the thread is paused."
+ };
};
}
-};
-
-/**
- * Creates a pause-scoped actor for the specified object.
- * @see ObjectActor
- */
-function PauseScopedObjectActor(obj, hooks) {
- ObjectActor.call(this, obj, hooks);
- this.hooks.promote = hooks.promote;
- this.hooks.isThreadLifetimePool = hooks.isThreadLifetimePool;
-}
-
-PauseScopedObjectActor.prototype = Object.create(PauseScopedActor.prototype);
-
-Object.assign(PauseScopedObjectActor.prototype, ObjectActor.prototype);
+});
-Object.assign(PauseScopedObjectActor.prototype, {
- constructor: PauseScopedObjectActor,
- actorPrefix: "pausedobj",
-
- onOwnPropertyNames:
- PauseScopedActor.withPaused(ObjectActor.prototype.onOwnPropertyNames),
-
- onPrototypeAndProperties:
- PauseScopedActor.withPaused(ObjectActor.prototype.onPrototypeAndProperties),
+const guardWithPaused = [
+ "decompile",
+ "displayString",
+ "ownPropertyNames",
+ "parameterNames",
+ "property",
+ "prototype",
+ "prototypeAndProperties",
+ "scope",
+];
- onPrototype: PauseScopedActor.withPaused(ObjectActor.prototype.onPrototype),
- onProperty: PauseScopedActor.withPaused(ObjectActor.prototype.onProperty),
- onDecompile: PauseScopedActor.withPaused(ObjectActor.prototype.onDecompile),
+guardWithPaused.forEach(f => {
+ proto[f] = proto.withPaused(ObjectActorProto[f]);
+});
- onDisplayString:
- PauseScopedActor.withPaused(ObjectActor.prototype.onDisplayString),
-
- onParameterNames:
- PauseScopedActor.withPaused(ObjectActor.prototype.onParameterNames),
-
+Object.assign(proto, {
/**
* Handle a protocol request to promote a pause-lifetime grip to a
* thread-lifetime grip.
*
* @param request object
* The protocol request object.
*/
- onThreadGrip: PauseScopedActor.withPaused(function(request) {
+ threadGrip: proto.withPaused(function(request) {
this.hooks.promote();
return {};
}),
/**
* Handle a protocol request to release a thread-lifetime grip.
*
* @param request object
* The protocol request object.
*/
- onRelease: PauseScopedActor.withPaused(function(request) {
+ destroy: proto.withPaused(function(request) {
if (this.hooks.isThreadLifetimePool()) {
return { error: "notReleasable",
message: "Only thread-lifetime actors can be released." };
}
- this.release();
- return {};
+ return protocol.Actor.prototype.destroy.call(this);
}),
});
-Object.assign(PauseScopedObjectActor.prototype.requestTypes, {
- "threadGrip": PauseScopedObjectActor.prototype.onThreadGrip,
-});
+exports.PauseScopedObjectActor = ActorClassWithSpec(objectSpec, proto);
+ // ActorClassWithSpec(objectSpec, {...ObjectActorProto, ...proto});
-exports.PauseScopedObjectActor = PauseScopedObjectActor;
--- a/devtools/server/actors/promises.js
+++ b/devtools/server/actors/promises.js
@@ -122,34 +122,34 @@ var PromisesActor = protocol.ActorClassW
incrementGripDepth: () => this._gripDepth++,
decrementGripDepth: () => this._gripDepth--,
createValueGrip: v =>
createValueGrip(v, this._navigationLifetimePool, this.objectGrip),
sources: () => this.parentActor.sources,
createEnvironmentActor: () => DevToolsUtils.reportException(
"PromisesActor", Error("createEnvironmentActor not yet implemented")),
getGlobalDebugObject: () => null,
- });
+ }, this.conn);
this._navigationLifetimePool.addActor(actor);
this._navigationLifetimePool.objectActors.set(promise, actor);
return actor;
},
/**
* Get a grip for the given Promise object.
*
* @param object value
* The Promise object
* @return object
* The grip for the given Promise object
*/
objectGrip: function(value) {
- return this._createObjectActorForPromise(value).grip();
+ return this._createObjectActorForPromise(value).form();
},
/**
* Get a list of ObjectActors for all live Promise Objects.
*/
listPromises: function() {
const promises = this.dbg.findObjects({ class: "Promise" });
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -987,17 +987,24 @@ const ThreadActor = ActorClassWithSpec(t
const actor = this.threadLifetimePool.get(actorID);
if (!actor) {
if (!res) {
res = { error: "notReleasable",
message: "Only thread-lifetime actors can be released." };
}
continue;
}
- actor.onRelease();
+
+ // We can still have old-style actors (e.g. object/long-string) in the pool, so we
+ // need to check onRelease existence.
+ if (actor.onRelease) {
+ actor.onRelease();
+ } else if (actor.destroy) {
+ actor.destroy();
+ }
}
return res ? res : {};
},
/**
* Get the script and source lists from the debugger.
*/
_discoverSources: function() {
@@ -1379,38 +1386,37 @@ const ThreadActor = ActorClassWithSpec(t
* The actor pool where the new object actor will be added.
*/
objectGrip: function(value, pool) {
if (!pool.objectActors) {
pool.objectActors = new WeakMap();
}
if (pool.objectActors.has(value)) {
- return pool.objectActors.get(value).grip();
- } else if (this.threadLifetimePool.objectActors.has(value)) {
- return this.threadLifetimePool.objectActors.get(value).grip();
+ return pool.objectActors.get(value).form();
+ }
+
+ if (this.threadLifetimePool.objectActors.has(value)) {
+ return this.threadLifetimePool.objectActors.get(value).form();
}
const actor = new PauseScopedObjectActor(value, {
getGripDepth: () => this._gripDepth,
incrementGripDepth: () => this._gripDepth++,
decrementGripDepth: () => this._gripDepth--,
- createValueGrip: v => createValueGrip(v, this._pausePool,
- this.pauseObjectGrip),
+ createValueGrip: v => createValueGrip(v, this._pausePool, this.pauseObjectGrip),
sources: () => this.sources,
- createEnvironmentActor: (e, p) =>
- this.createEnvironmentActor(e, p),
+ createEnvironmentActor: (e, p) => this.createEnvironmentActor(e, p),
promote: () => this.threadObjectGrip(actor),
- isThreadLifetimePool: () =>
- actor.registeredPool !== this.threadLifetimePool,
+ isThreadLifetimePool: () => actor.registeredPool !== this.threadLifetimePool,
getGlobalDebugObject: () => this.globalDebugObject
- });
+ }, this.conn);
pool.addActor(actor);
pool.objectActors.set(value, actor);
- return actor.grip();
+ return actor.form();
},
/**
* Create a grip for the given debuggee object with a pause lifetime.
*
* @param value Debugger.Object
* The debuggee object value.
*/
@@ -1730,17 +1736,17 @@ const ThreadActor = ActorClassWithSpec(t
for (const actorID of request.actors) {
// This code assumes that there are no lazily loaded actors returned
// by this call.
const actor = this.conn.getActor(actorID);
if (!actor) {
return { from: this.actorID,
error: "noSuchActor" };
}
- const handler = actor.onPrototypeAndProperties;
+ const handler = actor.prototypeAndProperties;
if (!handler) {
return { from: this.actorID,
error: "unrecognizedPacketType",
message: ('Actor "' + actorID +
'" does not recognize the packet type ' +
'"prototypeAndProperties"') };
}
result[actorID] = handler.call(actor, {});
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -476,19 +476,19 @@ WebConsoleActor.prototype =
getGripDepth: () => this._gripDepth,
incrementGripDepth: () => this._gripDepth++,
decrementGripDepth: () => this._gripDepth--,
createValueGrip: v => this.createValueGrip(v),
sources: () => DevToolsUtils.reportException("WebConsoleActor",
Error("sources not yet implemented")),
createEnvironmentActor: (env) => this.createEnvironmentActor(env),
getGlobalDebugObject: () => this.globalDebugObject
- });
+ }, this.conn);
pool.addActor(actor);
- return actor.grip();
+ return actor.form();
},
/**
* Create a grip for the given string.
*
* @param string string
* The string you want to create the grip for.
* @param object pool
--- a/devtools/shared/specs/index.js
+++ b/devtools/shared/specs/index.js
@@ -143,16 +143,21 @@ const Types = exports.__TypesForTests =
front: null,
},
{
types: ["domnode", "domnodelist"],
spec: "devtools/shared/specs/node",
front: "devtools/shared/fronts/node",
},
{
+ types: ["obj", "object.descriptor"],
+ spec: "devtools/shared/specs/object",
+ front: null,
+ },
+ {
types: ["perf"],
spec: "devtools/shared/specs/perf",
front: "devtools/shared/fronts/perf",
},
{
types: ["performance"],
spec: "devtools/shared/specs/performance",
front: "devtools/shared/fronts/performance",
--- a/devtools/shared/specs/moz.build
+++ b/devtools/shared/specs/moz.build
@@ -28,16 +28,17 @@ DevToolsModules(
'heap-snapshot-file.js',
'highlighters.js',
'index.js',
'inspector.js',
'layout.js',
'memory.js',
'network-event.js',
'node.js',
+ 'object.js',
'perf.js',
'performance-recording.js',
'performance.js',
'preference.js',
'promises.js',
'property-iterator.js',
'reflow.js',
'script.js',
new file mode 100644
--- /dev/null
+++ b/devtools/shared/specs/object.js
@@ -0,0 +1,187 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {
+ generateActorSpec,
+ Arg,
+ RetVal,
+ types,
+} = require("devtools/shared/protocol");
+
+types.addDictType("object.descriptor", {
+ configurable: "boolean",
+ enumerable: "boolean",
+ // Can be null if there is a getter for the property.
+ value: "nullable:json",
+ // Only set `value` exists.
+ writable: "nullable:boolean",
+ // Only set when `value` does not exist and there is a getter for the property.
+ get: "nullable:json",
+ // Only set when `value` does not exist and there is a setter for the property.
+ set: "nullable:json",
+});
+
+types.addDictType("object.definitionSite", {
+ source: "source",
+ line: "number",
+ column: "number",
+});
+
+types.addDictType("object.prototypeproperties", {
+ prototype: "object.descriptor",
+ ownProperties: "nullable:json",
+ ownSymbols: "nullable:array:object.descriptor",
+ safeGetterValues: "nullable:json",
+});
+
+types.addDictType("object.prototype", {
+ prototype: "object.descriptor",
+});
+
+types.addDictType("object.property", {
+ descriptor: "nullable:object.descriptor"
+});
+
+types.addDictType("object.bindings", {
+ arguments: "array:json",
+ variables: "json",
+});
+
+types.addDictType("object.scope", {
+ scope: "environment"
+});
+
+types.addDictType("object.enumProperties.Options", {
+ enumEntries: "nullable:boolean",
+ ignoreNonIndexedProperties: "nullable:boolean",
+ ignoreIndexedProperties: "nullable:boolean",
+ query: "nullable:string",
+ sort: "nullable:boolean",
+});
+
+types.addDictType("object.ownPropertyNames", {
+ ownPropertyNames: "array:string"
+});
+
+types.addDictType("object.displayString", {
+ displayString: "string"
+});
+
+types.addDictType("object.decompile", {
+ decompiledCode: "string"
+});
+
+types.addDictType("object.parameterNames", {
+ parameterNames: "nullable:array:string"
+});
+
+types.addDictType("object.dependentPromises", {
+ promises: "array:object.descriptor"
+});
+
+types.addDictType("object.originalSourceLocation", {
+ source: "source",
+ line: "number",
+ column: "number",
+ functionDisplayName: "string"
+});
+
+const objectSpec = generateActorSpec({
+ typeName: "obj",
+
+ methods: {
+ allocationStack: {
+ request: {},
+ response: {
+ allocationStack: RetVal("array:object.originalSourceLocation")
+ },
+ },
+ decompile: {
+ request: {
+ pretty: Arg(0, "boolean")
+ },
+ response: RetVal("object.decompile"),
+ },
+ definitionSite: {
+ request: {},
+ response: RetVal("object.definitionSite"),
+ },
+ dependentPromises: {
+ request: {},
+ response: RetVal("object.dependentPromises")
+ },
+ displayString: {
+ request: {},
+ response: RetVal("object.displayString")
+ },
+ enumEntries: {
+ request: {},
+ response: {
+ iterator: RetVal("propertyIterator")
+ }
+ },
+ enumProperties: {
+ request: {
+ options: Arg(0, "nullable:object.enumProperties.Options"),
+ },
+ response: {
+ iterator: RetVal("propertyIterator")
+ }
+ },
+ enumSymbols: {
+ request: {},
+ response: {
+ iterator: RetVal("symbolIterator")
+ }
+ },
+ fulfillmentStack: {
+ request: {},
+ response: {
+ fulfillmentStack: RetVal("array:object.originalSourceLocation")
+ },
+ },
+ ownPropertyNames: {
+ request: {},
+ response: RetVal("object.ownPropertyNames")
+ },
+ parameterNames: {
+ request: {},
+ response: RetVal("object.parameterNames")
+ },
+ prototypeAndProperties: {
+ request: {},
+ response: RetVal("object.prototypeproperties")
+ },
+ prototype: {
+ request: {},
+ response: RetVal("object.prototype")
+ },
+ property: {
+ request: {
+ name: Arg(0, "string")
+ },
+ response: RetVal("object.property")
+ },
+ rejectionStack: {
+ request: {},
+ response: {
+ rejectionStack: RetVal("array:object.originalSourceLocation")
+ },
+ },
+ release: { release: true },
+ scope: {
+ request: {},
+ response: RetVal("object.scope"),
+ },
+ // Needed for the PauseScopedObjectActor which extends the ObjectActor.
+ threadGrip: {
+ request: {},
+ response: {},
+ }
+ }
+});
+
+exports.objectSpec = objectSpec;
--- a/devtools/shared/specs/promises.js
+++ b/devtools/shared/specs/promises.js
@@ -7,17 +7,17 @@ const {
Arg,
RetVal,
generateActorSpec,
types
} = require("devtools/shared/protocol");
// Teach protocol.js how to deal with legacy actor types
types.addType("ObjectActor", {
- write: actor => actor.grip(),
+ write: actor => actor.form(),
read: grip => grip
});
const promisesSpec = generateActorSpec({
typeName: "promises",
events: {
// Event emitted for new promises allocated in debuggee and bufferred by
--- a/devtools/shared/specs/symbol-iterator.js
+++ b/devtools/shared/specs/symbol-iterator.js
@@ -15,30 +15,16 @@ types.addDictType("symboliterator.data",
ownSymbols: "array:symboliterator.ownsymbols",
});
types.addDictType("symboliterator.ownsymbols", {
name: "string",
descriptor: "nullable:object.descriptor",
});
-// XXX Move to the object spec in Bug 1450944.
-types.addDictType("object.descriptor", {
- configurable: "boolean",
- enumerable: "boolean",
- // Can be null if there is a getter for the property.
- value: "nullable:json",
- // Only set `value` exists.
- writable: "nullable:boolean",
- // Only set when `value` does not exist and there is a getter for the property.
- get: "nullable:json",
- // Only set when `value` does not exist and there is a setter for the property.
- set: "nullable:json",
-});
-
const symbolIteratorSpec = generateActorSpec({
typeName: "symbolIterator",
methods: {
slice: {
request: {
start: Option(0, "number"),
count: Option(0, "number"),