Bug 1383630 - ScriptedProxyHandler should report which first property a TypeError is thrown for. r=jandem

This commit is contained in:
Alexander J. Vincent 2017-08-03 13:33:02 -07:00
Родитель f5a2166003
Коммит 8e8d3afeee
5 изменённых файлов: 955 добавлений и 119 удалений

Просмотреть файл

@ -402,29 +402,29 @@ MSG_DEF(JSMSG_PROXY_SETPROTOTYPEOF_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy setP
MSG_DEF(JSMSG_PROXY_ISEXTENSIBLE_RETURNED_FALSE,0,JSEXN_TYPEERR,"proxy isExtensible handler must return the same extensibility as target")
MSG_DEF(JSMSG_INCONSISTENT_SETPROTOTYPEOF_TRAP,0,JSEXN_TYPEERR,"proxy setPrototypeOf handler returned true, even though the target's prototype is immutable because the target is non-extensible")
MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 0, JSEXN_TYPEERR, "can't change object's extensibility")
MSG_DEF(JSMSG_CANT_DEFINE_INVALID, 0, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor")
MSG_DEF(JSMSG_CANT_DEFINE_NEW, 0, JSEXN_TYPEERR, "proxy can't define a new property on a non-extensible object")
MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC, 0, JSEXN_TYPEERR, "proxy can't define a non-existent property as non-configurable")
MSG_DEF(JSMSG_CANT_DEFINE_INVALID, 2, JSEXN_TYPEERR, "proxy can't define an incompatible property descriptor ('{0}', {1})")
MSG_DEF(JSMSG_CANT_DEFINE_NEW, 1, JSEXN_TYPEERR, "proxy can't define a new property '{0}' on a non-extensible object")
MSG_DEF(JSMSG_CANT_DEFINE_NE_AS_NC, 1, JSEXN_TYPEERR, "proxy can't define a non-existent '{0}' property as non-configurable")
MSG_DEF(JSMSG_PROXY_DEFINE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy defineProperty handler returned false for property '{0}'")
MSG_DEF(JSMSG_PROXY_DELETE_RETURNED_FALSE, 1, JSEXN_TYPEERR, "can't delete property '{0}': proxy deleteProperty handler returned false")
MSG_DEF(JSMSG_PROXY_PREVENTEXTENSIONS_RETURNED_FALSE, 0, JSEXN_TYPEERR, "proxy preventExtensions handler returned false")
MSG_DEF(JSMSG_PROXY_SET_RETURNED_FALSE, 1, JSEXN_TYPEERR, "proxy set handler returned false for property '{0}'")
MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible")
MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC, 0, JSEXN_TYPEERR, "proxy can't report existing configurable property as non-configurable")
MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 0, JSEXN_TYPEERR, "proxy can't report an existing own property as non-existent on a non-extensible object")
MSG_DEF(JSMSG_CANT_REPORT_INVALID, 0, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor")
MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 0, JSEXN_TYPEERR, "proxy can't report a non-configurable own property as non-existent")
MSG_DEF(JSMSG_CANT_REPORT_NEW, 0, JSEXN_TYPEERR, "proxy can't report a new property on a non-extensible object")
MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 0, JSEXN_TYPEERR, "proxy can't report a non-existent property as non-configurable")
MSG_DEF(JSMSG_CANT_SET_NW_NC, 0, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property")
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter")
MSG_DEF(JSMSG_CANT_SKIP_NC, 0, JSEXN_TYPEERR, "proxy can't skip a non-configurable property")
MSG_DEF(JSMSG_ONWKEYS_STR_SYM, 0, JSEXN_TYPEERR, "proxy [[OwnPropertyKeys]] must return an array with only string and symbol elements")
MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 0, JSEXN_TYPEERR, "proxy must report the same value for a non-writable, non-configurable property")
MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 0, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property without a getter")
MSG_DEF(JSMSG_CANT_REPORT_C_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report existing configurable property '{0}' as non-configurable")
MSG_DEF(JSMSG_CANT_REPORT_E_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report an existing own property '{0}' as non-existent on a non-extensible object")
MSG_DEF(JSMSG_CANT_REPORT_INVALID, 2, JSEXN_TYPEERR, "proxy can't report an incompatible property descriptor ('{0}', {1})")
MSG_DEF(JSMSG_CANT_REPORT_NC_AS_NE, 1, JSEXN_TYPEERR, "proxy can't report a non-configurable own property '{0}' as non-existent")
MSG_DEF(JSMSG_CANT_REPORT_NEW, 1, JSEXN_TYPEERR, "proxy can't report a new property '{0}' on a non-extensible object")
MSG_DEF(JSMSG_CANT_REPORT_NE_AS_NC, 1, JSEXN_TYPEERR, "proxy can't report a non-existent property '{0}' as non-configurable")
MSG_DEF(JSMSG_CANT_SET_NW_NC, 1, JSEXN_TYPEERR, "proxy can't successfully set a non-writable, non-configurable property '{0}'")
MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 1, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property '{0}' without a setter")
MSG_DEF(JSMSG_CANT_SKIP_NC, 1, JSEXN_TYPEERR, "proxy can't skip a non-configurable property '{0}'")
MSG_DEF(JSMSG_OWNKEYS_STR_SYM, 0, JSEXN_TYPEERR, "proxy [[OwnPropertyKeys]] must return an array with only string and symbol elements")
MSG_DEF(JSMSG_MUST_REPORT_SAME_VALUE, 1, JSEXN_TYPEERR, "proxy must report the same value for the non-writable, non-configurable property '{0}'")
MSG_DEF(JSMSG_MUST_REPORT_UNDEFINED, 1, JSEXN_TYPEERR, "proxy must report undefined for a non-configurable accessor property '{0}' without a getter")
MSG_DEF(JSMSG_PROXY_CONSTRUCT_OBJECT, 0, JSEXN_TYPEERR, "proxy [[Construct]] must return an object")
MSG_DEF(JSMSG_PROXY_EXTENSIBILITY, 0, JSEXN_TYPEERR, "proxy must report same extensiblitity as target")
MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 0, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined")
MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 1, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined for property '{0}'")
MSG_DEF(JSMSG_PROXY_REVOKED, 0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
MSG_DEF(JSMSG_PROXY_ARG_REVOKED, 1, JSEXN_TYPEERR, "argument {0} cannot be a revoked proxy")
MSG_DEF(JSMSG_BAD_TRAP, 1, JSEXN_TYPEERR, "proxy handler's {0} trap wasn't undefined, null, or callable")

Просмотреть файл

@ -250,9 +250,9 @@ GetPropertyIfPresent(JSContext* cx, HandleObject obj, HandleId id, MutableHandle
}
bool
js::Throw(JSContext* cx, jsid id, unsigned errorNumber)
js::Throw(JSContext* cx, jsid id, unsigned errorNumber, const char* details)
{
MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == 1);
MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == (details ? 2 : 1));
RootedValue idVal(cx, IdToValue(id));
JSString* idstr = ValueToSource(cx, idVal);
@ -261,7 +261,15 @@ js::Throw(JSContext* cx, jsid id, unsigned errorNumber)
JSAutoByteString bytes(cx, idstr);
if (!bytes)
return false;
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr());
if (details) {
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr(),
details);
}
else {
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr());
}
return false;
}

