Backout changeset 24feaa8bd894 (bug 725907 part 4) under the suspicion of breaking Linux32 mochitest-chrome without framepointers

This commit is contained in:
Ehsan Akhgari 2012-07-04 19:24:50 -04:00
parent f32943d99a
commit 12d7f78c10
24 changed files with 254 additions and 511 deletions

View File

@ -1,11 +0,0 @@
// Array iterators reflect changes to elements of the underlying array.
load(libdir + "asserts.js");
var arr = [0, 1, 2];
var it = arr.iterator();
arr[0] = 1000;
arr[2] = 2000;
assertEq(it.next(), 1000);
assertEq(it.next(), 1);
assertEq(it.next(), 2000);
assertThrowsValue(function () { it.next(); }, StopIteration);

View File

@ -1,27 +0,0 @@
// Array.prototype.iterator is generic.
// That is, it can be applied to arraylike objects and strings, not just arrays.
load(libdir + "asserts.js");
function test(obj) {
var it = Array.prototype.iterator.call(obj);
for (var i = 0; i < (obj.length >>> 0); i++)
assertEq(it.next(), obj[i]);
assertThrowsValue(function () { it.next(); }, StopIteration);
}
test({length: 0});
test({length: 0, 0: 'x', 1: 'y'});
test({length: 2, 0: 'x', 1: 'y'});
test(Object.create(['x', 'y', 'z']));
test(Object.create({length: 2, 0: 'x', 1: 'y'}));
test("");
test("ponies");
// Perverse length values.
test({length: 0x1f00000000});
test({length: -0xfffffffe, 0: 'a', 1: 'b'});
test({length: "011", 9: 9, 10: 10, 11: 11});
test({length: -0});
test({length: 2.7, 0: 0, 1: 1, 2: 2});
test({length: {valueOf: function () { return 3; }}, 0: 0, 1: 1, 2: 2});

View File

@ -1,12 +0,0 @@
// If an array with an active iterator is lengthened, the iterator visits the new elements.
load(libdir + "asserts.js");
var arr = [0, 1];
var it = arr.iterator();
it.next();
it.next();
arr[2] = 2;
arr.length = 4;
assertEq(it.next(), 2);
assertEq(it.next(), undefined);
assertThrowsValue(function () { it.next(); }, StopIteration);

View File

@ -1,9 +0,0 @@
// Array.prototype.iterator applied to undefined or null does not throw (until .next is called).
load(libdir + "asserts.js");
for (var v of [undefined, null]) {
var it = Array.prototype.iterator.call(v);
// This will throw because the iterator is trying to get v.length.
assertThrowsInstanceOf(function () { it.next(); }, TypeError);
}

View File

@ -1,23 +0,0 @@
// An array iterator for a proxy calls the traps in a predictable order.
load(libdir + "asserts.js");
var s = '';
var it = Array.prototype.iterator.call(Proxy.create({
get: function (recipient, name) {
if (name == 'length') {
s += 'L';
return 2;
} else {
s += name;
return name;
}
}
}));
assertEq(it.next(), "0");
s += ' ';
assertEq(it.next(), "1");
s += ' ';
assertThrowsValue(function () { it.next(); }, StopIteration);
assertEq(s, "L0 L1 L");

View File

@ -1,9 +0,0 @@
// If an array is truncated to the left of an iterator it, it.next() throws StopIteration.
load(libdir + "asserts.js");
var arr = [0, 1, 2];
var it = arr.iterator();
it.next();
it.next();
arr.length = 1;
assertThrowsValue(function () { it.next(); }, StopIteration);

View File

