Backed out 20 changesets (bug 1138499, bug 1148750, bug 1149563, bug 1148652, bug 1123875, bug 1145636, bug 1147660, bug 1148568, bug 1142828) for talos other timeouts CLOSED TREE

Backed out changeset 3fc49391f7fe (bug 1148750)
Backed out changeset 75e867c1c39e (bug 1148750)
Backed out changeset 7f6a4e3976f0 (bug 1148750)
Backed out changeset 91d726011835 (bug 1149563)
Backed out changeset fc7c3cf0e526 (bug 1148652)
Backed out changeset 35a01c7e0f8d (bug 1148652)
Backed out changeset 3125cc5a7a65 (bug 1148652)
Backed out changeset a9f10724b83b (bug 1148568)
Backed out changeset 87132a806ab0 (bug 1147660)
Backed out changeset e6b410c7b847 (bug 1147660)
Backed out changeset c350fe54d9c0 (bug 1147660)
Backed out changeset e4b971996b94 (bug 1147660)
Backed out changeset f221db19fb75 (bug 1147660)
Backed out changeset c4599f0cff00 (bug 1142828)
Backed out changeset c7388a9c3935 (bug 1138499)
Backed out changeset 9b51b38317d6 (bug 1138499)
Backed out changeset ad243a3cd06f (bug 1138499)
Backed out changeset fafda276abd9 (bug 1138499)
Backed out changeset 0a00470fdc2a (bug 1145636)
Backed out changeset 06dbe25231c2 (bug 1123875)

--HG--
extra : amend_source : be3290b09642feacd7f517f1e354126b2e905998
This commit is contained in:
Wes Kocher 2015-04-01 19:17:45 -07:00
parent 6aaa172f43
commit 2f6554bf7c
23 changed files with 526 additions and 642 deletions

View File

@ -23,7 +23,6 @@ skip-if = debug == false
[test_bug963382.html]
skip-if = debug == false
[test_bug1041646.html]
[test_bug1123875.html]
[test_barewordGetsWindow.html]
[test_callback_default_thisval.html]
[test_cloneAndImportNode.html]

View File

@ -1,14 +0,0 @@
<!doctype html>
<meta charset=utf-8>
<title>Test for Bug 1123875</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<div id=log></div>
<script>
test(() => {
assert_throws(new TypeError, () => {
"use strict";
document.childNodes.length = 0;
});
}, "setting a readonly attribute on a proxy in strict mode should throw a TypeError");
</script>

View File

