Skip to content
This repository was archived by the owner on Feb 26, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions packages/api-utils/lib/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,36 @@

"use strict";

const create = Object.create;
const prototypeOf = Object.getPrototypeOf;

/**
* Function creates a new namespace. Optionally `prototype` object may be
* passed, in which case namespace objects will inherit from it. Returned value
* is a function that can be used to get access to the namespaced properties
* for the passed object.
* Returns a new namespace, function that may can be used to access an
* namespaced object of the argument argument. Namespaced object are associated
* with owner objects via weak references. Namespaced objects inherit from the
* owners ancestor namespaced object. If owner's ancestor is `null` then
* namespaced object inherits from given `prototype`. Namespaces can be used
* to define internal APIs that can be shared via enclosing `namespace`
* function.
* @examples
* const ns = Namespace();
* ns(myObject).secret = secret;
* const internals = ns();
* internals(object).secret = secret;
*/
exports.Namespace = function Namespace(prototype) {
prototype = prototype || Object.prototype;
function ns() {
const map = new WeakMap();
return function namespace(target) {
return map.get(target) ||
map.set(target, Object.create(prototype)), map.get(target);
if (!target) // If `target` is not an object return `target` itself.
return target;
// If target has no namespaced object yet, create one that inherits from
// the target prototype's namespaced object.
if (!map.has(target))
map.set(target, create(namespace(prototypeOf(target) || null)));

return map.get(target);
};
};

// `Namespace` is a e4x function in the scope, so we export the function also as
// `ns` as alias to avoid clashing.
exports.ns = exports.Namespace;
exports.ns = ns;
exports.Namespace = ns;
52 changes: 40 additions & 12 deletions packages/api-utils/tests/test-namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

"use strict";

let { Namespace, ns } = require("api-utils/namespace");
let { Cc, Ci, Cu } = require("chrome");
let { setTimeout } = require("api-utils/timer")
const { ns } = require("api-utils/namespace");
const { Cc, Ci, Cu } = require("chrome");
const { setTimeout } = require("api-utils/timer")

exports["test post GC references"] = function (assert, done) {
var target = {}, local = ns()
Expand All @@ -22,7 +22,7 @@ exports["test post GC references"] = function (assert, done) {
};

exports["test namsepace basics"] = function(assert) {
var privates = Namespace();
var privates = ns();
var object = { foo: function foo() { return "hello foo"; } };

assert.notEqual(privates(object), object,
Expand All @@ -35,7 +35,7 @@ exports["test namsepace basics"] = function(assert) {
};

exports["test namespace overlays"] = function(assert) {
var _ = new Namespace();
var _ = ns();
var object = { foo: 'foo' };

_(object).foo = 'bar';
Expand All @@ -56,25 +56,26 @@ exports["test namespace overlays"] = function(assert) {
};

exports["test shared namespaces"] = function(assert) {
var _ = new Namespace({ hello: 'hello world' });
var _ = ns();

var f1 = { hello: 1 };
var f2 = { foo: 'foo' };
var f2 = { foo: 'foo', hello: 2 };
_(f1).foo = _(f2).foo = 'bar';

assert.equal(_(f1).hello, _(f2).hello, "namespace can be shared");
assert.notEqual(f1.hello, _(f1).hello, "shared namespace can overlay");
assert.notEqual(f2.hello, _(f2).hello, "target is not affected");

_(f1).hello = 2;
_(f1).hello = 3;

assert.notEqual(_(f1).hello, _(f2).hello,
"namespaced property can be overided");
assert.equal(_(f2).hello, _({}).hello, "namespace does not change");
};

exports["test multi namespace"] = function(assert) {
var n1 = new Namespace();
var n2 = new Namespace();
var n1 = ns();
var n2 = ns();
var object = { baz: 1 };
n1(object).foo = 1;
n2(object).foo = 2;
Expand All @@ -87,8 +88,35 @@ exports["test multi namespace"] = function(assert) {
};

exports["test ns alias"] = function(assert) {
assert.strictEqual(ns, Namespace,
assert.strictEqual(ns, require('api-utils/namespace').Namespace,
"ns is an alias of Namespace");
}
};

exports["test ns inheritance"] = function(assert) {
let _ = ns();

let prototype = { level: 1 };
let object = Object.create(prototype);
let delegee = Object.create(object);

_(prototype).foo = {};

assert.ok(!Object.prototype.hasOwnProperty.call(_(delegee), "foo"),
"namespaced property is not copied to descendants");
assert.equal(_(delegee).foo, _(prototype).foo,
"namespaced properties are inherited by descendants");

_(object).foo = {};
assert.notEqual(_(object).foo, _(prototype).foo,
"namespaced properties may be shadowed");
assert.equal(_(object).foo, _(delegee).foo,
"shadwed properties are inherited by descendants");

_(object).bar = {};
assert.ok(!("bar" in _(prototype)),
"descendants properties are not copied to ancestors");
assert.ok(_(object).bar, _(delegee).bar,
"descendants properties are inherited");
};

require("test").run(exports);