@ -1,22 +0,0 @@
// Superficial tests for iterators created by Array.prototype.iterator
var proto = Object.getPrototypeOf([].iterator());
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
function check(it) {
assertEq(typeof it, 'object');
assertEq(Object.getPrototypeOf(it), proto);
assertEq(Object.getOwnPropertyNames(it).length, 0);
assertEq(it.iterator(), it);
// for-in enumerates the iterator's properties.
it.x = 0;
var s = '';
for (var p in it)
s += p + '.';
assertEq(s, 'x.');
}
check([].iterator());
check(Array.prototype.iterator.call({}));
check(Array.prototype.iterator.call(undefined));

View File

@ -1,5 +0,0 @@
// Iterator.prototype.next throws if applied to a value that isn't an iterator.
load(libdir + "asserts.js");
for (var v of [null, undefined, false, 0, "ponies", {}, [], this])
assertThrowsInstanceOf(function () { Iterator.prototype.next.call(v); }, TypeError);

View File

@ -1,6 +0,0 @@
// Iterator.prototype.next throws if applied to a non-iterator that inherits from an iterator.
load(libdir + "asserts.js");
var it = [1, 2].iterator();
var v = Object.create(it);
assertThrowsInstanceOf(function () { Iterator.prototype.next.call(v); }, TypeError);

View File

@ -1,8 +0,0 @@
// The .next method of array iterators works across compartment boundaries.
load(libdir + "asserts.js");
var g = newGlobal('new-compartment');
g.eval("var it = [1, 2].iterator();");
assertEq(g.it.next(), 1);
assertEq([].iterator().next.call(g.it), 2);
assertThrowsValue([].iterator().next.bind(g.it), StopIteration);

View File

@ -1,7 +0,0 @@
// Test superficial features of the Iterator.prototype.next builtin function.
assertEq(Iterator.prototype.next.length, 0);
var desc = Object.getOwnPropertyDescriptor(Iterator.prototype, "next");
assertEq(desc.configurable, true);
assertEq(desc.enumerable, false);
assertEq(desc.writable, true);

View File

@ -1,6 +0,0 @@
// Deleting the .next method makes for-of stop working on arrays.
load(libdir + "asserts.js");
var iterProto = Object.getPrototypeOf([].iterator());
delete iterProto.next;
assertThrowsInstanceOf(function () { for (var v of []) ; }, TypeError);

View File

@ -1,15 +0,0 @@
// Deleting the .next method of an iterator in the middle of a for-of loop
// causes a TypeError at the next iteration.
load(libdir + "asserts.js");
var iterProto = Object.getPrototypeOf([].iterator());
var s = '';
assertThrowsInstanceOf(function () {
for (var v of ['duck', 'duck', 'duck', 'goose', 'FAIL']) {
s += v;
if (v === 'goose')
delete iterProto.next;
s += '.';
}
}, TypeError);
assertEq(s, 'duck.duck.duck.goose.');

View File

@ -1,7 +0,0 @@
// A for-of loop exits if the iterator's .next method throws another compartment's StopIteration.
var g = newGlobal('new-compartment');
var it = g.eval("({ iterator: function () { return this; }, " +
"next: function () { throw StopIteration; } });");
for (x of it)
throw 'FAIL';

View File

@ -1,25 +0,0 @@
// The LHS of a for-of loop is not evaluated until after the .next() method returns.
var s;
function f() {
s += 'f';
return {};
}
// Test 1: .next() throws StopIteration right away. f is never called.
s = '';
for (f().x of [])
s += '.';
assertEq(s, '');
// Test 2: check proper interleaving of f calls, iterator.next() calls, and the loop body.
function g() {
s += 'g';
yield 0;
s += 'g';
yield 1;
s += 'g';
}
for (f().x of g())
s += '.';
assertEq(s, 'gf.gf.g');

View File