@ -783,7 +783,7 @@ static const JSFunctionSpec sInt64StaticFunctions[] = {
JS_FN("compare", Int64::Compare, 2, CTYPESFN_FLAGS),
JS_FN("lo", Int64::Lo, 1, CTYPESFN_FLAGS),
JS_FN("hi", Int64::Hi, 1, CTYPESFN_FLAGS),
// "join" is defined specially; see InitInt64Class.
JS_FN("join", Int64::Join, 2, CTYPESFN_FLAGS),
JS_FS_END
};
@ -791,7 +791,7 @@ static const JSFunctionSpec sUInt64StaticFunctions[] = {
JS_FN("compare", UInt64::Compare, 2, CTYPESFN_FLAGS),
JS_FN("lo", UInt64::Lo, 1, CTYPESFN_FLAGS),
JS_FN("hi", UInt64::Hi, 1, CTYPESFN_FLAGS),
// "join" is defined specially; see InitInt64Class.
JS_FN("join", UInt64::Join, 2, CTYPESFN_FLAGS),
JS_FS_END
};
@ -1102,8 +1102,10 @@ InitInt64Class(JSContext* cx,
RootedObject ctor(cx, JS_GetConstructor(cx, prototype));
if (!ctor)
return nullptr;
if (!JS_FreezeObject(cx, ctor))
return nullptr;
// Define the 'join' function as an extended native and stash
// Redefine the 'join' function as an extended native and stash
// ctypes.{Int64,UInt64}.prototype in a reserved slot of the new function.
MOZ_ASSERT(clasp == &sInt64ProtoClass || clasp == &sUInt64ProtoClass);
JSNative native = (clasp == &sInt64ProtoClass) ? Int64::Join : UInt64::Join;
@ -1115,8 +1117,6 @@ InitInt64Class(JSContext* cx,
js::SetFunctionNativeReserved(fun, SLOT_FN_INT64PROTO,
OBJECT_TO_JSVAL(prototype));
if (!JS_FreezeObject(cx, ctor))
return nullptr;
if (!JS_FreezeObject(cx, prototype))
return nullptr;

View File

@ -1,8 +0,0 @@
// If Array.of tries to overwrite a non-configurable property, it throws a TypeError.
load(libdir + "asserts.js");
function C() {
Object.defineProperty(this, 0, {value: "v", configurable: false});
}
assertThrowsInstanceOf(() => Array.of.call(C, 1, 2, 3), TypeError);

View File

@ -1,16 +0,0 @@
// Array.of does not overwrite non-configurable properties.
load(libdir + "asserts.js");
var obj;
function C() {
obj = this;
Object.defineProperty(this, 0, {value: "v", configurable: false});
}
try { Array.of.call(C, 1); } catch (e) {}
assertDeepEq(Object.getOwnPropertyDescriptor(obj, 0), {
configurable: false,
enumerable: false,
value: "v",
writable: false
});

View File

@ -1,9 +1,9 @@
# JSAPI Test Suite
=== JSAPI Test Suite
The tests in this directory exercise the JSAPI.
## Building and running the tests
--- Building and running the tests
If you built JS, you already built the tests.
@ -20,18 +20,16 @@ To run the tests in a debugger:
cd $OBJDIR/dist/bin
gdb ./jsapi-tests
--- Creating new tests
## Creating new tests
1. You can either add to an existing test*.cpp file or make a new one.
1. You can either add to an existing test*.cpp file or make a new one.
Copy an existing test and replace the body with your test code.
The test harness provides `cx`, `rt`, and `global` for your use.
2. If you made a new .cpp file, add it to the UNIFIED_SOURCES list
in moz.build.
2. If you made a new .cpp file, add it to the CPPSRCS list in Makefile.in.
## Writing test code
--- Writing test code
Here is a sample test:
@ -137,7 +135,7 @@ tests.h:
Otherwise SameValue(a, b) iff a === b.
## Custom test setup
--- Custom test setup
Before executing each test, the test framework calls the tests' init() member
function, which populates the rt, cx, and global member variables.

View File

@ -7,6 +7,15 @@
#include "jsapi-tests/tests.h"
static const unsigned IgnoreWithValue = JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY |
JSPROP_IGNORE_PERMANENT;
static const unsigned IgnoreAll = IgnoreWithValue | JSPROP_IGNORE_VALUE;
static const unsigned AllowConfigure = IgnoreAll & ~JSPROP_IGNORE_PERMANENT;
static const unsigned AllowEnumerate = IgnoreAll & ~JSPROP_IGNORE_ENUMERATE;
static const unsigned AllowWritable = IgnoreAll & ~JSPROP_IGNORE_READONLY;
static const unsigned ValueWithConfigurable = IgnoreWithValue & ~JSPROP_IGNORE_PERMANENT;
static bool
Getter(JSContext* cx, unsigned argc, JS::Value* vp)
{
@ -42,11 +51,9 @@ BEGIN_TEST(testDefinePropertyIgnoredAttributes)
JS::Rooted<JSPropertyDescriptor> desc(cx);
JS::RootedValue defineValue(cx);
// Try a getter. Allow it to fill in the defaults. Because we're passing a
// JSNative, JS_DefineProperty will infer JSPROP_GETTER even though we
// aren't passing it.
// Try a getter. Allow it to fill in the defaults.
CHECK(JS_DefineProperty(cx, obj, "foo", defineValue,
JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_PERMANENT | JSPROP_SHARED,
IgnoreAll | JSPROP_SHARED,
Getter));
CHECK(JS_GetPropertyDescriptor(cx, obj, "foo", &desc));
@ -56,42 +63,36 @@ BEGIN_TEST(testDefinePropertyIgnoredAttributes)
// Install another configurable property, so we can futz with it.
CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
JSPROP_IGNORE_ENUMERATE | JSPROP_SHARED,
AllowConfigure | JSPROP_SHARED,
Getter));
CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
CHECK(CheckDescriptor(desc, AccessorDescriptor, false, true, true));
// Rewrite the descriptor to now be enumerable, leaving the configurability
// unchanged.
// Rewrite the descriptor to now be enumerable, ensuring that the lack of
// configurablity stayed.
CHECK(JS_DefineProperty(cx, obj, "bar", defineValue,
JSPROP_IGNORE_PERMANENT | JSPROP_ENUMERATE | JSPROP_SHARED,
AllowEnumerate |
JSPROP_ENUMERATE |
JSPROP_SHARED,
Getter));
CHECK(JS_GetPropertyDescriptor(cx, obj, "bar", &desc));
CHECK(CheckDescriptor(desc, AccessorDescriptor, true, true, true));
// Now try the same game with a value property
defineValue.setObject(*obj);
CHECK(JS_DefineProperty(cx, obj, "baz", defineValue,
JSPROP_IGNORE_ENUMERATE |
JSPROP_IGNORE_READONLY |
JSPROP_IGNORE_PERMANENT));
CHECK(JS_DefineProperty(cx, obj, "baz", defineValue, IgnoreWithValue));
CHECK(JS_GetPropertyDescriptor(cx, obj, "baz", &desc));
CHECK(CheckDescriptor(desc, DataDescriptor, false, false, false));
// Now again with a configurable property
CHECK(JS_DefineProperty(cx, obj, "quux", defineValue,
JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY));
CHECK(JS_GetPropertyDescriptor(cx, obj, "quux", &desc));
CHECK(JS_DefineProperty(cx, obj, "quox", defineValue, ValueWithConfigurable));
CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
CHECK(CheckDescriptor(desc, DataDescriptor, false, false, true));
// Just make it writable. Leave the old value and everything else alone.
// Just make it writable. Leave the old value and everythign else alone.
defineValue.setUndefined();
CHECK(JS_DefineProperty(cx, obj, "quux", defineValue,
JSPROP_IGNORE_ENUMERATE |
JSPROP_IGNORE_PERMANENT |
JSPROP_IGNORE_VALUE));
CHECK(JS_GetPropertyDescriptor(cx, obj, "quux", &desc));
CHECK(JS_DefineProperty(cx, obj, "quox", defineValue, AllowWritable));
CHECK(JS_GetPropertyDescriptor(cx, obj, "quox", &desc));
CHECK(CheckDescriptor(desc, DataDescriptor, false, true, true));
CHECK_SAME(JS::ObjectValue(*obj), desc.value());

View File

@ -3438,7 +3438,6 @@ JS_DefineFunctions(JSContext* cx, HandleObject obj, const JSFunctionSpec* fs,
if (flags & JSPROP_DEFINE_LATE)
continue;
}
flags &= ~JSPROP_DEFINE_LATE;
/*
* Define a generic arity N+1 static method for the arity N prototype

View File

@ -2090,7 +2090,7 @@ inline int CheckIsSetterOp(JSSetterOp op);
reinterpret_cast<To>(s))
#define JS_CHECK_ACCESSOR_FLAGS(flags) \
(static_cast<mozilla::EnableIf<((flags) & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0>::Type>(0), \
(static_cast<mozilla::EnableIf<!((flags) & (JSPROP_READONLY | JSPROP_SHARED | JSPROP_PROPOP_ACCESSORS))>::Type>(0), \
(flags))
#define JS_PROPERTYOP_GETTER(v) \
@ -2521,13 +2521,6 @@ class PropertyDescriptorOperations
return (desc()->attrs & bits) != 0;
}
bool hasAll(unsigned bits) const {
return (desc()->attrs & bits) == bits;
}
// Non-API attributes bit used internally for arguments objects.
enum { SHADOWABLE = JSPROP_INTERNAL_USE_BIT };
public:
// Descriptors with JSGetterOp/JSSetterOp are considered data
// descriptors. It's complicated.
@ -2575,59 +2568,6 @@ class PropertyDescriptorOperations
unsigned attributes() const { return desc()->attrs; }
JSGetterOp getter() const { return desc()->getter; }
JSSetterOp setter() const { return desc()->setter; }
void assertValid() const {
#ifdef DEBUG
MOZ_ASSERT((attributes() & ~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE |
JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT |
JSPROP_READONLY | JSPROP_IGNORE_READONLY |
JSPROP_IGNORE_VALUE |
JSPROP_GETTER |
JSPROP_SETTER |
JSPROP_SHARED |
JSPROP_REDEFINE_NONCONFIGURABLE |
SHADOWABLE)) == 0);
MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE));
MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT));
if (isAccessorDescriptor()) {
MOZ_ASSERT(has(JSPROP_SHARED));
MOZ_ASSERT(!has(JSPROP_READONLY));
MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY));
MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE));
MOZ_ASSERT(!has(SHADOWABLE));
MOZ_ASSERT(value().isUndefined());
MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter());
MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter());
} else {
MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY | JSPROP_READONLY));
MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE), value().isUndefined());
}
MOZ_ASSERT(getter() != JS_PropertyStub);
MOZ_ASSERT(setter() != JS_StrictPropertyStub);
#endif
}
void assertComplete() const {
#ifdef DEBUG
assertValid();
MOZ_ASSERT((attributes() & ~(JSPROP_ENUMERATE |
JSPROP_PERMANENT |
JSPROP_READONLY |
JSPROP_GETTER |
JSPROP_SETTER |
JSPROP_SHARED |
JSPROP_REDEFINE_NONCONFIGURABLE |
SHADOWABLE)) == 0);
MOZ_ASSERT_IF(isAccessorDescriptor(), has(JSPROP_GETTER) && has(JSPROP_SETTER));
#endif
}
void assertCompleteIfFound() const {
#ifdef DEBUG
if (object())
assertComplete();
#endif
}
};
template <typename Outer>
@ -2693,19 +2633,7 @@ class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<
value().set(v);
}
void setConfigurable(bool configurable) {
setAttributes((desc()->attrs & ~(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT)) |
(configurable ? 0 : JSPROP_PERMANENT));
}
void setEnumerable(bool enumerable) {
setAttributes((desc()->attrs & ~(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE)) |
(enumerable ? JSPROP_ENUMERATE : 0));
}
void setWritable(bool writable) {
MOZ_ASSERT(!(desc()->attrs & (JSPROP_GETTER | JSPROP_SETTER)));
setAttributes((desc()->attrs & ~(JSPROP_IGNORE_READONLY | JSPROP_READONLY)) |
(writable ? 0 : JSPROP_READONLY));
}
void setEnumerable() { desc()->attrs |= JSPROP_ENUMERATE; }
void setAttributes(unsigned attrs) { desc()->attrs = attrs; }
void setGetter(JSGetterOp op) {
@ -2716,16 +2644,8 @@ class MutablePropertyDescriptorOperations : public PropertyDescriptorOperations<
MOZ_ASSERT(op != JS_StrictPropertyStub);
desc()->setter = op;
}
void setGetterObject(JSObject* obj) {
desc()->getter = reinterpret_cast<JSGetterOp>(obj);
desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY);
desc()->attrs |= JSPROP_GETTER | JSPROP_SHARED;
}
void setSetterObject(JSObject* obj) {
desc()->setter = reinterpret_cast<JSSetterOp>(obj);
desc()->attrs &= ~(JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY);
desc()->attrs |= JSPROP_SETTER | JSPROP_SHARED;
}
void setGetterObject(JSObject* obj) { desc()->getter = reinterpret_cast<JSGetterOp>(obj); }
void setSetterObject(JSObject* obj) { desc()->setter = reinterpret_cast<JSSetterOp>(obj); }
JS::MutableHandleObject getterObject() {
MOZ_ASSERT(this->hasGetterObject());

View File

@ -505,7 +505,7 @@ js::CanonicalizeArrayLengthValue(JSContext* cx, HandleValue v, uint32_t* newLen)
return false;
}
/* ES6 draft rev 34 (2015 Feb 20) 9.4.2.4 ArraySetLength */
/* ES6 20130308 draft 8.4.2.4 ArraySetLength */
bool
js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
unsigned attrs, HandleValue value, ObjectOpResult& result)
@ -515,26 +515,26 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
if (!arr->maybeCopyElementsForWrite(cx))
return false;
// Step 1.
/* Steps 1-2 are irrelevant in our implementation. */
/* Steps 3-5. */
uint32_t newLen;
if (attrs & JSPROP_IGNORE_VALUE) {
// The spec has us calling OrdinaryDefineOwnProperty if
// Desc.[[Value]] is absent, but our implementation is so different that
// this is impossible. Instead, set newLen to the current length and
// proceed to step 9.
newLen = arr->length();
} else {
// Step 2 is irrelevant in our implementation.
if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
return false;
// Steps 3-7.
MOZ_ASSERT_IF(attrs & JSPROP_IGNORE_VALUE, value.isUndefined());
if (!CanonicalizeArrayLengthValue(cx, value, &newLen))
return false;
// Abort if we're being asked to change enumerability or configurability.
// (The length property of arrays is non-configurable, so such attempts
// must fail.) This behavior is spread throughout the ArraySetLength spec
// algorithm, but we only need check it once as our array implementation
// is internally so different from the spec algorithm. (ES5 and ES6 define
// behavior by delegating to the default define-own-property algorithm --
// 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))
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step 8 is irrelevant in our implementation.
}
// Steps 9-11.
/* Steps 6-7. */
bool lengthIsWritable = arr->lengthIsWritable();
#ifdef DEBUG
{
@ -543,21 +543,10 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
MOZ_ASSERT(lengthShape->writable() == lengthIsWritable);
}
#endif
uint32_t oldLen = arr->length();
// Part of steps 1.a, 12.a, and 16: Fail if we're being asked to change
// enumerability or configurability, or otherwise break the object
// invariants. (ES6 checks these by calling OrdinaryDefineOwnProperty, but
// in SM, the array length property is hardly ordinary.)
if ((attrs & (JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT)) == 0 ||
(attrs & (JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE)) == JSPROP_ENUMERATE ||
(attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0 ||
(!lengthIsWritable && (attrs & (JSPROP_READONLY | JSPROP_IGNORE_READONLY)) == 0))
{
return result.fail(JSMSG_CANT_REDEFINE_PROP);
}
// Steps 12-13 for arrays with non-writable length.
/* Steps 8-9 for arrays with non-writable length. */
if (!lengthIsWritable) {
if (newLen == oldLen)
return result.succeed();
@ -565,7 +554,7 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
return result.fail(JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
}
// Step 19.
/* Step 8. */
bool succeeded = true;
do {
// The initialized length and capacity of an array only need updating
@ -627,10 +616,10 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
// If we're removing a relatively small number of elements, just do
// it exactly by the spec.
while (newLen < oldLen) {
// Step 15a.
/* Step 15a. */
oldLen--;
// Steps 15b-d.
/* Steps 15b-d. */
ObjectOpResult deleteSucceeded;
if (!DeleteElement(cx, arr, oldLen, deleteSucceeded))
return false;
@ -690,7 +679,7 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
MOZ_ASSERT(indexes[i] < index, "indexes should never repeat");
index = indexes[i];
// Steps 15b-d.
/* Steps 15b-d. */
ObjectOpResult deleteSucceeded;
if (!DeleteElement(cx, arr, index, deleteSucceeded))
return false;
@ -703,25 +692,22 @@ js::ArraySetLength(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
}
} while (false);
// Update array length. Technically we should have been doing this
// throughout the loop, in step 19.d.iii.
arr->setLength(cx, newLen);
/* Steps 12, 16. */
// Step 20.
if (attrs & JSPROP_READONLY) {
// Yes, we totally drop a non-stub getter/setter from a defineProperty
// API call on the floor here. Given that getter/setter will go away in
// the long run, with accessors replacing them both internally and at the
// API level, just run with this.
RootedShape lengthShape(cx, arr->lookup(cx, id));
if (!NativeObject::changeProperty(cx, arr, lengthShape,
lengthShape->attributes() | JSPROP_READONLY,
array_length_getter, array_length_setter))
{
return false;
}
// Yes, we totally drop a non-stub getter/setter from a defineProperty
// API call on the floor here. Given that getter/setter will go away in
// the long run, with accessors replacing them both internally and at the
// API level, just run with this.
RootedShape lengthShape(cx, arr->lookup(cx, id));
if (!NativeObject::changeProperty(cx, arr, lengthShape, attrs,
JSPROP_PERMANENT | JSPROP_READONLY | JSPROP_SHARED,
array_length_getter, array_length_setter))
{
return false;
}
arr->setLength(cx, newLen);
// All operations past here until the |!succeeded| code must be infallible,
// so that all element fields remain properly synchronized.

View File

@ -7264,7 +7264,7 @@ NewMemoryInfoObject(JSContext* cx)
for (size_t i = 0; i < mozilla::ArrayLength(getters); i++) {
if (!JS_DefineProperty(cx, obj, getters[i].name, UndefinedHandleValue,
JSPROP_ENUMERATE | JSPROP_SHARED,
JSPROP_READONLY | JSPROP_SHARED | JSPROP_ENUMERATE,
getters[i].getter, nullptr))
{
return nullptr;
@ -7294,7 +7294,7 @@ NewMemoryInfoObject(JSContext* cx)
for (size_t i = 0; i < mozilla::ArrayLength(zoneGetters); i++) {
if (!JS_DefineProperty(cx, zoneObj, zoneGetters[i].name, UndefinedHandleValue,
JSPROP_ENUMERATE | JSPROP_SHARED,
JSPROP_READONLY | JSPROP_SHARED | JSPROP_ENUMERATE,
zoneGetters[i].getter, nullptr))
{
return nullptr;

View File

@ -627,8 +627,44 @@ DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id,
MOZ_ASSERT(IsAnyTypedArray(obj));
// Steps 3.a-c.
uint64_t index;
if (IsTypedArrayIndex(id, &index))
return DefineTypedArrayElement(cx, obj, index, desc, result);
if (IsTypedArrayIndex(id, &index)) {
// These are all substeps of 3.c.
// Steps i-vi.
// We (wrongly) ignore out of range defines with a value.
if (index >= AnyTypedArrayLength(obj))
return result.succeed();
// Step vii.
if (desc.isAccessorDescriptor())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step viii.
if (desc.hasConfigurable() && desc.configurable())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step ix.
if (desc.hasEnumerable() && !desc.enumerable())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step x.
if (desc.hasWritable() && !desc.writable())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step xi.
if (desc.hasValue()) {
double d;
if (!ToNumber(cx, desc.value(), &d))
return false;
if (obj->is<TypedArrayObject>())
TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
else
SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
}
// Step xii.
return result.succeed();
}
// Step 4.
return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
@ -814,9 +850,9 @@ js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle<PropertyDescriptor> d
void
js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
{
desc.assertValid();
if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
if (!desc.hasValue())
desc.value().setUndefined();
if (!desc.hasWritable())
desc.attributesRef() |= JSPROP_READONLY;
desc.attributesRef() &= ~(JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
@ -827,11 +863,11 @@ js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
desc.setSetterObject(nullptr);
desc.attributesRef() |= JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
}
if (!desc.hasEnumerable())
desc.attributesRef() &= ~JSPROP_ENUMERATE;
if (!desc.hasConfigurable())
desc.attributesRef() |= JSPROP_PERMANENT;
desc.attributesRef() &= ~(JSPROP_IGNORE_PERMANENT | JSPROP_IGNORE_ENUMERATE);
desc.assertComplete();
}
bool
@ -3087,12 +3123,8 @@ bool
js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
MutableHandle<PropertyDescriptor> desc)
{
if (GetOwnPropertyOp op = obj->getOps()->getOwnPropertyDescriptor) {
bool ok = op(cx, obj, id, desc);
if (ok)
desc.assertCompleteIfFound();
return ok;
}
if (GetOwnPropertyOp op = obj->getOps()->getOwnPropertyDescriptor)
return op(cx, obj, id, desc);
RootedShape shape(cx);
if (!NativeLookupOwnProperty<CanGC>(cx, obj.as<NativeObject>(), id, &shape))
@ -3142,7 +3174,6 @@ js::GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
desc.value().set(value);
desc.object().set(obj);
desc.assertComplete();
return true;
}
@ -3150,7 +3181,6 @@ bool
js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
ObjectOpResult& result)
{
desc.assertValid();
if (DefinePropertyOp op = obj->getOps()->defineProperty)
return op(cx, obj, id, desc, result);
return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
@ -3258,12 +3288,8 @@ js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
RootedObject pobj(cx);
for (pobj = obj; pobj;) {
if (pobj->is<ProxyObject>()) {
bool ok = Proxy::getPropertyDescriptor(cx, pobj, id, desc);
if (ok)
desc.assertCompleteIfFound();
return ok;
}
if (pobj->is<ProxyObject>())
return Proxy::getPropertyDescriptor(cx, pobj, id, desc);
if (!GetOwnPropertyDescriptor(cx, pobj, id, desc))
return false;

View File

@ -840,6 +840,40 @@ Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp)
return true;
}
static inline unsigned
ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
{
/*
* Respect the fact that some callers may want to preserve existing attributes as much as
* possible, or install defaults otherwise.
*/
if (attrs & JSPROP_IGNORE_ENUMERATE) {
attrs &= ~JSPROP_IGNORE_ENUMERATE;
if (enumerable)
attrs |= JSPROP_ENUMERATE;
else
attrs &= ~JSPROP_ENUMERATE;
}
if (attrs & JSPROP_IGNORE_READONLY) {
attrs &= ~JSPROP_IGNORE_READONLY;
// Only update the writability if it's relevant
if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
if (!writable)
attrs |= JSPROP_READONLY;
else
attrs &= ~JSPROP_READONLY;
}
}
if (attrs & JSPROP_IGNORE_PERMANENT) {
attrs &= ~JSPROP_IGNORE_PERMANENT;
if (!configurable)
attrs |= JSPROP_PERMANENT;
else
attrs &= ~JSPROP_PERMANENT;
}
return attrs;
}
extern NativeObject*
InitClass(JSContext* cx, HandleObject obj, HandleObject parent_proto,
const Class* clasp, JSNative constructor, unsigned nargs,

View File

@ -710,16 +710,16 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut
if (!Walk(cx, obj, id, reviver, &newElement))
return false;
ObjectOpResult ignored;
if (newElement.isUndefined()) {
/* Step 2a(iii)(2). The spec deliberately ignores strict failure. */
/* Step 2a(iii)(2). */
ObjectOpResult ignored;
if (!DeleteProperty(cx, obj, id, ignored))
return false;
} else {
/* Step 2a(iii)(3). The spec deliberately ignores strict failure. */
Rooted<PropertyDescriptor> desc(cx);
desc.setDataDescriptor(newElement, JSPROP_ENUMERATE);
if (!StandardDefineProperty(cx, obj, id, desc, ignored))
/* Step 2a(iii)(3). */
// XXX This definition should ignore success/failure, when
// our property-definition APIs indicate that.
if (!DefineProperty(cx, obj, id, newElement))
return false;
}
}
@ -738,16 +738,16 @@ Walk(JSContext* cx, HandleObject holder, HandleId name, HandleValue reviver, Mut
if (!Walk(cx, obj, id, reviver, &newElement))
return false;
ObjectOpResult ignored;
if (newElement.isUndefined()) {
/* Step 2b(ii)(2). The spec deliberately ignores strict failure. */
/* Step 2b(ii)(2). */
ObjectOpResult ignored;
if (!DeleteProperty(cx, obj, id, ignored))
return false;
} else {
/* Step 2b(ii)(3). The spec deliberately ignores strict failure. */
Rooted<PropertyDescriptor> desc(cx);
desc.setDataDescriptor(newElement, JSPROP_ENUMERATE);
if (!StandardDefineProperty(cx, obj, id, desc, ignored))
/* Step 2b(ii)(3). */
// XXX This definition should ignore success/failure, when
// our property-definition APIs indicate that.
if (!DefineProperty(cx, obj, id, newElement))
return false;
}
}

View File

@ -55,7 +55,6 @@ BaseProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
vp.setUndefined();
return true;
}
desc.assertComplete();
MOZ_ASSERT(desc.getter() != JS_PropertyStub);
if (!desc.getter()) {
vp.set(desc.value());
@ -85,7 +84,6 @@ BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValu
Rooted<PropertyDescriptor> ownDesc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc))
return false;
ownDesc.assertCompleteIfFound();
// The rest is factored out into a separate function with a weird name.
// This algorithm continues just below.
@ -187,8 +185,6 @@ BaseProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy
Rooted<PropertyDescriptor> desc(cx);
if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
return false;
desc.assertCompleteIfFound();
if (desc.object() && desc.enumerable())
props[i++].set(id);
}

View File

@ -22,6 +22,16 @@ for (let test of [a,b]) {
assertDeepEq(prototype, desiredPrototype);
}
try {
eval(\`class a {
constructor() { };
static [\"prototype\"]() { };
}\`);
} catch (e if e instanceof TypeError) {
throw new Error("Congrats on making initprop respect non-writable " +
"non-configurable properties. Uncomment the test below " +
"for bonus points.");
/*
// As such, it should by a TypeError to try and overwrite "prototype" with a
// static member. The only way to try is with a computed property name; the rest
// are early errors.
@ -62,6 +72,8 @@ assertThrowsInstanceOf(() => eval(\`(
static set ["prototype"](x) { }
}
)\`), TypeError);
*/
}
`;
if (classesEnabled())

View File

@ -28,6 +28,13 @@ NativeObject::fixedData(size_t nslots) const
return reinterpret_cast<uint8_t*>(&fixedSlots()[nslots]);
}
/* static */ inline bool
NativeObject::changePropertyAttributes(JSContext* cx, HandleNativeObject obj,
HandleShape shape, unsigned attrs)
{
return !!changeProperty(cx, obj, shape, attrs, 0, shape->getter(), shape->setter());
}
inline void
NativeObject::removeLastProperty(ExclusiveContext* cx)
{

View File

@ -1102,6 +1102,118 @@ UpdateShapeTypeAndValue(ExclusiveContext* cx, NativeObject* obj, Shape* shape, c
return true;
}
static bool
NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
HandleValue v, HandleValue receiver, ObjectOpResult& result);
static inline bool
DefinePropertyOrElement(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
GetterOp getter, SetterOp setter, unsigned attrs, HandleValue value,
bool callSetterAfterwards, ObjectOpResult& result)
{
MOZ_ASSERT(getter != JS_PropertyStub);
MOZ_ASSERT(setter != JS_StrictPropertyStub);
/* Use dense storage for new indexed properties where possible. */
if (JSID_IS_INT(id) &&
!getter &&
!setter &&
attrs == JSPROP_ENUMERATE &&
(!obj->isIndexed() || !obj->containsPure(id)) &&
!IsAnyTypedArray(obj))
{
uint32_t index = JSID_TO_INT(id);
if (WouldDefinePastNonwritableLength(obj, index))
return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
NativeObject::EnsureDenseResult edResult = obj->ensureDenseElements(cx, index, 1);
if (edResult == NativeObject::ED_FAILED)
return false;
if (edResult == NativeObject::ED_OK) {
obj->setDenseElementWithType(cx, index, value);
if (!CallAddPropertyHookDense(cx, obj, index, value))
return false;
return result.succeed();
}
}
if (obj->is<ArrayObject>()) {
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
if (id == NameToId(cx->names().length)) {
if (!cx->shouldBeJSContext())
return false;
return ArraySetLength(cx->asJSContext(), arr, id, attrs, value, result);
}
uint32_t index;
if (IdIsIndex(id, &index)) {
if (WouldDefinePastNonwritableLength(obj, index))
return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
}
}
// Don't define new indexed properties on typed arrays.
if (IsAnyTypedArray(obj)) {
uint64_t index;
if (IsTypedArrayIndex(id, &index))
return result.succeed();
}
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, getter, setter,
SHAPE_INVALID_SLOT, attrs, 0));
if (!shape)
return false;
if (!UpdateShapeTypeAndValue(cx, obj, shape, value))
return false;
/*
* Clear any existing dense index after adding a sparse indexed property,
* and investigate converting the object to dense indexes.
*/
if (JSID_IS_INT(id)) {
if (!obj->maybeCopyElementsForWrite(cx))
return false;
uint32_t index = JSID_TO_INT(id);
NativeObject::removeDenseElementForSparseIndex(cx, obj, index);
NativeObject::EnsureDenseResult edResult = NativeObject::maybeDensifySparseElements(cx, obj);
if (edResult == NativeObject::ED_FAILED)
return false;
if (edResult == NativeObject::ED_OK) {
MOZ_ASSERT(!setter);
if (!CallAddPropertyHookDense(cx, obj, index, value))
return false;
return result.succeed();
}
}
if (!CallAddPropertyHook(cx, obj, shape, value))
return false;
if (callSetterAfterwards && setter) {
MOZ_ASSERT(!(attrs & JSPROP_GETTER));
MOZ_ASSERT(!(attrs & JSPROP_SETTER));
if (!cx->shouldBeJSContext())
return false;
RootedValue receiver(cx, ObjectValue(*obj));
return NativeSetExistingDataProperty(cx->asJSContext(), obj, shape, value, receiver,
result);
}
return result.succeed();
}
static unsigned
ApplyOrDefaultAttributes(unsigned attrs, const Shape* shape = nullptr)
{
bool enumerable = shape ? shape->enumerable() : false;
bool writable = shape ? shape->writable() : false;
bool configurable = shape ? shape->configurable() : false;
return ApplyAttributes(attrs, enumerable, writable, configurable);
}
static bool
PurgeProtoChain(ExclusiveContext* cx, JSObject* objArg, HandleId id)
{
@ -1170,152 +1282,95 @@ PurgeScopeChain(ExclusiveContext* cx, HandleObject obj, HandleId id)
return true;
}
static bool
AddOrChangeProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
Handle<PropertyDescriptor> desc)
/*
* Check whether we're redefining away a non-configurable getter, and
* throw if so.
*/
static inline bool
CheckAccessorRedefinition(ExclusiveContext* cx, HandleObject obj, HandleShape shape,
GetterOp getter, SetterOp setter, HandleId id, unsigned attrs)
{
desc.assertComplete();
MOZ_ASSERT(shape->isAccessorDescriptor());
if (shape->configurable() || (getter == shape->getter() && setter == shape->setter()))
return true;
if (!PurgeScopeChain(cx, obj, id))
return false;
// Use dense storage for new indexed properties where possible.
if (JSID_IS_INT(id) &&
!desc.getter() &&
!desc.setter() &&
desc.attributes() == JSPROP_ENUMERATE &&
(!obj->isIndexed() || !obj->containsPure(id)) &&
!IsAnyTypedArray(obj))
/*
* Only allow redefining if JSPROP_REDEFINE_NONCONFIGURABLE is set _and_
* the object is a non-DOM global. The idea is that a DOM object can
* never have such a thing on its proto chain directly on the web, so we
* should be OK optimizing access to accessors found on such an object.
*/
if ((attrs & JSPROP_REDEFINE_NONCONFIGURABLE) &&
obj->is<GlobalObject>() &&
!obj->getClass()->isDOMClass())
{
uint32_t index = JSID_TO_INT(id);
NativeObject::EnsureDenseResult edResult = obj->ensureDenseElements(cx, index, 1);
if (edResult == NativeObject::ED_FAILED)
return false;
if (edResult == NativeObject::ED_OK) {
obj->setDenseElementWithType(cx, index, desc.value());
if (!CallAddPropertyHookDense(cx, obj, index, desc.value()))
return false;
return true;
}
}
RootedShape shape(cx, NativeObject::putProperty(cx, obj, id, desc.getter(), desc.setter(),
SHAPE_INVALID_SLOT, desc.attributes(), 0));
if (!shape)
return false;
if (!UpdateShapeTypeAndValue(cx, obj, shape, desc.value()))
return false;
// Clear any existing dense index after adding a sparse indexed property,
// and investigate converting the object to dense indexes.
if (JSID_IS_INT(id)) {
if (!obj->maybeCopyElementsForWrite(cx))
return false;
uint32_t index = JSID_TO_INT(id);
NativeObject::removeDenseElementForSparseIndex(cx, obj, index);
NativeObject::EnsureDenseResult edResult =
NativeObject::maybeDensifySparseElements(cx, obj);
if (edResult == NativeObject::ED_FAILED)
return false;
if (edResult == NativeObject::ED_OK) {
MOZ_ASSERT(!desc.setter());
return CallAddPropertyHookDense(cx, obj, index, desc.value());
}
}
return CallAddPropertyHook(cx, obj, shape, desc.value());
}
static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; }
static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; }
static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; }
static bool IsAccessorDescriptor(unsigned attrs) {
return (attrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
}
static bool IsDataDescriptor(unsigned attrs) {
MOZ_ASSERT((attrs & (JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY)) == 0);
return !IsAccessorDescriptor(attrs);
}
template <AllowGC allowGC>
static MOZ_ALWAYS_INLINE bool
GetExistingProperty(JSContext* cx,
typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
typename MaybeRooted<Shape*, allowGC>::HandleType shape,
typename MaybeRooted<Value, allowGC>::MutableHandleType vp);
static bool
GetExistingPropertyValue(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
HandleShape shape, MutableHandleValue vp)
{
if (IsImplicitDenseOrTypedArrayElement(shape)) {
vp.set(obj->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
return true;
}
if (!cx->shouldBeJSContext())
if (!cx->isJSContext())
return false;
return GetExistingProperty<CanGC>(cx->asJSContext(), obj, obj, shape, vp);
return Throw(cx->asJSContext(), id, JSMSG_CANT_REDEFINE_PROP);
}
bool
js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
Handle<PropertyDescriptor> desc_,
Handle<JSPropertyDescriptor> desc,
ObjectOpResult& result)
{
desc_.assertValid();
GetterOp getter = desc.getter();
SetterOp setter = desc.setter();
unsigned attrs = desc.attributes();
MOZ_ASSERT(getter != JS_PropertyStub);
MOZ_ASSERT(setter != JS_StrictPropertyStub);
MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS));
// Section numbers and step numbers below refer to ES6 draft rev 36
// (17 March 2015).
//
// This function aims to implement 9.1.6 [[DefineOwnProperty]] as well as
// the [[DefineOwnProperty]] methods described in 9.4.2.1 (arrays), 9.4.4.2
// (arguments), and 9.4.5.3 (typed array views).
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
// Dispense with custom behavior of exotic native objects first.
if (obj->is<ArrayObject>()) {
// 9.4.2.1 step 2. Redefining an array's length is very special.
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
if (id == NameToId(cx->names().length)) {
if (!cx->shouldBeJSContext())
return false;
return ArraySetLength(cx->asJSContext(), arr, id, desc_.attributes(), desc_.value(),
result);
}
// 9.4.2.1 step 3. Don't extend a fixed-length array.
uint32_t index;
if (IdIsIndex(id, &index)) {
if (WouldDefinePastNonwritableLength(obj, index))
return result.fail(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH);
}
} else if (IsAnyTypedArray(obj)) {
// 9.4.5.3 step 3. Indexed properties of typed arrays are special.
uint64_t index;
if (IsTypedArrayIndex(id, &index)) {
if (!cx->shouldBeJSContext())
return false;
return DefineTypedArrayElement(cx->asJSContext(), obj, index, desc_, result);
}
} else if (obj->is<ArgumentsObject>()) {
if (id == NameToId(cx->names().length)) {
// Either we are resolving the .length property on this object, or
// redefining it. In the latter case only, we must set a bit. To
// distinguish the two cases, we note that when resolving, the
// property won't already exist; whereas the first time it is
// redefined, it will.
if (obj->containsPure(id))
obj->as<ArgumentsObject>().markLengthOverridden();
}
}
// 9.1.6.1 OrdinaryDefineOwnProperty steps 1-2.
RootedShape shape(cx);
if (desc_.hasValue()) {
RootedValue updateValue(cx, desc.value());
bool shouldDefine = true;
/*
* If defining a getter or setter, we must check for its counterpart and
* update the attributes and property ops. A getter or setter is really
* only half of a property.
*/
if (desc.isAccessorDescriptor()) {
if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
return false;
if (shape) {
/*
* If we are defining a getter whose setter was already defined, or
* vice versa, finish the job via obj->changeProperty.
*/
if (IsImplicitDenseOrTypedArrayElement(shape)) {
if (IsAnyTypedArray(obj)) {
/* Ignore getter/setter properties added to typed arrays. */
return result.succeed();
}
if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
return false;
shape = obj->lookup(cx, id);
}
if (shape->isAccessorDescriptor()) {
if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
return false;
attrs = ApplyOrDefaultAttributes(attrs, shape);
shape = NativeObject::changeProperty(cx, obj, shape, attrs,
JSPROP_GETTER | JSPROP_SETTER,
(attrs & JSPROP_GETTER)
? getter
: shape->getter(),
(attrs & JSPROP_SETTER)
? setter
: shape->setter());
if (!shape)
return false;
shouldDefine = false;
}
}
} else if (desc.hasValue()) {
// If we did a normal lookup here, it would cause resolve hook recursion in
// the following case. Suppose the first script we run in a lazy global is
// |parseInt()|.
@ -1329,146 +1384,93 @@ js::NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
//
// Therefore we do a special lookup that does not call the resolve hook.
NativeLookupOwnPropertyNoResolve(cx, obj, id, &shape);
if (shape) {
// If any other JSPROP_IGNORE_* attributes are present, copy the
// corresponding JSPROP_* attributes from the existing property.
if (IsImplicitDenseOrTypedArrayElement(shape)) {
attrs = ApplyAttributes(attrs, true, true, !IsAnyTypedArray(obj));
} else {
attrs = ApplyOrDefaultAttributes(attrs, shape);
// Do not redefine a nonconfigurable accessor property.
if (shape->isAccessorDescriptor()) {
if (!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
return false;
}
}
}
} else {
// We have been asked merely to update some attributes. If the
// property already exists and it's a data property, we can just
// call JSObject::changeProperty.
if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
return false;
}
// From this point, the step numbers refer to
// 9.1.6.3, ValidateAndApplyPropertyDescriptor.
// Step 1 is a redundant assertion.
if (shape) {
// Don't forget about arrays.
if (IsImplicitDenseOrTypedArrayElement(shape)) {
if (IsAnyTypedArray(obj)) {
/*
* Silently ignore attempts to change individual index attributes.
* FIXME: Uses the same broken behavior as for accessors. This should
* fail.
*/
return result.succeed();
}
if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
return false;
shape = obj->lookup(cx, id);
}
// Filling in desc: Here we make a copy of the desc_ argument. We will turn
// it into a complete descriptor before updating obj. The spec algorithm
// does not explicitly do this, but the end result is the same. Search for
// "fill in" below for places where the filling-in actually occurs.
Rooted<PropertyDescriptor> desc(cx, desc_);
// Step 2.
if (!shape) {
if (!obj->nonProxyIsExtensible())
return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
// Fill in missing desc fields with defaults.
CompletePropertyDescriptor(&desc);
if (!AddOrChangeProperty(cx, obj, id, desc))
return false;
return result.succeed();
}
// Non-standard hack: Allow redefining non-configurable properties if
// JSPROP_REDEFINE_NONCONFIGURABLE is set _and_ the object is a non-DOM
// global. The idea is that a DOM object can never have such a thing on
// its proto chain directly on the web, so we should be OK optimizing
// access to accessors found on such an object. Bug 1105518 contemplates
// removing this hack.
bool skipRedefineChecks = (desc.attributes() & JSPROP_REDEFINE_NONCONFIGURABLE) &&
obj->is<GlobalObject>() &&
!obj->getClass()->isDOMClass();
// Steps 3-4 are redundant.
// Step 5. We use shapeAttrs as a stand-in for shape in many places below
// since shape might not be a pointer to a real Shape (see
// IsImplicitDenseOrTypedArrayElement).
unsigned shapeAttrs = GetShapeAttributes(obj, shape);
if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks) {
if (desc.hasConfigurable() && desc.configurable())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
if (desc.hasEnumerable() && desc.enumerable() != IsEnumerable(shapeAttrs))
return result.fail(JSMSG_CANT_REDEFINE_PROP);
}
// Fill in desc.[[Configurable]] and desc.[[Enumerable]] if missing.
if (!desc.hasConfigurable())
desc.setConfigurable(IsConfigurable(shapeAttrs));
if (!desc.hasEnumerable())
desc.setEnumerable(IsEnumerable(shapeAttrs));
// Steps 6-9.
if (desc.isGenericDescriptor()) {
// Step 6. No further validation is required.
// Fill in desc. A generic descriptor has none of these fields, so copy
// everything from shape.
MOZ_ASSERT(!desc.hasValue());
MOZ_ASSERT(!desc.hasWritable());
MOZ_ASSERT(!desc.hasGetterObject());
MOZ_ASSERT(!desc.hasSetterObject());
if (IsDataDescriptor(shapeAttrs)) {
RootedValue currentValue(cx);
if (!GetExistingPropertyValue(cx, obj, id, shape, &currentValue))
if (shape->isAccessorDescriptor() &&
!CheckAccessorRedefinition(cx, obj, shape, getter, setter, id, attrs))
{
return false;
desc.setValue(currentValue);
desc.setWritable(IsWritable(shapeAttrs));
} else {
desc.setGetterObject(shape->getterObject());
desc.setSetterObject(shape->setterObject());
}
} else if (desc.isDataDescriptor() != IsDataDescriptor(shapeAttrs)) {
// Step 7.
if (!IsConfigurable(shapeAttrs) && !skipRedefineChecks)
return result.fail(JSMSG_CANT_REDEFINE_PROP);
}
// Fill in desc fields with default values (steps 7.b.i and 7.c.i).
CompletePropertyDescriptor(&desc);
} else if (desc.isDataDescriptor()) {
// Step 8.
bool frozen = !IsConfigurable(shapeAttrs) && !IsWritable(shapeAttrs);
if (frozen && desc.hasWritable() && desc.writable() && !skipRedefineChecks)
return result.fail(JSMSG_CANT_REDEFINE_PROP);
attrs = ApplyOrDefaultAttributes(attrs, shape);
if (frozen || !desc.hasValue()) {
RootedValue currentValue(cx);
if (!GetExistingPropertyValue(cx, obj, id, shape, &currentValue))
return false;
if (!desc.hasValue()) {
// Fill in desc.[[Value]].
desc.setValue(currentValue);
if (shape->isAccessorDescriptor() && !(attrs & JSPROP_IGNORE_READONLY)) {
// ES6 draft 2014-10-14 9.1.6.3 step 7.c: Since [[Writable]]
// is present, change the existing accessor property to a data
// property.
updateValue = UndefinedValue();
} else {
// Step 8.a.ii.1.
bool same;
if (!cx->shouldBeJSContext())
return false;
if (!SameValue(cx->asJSContext(), desc.value(), currentValue, &same))
return false;
if (!same && !skipRedefineChecks)
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// We are at most changing some attributes, and cannot convert
// from data descriptor to accessor, or vice versa. Take
// everything from the shape that we aren't changing.
uint32_t propMask = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
attrs = (shape->attributes() & ~propMask) | (attrs & propMask);
getter = shape->getter();
setter = shape->setter();
if (shape->hasSlot())
updateValue = obj->getSlot(shape->slot());
}
}
if (!desc.hasWritable())
desc.setWritable(IsWritable(shapeAttrs));
} else {
// Step 9. The spec says to use SameValue, but since the values in
// question are objects, we can just compare pointers.
if (desc.hasSetterObject()) {
if (!IsConfigurable(shapeAttrs) &&
desc.setterObject() != shape->setterObject() &&
!skipRedefineChecks)
{
return result.fail(JSMSG_CANT_REDEFINE_PROP);
}
} else {
// Fill in desc.[[Set]] from shape.
desc.setSetterObject(shape->setterObject());
}
if (desc.hasGetterObject()) {
if (!IsConfigurable(shapeAttrs) &&
desc.getterObject() != shape->getterObject() &&
!skipRedefineChecks)
{
return result.fail(JSMSG_CANT_REDEFINE_PROP);
}
} else {
// Fill in desc.[[Get]] from shape.
desc.setGetterObject(shape->getterObject());
}
}
// Step 10.
if (!AddOrChangeProperty(cx, obj, id, desc))
/*
* Purge the property cache of any properties named by id that are about
* to be shadowed in obj's scope chain.
*/
if (!PurgeScopeChain(cx, obj, id))
return false;
if (shouldDefine) {
// Handle the default cases here. Anyone that wanted to set non-default attributes has
// cleared the IGNORE flags by now. Since we can never get here with JSPROP_IGNORE_VALUE
// relevant, just clear it.
attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE;
return DefinePropertyOrElement(cx, obj, id, getter, setter,
attrs, updateValue, false, result);
}
MOZ_ASSERT(shape);
JS_ALWAYS_TRUE(UpdateShapeTypeAndValue(cx, obj, shape, updateValue));
if (!CallAddPropertyHook(cx, obj, shape, updateValue))
return false;
return result.succeed();
}
@ -1946,55 +1948,6 @@ MaybeReportUndeclaredVarAssignment(JSContext* cx, JSString* propname)
JSMSG_UNDECLARED_VAR, bytes.ptr());
}
/*
* Finish assignment to a shapeful data property of a native object obj. This
* conforms to no standard and there is a lot of legacy baggage here.
*/
static bool
NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
HandleValue v, HandleValue receiver, ObjectOpResult& result)
{
MOZ_ASSERT(obj->isNative());
MOZ_ASSERT(shape->isDataDescriptor());
if (shape->hasDefaultSetter()) {
if (shape->hasSlot()) {
// The common path. Standard data property.
// Global properties declared with 'var' will be initially
// defined with an undefined value, so don't treat the initial
// assignments to such properties as overwrites.
bool overwriting = !obj->is<GlobalObject>() || !obj->getSlot(shape->slot()).isUndefined();
obj->setSlotWithType(cx, shape, v, overwriting);
return result.succeed();
}
// Bizarre: shared (slotless) property that's writable but has no
// JSSetterOp. JS code can't define such a property, but it can be done
// through the JSAPI. Treat it as non-writable.
return result.fail(JSMSG_GETTER_ONLY);
}
MOZ_ASSERT(!obj->is<DynamicWithObject>()); // See bug 1128681.
uint32_t sample = cx->runtime()->propertyRemovals;
RootedId id(cx, shape->propid());
RootedValue value(cx, v);
if (!CallJSSetterOp(cx, shape->setterOp(), obj, id, &value, result))
return false;
// Update any slot for the shape with the value produced by the setter,
// unless the setter deleted the shape.
if (shape->hasSlot() &&
(MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
obj->contains(cx, shape)))
{
obj->setSlot(shape->slot(), value);
}
return true; // result is populated by CallJSSetterOp above.
}
/*
* When a [[Set]] operation finds no existing property with the given id
* or finds a writable data property on the prototype chain, we end up here.
@ -2034,6 +1987,17 @@ js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleId id, HandleVa
return false;
}
// If the property doesn't already exist, check for an inextensible
// receiver. (According to the specification, this is supposed to be
// enforced by [[DefineOwnProperty]], but we haven't implemented that yet.)
if (!existing) {
bool extensible;
if (!IsExtensible(cx, receiver, &extensible))
return false;
if (!extensible)
return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
}
// Invalidate SpiderMonkey-specific caches or bail.
const Class* clasp = receiver->getClass();
@ -2050,32 +2014,13 @@ js::SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleId id, HandleVa
JSSetterOp setter = clasp->setProperty;
MOZ_ASSERT(getter != JS_PropertyStub);
MOZ_ASSERT(setter != JS_StrictPropertyStub);
if (!DefineProperty(cx, receiver, id, v, getter, setter, attrs, result))
return false;
if (!receiver->is<NativeObject>())
return DefineProperty(cx, receiver, id, v, getter, setter, attrs, result);
// If the receiver is native, there is one more legacy wrinkle: the class
// JSSetterOp is called after defining the new property.
if (setter && receiver->is<NativeObject>()) {
if (!result)
return true;
Rooted<NativeObject*> nativeReceiver(cx, &receiver->as<NativeObject>());
if (!cx->shouldBeJSContext())
return false;
RootedValue receiverValue(cx, ObjectValue(*receiver));
// This lookup is a bit unfortunate, but not nearly the most
// unfortunate thing about Class getters and setters. Since the above
// DefineProperty call succeeded, receiver is native, and the property
// has a setter (and thus can't be a dense element), this lookup is
// guaranteed to succeed.
RootedShape shape(cx, nativeReceiver->lookup(cx, id));
MOZ_ASSERT(shape);
return NativeSetExistingDataProperty(cx->asJSContext(), nativeReceiver, shape, v,
receiverValue, result);
}
return true;
Rooted<NativeObject*> nativeReceiver(cx, &receiver->as<NativeObject>());
return DefinePropertyOrElement(cx, nativeReceiver, id, getter, setter, attrs, v, true, result);
}
// When setting |id| for |receiver| and |obj| has no property for id, continue
@ -2150,6 +2095,55 @@ SetDenseOrTypedArrayElement(JSContext* cx, HandleNativeObject obj, uint32_t inde
return result.succeed();
}
/*
* Finish assignment to a shapeful data property of a native object obj. This
* conforms to no standard and there is a lot of legacy baggage here.
*/
static bool
NativeSetExistingDataProperty(JSContext* cx, HandleNativeObject obj, HandleShape shape,
HandleValue v, HandleValue receiver, ObjectOpResult& result)
{
MOZ_ASSERT(obj->isNative());
MOZ_ASSERT(shape->isDataDescriptor());
if (shape->hasDefaultSetter()) {
if (shape->hasSlot()) {
// The common path. Standard data property.
// Global properties declared with 'var' will be initially
// defined with an undefined value, so don't treat the initial
// assignments to such properties as overwrites.
bool overwriting = !obj->is<GlobalObject>() || !obj->getSlot(shape->slot()).isUndefined();
obj->setSlotWithType(cx, shape, v, overwriting);
return result.succeed();
}
// Bizarre: shared (slotless) property that's writable but has no
// JSSetterOp. JS code can't define such a property, but it can be done
// through the JSAPI. Treat it as non-writable.
return result.fail(JSMSG_GETTER_ONLY);
}
MOZ_ASSERT(!obj->is<DynamicWithObject>()); // See bug 1128681.
uint32_t sample = cx->runtime()->propertyRemovals;
RootedId id(cx, shape->propid());
RootedValue value(cx, v);
if (!CallJSSetterOp(cx, shape->setterOp(), obj, id, &value, result))
return false;
// Update any slot for the shape with the value produced by the setter,
// unless the setter deleted the shape.
if (shape->hasSlot() &&
(MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
obj->contains(cx, shape)))
{
obj->setSlot(shape->slot(), value);
}
return true; // result is populated by CallJSSetterOp above.
}
/*
* Finish the assignment `receiver[id] = v` when an existing property (shape)
* has been found on a native object (pobj). This implements ES6 draft rev 32

View File

@ -691,8 +691,12 @@ class NativeObject : public JSObject
/* Change the given property into a sibling with the same id in this scope. */
static Shape*
changeProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
unsigned attrs, JSGetterOp getter, JSSetterOp setter);
changeProperty(ExclusiveContext* cx, HandleNativeObject obj,
HandleShape shape, unsigned attrs, unsigned mask,
JSGetterOp getter, JSSetterOp setter);
static inline bool changePropertyAttributes(JSContext* cx, HandleNativeObject obj,
HandleShape shape, unsigned attrs);
/* Remove the property named by id from this object. */
bool removeProperty(ExclusiveContext* cx, jsid id);

