Merge from mozilla-central.

This commit is contained in:
David Anderson
2012-07-20 12:02:04 -07:00
462 changed files with 9210 additions and 3355 deletions

View File

@@ -43,6 +43,7 @@
#include "jsinferinlines.h"
#include "jsobjinlines.h"
#include "builtin/Iterator-inl.h"
#include "vm/Stack-inl.h"
#include "vm/String-inl.h"
@@ -50,61 +51,6 @@ using namespace mozilla;
using namespace js;
using namespace js::gc;
static void iterator_finalize(FreeOp *fop, JSObject *obj);
static void iterator_trace(JSTracer *trc, JSObject *obj);
static JSObject *iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly);
Class js::IteratorClass = {
"Iterator",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
iterator_finalize,
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
iterator_trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
iterator_iterator,
NULL /* unused */
}
};
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 */
iterator_iterator,
NULL /* unused */
}
};
static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
void
@@ -116,27 +62,6 @@ NativeIterator::mark(JSTracer *trc)
MarkObject(trc, &obj, "obj");
}
static void
iterator_finalize(FreeOp *fop, JSObject *obj)
{
JS_ASSERT(obj->isIterator());
NativeIterator *ni = obj->getNativeIterator();
if (ni) {
obj->setPrivate(NULL);
fop->free_(ni);
}
}
static void
iterator_trace(JSTracer *trc, JSObject *obj)
{
NativeIterator *ni = obj->getNativeIterator();
if (ni)
ni->mark(trc);
}
struct IdHashPolicy {
typedef jsid Lookup;
static HashNumber hash(jsid id) {
@@ -416,18 +341,8 @@ GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
{
JS_CHECK_RECURSION(cx, return false);
/*
* for-of iteration does not fall back on __iterator__ or property
* enumeration. This is more conservative than the current proposed spec.
*/
if (flags == JSITER_FOR_OF) {
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE,
JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL);
return false;
}
/* Check whether we have a valid __iterator__ method. */
PropertyName *name = cx->runtime->atomState.iteratorAtom;
PropertyName *name = cx->runtime->atomState.iteratorIntrinsicAtom;
if (!GetMethod(cx, obj, name, 0, vp))
return false;
@@ -478,16 +393,16 @@ Compare(T *a, T *b, size_t c)
return true;
}
static inline JSObject *
NewIteratorObject(JSContext *cx, unsigned flags)
static inline PropertyIteratorObject *
NewPropertyIteratorObject(JSContext *cx, unsigned flags)
{
if (flags & JSITER_ENUMERATE) {
RootedTypeObject type(cx, cx->compartment->getEmptyType(cx));
if (!type)
return NULL;
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
ITERATOR_FINALIZE_KIND));
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PropertyIteratorObject::class_,
NULL, NULL, ITERATOR_FINALIZE_KIND));
if (!shape)
return NULL;
@@ -496,10 +411,10 @@ NewIteratorObject(JSContext *cx, unsigned flags)
return NULL;
JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
return obj;
return &obj->asPropertyIterator();
}
return NewBuiltinClassInstance(cx, &IteratorClass);
return &NewBuiltinClassInstance(cx, &PropertyIteratorObject::class_)->asPropertyIterator();
}
NativeIterator *
@@ -537,7 +452,7 @@ NativeIterator::init(JSObject *obj, unsigned flags, uint32_t slength, uint32_t k
}
static inline void
RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
RegisterEnumerator(JSContext *cx, PropertyIteratorObject *iterobj, NativeIterator *ni)
{
/* Register non-escaping native enumerators (for-in) with the current context. */
if (ni->flags & JSITER_ENUMERATE) {
@@ -561,7 +476,7 @@ VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVecto
types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
}
RootedObject iterobj(cx, NewIteratorObject(cx, flags));
Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags));
if (!iterobj)
return false;
@@ -614,7 +529,7 @@ VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVec
types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
}
RootedObject iterobj(cx, NewIteratorObject(cx, flags));
Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
if (!iterobj)
return false;
@@ -650,30 +565,45 @@ UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
bool
GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
{
if (flags == JSITER_FOR_OF) {
// for-of loop. The iterator is simply |obj.iterator()|.
Value method;
if (!obj->getProperty(cx, obj, cx->runtime->atomState.iteratorAtom, &method))
return false;
// Throw if obj.iterator isn't callable. js::Invoke is about to check
// for this kind of error anyway, but it would throw an inscrutable
// error message about |method| rather than this nice one about |obj|.
if (!method.isObject() || !method.toObject().isCallable()) {
char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, ObjectOrNullValue(obj), NULL);
if (!bytes)
return false;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_ITERABLE, bytes);
cx->free_(bytes);
return false;
}
if (!Invoke(cx, ObjectOrNullValue(obj), method, 0, NULL, vp))
return false;
if (!ToObject(cx, vp))
return false;
return true;
}
Vector<Shape *, 8> shapes(cx);
uint32_t key = 0;
bool keysOnly = (flags == JSITER_ENUMERATE);
if (obj) {
/* Enumerate Iterator.prototype directly. */
if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
/*
* Arrays and other classes representing iterable collections have
* the JSCLASS_FOR_OF_ITERATION flag. This flag means that the
* object responds to all other kinds of enumeration (for-in,
* for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the
* default way, ignoring the hook. The hook is used only when
* iterating in the style of a for-of loop.
*/
if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) {
JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF)));
if (!iterobj)
return false;
vp->setObject(*iterobj);
types::MarkIteratorUnknown(cx);
return true;
}
JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
if (!iterobj)
return false;
vp->setObject(*iterobj);
types::MarkIteratorUnknown(cx);
return true;
}
if (keysOnly) {
@@ -683,7 +613,7 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
* objects here, as they are not inserted into the cache and
* will result in a miss.
*/
JSObject *last = cx->runtime->nativeIterCache.last;
PropertyIteratorObject *last = cx->runtime->nativeIterCache.last;
JSObject *proto = obj->getProto();
if (last) {
NativeIterator *lastni = last->getNativeIterator();
@@ -722,7 +652,7 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
pobj = pobj->getProto();
} while (pobj);
JSObject *iterobj = cx->runtime->nativeIterCache.get(key);
PropertyIteratorObject *iterobj = cx->runtime->nativeIterCache.get(key);
if (iterobj) {
NativeIterator *ni = iterobj->getNativeIterator();
if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
@@ -769,7 +699,7 @@ GetIterator(JSContext *cx, HandleObject obj, unsigned flags, Value *vp)
return false;
}
JSObject *iterobj = &vp->toObject();
PropertyIteratorObject *iterobj = &vp->toObject().asPropertyIterator();
/* Cache the iterator object if possible. */
if (shapes.length())
@@ -791,12 +721,18 @@ GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags)
} /* namespace js */
static JSObject *
iterator_iterator(JSContext *cx, HandleObject obj, JSBool keysonly)
JSBool
js_ThrowStopIteration(JSContext *cx)
{
return obj;
JS_ASSERT(!JS_IsExceptionPending(cx));
RootedValue v(cx);
if (js_FindClassObject(cx, NullPtr(), JSProto_StopIteration, &v))
cx->setPendingException(v);
return JS_FALSE;
}
/*** Iterator objects ****************************************************************************/
static JSBool
Iterator(JSContext *cx, unsigned argc, Value *vp)
{
@@ -817,20 +753,10 @@ Iterator(JSContext *cx, unsigned argc, Value *vp)
return true;
}
JSBool
js_ThrowStopIteration(JSContext *cx)
{
JS_ASSERT(!JS_IsExceptionPending(cx));
RootedValue v(cx);
if (js_FindClassObject(cx, NullPtr(), JSProto_StopIteration, &v))
cx->setPendingException(v);
return JS_FALSE;
}
static bool
IsIterator(const Value &v)
{
return v.isObject() && v.toObject().hasClass(&IteratorClass);
return v.isObject() && v.toObject().hasClass(&PropertyIteratorObject::class_);
}
static bool
@@ -851,6 +777,14 @@ iterator_next_impl(JSContext *cx, CallArgs args)
return js_IteratorNext(cx, thisObj, &args.rval());
}
static JSBool
iterator_iterator(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval() = args.thisv();
return true;
}
static JSBool
iterator_next(JSContext *cx, unsigned argc, Value *vp)
{
@@ -858,10 +792,157 @@ iterator_next(JSContext *cx, unsigned argc, Value *vp)
return CallNonGenericMethod(cx, IsIterator, iterator_next_impl, args);
}
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
static JSFunctionSpec iterator_methods[] = {
JS_FN(js_next_str, iterator_next, 0,JSPROP_ROPERM),
JS_FN("iterator", iterator_iterator, 0, 0),
JS_FN("next", iterator_next, 0, 0),
JS_FS_END
};
static JSObject *
iterator_iteratorObject(JSContext *cx, HandleObject obj, JSBool keysonly)
{
return obj;
}
void
PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj)
{
if (NativeIterator *ni = obj->asPropertyIterator().getNativeIterator())
ni->mark(trc);
}
void
PropertyIteratorObject::finalize(FreeOp *fop, JSObject *obj)
{
if (NativeIterator *ni = obj->asPropertyIterator().getNativeIterator()) {
obj->asPropertyIterator().setNativeIterator(NULL);
fop->free_(ni);
}
}
Class PropertyIteratorObject::class_ = {
"Iterator",
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
JSCLASS_HAS_PRIVATE,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
finalize,
NULL, /* checkAccess */
NULL, /* call */
NULL, /* construct */
NULL, /* hasInstance */
trace,
{
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
iterator_iteratorObject,
NULL /* unused */
}
};
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, &ElementIteratorClass, proto, global);
if (iterobj) {
iterobj->setReservedSlot(TargetSlot, target);
iterobj->setReservedSlot(IndexSlot, Int32Value(0));
}
return iterobj;
}
static bool
IsElementIterator(const Value &v)
{
return v.isObject() && v.toObject().isElementIterator();
}
JSBool
ElementIteratorObject::next(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, IsElementIterator, next_impl, args);
}
bool
ElementIteratorObject::next_impl(JSContext *cx, CallArgs args)
{
JSObject *iterobj = &args.thisv().toObject();
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;
args.rval().setString(c);
} else {
if (!obj->getElement(cx, obj, i, &args.rval()))
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 js::ElementIteratorClass = {
"Iterator",
JSCLASS_IMPLEMENTS_BARRIERS |
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 */
};
JSFunctionSpec ElementIteratorObject::methods[] = {
JS_FN("next", next, 0, 0),
JS_FS_END
};
@@ -870,11 +951,7 @@ static JSBool
CloseGenerator(JSContext *cx, JSObject *genobj);
#endif
/*
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
* Otherwise construct the default iterator.
*/
JSBool
bool
js::ValueToIterator(JSContext *cx, unsigned flags, Value *vp)
{
/* JSITER_KEYVALUE must always come with JSITER_FOREACH */
@@ -918,9 +995,9 @@ js::CloseIterator(JSContext *cx, JSObject *obj)
{
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
if (obj->isIterator()) {
if (obj->isPropertyIterator()) {
/* Remove enumerators from the active list, which is a stack. */
NativeIterator *ni = obj->getNativeIterator();
NativeIterator *ni = obj->asPropertyIterator().getNativeIterator();
if (ni->flags & JSITER_ENUMERATE) {
JS_ASSERT(cx->enumerators == obj);
@@ -958,8 +1035,8 @@ js::UnwindIteratorForException(JSContext *cx, JSObject *obj)
void
js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj)
{
if (obj->isIterator()) {
NativeIterator *ni = obj->getNativeIterator();
if (obj->isPropertyIterator()) {
NativeIterator *ni = obj->asPropertyIterator().getNativeIterator();
if (ni->flags & JSITER_ENUMERATE) {
JS_ASSERT(cx->enumerators == obj);
cx->enumerators = ni->next;
@@ -988,7 +1065,7 @@ template<typename StringPredicate>
static bool
SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate)
{
JSObject *iterobj = cx->enumerators;
PropertyIteratorObject *iterobj = cx->enumerators;
while (iterobj) {
again:
NativeIterator *ni = iterobj->getNativeIterator();
@@ -1111,83 +1188,14 @@ 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);
}
bool
js_IteratorMore(JSContext *cx, HandleObject iterobj, Value *rval)
{
/* Fast path for native iterators */
NativeIterator *ni = NULL;
if (iterobj->isIterator()) {
if (iterobj->isPropertyIterator()) {
/* Key iterators are handled by fast-paths. */
ni = iterobj->getNativeIterator();
ni = iterobj->asPropertyIterator().getNativeIterator();
bool more = ni->props_cursor < ni->props_end;
if (ni->isKeyIter() || !more) {
rval->setBoolean(more);
@@ -1215,25 +1223,6 @@ 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))
@@ -1261,12 +1250,12 @@ bool
js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
{
/* Fast path for native iterators */
if (iterobj->isIterator()) {
if (iterobj->isPropertyIterator()) {
/*
* Implement next directly as all the methods of the native iterator are
* read-only and permanent.
*/
NativeIterator *ni = iterobj->getNativeIterator();
NativeIterator *ni = iterobj->asPropertyIterator().getNativeIterator();
if (ni->isKeyIter()) {
JS_ASSERT(ni->props_cursor < ni->props_end);
*rval = StringValue(*ni->current());
@@ -1305,7 +1294,7 @@ stopiter_hasInstance(JSContext *cx, HandleObject obj, const Value *v, JSBool *bp
}
Class js::StopIterationClass = {
js_StopIteration_str,
"StopIteration",
JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) |
JSCLASS_FREEZE_PROTO,
JS_PropertyStub, /* addProperty */
@@ -1322,6 +1311,8 @@ Class js::StopIterationClass = {
NULL /* construct */
};
/*** Generators **********************************************************************************/
#if JS_HAS_GENERATORS
static void
@@ -1420,7 +1411,7 @@ Class js::GeneratorClass = {
NULL, /* equality */
NULL, /* outerObject */
NULL, /* innerObject */
iterator_iterator,
iterator_iteratorObject,
NULL /* unused */
}
};
@@ -1561,7 +1552,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
gen->regs = cx->regs();
cx->enterGenerator(gen); /* OOM check above. */
JSObject *enumerators = cx->enumerators;
PropertyIteratorObject *enumerators = cx->enumerators;
cx->enumerators = gen->enumerators;
ok = RunScript(cx, fp->script(), fp);
@@ -1751,92 +1742,88 @@ generator_close(JSContext *cx, unsigned argc, Value *vp)
return CallNonGenericMethod(cx, IsGenerator, generator_close_impl, args);
}
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
static JSFunctionSpec generator_methods[] = {
JS_FN(js_next_str, generator_next, 0,JSPROP_ROPERM),
JS_FN(js_send_str, generator_send, 1,JSPROP_ROPERM),
JS_FN(js_throw_str, generator_throw, 1,JSPROP_ROPERM),
JS_FN(js_close_str, generator_close, 0,JSPROP_ROPERM),
JS_FN("iterator", iterator_iterator, 0, 0),
JS_FN("next", generator_next, 0,JSPROP_ROPERM),
JS_FN("send", generator_send, 1,JSPROP_ROPERM),
JS_FN("throw", generator_throw, 1,JSPROP_ROPERM),
JS_FN("close", generator_close, 0,JSPROP_ROPERM),
JS_FS_END
};
#endif /* JS_HAS_GENERATORS */
static bool
InitIteratorClass(JSContext *cx, Handle<GlobalObject*> global)
{
RootedObject iteratorProto(cx, global->createBlankPrototype(cx, &IteratorClass));
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);
iteratorProto->setNativeIterator(ni);
RootedFunction ctor(cx, 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;
return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
}
/* static */ bool
GlobalObject::initGeneratorClass(JSContext *cx, Handle<GlobalObject*> global)
GlobalObject::initIteratorClasses(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;
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);
Rooted<JSFunction*> ctor(cx, 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;
}
Rooted<JSObject*> proto(cx);
if (global->getSlot(ELEMENT_ITERATOR_PROTO).isUndefined()) {
Class *cls = &ElementIteratorClass;
proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto);
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, ElementIteratorObject::methods))
return false;
global->setReservedSlot(ELEMENT_ITERATOR_PROTO, ObjectValue(*proto));
}
#if JS_HAS_GENERATORS
RootedObject proto(cx, global->createBlankPrototype(cx, &GeneratorClass));
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
return false;
global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
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));
}
#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());
/*
* 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.
*/
RootedObject iter(cx);
if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
if (!GlobalObject::initIteratorClasses(cx, global))
return NULL;
if (iter)
return iter;
if (!InitIteratorClass(cx, global) || !GlobalObject::initGeneratorClass(cx, global))
return NULL;
return InitStopIterationClass(cx, global);
return global->getIteratorPrototype();
}