@ -1,31 +0,0 @@
// The LHS of a for-loop is not bound to a particular scope until after the .next() method returns.
var obj = {};
// Test 1
function g() {
obj.x = 0;
yield 1;
}
var x = 2, n = 0;
with (obj) {
for (x of g()) // g().next() inserts a binding for x on obj
n++;
}
assertEq(x, 2);
assertEq(obj.x, 1);
assertEq(n, 1);
// Test 2
function h() {
delete obj.x;
yield 3;
}
n = 0;
with (obj) {
for (x of h()) // h().next() deletes the binding for x on obj
n++;
}
assertEq(x, 3);
assertEq("x" in obj, false);
assertEq(n, 1);

View File

@ -1,41 +0,0 @@
// for-of on a proxy causes a predictable sequence of trap calls.
var s = '';
var i = 0;
var next_fn = Proxy.createFunction({}, function () {
s += "n";
if (i == 3)
throw StopIteration;
return i++;
});
var it = Proxy.create({
get: function (receiver, name) {
if (name == 'toSource') {
s += '?';
return function () 'it';
}
assertEq(name, "next");
s += "N";
return next_fn;
}
});
var iterator_fn = Proxy.createFunction({}, function () {
s += 'i';
return it;
});
var obj = Proxy.create({
get: function (receiver, name) {
assertEq(name, "iterator");
s += "I";
return iterator_fn;
}
});
for (var v of obj)
s += v;
assertEq(s, 'IiNn0Nn1Nn2Nn');

View File

@ -1,14 +0,0 @@
// String.prototype.iterator is generic.
load(libdir + "asserts.js");
function test(obj) {
var it = Array.prototype.iterator.call(obj);
for (var i = 0; i < (obj.length >>> 0); i++)
assertEq(it.next(), obj[i]);
assertThrowsValue(function () { it.next(); }, StopIteration);
}
test({length: 0});
test(Object.create(['x', 'y', 'z']));
test(Object.create({length: 2, 0: 'x', 1: 'y'}));

View File

@ -1,26 +0,0 @@
// for-of works on strings and String objects.
function test(s) {
var copy = '';
for (var v of s) {
assertEq(typeof v, 'string');
assertEq(v.length, 1);
copy += v;
}
assertEq(copy, String(s));
}
test('');
test('abc');
test('a \0 \ufffe \ufeff');
// Non-BMP characters are generally passed to JS in UTF-16, as surrogate pairs.
// ES requires that such pairs be treated as two 16-bit "characters" in pretty
// much every circumstance, including string indexing. We anticipate the same
// requirement will be imposed here, though it's not a sure thing.
test('\ud808\udf45');
test(new String(''));
test(new String('abc'));
test(new String('a \0 \ufffe \ufeff'));
test(new String('\ud808\udf45'));

View File

