Bug 1155877 - ObjectUtils.strict. r=mossop

--HG--
extra : rebase_source : e69178e9208b149fc66c61f15d74750d1d765907
This commit is contained in:
David Rajchenbach-Teller 2015-04-18 00:21:02 +02:00
parent ec1aede15a
commit 64d710a064
4 changed files with 83 additions and 0 deletions

View File

@ -151,6 +151,13 @@ this.AppConstants = Object.freeze({
false,
#endif
DEBUG:
#ifdef DEBUG
true,
#else
false,
#endif
MOZ_APP_NAME: "@MOZ_APP_NAME@",
MOZ_APP_VERSION: "@MOZ_APP_VERSION@",
MOZ_BUILD_APP: "@MOZ_BUILD_APP@",

View File

@ -14,6 +14,12 @@ this.EXPORTED_SYMBOLS = [
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
// Used only to cause test failures.
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
this.ObjectUtils = {
/**
* This tests objects & values for deep equality.
@ -30,6 +36,25 @@ this.ObjectUtils = {
deepEqual: function(a, b) {
return _deepEqual(a, b);
},
/**
* A thin wrapper on an object, designed to prevent client code from
* accessing non-existent properties because of typos.
*
* // Without `strict`
* let foo = { myProperty: 1 };
* foo.MyProperty; // undefined
*
* // With `strict`
* let strictFoo = ObjectUtils.strict(foo);
* strictFoo.myProperty; // 1
* strictFoo.MyProperty; // TypeError: No such property "MyProperty"
*
* Note that `strict` has no effect in non-DEBUG mode.
*/
strict: function(obj) {
return _strict(obj);
}
};
// ... Start of previously MIT-licensed code.
@ -129,3 +154,21 @@ function objEquiv(a, b) {
}
// ... End of previously MIT-licensed code.
function _strict(obj) {
if (typeof obj != "object") {
throw new TypeError("Expected an object");
}
return new Proxy(obj, {
get: function(target, name) {
if (name in obj) {
return obj[name];
}
let error = new TypeError(`No such property: "${name}"`);
Promise.reject(error); // Cause an xpcshell/mochitest failure.
throw error;
}
});
}

View File

@ -0,0 +1,32 @@
"use strict";
let {ObjectUtils} = Components.utils.import("resource://gre/modules/ObjectUtils.jsm", {});
let {Promise} = Components.utils.import("resource://gre/modules/Promise.jsm", {});
add_task(function* init() {
// The code will cause uncaught rejections on purpose.
Promise.Debugging.clearUncaughtErrorObservers();
});
add_task(function* test_strict() {
let loose = { a: 1 };
let strict = ObjectUtils.strict(loose);
loose.a; // Should not throw.
loose.b || undefined; // Should not throw.
strict.a; // Should not throw.
Assert.throws(() => strict.b, /No such property: "b"/);
"b" in strict; // Should not throw.
strict.b = 2;
strict.b; // Should not throw.
Assert.throws(() => strict.c, /No such property: "c"/);
"c" in strict; // Should not throw.
loose.c = 3;
strict.c; // Should not throw.
});
function run_test() {
run_next_test();
}

View File

@ -17,6 +17,7 @@ support-files =
[test_Log.js]
[test_NewTabUtils.js]
[test_ObjectUtils.js]
[test_ObjectUtils_strict.js]
[test_PermissionsUtils.js]
[test_Preferences.js]
[test_Promise.js]