diff --git a/dom/base/WindowNamedPropertiesHandler.cpp b/dom/base/WindowNamedPropertiesHandler.cpp index a2d4dc3ef0..6857b2b5d5 100644 --- a/dom/base/WindowNamedPropertiesHandler.cpp +++ b/dom/base/WindowNamedPropertiesHandler.cpp @@ -159,7 +159,8 @@ bool WindowNamedPropertiesHandler::defineProperty(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, - JS::MutableHandle aDesc) const + JS::MutableHandle aDesc, + JS::ObjectOpResult &result) const { ErrorResult rv; rv.ThrowTypeError(MSG_DEFINEPROPERTY_ON_GSP); diff --git a/dom/base/WindowNamedPropertiesHandler.h b/dom/base/WindowNamedPropertiesHandler.h index 07645bfd7d..dccc7ac50e 100644 --- a/dom/base/WindowNamedPropertiesHandler.h +++ b/dom/base/WindowNamedPropertiesHandler.h @@ -28,7 +28,8 @@ public: virtual bool defineProperty(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, - JS::MutableHandle aDesc) const override; + JS::MutableHandle aDesc, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropNames(JSContext* aCx, JS::Handle aProxy, unsigned flags, JS::AutoIdVector& aProps) const override; diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index cb7fe86157..d30ec7dce9 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1170,13 +1170,7 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto) NS_ENSURE_SUCCESS(rv, rv); if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() && !JS_DefineUCProperty(cx, global, mData->mNameUTF16, - NS_strlen(mData->mNameUTF16), - desc.value(), - // Descriptors never store JSNatives for accessors: - // they have either JSFunctions or JSPropertyOps. - desc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter()))) { + NS_strlen(mData->mNameUTF16), desc)) { return NS_ERROR_UNEXPECTED; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 8c075c7ce1..863f4964ba 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -622,8 +622,8 @@ public: virtual bool defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, - JS::MutableHandle desc) - const override; + JS::MutableHandle desc, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext *cx, JS::Handle proxy, JS::AutoIdVector &props) const override; @@ -646,8 +646,8 @@ public: virtual bool set(JSContext *cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, - bool strict, - JS::MutableHandle vp) const override; + JS::MutableHandle vp, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; // SpiderMonkey extensions virtual bool getPropertyDescriptor(JSContext* cx, @@ -783,8 +783,8 @@ bool nsOuterWindowProxy::defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, - JS::MutableHandle desc) - const + JS::MutableHandle desc, + JS::ObjectOpResult &result) const { int32_t index = GetArrayIndexFromId(cx, id); if (IsArrayIndex(index)) { @@ -792,7 +792,7 @@ nsOuterWindowProxy::defineProperty(JSContext* cx, // since we have no indexed setter or indexed creator. That means // throwing in strict mode (FIXME: Bug 828137), doing nothing in // non-strict mode. - return true; + return result.succeed(); } // For now, allow chrome code to define non-configurable properties @@ -803,7 +803,7 @@ nsOuterWindowProxy::defineProperty(JSContext* cx, return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW); } - return js::Wrapper::defineProperty(cx, proxy, id, desc); + return js::Wrapper::defineProperty(cx, proxy, id, desc, result); } bool @@ -917,19 +917,17 @@ bool nsOuterWindowProxy::set(JSContext *cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, - bool strict, - JS::MutableHandle vp) const + JS::MutableHandle vp, + JS::ObjectOpResult &result) const { int32_t index = GetArrayIndexFromId(cx, id); if (IsArrayIndex(index)) { // Reject (which means throw if and only if strict) the set. - if (strict) { - // XXXbz This needs to throw, but see bug 828137. - } - return true; + // XXX See bug 828137. + return result.succeed(); } - return js::Wrapper::set(cx, proxy, receiver, id, strict, vp); + return js::Wrapper::set(cx, proxy, receiver, id, vp, result); } bool diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 0576e36e39..337681b3f5 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1469,13 +1469,14 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle wrapper, bool XrayDefineProperty(JSContext* cx, JS::Handle wrapper, JS::Handle obj, JS::Handle id, - JS::MutableHandle desc, bool* defined) + JS::MutableHandle desc, + JS::ObjectOpResult &result, bool *defined) { if (!js::IsProxy(obj)) - return true; + return true; const DOMProxyHandler* handler = GetDOMProxyHandler(obj); - return handler->defineProperty(cx, wrapper, id, desc, defined); + return handler->defineProperty(cx, wrapper, id, desc, result, defined); } template diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index f735474f11..9166702e61 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -2450,12 +2450,17 @@ XrayResolveOwnProperty(JSContext* cx, JS::Handle wrapper, * wrapper is the Xray JS object. * obj is the target object of the Xray, a binding's instance object or a * interface or interface prototype object. + * id and desc are the parameters for the property to be defined. + * result is the out-parameter indicating success (read it only if + * this returns true and also sets *defined to true). * defined will be set to true if a property was set as a result of this call. */ bool XrayDefineProperty(JSContext* cx, JS::Handle wrapper, JS::Handle obj, JS::Handle id, - JS::MutableHandle desc, bool* defined); + JS::MutableHandle desc, + JS::ObjectOpResult &result, + bool *defined); /** * Add to props the property keys of all indexed or named properties of obj and diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 8b6a916fed..cb630d97ff 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -7623,10 +7623,7 @@ class CGResolveHook(CGAbstractBindingMethod): // has already defined it on the object. Don't try to also // define it. if (!desc.value().isUndefined() && - !JS_DefinePropertyById(cx, obj, id, desc.value(), - desc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter()))) { + !JS_DefinePropertyById(cx, obj, id, desc)) { return false; } *resolvedp = true; @@ -9621,10 +9618,7 @@ class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod): // try to also define it. if (objDesc.object() && !objDesc.value().isUndefined() && - !JS_DefinePropertyById(cx, obj, id, objDesc.value(), - objDesc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(objDesc.getter()), - JS_PROPERTYOP_SETTER(objDesc.setter()))) { + !JS_DefinePropertyById(cx, obj, id, objDesc)) { return false; } } @@ -10187,10 +10181,13 @@ class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod): class CGDOMJSProxyHandler_defineProperty(ClassMethod): def __init__(self, descriptor): + # The usual convention is to name the ObjectOpResult out-parameter + # `result`, but that name is a bit overloaded around here. args = [Argument('JSContext*', 'cx'), Argument('JS::Handle', 'proxy'), Argument('JS::Handle', 'id'), Argument('JS::MutableHandle', 'desc'), + Argument('JS::ObjectOpResult&', 'opresult'), Argument('bool*', 'defined')] ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True, const=True) self.descriptor = descriptor @@ -10208,7 +10205,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod): if (IsArrayIndex(index)) { *defined = true; $*{callSetter} - return true; + return opresult.succeed(); } """, callSetter=CGProxyIndexedSetter(self.descriptor).define()) @@ -10221,7 +10218,9 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod): set += fill( """ if (IsArrayIndex(GetArrayIndexFromId(cx, id))) { - return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}"); + return js::IsInNonStrictPropertySet(cx) + ? opresult.succeed() + : ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}"); } """, name=self.descriptor.name) @@ -10236,8 +10235,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod): "}\n" "if (hasUnforgeable) {\n" " *defined = true;\n" - " bool unused;\n" - " return js::DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n" + " return js::DefineOwnProperty(cx, ${holder}, id, desc, opresult);\n" "}\n") set += CallOnUnforgeableHolder(self.descriptor, defineOnUnforgeable, @@ -10254,7 +10252,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod): *defined = true; $*{callSetter} - return true; + return opresult.succeed(); """, callSetter=CGProxyNamedSetter(self.descriptor).define()) else: @@ -10270,7 +10268,9 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod): $*{presenceChecker} if (found) { - return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}"); + return js::IsInNonStrictPropertySet(cx) + ? opresult.succeed() + : ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}"); } """, presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define(), diff --git a/dom/bindings/DOMJSProxyHandler.cpp b/dom/bindings/DOMJSProxyHandler.cpp index 0ada91f282..246b4aa65b 100644 --- a/dom/bindings/DOMJSProxyHandler.cpp +++ b/dom/bindings/DOMJSProxyHandler.cpp @@ -195,18 +195,15 @@ BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx, bool DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, - MutableHandle desc, bool* defined) const + MutableHandle desc, + JS::ObjectOpResult &result, bool *defined) const { if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) { - return JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT | - JSREPORT_STRICT_MODE_ERROR, - js::GetErrorMessage, nullptr, - JSMSG_GETTER_ONLY); + return result.failGetterOnly(); } if (xpc::WrapperFactory::IsXrayWrapper(proxy)) { - return true; + return result.succeed(); } JSObject* expando = EnsureExpandoObject(cx, proxy); @@ -214,13 +211,16 @@ DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle proxy, JS:: return false; } - bool dummy; - return js::DefineOwnProperty(cx, expando, id, desc, &dummy); + if (!js::DefineOwnProperty(cx, expando, id, desc, result)) { + return false; + } + *defined = true; + return true; } bool DOMProxyHandler::set(JSContext *cx, Handle proxy, Handle receiver, - Handle id, bool strict, MutableHandle vp) const + Handle id, MutableHandle vp, ObjectOpResult &result) const { MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), "Should not have a XrayWrapper here"); @@ -229,7 +229,7 @@ DOMProxyHandler::set(JSContext *cx, Handle proxy, Handle r return false; } if (done) { - return true; + return result.succeed(); } // Make sure to ignore our named properties when checking for own @@ -254,7 +254,7 @@ DOMProxyHandler::set(JSContext *cx, Handle proxy, Handle r } return js::SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, - &desc, descIsOwn, strict, vp); + &desc, descIsOwn, vp, result); } bool diff --git a/dom/bindings/DOMJSProxyHandler.h b/dom/bindings/DOMJSProxyHandler.h index 42fb9a453f..547cf33000 100644 --- a/dom/bindings/DOMJSProxyHandler.h +++ b/dom/bindings/DOMJSProxyHandler.h @@ -105,25 +105,26 @@ public: {} bool defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, - JS::MutableHandle desc) const override + JS::MutableHandle desc, + JS::ObjectOpResult &result) const MOZ_OVERRIDE { bool unused; - return defineProperty(cx, proxy, id, desc, &unused); + return defineProperty(cx, proxy, id, desc, result, &unused); } virtual bool defineProperty(JSContext* cx, JS::Handle proxy, JS::Handle id, - JS::MutableHandle desc, bool* defined) - const; + JS::MutableHandle desc, + JS::ObjectOpResult &result, bool *defined) const; bool delete_(JSContext* cx, JS::Handle proxy, - JS::Handle id, bool* bp) const override; + JS::Handle id, bool* bp) const MOZ_OVERRIDE; bool preventExtensions(JSContext *cx, JS::Handle proxy, - bool *succeeded) const override; + bool *succeeded) const MOZ_OVERRIDE; bool isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) const override; bool has(JSContext* cx, JS::Handle proxy, JS::Handle id, - bool* bp) const override; + bool* bp) const MOZ_OVERRIDE; bool set(JSContext *cx, JS::Handle proxy, JS::Handle receiver, - JS::Handle id, bool strict, JS::MutableHandle vp) - const override; + JS::Handle id, JS::MutableHandle vp, JS::ObjectOpResult &result) + const MOZ_OVERRIDE; /* * If assigning to proxy[id] hits a named setter with OverrideBuiltins or diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index d9f23af72e..9841de0d08 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -169,8 +169,8 @@ static bool NPObjWrapper_DelProperty(JSContext *cx, JS::Handle obj, JS::Handle id, bool *succeeded); static bool -NPObjWrapper_SetProperty(JSContext *cx, JS::Handle obj, JS::Handle id, bool strict, - JS::MutableHandle vp); +NPObjWrapper_SetProperty(JSContext *cx, JS::Handle obj, JS::Handle id, + JS::MutableHandle vp, JS::ObjectOpResult &result); static bool NPObjWrapper_GetProperty(JSContext *cx, JS::Handle obj, JS::Handle id, JS::MutableHandle vp); @@ -1326,8 +1326,8 @@ NPObjWrapper_DelProperty(JSContext *cx, JS::Handle obj, JS::Handle obj, JS::Handle id, bool strict, - JS::MutableHandle vp) +NPObjWrapper_SetProperty(JSContext *cx, JS::Handle obj, JS::Handle id, + JS::MutableHandle vp, JS::ObjectOpResult &result) { NPObject *npobj = GetNPObject(cx, obj); @@ -1382,7 +1382,7 @@ NPObjWrapper_SetProperty(JSContext *cx, JS::Handle obj, JS::Handle desc) const override; virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const override; + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override; @@ -101,7 +102,8 @@ class CPOWProxyHandler : public BaseProxyHandler virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const override; virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver, - JS::HandleId id, bool strict, JS::MutableHandleValue vp) const override; + JS::HandleId id, JS::MutableHandleValue vp, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; @@ -202,14 +204,16 @@ WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, Handle bool CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const + MutableHandle desc, + ObjectOpResult &result) const { - FORWARD(defineProperty, (cx, proxy, id, desc)); + FORWARD(defineProperty, (cx, proxy, id, desc, result)); } bool WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) + MutableHandle desc, + ObjectOpResult &result) { ObjectId objId = idOf(proxy); @@ -227,7 +231,7 @@ WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, LOG_STACK(); - return ok(cx, status); + return ok(cx, status, result); } bool @@ -431,14 +435,14 @@ WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleObject receiver, bool CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver, - JS::HandleId id, bool strict, JS::MutableHandleValue vp) const + JS::HandleId id, JS::MutableHandleValue vp, JS::ObjectOpResult &result) const { - FORWARD(set, (cx, proxy, receiver, id, strict, vp)); + FORWARD(set, (cx, proxy, receiver, id, vp, result)); } bool WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver, - JS::HandleId id, bool strict, JS::MutableHandleValue vp) + JS::HandleId id, JS::MutableHandleValue vp, JS::ObjectOpResult &result) { ObjectId objId = idOf(proxy); @@ -455,16 +459,16 @@ WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiv return false; ReturnStatus status; - JSVariant result; - if (!SendSet(objId, receiverVar, idVar, strict, val, &status, &result)) + JSVariant resultValue; + if (!SendSet(objId, receiverVar, idVar, val, &status, &resultValue)) return ipcfail(cx); LOG_STACK(); - if (!ok(cx, status)) + if (!ok(cx, status, result)) return false; - return fromVariant(cx, result, vp); + return fromVariant(cx, resultValue, vp); } bool @@ -934,6 +938,16 @@ WrapperOwner::ok(JSContext* cx, const ReturnStatus& status) return false; } +bool +WrapperOwner::ok(JSContext *cx, const ReturnStatus &status, ObjectOpResult &result) +{ + if (status.type() == ReturnStatus::TReturnObjectOpResult) + return result.fail(status.get_ReturnObjectOpResult().code()); + if (!ok(cx, status)) + return false; + return result.succeed(); +} + static RemoteObject MakeRemoteObject(JSContext* cx, ObjectId id, HandleObject obj) { diff --git a/js/ipc/WrapperOwner.h b/js/ipc/WrapperOwner.h index d73c0ff9e5..2e131bf022 100644 --- a/js/ipc/WrapperOwner.h +++ b/js/ipc/WrapperOwner.h @@ -32,7 +32,8 @@ class WrapperOwner : public virtual JavaScriptShared bool getOwnPropertyDescriptor(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle desc); bool defineProperty(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, - JS::MutableHandle desc); + JS::MutableHandle desc, + JS::ObjectOpResult &result); bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy, JS::AutoIdVector& props); bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp); bool preventExtensions(JSContext* cx, JS::HandleObject proxy, bool* succeeded); @@ -41,7 +42,7 @@ class WrapperOwner : public virtual JavaScriptShared bool get(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver, JS::HandleId id, JS::MutableHandleValue vp); bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver, - JS::HandleId id, bool strict, JS::MutableHandleValue vp); + JS::HandleId id, JS::MutableHandleValue vp, JS::ObjectOpResult &result); bool callOrConstruct(JSContext* cx, JS::HandleObject proxy, const JS::CallArgs& args, bool construct); @@ -93,6 +94,10 @@ class WrapperOwner : public virtual JavaScriptShared bool ipcfail(JSContext* cx); // Check whether a return status is okay, and if not, propagate its error. + // + // If 'status' might be a ReturnObjectOpResult, which is only possible for + // a subset of the operations below, 'result' must be passed. + bool ok(JSContext *cx, const ReturnStatus &status, JS::ObjectOpResult &result); bool ok(JSContext* cx, const ReturnStatus& status); bool inactive_; @@ -123,8 +128,8 @@ class WrapperOwner : public virtual JavaScriptShared const JSIDVariant& id, ReturnStatus* rs, JSVariant* result) = 0; virtual bool SendSet(const ObjectId& objId, const ObjectVariant& receiverVar, - const JSIDVariant& id, const bool& strict, - const JSVariant& value, ReturnStatus* rs, JSVariant* result) = 0; + const JSIDVariant &id, const JSVariant &value, + ReturnStatus *rs, JSVariant *result) = 0; virtual bool SendIsExtensible(const ObjectId& objId, ReturnStatus* rs, bool* result) = 0; diff --git a/js/public/Class.h b/js/public/Class.h index 37b9e47fea..d4239a375d 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -44,6 +44,127 @@ namespace JS { class AutoIdVector; +/* + * Per ES6, the [[DefineOwnProperty]] internal method has three different + * possible outcomes: + * + * - It can throw an exception (which we indicate by returning false). + * + * - It can return true, indicating unvarnished success. + * + * - It can return false, indicating "strict failure". The property could + * not be defined. It's an error, but no exception was thrown. + * + * It's not just [[DefineOwnProperty]]: all the mutating internal methods have + * the same three outcomes. (The other affected internal methods are [[Set]], + * [[Delete]], [[SetPrototypeOf]], and [[PreventExtensions]].) + * + * If you think this design is awful, you're not alone. But as it's the + * standard, we must represent these boolean "success" values somehow. + * ObjectOpSuccess is the class for this. It's like a bool, but when it's false + * it also stores an error code. + * + * Typical usage: + * + * ObjectOpResult result; + * if (!DefineProperty(cx, obj, id, ..., result)) + * return false; + * if (!result) + * return result.reportError(cx, obj, id); + * + * Users don't have to call `result.report()`; another possible ending is: + * + * argv.rval().setBoolean(bool(result)); + * return true; + */ +class ObjectOpResult +{ + private: + uint32_t code_; + + public: + enum { OkCode = 0, Uninitialized = 0xffffffff }; + + ObjectOpResult() : code_(Uninitialized) {} + + /* Return true if fail() was not called. */ + bool ok() const { + MOZ_ASSERT(code_ != Uninitialized); + return code_ == OkCode; + } + + explicit operator bool() const { return ok(); } + + /* Set this ObjectOpResult to true and return true. */ + bool succeed() { + code_ = OkCode; + return true; + } + + /* + * Set this ObjectOpResult to false with an error code. + * + * Always returns true, as a convenience. Typical usage will be: + * + * if (funny condition) + * return result.fail(JSMSG_CANT_DO_THE_THINGS); + * + * The true return value indicates that no exception is pending, and it + * would be OK to ignore the failure and continue. + */ + bool fail(uint32_t msg) { + MOZ_ASSERT(msg != OkCode); + code_ = msg; + return true; + } + + JS_PUBLIC_API(bool) failCantRedefineProp(); + JS_PUBLIC_API(bool) failReadOnly(); + JS_PUBLIC_API(bool) failGetterOnly(); + JS_PUBLIC_API(bool) failCantSetInterposed(); + + uint32_t failureCode() const { + MOZ_ASSERT(!ok()); + return code_; + } + + /* + * Report an error or warning if necessary; return true to proceed and + * false if an error was reported. Call this when failure should cause + * a warning if extraWarnings are enabled. + * + * The precise rules are like this: + * + * - If ok(), then we succeeded. Do nothing and return true. + * - Otherwise, if |strict| is true, or if cx has both extraWarnings and + * werrorOption enabled, throw a TypeError and return false. + * - Otherwise, if cx has extraWarnings enabled, emit a warning and + * return true. + * - Otherwise, do nothing and return true. + */ + bool checkStrictErrorOrWarning(JSContext *cx, HandleObject obj, HandleId id, bool strict) { + if (ok()) + return true; + return reportStrictErrorOrWarning(cx, obj, id, strict); + } + + /* + * Convenience method. Return true if ok() or if strict is false; otherwise + * throw a TypeError and return false. + */ + bool checkStrict(JSContext *cx, HandleObject obj, HandleId id) { + return checkStrictErrorOrWarning(cx, obj, id, true); + } + + /* Throw a TypeError. Call this only if !ok(). */ + bool reportError(JSContext *cx, HandleObject obj, HandleId id) { + return reportStrictErrorOrWarning(cx, obj, id, true); + } + + /* Helper function for checkStrictErrorOrWarning's slow path. */ + JS_PUBLIC_API(bool) reportStrictErrorOrWarning(JSContext *cx, HandleObject obj, HandleId id, bool strict); +}; + } // JSClass operation signatures. @@ -62,7 +183,7 @@ typedef bool // set. typedef bool (* JSStrictPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, - bool strict, JS::MutableHandleValue vp); + JS::MutableHandleValue vp, JS::ObjectOpResult &result); // Delete a property named by id in obj. // @@ -166,7 +287,8 @@ typedef bool JS::MutableHandleObject objp, JS::MutableHandle propp); typedef bool (* DefinePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs, + JS::ObjectOpResult &result); typedef bool (* HasPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp); typedef bool @@ -174,7 +296,7 @@ typedef bool JS::MutableHandleValue vp); typedef bool (* SetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id, - JS::MutableHandleValue vp, bool strict); + JS::MutableHandleValue vp, JS::ObjectOpResult &result); typedef bool (* GetOwnPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandle desc); diff --git a/js/public/Proxy.h b/js/public/Proxy.h index 2279ce2eb9..4f37aba5bc 100644 --- a/js/public/Proxy.h +++ b/js/public/Proxy.h @@ -26,6 +26,7 @@ using JS::MutableHandle; using JS::MutableHandleObject; using JS::MutableHandleValue; using JS::NativeImpl; +using JS::ObjectOpResult; using JS::PrivateValue; using JS::Value; @@ -251,7 +252,8 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc) const = 0; virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const = 0; + MutableHandle desc, + ObjectOpResult &result) const = 0; virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const = 0; virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const = 0; @@ -289,7 +291,7 @@ class JS_FRIEND_API(BaseProxyHandler) virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const; virtual bool set(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const; + HandleId id, MutableHandleValue vp, ObjectOpResult &result) const; /* * [[Call]] and [[Construct]] are standard internal methods but according @@ -368,7 +370,8 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const override; + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, @@ -388,7 +391,8 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const override; virtual bool set(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const override; + HandleId id, MutableHandleValue vp, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; diff --git a/js/src/NamespaceImports.h b/js/src/NamespaceImports.h index 7832e43098..a78d42d00e 100644 --- a/js/src/NamespaceImports.h +++ b/js/src/NamespaceImports.h @@ -45,6 +45,7 @@ class MOZ_STACK_CLASS SourceBufferHolder; class HandleValueArray; +class ObjectOpResult; } // Do the importing. @@ -143,6 +144,8 @@ using JS::FalseHandleValue; using JS::HandleValueArray; +using JS::ObjectOpResult; + using JS::Zone; } /* namespace js */ diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 6e5d89322b..eba33751ba 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -833,8 +833,7 @@ js::obj_defineProperty(JSContext* cx, unsigned argc, Value* vp) if (!desc.initialize(cx, args.get(2))) return false; - bool ignored; - if (!StandardDefineProperty(cx, obj, id, desc, true, &ignored)) + if (!StandardDefineProperty(cx, obj, id, desc)) return false; args.rval().setObject(*obj); diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 0c2fbd7204..6997895ab6 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -1755,7 +1755,8 @@ ReportPropertyError(JSContext* cx, bool TypedObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) + PropertyOp getter, StrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) { Rooted typedObj(cx, &obj->as()); return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj); @@ -1907,7 +1908,7 @@ TypedObject::obj_getArrayElement(JSContext* cx, bool TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { Rooted typedObj(cx, &obj->as()); @@ -1926,13 +1927,13 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleObject recei nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH); return false; } - return SetNonWritableProperty(cx, id, strict); + return result.failReadOnly(); } uint32_t index; if (IdIsIndex(id, &index)) { if (obj != receiver) - return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false); + return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result); if (index >= uint32_t(typedObj->length())) { JS_ReportErrorNumber(cx, GetErrorMessage, @@ -1943,7 +1944,9 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleObject recei Rooted elementType(cx); elementType = &typedObj->typeDescr().as().elementType(); size_t offset = elementType->size() * index; - return ConvertAndCopyTo(cx, elementType, typedObj, offset, NullPtr(), vp); + if (!ConvertAndCopyTo(cx, elementType, typedObj, offset, NullPtr(), vp)) + return false; + return result.succeed(); } break; } @@ -1956,16 +1959,18 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleObject recei break; if (obj != receiver) - return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false); + return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result); size_t offset = descr->fieldOffset(fieldIndex); Rooted fieldType(cx, &descr->fieldDescr(fieldIndex)); RootedAtom fieldName(cx, &descr->fieldName(fieldIndex)); - return ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, vp); + if (!ConvertAndCopyTo(cx, fieldType, typedObj, offset, fieldName, vp)) + return false; + return result.succeed(); } } - return SetPropertyOnProto(cx, obj, receiver, id, vp, strict); + return SetPropertyOnProto(cx, obj, receiver, id, vp, result); } bool diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 32ec2f5fde..aff3caff7c 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -528,7 +528,8 @@ class TypedObject : public JSObject MutableHandleObject objp, MutableHandleShape propp); static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs); + PropertyOp getter, StrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result); static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); @@ -539,7 +540,7 @@ class TypedObject : public JSObject uint32_t index, MutableHandleValue vp); static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp, bool strict); + HandleId id, MutableHandleValue vp, ObjectOpResult &result); static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc); diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index be66884728..693d2ad49c 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -287,7 +287,8 @@ namespace ArrayType { bool LengthGetter(JSContext* cx, const JS::CallArgs& args); static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); - static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp); + static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp, + ObjectOpResult &result); static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp); } @@ -300,9 +301,9 @@ namespace StructType { bool FieldsArrayGetter(JSContext* cx, const JS::CallArgs& args); static bool FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, - MutableHandleValue vp); - static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, - MutableHandleValue vp); + MutableHandleValue vp); + static bool FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, + MutableHandleValue vp, ObjectOpResult &result); static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp); static bool Define(JSContext* cx, unsigned argc, jsval* vp); } @@ -4628,7 +4629,8 @@ ArrayType::Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandle } bool -ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) +ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp, + ObjectOpResult &result) { // This should never happen, but we'll check to be safe. if (!CData::IsCData(obj)) { @@ -4640,7 +4642,7 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, // CData, regardless of CType.) JSObject* typeObj = CData::GetCType(obj); if (CType::GetTypeCode(typeObj) != TYPE_array) - return true; + return result.succeed(); // Convert the index to a size_t and bounds-check it. size_t index; @@ -4650,7 +4652,7 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, if (!ok && JSID_IS_STRING(idval) && !StringToInteger(cx, JSID_TO_STRING(idval), &dummy)) { // String either isn't a number, or doesn't fit in size_t. // Chances are it's a regular property lookup, so return. - return true; + return result.succeed(); } if (!ok || index >= length) { JS_ReportError(cx, "invalid index"); @@ -4660,7 +4662,9 @@ ArrayType::Setter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, JSObject* baseType = GetBaseType(typeObj); size_t elementSize = CType::GetSize(baseType); char* data = static_cast(CData::GetData(obj)) + elementSize * index; - return ImplicitConvert(cx, vp, baseType, data, false, nullptr); + if (!ImplicitConvert(cx, vp, baseType, data, false, nullptr)) + return false; + return result.succeed(); } bool @@ -5293,7 +5297,8 @@ StructType::FieldGetter(JSContext* cx, HandleObject obj, HandleId idval, Mutable } bool -StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool strict, MutableHandleValue vp) +StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp, + ObjectOpResult &result) { if (!CData::IsCData(obj)) { JS_ReportError(cx, "not a CData"); @@ -5311,7 +5316,9 @@ StructType::FieldSetter(JSContext* cx, HandleObject obj, HandleId idval, bool st return false; char* data = static_cast(CData::GetData(obj)) + field->mOffset; - return ImplicitConvert(cx, vp, field->mType, data, false, nullptr); + if (!ImplicitConvert(cx, vp, field->mType, data, false, nullptr)) + return false; + return result.succeed(); } bool diff --git a/js/src/jit-test/tests/basic/expression-autopsy.js b/js/src/jit-test/tests/basic/expression-autopsy.js index cddcf95d89..061503711f 100644 --- a/js/src/jit-test/tests/basic/expression-autopsy.js +++ b/js/src/jit-test/tests/basic/expression-autopsy.js @@ -99,7 +99,7 @@ check("o[- (o)]"); // A few one off tests check_one("6", (function () { 6() }), " is not a function"); -check_one("Array.prototype.reverse.call(...)", (function () { Array.prototype.reverse.call('123'); }), " is read-only"); +check_one("0", (function () { Array.prototype.reverse.call('123'); }), " is read-only"); check_one(`(intermediate value)[Symbol.iterator](...).next(...).value`, function () { var [{ x }] = [null, {}]; }, " is null"); check_one(`(intermediate value)[Symbol.iterator](...).next(...).value`, diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index cd61ffb13e..7cde5360c3 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -8382,7 +8382,7 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_ MOZ_ASSERT(op == JSOP_SETPROP || op == JSOP_STRICTSETPROP); RootedValue v(cx, rhs); - if (!SetProperty(cx, obj, obj, id, &v, op == JSOP_STRICTSETPROP)) + if (!PutProperty(cx, obj, id, &v, op == JSOP_STRICTSETPROP)) return false; } diff --git a/js/src/jit/IonCaches.cpp b/js/src/jit/IonCaches.cpp index 98cebd1f25..c549233df7 100644 --- a/js/src/jit/IonCaches.cpp +++ b/js/src/jit/IonCaches.cpp @@ -1460,6 +1460,13 @@ GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript return linkAndAttachStub(cx, masm, attacher, ion, "typed array length"); } +static void +PushObjectOpResult(MacroAssembler &masm, uint32_t value = ObjectOpResult::Uninitialized) +{ + static_assert(sizeof(ObjectOpResult) == sizeof(int32_t), + "ObjectOpResult size must match size reserved by masm.Push() here"); + masm.Push(Imm32(value)); +} static bool EmitCallProxyGet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, @@ -1504,6 +1511,9 @@ EmitCallProxyGet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& at masm.Push(object); masm.movePtr(StackPointer, argProxyReg); + // Unused space, to keep the same stack layout as Proxy::set frames. + PushObjectOpResult(masm, 0); + masm.loadJSContext(argJSContextReg); if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic)) @@ -2118,6 +2128,54 @@ IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleSh return true; } +static bool +ReportStrictErrorOrWarning(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool strict, + JS::ObjectOpResult &result) +{ + return result.reportStrictErrorOrWarning(cx, obj, id, strict); +} + +template +void +EmitObjectOpResultCheck(MacroAssembler &masm, Label *failure, bool strict, + Register scratchReg, + Register argJSContextReg, + Register argObjReg, + Register argIdReg, + Register argStrictReg, + Register argResultReg) +{ + // if (!result) { + Label noStrictError; + masm.branch32(Assembler::Equal, + Address(StackPointer, + FrameLayout::offsetOfObjectOpResult()), + Imm32(ObjectOpResult::OkCode), + &noStrictError); + + // if (!ReportStrictErrorOrWarning(cx, obj, id, strict, &result)) + // goto failure; + masm.loadJSContext(argJSContextReg); + masm.computeEffectiveAddress( + Address(StackPointer, FrameLayout::offsetOfId()), + argIdReg); + masm.move32(Imm32(strict), argStrictReg); + masm.computeEffectiveAddress( + Address(StackPointer, FrameLayout::offsetOfObjectOpResult()), + argResultReg); + masm.setupUnalignedABICall(5, scratchReg); + masm.passABIArg(argJSContextReg); + masm.passABIArg(argObjReg); + masm.passABIArg(argIdReg); + masm.passABIArg(argStrictReg); + masm.passABIArg(argResultReg); + masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ReportStrictErrorOrWarning)); + masm.branchIfFalseBool(ReturnReg, failure); + + // } + masm.bind(&noStrictError); +} + static bool EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher, HandleId propId, RegisterSet liveRegs, Register object, @@ -2125,18 +2183,23 @@ EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& at { MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs); - // Remaining registers should be free, but we need to use |object| still - // so leave it alone. + // Remaining registers should be free, but we still need to use |object| so + // leave it alone. + // + // WARNING: We do not take() the register used by |value|, if any, so + // regSet is going to re-allocate it. Hence the emitted code must not touch + // any of the registers allocated from regSet until after the last use of + // |value|. (We can't afford to take it, either, because x86.) RegisterSet regSet(RegisterSet::All()); regSet.take(AnyRegister(object)); // Proxy::set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, - // bool strict, MutableHandleValue vp) + // MutableHandleValue vp, ObjectOpResult &result) Register argJSContextReg = regSet.takeGeneral(); Register argProxyReg = regSet.takeGeneral(); Register argIdReg = regSet.takeGeneral(); Register argVpReg = regSet.takeGeneral(); - Register argStrictReg = regSet.takeGeneral(); + Register argResultReg = regSet.takeGeneral(); Register scratch = regSet.takeGeneral(); @@ -2156,8 +2219,11 @@ EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& at masm.Push(object); masm.movePtr(StackPointer, argProxyReg); + // Allocate result out-param. + PushObjectOpResult(masm); + masm.movePtr(StackPointer, argResultReg); + masm.loadJSContext(argJSContextReg); - masm.move32(Imm32(strict? 1 : 0), argStrictReg); if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic)) return false; @@ -2169,13 +2235,19 @@ EmitCallProxySet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& at masm.passABIArg(argProxyReg); masm.passABIArg(argProxyReg); masm.passABIArg(argIdReg); - masm.passABIArg(argStrictReg); masm.passABIArg(argVpReg); + masm.passABIArg(argResultReg); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Proxy::set)); - // Test for failure. + // Test for error. masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); + // Test for strict failure. We emit the check even in non-strict mode in + // order to pick up the warning if extraWarnings is enabled. + EmitObjectOpResultCheck(masm, masm.exceptionLabel(), strict, + scratch, argJSContextReg, argProxyReg, + argIdReg, argVpReg, argResultReg); + // masm.leaveExitFrame & pop locals masm.adjustStack(IonOOLProxyExitFrameLayout::Size()); @@ -2386,24 +2458,28 @@ GenerateCallSetter(JSContext* cx, IonScript* ion, MacroAssembler& masm, Register argVpReg = regSet.takeGeneral(); Register argObjReg = regSet.takeGeneral(); Register argIdReg = regSet.takeGeneral(); - Register argStrictReg = regSet.takeGeneral(); - - attacher.pushStubCodePointer(masm); + Register argResultReg = regSet.takeGeneral(); StrictPropertyOp target = shape->setterOp(); MOZ_ASSERT(target); // JSStrictPropertyOp: bool fn(JSContext* cx, HandleObject obj, - // HandleId id, bool strict, MutableHandleValue vp); + // HandleId id, MutableHandleValue vp, ObjectOpResult &result); - // Push args on stack first so we can take pointers to make handles. + // First, allocate an ObjectOpResult on the stack. We push this before + // the stubCode pointer in order to match the layout of + // IonOOLSetterOpExitFrameLayout. + PushObjectOpResult(masm); + masm.movePtr(StackPointer, argResultReg); + + attacher.pushStubCodePointer(masm); + + // Push args on stack so we can take pointers to make handles. if (value.constant()) masm.Push(value.value()); else masm.Push(value.reg()); masm.movePtr(StackPointer, argVpReg); - masm.move32(Imm32(strict ? 1 : 0), argStrictReg); - // push canonical jsid from shape instead of propertyname. masm.Push(shape->propid(), argIdReg); masm.movePtr(StackPointer, argIdReg); @@ -2415,22 +2491,27 @@ GenerateCallSetter(JSContext* cx, IonScript* ion, MacroAssembler& masm, if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic)) return false; - masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayout::Token()); + masm.enterFakeExitFrame(IonOOLSetterOpExitFrameLayout::Token()); // Make the call. masm.setupUnalignedABICall(5, scratchReg); masm.passABIArg(argJSContextReg); masm.passABIArg(argObjReg); masm.passABIArg(argIdReg); - masm.passABIArg(argStrictReg); masm.passABIArg(argVpReg); + masm.passABIArg(argResultReg); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target)); - // Test for failure. + // Test for error. masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); + // Test for failure. + EmitObjectOpResultCheck(masm, failure, strict, scratchReg, + argJSContextReg, argObjReg, + argIdReg, argVpReg, argResultReg); + // masm.leaveExitFrame & pop locals. - masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size()); + masm.adjustStack(IonOOLSetterOpExitFrameLayout::Size()); } else { MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape)); diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index d6a6e64238..24aeaf6ae3 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -1316,7 +1316,7 @@ MarkJitExitFrame(JSTracer* trc, const JitFrameIterator& frame) } if (frame.isExitFrameLayout()) { - IonOOLNativeExitFrameLayout* oolnative = + IonOOLNativeExitFrameLayout *oolnative = frame.exitFrame()->as(); gc::MarkJitCodeRoot(trc, oolnative->stubCode(), "ion-ool-native-code"); gc::MarkValueRoot(trc, oolnative->vp(), "iol-ool-native-vp"); @@ -1325,9 +1325,16 @@ MarkJitExitFrame(JSTracer* trc, const JitFrameIterator& frame) return; } - if (frame.isExitFrameLayout()) { - IonOOLPropertyOpExitFrameLayout* oolgetter = - frame.exitFrame()->as(); + if (frame.isExitFrameLayout() || + frame.isExitFrameLayout()) + { + // A SetterOp frame is a different size, but that's the only relevant + // difference between the two. The fields that need marking are all in + // the common base class. + IonOOLPropertyOpExitFrameLayout *oolgetter = + frame.isExitFrameLayout() + ? frame.exitFrame()->as() + : frame.exitFrame()->as(); gc::MarkJitCodeRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code"); gc::MarkValueRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp"); gc::MarkIdRoot(trc, oolgetter->id(), "ion-ool-property-op-id"); @@ -1335,6 +1342,7 @@ MarkJitExitFrame(JSTracer* trc, const JitFrameIterator& frame) return; } + if (frame.isExitFrameLayout()) { IonOOLProxyExitFrameLayout* oolproxy = frame.exitFrame()->as(); gc::MarkJitCodeRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code"); diff --git a/js/src/jit/JitFrames.h b/js/src/jit/JitFrames.h index bfe289f57d..dc0fc061f8 100644 --- a/js/src/jit/JitFrames.h +++ b/js/src/jit/JitFrames.h @@ -494,7 +494,8 @@ enum ExitFrameTokenValues IonDOMMethodExitFrameLayoutToken = 0x3, IonOOLNativeExitFrameLayoutToken = 0x4, IonOOLPropertyOpExitFrameLayoutToken = 0x5, - IonOOLProxyExitFrameLayoutToken = 0x6, + IonOOLSetterOpExitFrameLayoutToken = 0x6, + IonOOLProxyExitFrameLayoutToken = 0x7, LazyLinkExitFrameLayoutToken = 0xFE, ExitFrameLayoutBareToken = 0xFF }; @@ -628,7 +629,7 @@ class IonOOLNativeExitFrameLayout class IonOOLPropertyOpExitFrameLayout { - protected: // only to silence a clang warning about unused private fields + protected: ExitFooterFrame footer_; ExitFrameLayout exit_; @@ -653,6 +654,10 @@ class IonOOLPropertyOpExitFrameLayout return sizeof(IonOOLPropertyOpExitFrameLayout); } + static size_t offsetOfId() { + return offsetof(IonOOLPropertyOpExitFrameLayout, id_); + } + static size_t offsetOfResult() { return offsetof(IonOOLPropertyOpExitFrameLayout, vp0_); } @@ -671,16 +676,36 @@ class IonOOLPropertyOpExitFrameLayout } }; +class IonOOLSetterOpExitFrameLayout : public IonOOLPropertyOpExitFrameLayout +{ + protected: // only to silence a clang warning about unused private fields + JS::ObjectOpResult result_; + + public: + static JitCode *Token() { return (JitCode *)IonOOLSetterOpExitFrameLayoutToken; } + + static size_t offsetOfObjectOpResult() { + return offsetof(IonOOLSetterOpExitFrameLayout, result_); + } + + static size_t Size() { + return sizeof(IonOOLSetterOpExitFrameLayout); + } +}; + // Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, // MutableHandleValue vp) // Proxy::set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, -// bool strict, MutableHandleValue vp) +// MutableHandleValue vp, ObjectOpResult &result) class IonOOLProxyExitFrameLayout { protected: // only to silence a clang warning about unused private fields ExitFooterFrame footer_; ExitFrameLayout exit_; + // result out-parameter (unused for Proxy::get) + JS::ObjectOpResult result_; + // The proxy object. JSObject* proxy_; @@ -709,6 +734,14 @@ class IonOOLProxyExitFrameLayout return offsetof(IonOOLProxyExitFrameLayout, vp0_); } + static size_t offsetOfId() { + return offsetof(IonOOLProxyExitFrameLayout, id_); + } + + static size_t offsetOfObjectOpResult() { + return offsetof(IonOOLProxyExitFrameLayout, result_); + } + inline JitCode** stubCode() { return &stubCode_; } diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 4964f01b0f..f8450bddbd 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -448,18 +448,24 @@ SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValu return true; } + ObjectOpResult result; if (MOZ_LIKELY(!obj->getOps()->setProperty)) { - return NativeSetProperty( - cx, obj.as(), obj.as(), id, - (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME || - op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) - ? Unqualified - : Qualified, - &v, - strict); + if (!NativeSetProperty( + cx, obj.as(), obj.as(), id, + (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME || + op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) + ? Unqualified + : Qualified, + &v, + result)) + { + return false; + } + } else { + if (!SetProperty(cx, obj, obj, id, &v, result)) + return false; } - - return SetProperty(cx, obj, obj, id, &v, strict); + return result.checkStrictErrorOrWarning(cx, obj, id, strict); } bool diff --git a/js/src/js.msg b/js/src/js.msg index 0f8e04d18d..838b0495a2 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -50,6 +50,7 @@ MSG_DEF(JSMSG_BAD_SORT_ARG, 0, JSEXN_TYPEERR, "invalid Array.prototyp MSG_DEF(JSMSG_CANT_WATCH, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") MSG_DEF(JSMSG_READ_ONLY, 1, JSEXN_TYPEERR, "{0} is read-only") MSG_DEF(JSMSG_CANT_DELETE, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted") +MSG_DEF(JSMSG_CANT_TRUNCATE_ARRAY, 0, JSEXN_TYPEERR, "can't delete non-configurable array element") MSG_DEF(JSMSG_NOT_FUNCTION, 1, JSEXN_TYPEERR, "{0} is not a function") MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 1, JSEXN_TYPEERR, "{0} is not a constructor") MSG_DEF(JSMSG_CANT_CONVERT, 1, JSEXN_ERR, "can't convert {0} to an integer") @@ -83,7 +84,7 @@ MSG_DEF(JSMSG_MISSING_FUN_ARG, 2, JSEXN_TYPEERR, "missing argument {0} w MSG_DEF(JSMSG_NOT_NONNULL_OBJECT, 1, JSEXN_TYPEERR, "{0} is not a non-null object") MSG_DEF(JSMSG_INVALID_DESCRIPTOR, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified") MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 1, JSEXN_TYPEERR, "{0} is not extensible") -MSG_DEF(JSMSG_CANT_REDEFINE_PROP, 1, JSEXN_TYPEERR, "can't redefine non-configurable property '{0}'") +MSG_DEF(JSMSG_CANT_REDEFINE_PROP, 1, JSEXN_TYPEERR, "can't redefine non-configurable property {0}") MSG_DEF(JSMSG_CANT_APPEND_TO_ARRAY, 0, JSEXN_TYPEERR, "can't add elements past the end of an array if its length property is unwritable") MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't redefine array length") MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length") @@ -464,3 +465,6 @@ MSG_DEF(JSMSG_SYMBOL_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert symbol t MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY, 0, JSEXN_TYPEERR, "invalid array type for the operation") MSG_DEF(JSMSG_ATOMICS_TOO_LONG, 0, JSEXN_RANGEERR, "timeout value too large") MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 0, JSEXN_ERR, "waiting is not allowed on this thread") + +// XPConnect wrappers +MSG_DEF(JSMSG_CANT_SET_INTERPOSED, 1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'") diff --git a/js/src/jsapi-tests/testForwardSetProperty.cpp b/js/src/jsapi-tests/testForwardSetProperty.cpp index 1886b8659c..59ad29d0f7 100644 --- a/js/src/jsapi-tests/testForwardSetProperty.cpp +++ b/js/src/jsapi-tests/testForwardSetProperty.cpp @@ -45,11 +45,14 @@ BEGIN_TEST(testForwardSetProperty) // Non-strict setter - CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, v3, true, setval)); + ObjectOpResult result; + CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, v3, result)); + CHECK(result); EXEC("assertEq(foundValue, obj3, 'wrong receiver passed to setter');"); - CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, true, setval)); + CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, setval, result)); + CHECK(result); EXEC("assertEq(typeof foundValue === 'object', true, \n" " 'passing 42 as receiver to non-strict setter ' + \n" @@ -69,12 +72,13 @@ BEGIN_TEST(testForwardSetProperty) "obj1;", &v1); - CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, v3, true, setval)); + CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, v3, result)); + CHECK(result); EXEC("assertEq(foundValue, obj3, 'wrong receiver passed to strict setter');"); - CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, true, setval)); - + CHECK(JS_ForwardSetPropertyTo(cx, obj2, prop, setval, setval, result)); + CHECK(result); JS::RootedValue strictSetSupported(cx); EVAL("var strictSetSupported = false; \n" diff --git a/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp b/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp index 9d2769b786..0326129b2d 100644 --- a/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp +++ b/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp @@ -28,13 +28,13 @@ class CustomProxyHandler : public DirectProxyHandler { } bool set(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const override + HandleId id, MutableHandleValue vp, ObjectOpResult &result) const MOZ_OVERRIDE { Rooted desc(cx); if (!DirectProxyHandler::getPropertyDescriptor(cx, proxy, id, &desc)) return false; return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, - desc.object() == proxy, strict, vp); + desc.object() == proxy, vp, result); } private: diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 72dda9a305..0b9ae8cc4c 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -129,6 +129,70 @@ JS::CallArgs::requireAtLeast(JSContext* cx, const char* fnname, unsigned require return true; } +static bool +ErrorTakesIdArgument(unsigned msg) +{ + MOZ_ASSERT(msg < JSErr_Limit); + unsigned argCount = js_ErrorFormatString[msg].argCount; + MOZ_ASSERT(argCount <= 1); + return argCount == 1; +} + +JS_PUBLIC_API(bool) +JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext *cx, HandleObject obj, HandleId id, + bool strict) +{ + static_assert(unsigned(OkCode) == unsigned(JSMSG_NOT_AN_ERROR), + "unsigned value of OkCode must not be an error code"); + MOZ_ASSERT(code_ != Uninitialized); + MOZ_ASSERT(!ok()); + + unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT); + if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE) { + RootedValue val(cx, ObjectValue(*obj)); + return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val, + NullPtr(), nullptr, nullptr); + } + if (ErrorTakesIdArgument(code_)) { + RootedValue idv(cx, IdToValue(id)); + RootedString str(cx, ValueToSource(cx, idv)); + if (!str) + return false; + + JSAutoByteString propName(cx, str); + if (!propName) + return false; + + return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_, + propName.ptr()); + } + return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_); +} + +JS_PUBLIC_API(bool) +JS::ObjectOpResult::failCantRedefineProp() +{ + return fail(JSMSG_CANT_REDEFINE_PROP); +} + +JS_PUBLIC_API(bool) +JS::ObjectOpResult::failReadOnly() +{ + return fail(JSMSG_READ_ONLY); +} + +JS_PUBLIC_API(bool) +JS::ObjectOpResult::failGetterOnly() +{ + return fail(JSMSG_GETTER_ONLY); +} + +JS_PUBLIC_API(bool) +JS::ObjectOpResult::failCantSetInterposed() +{ + return fail(JSMSG_CANT_SET_INTERPOSED); +} + JS_PUBLIC_API(int64_t) JS_Now() { @@ -1787,9 +1851,10 @@ JS_PropertyStub(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue } JS_PUBLIC_API(bool) -JS_StrictPropertyStub(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) +JS_StrictPropertyStub(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, + ObjectOpResult &result) { - return true; + return result.succeed(); } JS_PUBLIC_API(JSObject*) @@ -2386,6 +2451,33 @@ JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, double value NativeOpWrapper(getter), NativeOpWrapper(setter), attrs, 0); } +static bool +DefinePropertyByDescriptor(JSContext *cx, HandleObject obj, HandleId id, + Handle desc, ObjectOpResult &result) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj, id, desc); + return DefineProperty(cx, obj, id, desc.value(), desc.getter(), desc.setter(), + desc.attributes(), result); +} + +JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, + Handle desc, ObjectOpResult &result) +{ + return DefinePropertyByDescriptor(cx, obj, id, desc, result); +} + +JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, + Handle desc) +{ + ObjectOpResult result; + return DefinePropertyByDescriptor(cx, obj, id, desc, result) && + result.checkStrict(cx, obj, id); +} + static bool DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value, unsigned attrs, Native getter, Native setter) @@ -2642,6 +2734,31 @@ JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_ getter, setter, attrs, 0); } +JS_PUBLIC_API(bool) +JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen, + Handle desc, + ObjectOpResult &result) +{ + JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); + if (!atom) + return false; + RootedId id(cx, AtomToId(atom)); + return DefinePropertyByDescriptor(cx, obj, id, desc, result); +} + +JS_PUBLIC_API(bool) +JS_DefineUCProperty(JSContext *cx, HandleObject obj, const char16_t *name, size_t namelen, + Handle desc) +{ + JSAtom *atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen)); + if (!atom) + return false; + RootedId id(cx, AtomToId(atom)); + ObjectOpResult result; + return DefinePropertyByDescriptor(cx, obj, id, desc, result) && + result.checkStrict(cx, obj, id); +} + JS_PUBLIC_API(JSObject*) JS_DefineObject(JSContext* cx, HandleObject obj, const char* name, const JSClass* jsclasp, unsigned attrs) @@ -2884,25 +3001,25 @@ JS_SetPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue v) CHECK_REQUEST(cx); assertSameCompartment(cx, obj, id); - return SetProperty(cx, obj, obj, id, &value, false); + ObjectOpResult ignored; + return SetProperty(cx, obj, obj, id, &value, ignored); } JS_PUBLIC_API(bool) -JS_ForwardSetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue onBehalfOf, - bool strict, JS::HandleValue v) +JS_ForwardSetPropertyTo(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, + HandleValue receiver, ObjectOpResult &result) { AssertHeapIsIdle(cx); CHECK_REQUEST(cx); - assertSameCompartment(cx, obj, id); - assertSameCompartment(cx, onBehalfOf); + assertSameCompartment(cx, obj, id, receiver); // XXX Bug 603201 will eliminate this ToObject. - RootedObject receiver(cx, ToObject(cx, onBehalfOf)); - if (!receiver) + RootedObject receiverObj(cx, ToObject(cx, receiver)); + if (!receiverObj) return false; RootedValue value(cx, v); - return SetProperty(cx, obj, receiver, id, &value, strict); + return SetProperty(cx, obj, receiverObj, id, &value, result); } static bool @@ -2912,7 +3029,8 @@ SetElement(JSContext* cx, HandleObject obj, uint32_t index, MutableHandleValue v CHECK_REQUEST(cx); assertSameCompartment(cx, obj, vp); - return SetElement(cx, obj, obj, index, vp, false); + ObjectOpResult ignored; + return SetElement(cx, obj, obj, index, vp, ignored); } JS_PUBLIC_API(bool) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 29c1af50d9..42921eef19 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2036,8 +2036,8 @@ JS_PropertyStub(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp); extern JS_PUBLIC_API(bool) -JS_StrictPropertyStub(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool strict, - JS::MutableHandleValue vp); +JS_StrictPropertyStub(JSContext *cx, JS::HandleObject obj, JS::HandleId id, + JS::MutableHandleValue vp, JS::ObjectOpResult &result); template struct JSConstScalarSpec { @@ -2555,92 +2555,7 @@ JS_PreventExtensions(JSContext* cx, JS::HandleObject obj, bool* succeeded); extern JS_PUBLIC_API(JSObject*) JS_New(JSContext* cx, JS::HandleObject ctor, const JS::HandleValueArray& args); -extern JS_PUBLIC_API(JSObject*) -JS_DefineObject(JSContext* cx, JS::HandleObject obj, const char* name, - const JSClass* clasp = nullptr, unsigned attrs = 0); - -extern JS_PUBLIC_API(bool) -JS_DefineConstDoubles(JSContext* cx, JS::HandleObject obj, const JSConstDoubleSpec* cds); - -extern JS_PUBLIC_API(bool) -JS_DefineConstIntegers(JSContext* cx, JS::HandleObject obj, const JSConstIntegerSpec* cis); - -extern JS_PUBLIC_API(bool) -JS_DefineProperties(JSContext* cx, JS::HandleObject obj, const JSPropertySpec* ps); - -extern JS_PUBLIC_API(bool) -JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleValue value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleObject value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleString value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, int32_t value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, uint32_t value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, double value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleString value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, int32_t value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, uint32_t value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, double value, - unsigned attrs, - JSNative getter = nullptr, JSNative setter = nullptr); - -extern JS_PUBLIC_API(bool) -JS_AlreadyHasOwnProperty(JSContext* cx, JS::HandleObject obj, const char* name, - bool* foundp); - -extern JS_PUBLIC_API(bool) -JS_AlreadyHasOwnPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, - bool* foundp); - -extern JS_PUBLIC_API(bool) -JS_HasProperty(JSContext* cx, JS::HandleObject obj, const char* name, bool* foundp); - -extern JS_PUBLIC_API(bool) -JS_HasPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp); +/*** Property descriptors ************************************************************************/ struct JSPropertyDescriptor { JSObject* obj; @@ -2829,6 +2744,108 @@ ParsePropertyDescriptorObject(JSContext* cx, } // namespace JS +/*** [[DefineOwnProperty]] and variations ********************************************************/ + +extern JS_PUBLIC_API(bool) +JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::HandleValue value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::HandleObject value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, JS::HandleString value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, int32_t value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, uint32_t value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefineProperty(JSContext *cx, JS::HandleObject obj, const char *name, double value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleString value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, int32_t value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, uint32_t value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, double value, + unsigned attrs, + JSNative getter = nullptr, JSNative setter = nullptr); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, + JS::Handle desc, + JS::ObjectOpResult &result); + +extern JS_PUBLIC_API(bool) +JS_DefinePropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, + JS::Handle desc); + +extern JS_PUBLIC_API(JSObject *) +JS_DefineObject(JSContext *cx, JS::HandleObject obj, const char *name, + const JSClass *clasp = nullptr, unsigned attrs = 0); + +extern JS_PUBLIC_API(bool) +JS_DefineConstDoubles(JSContext *cx, JS::HandleObject obj, const JSConstDoubleSpec *cds); + +extern JS_PUBLIC_API(bool) +JS_DefineConstIntegers(JSContext *cx, JS::HandleObject obj, const JSConstIntegerSpec *cis); + +extern JS_PUBLIC_API(bool) +JS_DefineProperties(JSContext *cx, JS::HandleObject obj, const JSPropertySpec *ps); + + +/* * */ + +extern JS_PUBLIC_API(bool) +JS_AlreadyHasOwnProperty(JSContext *cx, JS::HandleObject obj, const char *name, + bool *foundp); + +extern JS_PUBLIC_API(bool) +JS_AlreadyHasOwnPropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, + bool *foundp); + +extern JS_PUBLIC_API(bool) +JS_HasProperty(JSContext *cx, JS::HandleObject obj, const char *name, bool *foundp); + +extern JS_PUBLIC_API(bool) +JS_HasPropertyById(JSContext *cx, JS::HandleObject obj, JS::HandleId id, bool *foundp); + + extern JS_PUBLIC_API(bool) JS_GetOwnPropertyDescriptorById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandle desc); @@ -2867,8 +2884,8 @@ extern JS_PUBLIC_API(bool) JS_SetPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v); extern JS_PUBLIC_API(bool) -JS_ForwardSetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue onBehalfOf, - bool strict, JS::HandleValue vp); +JS_ForwardSetPropertyTo(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v, + JS::HandleValue receiver, JS::ObjectOpResult &result); extern JS_PUBLIC_API(bool) JS_DeleteProperty(JSContext* cx, JS::HandleObject obj, const char* name); @@ -2912,6 +2929,15 @@ JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, s double value, unsigned attrs, JSNative getter = nullptr, JSNative setter = nullptr); +extern JS_PUBLIC_API(bool) +JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen, + JS::Handle desc, + JS::ObjectOpResult &result); + +extern JS_PUBLIC_API(bool) +JS_DefineUCProperty(JSContext *cx, JS::HandleObject obj, const char16_t *name, size_t namelen, + JS::Handle desc); + extern JS_PUBLIC_API(bool) JS_AlreadyHasOwnUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen, bool* foundp); diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 542f2344c8..13a724aa97 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -363,7 +363,7 @@ SetArrayElement(JSContext* cx, HandleObject obj, double index, HandleValue v) return false; RootedValue tmp(cx, v); - return SetProperty(cx, obj, obj, id, &tmp, true); + return SetProperty(cx, obj, obj, id, &tmp); } /* @@ -433,7 +433,7 @@ bool js::SetLengthProperty(JSContext* cx, HandleObject obj, double length) { RootedValue v(cx, NumberValue(length)); - return SetProperty(cx, obj, obj, cx->names().length, &v, true); + return SetProperty(cx, obj, obj, cx->names().length, &v); } /* @@ -459,7 +459,8 @@ array_length_getter(JSContext* cx, HandleObject obj_, HandleId id, MutableHandle } static bool -array_length_setter(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) +array_length_setter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, + ObjectOpResult &result) { if (!obj->is()) { // This array .length property was found on the prototype @@ -467,13 +468,14 @@ array_length_setter(JSContext* cx, HandleObject obj, HandleId id, bool strict, M // we're here, do an impression of SetPropertyByDefining. const Class* clasp = obj->getClass(); return DefineProperty(cx, obj, cx->names().length, vp, - clasp->getProperty, clasp->setProperty, JSPROP_ENUMERATE); + clasp->getProperty, clasp->setProperty, JSPROP_ENUMERATE, result); } Rooted arr(cx, &obj->as()); MOZ_ASSERT(arr->lengthIsWritable(), "setter shouldn't be called if property is non-writable"); - return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, strict); + + return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, result); } struct ReverseIndexComparator @@ -506,7 +508,7 @@ js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen) /* ES6 20130308 draft 8.4.2.4 ArraySetLength */ bool js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, - unsigned attrs, HandleValue value, bool setterIsStrict) + unsigned attrs, HandleValue value, ObjectOpResult &result) { MOZ_ASSERT(id == NameToId(cx->names().length)); @@ -529,11 +531,8 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, // OrdinaryDefineOwnProperty in ES6, the default [[DefineOwnProperty]] in // ES5 -- but we reimplement all the conflict-detection bits ourselves here // so that we can use a customized length representation.) - if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE)) { - if (!setterIsStrict) - return true; - return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP); - } + if (!(attrs & JSPROP_PERMANENT) || (attrs & JSPROP_ENUMERATE)) + return result.fail(JSMSG_CANT_REDEFINE_PROP); /* Steps 6-7. */ bool lengthIsWritable = arr->lengthIsWritable(); @@ -550,14 +549,9 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, /* Steps 8-9 for arrays with non-writable length. */ if (!lengthIsWritable) { if (newLen == oldLen) - return true; + return result.succeed(); - if (setterIsStrict) { - return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, GetErrorMessage, nullptr, - JSMSG_CANT_REDEFINE_ARRAY_LENGTH); - } - - return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING); + return result.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH); } /* Step 8. */ @@ -738,53 +732,20 @@ js::ArraySetLength(JSContext* cx, Handle arr, HandleId id, } } - if (setterIsStrict && !succeeded) { - RootedId elementId(cx); - if (!IndexToId(cx, newLen - 1, &elementId)) - return false; - return arr->reportNotConfigurable(cx, elementId); - } + if (!succeeded) + return result.fail(JSMSG_CANT_TRUNCATE_ARRAY); - return true; + return result.succeed(); } bool -js::WouldDefinePastNonwritableLength(ExclusiveContext* cx, - HandleObject obj, uint32_t index, bool strict, - bool* definesPast) +js::WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index) { - if (!obj->is()) { - *definesPast = false; - return true; - } + if (!obj->is()) + return false; - Rooted arr(cx, &obj->as()); - uint32_t length = arr->length(); - if (index < length) { - *definesPast = false; - return true; - } - - if (arr->lengthIsWritable()) { - *definesPast = false; - return true; - } - - *definesPast = true; - - // Error in strict mode code or warn with strict option. - unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_STRICT | JSREPORT_WARNING); - if (!cx->isJSContext()) - return true; - - JSContext* ncx = cx->asJSContext(); - - if (!strict && !ncx->compartment()->options().extraWarnings(ncx)) - return true; - - // XXX include the index and maybe array length in the error message - return JS_ReportErrorFlagsAndNumber(ncx, flags, GetErrorMessage, nullptr, - JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); + ArrayObject *arr = &obj->as(); + return !arr->lengthIsWritable() && index >= arr->length(); } static bool @@ -1308,7 +1269,7 @@ InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start, uint32_t coun value = *vector++; indexv = DoubleValue(index); if (!ValueToId(cx, indexv, &id) || - !SetProperty(cx, obj, obj, id, &value, true)) + !SetProperty(cx, obj, obj, id, &value)) { return false; } @@ -3112,8 +3073,7 @@ array_of(JSContext* cx, unsigned argc, Value* vp) } // Steps 9-10. - RootedValue v(cx, NumberValue(args.length())); - if (!SetProperty(cx, obj, obj, cx->names().length, &v, true)) + if (!SetLengthProperty(cx, obj, args.length())) return false; // Step 11. diff --git a/js/src/jsarray.h b/js/src/jsarray.h index d84cfb48e6..332f8fc51d 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -101,9 +101,7 @@ NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::In * increase the length of the array. */ extern bool -WouldDefinePastNonwritableLength(ExclusiveContext* cx, - HandleObject obj, uint32_t index, bool strict, - bool* definesPast); +WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index); /* * Canonicalize |vp| to a uint32_t value potentially suitable for use as an diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 395587cfef..2555117bc5 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -132,6 +132,15 @@ class CompartmentChecker void check(InterpreterFrame* fp); void check(AbstractFramePtr frame); void check(SavedStacks* stacks); + + void check(Handle desc) { + check(desc.object()); + if (desc.hasGetterObject()) + check(desc.getterObject()); + if (desc.hasSetterObject()) + check(desc.setterObject()); + check(desc.value()); + } }; /* @@ -307,12 +316,12 @@ CallJSPropertyOp(JSContext* cx, PropertyOp op, HandleObject receiver, HandleId i MOZ_ALWAYS_INLINE bool CallJSPropertyOpSetter(JSContext* cx, StrictPropertyOp op, HandleObject obj, HandleId id, - bool strict, MutableHandleValue vp) + MutableHandleValue vp, ObjectOpResult &result) { JS_CHECK_RECURSION(cx, return false); assertSameCompartment(cx, obj, id, vp); - return op(cx, obj, id, strict, vp); + return op(cx, obj, id, vp, result); } static inline bool @@ -330,20 +339,22 @@ CallJSDeletePropertyOp(JSContext* cx, JSDeletePropertyOp op, HandleObject receiv inline bool CallSetter(JSContext* cx, HandleObject obj, HandleId id, StrictPropertyOp op, unsigned attrs, - bool strict, MutableHandleValue vp) + MutableHandleValue vp, ObjectOpResult &result) { if (attrs & JSPROP_SETTER) { RootedValue opv(cx, CastAsObjectJsval(op)); - return InvokeGetterOrSetter(cx, obj, opv, 1, vp.address(), vp); + if (!InvokeGetterOrSetter(cx, obj, opv, 1, vp.address(), vp)) + return false; + return result.succeed(); } if (attrs & JSPROP_GETTER) - return ReportGetterOnlyAssignment(cx, strict); + return result.fail(JSMSG_GETTER_ONLY); if (!op) - return true; + return result.succeed(); - return CallJSPropertyOpSetter(cx, op, obj, id, strict, vp); + return CallJSPropertyOpSetter(cx, op, obj, id, vp, result); } inline uintptr_t diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index c62e1f2bef..de006953c1 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1169,7 +1169,7 @@ js::GetObjectMetadata(JSObject* obj) JS_FRIEND_API(bool) js::DefineOwnProperty(JSContext* cx, JSObject* objArg, jsid idArg, - JS::Handle descriptor, bool* bp) + JS::Handle descriptor, ObjectOpResult &result) { RootedObject obj(cx, objArg); RootedId id(cx, idArg); @@ -1181,7 +1181,7 @@ js::DefineOwnProperty(JSContext* cx, JSObject* objArg, jsid idArg, if (descriptor.hasSetterObject()) assertSameCompartment(cx, descriptor.setterObject()); - return StandardDefineProperty(cx, obj, id, descriptor, bp); + return StandardDefineProperty(cx, obj, id, descriptor, result); } JS_FRIEND_API(bool) diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index ab3590044f..8e3dea213a 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -311,7 +311,8 @@ proxy_LookupProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::M JS::MutableHandle propp); extern JS_FRIEND_API(bool) proxy_DefineProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs, + JS::ObjectOpResult &result); extern JS_FRIEND_API(bool) proxy_HasProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp); extern JS_FRIEND_API(bool) @@ -319,7 +320,7 @@ proxy_GetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleObject receiver JS::MutableHandleValue vp); extern JS_FRIEND_API(bool) proxy_SetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id, - JS::MutableHandleValue bp, bool strict); + JS::MutableHandleValue bp, JS::ObjectOpResult &result); extern JS_FRIEND_API(bool) proxy_GetOwnPropertyDescriptor(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandle desc); @@ -2550,7 +2551,8 @@ JS_FRIEND_API(bool) SetPropertyIgnoringNamedGetter(JSContext* cx, const BaseProxyHandler* handler, JS::HandleObject proxy, JS::HandleObject receiver, JS::HandleId id, JS::MutableHandle desc, - bool descIsOwn, bool strict, JS::MutableHandleValue vp); + bool descIsOwn, JS::MutableHandleValue vp, + JS::ObjectOpResult &result); JS_FRIEND_API(void) ReportErrorWithId(JSContext* cx, const char* msg, JS::HandleId id); @@ -2621,7 +2623,7 @@ ReportIsNotFunction(JSContext* cx, JS::HandleValue v); extern JS_FRIEND_API(bool) DefineOwnProperty(JSContext* cx, JSObject* objArg, jsid idArg, - JS::Handle descriptor, bool* bp); + JS::Handle descriptor, JS::ObjectOpResult &result); } /* namespace js */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 74cec63adb..a912ea6f22 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -445,41 +445,12 @@ js::Throw(JSContext* cx, JSObject* obj, unsigned errorNumber) return false; } -static bool -Reject(JSContext* cx, unsigned errorNumber, bool throwError, jsid id, bool* rval) -{ - if (throwError) - return Throw(cx, id, errorNumber); - - *rval = false; - return true; -} - -static bool -Reject(JSContext* cx, JSObject* obj, unsigned errorNumber, bool throwError, bool* rval) -{ - if (throwError) - return Throw(cx, obj, errorNumber); - - *rval = false; - return true; -} - -static bool -Reject(JSContext* cx, HandleId id, unsigned errorNumber, bool throwError, bool* rval) -{ - if (throwError) - return Throw(cx, id, errorNumber); - - *rval = false; - return true; -} /*** Standard-compliant property definition (used by Object.defineProperty) **********************/ static bool DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const PropDesc& desc, - bool throwError, bool* rval) + ObjectOpResult &result) { /* 8.12.9 step 1. */ RootedShape shape(cx); @@ -495,20 +466,19 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) - return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval); - - *rval = true; + return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); if (desc.isGenericDescriptor() || desc.isDataDescriptor()) { MOZ_ASSERT(!obj->getOps()->defineProperty); RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue()); - return NativeDefineProperty(cx, obj, id, v, nullptr, nullptr, desc.attributes()); + return NativeDefineProperty(cx, obj, id, v, nullptr, nullptr, desc.attributes(), + result); } MOZ_ASSERT(desc.isAccessorDescriptor()); return NativeDefineProperty(cx, obj, id, UndefinedHandleValue, - desc.getter(), desc.setter(), desc.attributes()); + desc.getter(), desc.setter(), desc.attributes(), result); } /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */ @@ -585,7 +555,7 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const desc.isDataDescriptor() && (desc.hasWritable() ? desc.writable() : shape->writable())) { - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } if (!NativeGetExistingProperty(cx, obj, obj, shape, &v)) @@ -611,16 +581,16 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const * flag we veto from true to false for non-configurable * PropertyOp-based data properties and test before the * SameValue check later on in order to re-use that "if - * (!SameValue) Reject" logic. + * (!SameValue) return false" logic. * * This function is large and complex enough that it * seems best to repeat a small bit of code and return - * Reject(...) ASAP, instead of being clever. + * result.fail() ASAP, instead of being clever. */ if (!shapeConfigurable && (!shape->hasDefaultGetter() || !shape->hasDefaultSetter())) { - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } break; } @@ -639,15 +609,14 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const break; /* The conditions imposed by step 5 or step 6 apply. */ - *rval = true; - return true; + return result.succeed(); } while (0); /* 8.12.9 step 7. */ if (!shapeConfigurable) { if ((desc.hasConfigurable() && desc.configurable()) || (desc.hasEnumerable() && desc.enumerable() != shape->enumerable())) { - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } } @@ -658,19 +627,19 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const } else if (desc.isDataDescriptor() != shapeDataDescriptor) { /* 8.12.9 step 9. */ if (!shapeConfigurable) - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } else if (desc.isDataDescriptor()) { /* 8.12.9 step 10. */ MOZ_ASSERT(shapeDataDescriptor); if (!shapeConfigurable && !shape->writable()) { if (desc.hasWritable() && desc.writable()) - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); if (desc.hasValue()) { bool same; if (!SameValue(cx, desc.value(), v, &same)) return false; if (!same) - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } } @@ -685,7 +654,7 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const if (!SameValue(cx, desc.setterValue(), setter, &same)) return false; if (!same) - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } if (desc.hasGet()) { @@ -694,7 +663,7 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const if (!SameValue(cx, desc.getterValue(), getter, &same)) return false; if (!same) - return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } } } @@ -759,8 +728,6 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const } } - *rval = true; - /* * Since "data" properties implemented using native C functions may rely on * side effects during setting, we must make them aware that they have been @@ -771,18 +738,18 @@ DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id, const * redefining it or we had invoked its setter to change its value). */ if (callDelProperty) { - bool succeeded; - if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, &succeeded)) + bool ignored; + if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, &ignored)) return false; } - return NativeDefineProperty(cx, obj, id, v, getter, setter, attrs); + return NativeDefineProperty(cx, obj, id, v, getter, setter, attrs, result); } /* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */ static bool DefinePropertyOnArray(JSContext* cx, Handle arr, HandleId id, const PropDesc& desc, - bool throwError, bool* rval) + ObjectOpResult &result) { /* Step 2. */ if (id == NameToId(cx->names().length)) { @@ -804,23 +771,23 @@ DefinePropertyOnArray(JSContext* cx, Handle arr, HandleId id, cons } if (desc.hasConfigurable() && desc.configurable()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); if (desc.hasEnumerable() && desc.enumerable()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); if (desc.isAccessorDescriptor()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); unsigned attrs = arr->lookup(cx, id)->attributes(); if (!arr->lengthIsWritable()) { if (desc.hasWritable() && desc.writable()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); } else { if (desc.hasWritable() && !desc.writable()) attrs = attrs | JSPROP_READONLY; } - return ArraySetLength(cx, arr, id, attrs, v, throwError); + return ArraySetLength(cx, arr, id, attrs, v, result); } /* Step 3. */ @@ -831,22 +798,21 @@ DefinePropertyOnArray(JSContext* cx, Handle arr, HandleId id, cons /* Steps 3a, 3e. */ if (index >= oldLen && !arr->lengthIsWritable()) - return Reject(cx, arr, JSMSG_CANT_APPEND_TO_ARRAY, throwError, rval); + return result.fail(JSMSG_CANT_APPEND_TO_ARRAY); /* Steps 3f-j. */ - return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval); + return DefinePropertyOnObject(cx, arr, id, desc, result); } /* Step 4. */ - return DefinePropertyOnObject(cx, arr, id, desc, throwError, rval); + return DefinePropertyOnObject(cx, arr, id, desc, result); } // ES6 draft rev31 9.4.5.3 [[DefineOwnProperty]] static bool DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id, const PropDesc& desc, - bool throwError, bool* rval) + ObjectOpResult &result) { - MOZ_ASSERT(IsAnyTypedArray(obj)); // Steps 3.a-c. uint64_t index; @@ -854,26 +820,24 @@ DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id, const P // These are all substeps of 3.c. // Steps i-vi. // We (wrongly) ignore out of range defines with a value. - if (index >= AnyTypedArrayLength(obj)) { - *rval = true; - return true; - } + if (index >= AnyTypedArrayLength(obj)) + return result.succeed(); // Step vii. if (desc.isAccessorDescriptor()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); // Step viii. if (desc.hasConfigurable() && desc.configurable()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); // Step ix. if (desc.hasEnumerable() && !desc.enumerable()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); // Step x. if (desc.hasWritable() && !desc.writable()) - return Reject(cx, id, JSMSG_CANT_REDEFINE_PROP, throwError, rval); + return result.fail(JSMSG_CANT_REDEFINE_PROP); // Step xi. if (desc.hasValue()) { @@ -888,25 +852,24 @@ DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id, const P } // Step xii. - *rval = true; - return true; + return result.succeed(); } // Step 4. - return DefinePropertyOnObject(cx, obj.as(), id, desc, throwError, rval); + return DefinePropertyOnObject(cx, obj.as(), id, desc, result); } bool js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, const PropDesc& desc, - bool throwError, bool* rval) + ObjectOpResult &result) { if (obj->is()) { Rooted arr(cx, &obj->as()); - return DefinePropertyOnArray(cx, arr, id, desc, throwError, rval); + return DefinePropertyOnArray(cx, arr, id, desc, result); } if (IsAnyTypedArray(obj)) - return DefinePropertyOnTypedArray(cx, obj, id, desc, throwError, rval); + return DefinePropertyOnTypedArray(cx, obj, id, desc, result); if (obj->is() && !UnboxedPlainObject::convertToNative(cx, obj)) return false; @@ -916,21 +879,38 @@ js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, const P Rooted pd(cx); desc.populatePropertyDescriptor(obj, &pd); pd.object().set(obj); - return Proxy::defineProperty(cx, obj, id, &pd); + return Proxy::defineProperty(cx, obj, id, &pd, result); } - return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval); + return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); } - return DefinePropertyOnObject(cx, obj.as(), id, desc, throwError, rval); + return DefinePropertyOnObject(cx, obj.as(), id, desc, result); } bool js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle descriptor, bool* bp) + Handle descriptor, ObjectOpResult &result) { Rooted desc(cx); desc.initFromPropertyDescriptor(descriptor); - return StandardDefineProperty(cx, obj, id, desc, true, bp); + return StandardDefineProperty(cx, obj, id, desc, result); +} + +bool +js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc) +{ + ObjectOpResult success; + return StandardDefineProperty(cx, obj, id, desc, success) && + success.checkStrict(cx, obj, id); +} + +bool +js::StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, + Handle desc) +{ + ObjectOpResult success; + return StandardDefineProperty(cx, obj, id, desc, success) && + success.checkStrict(cx, obj, id); } bool @@ -963,9 +943,8 @@ js::DefineProperties(JSContext* cx, HandleObject obj, HandleObject props) if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs)) return false; - bool dummy; for (size_t i = 0, len = ids.length(); i < len; i++) { - if (!StandardDefineProperty(cx, obj, ids[i], descs[i], true, &dummy)) + if (!StandardDefineProperty(cx, obj, ids[i], descs[i])) return false; } @@ -1083,8 +1062,7 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level) desc.object().set(obj); // 8.a.i-ii. / 9.a.iii.3-4 - bool result; - if (!StandardDefineProperty(cx, obj, id, desc, &result)) + if (!StandardDefineProperty(cx, obj, id, desc)) return false; } } @@ -1676,24 +1654,24 @@ js::CreateThisForFunction(JSContext* cx, HandleObject callee, NewObjectKind newK /* static */ bool JSObject::nonNativeSetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp, bool strict) + HandleId id, MutableHandleValue vp, ObjectOpResult &result) { if (MOZ_UNLIKELY(obj->watched())) { WatchpointMap* wpmap = cx->compartment()->watchpointMap; if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp)) return false; } - return obj->getOps()->setProperty(cx, obj, receiver, id, vp, strict); + return obj->getOps()->setProperty(cx, obj, receiver, id, vp, result); } /* static */ bool JSObject::nonNativeSetElement(JSContext* cx, HandleObject obj, HandleObject receiver, - uint32_t index, MutableHandleValue vp, bool strict) + uint32_t index, MutableHandleValue vp, ObjectOpResult &result) { RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; - return nonNativeSetProperty(cx, obj, receiver, id, vp, strict); + return nonNativeSetProperty(cx, obj, receiver, id, vp, result); } JS_FRIEND_API(bool) @@ -1724,8 +1702,7 @@ JS_CopyPropertyFrom(JSContext* cx, HandleId id, HandleObject target, if (!cx->compartment()->wrap(cx, &desc)) return false; - bool ignored; - return StandardDefineProperty(cx, target, wrappedId, desc, &ignored); + return StandardDefineProperty(cx, target, wrappedId, desc); } JS_FRIEND_API(bool) @@ -3206,7 +3183,8 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, bool js::DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) { MOZ_ASSERT(getter != JS_PropertyStub); MOZ_ASSERT(setter != JS_StrictPropertyStub); @@ -3216,15 +3194,54 @@ js::DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleVa if (op) { if (!cx->shouldBeJSContext()) return false; - return op(cx->asJSContext(), obj, id, value, getter, setter, attrs); + return op(cx->asJSContext(), obj, id, value, getter, setter, attrs, result); } - return NativeDefineProperty(cx, obj.as(), id, value, getter, setter, attrs); + return NativeDefineProperty(cx, obj.as(), id, value, getter, setter, attrs, + result); } bool -js::DefineProperty(ExclusiveContext* cx, HandleObject obj, - PropertyName* name, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) +js::DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) +{ + RootedId id(cx, NameToId(name)); + return DefineProperty(cx, obj, id, value, getter, setter, attrs, result); +} + +bool +js::DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) +{ + MOZ_ASSERT(getter != JS_PropertyStub); + MOZ_ASSERT(setter != JS_StrictPropertyStub); + + RootedId id(cx); + if (!IndexToId(cx, index, &id)) + return false; + return DefineProperty(cx, obj, id, value, getter, setter, attrs, result); +} + +bool +js::DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) +{ + ObjectOpResult result; + if (!DefineProperty(cx, obj, id, value, getter, setter, attrs, result)) + return false; + if (!result) { + if (!cx->shouldBeJSContext()) + return false; + result.reportError(cx->asJSContext(), obj, id); + return false; + } + return true; +} + +bool +js::DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) { RootedId id(cx, NameToId(name)); return DefineProperty(cx, obj, id, value, getter, setter, attrs); @@ -3232,7 +3249,7 @@ js::DefineProperty(ExclusiveContext* cx, HandleObject obj, bool js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) { MOZ_ASSERT(getter != JS_PropertyStub); MOZ_ASSERT(setter != JS_StrictPropertyStub); @@ -3243,6 +3260,22 @@ js::DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, Handle return DefineProperty(cx, obj, id, value, getter, setter, attrs); } +bool +js::SetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name, + MutableHandleValue vp) +{ + RootedId id(cx, NameToId(name)); + return SetProperty(cx, obj, receiver, id, vp); +} + +bool +js::PutProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, MutableHandleValue value, + bool strict) +{ + RootedId id(cx, NameToId(name)); + return PutProperty(cx, obj, id, value, strict); +} + /*** SpiderMonkey nonstandard internal methods ***************************************************/ diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 044ba7798c..e99378f0db 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -519,10 +519,10 @@ class JSObject : public js::gc::Cell static bool nonNativeSetProperty(JSContext* cx, js::HandleObject obj, js::HandleObject receiver, js::HandleId id, - js::MutableHandleValue vp, bool strict); + js::MutableHandleValue vp, JS::ObjectOpResult &result); static bool nonNativeSetElement(JSContext* cx, js::HandleObject obj, js::HandleObject receiver, uint32_t index, - js::MutableHandleValue vp, bool strict); + js::MutableHandleValue vp, JS::ObjectOpResult &result); static bool swap(JSContext* cx, JS::HandleObject a, JS::HandleObject b); @@ -787,13 +787,40 @@ GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, * the DefineProperty functions do not enforce some invariants mandated by ES6. */ extern bool -StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, - const PropDesc& desc, bool throwError, bool* rval); +StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc, + ObjectOpResult &result); extern bool StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, - Handle descriptor, bool* bp); + Handle descriptor, ObjectOpResult &result); +/* + * For convenience, signatures identical to the above except without the + * ObjectOpResult out-parameter. They throw a TypeError on failure. + */ +extern bool +StandardDefineProperty(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc); + +extern bool +StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id, + Handle desc); + +extern bool +DefineProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp, unsigned attrs, ObjectOpResult &result); + +extern bool +DefineProperty(ExclusiveContext *cx, HandleObject obj, PropertyName *name, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp, unsigned attrs, ObjectOpResult &result); + +extern bool +DefineElement(ExclusiveContext *cx, HandleObject obj, uint32_t index, HandleValue value, + JSPropertyOp getter, JSStrictPropertyOp, unsigned attrs, ObjectOpResult &result); + +/* + * When the 'result' out-param is omitted, the behavior is the same as above, except + * that any failure results in a TypeError. + */ extern bool DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value, JSPropertyOp getter = nullptr, @@ -876,19 +903,49 @@ GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, */ inline bool SetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp, bool strict); + MutableHandleValue vp, ObjectOpResult &result); inline bool SetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, PropertyName* name, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { RootedId id(cx, NameToId(name)); - return SetProperty(cx, obj, receiver, id, vp, strict); + return SetProperty(cx, obj, receiver, id, vp, result); } inline bool SetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp, bool strict); + MutableHandleValue vp, ObjectOpResult &result); + +inline bool +SetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, + MutableHandleValue vp) +{ + ObjectOpResult result; + return SetProperty(cx, obj, receiver, id, vp, result) && + result.checkStrict(cx, receiver, id); +} + +extern bool +SetProperty(JSContext *cx, HandleObject obj, HandleObject receiver, HandlePropertyName name, + MutableHandleValue vp); + +/* + * ES6 draft rev 31 (15 Jan 2015) 7.3.3 Put (O, P, V, Throw), except that on + * success, the spec says this is supposed to return a boolean value, which we + * don't bother doing. + */ +inline bool +PutProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue value, bool strict) +{ + ObjectOpResult result; + return SetProperty(cx, obj, obj, id, value, result) && + result.checkStrictErrorOrWarning(cx, obj, id, strict); +} + +extern bool +PutProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, MutableHandleValue value, + bool strict); /* * ES6 [[Delete]]. Equivalent to the JS code `delete obj[id]`. diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index be04db1ef1..a388efa73d 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -742,7 +742,7 @@ NodeBuilder::newArray(NodeVector& elts, MutableHandleValue dst) if (val.isMagic(JS_SERIALIZE_NO_NODE)) continue; - if (!SetElement(cx, array, array, i, &val, false)) + if (!DefineElement(cx, array, i, val)) return false; } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 5c6ba90a5e..3db76e0e4e 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -2175,7 +2175,7 @@ class MOZ_STACK_CLASS StringRegExpGuard // Handle everything else generically (including throwing if .lastIndex is non-writable). RootedValue zero(cx, Int32Value(0)); - return SetProperty(cx, obj_, obj_, cx->names().lastIndex, &zero, true); + return SetProperty(cx, obj_, obj_, cx->names().lastIndex, &zero); } RegExpShared& regExp() { return *re_; } diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 59017d5011..92702845cb 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -117,7 +117,8 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const override; + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper, AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override; @@ -134,8 +135,8 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override; virtual bool get(JSContext* cx, HandleObject wrapper, HandleObject receiver, HandleId id, MutableHandleValue vp) const override; - virtual bool set(JSContext* cx, HandleObject wrapper, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const override; + virtual bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override; virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override; @@ -182,7 +183,8 @@ class JS_FRIEND_API(SecurityWrapper) : public Base bool* bp) const override; virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const override; + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override; virtual bool preventExtensions(JSContext* cx, HandleObject wrapper, bool* succeeded) const override; virtual bool setPrototypeOf(JSContext* cx, HandleObject proxy, HandleObject proto, diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index ad85874bd4..b287e4d997 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -73,7 +73,7 @@ BaseProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver, bool BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const + HandleId id, MutableHandleValue vp, ObjectOpResult &result) const { assertEnteredPolicy(cx, proxy, id, SET); @@ -94,7 +94,7 @@ BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, if (!GetPrototype(cx, proxy, &proto)) return false; if (proto) - return SetProperty(cx, proto, receiver, id, vp, strict); + return SetProperty(cx, proto, receiver, id, vp, result); // Change ownDesc to be a complete descriptor for a configurable, // writable, enumerable data property. Then fall through to step 5. @@ -106,19 +106,14 @@ BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, if (ownDesc.isDataDescriptor()) { // Steps 5.a-b, adapted to our nonstandard implementation of ES6 // [[Set]] return values. - if (!ownDesc.isWritable()) { - if (strict) - return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR); - if (cx->compartment()->options().extraWarnings(cx)) - return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING); - return true; - } + if (!ownDesc.isWritable()) + return result.fail(JSMSG_READ_ONLY); // Nonstandard SpiderMonkey special case: setter ops. StrictPropertyOp setter = ownDesc.setter(); MOZ_ASSERT(setter != JS_StrictPropertyStub); if (setter && setter != JS_StrictPropertyStub) - return CallSetter(cx, receiver, id, setter, ownDesc.attributes(), strict, vp); + return CallSetter(cx, receiver, id, setter, ownDesc.attributes(), vp, result); // Steps 5.c-d. Adapt for SpiderMonkey by using HasOwnProperty instead // of the standard [[GetOwnProperty]]. @@ -137,8 +132,8 @@ BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, const Class* clasp = receiver->getClass(); MOZ_ASSERT(clasp->getProperty != JS_PropertyStub); MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub); - return DefineProperty(cx, receiver, id, vp, - clasp->getProperty, clasp->setProperty, attrs); + return DefineProperty(cx, receiver, id, vp, clasp->getProperty, clasp->setProperty, + attrs, result); } // Step 6. @@ -147,16 +142,18 @@ BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, if (ownDesc.hasSetterObject()) setter = ownDesc.setterObject(); if (!setter) - return ReportGetterOnlyAssignment(cx, strict); + return result.fail(JSMSG_GETTER_ONLY); RootedValue setterValue(cx, ObjectValue(*setter)); - return InvokeGetterOrSetter(cx, receiver, setterValue, 1, vp.address(), vp); + if (!InvokeGetterOrSetter(cx, receiver, setterValue, 1, vp.address(), vp)) + return false; + return result.succeed(); } bool js::SetPropertyIgnoringNamedGetter(JSContext* cx, const BaseProxyHandler* handler, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandle desc, - bool descIsOwn, bool strict, MutableHandleValue vp) + bool descIsOwn, MutableHandleValue vp, ObjectOpResult &result) { /* The control-flow here differs from ::get() because of the fall-through case below. */ MOZ_ASSERT_IF(descIsOwn, desc.object()); @@ -165,35 +162,37 @@ js::SetPropertyIgnoringNamedGetter(JSContext* cx, const BaseProxyHandler* handle MOZ_ASSERT(desc.setter() != JS_StrictPropertyStub); // Check for read-only properties. - if (desc.isReadonly()) { - if (strict) - return Throw(cx, id, descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP); - return true; - } + if (desc.isReadonly()) + return result.fail(descIsOwn ? JSMSG_READ_ONLY : JSMSG_CANT_REDEFINE_PROP); if (desc.hasSetterObject() || desc.setter()) { - if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp)) + if (!CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), vp, result)) return false; - if (!proxy->is() || proxy->as().handler() != handler) - return true; - if (desc.isShared()) + if (!result) return true; + if (!proxy->is() || + proxy->as().handler() != handler || + desc.isShared()) + { + return result.succeed(); + } } desc.value().set(vp.get()); if (descIsOwn) { MOZ_ASSERT(desc.object() == proxy); - return handler->defineProperty(cx, proxy, id, desc); + return handler->defineProperty(cx, proxy, id, desc, result); } - return DefineProperty(cx, receiver, id, desc.value(), - desc.getter(), desc.setter(), desc.attributes()); + return DefineProperty(cx, receiver, id, desc.value(), desc.getter(), desc.setter(), + desc.attributes(), result); } desc.object().set(receiver); desc.value().set(vp.get()); desc.setAttributes(JSPROP_ENUMERATE); desc.setGetter(nullptr); desc.setSetter(nullptr); // Pick up the class getter/setter. - return DefineProperty(cx, receiver, id, desc.value(), nullptr, nullptr, JSPROP_ENUMERATE); + return DefineProperty(cx, receiver, id, desc.value(), nullptr, nullptr, JSPROP_ENUMERATE, + result); } bool diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp index 7cc9d12fda..f39e6d6e99 100644 --- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -49,12 +49,13 @@ CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext* cx, HandleObject wr bool CrossCompartmentWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const + MutableHandle desc, + ObjectOpResult &result) const { Rooted desc2(cx, desc); PIERCE(cx, wrapper, cx->compartment()->wrap(cx, &desc2), - Wrapper::defineProperty(cx, wrapper, id, &desc2), + Wrapper::defineProperty(cx, wrapper, id, &desc2, result), NOTHING); } @@ -168,13 +169,13 @@ CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleObject r bool CrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const + HandleId id, MutableHandleValue vp, ObjectOpResult &result) const { RootedObject receiverCopy(cx, receiver); PIERCE(cx, wrapper, cx->compartment()->wrap(cx, &receiverCopy) && cx->compartment()->wrap(cx, vp), - Wrapper::set(cx, wrapper, receiverCopy, id, strict, vp), + Wrapper::set(cx, wrapper, receiverCopy, id, vp, result), NOTHING); } diff --git a/js/src/proxy/DeadObjectProxy.cpp b/js/src/proxy/DeadObjectProxy.cpp index 52274c784a..218bb07aa5 100644 --- a/js/src/proxy/DeadObjectProxy.cpp +++ b/js/src/proxy/DeadObjectProxy.cpp @@ -32,7 +32,8 @@ DeadObjectProxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, H bool DeadObjectProxy::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const + MutableHandle desc, + ObjectOpResult &result) const { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); return false; diff --git a/js/src/proxy/DeadObjectProxy.h b/js/src/proxy/DeadObjectProxy.h index 358ee6dee0..13c79d6c86 100644 --- a/js/src/proxy/DeadObjectProxy.h +++ b/js/src/proxy/DeadObjectProxy.h @@ -22,7 +22,8 @@ class DeadObjectProxy : public BaseProxyHandler virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const override; + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, HandleObject wrapper, AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override; diff --git a/js/src/proxy/DirectProxyHandler.cpp b/js/src/proxy/DirectProxyHandler.cpp index a20ab11fad..038e0bac7f 100644 --- a/js/src/proxy/DirectProxyHandler.cpp +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -34,12 +34,12 @@ DirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, bool DirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const + MutableHandle desc, + ObjectOpResult &result) const { assertEnteredPolicy(cx, proxy, id, SET); RootedObject target(cx, proxy->as().target()); - bool ignored; - return StandardDefineProperty(cx, target, id, desc, &ignored); + return StandardDefineProperty(cx, target, id, desc, result); } bool @@ -223,11 +223,11 @@ DirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver bool DirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const + HandleId id, MutableHandleValue vp, ObjectOpResult &result) const { assertEnteredPolicy(cx, proxy, id, SET); RootedObject target(cx, proxy->as().target()); - return SetProperty(cx, target, receiver, id, vp, strict); + return SetProperty(cx, target, receiver, id, vp, result); } bool diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index 52c0d2be2f..d9474a2090 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -134,14 +134,17 @@ Proxy::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, bool Proxy::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) + MutableHandle desc, ObjectOpResult &result) { JS_CHECK_RECURSION(cx, return false); const BaseProxyHandler* handler = proxy->as().handler(); AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); - if (!policy.allowed()) - return policy.returnValue(); - return proxy->as().handler()->defineProperty(cx, proxy, id, desc); + if (!policy.allowed()) { + if (!policy.returnValue()) + return false; + return result.succeed(); + } + return proxy->as().handler()->defineProperty(cx, proxy, id, desc, result); } bool @@ -301,20 +304,23 @@ Proxy::callProp(JSContext* cx, HandleObject proxy, HandleObject receiver, Handle } bool -Proxy::set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, - MutableHandleValue vp) +Proxy::set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) { JS_CHECK_RECURSION(cx, return false); const BaseProxyHandler* handler = proxy->as().handler(); AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true); - if (!policy.allowed()) - return policy.returnValue(); + if (!policy.allowed()) { + if (!policy.returnValue()) + return false; + return result.succeed(); + } // Special case. See the comment on BaseProxyHandler::mHasPrototype. if (handler->hasPrototype()) - return handler->BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); + return handler->BaseProxyHandler::set(cx, proxy, receiver, id, vp, result); - return handler->set(cx, proxy, receiver, id, strict, vp); + return handler->set(cx, proxy, receiver, id, vp, result); } bool @@ -551,7 +557,8 @@ js::proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id, bool js::proxy_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) + PropertyOp getter, StrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) { Rooted desc(cx); desc.object().set(obj); @@ -559,7 +566,7 @@ js::proxy_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, HandleVal desc.setAttributes(attrs); desc.setGetter(getter); desc.setSetter(setter); - return Proxy::defineProperty(cx, obj, id, &desc); + return Proxy::defineProperty(cx, obj, id, &desc, result); } bool @@ -577,9 +584,9 @@ js::proxy_GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, Ha bool js::proxy_SetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { - return Proxy::set(cx, obj, receiver, id, strict, vp); + return Proxy::set(cx, obj, receiver, id, vp, result); } bool diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h index bec5c51400..464c8775b4 100644 --- a/js/src/proxy/Proxy.h +++ b/js/src/proxy/Proxy.h @@ -29,7 +29,7 @@ class Proxy static bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc); static bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc); + MutableHandle desc, ObjectOpResult &result); static bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props); static bool delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp); static bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp); @@ -42,7 +42,7 @@ class Proxy static bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp); static bool set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp); + MutableHandleValue vp, ObjectOpResult &result); static bool call(JSContext* cx, HandleObject proxy, const CallArgs& args); static bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args); diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 6dcd29b096..8c72775bf3 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -552,38 +552,37 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject return true; } -// ES6 (5 April 2014) 9.5.6 Proxy.[[DefineOwnProperty]](O,P) +// ES6 draft rev 31 (15 Jan 2015) 9.5.6 Proxy.[[DefineOwnProperty]](P, Desc) bool ScriptedDirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const + MutableHandle desc, + ObjectOpResult &result) const { - // step 2 + // step 2-4 RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); - - // step 3 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } - // step 4 + // step 5 RootedObject target(cx, proxy->as().target()); - // step 5-6 + // steps 6-7 RootedValue trap(cx); if (!GetProperty(cx, handler, handler, cx->names().defineProperty, &trap)) return false; - // step 7 + // step 8 if (trap.isUndefined()) - return DirectProxyHandler::defineProperty(cx, proxy, id, desc); + return DirectProxyHandler::defineProperty(cx, proxy, id, desc, result); - // step 8-9 + // step 9 RootedValue descObj(cx); if (!NewPropertyDescriptorObject(cx, desc, &descObj)) return false; - // step 10, 12 + // steps 10-11 RootedValue propKey(cx); if (!IdToStringOrSymbol(cx, id, &propKey)) return false; @@ -597,48 +596,50 @@ ScriptedDirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, Ha if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 11, 13 - if (ToBoolean(trapResult)) { - // step 14-15 - Rooted targetDesc(cx); - if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) - return false; + // FIXME - bug 1132522: Step 12 is not implemented yet. + // if (!ToBoolean(trapResult)) + // return result.fail(JSMSG_PROXY_DEFINE_RETURNED_FALSE); - // step 16-17 - bool extensibleTarget; - if (!IsExtensible(cx, target, &extensibleTarget)) - return false; + // step 13-14 + Rooted targetDesc(cx); + if (!GetOwnPropertyDescriptor(cx, target, id, &targetDesc)) + return false; - // step 18-19 - bool settingConfigFalse = desc.isPermanent(); - if (!targetDesc.object()) { - // step 20a - if (!extensibleTarget) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW); - return false; - } - // step 20b - if (settingConfigFalse) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC); - return false; - } - } else { - // step 21 - bool valid; - Rooted pd(cx); - pd.initFromPropertyDescriptor(desc); - if (!ValidatePropertyDescriptor(cx, extensibleTarget, pd, targetDesc, &valid)) - return false; - if (!valid || (settingConfigFalse && !targetDesc.isPermanent())) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID); - return false; - } + // step 15-16 + bool extensibleTarget; + if (!IsExtensible(cx, target, &extensibleTarget)) + return false; + + // step 17-18 + // FIXME bug 1133081: settingConfigFalse should be false if we have + // JSPROP_IGNORE_PERMANENT. + bool settingConfigFalse = desc.isPermanent(); + if (!targetDesc.object()) { + // step 19.a + if (!extensibleTarget) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW); + return false; + } + // step 19.b + if (settingConfigFalse) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC); + return false; + } + } else { + // step 20 + bool valid; + Rooted pd(cx); + pd.initFromPropertyDescriptor(desc); + if (!ValidatePropertyDescriptor(cx, extensibleTarget, pd, targetDesc, &valid)) + return false; + if (!valid || (settingConfigFalse && !targetDesc.isPermanent())) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID); + return false; } } - // [[DefineProperty]] should return a boolean value, which is used to do things like - // strict-mode throwing. At present, the engine is not prepared to do that. See bug 826587. - return true; + // step 21 + return result.succeed(); } // ES6 (5 April 2014) 9.5.12 Proxy.[[OwnPropertyKeys]]() @@ -934,7 +935,7 @@ ScriptedDirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject // ES6 (22 May, 2014) 9.5.9 Proxy.[[SetP]](P, V, Receiver) bool ScriptedDirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const + HandleId id, MutableHandleValue vp, ObjectOpResult &result) const { // step 2 RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); @@ -955,7 +956,7 @@ ScriptedDirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject // step 7 if (trap.isUndefined()) - return DirectProxyHandler::set(cx, proxy, receiver, id, strict, vp); + return DirectProxyHandler::set(cx, proxy, receiver, id, vp, result); // step 8,10 RootedValue value(cx); @@ -971,7 +972,9 @@ ScriptedDirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; - // step 9 + // FIXME - bug 1132522: Step 9 is not implemented yet. + // if (!ToBoolean(trapResult)) + // return result.fail(JSMSG_PROXY_SET_RETURNED_FALSE); bool success = ToBoolean(trapResult); if (success) { @@ -1000,8 +1003,9 @@ ScriptedDirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject } // step 11, 15 + // XXX FIXME - This use of vp is wrong. Bug 1132522 can clean it up. vp.setBoolean(success); - return true; + return result.succeed(); } diff --git a/js/src/proxy/ScriptedDirectProxyHandler.h b/js/src/proxy/ScriptedDirectProxyHandler.h index fd36bdf0f0..9241b1dc01 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.h +++ b/js/src/proxy/ScriptedDirectProxyHandler.h @@ -22,7 +22,8 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const override; + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override; @@ -44,7 +45,7 @@ class ScriptedDirectProxyHandler : public DirectProxyHandler { virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const override; virtual bool set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp) const override; + MutableHandleValue vp, ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override; diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.cpp b/js/src/proxy/ScriptedIndirectProxyHandler.cpp index 9effca3071..1801a75f7a 100644 --- a/js/src/proxy/ScriptedIndirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp @@ -196,13 +196,15 @@ ScriptedIndirectProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObje bool ScriptedIndirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const + MutableHandle desc, + ObjectOpResult &result) const { RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); RootedValue fval(cx), value(cx); return GetFundamentalTrap(cx, handler, cx->names().defineProperty, &fval) && NewPropertyDescriptorObject(cx, desc, &value) && - Trap2(cx, handler, fval, id, value, &value); + Trap2(cx, handler, fval, id, value, &value) && + result.succeed(); } bool @@ -294,7 +296,7 @@ ScriptedIndirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObjec bool ScriptedIndirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const + HandleId id, MutableHandleValue vp, ObjectOpResult &result) const { RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy)); RootedValue idv(cx); @@ -308,13 +310,16 @@ ScriptedIndirectProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObjec if (!GetDerivedTrap(cx, handler, cx->names().set, &fval)) return false; if (!IsCallable(fval)) - return derivedSet(cx, proxy, receiver, id, strict, vp); - return Trap(cx, handler, fval, 3, argv.begin(), &idv); + return derivedSet(cx, proxy, receiver, id, vp, result); + if (!Trap(cx, handler, fval, 3, argv.begin(), &idv)) + return false; + return result.succeed(); } bool ScriptedIndirectProxyHandler::derivedSet(JSContext* cx, HandleObject proxy, HandleObject receiver, - HandleId id, bool strict, MutableHandleValue vp) const + HandleId id, MutableHandleValue vp, + ObjectOpResult &result) const { // Find an own or inherited property. The code here is strange for maximum // backward compatibility with earlier code written before ES6 and before @@ -328,8 +333,8 @@ ScriptedIndirectProxyHandler::derivedSet(JSContext* cx, HandleObject proxy, Hand return false; } - return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, strict, - vp); + return SetPropertyIgnoringNamedGetter(cx, this, proxy, receiver, id, &desc, descIsOwn, vp, + result); } bool diff --git a/js/src/proxy/ScriptedIndirectProxyHandler.h b/js/src/proxy/ScriptedIndirectProxyHandler.h index 707a7c9d32..74b5a20b11 100644 --- a/js/src/proxy/ScriptedIndirectProxyHandler.h +++ b/js/src/proxy/ScriptedIndirectProxyHandler.h @@ -23,7 +23,8 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const override; + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override; @@ -35,7 +36,7 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, MutableHandleValue vp) const override; virtual bool set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp) const override; + MutableHandleValue vp, ObjectOpResult &result) const MOZ_OVERRIDE; /* SpiderMonkey extensions. */ virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id, @@ -53,7 +54,7 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler private: bool derivedSet(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp) const; + MutableHandleValue vp, ObjectOpResult &result) const; }; /* Derived class to handle Proxy.createFunction() */ diff --git a/js/src/proxy/SecurityWrapper.cpp b/js/src/proxy/SecurityWrapper.cpp index d27d2cc41c..354292ae9c 100644 --- a/js/src/proxy/SecurityWrapper.cpp +++ b/js/src/proxy/SecurityWrapper.cpp @@ -105,7 +105,8 @@ SecurityWrapper::boxedValue_unbox(JSContext* cx, HandleObject obj, Mutable template bool SecurityWrapper::defineProperty(JSContext* cx, HandleObject wrapper, - HandleId id, MutableHandle desc) const + HandleId id, MutableHandle desc, + ObjectOpResult &result) const { if (desc.getter() || desc.setter()) { RootedValue idVal(cx, IdToValue(id)); @@ -121,7 +122,7 @@ SecurityWrapper::defineProperty(JSContext* cx, HandleObject wrapper, return false; } - return Base::defineProperty(cx, wrapper, id, desc); + return Base::defineProperty(cx, wrapper, id, desc, result); } template diff --git a/js/src/tests/js1_5/extensions/regress-365869.js b/js/src/tests/js1_5/extensions/regress-365869.js index 1910ee2b87..f027cf84af 100644 --- a/js/src/tests/js1_5/extensions/regress-365869.js +++ b/js/src/tests/js1_5/extensions/regress-365869.js @@ -33,7 +33,7 @@ function test() try { - expect = "TypeError: can't redefine non-configurable property '5'"; + expect = "TypeError: can't redefine non-configurable property 5"; "012345".__defineSetter__(5, function(){}); } catch(ex) diff --git a/js/src/tests/js1_6/Array/regress-304828.js b/js/src/tests/js1_6/Array/regress-304828.js index 0a48aa87af..10713aa4ae 100644 --- a/js/src/tests/js1_6/Array/regress-304828.js +++ b/js/src/tests/js1_6/Array/regress-304828.js @@ -29,7 +29,7 @@ reportCompare(expect, actual, summary + ': join'); // reverse value = '123'; -expect = 'TypeError: Array.prototype.reverse.call(...) is read-only'; +expect = 'TypeError: 0 is read-only'; try { actual = Array.prototype.reverse.call(value) + ''; @@ -42,7 +42,7 @@ reportCompare(expect, actual, summary + ': reverse'); // sort value = 'cba'; -expect = 'TypeError: Array.prototype.sort.call(...) is read-only'; +expect = 'TypeError: 0 is read-only'; try { actual = Array.prototype.sort.call(value) + ''; @@ -100,7 +100,7 @@ reportCompare('abc', value, summary + ': pop'); // unshift value = 'def'; -expect = 'TypeError: Array.prototype.unshift.call(...) is read-only'; +expect = 'TypeError: 0 is read-only'; try { actual = Array.prototype.unshift.call(value, 'a', 'b', 'c'); @@ -114,7 +114,7 @@ reportCompare('def', value, summary + ': unshift'); // shift value = 'abc'; -expect = 'TypeError: Array.prototype.shift.call(...) is read-only'; +expect = 'TypeError: 0 is read-only'; try { actual = Array.prototype.shift.call(value); @@ -128,7 +128,7 @@ reportCompare('abc', value, summary + ': shift'); // splice value = 'abc'; -expect = 'TypeError: Array.prototype.splice.call(...) is read-only'; +expect = 'TypeError: 1 is read-only'; try { actual = Array.prototype.splice.call(value, 1, 1) + ''; diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 5c9c5d3be1..a22db5e4c4 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -317,10 +317,11 @@ ArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) } static bool -ArgSetter(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) +ArgSetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, + ObjectOpResult &result) { if (!obj->is()) - return true; + return result.succeed(); Handle argsobj = obj.as(); Rooted desc(cx); @@ -339,7 +340,7 @@ ArgSetter(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHand argsobj->setElement(cx, arg, vp); if (arg < script->functionNonDelazifying()->nargs()) TypeScript::SetArgument(cx, script, arg, vp); - return true; + return result.succeed(); } } else { MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee)); @@ -354,7 +355,7 @@ ArgSetter(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHand */ bool succeeded; return NativeDeleteProperty(cx, argsobj, id, &succeeded) && - NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs); + NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result); } static bool @@ -438,10 +439,11 @@ StrictArgGetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue } static bool -StrictArgSetter(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) +StrictArgSetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, + ObjectOpResult &result) { if (!obj->is()) - return true; + return result.succeed(); Handle argsobj = obj.as(); Rooted desc(cx); @@ -456,7 +458,7 @@ StrictArgSetter(JSContext* cx, HandleObject obj, HandleId id, bool strict, Mutab unsigned arg = unsigned(JSID_TO_INT(id)); if (arg < argsobj->initialLength()) { argsobj->setElement(cx, arg, vp); - return true; + return result.succeed(); } } else { MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().length)); @@ -469,7 +471,7 @@ StrictArgSetter(JSContext* cx, HandleObject obj, HandleId id, bool strict, Mutab */ bool succeeded; return NativeDeleteProperty(cx, argsobj, id, &succeeded) && - NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs); + NativeDefineProperty(cx, argsobj, id, vp, nullptr, nullptr, attrs, result); } static bool diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 9c4be93e85..73b4f5fdfd 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -6720,8 +6720,7 @@ DebuggerObject_defineProperty(JSContext* cx, unsigned argc, Value* vp) return false; ErrorCopier ec(ac); - bool dummy; - if (!StandardDefineProperty(cx, obj, id, desc, true, &dummy)) + if (!StandardDefineProperty(cx, obj, id, desc)) return false; } @@ -6764,8 +6763,7 @@ DebuggerObject_defineProperties(JSContext* cx, unsigned argc, Value* vp) ErrorCopier ec(ac); for (size_t i = 0; i < n; i++) { - bool dummy; - if (!StandardDefineProperty(cx, obj, ids[i], descs[i], true, &dummy)) + if (!StandardDefineProperty(cx, obj, ids[i], descs[i])) return false; } } @@ -7512,7 +7510,7 @@ DebuggerEnv_setVariable(JSContext* cx, unsigned argc, Value* vp) } /* Just set the property. */ - if (!SetProperty(cx, env, env, id, &v, true)) + if (!SetProperty(cx, env, env, id, &v)) return false; } diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index e9592f55f4..56ba00cb9a 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -607,7 +607,10 @@ class GlobalObject : public NativeObject #endif RootedObject holder(cx, intrinsicsHolder()); RootedValue valCopy(cx, value); - return SetProperty(cx, holder, holder, name, &valCopy, false); + ObjectOpResult result; + bool ok = SetProperty(cx, holder, holder, name, &valCopy, result); + MOZ_ASSERT_IF(ok, result); + return ok; } bool getSelfHostedFunction(JSContext* cx, HandleAtom selfHostedName, HandleAtom name, diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index b43e89c604..1a9db80a4f 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -313,19 +313,20 @@ SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject s RootedPropertyName name(cx, script->getName(pc)); RootedValue valCopy(cx, val); - /* - * In strict-mode, we need to trigger an error when trying to assign to an - * undeclared global variable. To do this, we call NativeSetProperty - * directly and pass Unqualified. - */ + // In strict mode, assigning to an undeclared global variable is an + // error. To detect this, we call NativeSetProperty directly and pass + // Unqualified. It stores the error, if any, in |result|. + bool ok; + ObjectOpResult result; + RootedId id(cx, NameToId(name)); if (scope->isUnqualifiedVarObj()) { MOZ_ASSERT(!scope->getOps()->setProperty); - RootedId id(cx, NameToId(name)); - return NativeSetProperty(cx, scope.as(), scope.as(), id, - Unqualified, &valCopy, strict); + ok = NativeSetProperty(cx, scope.as(), scope.as(), id, + Unqualified, &valCopy, result); + } else { + ok = SetProperty(cx, scope, scope, id, &valCopy, result); } - - return SetProperty(cx, scope, scope, name, &valCopy, strict); + return ok && result.checkStrictErrorOrWarning(cx, scope, id, strict); } inline bool @@ -339,7 +340,7 @@ InitPropertyOperation(JSContext *cx, JSOp op, HandleObject obj, HandleId id, Han MOZ_ASSERT(obj->as().layout().lookup(id)); RootedValue v(cx, rhs); - return SetProperty(cx, obj, obj, id, &v, false); + return PutProperty(cx, obj, id, &v, false); } inline bool diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 8c334db352..0597802c06 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -310,19 +310,19 @@ SetObjectProperty(JSContext* cx, JSOp op, HandleValue lval, HandleId id, Mutable RootedObject obj(cx, &lval.toObject()); - bool strict = op == JSOP_STRICTSETPROP; + ObjectOpResult result; if (MOZ_LIKELY(!obj->getOps()->setProperty)) { if (!NativeSetProperty(cx, obj.as(), obj.as(), id, - Qualified, rref, strict)) + Qualified, rref, result)) { return false; } } else { - if (!SetProperty(cx, obj, obj, id, rref, strict)) + if (!SetProperty(cx, obj, obj, id, rref, result)) return false; } - return true; + return result.checkStrictErrorOrWarning(cx, obj, id, op == JSOP_STRICTSETPROP); } static bool @@ -1425,7 +1425,7 @@ SetObjectElementOperation(JSContext* cx, Handle obj, HandleId id, con return false; RootedValue tmp(cx, value); - return SetProperty(cx, obj, obj, id, &tmp, strict); + return PutProperty(cx, obj, id, &tmp, strict); } static MOZ_NEVER_INLINE bool @@ -3859,7 +3859,7 @@ js::DefFunOperation(JSContext* cx, HandleScript script, HandleObject scopeChain, */ /* Step 5f. */ - return SetProperty(cx, parent, parent, name, &rval, script->strict()); + return PutProperty(cx, parent, name, &rval, script->strict()); } bool diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 600c84c3c4..350a187a85 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -598,18 +598,6 @@ NativeLookupProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* return NativeLookupProperty(cx, obj, id, objp, propp); } -inline bool -NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* name, - HandleValue value, PropertyOp getter, StrictPropertyOp setter, - unsigned attrs) -{ - MOZ_ASSERT(getter != JS_PropertyStub); - MOZ_ASSERT(setter != JS_StrictPropertyStub); - - RootedId id(cx, NameToId(name)); - return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs); -} - inline bool WarnIfNotConstructing(JSContext* cx, const CallArgs& args, const char* builtinName) { diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index df202e7eae..0642bd1f98 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1129,13 +1129,13 @@ UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, c static bool NativeSet(JSContext* cx, HandleNativeObject obj, HandleObject receiver, - HandleShape shape, bool strict, MutableHandleValue vp); + HandleShape shape, MutableHandleValue vp, ObjectOpResult &result); static inline bool DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, PropertyOp getter, StrictPropertyOp setter, unsigned attrs, HandleValue value, - bool callSetterAfterwards, bool setterIsStrict) + bool callSetterAfterwards, ObjectOpResult &result) { MOZ_ASSERT(getter != JS_PropertyStub); MOZ_ASSERT(setter != JS_StrictPropertyStub); @@ -1149,20 +1149,17 @@ DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId i !IsAnyTypedArray(obj)) { uint32_t index = JSID_TO_INT(id); - bool definesPast; - if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast)) - return false; - if (definesPast) - return true; + if (WouldDefinePastNonwritableLength(obj, index)) + return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); - NativeObject::EnsureDenseResult result; - result = obj->ensureDenseElements(cx, index, 1); - - if (result == NativeObject::ED_FAILED) + NativeObject::EnsureDenseResult edResult = obj->ensureDenseElements(cx, index, 1); + if (edResult == NativeObject::ED_FAILED) return false; - if (result == NativeObject::ED_OK) { + if (edResult == NativeObject::ED_OK) { obj->setDenseElementWithType(cx, index, value); - return CallAddPropertyHookDense(cx, obj, index, value); + if (!CallAddPropertyHookDense(cx, obj, index, value)) + return false; + return result.succeed(); } } @@ -1171,16 +1168,13 @@ DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId i if (id == NameToId(cx->names().length)) { if (!cx->shouldBeJSContext()) return false; - return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, setterIsStrict); + return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, result); } uint32_t index; if (IdIsIndex(id, &index)) { - bool definesPast; - if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast)) - return false; - if (definesPast) - return true; + if (WouldDefinePastNonwritableLength(obj, index)) + return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); } } @@ -1188,7 +1182,7 @@ DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId i if (IsAnyTypedArray(obj)) { uint64_t index; if (IsTypedArrayIndex(id, &index)) - return true; + return result.succeed(); } AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); @@ -1210,12 +1204,14 @@ DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId i uint32_t index = JSID_TO_INT(id); NativeObject::removeDenseElementForSparseIndex(cx, obj, index); - NativeObject::EnsureDenseResult result = NativeObject::maybeDensifySparseElements(cx, obj); - if (result == NativeObject::ED_FAILED) + NativeObject::EnsureDenseResult edResult = NativeObject::maybeDensifySparseElements(cx, obj); + if (edResult == NativeObject::ED_FAILED) return false; - if (result == NativeObject::ED_OK) { + if (edResult == NativeObject::ED_OK) { MOZ_ASSERT(!setter); - return CallAddPropertyHookDense(cx, obj, index, value); + if (!CallAddPropertyHookDense(cx, obj, index, value)) + return false; + return result.succeed(); } } @@ -1226,9 +1222,10 @@ DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId i if (!cx->shouldBeJSContext()) return false; RootedValue nvalue(cx, value); - return NativeSet(cx->asJSContext(), obj, obj, shape, setterIsStrict, &nvalue); + return NativeSet(cx->asJSContext(), obj, obj, shape, &nvalue, result); } - return true; + + return result.succeed(); } static unsigned @@ -1341,7 +1338,8 @@ CheckAccessorRedefinition(ExclusiveContext* cx, HandleObject obj, HandleShape sh bool js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) + PropertyOp getter, StrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) { MOZ_ASSERT(getter != JS_PropertyStub); MOZ_ASSERT(setter != JS_StrictPropertyStub); @@ -1369,7 +1367,7 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId if (IsImplicitDenseOrTypedArrayElement(shape)) { if (IsAnyTypedArray(obj)) { /* Ignore getter/setter properties added to typed arrays. */ - return true; + return result.succeed(); } if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id))) return false; @@ -1434,11 +1432,11 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId if (IsImplicitDenseOrTypedArrayElement(shape)) { if (IsAnyTypedArray(obj)) { /* - * Silently ignore attempts to change individial index attributes. + * Silently ignore attempts to change individual index attributes. * FIXME: Uses the same broken behavior as for accessors. This should - * probably throw. + * fail. */ - return true; + return result.succeed(); } if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id))) return false; @@ -1485,14 +1483,16 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId // relevant, just clear it. attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE; return DefinePropertyOrElement(cx, obj, id, getter, setter, - attrs, updateValue, false, false); + attrs, updateValue, false, result); } MOZ_ASSERT(shape); JS_ALWAYS_TRUE(UpdateShapeTypeAndValue(cx, obj, shape, updateValue)); - return CallAddPropertyHook(cx, obj, shape, updateValue); + if (!CallAddPropertyHook(cx, obj, shape, updateValue)) + return false; + return result.succeed(); } template @@ -1546,13 +1546,23 @@ js::NativeLookupElement(JSContext* cx, HandleNativeObject obj, uint32_t index, } bool -js::NativeDefineElement(ExclusiveContext* cx, HandleNativeObject obj, uint32_t index, HandleValue value, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) +js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, PropertyName *name, + HandleValue value, PropertyOp getter, StrictPropertyOp setter, + unsigned attrs, ObjectOpResult &result) +{ + RootedId id(cx, NameToId(name)); + return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result); +} + +bool +js::NativeDefineElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index, + HandleValue value, PropertyOp getter, StrictPropertyOp setter, + unsigned attrs, ObjectOpResult &result) { RootedId id(cx); if (index <= JSID_INT_MAX) { id = INT_TO_JSID(index); - return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs); + return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result); } AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter); @@ -1560,6 +1570,35 @@ js::NativeDefineElement(ExclusiveContext* cx, HandleNativeObject obj, uint32_t i if (!IndexToId(cx, index, &id)) return false; + return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result); +} + +bool +js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, + HandleValue value, PropertyOp getter, StrictPropertyOp setter, + unsigned attrs) +{ + ObjectOpResult result; + if (!NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result)) + return false; + if (!result) { + // Off-main-thread callers should not get here: they must call this + // function only with known-valid arguments. Populating a new + // PlainObject with configurable properties is fine. + if (!cx->shouldBeJSContext()) + return false; + result.reportError(cx->asJSContext(), obj, id); + return false; + } + return true; +} + +bool +js::NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, PropertyName *name, + HandleValue value, PropertyOp getter, StrictPropertyOp setter, + unsigned attrs) +{ + RootedId id(cx, NameToId(name)); return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs); } @@ -1961,7 +2000,7 @@ MaybeReportUndeclaredVarAssignment(JSContext* cx, JSString* propname) */ bool js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, HandleValue v, bool strict, bool objHasOwn) + HandleId id, HandleValue v, bool objHasOwn, ObjectOpResult &result) { // Step 5.c-d: Test whether receiver has an existing own property // receiver[id]. The spec calls [[GetOwnProperty]]; js::HasOwnProperty is @@ -1992,15 +2031,8 @@ js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleObject receiver bool extensible; if (!IsExtensible(cx, receiver, &extensible)) return false; - if (!extensible) { - // Error in strict mode code, warn with extra warnings option, - // otherwise do nothing. - if (strict) - return receiver->reportNotExtensible(cx); - if (cx->compartment()->options().extraWarnings(cx)) - return receiver->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING); - return true; - } + if (!extensible) + return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE); } // Invalidate SpiderMonkey-specific caches or bail. @@ -2020,37 +2052,24 @@ js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleObject receiver MOZ_ASSERT(getter != JS_PropertyStub); MOZ_ASSERT(setter != JS_StrictPropertyStub); if (!receiver->is()) - return DefineProperty(cx, receiver, id, v, getter, setter, attrs); + return DefineProperty(cx, receiver, id, v, getter, setter, attrs, result); Rooted nativeReceiver(cx, &receiver->as()); - return DefinePropertyOrElement(cx, nativeReceiver, id, getter, setter, attrs, v, true, strict); + return DefinePropertyOrElement(cx, nativeReceiver, id, getter, setter, attrs, v, true, result); } // When setting |id| for |receiver| and |obj| has no property for id, continue // the search up the prototype chain. bool js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp, bool strict) + HandleId id, MutableHandleValue vp, ObjectOpResult &result) { MOZ_ASSERT(!obj->is()); RootedObject proto(cx, obj->getProto()); if (proto) - return SetProperty(cx, proto, receiver, id, vp, strict); - return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false); -} - -bool -js::SetNonWritableProperty(JSContext* cx, HandleId id, bool strict) -{ - // Setting a non-writable property is an error in strict mode code, a - // warning with the extra warnings option, and otherwise does nothing. - - if (strict) - return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR); - if (cx->compartment()->options().extraWarnings(cx)) - return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING); - return true; + return SetProperty(cx, proto, receiver, id, vp, result); + return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result); } /* @@ -2064,7 +2083,7 @@ js::SetNonWritableProperty(JSContext* cx, HandleId id, bool strict) */ static bool SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiver, HandleId id, - QualifiedBool qualified, HandleValue v, bool strict) + QualifiedBool qualified, HandleValue v, ObjectOpResult &result) { // We should never add properties to lexical blocks. MOZ_ASSERT(!receiver->is()); @@ -2074,7 +2093,7 @@ SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleObject recei return false; } - return SetPropertyByDefining(cx, obj, receiver, id, v, strict, false); + return SetPropertyByDefining(cx, obj, receiver, id, v, false, result); } /* @@ -2083,7 +2102,7 @@ SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleObject recei */ static bool SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t index, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { if (IsAnyTypedArray(obj)) { double d; @@ -2100,20 +2119,17 @@ SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t inde else SharedTypedArrayObject::setElement(obj->as(), index, d); } - return true; + return result.succeed(); } - bool definesPast; - if (!WouldDefinePastNonwritableLength(cx, obj, index, strict, &definesPast)) - return false; - if (definesPast) - return true; + if (WouldDefinePastNonwritableLength(obj, index)) + return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH); if (!obj->maybeCopyElementsForWrite(cx)) return false; obj->setDenseElementWithType(cx, index, vp); - return true; + return result.succeed(); } /* @@ -2122,7 +2138,7 @@ SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t inde */ static bool NativeSet(JSContext* cx, HandleNativeObject obj, HandleObject receiver, - HandleShape shape, bool strict, MutableHandleValue vp) + HandleShape shape, MutableHandleValue vp, ObjectOpResult &result) { MOZ_ASSERT(obj->isNative()); @@ -2134,7 +2150,7 @@ NativeSet(JSContext* cx, HandleNativeObject obj, HandleObject receiver, // assignments to such properties as overwrites. bool overwriting = !obj->is() || !obj->getSlot(shape->slot()).isUndefined(); obj->setSlotWithType(cx, shape, vp, overwriting); - return true; + return result.succeed(); } } @@ -2146,13 +2162,13 @@ NativeSet(JSContext* cx, HandleNativeObject obj, HandleObject receiver, * or throw if we're in strict mode. */ if (!shape->hasGetterValue() && shape->hasDefaultSetter()) - return ReportGetterOnlyAssignment(cx, strict); + return result.fail(JSMSG_GETTER_ONLY); } RootedValue ovp(cx, vp); uint32_t sample = cx->runtime()->propertyRemovals; - if (!shape->set(cx, obj, receiver, strict, vp)) + if (!shape->set(cx, obj, receiver, vp, result)) return false; /* @@ -2166,7 +2182,7 @@ NativeSet(JSContext* cx, HandleNativeObject obj, HandleObject receiver, obj->setSlot(shape->slot(), vp); } - return true; + return true; // result is populated by shape->set() above. } /* @@ -2178,30 +2194,31 @@ NativeSet(JSContext* cx, HandleNativeObject obj, HandleObject receiver, */ static bool SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiver, HandleId id, - HandleNativeObject pobj, HandleShape shape, MutableHandleValue vp, bool strict) + HandleNativeObject pobj, HandleShape shape, MutableHandleValue vp, + ObjectOpResult &result) { if (IsImplicitDenseOrTypedArrayElement(shape)) { /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */ if (pobj == receiver) - return SetDenseOrTypedArrayElement(cx, pobj, JSID_TO_INT(id), vp, strict); + return SetDenseOrTypedArrayElement(cx, pobj, JSID_TO_INT(id), vp, result); } else { /* ES5 8.12.4 [[Put]] step 2. */ if (shape->isAccessorDescriptor()) { if (shape->hasDefaultSetter()) - return ReportGetterOnlyAssignment(cx, strict); + return result.fail(JSMSG_GETTER_ONLY); } else { MOZ_ASSERT(shape->isDataDescriptor()); if (!shape->writable()) - return SetNonWritableProperty(cx, id, strict); + return result.fail(JSMSG_READ_ONLY); } if (pobj == receiver) { if (pobj->is() && id == NameToId(cx->names().length)) { Rooted arr(cx, &pobj->as()); - return ArraySetLength(cx, arr, id, shape->attributes(), vp, strict); + return ArraySetLength(cx, arr, id, shape->attributes(), vp, result); } - return NativeSet(cx, obj, receiver, shape, strict, vp); + return NativeSet(cx, obj, receiver, shape, vp, result); } // pobj[id] is not an own property of receiver. Call the setter or shadow it. @@ -2210,19 +2227,19 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiver { // Weird special case: slotless property with default setter. if (shape->hasDefaultSetter() && !shape->hasGetterValue()) - return true; + return result.succeed(); - return shape->set(cx, obj, receiver, strict, vp); + return shape->set(cx, obj, receiver, vp, result); } } // Shadow pobj[id] by defining a new data property receiver[id]. - return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, obj == pobj); + return SetPropertyByDefining(cx, obj, receiver, id, vp, obj == pobj, result); } bool js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiver, HandleId id, - QualifiedBool qualified, MutableHandleValue vp, bool strict) + QualifiedBool qualified, MutableHandleValue vp, ObjectOpResult &result) { // Fire watchpoints, if any. if (MOZ_UNLIKELY(obj->watched())) { @@ -2248,7 +2265,7 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiv if (shape) { // Steps 5-6. - return SetExistingProperty(cx, obj, receiver, id, pobj, shape, vp, strict); + return SetExistingProperty(cx, obj, receiver, id, pobj, shape, vp, result); } // Steps 4.a-b. The check for 'done' on this next line is tricky. @@ -2262,7 +2279,7 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiv RootedObject proto(cx, done ? nullptr : pobj->getProto()); if (!proto) { // Step 4.d.i (and step 5). - return SetNonexistentProperty(cx, obj, receiver, id, qualified, vp, strict); + return SetNonexistentProperty(cx, obj, receiver, id, qualified, vp, result); } // Step 4.c.i. If the prototype is also native, this step is a @@ -2279,10 +2296,10 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiv if (!HasProperty(cx, proto, id, &found)) return false; if (!found) - return SetNonexistentProperty(cx, obj, receiver, id, qualified, vp, strict); + return SetNonexistentProperty(cx, obj, receiver, id, qualified, vp, result); } - return SetProperty(cx, proto, receiver, id, vp, strict); + return SetProperty(cx, proto, receiver, id, vp, result); } pobj = &proto->as(); } @@ -2290,12 +2307,12 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiv bool js::NativeSetElement(JSContext* cx, HandleNativeObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { RootedId id(cx); if (!IndexToId(cx, index, &id)) return false; - return NativeSetProperty(cx, obj, receiver, id, Qualified, vp, strict); + return NativeSetProperty(cx, obj, receiver, id, Qualified, vp, result); } /*** [[Delete]] **********************************************************************************/ diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index 98744b7b24..f0b578d621 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -83,12 +83,11 @@ class ArrayObject; * * |id| must be "length", |attrs| are the attributes to be used for the newly- * changed length property, |value| is the value for the new length, and - * |setterIsStrict| indicates whether invalid changes will cause a TypeError - * to be thrown. + * |result| receives an error code if the change is invalid. */ extern bool ArraySetLength(JSContext* cx, Handle obj, HandleId id, - unsigned attrs, HandleValue value, bool setterIsStrict); + unsigned attrs, HandleValue value, ObjectOpResult &result); /* * Elements header used for native objects. The elements component of such objects @@ -189,7 +188,7 @@ class ObjectElements friend bool ArraySetLength(JSContext* cx, Handle obj, HandleId id, - unsigned attrs, HandleValue value, bool setterIsStrict); + unsigned attrs, HandleValue value, ObjectOpResult &result); /* See Flags enum above. */ uint32_t flags; @@ -347,10 +346,6 @@ class NativeObject : public JSObject /* Slots for object dense elements. */ js::HeapSlot* elements_; - friend bool - ArraySetLength(JSContext* cx, Handle obj, HandleId id, unsigned attrs, - HandleValue value, bool setterIsStrict); - friend class ::JSObject; private: @@ -1238,16 +1233,28 @@ IsObjectValueInCompartment(Value v, JSCompartment* comp) extern bool NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); + PropertyOp getter, StrictPropertyOp, unsigned attrs, + ObjectOpResult &result); -inline bool +extern bool NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* name, HandleValue value, PropertyOp getter, StrictPropertyOp setter, - unsigned attrs); + unsigned attrs, ObjectOpResult &result); extern bool NativeDefineElement(ExclusiveContext* cx, HandleNativeObject obj, uint32_t index, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); + PropertyOp getter, StrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result); + +/* If the result out-param is omitted, throw on failure. */ +extern bool +NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value, + PropertyOp getter, StrictPropertyOp setter, unsigned attrs); + +extern bool +NativeDefineProperty(ExclusiveContext *cx, HandleNativeObject obj, PropertyName *name, + HandleValue value, PropertyOp getter, StrictPropertyOp setter, + unsigned attrs); extern bool NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp); @@ -1276,16 +1283,12 @@ NativeGetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, MutableH } bool -SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, HandleValue v, bool strict, bool objHasOwn); +SetPropertyByDefining(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, + HandleValue v, bool objHasOwn, ObjectOpResult &result); bool -SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp, bool strict); - -// Report any error or warning when writing to a non-writable property. -bool -SetNonWritableProperty(JSContext* cx, HandleId id, bool strict); +SetPropertyOnProto(JSContext *cx, HandleObject obj, HandleObject receiver, HandleId id, + MutableHandleValue vp, ObjectOpResult &result); /* * Indicates whether an assignment operation is qualified (`x.y = 0`) or @@ -1301,11 +1304,11 @@ enum QualifiedBool { extern bool NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiver, HandleId id, - QualifiedBool qualified, MutableHandleValue vp, bool strict); + QualifiedBool qualified, MutableHandleValue vp, ObjectOpResult &result); extern bool NativeSetElement(JSContext* cx, HandleNativeObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp, bool strict); + MutableHandleValue vp, ObjectOpResult &result); extern bool NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* succeeded); @@ -1417,20 +1420,20 @@ js::GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, jsid id, V inline bool js::SetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp, bool strict) + HandleId id, MutableHandleValue vp, ObjectOpResult &result) { if (obj->getOps()->setProperty) - return JSObject::nonNativeSetProperty(cx, obj, receiver, id, vp, strict); - return NativeSetProperty(cx, obj.as(), receiver, id, Qualified, vp, strict); + return JSObject::nonNativeSetProperty(cx, obj, receiver, id, vp, result); + return NativeSetProperty(cx, obj.as(), receiver, id, Qualified, vp, result); } inline bool js::SetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { if (obj->getOps()->setProperty) - return JSObject::nonNativeSetElement(cx, obj, receiver, index, vp, strict); - return NativeSetElement(cx, obj.as(), receiver, index, vp, strict); + return JSObject::nonNativeSetElement(cx, obj, receiver, index, vp, result); + return NativeSetElement(cx, obj.as(), receiver, index, vp, result); } #endif /* vm_NativeObject_h */ diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index f6c2cf8d89..ef162d3fa5 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -298,7 +298,7 @@ CallObject::createHollowForDebug(JSContext* cx, HandleFunction callee) RootedScript script(cx, callee->nonLazyScript()); for (BindingIter bi(script); !bi.done(); bi++) { id = NameToId(bi->name()); - if (!SetProperty(cx, callobj, callobj, id, &optimizedOut, true)) + if (!SetProperty(cx, callobj, callobj, id, &optimizedOut)) return nullptr; } @@ -503,10 +503,11 @@ with_LookupProperty(JSContext* cx, HandleObject obj, HandleId id, static bool with_DefineProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue value, - JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs) + JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) { RootedObject actual(cx, &obj->as().object()); - return DefineProperty(cx, actual, id, value, getter, setter, attrs); + return DefineProperty(cx, actual, id, value, getter, setter, attrs, result); } static bool @@ -534,13 +535,13 @@ with_GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleI static bool with_SetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { RootedObject actual(cx, &obj->as().object()); RootedObject actualReceiver(cx, receiver); if (receiver == obj) actualReceiver = actual; - return SetProperty(cx, actual, actualReceiver, id, vp, strict); + return SetProperty(cx, actual, actualReceiver, id, vp, result); } static bool @@ -1009,7 +1010,7 @@ uninitialized_GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver static bool uninitialized_SetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id, - MutableHandleValue vp, bool strict) + MutableHandleValue vp, ObjectOpResult &result) { ReportUninitializedLexicalId(cx, id); return false; @@ -1671,8 +1672,8 @@ class DebugScopeProxy : public BaseProxyHandler } } - bool set(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict, - MutableHandleValue vp) const override + bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) const MOZ_OVERRIDE { Rooted debugScope(cx, &proxy->as()); Rooted scope(cx, &proxy->as().scope()); @@ -1686,16 +1687,17 @@ class DebugScopeProxy : public BaseProxyHandler switch (access) { case ACCESS_UNALIASED: - return true; + return result.succeed(); case ACCESS_GENERIC: - return SetProperty(cx, scope, scope, id, vp, strict); + return SetProperty(cx, scope, scope, id, vp, result); default: MOZ_CRASH("bad AccessResult"); } } bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id, - MutableHandle desc) const override + MutableHandle desc, + ObjectOpResult &result) const MOZ_OVERRIDE { Rooted scope(cx, &proxy->as().scope()); @@ -1705,13 +1707,7 @@ class DebugScopeProxy : public BaseProxyHandler if (found) return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP); - return JS_DefinePropertyById(cx, scope, id, desc.value(), - // Descriptors never store JSNatives for - // accessors: they have either JSFunctions - // or JSPropertyOps. - desc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter())); + return JS_DefinePropertyById(cx, scope, id, desc, result); } bool ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props) const override diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index b87240a8a3..317bc6b5f2 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -373,7 +373,8 @@ js::intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp) MOZ_ASSERT_IF(arrobj->is(), idx < uint32_t(arrobj->as().length())); RootedValue tmp(cx, args[elemi]); // XXX: Always non-strict. - if (!SetElement(cx, arrobj, arrobj, idx, &tmp, false)) + ObjectOpResult ignored; + if (!SetElement(cx, arrobj, arrobj, idx, &tmp, ignored)) return false; } else { MOZ_ASSERT(idx < arrobj->as().getDenseInitializedLength()); @@ -421,8 +422,7 @@ js::intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp) desc = PropDesc(value, writable, enumerable, configurable); - bool result; - return StandardDefineProperty(cx, obj, id, desc, true, &result); + return StandardDefineProperty(cx, obj, id, desc); } bool diff --git a/js/src/vm/Shape-inl.h b/js/src/vm/Shape-inl.h index 7426eb58e3..a42e8e5fc2 100644 --- a/js/src/vm/Shape-inl.h +++ b/js/src/vm/Shape-inl.h @@ -41,25 +41,27 @@ Shape::search(ExclusiveContext* cx, jsid id) } inline bool -Shape::set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, bool strict, - MutableHandleValue vp) +Shape::set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, MutableHandleValue vp, + ObjectOpResult &result) { MOZ_ASSERT_IF(hasDefaultSetter(), hasGetterValue()); MOZ_ASSERT(!obj->is()); // See bug 1128681. if (attrs & JSPROP_SETTER) { Value fval = setterValue(); - return InvokeGetterOrSetter(cx, receiver, fval, 1, vp.address(), vp); + if (!InvokeGetterOrSetter(cx, receiver, fval, 1, vp.address(), vp)) + return false; + return result.succeed(); } if (attrs & JSPROP_GETTER) - return ReportGetterOnlyAssignment(cx, strict); + return result.fail(JSMSG_GETTER_ONLY); if (!setterOp()) - return true; + return result.succeed(); RootedId id(cx, propid()); - return CallJSPropertyOpSetter(cx, setterOp(), obj, id, strict, vp); + return CallJSPropertyOpSetter(cx, setterOp(), obj, id, vp, result); } /* static */ inline Shape* diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index e54fd13a66..4b2fcb0e75 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -959,8 +959,8 @@ class Shape : public gc::TenuredCell setter() == rawSetter; } - bool set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, bool strict, - MutableHandleValue vp); + bool set(JSContext* cx, HandleNativeObject obj, HandleObject receiver, MutableHandleValue vp, + ObjectOpResult &result); BaseShape* base() const { return base_.get(); } diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index 9dfafb0024..a42325f9bb 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -362,12 +362,13 @@ UnboxedPlainObject::obj_lookupProperty(JSContext* cx, HandleObject obj, /* static */ bool UnboxedPlainObject::obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs) + PropertyOp getter, StrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result) { if (!convertToNative(cx, obj)) return false; - return DefineProperty(cx, obj, id, v, getter, setter, attrs); + return DefineProperty(cx, obj, id, v, getter, setter, attrs, result); } /* static */ bool @@ -409,24 +410,24 @@ UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleObjec /* static */ bool UnboxedPlainObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp, bool strict) + HandleId id, MutableHandleValue vp, ObjectOpResult &result) { const UnboxedLayout& layout = obj->as().layout(); if (const UnboxedLayout::Property* property = layout.lookup(id)) { if (obj == receiver) { if (obj->as().setValue(cx, *property, vp)) - return true; + return result.succeed(); if (!convertToNative(cx, obj)) return false; - return SetProperty(cx, obj, receiver, id, vp, strict); + return SetProperty(cx, obj, receiver, id, vp, result); } - return SetPropertyByDefining(cx, obj, receiver, id, vp, strict, false); + return SetPropertyByDefining(cx, obj, receiver, id, vp, false, result); } - return SetPropertyOnProto(cx, obj, receiver, id, vp, strict); + return SetPropertyOnProto(cx, obj, receiver, id, vp, result); } /* static */ bool diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index 1c32091fc4..1f125c0240 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -162,7 +162,8 @@ class UnboxedPlainObject : public JSObject MutableHandleShape propp); static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v, - PropertyOp getter, StrictPropertyOp setter, unsigned attrs); + PropertyOp getter, StrictPropertyOp setter, unsigned attrs, + ObjectOpResult &result); static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp); @@ -170,7 +171,7 @@ class UnboxedPlainObject : public JSObject HandleId id, MutableHandleValue vp); static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleObject receiver, - HandleId id, MutableHandleValue vp, bool strict); + HandleId id, MutableHandleValue vp, ObjectOpResult &result); static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandle desc); diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 29bea0166e..bb8dd4c936 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,11 +29,11 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 239; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 241; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); -static_assert(JSErr_Limit == 371, +static_assert(JSErr_Limit == 373, "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or " "removed MSG_DEFs from js.msg, you should increment " "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's " diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index dc022668ff..2a4aec7b27 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -357,13 +357,14 @@ sandbox_convert(JSContext* cx, HandleObject obj, JSType type, MutableHandleValue static bool writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, - bool strict, JS::MutableHandleValue vp) + JS::MutableHandleValue vp, JS::ObjectOpResult &result) { RootedObject proto(cx); if (!JS_GetPrototype(cx, obj, &proto)) return false; - return JS_SetPropertyById(cx, proto, id, vp); + RootedValue receiver(cx, ObjectValue(*proto)); + return JS_ForwardSetPropertyTo(cx, proto, id, vp, receiver, result); } static bool @@ -738,10 +739,10 @@ bool xpc::SandboxProxyHandler::set(JSContext* cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, - bool strict, - JS::MutableHandle vp) const + JS::MutableHandle vp, + JS::ObjectOpResult &result) const { - return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp); + return BaseProxyHandler::set(cx, proxy, receiver, id, vp, result); } bool diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp index c0a6cd6618..0d22f5ddeb 100644 --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -659,7 +659,8 @@ static const JSFunctionSpec glob_functions[] = { }; static bool -env_setProperty(JSContext* cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp) +env_setProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp, + ObjectOpResult &result) { /* XXX porting may be easy, but these don't seem to supply setenv by default */ #if !defined SOLARIS @@ -709,7 +710,7 @@ env_setProperty(JSContext* cx, HandleObject obj, HandleId id, bool strict, Mutab } vp.set(STRING_TO_JSVAL(valstr)); #endif /* !defined SOLARIS */ - return true; + return result.succeed(); } static bool diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp index 64da5c0827..5d0e0f0435 100644 --- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp +++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp @@ -364,13 +364,7 @@ DefinePropertyIfFound(XPCCallContext& ccx, AutoResolveName arn(ccx, id); if (resolved) *resolved = true; - return JS_DefinePropertyById(ccx, obj, id, desc.value(), - // Descriptors never store JSNatives - // for accessors: they have either - // JSFunctions or JSPropertyOps. - desc.attributes(), - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter())); + return JS_DefinePropertyById(ccx, obj, id, desc); } } @@ -433,9 +427,10 @@ XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, } static bool -XPC_WN_OnlyIWrite_SetPropertyStub(JSContext* cx, HandleObject obj, HandleId id, bool strict, - MutableHandleValue vp) +XPC_WN_OnlyIWrite_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) { + result.succeed(); return XPC_WN_OnlyIWrite_AddPropertyStub(cx, obj, id, vp); } @@ -454,10 +449,10 @@ XPC_WN_CantDeletePropertyStub(JSContext* cx, HandleObject obj, HandleId id, } static bool -XPC_WN_CannotModifyStrictPropertyStub(JSContext* cx, HandleObject obj, HandleId id, bool strict, - MutableHandleValue vp) +XPC_WN_CannotModifySetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) { - return XPC_WN_CannotModifyPropertyStub(cx, obj, id, vp); + return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx); } static bool @@ -713,9 +708,10 @@ XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, HandleObject obj, HandleId id, } static bool -XPC_WN_MaybeResolvingStrictPropertyStub(JSContext* cx, HandleObject obj, HandleId id, bool strict, - MutableHandleValue vp) +XPC_WN_MaybeResolvingSetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) { + result.succeed(); return XPC_WN_MaybeResolvingPropertyStub(cx, obj, id, vp); } @@ -753,6 +749,11 @@ XPC_WN_MaybeResolvingDeletePropertyStub(JSContext* cx, HandleObject obj, HandleI return Throw(rv, cx); \ return retval; +#define POST_HELPER_STUB_WITH_OBJECTOPRESULT(failMethod) \ + if (NS_FAILED(rv)) \ + return Throw(rv, cx); \ + return retval ? result.succeed() : result.failMethod(); + static bool XPC_WN_Helper_AddProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp) @@ -772,12 +773,12 @@ XPC_WN_Helper_GetProperty(JSContext* cx, HandleObject obj, HandleId id, } bool -XPC_WN_Helper_SetProperty(JSContext* cx, HandleObject obj, HandleId id, bool strict, - MutableHandleValue vp) +XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) { PRE_HELPER_STUB SetProperty(wrapper, cx, obj, id, vp.address(), &retval); - POST_HELPER_STUB + POST_HELPER_STUB_WITH_OBJECTOPRESULT(failReadOnly) } static bool @@ -1029,9 +1030,9 @@ XPCNativeScriptableShared::PopulateJSClass() else if (mFlags.UseJSStubForSetProperty()) setProperty = nullptr; else if (mFlags.AllowPropModsDuringResolve()) - setProperty = XPC_WN_MaybeResolvingStrictPropertyStub; + setProperty = XPC_WN_MaybeResolvingSetPropertyStub; else - setProperty = XPC_WN_CannotModifyStrictPropertyStub; + setProperty = XPC_WN_CannotModifySetPropertyStub; mJSClass.base.setProperty = setProperty; MOZ_ASSERT_IF(mFlags.WantEnumerate(), !mFlags.WantNewEnumerate()); @@ -1358,9 +1359,10 @@ XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext* cx, HandleObject obj, HandleI } static bool -XPC_WN_OnlyIWrite_Proto_SetPropertyStub(JSContext* cx, HandleObject obj, HandleId id, bool strict, - MutableHandleValue vp) +XPC_WN_OnlyIWrite_Proto_SetPropertyStub(JSContext *cx, HandleObject obj, HandleId id, + MutableHandleValue vp, ObjectOpResult &result) { + result.succeed(); return XPC_WN_OnlyIWrite_Proto_AddPropertyStub(cx, obj, id, vp); } diff --git a/js/xpconnect/wrappers/AddonWrapper.cpp b/js/xpconnect/wrappers/AddonWrapper.cpp index 00e33e9333..e30f1315d7 100644 --- a/js/xpconnect/wrappers/AddonWrapper.cpp +++ b/js/xpconnect/wrappers/AddonWrapper.cpp @@ -120,14 +120,15 @@ AddonWrapper::get(JSContext* cx, JS::Handle wrapper, JS::Handle template bool AddonWrapper::set(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject receiver, - JS::HandleId id, bool strict, JS::MutableHandleValue vp) const + JS::HandleId id, JS::MutableHandleValue vp, + JS::ObjectOpResult &result) const { Rooted desc(cx); if (!Interpose(cx, wrapper, nullptr, id, &desc)) return false; if (!desc.object()) - return Base::set(cx, wrapper, receiver, id, strict, vp); + return Base::set(cx, wrapper, receiver, id, vp, result); if (desc.setter()) { MOZ_ASSERT(desc.hasSetterObject()); @@ -135,27 +136,26 @@ AddonWrapper::set(JSContext* cx, JS::HandleObject wrapper, JS::HandleObjec JS::AutoValueVector args(cx); args.append(vp); RootedValue fval(cx, ObjectValue(*desc.setterObject())); - return JS_CallFunctionValue(cx, receiver, fval, args, vp); - } else { - if (!strict) - return true; - - js::ReportErrorWithId(cx, "unable to set interposed data property %s", id); - return false; + if (!JS_CallFunctionValue(cx, receiver, fval, args, vp)) + return false; + return result.succeed(); } + + return result.failCantSetInterposed(); } template bool AddonWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const + MutableHandle desc, + JS::ObjectOpResult &result) const { Rooted interpDesc(cx); if (!Interpose(cx, wrapper, nullptr, id, &interpDesc)) return false; if (!interpDesc.object()) - return Base::defineProperty(cx, wrapper, id, desc); + return Base::defineProperty(cx, wrapper, id, desc, result); js::ReportErrorWithId(cx, "unable to modify interposed property %s", id); return false; diff --git a/js/xpconnect/wrappers/AddonWrapper.h b/js/xpconnect/wrappers/AddonWrapper.h index 401ae4f32e..df39560176 100644 --- a/js/xpconnect/wrappers/AddonWrapper.h +++ b/js/xpconnect/wrappers/AddonWrapper.h @@ -28,12 +28,14 @@ class AddonWrapper : public Base { JS::Handle id, JS::MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, - JS::MutableHandle desc) const override; + JS::MutableHandle desc, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp) const override; virtual bool get(JSContext* cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const override; virtual bool set(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject receiver, - JS::HandleId id, bool strict, JS::MutableHandleValue vp) const override; + JS::HandleId id, JS::MutableHandleValue vp, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle wrapper, JS::Handle id, diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp index 6d407208ed..5b873a0bb7 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.cpp +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.cpp @@ -21,21 +21,22 @@ const ChromeObjectWrapper ChromeObjectWrapper::singleton; bool ChromeObjectWrapper::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc) const + MutableHandle desc, + JS::ObjectOpResult &result) const { if (!AccessCheck::checkPassToPrivilegedCode(cx, wrapper, desc.value())) return false; - return ChromeObjectWrapperBase::defineProperty(cx, wrapper, id, desc); + return ChromeObjectWrapperBase::defineProperty(cx, wrapper, id, desc, result); } bool ChromeObjectWrapper::set(JSContext* cx, HandleObject wrapper, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp) const + MutableHandleValue vp, ObjectOpResult &result) const { if (!AccessCheck::checkPassToPrivilegedCode(cx, wrapper, vp)) return false; - return ChromeObjectWrapperBase::set(cx, wrapper, receiver, id, strict, vp); + return ChromeObjectWrapperBase::set(cx, wrapper, receiver, id, vp, result); } } diff --git a/js/xpconnect/wrappers/ChromeObjectWrapper.h b/js/xpconnect/wrappers/ChromeObjectWrapper.h index cc4a7dcd4e..ffcab3c4cb 100644 --- a/js/xpconnect/wrappers/ChromeObjectWrapper.h +++ b/js/xpconnect/wrappers/ChromeObjectWrapper.h @@ -29,10 +29,12 @@ class ChromeObjectWrapper : public ChromeObjectWrapperBase virtual bool defineProperty(JSContext* cx, JS::Handle wrapper, JS::Handle id, - JS::MutableHandle desc) const override; + JS::MutableHandle desc, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool set(JSContext* cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, - bool strict, JS::MutableHandle vp) const override; + JS::MutableHandle vp, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; static const ChromeObjectWrapper singleton; }; diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 6a58ee2da3..14e0646284 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -234,7 +234,8 @@ CrossOriginXrayWrapper::ownPropertyKeys(JSContext* cx, JS::Handle wra bool CrossOriginXrayWrapper::defineProperty(JSContext* cx, JS::Handle wrapper, JS::Handle id, - JS::MutableHandle desc) const + JS::MutableHandle desc, + JS::ObjectOpResult &result) const { JS_ReportError(cx, "Permission denied to define property on cross-origin object"); return false; diff --git a/js/xpconnect/wrappers/FilteringWrapper.h b/js/xpconnect/wrappers/FilteringWrapper.h index 3436b544e8..df6e1697f6 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.h +++ b/js/xpconnect/wrappers/FilteringWrapper.h @@ -73,7 +73,8 @@ class CrossOriginXrayWrapper : public SecurityXrayDOM { JS::MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, JS::Handle wrapper, JS::Handle id, - JS::MutableHandle desc) const override; + JS::MutableHandle desc, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, JS::Handle wrapper, JS::AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, JS::Handle wrapper, diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 37307a6f78..5824894901 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -568,9 +568,10 @@ JSXrayTraits::delete_(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp bool JSXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, - MutableHandle desc, - Handle existingDesc, - bool* defined) + MutableHandle desc, + Handle existingDesc, + ObjectOpResult &result, + bool *defined) { *defined = false; RootedObject holder(cx, ensureHolder(cx, wrapper)); @@ -613,9 +614,7 @@ JSXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, JSAutoCompartment ac(cx, target); if (!JS_WrapPropertyDescriptor(cx, desc) || - !JS_DefinePropertyById(cx, target, id, desc.value(), - desc.attributes(), - JS_STUBGETTER, JS_STUBSETTER)) + !JS_DefinePropertyById(cx, target, id, desc, result)) { return false; } @@ -1190,13 +1189,7 @@ XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext* cx, HandleObject wr FillPropertyDescriptor(desc, wrapper, 0, ObjectValue(*JS_GetFunctionObject(toString))); - return JS_DefinePropertyById(cx, holder, id, desc.value(), - // Descriptors never store JSNatives for - // accessors: they have either JSFunctions - // or JSPropertyOps. - desc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter())) && + return JS_DefinePropertyById(cx, holder, id, desc) && JS_GetPropertyDescriptorById(cx, holder, id, desc); } @@ -1251,14 +1244,7 @@ XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext* cx, HandleObject wr if (desc.hasSetterObject()) desc.setSetterObject(&fval.toObject()); - // Define the property. - return JS_DefinePropertyById(cx, holder, id, desc.value(), - // Descriptors never store JSNatives for - // accessors: they have either JSFunctions or - // JSPropertyOps. - desc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter())); + return JS_DefinePropertyById(cx, holder, id, desc); } static bool @@ -1397,7 +1383,8 @@ XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsW bool XPCWrappedNativeXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc, - Handle existingDesc, bool* defined) + Handle existingDesc, + JS::ObjectOpResult &result, bool *defined) { *defined = false; RootedObject holder(cx, singleton.ensureHolder(cx, wrapper)); @@ -1407,7 +1394,7 @@ XPCWrappedNativeXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, int32_t index = GetArrayIndexFromId(cx, id); if (IsArrayIndex(index) && IsWindow(cx, wrapper)) { *defined = true; - return true; + return result.succeed(); } return true; @@ -1550,20 +1537,15 @@ DOMXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, Handl if (!desc.object() || !cacheOnHolder) return true; - return JS_DefinePropertyById(cx, holder, id, desc.value(), - // Descriptors never store JSNatives for - // accessors: they have either JSFunctions or - // JSPropertyOps. - desc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter())) && + return JS_DefinePropertyById(cx, holder, id, desc) && JS_GetPropertyDescriptorById(cx, holder, id, desc); } bool DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, MutableHandle desc, - Handle existingDesc, bool* defined) + Handle existingDesc, + JS::ObjectOpResult &result, bool *defined) { // Check for an indexed property on a Window. If that's happening, do // nothing but claim we defined it so it won't get added as an expando. @@ -1571,12 +1553,12 @@ DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id, int32_t index = GetArrayIndexFromId(cx, id); if (IsArrayIndex(index)) { *defined = true; - return true; + return result.succeed(); } } JS::Rooted obj(cx, getTargetObject(wrapper)); - return XrayDefineProperty(cx, wrapper, obj, id, desc, defined); + return XrayDefineProperty(cx, wrapper, obj, id, desc, result, defined); } bool @@ -1874,13 +1856,7 @@ XrayWrapper::getPropertyDescriptor(JSContext* cx, HandleObject wra if (!desc.object()) return true; - if (!JS_DefinePropertyById(cx, holder, id, desc.value(), - // Descriptors never store JSNatives for - // accessors: they have either JSFunctions or - // JSPropertyOps. - desc.attributes() | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(desc.getter()), - JS_PROPERTYOP_SETTER(desc.setter())) || + if (!JS_DefinePropertyById(cx, holder, id, desc) || !JS_GetPropertyDescriptorById(cx, holder, id, desc)) { return false; @@ -1967,8 +1943,8 @@ RecreateLostWaivers(JSContext* cx, JSPropertyDescriptor* orig, template bool XrayWrapper::defineProperty(JSContext* cx, HandleObject wrapper, - HandleId id, MutableHandle desc) - const + HandleId id, MutableHandle desc, + ObjectOpResult &result) const { assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); @@ -1990,17 +1966,17 @@ XrayWrapper::defineProperty(JSContext* cx, HandleObject wrapper, (existing_desc.isReadonly() && !desc.isReadonly())) { // We should technically report non-configurability in strict mode, but - // doing that via JSAPI is a lot of trouble. - return true; + // doing that via JSAPI used to be a lot of trouble. See bug 1135997. + return result.succeed(); } if (existing_desc.isReadonly()) { // Same as the above for non-writability. - return true; + return result.succeed(); } } bool defined = false; - if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, &defined)) + if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, result, &defined)) return false; if (defined) return true; @@ -2025,13 +2001,7 @@ XrayWrapper::defineProperty(JSContext* cx, HandleObject wrapper, if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc)) return false; - return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc.value(), - // Descriptors never store JSNatives for - // accessors: they have either JSFunctions - // or JSPropertyOps. - wrappedDesc.get().attrs | JSPROP_PROPOP_ACCESSORS, - JS_PROPERTYOP_GETTER(wrappedDesc.getter()), - JS_PROPERTYOP_SETTER(wrappedDesc.setter())); + return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc, result); } template @@ -2080,13 +2050,13 @@ template bool XrayWrapper::set(JSContext* cx, HandleObject wrapper, HandleObject receiver, HandleId id, - bool strict, MutableHandleValue vp) const + MutableHandleValue vp, ObjectOpResult &result) const { MOZ_ASSERT(!Traits::HasPrototype); // Skip our Base if it isn't already BaseProxyHandler. // NB: None of the functions we call are prepared for the receiver not // being the wrapper, so ignore the receiver here. - return js::BaseProxyHandler::set(cx, wrapper, wrapper, id, strict, vp); + return js::BaseProxyHandler::set(cx, wrapper, wrapper, id, vp, result); } template diff --git a/js/xpconnect/wrappers/XrayWrapper.h b/js/xpconnect/wrappers/XrayWrapper.h index 69473340fe..dc89daa111 100644 --- a/js/xpconnect/wrappers/XrayWrapper.h +++ b/js/xpconnect/wrappers/XrayWrapper.h @@ -126,7 +126,8 @@ public: JS::MutableHandle desc) override; bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::MutableHandle desc, - JS::Handle existingDesc, bool* defined); + JS::Handle existingDesc, + JS::ObjectOpResult &result, bool *defined); virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags, JS::AutoIdVector& props); static bool call(JSContext* cx, JS::HandleObject wrapper, @@ -177,7 +178,8 @@ public: JS::MutableHandle desc) override; bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::MutableHandle desc, - JS::Handle existingDesc, bool* defined); + JS::Handle existingDesc, + JS::ObjectOpResult &result, bool *defined); virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags, JS::AutoIdVector& props); static bool call(JSContext* cx, JS::HandleObject wrapper, @@ -219,7 +221,8 @@ public: bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::MutableHandle desc, - JS::Handle existingDesc, bool* defined); + JS::Handle existingDesc, + JS::ObjectOpResult &result, bool *defined); virtual bool enumerateNames(JSContext* cx, JS::HandleObject wrapper, unsigned flags, JS::AutoIdVector& props); @@ -336,7 +339,8 @@ public: bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::MutableHandle desc, - JS::Handle existingDesc, bool* defined) + JS::Handle existingDesc, + JS::ObjectOpResult &result, bool *defined) { *defined = false; return true; @@ -408,7 +412,8 @@ class XrayWrapper : public Base { virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle wrapper, JS::Handle id, JS::MutableHandle desc) const override; virtual bool defineProperty(JSContext* cx, JS::Handle wrapper, JS::Handle id, - JS::MutableHandle desc) const override; + JS::MutableHandle desc, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool ownPropertyKeys(JSContext* cx, JS::Handle wrapper, JS::AutoIdVector& props) const override; virtual bool delete_(JSContext* cx, JS::Handle wrapper, @@ -428,7 +433,8 @@ class XrayWrapper : public Base { virtual bool get(JSContext* cx, JS::Handle wrapper, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const override; virtual bool set(JSContext* cx, JS::Handle wrapper, JS::Handle receiver, - JS::Handle id, bool strict, JS::MutableHandle vp) const override; + JS::Handle id, JS::MutableHandle vp, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool call(JSContext* cx, JS::Handle wrapper, const JS::CallArgs& args) const override; virtual bool construct(JSContext* cx, JS::Handle wrapper, @@ -501,7 +507,8 @@ public: virtual bool get(JSContext* cx, JS::Handle proxy, JS::Handle receiver, JS::Handle id, JS::MutableHandle vp) const override; virtual bool set(JSContext* cx, JS::Handle proxy, JS::Handle receiver, - JS::Handle id, bool strict, JS::MutableHandle vp) const override; + JS::Handle id, JS::MutableHandle vp, + JS::ObjectOpResult &result) const MOZ_OVERRIDE; virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle proxy, JS::Handle id,