@ -1811,7 +1811,9 @@ static JSStdName standard_class_names[] = {
{js_InitXMLClass, EAGER_ATOM(isXMLName), CLASP(XML)},
#endif
#if JS_HAS_GENERATORS
{js_InitIteratorClasses, EAGER_CLASS_ATOM(Iterator), &PropertyIteratorObject::class_},
#endif
/* Typed Arrays */
{js_InitTypedArrayClasses, EAGER_CLASS_ATOM(ArrayBuffer), &ArrayBufferClass},
@ -4410,8 +4412,11 @@ JS_PUBLIC_API(JSBool)
JS_ArrayIterator(JSContext *cx, unsigned argc, jsval *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
Rooted<Value> target(cx, args.thisv());
JSObject *iterobj = ElementIteratorObject::create(cx, target);
JSObject *target = NonNullObject(cx, args.thisv());
if (!target)
return false;
Rooted<JSObject*> iterobj(cx, target);
iterobj = ElementIteratorObject::create(cx, iterobj);
if (!iterobj)
return false;
vp->setObject(*iterobj);

View File

@ -3786,7 +3786,7 @@ struct JSClass {
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
* prevously allowed, but is now an ES5 violation and thus unsupported.
*/
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 9)
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 8)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \

View File

@ -52,6 +52,31 @@ using namespace mozilla;
using namespace js;
using namespace js::gc;
Class js::ElementIteratorClass = {
"ElementIterator",
JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL, /* finalize */
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
NULL, /* trace */
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
NULL, /* iteratorObject */
NULL /* unused */
}
};
static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
void
@ -713,18 +738,6 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
}
JSBool
js_ThrowStopIteration(JSContext *cx)
{
JS_ASSERT(!cx->isExceptionPending());
Value v;
if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
cx->setPendingException(v);
return false;
}
/*** Iterator objects ****************************************************************************/
static JSBool
Iterator(JSContext *cx, unsigned argc, Value *vp)
{
@ -745,6 +758,17 @@ Iterator(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JSBool
js_ThrowStopIteration(JSContext *cx)
{
Value v;
JS_ASSERT(!JS_IsExceptionPending(cx));
if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
cx->setPendingException(v);
return JS_FALSE;
}
static JSBool
iterator_iterator(JSContext *cx, unsigned argc, Value *vp)
{
@ -784,7 +808,7 @@ static JSFunctionSpec iterator_methods[] = {
JS_FS_END
};
static JSObject *
JSObject *
iterator_iteratorObject(JSContext *cx, HandleObject obj, JSBool keysonly)
{
return obj;
@ -833,99 +857,6 @@ Class PropertyIteratorObject::class_ = {
}
};
const uint32_t CLOSED_INDEX = UINT32_MAX;
JSObject *
ElementIteratorObject::create(JSContext *cx, Handle<Value> target)
{
GlobalObject *global = GetCurrentGlobal(cx);
Rooted<JSObject*> proto(cx, global->getOrCreateElementIteratorPrototype(cx));
if (!proto)
return NULL;
JSObject *iterobj = NewObjectWithGivenProto(cx, &class_, proto, global);
if (iterobj) {
iterobj->setReservedSlot(TargetSlot, target);
iterobj->setReservedSlot(IndexSlot, Int32Value(0));
}
return iterobj;
}
JSBool
ElementIteratorObject::next(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
JSObject *iterobj;
if (!NonGenericMethodGuard(cx, args, next, &class_, &iterobj))
return false;
if (!iterobj)
return true;
uint32_t i, length;
Value target = iterobj->getReservedSlot(TargetSlot);
Rooted<JSObject*> obj(cx);
// Get target.length.
if (target.isString()) {
length = uint32_t(target.toString()->length());
} else {
obj = ValueToObject(cx, target);
if (!obj)
goto close;
if (!js_GetLengthProperty(cx, obj, &length))
goto close;
}
// Check target.length.
i = uint32_t(iterobj->getReservedSlot(IndexSlot).toInt32());
if (i >= length) {
js_ThrowStopIteration(cx);
goto close;
}
// Get target[i].
JS_ASSERT(i + 1 > i);
if (target.isString()) {
JSString *c = cx->runtime->staticStrings.getUnitStringForElement(cx, target.toString(), i);
if (!c)
goto close;
vp->setString(c);
} else {
if (!obj->getElement(cx, obj, i, vp))
goto close;
}
// On success, bump the index.
iterobj->setReservedSlot(IndexSlot, Int32Value(int32_t(i + 1)));
return true;
close:
// Close the iterator. The TargetSlot will never be used again, so don't keep a
// reference to it.
iterobj->setReservedSlot(TargetSlot, UndefinedValue());
iterobj->setReservedSlot(IndexSlot, Int32Value(int32_t(CLOSED_INDEX)));
return false;
}
Class ElementIteratorObject::class_ = {
"Iterator",
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(NumSlots),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
NULL /* finalize */
};
JSFunctionSpec ElementIteratorObject::methods[] = {
JS_FN("next", next, 0, 0),
JS_FS_END
};
#if JS_HAS_GENERATORS
static JSBool
CloseGenerator(JSContext *cx, JSObject *genobj);
@ -1168,6 +1099,75 @@ js_SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint
return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
}
const uint32_t CLOSED_INDEX = UINT32_MAX;
JSObject *
ElementIteratorObject::create(JSContext *cx, HandleObject obj)
{
JS_ASSERT(obj);
JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj);
if (iterobj) {
iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj));
iterobj->setReservedSlot(IndexSlot, Int32Value(0));
}
return iterobj;
}
inline uint32_t
ElementIteratorObject::getIndex() const
{
return uint32_t(getReservedSlot(IndexSlot).toInt32());
}
inline JSObject *
ElementIteratorObject::getTargetObject() const
{
return &getReservedSlot(TargetSlot).toObject();
}
inline void
ElementIteratorObject::setIndex(uint32_t index)
{
setReservedSlot(IndexSlot, Int32Value(int32_t(index)));
}
bool
ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp)
{
Rooted<ElementIteratorObject*> self(cx, this);
uint32_t i, length;
RootedObject obj(cx, getTargetObject());
if (!js_GetLengthProperty(cx, obj, &length))
goto error;
i = self->getIndex();
if (i >= length) {
self->setIndex(CLOSED_INDEX);
vp->setMagic(JS_NO_ITER_VALUE);
return true;
}
JS_ASSERT(i + 1 > i);
if (!obj->getElement(cx, obj, i, vp))
goto error;
/* On success, bump the index. */
self->setIndex(i + 1);
return true;
error:
self->setIndex(CLOSED_INDEX);
return false;
}
inline js::ElementIteratorObject *
JSObject::asElementIterator()
{
JS_ASSERT(isElementIterator());
return static_cast<js::ElementIteratorObject *>(this);
}
JSBool
js_IteratorMore(JSContext *cx, HandleObject iterobj, Value *rval)
{
@ -1203,6 +1203,25 @@ js_IteratorMore(JSContext *cx, HandleObject iterobj, Value *rval)
return false;
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
return false;
} else if (iterobj->isElementIterator()) {
/*
* Like native iterators, element iterators do not have a .next
* method, so this fast path is necessary for correctness.
*/
if (!iterobj->asElementIterator()->iteratorNext(cx, rval))
return false;
if (rval->isMagic(JS_NO_ITER_VALUE)) {
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
rval->setBoolean(false);
return true;
}
} else if (iterobj->isProxy()) {
if (!Proxy::iteratorNext(cx, iterobj, rval))
return false;
if (rval->isMagic(JS_NO_ITER_VALUE)) {
rval->setBoolean(false);
return true;
}
} else {
/* Call the iterator object's .next method. */
if (!GetMethod(cx, iterobj, cx->runtime->atomState.nextAtom, 0, rval))
@ -1291,8 +1310,6 @@ Class js::StopIterationClass = {
NULL /* construct */
};
/*** Generators **********************************************************************************/
#if JS_HAS_GENERATORS
static void
@ -1687,76 +1704,84 @@ static JSFunctionSpec generator_methods[] = {
#endif /* JS_HAS_GENERATORS */
/* static */ bool
GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
static bool
InitIteratorClass(JSContext *cx, Handle<GlobalObject*> global)
{
Rooted<JSObject*> iteratorProto(cx);
Value iteratorProtoVal = global->getPrototype(JSProto_Iterator);
if (iteratorProtoVal.isObject()) {
iteratorProto = &iteratorProtoVal.toObject();
} else {
iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_);
if (!iteratorProto)
return false;
Rooted<JSObject*> iteratorProto(cx,
global->createBlankPrototype(cx, &PropertyIteratorObject::class_));
if (!iteratorProto)
return false;
AutoIdVector blank(cx);
NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
if (!ni)
return false;
ni->init(NULL, 0 /* flags */, 0, 0);
AutoIdVector blank(cx);
NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
if (!ni)
return false;
ni->init(NULL, 0 /* flags */, 0, 0);
iteratorProto->asPropertyIterator().setNativeIterator(ni);
iteratorProto->asPropertyIterator().setNativeIterator(ni);
Rooted<JSFunction*> ctor(cx);
ctor = global->createConstructor(cx, Iterator, CLASS_NAME(cx, Iterator), 2);
if (!ctor)
return false;
if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
return false;
if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
return false;
if (!DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto))
return false;
}
RootedFunction ctor(cx);
ctor = global->createConstructor(cx, Iterator, CLASS_NAME(cx, Iterator), 2);
if (!ctor)
return false;
Rooted<JSObject*> proto(cx);
if (global->getSlot(ELEMENT_ITERATOR_PROTO).isUndefined()) {
Class *cls = &ElementIteratorObject::class_;
proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto);
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, ElementIteratorObject::methods))
return false;
global->setReservedSlot(ELEMENT_ITERATOR_PROTO, ObjectValue(*proto));
}
if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
return false;
if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
return false;
return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
}
/* static */ bool
GlobalObject::initGeneratorClass(JSContext *cx, Handle<GlobalObject*> global)
{
#if JS_HAS_GENERATORS
if (global->getSlot(GENERATOR_PROTO).isUndefined()) {
proto = global->createBlankPrototype(cx, &GeneratorClass);
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
return false;
global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
}
RootedObject proto(cx, global->createBlankPrototype(cx, &GeneratorClass));
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
return false;
global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
#endif
if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
proto = global->createBlankPrototype(cx, &StopIterationClass);
if (!proto || !proto->freeze(cx))
return false;
/* This should use a non-JSProtoKey'd slot, but this is easier for now. */
if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
return false;
MarkStandardClassInitializedNoProto(global, &StopIterationClass);
}
return true;
}
static JSObject *
InitStopIterationClass(JSContext *cx, Handle<GlobalObject*> global)
{
RootedObject proto(cx, global->createBlankPrototype(cx, &StopIterationClass));
if (!proto || !proto->freeze(cx))
return NULL;
/* This should use a non-JSProtoKey'd slot, but this is easier for now. */
if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
return NULL;
MarkStandardClassInitializedNoProto(global, &StopIterationClass);
return proto;
}
JSObject *
js_InitIteratorClasses(JSContext *cx, JSObject *obj)
{
JS_ASSERT(obj->isNative());
Rooted<GlobalObject*> global(cx, &obj->asGlobal());
if (!GlobalObject::initIteratorClasses(cx, global))
/*
* Bail if Iterator has already been initialized. We test for Iterator
* rather than for StopIteration because if js_InitIteratorClasses recurs,
* as happens when the StopIteration object is frozen, initializing the
* Iterator class a second time will assert.
*/
JSObject *iter;
if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
return NULL;
return global->getIteratorPrototype();
if (iter)
return iter;
if (!InitIteratorClass(cx, global) || !GlobalObject::initGeneratorClass(cx, global))
return NULL;
return InitStopIterationClass(cx, global);
}