Просмотреть файл

@ -1391,7 +1391,7 @@ GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method
/* Helpers for throwing. These always return false. */
extern bool
Throw(JSContext* cx, jsid id, unsigned errorNumber);
Throw(JSContext* cx, jsid id, unsigned errorNumber, const char* details = nullptr);
extern bool
Throw(JSContext* cx, JSObject* obj, unsigned errorNumber);

Просмотреть файл

@ -23,14 +23,23 @@ using mozilla::ArrayLength;
// 9.1.6.3 ValidateAndApplyPropertyDescriptor with two additional constant
// arguments. Therefore step numbering is from the latter method, and
// resulting dead code has been removed.
// If an exception should be thrown, we will set errorDetails.
static bool
IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDescriptor> desc,
Handle<PropertyDescriptor> current, bool* bp)
Handle<PropertyDescriptor> current, const char** errorDetails)
{
// precondition: we won't set details if checks pass, so it must be null here.
MOZ_ASSERT(*errorDetails == nullptr);
// Step 2.
if (!current.object()) {
// Step 2a-b,e. As |O| is always undefined, steps 2c-d fall away.
*bp = extensible;
if (!extensible) {
static const char* DETAILS_NOT_EXTENSIBLE =
"proxy can't report an extensible object as non-extensible";
*errorDetails = DETAILS_NOT_EXTENSIBLE;
}
return true;
}
@ -39,7 +48,6 @@ IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDe
!desc.hasGetterObject() && !desc.hasSetterObject() &&
!desc.hasEnumerable() && !desc.hasConfigurable())
{
*bp = true;
return true;
}
@ -51,44 +59,48 @@ IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDe
(!desc.hasEnumerable() || desc.enumerable() == current.enumerable()) &&
(!desc.hasConfigurable() || desc.configurable() == current.configurable()))
{
if (!desc.hasValue()) {
*bp = true;
if (!desc.hasValue())
return true;
}
bool same = false;
if (!SameValue(cx, desc.value(), current.value(), &same))
return false;
if (same) {
*bp = true;
if (same)
return true;
}
}
// Step 5.
if (!current.configurable()) {
// Step 5a.
if (desc.hasConfigurable() && desc.configurable()) {
*bp = false;
static const char* DETAILS_CANT_REPORT_NC_AS_C =
"proxy can't report an existing non-configurable property as configurable";
*errorDetails = DETAILS_CANT_REPORT_NC_AS_C;
return true;
}
// Step 5b.
if (desc.hasEnumerable() && desc.enumerable() != current.enumerable()) {
*bp = false;
static const char* DETAILS_ENUM_DIFFERENT =
"proxy can't report a different 'enumerable' from target when target is not configurable";
*errorDetails = DETAILS_ENUM_DIFFERENT;
return true;
}
}
// Step 6.
if (desc.isGenericDescriptor()) {
*bp = true;
if (desc.isGenericDescriptor())
return true;
}
// Step 7.
if (current.isDataDescriptor() != desc.isDataDescriptor()) {
// Steps 7a, 11. As |O| is always undefined, steps 2b-c fall away.
*bp = current.configurable();
if (!current.configurable()) {
static const char* DETAILS_CURRENT_NC_DIFF_TYPE =
"proxy can't report a different descriptor type when target is not configurable";
*errorDetails = DETAILS_CURRENT_NC_DIFF_TYPE;
}
return true;
}
@ -97,7 +109,9 @@ IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDe
MOZ_ASSERT(desc.isDataDescriptor()); // by step 7
if (!current.configurable() && !current.writable()) {
if (desc.hasWritable() && desc.writable()) {
*bp = false;
static const char* DETAILS_CANT_REPORT_NW_AS_W =
"proxy can't report a non-configurable, non-writable property as writable";
*errorDetails = DETAILS_CANT_REPORT_NW_AS_W;
return true;
}
@ -106,22 +120,33 @@ IsCompatiblePropertyDescriptor(JSContext* cx, bool extensible, Handle<PropertyDe
if (!SameValue(cx, desc.value(), current.value(), &same))
return false;
if (!same) {
*bp = false;
static const char* DETAILS_DIFFERENT_VALUE =
"proxy must report the same value for the non-writable, non-configurable property";
*errorDetails = DETAILS_DIFFERENT_VALUE;
return true;
}
}
}
*bp = true;
return true;
}
// Step 9.
MOZ_ASSERT(current.isAccessorDescriptor()); // by step 8
MOZ_ASSERT(desc.isAccessorDescriptor()); // by step 7
*bp = (current.configurable() ||
((!desc.hasSetterObject() || desc.setter() == current.setter()) &&
(!desc.hasGetterObject() || desc.getter() == current.getter())));
if (current.configurable())
return true;
if (desc.hasSetterObject() && (desc.setter() != current.setter())) {
static const char* DETAILS_SETTERS_DIFFERENT =
"proxy can't report different setters for a currently non-configurable property";
*errorDetails = DETAILS_SETTERS_DIFFERENT;
}
else if (desc.hasGetterObject() && (desc.getter() != current.getter())) {
static const char* DETAILS_GETTERS_DIFFERENT =
"proxy can't report different getters for a currently non-configurable property";
*errorDetails = DETAILS_GETTERS_DIFFERENT;
}
return true;
}
@ -474,10 +499,8 @@ ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy
return false;
// Step 9.
if (!trapResult.isUndefined() && !trapResult.isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_GETOWN_OBJORUNDEF);
return false;
}
if (!trapResult.isUndefined() && !trapResult.isObject())
return js::Throw(cx, id, JSMSG_PROXY_GETOWN_OBJORUNDEF);
// Step 10.
Rooted<PropertyDescriptor> targetDesc(cx);
@ -493,10 +516,8 @@ ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy
}
// Step 11b.
if (!targetDesc.configurable()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
return false;
}
if (!targetDesc.configurable())
return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE);
// Steps 11c-d.
bool extensibleTarget;
@ -504,10 +525,8 @@ ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy
return false;
// Step 11e.
if (!extensibleTarget) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
return false;
}
if (!extensibleTarget)
return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE);
// Step 11f.
desc.object().set(nullptr);
@ -528,27 +547,22 @@ ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy
CompletePropertyDescriptor(&resultDesc);
// Step 15.
bool valid;
if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc, &valid))
const char* errorDetails = nullptr;
if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, resultDesc, targetDesc,
&errorDetails))
return false;
// Step 16.
if (!valid) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_INVALID);
return false;
}
if (errorDetails)
return js::Throw(cx, id, JSMSG_CANT_REPORT_INVALID, errorDetails);
// Step 17.
if (!resultDesc.configurable()) {
if (!targetDesc.object()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NE_AS_NC);
return false;
}
if (!targetDesc.object())
return js::Throw(cx, id, JSMSG_CANT_REPORT_NE_AS_NC);
if (targetDesc.configurable()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_C_AS_NC);
return false;
}
if (targetDesc.configurable())
return js::Throw(cx, id, JSMSG_CANT_REPORT_C_AS_NC);
}
// Step 18.
@ -625,25 +639,27 @@ ScriptedProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId
// Steps 15-16.
if (!targetDesc.object()) {
// Step 15a.
if (!extensibleTarget) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NEW);
return false;
}
if (!extensibleTarget)
return js::Throw(cx, id, JSMSG_CANT_DEFINE_NEW);
// Step 15b.
if (settingConfigFalse) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_NE_AS_NC);
return false;
}
if (settingConfigFalse)
return js::Throw(cx, id, JSMSG_CANT_DEFINE_NE_AS_NC);
} else {
// Steps 16a-b.
bool valid;
if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc, &valid))
// Step 16a.
const char* errorDetails = nullptr;
if (!IsCompatiblePropertyDescriptor(cx, extensibleTarget, desc, targetDesc,
&errorDetails))
return false;
if (!valid || (settingConfigFalse && targetDesc.configurable())) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_DEFINE_INVALID);
return false;
if (errorDetails)
return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, errorDetails);
// Step 16b.
if (settingConfigFalse && targetDesc.configurable()) {
static const char* DETAILS_CANT_REPORT_C_AS_NC =
"proxy can't define an existing configurable property as non-configurable";
return js::Throw(cx, id, JSMSG_CANT_DEFINE_INVALID, DETAILS_CANT_REPORT_C_AS_NC);
}
}
@ -677,7 +693,7 @@ CreateFilteredListFromArrayLike(JSContext* cx, HandleValue v, AutoIdVector& prop
// Step 6c.
if (!next.isString() && !next.isSymbol()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ONWKEYS_STR_SYM);
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OWNKEYS_STR_SYM);
return false;
}
@ -788,10 +804,8 @@ ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdV
auto ptr = uncheckedResultKeys.lookup(targetNonconfigurableKeys[i]);
// Step 17a.
if (!ptr) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SKIP_NC);
return false;
}
if (!ptr)
return js::Throw(cx, targetNonconfigurableKeys[i], JSMSG_CANT_SKIP_NC);
// Step 17b.
uncheckedResultKeys.remove(ptr);
@ -808,20 +822,16 @@ ScriptedProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdV
auto ptr = uncheckedResultKeys.lookup(targetConfigurableKeys[i]);
// Step 19a.
if (!ptr) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
return false;
}
if (!ptr)
return js::Throw(cx, targetConfigurableKeys[i], JSMSG_CANT_REPORT_E_AS_NE);
// Step 19b.
uncheckedResultKeys.remove(ptr);
}
// Step 20.
if (!uncheckedResultKeys.empty()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW);
return false;
}
if (!uncheckedResultKeys.empty())
return js::Throw(cx, uncheckedResultKeys.all().front(), JSMSG_CANT_REPORT_NEW);
// Step 21.
return props.appendAll(trapResult);
@ -933,10 +943,8 @@ ScriptedProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool*
// Step 9b.
if (desc.object()) {
// Step 9b(i).
if (!desc.configurable()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NC_AS_NE);
return false;
}
if (!desc.configurable())
return js::Throw(cx, id, JSMSG_CANT_REPORT_NC_AS_NE);
// Step 9b(ii).
bool extensible;
@ -944,10 +952,8 @@ ScriptedProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool*
return false;
// Step 9b(iii).
if (!extensible) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE);
return false;
}
if (!extensible)
return js::Throw(cx, id, JSMSG_CANT_REPORT_E_AS_NE);
}
}
@ -1011,18 +1017,17 @@ ScriptedProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receive
bool same;
if (!SameValue(cx, trapResult, desc.value(), &same))
return false;
if (!same) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_SAME_VALUE);
return false;
}
if (!same)
return js::Throw(cx, id, JSMSG_MUST_REPORT_SAME_VALUE);
}
// Step 10b.
if (desc.isAccessorDescriptor() && !desc.configurable() && desc.getterObject() == nullptr) {
if (!trapResult.isUndefined()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MUST_REPORT_UNDEFINED);
return false;
}
if (desc.isAccessorDescriptor() &&
!desc.configurable() &&
(desc.getterObject() == nullptr) &&
!trapResult.isUndefined())
{
return js::Throw(cx, id, JSMSG_MUST_REPORT_UNDEFINED);
}
}
@ -1091,17 +1096,13 @@ ScriptedProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, Handle
bool same;
if (!SameValue(cx, v, desc.value(), &same))
return false;
if (!same) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_NW_NC);
return false;
}
if (!same)
return js::Throw(cx, id, JSMSG_CANT_SET_NW_NC);
}
// Step 11b.
if (desc.isAccessorDescriptor() && !desc.configurable() && desc.setterObject() == nullptr) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_WO_SETTER);
return false;
}
if (desc.isAccessorDescriptor() && !desc.configurable() && desc.setterObject() == nullptr)
return js::Throw(cx, id, JSMSG_CANT_SET_WO_SETTER);
}
// Step 12.