View File

@ -866,11 +866,13 @@ NativeObject::putProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleId
/* static */ Shape*
NativeObject::changeProperty(ExclusiveContext* cx, HandleNativeObject obj, HandleShape shape,
unsigned attrs, GetterOp getter, SetterOp setter)
unsigned attrs, unsigned mask, GetterOp getter, SetterOp setter)
{
MOZ_ASSERT(obj->containsPure(shape));
MOZ_ASSERT(getter != JS_PropertyStub);
MOZ_ASSERT(setter != JS_StrictPropertyStub);
attrs |= shape->attrs & mask;
MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
/* Allow only shared (slotless) => unshared (slotful) transition. */

View File

@ -2105,51 +2105,6 @@ js::StringIsTypedArrayIndex(const char16_t* s, size_t length, uint64_t* indexp);
template bool
js::StringIsTypedArrayIndex(const Latin1Char* s, size_t length, uint64_t* indexp);
/* ES6 draft rev 34 (2015 Feb 20) 9.4.5.3 [[DefineOwnProperty]] step 3.c. */
bool
js::DefineTypedArrayElement(JSContext *cx, HandleObject obj, uint64_t index,
Handle<PropertyDescriptor> desc, ObjectOpResult &result)
{
MOZ_ASSERT(IsAnyTypedArray(obj));
// These are all substeps of 3.c.
// Steps i-vi.
// We (wrongly) ignore out of range defines with a value.
if (index >= AnyTypedArrayLength(obj))
return result.succeed();
// Step vii.
if (desc.isAccessorDescriptor())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step viii.
if (desc.hasConfigurable() && desc.configurable())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step ix.
if (desc.hasEnumerable() && !desc.enumerable())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step x.
if (desc.hasWritable() && !desc.writable())
return result.fail(JSMSG_CANT_REDEFINE_PROP);
// Step xi.
if (desc.hasValue()) {
double d;
if (!ToNumber(cx, desc.value(), &d))
return false;
if (obj->is<TypedArrayObject>())
TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
else
SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
}
// Step xii.
return result.succeed();
}
/* JS Friend API */
JS_FRIEND_API(bool)

View File

@ -277,14 +277,6 @@ IsTypedArrayIndex(jsid id, uint64_t* indexp)
return StringIsTypedArrayIndex(s, length, indexp);
}
/*
* Implements [[DefineOwnProperty]] for TypedArrays and SharedTypedArrays
* when the property key is a TypedArray index.
*/
bool
DefineTypedArrayElement(JSContext *cx, HandleObject arr, uint64_t index,
Handle<PropertyDescriptor> desc, ObjectOpResult &result);
static inline unsigned
TypedArrayShift(Scalar::Type viewType)
{

View File

@ -498,16 +498,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
"Underlying object has getter");
is(trickyObject.nonConfigurableGetterSetterProp, undefined,
"Filtering properly over Xray here too");
is((trickyObject.nonConfigurableGetterSetterProp = 'redefined'), 'redefined',
"Assigning to non-configurable prop should fail silently in non-strict mode");
checkThrows(function() {
"use strict";
trickyObject.nonConfigurableGetterSetterProp = 'redefined';
}, /config/, "Should throw when redefining non-configurable prop in strict mode");
trickyObject.nonConfigurableGetterSetterProp = 'redefined';
}, /config/, "Should throw when redefining non-configurable prop");
is(trickyObject.nonConfigurableGetterSetterProp, undefined,
"Redefinition should have failed");
"Redefinition should have thrown");
is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
"Redefinition really should have failed");
"Redefinition really should have thrown");
checkThrows(function() { trickyObject.hasOwnProperty = 33; }, /shadow/,
"Should reject shadowing of pre-existing inherited properties over Xrays");