View File

@ -83,32 +83,66 @@ class PropertyIteratorObject : public JSObject
static void finalize(FreeOp *fop, JSObject *obj);
};
/*
* Array iterators are roughly like this:
*
* Array.prototype.iterator = function iterator() {
* for (var i = 0; i < (this.length >>> 0); i++)
* yield this[i];
* }
*
* However they are not generators. They are a different class. The semantics
* of Array iterators will be given in the eventual ES6 spec in full detail.
*/
class ElementIteratorObject : public JSObject
{
public:
static JSObject *create(JSContext *cx, Handle<Value> target);
static Class class_;
static JSFunctionSpec methods[];
private:
enum {
TargetSlot,
IndexSlot,
NumSlots
};
static JSBool next(JSContext *cx, unsigned argc, Value *vp);
static JSObject *create(JSContext *cx, HandleObject target);
inline uint32_t getIndex() const;
inline void setIndex(uint32_t index);
inline JSObject *getTargetObject() const;
/*
Array iterators are like this:
Array.prototype[iterate] = function () {
for (var i = 0; i < (this.length >>> 0); i++) {
var desc = Object.getOwnPropertyDescriptor(this, i);
yield desc === undefined ? undefined : this[i];
}
}
This has the following implications:
- Array iterators are generic; Array.prototype[iterate] can be transferred to
any other object to create iterators over it.
- The next() method of an Array iterator is non-reentrant. Trying to reenter,
e.g. by using it on an object with a length getter that calls it.next() on
the same iterator, causes a TypeError.
- The iterator fetches obj.length every time its next() method is called.
- The iterator converts obj.length to a whole number using ToUint32. As a
consequence the iterator can't go on forever; it can yield at most 2^32-1
values. Then i will be 0xffffffff, and no possible length value will be
greater than that.
- The iterator does not skip "array holes". When it encounters a hole, it
yields undefined.
- The iterator never consults the prototype chain.
- If an element has a getter which throws, the exception is propagated, and
the iterator is closed (that is, all future calls to next() will simply
throw StopIteration).
Note that if next() were reentrant, even more details of its inner
workings would be observable.
*/
/*
* If there are any more elements to visit, store the value of the next
* element in *vp, increment the index, and return true. If not, call
* vp->setMagic(JS_NO_ITER_VALUE) and return true. Return false on error.
*/
bool iteratorNext(JSContext *cx, Value *vp);
};
bool