Просмотреть файл

@ -0,0 +1,827 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
/* These tests are not checking whether an exception is thrown or not for
* proxies: those tests should already exist in js/src/tests/ecma_6/Proxy .
* We expect TypeErrors to be thrown in these tests, with a stringification
* of the error message showing whatever property name the error is being
* reported for.
*
* Beyond the presence of the property name, these tests do not care about the
* contents of the message.
*
* The reason for requiring the property name is simple: with ECMAScript
* proxies, it can be really hard to figure out what little assertion causes a
* TypeError in the first place.
*/
"use strict";
function assertThrowsTypeErrorIncludes(f, propStr, details) {
var fullmsg;
try {
f();
}
catch (exc) {
if (!(exc instanceof TypeError))
fullmsg =
"Assertion failed: expected TypeError, got " + exc;
else if (!exc.message.includes(propStr))
fullmsg =
`Assertion failed: expected TypeError message '${exc.message}' to include '${propStr}'`;
else if (details && !exc.message.includes(details))
fullmsg =
`Assertion failed: expected TypeError message '${exc.message}' to include '${details}'`;
else
return;
}
if (fullmsg === undefined) {
fullmsg =
"Assertion failed: expected TypeError, no exception thrown";
}
throw new Error(fullmsg);
}
const STR = "one", STR_NAME = `"one"`;
const SYM = Symbol("two"), SYM_NAME = `'Symbol("two")'`;
function errorHasPropertyTests(test) {
assertThrowsTypeErrorIncludes(() => test(STR), STR_NAME);
assertThrowsTypeErrorIncludes(() => test(SYM), SYM_NAME);
}
function errorHasPropertyTestsWithDetails(test) {
let [throwable, details] = test(STR);
assertThrowsTypeErrorIncludes(throwable, STR_NAME, details);
[throwable, details] = test(SYM);
assertThrowsTypeErrorIncludes(throwable, SYM_NAME, details);
}
// getOwnPropertyDescriptor
function testGetOwnPropertyDescriptor_OBJORUNDEF(propName) {
// JSMSG_PROXY_GETOWN_OBJORUNDEF
const h = {
getOwnPropertyDescriptor: () => 2
};
const t = {};
const p = new Proxy(t, h);
Reflect.getOwnPropertyDescriptor(p, propName);
}
function testGetOwnPropertyDescriptor_NC_AS_NE(propName) {
// JSMSG_CANT_REPORT_NC_AS_NE
const h = {
getOwnPropertyDescriptor: () => undefined
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
Reflect.getOwnPropertyDescriptor(p, propName);
}
function testGetOwnPropertyDescriptor_E_AS_NE(propName) {
// JSMSG_CANT_REPORT_E_AS_NE
const h = {
getOwnPropertyDescriptor: () => undefined,
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
Reflect.preventExtensions(t);
const p = new Proxy(t, h);
Reflect.getOwnPropertyDescriptor(p, propName);
}
function testGetOwnPropertyDescriptor_NE_AS_NC(propName) {
// JSMSG_CANT_REPORT_NE_AS_NC
const h = {
getOwnPropertyDescriptor: function() {
return {
value: 1,
writable: true,
enumerable: true,
configurable: false
};
}
};
const t = {};
const p = new Proxy(t, h);
Reflect.getOwnPropertyDescriptor(p, propName);
}
function testGetOwnPropertyDescriptor_C_AS_NC(propName) {
// JSMSG_CANT_REPORT_C_AS_NC
const h = {
getOwnPropertyDescriptor: function() {
return {
value: 1,
writable: true,
enumerable: true,
configurable: false // here's the difference
};
}
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
const p = new Proxy(t, h);
Reflect.getOwnPropertyDescriptor(p, propName);
}
function testGetOwnPropertyDescriptor_INVALID_NOT_EXTENSIBLE(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_NOT_EXTENSIBLE
const h = {
getOwnPropertyDescriptor: function() {
return {
value: 1,
writable: true,
enumerable: true,
configurable: true
};
}
};
const t = {};
Reflect.preventExtensions(t);
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy can't report an extensible object as non-extensible"];
}
function testGetOwnPropertyDescriptor_INVALID_C_AS_NC(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_CANT_REPORT_NC_AS_C
const h = {
getOwnPropertyDescriptor: function() {
return {
value: 1,
writable: true,
enumerable: true,
configurable: true
};
}
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy can't report an existing non-configurable property as configurable"];
}
function testGetOwnPropertyDescriptor_INVALID_ENUM_DIFFERENT_CURRENT(cEnumerable, propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_ENUM_DIFFERENT
const h = {
getOwnPropertyDescriptor: function() {
return {
value: 1,
writable: true,
enumerable: !cEnumerable,
configurable: false
};
}
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: cEnumerable,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy can't report a different 'enumerable' from target when target is not configurable"];
}
function testGetOwnPropertyDescriptor_INVALID_CURRENT_NC_DIFF_TYPE(cAccessor, propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_CURRENT_NC_DIFF_TYPE
const accDesc = {
get: () => 1,
enumerable: true,
configurable: false,
};
const dataDesc = {
value: 1,
writable: true,
enumerable: true,
configurable: false
};
const h = {
getOwnPropertyDescriptor: () => { return (cAccessor ? dataDesc : accDesc); }
};
const t = {};
Reflect.defineProperty(t, propName, cAccessor ? accDesc : dataDesc);
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy can't report a different descriptor type when target is not configurable"];
}
function testGetOwnPropertyDescriptor_INVALID_NW_AS_W(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_CANT_REPORT_NW_AS_W
const h = {
getOwnPropertyDescriptor: function() {
return {
value: 1,
writable: true,
enumerable: true,
configurable: false
};
}
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 2,
writable: false,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy can't report a non-configurable, non-writable property as writable"];
}
function testGetOwnPropertyDescriptor_INVALID_DIFFERENT_VALUE(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_DIFFERENT_VALUE
const h = {
getOwnPropertyDescriptor: function() {
return {
value: 1,
writable: false,
enumerable: true,
configurable: false
};
}
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 2,
writable: false,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy must report the same value for the non-writable, non-configurable property"];
}
function testGetOwnPropertyDescriptor_INVALID_SETTERS_DIFFERENT(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_SETTERS_DIFFERENT
const g = () => 1;
const h = {
getOwnPropertyDescriptor: function() {
return {
get: g,
set: () => 2,
enumerable: true,
configurable: false
};
}
};
const t = {};
Reflect.defineProperty(t, propName, {
get: g,
set: () => 2,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy can't report different setters for a currently non-configurable property"];
}
function testGetOwnPropertyDescriptor_INVALID_GETTERS_DIFFERENT(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_GETTERS_DIFFERENT
const h = {
getOwnPropertyDescriptor: function() {
return {
get: () => 2,
enumerable: true,
configurable: false
};
}
};
const t = {};
Reflect.defineProperty(t, propName, {
get: () => 2,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.getOwnPropertyDescriptor(p, propName); },
"proxy can't report different getters for a currently non-configurable property"];
}
// defineProperty
function testDefineProperty_CANT_DEFINE_NEW(propName) {
// JSMSG_CANT_DEFINE_NEW
const h = {
defineProperty: () => true
};
const t = {};
Reflect.preventExtensions(t);
const p = new Proxy(t, h);
Reflect.defineProperty(p, propName, {});
}
function testDefineProperty_NE_AS_NC(propName) {
// JSMSG_CANT_DEFINE_NE_AS_NC
const h = {
defineProperty: () => true
};
const t = {};
const p = new Proxy(t, h);
Reflect.defineProperty(p, propName, {
value: 1,
enumerable: true,
writable: true,
configurable: false,
});
}
/* Reflect.defineProperty(proxy, propName, desc) cannot throw
* JSMSG_CANT_REPORT_INVALID with DETAILS_NOT_EXTENSIBLE. Here's why:
*
* To throw with DETAILS_NOT_EXTENSIBLE, current must be undefined and the
* target must not be extensible, inside ValidateAndApplyPropertyDescriptor.
*
* ValidateAndApplyPropertyDescriptor's current is also
* IsCompatiblePropertyDescriptor's current, and therefore also
* targetDesc in [[DefineOwnProperty]] for proxies at step 16b.
*
* BUT step 16 is not reached if targetDesc in [[DefineOwnProperty]] is
* undefined: instead step 15 is invoked. QED.
*/
function testDefineProperty_INVALID_NC_AS_C(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_CANT_REPORT_NC_AS_C
const h = {
defineProperty: function() {
return true;
}
};
const newDesc = {
value: 1,
writable: true,
enumerable: true,
configurable: true
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, newDesc); },
"proxy can't report an existing non-configurable property as configurable"];
}
function testDefineProperty_INVALID_ENUM_DIFFERENT_CURRENT(cEnumerable, propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_ENUM_DIFFERENT
const h = {
defineProperty: function() {
return true;
}
};
const newDesc = {
value: 1,
writable: true,
enumerable: !cEnumerable,
configurable: false
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: cEnumerable,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, newDesc); },
"proxy can't report a different 'enumerable' from target when target is not configurable"];
}
function testDefineProperty_INVALID_CURRENT_NC_DIFF_TYPE(cAccessor, propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_CURRENT_NC_DIFF_TYPE
const accDesc = {
get: () => 1,
enumerable: true,
configurable: false,
};
const dataDesc = {
value: 1,
writable: true,
enumerable: true,
configurable: false
};
const h = {
defineProperty: () => true,
};
const t = {};
Reflect.defineProperty(t, propName, cAccessor ? accDesc : dataDesc);
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, cAccessor ? dataDesc : accDesc); },
"proxy can't report a different descriptor type when target is not configurable"];
}
function testDefineProperty_INVALID_NW_AS_W(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_CANT_REPORT_NW_AS_W
const h = {
defineProperty: function() {
return true;
}
};
const newDesc = {
value: 1,
writable: true,
enumerable: true,
configurable: false
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 2,
writable: false,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, newDesc); },
"proxy can't report a non-configurable, non-writable property as writable"];
}
function testDefineProperty_INVALID_DIFFERENT_VALUE(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_DIFFERENT_VALUE
const h = {
defineProperty: function() {
return true;
}
};
const newDesc = {
value: 1,
writable: false,
enumerable: true,
configurable: false
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 2,
writable: false,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, newDesc); },
"proxy must report the same value for the non-writable, non-configurable property"];
}
function testDefineProperty_INVALID_SETTERS_DIFFERENT(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_SETTERS_DIFFERENT
const g = () => 1;
const h = {
defineProperty: function() {
return true;
}
};
const newDesc = {
get: g,
set: () => 2,
enumerable: true,
configurable: false
};
const t = {};
Reflect.defineProperty(t, propName, {
get: g,
set: () => 2,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, newDesc); },
"proxy can't report different setters for a currently non-configurable property"];
}
function testDefineProperty_INVALID_GETTERS_DIFFERENT(propName) {
// JSMSG_CANT_REPORT_INVALID, DETAILS_GETTERS_DIFFERENT
const h = {
defineProperty: function() {
return true;
}
};
const newDesc = {
get: () => 2,
enumerable: true,
configurable: false
};
const t = {};
Reflect.defineProperty(t, propName, {
get: () => 2,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, newDesc); },
"proxy can't report different getters for a currently non-configurable property"];
}
function testDefineProperty_INVALID_C_AS_NC(propName) {
const h = {
defineProperty: function() {
return true;
}
};
const newDesc = {
value: 1,
writable: true,
enumerable: true,
configurable: false
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
const p = new Proxy(t, h);
return [() => { Reflect.defineProperty(p, propName, newDesc); },
"proxy can't define an existing configurable property as non-configurable"];
}
// ownKeys
function testOwnKeys_CANT_SKIP_NC(propName) {
// JSMSG_CANT_SKIP_NC
const h = {
ownKeys: () => []
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
Reflect.ownKeys(p);
}
function testOwnKeys_E_AS_NE(propName) {
// JSMSG_CANT_REPORT_E_AS_NE
const h = {
ownKeys: () => []
};
const t = {};
Reflect.defineProperty(t, propName, {
configurable: true,
value: 1,
writable: true,
enumerable: true
});
Reflect.preventExtensions(t);
const p = new Proxy(t, h);
Reflect.ownKeys(p);
}
// has
function testHas_NC_AS_NE(propName) {
// JSMSG_CANT_REPORT_NC_AS_NE
const h = {
has: () => undefined
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
Reflect.has(p, propName);
}
function testHas_E_AS_NE(propName) {
// JSMSG_CANT_REPORT_E_AS_NE
const h = {
has: () => undefined
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: true,
enumerable: true,
configurable: true
});
Reflect.preventExtensions(t);
const p = new Proxy(t, h);
Reflect.has(p, propName);
}
// get
function testGet_SAME_VALUE(propName) {
const h = {
get: () => 2
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: false,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
Reflect.get(p, propName);
}
function testGet_MUST_REPORT_UNDEFINED(propName) {
// JSMSG_MUST_REPORT_UNDEFINED
const h = {
get: () => 2
};
const t = {};
Reflect.defineProperty(t, propName, {
set: () => { /* do nothing */},
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
Reflect.get(p, propName);
}
// set
function testSet_CANT_SET_NW_NC(propName) {
// JSMSG_CANT_SET_NW_NC
const h = {
set: () => true,
};
const t = {};
Reflect.defineProperty(t, propName, {
value: 1,
writable: false,
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
Reflect.set(p, propName, 3);
}
function testSet_WO_SETTER(propName) {
// JSMSG_MUST_REPORT_UNDEFINED
const h = {
set: () => true
};
const t = {};
Reflect.defineProperty(t, propName, {
get: () => { /* do nothing */},
enumerable: true,
configurable: false
});
const p = new Proxy(t, h);
Reflect.set(p, propName, 1);
}
// test sequence
[
testGetOwnPropertyDescriptor_OBJORUNDEF,
testGetOwnPropertyDescriptor_NC_AS_NE,
testGetOwnPropertyDescriptor_E_AS_NE,
testGetOwnPropertyDescriptor_NE_AS_NC,
testGetOwnPropertyDescriptor_C_AS_NC,
testDefineProperty_CANT_DEFINE_NEW,
testDefineProperty_NE_AS_NC,
testOwnKeys_CANT_SKIP_NC,
testOwnKeys_E_AS_NE,
testHas_NC_AS_NE,
testHas_E_AS_NE,
testGet_SAME_VALUE,
testGet_MUST_REPORT_UNDEFINED,
testSet_CANT_SET_NW_NC,
testSet_WO_SETTER,
].forEach(errorHasPropertyTests);
[
testGetOwnPropertyDescriptor_INVALID_NOT_EXTENSIBLE,
testGetOwnPropertyDescriptor_INVALID_C_AS_NC,
testGetOwnPropertyDescriptor_INVALID_ENUM_DIFFERENT_CURRENT.bind(null, true),
testGetOwnPropertyDescriptor_INVALID_ENUM_DIFFERENT_CURRENT.bind(null, false),
testGetOwnPropertyDescriptor_INVALID_CURRENT_NC_DIFF_TYPE.bind(null, true),
testGetOwnPropertyDescriptor_INVALID_CURRENT_NC_DIFF_TYPE.bind(null, false),
testGetOwnPropertyDescriptor_INVALID_NW_AS_W,
testGetOwnPropertyDescriptor_INVALID_DIFFERENT_VALUE,
testGetOwnPropertyDescriptor_INVALID_SETTERS_DIFFERENT,
testGetOwnPropertyDescriptor_INVALID_GETTERS_DIFFERENT,
testDefineProperty_INVALID_NC_AS_C,
testDefineProperty_INVALID_ENUM_DIFFERENT_CURRENT.bind(null, true),
testDefineProperty_INVALID_ENUM_DIFFERENT_CURRENT.bind(null, false),
testDefineProperty_INVALID_CURRENT_NC_DIFF_TYPE.bind(null, true),
testDefineProperty_INVALID_CURRENT_NC_DIFF_TYPE.bind(null, false),
testDefineProperty_INVALID_NW_AS_W,
testDefineProperty_INVALID_DIFFERENT_VALUE,
testDefineProperty_INVALID_SETTERS_DIFFERENT,
testDefineProperty_INVALID_GETTERS_DIFFERENT,
testDefineProperty_INVALID_C_AS_NC,
].forEach(errorHasPropertyTestsWithDetails);
reportCompare(0, 0, 'ok');