mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 688852 - Rewrite Array.prototype.concat to use spec steps; r=Waldo
--HG-- extra : rebase_source : b82607aabd4f959e7c204073a440225c6a12da6c
This commit is contained in:
parent
180aaf7726
commit
a914924b84
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/RangedPtr.h"
|
||||||
#include "mozilla/TypeTraits.h"
|
#include "mozilla/TypeTraits.h"
|
||||||
|
|
||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
@ -337,6 +338,14 @@ class MOZ_STACK_CLASS CallArgsBase :
|
|||||||
return i < argc_ && !this->argv_[i].isUndefined();
|
return i < argc_ && !this->argv_[i].isUndefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns thisv and args in a single array. (That is, a pointer to |this|
|
||||||
|
* followed by any provided arguments.)
|
||||||
|
*/
|
||||||
|
mozilla::RangedPtr<const Value> thisAndArgs() const {
|
||||||
|
return mozilla::RangedPtr<const Value>(this->argv_ - 1, argc_ + 1);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// These methods are publicly exposed, but we're less sure of the interface
|
// These methods are publicly exposed, but we're less sure of the interface
|
||||||
// here than we'd like (because they're hackish and drop assertions). Try
|
// here than we'd like (because they're hackish and drop assertions). Try
|
||||||
|
4
js/src/jit-test/tests/arrays/concat-define-element.js
Normal file
4
js/src/jit-test/tests/arrays/concat-define-element.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
var bigarr = [];
|
||||||
|
bigarr[5000] = 17;
|
||||||
|
Object.defineProperty(Object.prototype, 5000, { set: function() { throw 42; } });
|
||||||
|
assertEq([].concat(bigarr).length, 5001);
|
@ -0,0 +1,2 @@
|
|||||||
|
Object.prototype[0] = "foo";
|
||||||
|
assertEq([/* hole */, 5].concat().hasOwnProperty("0"), true);
|
104
js/src/jit-test/tests/arrays/concat-overflow.js
Normal file
104
js/src/jit-test/tests/arrays/concat-overflow.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
function testOverflowOneSplat() {
|
||||||
|
var threw = false;
|
||||||
|
var c;
|
||||||
|
try {
|
||||||
|
c = (new Array(4294967295)).concat(['a']);
|
||||||
|
} catch(e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assertEq(threw, false);
|
||||||
|
assertEq(c[4294967295], 'a');
|
||||||
|
assertEq(c.length, 0);
|
||||||
|
}
|
||||||
|
testOverflowOneSplat();
|
||||||
|
|
||||||
|
function testOverflowOneArg() {
|
||||||
|
var threw = false;
|
||||||
|
var c;
|
||||||
|
try {
|
||||||
|
c = (new Array(4294967295)).concat('a');
|
||||||
|
} catch(e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assertEq(threw, false);
|
||||||
|
assertEq(c[4294967295], 'a');
|
||||||
|
assertEq(c.length, 0);
|
||||||
|
}
|
||||||
|
testOverflowOneArg();
|
||||||
|
|
||||||
|
function testOverflowManySplat() {
|
||||||
|
var threw = false;
|
||||||
|
var c;
|
||||||
|
try {
|
||||||
|
c = (new Array(4294967294)).concat(['a', 'b', 'c']);
|
||||||
|
} catch(e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assertEq(threw, false);
|
||||||
|
assertEq(c[4294967294], 'a');
|
||||||
|
assertEq(c[4294967295], 'b');
|
||||||
|
assertEq(c[4294967296], 'c');
|
||||||
|
assertEq(c.length, 4294967295);
|
||||||
|
}
|
||||||
|
testOverflowManySplat();
|
||||||
|
|
||||||
|
function testOverflowManyArg() {
|
||||||
|
var threw = false;
|
||||||
|
var c;
|
||||||
|
try {
|
||||||
|
c = (new Array(4294967294)).concat('a', 'b', 'c');
|
||||||
|
} catch(e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assertEq(threw, false);
|
||||||
|
assertEq(c[4294967294], 'a');
|
||||||
|
assertEq(c[4294967295], 'b');
|
||||||
|
assertEq(c[4294967296], 'c');
|
||||||
|
assertEq(c.length, 4294967295);
|
||||||
|
}
|
||||||
|
testOverflowManyArg();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure we run for side-effects, even when overflowing.
|
||||||
|
*/
|
||||||
|
var count;
|
||||||
|
function MakeFunky() {
|
||||||
|
var o = new Array();
|
||||||
|
Object.defineProperty(o, "0", { get: function() { count++; return 'a'; } });
|
||||||
|
o.length = 1;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testReadWhenOverflowing() {
|
||||||
|
count = 0;
|
||||||
|
var threw = false;
|
||||||
|
var c;
|
||||||
|
try {
|
||||||
|
c = (new Array(4294967294)).concat(MakeFunky(), MakeFunky(), MakeFunky());
|
||||||
|
} catch(e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assertEq(threw, false);
|
||||||
|
assertEq(c[4294967294], 'a');
|
||||||
|
assertEq(c[4294967295], 'a');
|
||||||
|
assertEq(c[4294967296], 'a');
|
||||||
|
assertEq(c.length, 4294967295);
|
||||||
|
assertEq(count, 3);
|
||||||
|
}
|
||||||
|
testReadWhenOverflowing();
|
||||||
|
|
||||||
|
function testDenseFastpathCallsGetters() {
|
||||||
|
count = 0;
|
||||||
|
var threw = false;
|
||||||
|
var c;
|
||||||
|
try {
|
||||||
|
c = MakeFunky().concat([]);
|
||||||
|
} catch(e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assertEq(threw, false);
|
||||||
|
assertEq(c[0], 'a');
|
||||||
|
assertEq(c.length, 1);
|
||||||
|
assertEq(count, 1);
|
||||||
|
}
|
||||||
|
testDenseFastpathCallsGetters();
|
2
js/src/jit-test/tests/arrays/concat-use-correct-this.js
Normal file
2
js/src/jit-test/tests/arrays/concat-use-correct-this.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
assertEq(typeof (Array.prototype.concat).call("foo")[0], "object")
|
||||||
|
assertEq(Array.prototype.concat.call("foo")[0] instanceof String, true);
|
@ -9,6 +9,7 @@
|
|||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
#include "mozilla/FloatingPoint.h"
|
#include "mozilla/FloatingPoint.h"
|
||||||
#include "mozilla/MathAlgorithms.h"
|
#include "mozilla/MathAlgorithms.h"
|
||||||
|
#include "mozilla/RangedPtr.h"
|
||||||
#include "mozilla/Util.h"
|
#include "mozilla/Util.h"
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
@ -48,6 +49,7 @@ using mozilla::CeilingLog2;
|
|||||||
using mozilla::DebugOnly;
|
using mozilla::DebugOnly;
|
||||||
using mozilla::IsNaN;
|
using mozilla::IsNaN;
|
||||||
using mozilla::PointerRangeSize;
|
using mozilla::PointerRangeSize;
|
||||||
|
using mozilla::RangedPtr;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
js::GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp)
|
js::GetLengthProperty(JSContext *cx, HandleObject obj, uint32_t *lengthp)
|
||||||
@ -303,6 +305,50 @@ SetArrayElement(JSContext *cx, HandleObject obj, double index, HandleValue v)
|
|||||||
return JSObject::setGeneric(cx, obj, obj, id, &tmp, true);
|
return JSObject::setGeneric(cx, obj, obj, id, &tmp, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls [[DefineOwnProperty]] on the given object to create a writable,
|
||||||
|
* enumerable, configurable data property with the given value. The property
|
||||||
|
* must not already exist on the object.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
DefineElementNoConflict(JSContext *cx, HandleObject obj, double index, HandleValue v)
|
||||||
|
{
|
||||||
|
JS_ASSERT(index >= 0);
|
||||||
|
|
||||||
|
if (obj->is<ArrayObject>() && !obj->isIndexed()) {
|
||||||
|
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
|
||||||
|
/* Predicted/prefetched code should favor the remains-dense case. */
|
||||||
|
JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
|
||||||
|
do {
|
||||||
|
if (index > uint32_t(-1))
|
||||||
|
break;
|
||||||
|
uint32_t idx = uint32_t(index);
|
||||||
|
if (idx >= arr->length() && !arr->lengthIsWritable()) {
|
||||||
|
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
|
||||||
|
JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = arr->ensureDenseElements(cx, idx, 1);
|
||||||
|
if (result != JSObject::ED_OK)
|
||||||
|
break;
|
||||||
|
if (idx >= arr->length())
|
||||||
|
arr->setLengthInt32(idx + 1);
|
||||||
|
JSObject::setDenseElementWithType(cx, arr, idx, v);
|
||||||
|
return true;
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
if (result == JSObject::ED_FAILED)
|
||||||
|
return false;
|
||||||
|
JS_ASSERT(result == JSObject::ED_SPARSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedId id(cx);
|
||||||
|
if (!DoubleIndexToId(cx, index, &id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return JSObject::defineGeneric(cx, obj, id, v);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attempt to delete the element |index| from |obj| as if by
|
* Attempt to delete the element |index| from |obj| as if by
|
||||||
* |obj.[[Delete]](index)|.
|
* |obj.[[Delete]](index)|.
|
||||||
@ -2593,80 +2639,89 @@ js::array_concat_dense(JSContext *cx, Handle<ArrayObject*> arr1, Handle<ArrayObj
|
|||||||
}
|
}
|
||||||
#endif /* JS_ION */
|
#endif /* JS_ION */
|
||||||
|
|
||||||
/*
|
/* ES5 15.4.4.4. */
|
||||||
* Python-esque sequence operations.
|
|
||||||
*/
|
|
||||||
bool
|
bool
|
||||||
js::array_concat(JSContext *cx, unsigned argc, Value *vp)
|
js::array_concat(JSContext *cx, unsigned argc, Value *vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
|
||||||
/* Treat our |this| object as the first argument; see ECMA 15.4.4.4. */
|
/* Step 1. */
|
||||||
Value *p = args.array() - 1;
|
RootedObject obj(cx, ToObject(cx, args.thisv()));
|
||||||
|
if (!obj)
|
||||||
/* Create a new Array object and root it using *vp. */
|
|
||||||
RootedObject aobj(cx, ToObject(cx, args.thisv()));
|
|
||||||
if (!aobj)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Rooted<ArrayObject*> narr(cx);
|
/* Step 3-4. */
|
||||||
uint32_t length;
|
double n = 0;
|
||||||
if (aobj->is<ArrayObject>() && !aobj->isIndexed()) {
|
size_t nitems = args.length() + 1;
|
||||||
length = aobj->as<ArrayObject>().length();
|
RangedPtr<const Value> items = args.thisAndArgs();
|
||||||
uint32_t initlen = aobj->getDenseInitializedLength();
|
|
||||||
narr = NewDenseCopiedArray(cx, initlen, aobj, 0);
|
/* Iterate the modified |this| and not the original. */
|
||||||
if (!narr)
|
args.setThis(ObjectValue(*obj));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step 2. This may alse inline the first iteration of Step 5 if it is
|
||||||
|
* possible to perform a fast, dense copy.
|
||||||
|
*/
|
||||||
|
Rooted<ArrayObject*> arr(cx);
|
||||||
|
if (obj->is<ArrayObject>() && !ObjectMayHaveExtraIndexedProperties(obj)) {
|
||||||
|
uint32_t initlen = obj->getDenseInitializedLength();
|
||||||
|
arr = NewDenseCopiedArray(cx, initlen, obj, 0);
|
||||||
|
if (!arr)
|
||||||
return false;
|
return false;
|
||||||
TryReuseArrayType(aobj, narr);
|
TryReuseArrayType(obj, arr);
|
||||||
ArrayObject::setLength(cx, narr, length);
|
n = obj->as<ArrayObject>().length();
|
||||||
args.rval().setObject(*narr);
|
items++;
|
||||||
if (argc == 0)
|
nitems--;
|
||||||
return true;
|
|
||||||
argc--;
|
|
||||||
p++;
|
|
||||||
} else {
|
} else {
|
||||||
narr = NewDenseEmptyArray(cx);
|
arr = NewDenseEmptyArray(cx);
|
||||||
if (!narr)
|
if (!arr)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setObject(*narr);
|
|
||||||
length = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loop over [0, argc] to concat args into narr, expanding all Arrays. */
|
/* Step 5. */
|
||||||
for (unsigned i = 0; i <= argc; i++) {
|
RootedObject elemObj(cx);
|
||||||
|
RootedValue subElement(cx);
|
||||||
|
for (; nitems > 0; --nitems, ++items) {
|
||||||
|
HandleValue elem = HandleValue::fromMarkedLocation(&*items);
|
||||||
|
|
||||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||||
return false;
|
return false;
|
||||||
HandleValue v = HandleValue::fromMarkedLocation(&p[i]);
|
|
||||||
if (v.isObject()) {
|
/* Step 5b. */
|
||||||
RootedObject obj(cx, &v.toObject());
|
if (IsObjectWithClass(elem, ESClass_Array, cx)) {
|
||||||
if (ObjectClassIs(obj, ESClass_Array, cx)) {
|
elemObj = &elem.toObject();
|
||||||
uint32_t alength;
|
|
||||||
if (!GetLengthProperty(cx, obj, &alength))
|
/* Step 5b(ii). */
|
||||||
|
uint32_t len;
|
||||||
|
if (!GetLengthProperty(cx, elemObj, &len))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Step 5b(i), 5b(iii). */
|
||||||
|
for (uint32_t k = 0; k < len; ++k) {
|
||||||
|
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||||
return false;
|
return false;
|
||||||
RootedValue tmp(cx);
|
|
||||||
for (uint32_t slot = 0; slot < alength; slot++) {
|
|
||||||
bool hole;
|
|
||||||
if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, slot, &hole, &tmp))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/*
|
bool exists;
|
||||||
* Per ECMA 262, 15.4.4.4, step 9, ignore nonexistent
|
if (!JSObject::getElementIfPresent(cx, elemObj, elemObj, k, &subElement, &exists))
|
||||||
* properties.
|
return false;
|
||||||
*/
|
|
||||||
if (!hole && !SetArrayElement(cx, narr, length + slot, tmp))
|
if (exists && !DefineElementNoConflict(cx, arr, n + k, subElement))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
length += alength;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
n += len;
|
||||||
|
} else {
|
||||||
|
/* Step 5c(i). */
|
||||||
|
if (!DefineElementNoConflict(cx, arr, n, elem))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!SetArrayElement(cx, narr, length, v))
|
/* Step 5c(ii). */
|
||||||
return false;
|
n++;
|
||||||
length++;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SetLengthProperty(cx, narr, length);
|
/* Step 6. */
|
||||||
|
args.rval().setObject(*arr);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
Loading…
Reference in New Issue
Block a user