View File

@ -71,8 +71,7 @@ class GlobalObject : public JSObject
/* One-off properties stored after slots for built-ins. */
static const unsigned THROWTYPEERROR = STANDARD_CLASS_SLOTS;
static const unsigned ELEMENT_ITERATOR_PROTO = THROWTYPEERROR + 1;
static const unsigned GENERATOR_PROTO = ELEMENT_ITERATOR_PROTO + 1;
static const unsigned GENERATOR_PROTO = THROWTYPEERROR + 1;
static const unsigned REGEXP_STATICS = GENERATOR_PROTO + 1;
static const unsigned FUNCTION_NS = REGEXP_STATICS + 1;
static const unsigned RUNTIME_CODEGEN_ENABLED = FUNCTION_NS + 1;
@ -268,28 +267,14 @@ class GlobalObject : public JSObject
return &self->getPrototype(key).toObject();
}
JSObject *getIteratorPrototype() {
return &getPrototype(JSProto_Iterator).toObject();
}
private:
JSObject *getOrCreateIteratorSubclassPrototype(JSContext *cx, unsigned slot) {
Value v = getSlotRef(slot);
JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
Value v = getSlotRef(GENERATOR_PROTO);
if (v.isObject())
return &v.toObject();
Rooted<GlobalObject*> self(cx, this);
if (!initIteratorClasses(cx, self))
if (!js_InitIteratorClasses(cx, this))
return NULL;
return &self->getSlot(slot).toObject();
}
public:
JSObject *getOrCreateElementIteratorPrototype(JSContext *cx) {
return getOrCreateIteratorSubclassPrototype(cx, ELEMENT_ITERATOR_PROTO);
}
JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
return getOrCreateIteratorSubclassPrototype(cx, GENERATOR_PROTO);
return &self->getSlot(GENERATOR_PROTO).toObject();
}
inline RegExpStatics *getRegExpStatics() const;
@ -314,9 +299,7 @@ class GlobalObject : public JSObject
bool getFunctionNamespace(JSContext *cx, Value *vp);
// Implemented in jsiter.cpp.
static bool initIteratorClasses(JSContext *cx, Handle<GlobalObject*> global);
static bool initGeneratorClass(JSContext *cx, Handle<GlobalObject*> global);
static bool initStandardClasses(JSContext *cx, Handle<GlobalObject*> global);
typedef js::Vector<js::Debugger *, 0, js::SystemAllocPolicy> DebuggerVector;