diff --git a/browser/devtools/app-manager/app-projects.js b/browser/devtools/app-manager/app-projects.js index eb3c2ab32c6a..f4b1c20100ce 100644 --- a/browser/devtools/app-manager/app-projects.js +++ b/browser/devtools/app-manager/app-projects.js @@ -84,6 +84,8 @@ const IDB = { add: function(project) { let deferred = promise.defer(); + project = JSON.parse(JSON.stringify(project)); + if (!project.location) { // We need to make sure this object has a `.location` property. deferred.reject("Missing location property on project object."); diff --git a/js/public/Class.h b/js/public/Class.h index 31eebcedb001..8d3a08ad066b 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -584,7 +584,11 @@ Valueify(const JSClass *c) enum ESClassValue { ESClass_Object, ESClass_Array, ESClass_Number, ESClass_String, ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer, - ESClass_Date, ESClass_Set, ESClass_Map + ESClass_Date, ESClass_Set, ESClass_Map, + + // Special snowflake for the ES6 IsArray method. + // Please don't use it without calling that function. + ESClass_IsArray }; /* diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index b376a378e6b0..4af8f5ae8d02 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -2676,7 +2676,8 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp) HandleValue v = HandleValue::fromMarkedLocation(&p[i]); if (v.isObject()) { RootedObject obj(cx, &v.toObject()); - if (ObjectClassIs(obj, ESClass_Array, cx)) { + // This should be IsConcatSpreadable + if (IsArray(obj, cx)) { uint32_t alength; if (!GetLengthProperty(cx, obj, &alength)) return false; @@ -3039,7 +3040,11 @@ static bool array_isArray(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - bool isArray = args.length() > 0 && IsObjectWithClass(args[0], ESClass_Array, cx); + bool isArray = false; + if (args.get(0).isObject()) { + RootedObject obj(cx, &args[0].toObject()); + isArray = IsArray(obj, cx); + } args.rval().setBoolean(isArray); return true; } diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 7b2ae5a08129..1643cb339cad 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -719,7 +719,10 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx) switch (classValue) { case ESClass_Object: return obj->is(); - case ESClass_Array: return obj->is(); + case ESClass_Array: + case ESClass_IsArray: + // There difference between those is only relevant for proxies. + return obj->is(); case ESClass_Number: return obj->is(); case ESClass_String: return obj->is(); case ESClass_Boolean: return obj->is(); @@ -742,6 +745,16 @@ IsObjectWithClass(const Value &v, ESClassValue classValue, JSContext *cx) return ObjectClassIs(obj, classValue, cx); } +// ES6 7.2.2 +inline bool +IsArray(HandleObject obj, JSContext *cx) +{ + if (obj->is()) + return true; + + return ObjectClassIs(obj, ESClass_IsArray, cx); +} + inline bool Unbox(JSContext *cx, HandleObject obj, MutableHandleValue vp) { diff --git a/js/src/json.cpp b/js/src/json.cpp index b2cf669bcab4..d24b639643a5 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -322,7 +322,7 @@ JO(JSContext *cx, HandleObject obj, StringifyContext *scx) Maybe ids; const AutoIdVector *props; if (scx->replacer && !scx->replacer->isCallable()) { - MOZ_ASSERT(JS_IsArrayObject(cx, scx->replacer)); + MOZ_ASSERT(IsArray(scx->replacer, cx)); props = &scx->propertyList; } else { MOZ_ASSERT_IF(scx->replacer, scx->propertyList.length() == 0); @@ -507,7 +507,7 @@ Str(JSContext *cx, const Value &v, StringifyContext *scx) scx->depth++; bool ok; - if (ObjectClassIs(obj, ESClass_Array, cx)) + if (IsArray(obj, cx)) ok = JA(cx, obj, scx); else ok = JO(cx, obj, scx); @@ -529,7 +529,7 @@ js_Stringify(JSContext *cx, MutableHandleValue vp, JSObject *replacer_, Value sp if (replacer) { if (replacer->isCallable()) { /* Step 4a(i): use replacer to transform values. */ - } else if (ObjectClassIs(replacer, ESClass_Array, cx)) { + } else if (IsArray(replacer, cx)) { /* * Step 4b: The spec algorithm is unhelpfully vague about the exact * steps taken when the replacer is an array, regarding the exact @@ -560,7 +560,8 @@ js_Stringify(JSContext *cx, MutableHandleValue vp, JSObject *replacer_, Value sp /* Step 4b(ii). */ uint32_t len; - JS_ALWAYS_TRUE(GetLengthProperty(cx, replacer, &len)); + if (!GetLengthProperty(cx, replacer, &len)) + return false; if (replacer->is() && !replacer->isIndexed()) len = Min(len, replacer->as().getDenseInitializedLength()); @@ -693,7 +694,7 @@ Walk(JSContext *cx, HandleObject holder, HandleId name, HandleValue reviver, Mut if (val.isObject()) { RootedObject obj(cx, &val.toObject()); - if (ObjectClassIs(obj, ESClass_Array, cx)) { + if (IsArray(obj, cx)) { /* Step 2a(ii). */ uint32_t length; if (!GetLengthProperty(cx, obj, &length)) diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 61b68ca2f161..f957a411aa6f 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -1094,6 +1094,50 @@ ScriptedDirectProxyHandler::construct(JSContext *cx, HandleObject proxy, const C return true; } +bool +ScriptedDirectProxyHandler::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const +{ + ReportIncompatible(cx, args); + return false; +} + +bool +ScriptedDirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, + JSContext *cx) const +{ + // Special case IsArray. In every other instance ES wants to have exactly + // one object type and not a proxy around it, so return false. + if (classValue != ESClass_IsArray) + return false; + + // In ES6 IsArray is supposed to poke at the Proxy target, instead we do this here. + // The reason for this is that we have proxies for which looking at the target might + // be impossible. So instead we use our little objectClassIs function that just works + // already across different wrappers. + RootedObject target(cx, proxy->as().target()); + if (!target) + return false; + + return IsArray(target, cx); +} + +bool +ScriptedDirectProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, + RegExpGuard *g) const +{ + MOZ_CRASH("Should not end up in ScriptedDirectProxyHandler::regexp_toShared"); + return false; +} + +bool +ScriptedDirectProxyHandler::boxedValue_unbox(JSContext *cx, HandleObject proxy, + MutableHandleValue vp) const +{ + MOZ_CRASH("Should not end up in ScriptedDirectProxyHandler::boxedValue_unbox"); + return false; +} + bool ScriptedDirectProxyHandler::isCallable(JSObject *obj) const { diff --git a/js/src/proxy/ScriptedDirectProxyHandler.h b/js/src/proxy/ScriptedDirectProxyHandler.h index c23fa6cc066c..bb53401626a8 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.h +++ b/js/src/proxy/ScriptedDirectProxyHandler.h @@ -64,6 +64,16 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); } + // A scripted proxy should not be treated as generic in most contexts. + virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, + CallArgs args) const MOZ_OVERRIDE; + virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, + JSContext *cx) const MOZ_OVERRIDE; + virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, + RegExpGuard *g) const MOZ_OVERRIDE; + virtual bool boxedValue_unbox(JSContext *cx, HandleObject proxy, + MutableHandleValue vp) const MOZ_OVERRIDE; + virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE; virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE; diff --git a/js/src/tests/ecma_6/TypedArray/entries.js b/js/src/tests/ecma_6/TypedArray/entries.js index 276eb34e08d9..72099efd2aa2 100644 --- a/js/src/tests/ecma_6/TypedArray/entries.js +++ b/js/src/tests/ecma_6/TypedArray/entries.js @@ -35,14 +35,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.entries.call(invalidReceiver); }, TypeError, "Assert that entries fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.entries.call(new Proxy(new constructor(), {})); } if (typeof reportCompare === "function") diff --git a/js/src/tests/ecma_6/TypedArray/every-and-some.js b/js/src/tests/ecma_6/TypedArray/every-and-some.js index 132ea16db22d..11f924447fac 100644 --- a/js/src/tests/ecma_6/TypedArray/every-and-some.js +++ b/js/src/tests/ecma_6/TypedArray/every-and-some.js @@ -111,14 +111,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.every.call(invalidReceiver, () => true); }, TypeError, "Assert that every fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.every.call(new Proxy(new constructor(), {}), () => true); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { @@ -237,14 +236,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.some.call(invalidReceiver, () => true); }, TypeError, "Assert that some fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.some.call(new Proxy(new constructor(), {}), () => false); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { @@ -258,4 +256,4 @@ assertEq(new Float32Array([undefined, , NaN]).some(v => v === v), false); assertEq(new Float64Array([undefined, , NaN]).some(v => v === v), false); if (typeof reportCompare === "function") - reportCompare(true, true); \ No newline at end of file + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/fill.js b/js/src/tests/ecma_6/TypedArray/fill.js index 9195428a8653..b297d146aa69 100644 --- a/js/src/tests/ecma_6/TypedArray/fill.js +++ b/js/src/tests/ecma_6/TypedArray/fill.js @@ -53,14 +53,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./] + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.fill.call(invalidReceiver, 1); }, TypeError); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.fill.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. Object.defineProperty(new constructor([1, 2, 3]), "length", { diff --git a/js/src/tests/ecma_6/TypedArray/includes.js b/js/src/tests/ecma_6/TypedArray/includes.js index 5560a97481fd..15e98f73cad7 100644 --- a/js/src/tests/ecma_6/TypedArray/includes.js +++ b/js/src/tests/ecma_6/TypedArray/includes.js @@ -35,14 +35,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.includes.call(invalidReceiver); }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.includes.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { diff --git a/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js b/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js index 9dfdd8c9b631..4c85860f5abf 100644 --- a/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js +++ b/js/src/tests/ecma_6/TypedArray/indexOf-and-lastIndexOf.js @@ -41,14 +41,13 @@ for (var constructor of constructors) { assertEq(new constructor([1, 2, 1, 2, 1]).indexOf(1, -2), 4); // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.indexOf.call(invalidReceiver); }, TypeError, "Assert that indexOf fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.indexOf.call(new Proxy(new constructor(), {})); // test that this.length is never called assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { @@ -94,14 +93,13 @@ for (var constructor of constructors) { assertEq(new constructor([1, 2, 1, 2, 1]).lastIndexOf(1, -2), 2); // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.lastIndexOf.call(invalidReceiver); }, TypeError, "Assert that lastIndexOf fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.lastIndexOf.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([0, 1, 2, 3, 5]), "length", { diff --git a/js/src/tests/ecma_6/TypedArray/join.js b/js/src/tests/ecma_6/TypedArray/join.js index b1a6104784bc..e821f99b1fcc 100644 --- a/js/src/tests/ecma_6/TypedArray/join.js +++ b/js/src/tests/ecma_6/TypedArray/join.js @@ -36,14 +36,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.join.call(invalidReceiver); }, TypeError, "Assert that join fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.join.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. assertEq(Object.defineProperty(new constructor([1, 2, 3]), "length", { diff --git a/js/src/tests/ecma_6/TypedArray/keys.js b/js/src/tests/ecma_6/TypedArray/keys.js index 372be110b386..46b476d98fda 100644 --- a/js/src/tests/ecma_6/TypedArray/keys.js +++ b/js/src/tests/ecma_6/TypedArray/keys.js @@ -35,14 +35,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.keys.call(invalidReceiver); }, TypeError, "Assert that keys fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.keys.call(new Proxy(new constructor(), {})); } if (typeof reportCompare === "function") diff --git a/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js b/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js index 021f910fb3eb..181e2926b3bc 100644 --- a/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js +++ b/js/src/tests/ecma_6/TypedArray/reduce-and-reduceRight.js @@ -87,14 +87,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(3), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.reduce.call(invalidReceiver, () => {}); }, TypeError, "Assert that reduce fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.reduce.call(new Proxy(new constructor(3), {}), () => {}); // Test that the length getter is never called. assertEq(Object.defineProperty(arr, "length", { @@ -181,14 +180,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(3), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.reduceRight.call(invalidReceiver, () => {}); }, TypeError, "Assert that reduceRight fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.reduceRight.call(new Proxy(new constructor(3), {}), () => {}); // Test that the length getter is never called. assertEq(Object.defineProperty(arr, "length", { @@ -199,4 +197,4 @@ for (var constructor of constructors) { } if (typeof reportCompare === "function") - reportCompare(true, true); \ No newline at end of file + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/reverse.js b/js/src/tests/ecma_6/TypedArray/reverse.js index 0404d46bc8e2..d4196a6adc6d 100644 --- a/js/src/tests/ecma_6/TypedArray/reverse.js +++ b/js/src/tests/ecma_6/TypedArray/reverse.js @@ -30,14 +30,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.reverse.call(invalidReceiver); }, TypeError, "Assert that reverse fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.reverse.call(new Proxy(new constructor(), {})); // Test that the length getter is never called. Object.defineProperty(new constructor([1, 2, 3]), "length", { diff --git a/js/src/tests/ecma_6/TypedArray/values.js b/js/src/tests/ecma_6/TypedArray/values.js index af173708bbe5..2635a6abfc67 100644 --- a/js/src/tests/ecma_6/TypedArray/values.js +++ b/js/src/tests/ecma_6/TypedArray/values.js @@ -36,14 +36,13 @@ for (var constructor of constructors) { } // Throws if `this` isn't a TypedArray. - var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./]; + var invalidReceivers = [undefined, null, 1, false, "", Symbol(), [], {}, /./, + new Proxy(new constructor(), {})]; invalidReceivers.forEach(invalidReceiver => { assertThrowsInstanceOf(() => { constructor.prototype.values.call(invalidReceiver); }, TypeError, "Assert that values fails if this value is not a TypedArray"); }); - // FIXME: Should throw exception if `this` is a proxy, see bug 1115361. - constructor.prototype.values.call(new Proxy(new constructor(), {})); } if (typeof reportCompare === "function") diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 1122ac18c148..b82b118c737f 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -489,6 +489,7 @@ RegExpToShared(JSContext *cx, HandleObject obj, RegExpGuard *g) { if (obj->is()) return obj->as().getShared(cx, g); + MOZ_ASSERT(Proxy::objectClassIs(obj, ESClass_RegExp, cx)); return Proxy::regexp_toShared(cx, obj, g); }