diff --git a/devtools/server/actors/object.js b/devtools/server/actors/object.js index df8d83bc3979..49326abe4ac3 100644 --- a/devtools/server/actors/object.js +++ b/devtools/server/actors/object.js @@ -2260,7 +2260,7 @@ function makeDebuggeeValueIfNeeded(obj, value) { } /** - * Creates an actor for the specied "very long" string. "Very long" is specified + * Creates an actor for the specified "very long" string. "Very long" is specified * at the server's discretion. * * @param string String @@ -2316,7 +2316,7 @@ LongStringActor.prototype = { */ onRelease: function () { // TODO: also check if registeredPool === threadActor.threadLifetimePool - // when the web console moves aray from manually releasing pause-scoped + // when the web console moves away from manually releasing pause-scoped // actors. this._releaseActor(); this.registeredPool.removeActor(this); @@ -2336,7 +2336,70 @@ LongStringActor.prototype.requestTypes = { }; /** - * Creates an actor for the specied ArrayBuffer. + * Creates an actor for the specified symbol. + * + * @param symbol Symbol + * The symbol. + */ +function SymbolActor(symbol) { + this.symbol = symbol; +} + +SymbolActor.prototype = { + actorPrefix: "symbol", + + rawValue: function () { + return this.symbol; + }, + + destroy: function () { + // Because symbolActors is not a weak map, we won't automatically leave + // it so we need to manually leave on destroy so that we don't leak + // memory. + this._releaseActor(); + }, + + /** + * Returns a grip for this actor for returning in a protocol message. + */ + grip: function () { + let form = { + type: "symbol", + actor: this.actorID, + }; + let name = getSymbolName(this.symbol); + if (name !== undefined) { + // Create a grip for the name because it might be a longString. + form.name = createValueGrip(name, this.registeredPool); + } + return form; + }, + + /** + * Handle a request to release this SymbolActor instance. + */ + onRelease: function () { + // TODO: also check if registeredPool === threadActor.threadLifetimePool + // when the web console moves away from manually releasing pause-scoped + // actors. + this._releaseActor(); + this.registeredPool.removeActor(this); + return {}; + }, + + _releaseActor: function () { + if (this.registeredPool && this.registeredPool.symbolActors) { + delete this.registeredPool.symbolActors[this.symbol]; + } + } +}; + +SymbolActor.prototype.requestTypes = { + "release": SymbolActor.prototype.onRelease +}; + +/** + * Creates an actor for the specified ArrayBuffer. * * @param buffer ArrayBuffer * The buffer. @@ -2433,14 +2496,7 @@ function createValueGrip(value, pool, makeObjectGrip) { return makeObjectGrip(value, pool); case "symbol": - let form = { - type: "symbol" - }; - let name = getSymbolName(value); - if (name !== undefined) { - form.name = createValueGrip(name, pool, makeObjectGrip); - } - return form; + return symbolGrip(value, pool); default: assert(false, "Failed to provide a grip for: " + value); @@ -2489,6 +2545,29 @@ function longStringGrip(str, pool) { return actor.grip(); } +/** + * Create a grip for the given symbol. + * + * @param sym Symbol + * The symbol we are creating a grip for. + * @param pool ActorPool + * The actor pool where the new actor will be added. + */ +function symbolGrip(sym, pool) { + if (!pool.symbolActors) { + pool.symbolActors = Object.create(null); + } + + if (sym in pool.symbolActors) { + return pool.symbolActors[sym].grip(); + } + + let actor = new SymbolActor(sym); + pool.addActor(actor); + pool.symbolActors[sym] = actor; + return actor.grip(); +} + /** * Create a grip for the given ArrayBuffer. * @@ -2594,6 +2673,7 @@ function isArrayIndex(str) { exports.ObjectActor = ObjectActor; exports.PropertyIteratorActor = PropertyIteratorActor; exports.LongStringActor = LongStringActor; +exports.SymbolActor = SymbolActor; exports.createValueGrip = createValueGrip; exports.stringIsLong = stringIsLong; exports.longStringGrip = longStringGrip; diff --git a/devtools/server/tests/unit/test_symbolactor.js b/devtools/server/tests/unit/test_symbolactor.js new file mode 100644 index 000000000000..79b53bd06b97 --- /dev/null +++ b/devtools/server/tests/unit/test_symbolactor.js @@ -0,0 +1,49 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2; js-indent-level: 2 -*- */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { SymbolActor } = require("devtools/server/actors/object"); + +function run_test() { + test_SA_destroy(); + test_SA_grip(); + test_SA_raw(); +} + +const SYMBOL_NAME = "abc"; +const TEST_SYMBOL = Symbol(SYMBOL_NAME); + +function makeMockSymbolActor() { + let symbol = TEST_SYMBOL; + let actor = new SymbolActor(symbol); + actor.actorID = "symbol1"; + actor.registeredPool = { + symbolActors: { + [symbol]: actor + } + }; + return actor; +} + +function test_SA_destroy() { + let actor = makeMockSymbolActor(); + strictEqual(actor.registeredPool.symbolActors[TEST_SYMBOL], actor); + + actor.destroy(); + strictEqual(TEST_SYMBOL in actor.registeredPool.symbolActors, false); +} + +function test_SA_grip() { + let actor = makeMockSymbolActor(); + let grip = actor.grip(); + strictEqual(grip.type, "symbol"); + strictEqual(grip.actor, actor.actorID); + strictEqual(grip.name, SYMBOL_NAME); +} + +function test_SA_raw() { + let actor = makeMockSymbolActor(); + strictEqual(actor.rawValue(), TEST_SYMBOL); +} diff --git a/devtools/server/tests/unit/xpcshell.ini b/devtools/server/tests/unit/xpcshell.ini index 9fddbe4a31d0..0152594e0462 100644 --- a/devtools/server/tests/unit/xpcshell.ini +++ b/devtools/server/tests/unit/xpcshell.ini @@ -235,3 +235,4 @@ support-files = xpcshell_debugging_script.js [test_safe-getter.js] [test_client_close.js] [test_shapes_highlighter_helpers.js] +[test_symbolactor.js]