mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 904701 - Implement prototype madness for ES6 generators. r=bhackett, r=jorendorff
Add a Generator constructor, like the Function constructor. Define the GeneratorFunctionPrototype, GeneratorFunction, and GeneratorObjectPrototype meta-objects. When cloning or creating a star generator, give it GeneratorFunctionPrototype as its prototype. Each star generator function has a ".prototype" property, which has GeneratorObjectPrototype as its prototype. That prototype does not have a ".constructor" link. If Function.prototype.toSource is called on a non-function, chain up to Object.prototype.toSource instead. Prototypes of generator objects are no longer made with GeneratorObject::class_. This allows us to elide some "null" checks from bug 352885. Instead calling a generator on a method now signals a typeerror. Make the "send" generator method a simple alias to "next".
This commit is contained in:
parent
7549ab3ad1
commit
bb8edee39e
@ -87,8 +87,8 @@ obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
|
||||
#if JS_HAS_TOSOURCE
|
||||
static bool
|
||||
obj_toSource(JSContext *cx, unsigned argc, Value *vp)
|
||||
bool
|
||||
js::obj_toSource(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
@ -14,10 +14,14 @@ namespace js {
|
||||
extern const JSFunctionSpec object_methods[];
|
||||
extern const JSFunctionSpec object_static_methods[];
|
||||
|
||||
/* Object constructor native. Exposed only so the JIT can know its address. */
|
||||
// Object constructor native. Exposed only so the JIT can know its address.
|
||||
extern bool
|
||||
obj_construct(JSContext *cx, unsigned argc, js::Value *vp);
|
||||
|
||||
// Object.prototype.toSource. Exposed so that Function.prototype.toSource can chain up.
|
||||
extern bool
|
||||
obj_toSource(JSContext *cx, unsigned argc, js::Value *vp);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* builtin_Object_h */
|
||||
|
@ -451,9 +451,10 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha
|
||||
|
||||
// Compile a JS function body, which might appear as the value of an event
|
||||
// handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
|
||||
bool
|
||||
frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length)
|
||||
static bool
|
||||
CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length,
|
||||
GeneratorKind generatorKind)
|
||||
{
|
||||
#if JS_TRACE_LOGGING
|
||||
js::AutoTraceLog logger(js::TraceLogging::defaultLogger(),
|
||||
@ -520,7 +521,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
|
||||
ParseNode *fn;
|
||||
while (true) {
|
||||
Directives newDirectives = directives;
|
||||
fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives);
|
||||
fn = parser.standaloneFunctionBody(fun, formals, generatorKind, directives, &newDirectives);
|
||||
if (fn)
|
||||
break;
|
||||
|
||||
@ -585,3 +586,18 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length)
|
||||
{
|
||||
return CompileFunctionBody(cx, fun, options, formals, chars, length, NotGenerator);
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun,
|
||||
CompileOptions options, const AutoNameVector &formals,
|
||||
const jschar *chars, size_t length)
|
||||
{
|
||||
return CompileFunctionBody(cx, fun, options, formals, chars, length, StarGenerator);
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *chars, size_t
|
||||
bool
|
||||
CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length);
|
||||
bool
|
||||
CompileStarGeneratorBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
|
||||
const AutoNameVector &formals, const jschar *chars, size_t length);
|
||||
|
||||
/*
|
||||
* This should be called while still on the main thread if compilation will
|
||||
|
@ -1244,7 +1244,7 @@ struct BindData
|
||||
template <typename ParseHandler>
|
||||
JSFunction *
|
||||
Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
|
||||
FunctionSyntaxKind kind)
|
||||
FunctionSyntaxKind kind, JSObject *proto)
|
||||
{
|
||||
JS_ASSERT_IF(kind == Statement, atom != NULL);
|
||||
|
||||
@ -1267,8 +1267,8 @@ Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
|
||||
: (kind == Arrow)
|
||||
? JSFunction::INTERPRETED_LAMBDA_ARROW
|
||||
: JSFunction::INTERPRETED;
|
||||
fun = NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom,
|
||||
JSFunction::FinalizeKind, MaybeSingletonObject);
|
||||
fun = NewFunctionWithProto(context, NullPtr(), NULL, 0, flags, parent, atom, proto,
|
||||
JSFunction::FinalizeKind, MaybeSingletonObject);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
if (options().selfHostingMode)
|
||||
@ -1999,7 +1999,17 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
||||
if (bodyProcessed)
|
||||
return pn;
|
||||
|
||||
RootedFunction fun(context, newFunction(pc, funName, kind));
|
||||
RootedObject proto(context);
|
||||
if (generatorKind == StarGenerator) {
|
||||
// If we are off the main thread, the generator meta-objects have
|
||||
// already been created by js::StartOffThreadParseScript, so cx will not
|
||||
// be necessary.
|
||||
JSContext *cx = context->maybeJSContext();
|
||||
proto = context->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
|
||||
if (!proto)
|
||||
return null();
|
||||
}
|
||||
RootedFunction fun(context, newFunction(pc, funName, kind, proto));
|
||||
if (!fun)
|
||||
return null();
|
||||
|
||||
|
@ -407,7 +407,8 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
* Create a new function object given parse context (pc) and a name (which
|
||||
* is optional if this is a function expression).
|
||||
*/
|
||||
JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind);
|
||||
JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind,
|
||||
JSObject *proto = NULL);
|
||||
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
|
@ -2904,7 +2904,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 + 25)
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT (JSProto_LIMIT * 3 + 26)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
177
js/src/jsfun.cpp
177
js/src/jsfun.cpp
@ -27,6 +27,7 @@
|
||||
#include "jswrapper.h"
|
||||
|
||||
#include "builtin/Eval.h"
|
||||
#include "builtin/Object.h"
|
||||
#include "frontend/BytecodeCompiler.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "gc/Marking.h"
|
||||
@ -195,40 +196,51 @@ ResolveInterpretedFunctionPrototype(JSContext *cx, HandleObject obj)
|
||||
JS_ASSERT(!fun->isFunctionPrototype());
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Assert that fun is not a compiler-created function object, which
|
||||
* must never leak to script or embedding code and then be mutated.
|
||||
* Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
|
||||
*/
|
||||
// Assert that fun is not a compiler-created function object, which
|
||||
// must never leak to script or embedding code and then be mutated.
|
||||
// Also assert that obj is not bound, per the ES5 15.3.4.5 ref above.
|
||||
JS_ASSERT(!IsInternalFunctionObject(obj));
|
||||
JS_ASSERT(!obj->isBoundFunction());
|
||||
|
||||
/*
|
||||
* Make the prototype object an instance of Object with the same parent
|
||||
* as the function object itself.
|
||||
*/
|
||||
JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx);
|
||||
// Make the prototype object an instance of Object with the same parent as
|
||||
// the function object itself, unless the function is an ES6 generator. In
|
||||
// that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is
|
||||
// the GeneratorObjectPrototype singleton.
|
||||
bool isStarGenerator = obj->as<JSFunction>().isStarGenerator();
|
||||
JSObject *objProto;
|
||||
if (isStarGenerator)
|
||||
objProto = obj->global().getOrCreateStarGeneratorObjectPrototype(cx);
|
||||
else
|
||||
objProto = obj->global().getOrCreateObjectPrototype(cx);
|
||||
if (!objProto)
|
||||
return NULL;
|
||||
RootedObject proto(cx, NewObjectWithGivenProto(cx, &JSObject::class_, objProto, NULL, SingletonObject));
|
||||
Class *clasp = &JSObject::class_;
|
||||
|
||||
RootedObject proto(cx, NewObjectWithGivenProto(cx, clasp, objProto, NULL, SingletonObject));
|
||||
if (!proto)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Per ES5 15.3.5.2 a user-defined function's .prototype property is
|
||||
* initially non-configurable, non-enumerable, and writable. Per ES5 13.2
|
||||
* the prototype's .constructor property is configurable, non-enumerable,
|
||||
* and writable.
|
||||
*/
|
||||
// Per ES5 15.3.5.2 a user-defined function's .prototype property is
|
||||
// initially non-configurable, non-enumerable, and writable.
|
||||
RootedValue protoVal(cx, ObjectValue(*proto));
|
||||
RootedValue objVal(cx, ObjectValue(*obj));
|
||||
if (!JSObject::defineProperty(cx, obj, cx->names().classPrototype,
|
||||
protoVal, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_PERMANENT) ||
|
||||
!JSObject::defineProperty(cx, proto, cx->names().constructor,
|
||||
objVal, JS_PropertyStub, JS_StrictPropertyStub, 0))
|
||||
JSPROP_PERMANENT))
|
||||
{
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Per ES5 13.2 the prototype's .constructor property is configurable,
|
||||
// non-enumerable, and writable. However, per the 15 July 2013 ES6 draft,
|
||||
// section 15.19.3, the .prototype of a generator function does not link
|
||||
// back with a .constructor.
|
||||
if (!isStarGenerator) {
|
||||
RootedValue objVal(cx, ObjectValue(*obj));
|
||||
if (!JSObject::defineProperty(cx, proto, cx->names().constructor,
|
||||
objVal, JS_PropertyStub, JS_StrictPropertyStub, 0))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return proto;
|
||||
@ -327,12 +339,15 @@ bool
|
||||
js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
|
||||
MutableHandleObject objp)
|
||||
{
|
||||
enum FirstWordFlag {
|
||||
HasAtom = 0x1,
|
||||
IsStarGenerator = 0x2
|
||||
};
|
||||
|
||||
/* NB: Keep this in sync with CloneFunctionAndScript. */
|
||||
RootedAtom atom(xdr->cx());
|
||||
uint32_t firstword; /* flag telling whether fun->atom is non-null,
|
||||
plus for fun->u.i.skipmin, fun->u.i.wrapper,
|
||||
and 14 bits reserved for future use */
|
||||
uint32_t flagsword; /* word for argument count and fun->flags */
|
||||
uint32_t firstword = 0; /* bitmask of FirstWordFlag */
|
||||
uint32_t flagsword = 0; /* word for argument count and fun->flags */
|
||||
|
||||
JSContext *cx = xdr->cx();
|
||||
RootedFunction fun(cx);
|
||||
@ -347,24 +362,38 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, Han
|
||||
}
|
||||
return false;
|
||||
}
|
||||
firstword = !!fun->atom();
|
||||
if (fun->atom())
|
||||
firstword |= HasAtom;
|
||||
if (fun->isStarGenerator())
|
||||
firstword |= IsStarGenerator;
|
||||
script = fun->getOrCreateScript(cx);
|
||||
if (!script)
|
||||
return false;
|
||||
atom = fun->atom();
|
||||
flagsword = (fun->nargs << 16) | fun->flags;
|
||||
|
||||
if (!xdr->codeUint32(&firstword))
|
||||
return false;
|
||||
} else {
|
||||
fun = NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED, NullPtr(), NullPtr(),
|
||||
JSFunction::FinalizeKind, TenuredObject);
|
||||
if (!xdr->codeUint32(&firstword))
|
||||
return false;
|
||||
|
||||
JSObject *proto = NULL;
|
||||
if (firstword & IsStarGenerator) {
|
||||
proto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
fun = NewFunctionWithProto(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED,
|
||||
NullPtr(), NullPtr(), proto,
|
||||
JSFunction::FinalizeKind, TenuredObject);
|
||||
if (!fun)
|
||||
return false;
|
||||
atom = NULL;
|
||||
script = NULL;
|
||||
}
|
||||
|
||||
if (!xdr->codeUint32(&firstword))
|
||||
return false;
|
||||
if ((firstword & 1U) && !XDRAtom(xdr, &atom))
|
||||
if ((firstword & HasAtom) && !XDRAtom(xdr, &atom))
|
||||
return false;
|
||||
if (!xdr->codeUint32(&flagsword))
|
||||
return false;
|
||||
@ -399,10 +428,15 @@ JSObject *
|
||||
js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
|
||||
{
|
||||
/* NB: Keep this in sync with XDRInterpretedFunction. */
|
||||
|
||||
RootedFunction clone(cx, NewFunction(cx, NullPtr(), NULL, 0,
|
||||
JSFunction::INTERPRETED, NullPtr(), NullPtr(),
|
||||
JSFunction::FinalizeKind, TenuredObject));
|
||||
JSObject *cloneProto = NULL;
|
||||
if (srcFun->isStarGenerator()) {
|
||||
cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
|
||||
if (!cloneProto)
|
||||
return NULL;
|
||||
}
|
||||
RootedFunction clone(cx, NewFunctionWithProto(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED,
|
||||
NullPtr(), NullPtr(), cloneProto,
|
||||
JSFunction::FinalizeKind, TenuredObject));
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
@ -605,7 +639,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
|
||||
return NULL;
|
||||
}
|
||||
if (!fun->isArrow()) {
|
||||
if (!out.append("function "))
|
||||
if (!(fun->isStarGenerator() ? out.append("function* ") : out.append("function ")))
|
||||
return NULL;
|
||||
}
|
||||
if (fun->atom()) {
|
||||
@ -795,6 +829,9 @@ fun_toSource(JSContext *cx, unsigned argc, Value *vp)
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
if (!obj->is<JSFunction>() && !obj->is<FunctionProxyObject>())
|
||||
return obj_toSource(cx, argc, vp);
|
||||
|
||||
RootedString str(cx, fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT));
|
||||
if (!str)
|
||||
return false;
|
||||
@ -1327,8 +1364,8 @@ const JSFunctionSpec js::function_methods[] = {
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
bool
|
||||
js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
static bool
|
||||
FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind generatorKind)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedString arg(cx); // used multiple times below
|
||||
@ -1345,6 +1382,9 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
||||
bool hasRest = false;
|
||||
|
||||
bool isStarGenerator = generatorKind == StarGenerator;
|
||||
JS_ASSERT(generatorKind != LegacyGenerator);
|
||||
|
||||
const char *filename;
|
||||
unsigned lineno;
|
||||
JSPrincipals *originPrincipals;
|
||||
@ -1437,6 +1477,7 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
*/
|
||||
TokenStream ts(cx, options, collected_args.get(), args_length,
|
||||
/* strictModeGetter = */ NULL);
|
||||
bool yieldIsValidName = ts.versionNumber() < JSVERSION_1_7 && !isStarGenerator;
|
||||
|
||||
/* The argument string may be empty or contain no tokens. */
|
||||
TokenKind tt = ts.getToken();
|
||||
@ -1451,14 +1492,14 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
|
||||
if (tt == TOK_YIELD && yieldIsValidName)
|
||||
tt = TOK_NAME;
|
||||
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
hasRest = true;
|
||||
tt = ts.getToken();
|
||||
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
|
||||
if (tt == TOK_YIELD && yieldIsValidName)
|
||||
tt = TOK_NAME;
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt != TOK_ERROR)
|
||||
@ -1519,24 +1560,47 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
* and so would a call to f from another top-level's script or function.
|
||||
*/
|
||||
RootedAtom anonymousAtom(cx, cx->names().anonymous);
|
||||
RootedFunction fun(cx, NewFunction(cx, NullPtr(), NULL, 0, JSFunction::INTERPRETED_LAMBDA,
|
||||
global, anonymousAtom, JSFunction::FinalizeKind,
|
||||
TenuredObject));
|
||||
JSObject *proto = NULL;
|
||||
if (isStarGenerator) {
|
||||
proto = global->getOrCreateStarGeneratorFunctionPrototype(cx);
|
||||
if (!proto)
|
||||
return false;
|
||||
}
|
||||
RootedFunction fun(cx, NewFunctionWithProto(cx, NullPtr(), NULL, 0,
|
||||
JSFunction::INTERPRETED_LAMBDA, global,
|
||||
anonymousAtom, proto,
|
||||
JSFunction::FinalizeKind, TenuredObject));
|
||||
if (!fun)
|
||||
return false;
|
||||
|
||||
if (hasRest)
|
||||
fun->setHasRest();
|
||||
|
||||
bool ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length);
|
||||
bool ok;
|
||||
if (isStarGenerator)
|
||||
ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, chars, length);
|
||||
else
|
||||
ok = frontend::CompileFunctionBody(cx, &fun, options, formals, chars, length);
|
||||
args.rval().setObject(*fun);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
return FunctionConstructor(cx, argc, vp, NotGenerator);
|
||||
}
|
||||
|
||||
bool
|
||||
js::Generator(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
return FunctionConstructor(cx, argc, vp, StarGenerator);
|
||||
}
|
||||
|
||||
bool
|
||||
JSFunction::isBuiltinFunctionConstructor()
|
||||
{
|
||||
return maybeNative() == Function;
|
||||
return maybeNative() == Function || maybeNative() == Generator;
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
@ -1544,6 +1608,17 @@ js::NewFunction(ExclusiveContext *cx, HandleObject funobjArg, Native native, uns
|
||||
JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
|
||||
gc::AllocKind allocKind /* = JSFunction::FinalizeKind */,
|
||||
NewObjectKind newKind /* = GenericObject */)
|
||||
{
|
||||
return NewFunctionWithProto(cx, funobjArg, native, nargs, flags, parent, atom, NULL,
|
||||
allocKind, newKind);
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
js::NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobjArg, Native native,
|
||||
unsigned nargs, JSFunction::Flags flags, HandleObject parent,
|
||||
HandleAtom atom, JSObject *proto,
|
||||
gc::AllocKind allocKind /* = JSFunction::FinalizeKind */,
|
||||
NewObjectKind newKind /* = GenericObject */)
|
||||
{
|
||||
JS_ASSERT(allocKind == JSFunction::FinalizeKind || allocKind == JSFunction::ExtendedFinalizeKind);
|
||||
JS_ASSERT(sizeof(JSFunction) <= gc::Arena::thingSize(JSFunction::FinalizeKind));
|
||||
@ -1560,7 +1635,7 @@ js::NewFunction(ExclusiveContext *cx, HandleObject funobjArg, Native native, uns
|
||||
// that hasSingletonType implies isInterpreted.
|
||||
if (native && !IsAsmJSModuleNative(native))
|
||||
newKind = SingletonObject;
|
||||
funobj = NewObjectWithClassProto(cx, &JSFunction::class_, NULL,
|
||||
funobj = NewObjectWithClassProto(cx, &JSFunction::class_, proto,
|
||||
SkipScopeParent(parent), allocKind, newKind);
|
||||
if (!funobj)
|
||||
return NULL;
|
||||
@ -1603,7 +1678,13 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
|
||||
return NULL;
|
||||
|
||||
NewObjectKind newKind = useSameScript ? newKindArg : SingletonObject;
|
||||
JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, NULL,
|
||||
JSObject *cloneProto = NULL;
|
||||
if (fun->isStarGenerator()) {
|
||||
cloneProto = cx->global()->getOrCreateStarGeneratorFunctionPrototype(cx);
|
||||
if (!cloneProto)
|
||||
return NULL;
|
||||
}
|
||||
JSObject *cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto,
|
||||
SkipScopeParent(parent), allocKind, newKind);
|
||||
if (!cloneobj)
|
||||
return NULL;
|
||||
|
@ -410,12 +410,22 @@ namespace js {
|
||||
extern bool
|
||||
Function(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
extern bool
|
||||
Generator(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
extern JSFunction *
|
||||
NewFunction(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs,
|
||||
JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
|
||||
gc::AllocKind allocKind = JSFunction::FinalizeKind,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
|
||||
// If proto is NULL, Function.prototype is used instead.
|
||||
extern JSFunction *
|
||||
NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobj, JSNative native, unsigned nargs,
|
||||
JSFunction::Flags flags, HandleObject parent, HandleAtom atom,
|
||||
JSObject *proto, gc::AllocKind allocKind = JSFunction::FinalizeKind,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
|
||||
extern JSFunction *
|
||||
DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native,
|
||||
unsigned nargs, unsigned flags,
|
||||
|
@ -958,7 +958,7 @@ const JSFunctionSpec ElementIteratorObject::methods[] = {
|
||||
};
|
||||
|
||||
static bool
|
||||
CloseGenerator(JSContext *cx, HandleObject genobj);
|
||||
CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
|
||||
|
||||
bool
|
||||
js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
|
||||
@ -999,6 +999,30 @@ js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
|
||||
return GetIterator(cx, obj, flags, vp);
|
||||
}
|
||||
|
||||
bool
|
||||
IsGenerator(HandleValue v)
|
||||
{
|
||||
return v.isObject() && v.toObject().is<GeneratorObject>();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsLegacyGenerator(HandleObject obj)
|
||||
{
|
||||
if (!obj->is<GeneratorObject>())
|
||||
return false;
|
||||
JSGenerator *gen = obj->as<GeneratorObject>().getGenerator();
|
||||
return gen->regs.fp()->script()->isLegacyGenerator();
|
||||
}
|
||||
|
||||
bool
|
||||
IsLegacyGenerator(HandleValue v)
|
||||
{
|
||||
if (!IsGenerator(v))
|
||||
return false;
|
||||
JSGenerator *gen = v.toObject().as<GeneratorObject>().getGenerator();
|
||||
return gen->regs.fp()->script()->isLegacyGenerator();
|
||||
}
|
||||
|
||||
bool
|
||||
js::CloseIterator(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
@ -1020,9 +1044,8 @@ js::CloseIterator(JSContext *cx, HandleObject obj)
|
||||
*/
|
||||
ni->props_cursor = ni->props_array;
|
||||
}
|
||||
} else if (obj->is<GeneratorObject>()) {
|
||||
// FIXME: Only close legacy generators.
|
||||
return CloseGenerator(cx, obj);
|
||||
} else if (IsLegacyGenerator(obj)) {
|
||||
return CloseLegacyGenerator(cx, obj);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1477,15 +1500,25 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
|
||||
|
||||
JS_ASSERT(stackfp->script()->isGenerator());
|
||||
|
||||
if (stackfp->script()->isStarGenerator()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Rooted<GlobalObject*> global(cx, &stackfp->global());
|
||||
RootedObject obj(cx);
|
||||
{
|
||||
JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
|
||||
if (stackfp->script()->isStarGenerator()) {
|
||||
RootedValue pval(cx);
|
||||
RootedObject fun(cx, stackfp->fun());
|
||||
// FIXME: This would be faster if we could avoid doing a lookup to get
|
||||
// the prototype for the instance. Bug 906600.
|
||||
if (!JSObject::getProperty(cx, fun, fun, cx->names().classPrototype, &pval))
|
||||
return NULL;
|
||||
JSObject *proto = pval.isObject() ? &pval.toObject() : NULL;
|
||||
if (!proto) {
|
||||
proto = global->getOrCreateStarGeneratorObjectPrototype(cx);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
}
|
||||
obj = NewObjectWithGivenProto(cx, &GeneratorObject::class_, proto, global);
|
||||
} else {
|
||||
JS_ASSERT(stackfp->script()->isLegacyGenerator());
|
||||
JSObject *proto = global->getOrCreateLegacyGeneratorObjectPrototype(cx);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
obj = NewObjectWithGivenProto(cx, &GeneratorObject::class_, proto, global);
|
||||
@ -1623,66 +1656,17 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj,
|
||||
}
|
||||
|
||||
static bool
|
||||
CloseGenerator(JSContext *cx, HandleObject obj)
|
||||
CloseLegacyGenerator(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
JS_ASSERT(IsLegacyGenerator(obj));
|
||||
|
||||
JSGenerator *gen = obj->as<GeneratorObject>().getGenerator();
|
||||
if (!gen) {
|
||||
/* Generator prototype object. */
|
||||
return true;
|
||||
}
|
||||
|
||||
// FIXME: Assert that gen is a legacy generator.
|
||||
|
||||
if (gen->state == JSGEN_CLOSED)
|
||||
return true;
|
||||
|
||||
return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
IsGenerator(HandleValue v)
|
||||
{
|
||||
return v.isObject() && v.toObject().is<GeneratorObject>();
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
generator_send_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
// FIXME: Change assertion to IsLegacyGenerator().
|
||||
JS_ASSERT(IsGenerator(args.thisv()));
|
||||
|
||||
RootedObject thisObj(cx, &args.thisv().toObject());
|
||||
|
||||
JSGenerator *gen = thisObj->as<GeneratorObject>().getGenerator();
|
||||
if (!gen || gen->state == JSGEN_CLOSED) {
|
||||
/* This happens when obj is the generator prototype. See bug 352885. */
|
||||
return js_ThrowStopIteration(cx);
|
||||
}
|
||||
|
||||
if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
|
||||
RootedValue val(cx, args[0]);
|
||||
js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
|
||||
JSDVG_SEARCH_STACK, val, NullPtr());
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: next() takes the send value as an optional argument in ES6
|
||||
// generator objects.
|
||||
if (!SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0)))
|
||||
return false;
|
||||
|
||||
args.rval().set(gen->fp->returnValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
generator_send(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
// FIXME: send() is only a method on legacy generator objects.
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsGenerator, generator_send_impl>(cx, args);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
generator_next_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
@ -1691,12 +1675,17 @@ generator_next_impl(JSContext *cx, CallArgs args)
|
||||
RootedObject thisObj(cx, &args.thisv().toObject());
|
||||
|
||||
JSGenerator *gen = thisObj->as<GeneratorObject>().getGenerator();
|
||||
if (!gen || gen->state == JSGEN_CLOSED) {
|
||||
/* This happens when obj is the generator prototype. See bug 352885. */
|
||||
if (gen->state == JSGEN_CLOSED)
|
||||
return js_ThrowStopIteration(cx);
|
||||
|
||||
if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
|
||||
RootedValue val(cx, args[0]);
|
||||
js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
|
||||
JSDVG_SEARCH_STACK, val, NullPtr());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SendToGenerator(cx, JSGENOP_NEXT, thisObj, gen, JS::UndefinedHandleValue))
|
||||
if (!SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0)))
|
||||
return false;
|
||||
|
||||
args.rval().set(gen->fp->returnValue());
|
||||
@ -1718,8 +1707,7 @@ generator_throw_impl(JSContext *cx, CallArgs args)
|
||||
RootedObject thisObj(cx, &args.thisv().toObject());
|
||||
|
||||
JSGenerator *gen = thisObj->as<GeneratorObject>().getGenerator();
|
||||
if (!gen || gen->state == JSGEN_CLOSED) {
|
||||
/* This happens when obj is the generator prototype. See bug 352885. */
|
||||
if (gen->state == JSGEN_CLOSED) {
|
||||
cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
|
||||
return false;
|
||||
}
|
||||
@ -1741,14 +1729,12 @@ generator_throw(JSContext *cx, unsigned argc, Value *vp)
|
||||
JS_ALWAYS_INLINE bool
|
||||
generator_close_impl(JSContext *cx, CallArgs args)
|
||||
{
|
||||
// FIXME: Change assertion to IsLegacyGenerator().
|
||||
JS_ASSERT(IsGenerator(args.thisv()));
|
||||
JS_ASSERT(IsLegacyGenerator(args.thisv()));
|
||||
|
||||
RootedObject thisObj(cx, &args.thisv().toObject());
|
||||
|
||||
JSGenerator *gen = thisObj->as<GeneratorObject>().getGenerator();
|
||||
if (!gen || gen->state == JSGEN_CLOSED) {
|
||||
/* This happens when obj is the generator prototype. See bug 352885. */
|
||||
if (gen->state == JSGEN_CLOSED) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
@ -1769,22 +1755,47 @@ generator_close_impl(JSContext *cx, CallArgs args)
|
||||
bool
|
||||
generator_close(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
// FIXME: close() is only a method on legacy generator objects.
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsGenerator, generator_close_impl>(cx, args);
|
||||
return CallNonGenericMethod<IsLegacyGenerator, generator_close_impl>(cx, args);
|
||||
}
|
||||
|
||||
#define JSPROP_ROPERM (JSPROP_READONLY | JSPROP_PERMANENT)
|
||||
|
||||
static const JSFunctionSpec generator_methods[] = {
|
||||
static const JSFunctionSpec legacy_generator_methods[] = {
|
||||
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("next", generator_next, 1,JSPROP_ROPERM),
|
||||
// Send is exactly the same as next.
|
||||
JS_FN("send", generator_next, 1,JSPROP_ROPERM),
|
||||
JS_FN("throw", generator_throw, 1,JSPROP_ROPERM),
|
||||
JS_FN("close", generator_close, 0,JSPROP_ROPERM),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSFunctionSpec star_generator_methods[] = {
|
||||
JS_FN("iterator", iterator_iterator, 0, 0),
|
||||
JS_FN("next", generator_next, 1,JSPROP_ROPERM),
|
||||
JS_FN("throw", generator_throw, 1,JSPROP_ROPERM),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static JSObject*
|
||||
NewObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global)
|
||||
{
|
||||
JSObject *proto = global->getOrCreateObjectPrototype(cx);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global);
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
NewObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global)
|
||||
{
|
||||
JSObject *proto = global->getOrCreateFunctionPrototype(cx);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
|
||||
{
|
||||
@ -1826,11 +1837,41 @@ GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
|
||||
global->setReservedSlot(ELEMENT_ITERATOR_PROTO, ObjectValue(*proto));
|
||||
}
|
||||
|
||||
if (global->getSlot(GENERATOR_PROTO).isUndefined()) {
|
||||
proto = global->createBlankPrototype(cx, &GeneratorObject::class_);
|
||||
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
|
||||
if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
|
||||
proto = NewObjectWithObjectPrototype(cx, global);
|
||||
if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, legacy_generator_methods))
|
||||
return false;
|
||||
global->setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
|
||||
global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
|
||||
}
|
||||
|
||||
if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) {
|
||||
RootedObject genObjectProto(cx, NewObjectWithObjectPrototype(cx, global));
|
||||
if (!genObjectProto)
|
||||
return false;
|
||||
if (!DefinePropertiesAndBrand(cx, genObjectProto, NULL, star_generator_methods))
|
||||
return false;
|
||||
|
||||
RootedObject genFunctionProto(cx, NewObjectWithFunctionPrototype(cx, global));
|
||||
if (!genFunctionProto)
|
||||
return false;
|
||||
if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
|
||||
return false;
|
||||
|
||||
RootedValue function(cx, global->getConstructor(JSProto_Function));
|
||||
if (!function.toObjectOrNull())
|
||||
return false;
|
||||
RootedAtom name(cx, cx->names().GeneratorFunction);
|
||||
RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1,
|
||||
JSFunction::NATIVE_CTOR, global, name,
|
||||
&function.toObject()));
|
||||
if (!genFunction)
|
||||
return false;
|
||||
if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
|
||||
return false;
|
||||
|
||||
global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
|
||||
global->setSlot(JSProto_GeneratorFunction, ObjectValue(*genFunction));
|
||||
global->setSlot(JSProto_GeneratorFunction + JSProto_LIMIT, ObjectValue(*genFunctionProto));
|
||||
}
|
||||
|
||||
if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
|
||||
|
@ -69,5 +69,6 @@
|
||||
macro(ArrayType, 50, js_InitBinaryDataClasses) \
|
||||
macro(StructType, 51, js_InitBinaryDataClasses) \
|
||||
macro(ArrayTypeObject, 52, js_InitBinaryDataClasses) \
|
||||
macro(GeneratorFunction, 53, js_InitIteratorClasses) \
|
||||
|
||||
#endif /* jsprototypes_h */
|
||||
|
@ -236,7 +236,8 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
|
||||
// pointers can be changed infallibly after parsing finishes.
|
||||
if (!js_GetClassObject(cx, cx->global(), JSProto_Function, &obj) ||
|
||||
!js_GetClassObject(cx, cx->global(), JSProto_Array, &obj) ||
|
||||
!js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj))
|
||||
!js_GetClassObject(cx, cx->global(), JSProto_RegExp, &obj) ||
|
||||
!js_GetClassObject(cx, cx->global(), JSProto_GeneratorFunction, &obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -244,7 +245,8 @@ js::StartOffThreadParseScript(JSContext *cx, const CompileOptions &options,
|
||||
AutoCompartment ac(cx, global);
|
||||
if (!js_GetClassObject(cx, global, JSProto_Function, &obj) ||
|
||||
!js_GetClassObject(cx, global, JSProto_Array, &obj) ||
|
||||
!js_GetClassObject(cx, global, JSProto_RegExp, &obj))
|
||||
!js_GetClassObject(cx, global, JSProto_RegExp, &obj) ||
|
||||
!js_GetClassObject(cx, global, JSProto_GeneratorFunction, &obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
150
js/src/tests/ecma_6/Generators/runtime.js
Normal file
150
js/src/tests/ecma_6/Generators/runtime.js
Normal file
@ -0,0 +1,150 @@
|
||||
// This file was written by Andy Wingo <wingo@igalia.com> and originally
|
||||
// contributed to V8 as generators-runtime.js, available here:
|
||||
//
|
||||
// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-runtime.js
|
||||
|
||||
// Test aspects of the generator runtime.
|
||||
|
||||
// See http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.19.3.
|
||||
|
||||
function assertSyntaxError(str) {
|
||||
var msg;
|
||||
var evil = eval;
|
||||
try {
|
||||
// Non-direct eval.
|
||||
evil(str);
|
||||
} catch (exc) {
|
||||
if (exc instanceof SyntaxError)
|
||||
return;
|
||||
msg = "Assertion failed: expected SyntaxError, got " + exc;
|
||||
}
|
||||
if (msg === undefined)
|
||||
msg = "Assertion failed: expected SyntaxError, but no exception thrown";
|
||||
throw new Error(msg + " - " + str);
|
||||
}
|
||||
|
||||
function assertFalse(a) { assertEq(a, false) }
|
||||
function assertTrue(a) { assertEq(a, true) }
|
||||
function assertNotEq(found, not_expected) { assertFalse(found === expected) }
|
||||
function assertArrayEq(found, expected) {
|
||||
assertEq(found.length, expected.length);
|
||||
for (var i = 0; i < expected.length; i++)
|
||||
assertEq(found[i], expected[i]);
|
||||
}
|
||||
|
||||
|
||||
function f() { }
|
||||
function* g() { yield 1; }
|
||||
var GeneratorFunctionPrototype = Object.getPrototypeOf(g);
|
||||
var GeneratorFunction = GeneratorFunctionPrototype.constructor;
|
||||
var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
|
||||
|
||||
|
||||
// A generator function should have the same set of properties as any
|
||||
// other function.
|
||||
function TestGeneratorFunctionInstance() {
|
||||
var f_own_property_names = Object.getOwnPropertyNames(f);
|
||||
var g_own_property_names = Object.getOwnPropertyNames(g);
|
||||
|
||||
f_own_property_names.sort();
|
||||
g_own_property_names.sort();
|
||||
|
||||
assertArrayEq(f_own_property_names, g_own_property_names);
|
||||
var i;
|
||||
for (i = 0; i < f_own_property_names.length; i++) {
|
||||
var prop = f_own_property_names[i];
|
||||
var f_desc = Object.getOwnPropertyDescriptor(f, prop);
|
||||
var g_desc = Object.getOwnPropertyDescriptor(g, prop);
|
||||
assertEq(f_desc.configurable, g_desc.configurable, prop);
|
||||
assertEq(f_desc.writable, g_desc.writable, prop);
|
||||
assertEq(f_desc.enumerable, g_desc.enumerable, prop);
|
||||
}
|
||||
}
|
||||
TestGeneratorFunctionInstance();
|
||||
|
||||
|
||||
// Generators have an additional object interposed in the chain between
|
||||
// themselves and Function.prototype.
|
||||
function TestGeneratorFunctionPrototype() {
|
||||
// Sanity check.
|
||||
assertEq(Object.getPrototypeOf(f), Function.prototype);
|
||||
assertNotEq(GeneratorFunctionPrototype, Function.prototype);
|
||||
assertEq(Object.getPrototypeOf(GeneratorFunctionPrototype),
|
||||
Function.prototype);
|
||||
assertEq(Object.getPrototypeOf(function* () {}),
|
||||
GeneratorFunctionPrototype);
|
||||
}
|
||||
TestGeneratorFunctionPrototype();
|
||||
|
||||
|
||||
// Functions that we associate with generator objects are actually defined by
|
||||
// a common prototype.
|
||||
function TestGeneratorObjectPrototype() {
|
||||
assertEq(Object.getPrototypeOf(GeneratorObjectPrototype),
|
||||
Object.prototype);
|
||||
assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype),
|
||||
GeneratorObjectPrototype);
|
||||
|
||||
var expected_property_names = ["iterator", "next", "throw", "constructor"];
|
||||
var found_property_names =
|
||||
Object.getOwnPropertyNames(GeneratorObjectPrototype);
|
||||
|
||||
expected_property_names.sort();
|
||||
found_property_names.sort();
|
||||
|
||||
assertArrayEq(found_property_names, expected_property_names);
|
||||
}
|
||||
TestGeneratorObjectPrototype();
|
||||
|
||||
|
||||
// This tests the object that would be called "GeneratorFunction", if it were
|
||||
// like "Function".
|
||||
function TestGeneratorFunction() {
|
||||
assertEq(GeneratorFunctionPrototype, GeneratorFunction.prototype);
|
||||
assertTrue(g instanceof GeneratorFunction);
|
||||
|
||||
assertEq(Function, Object.getPrototypeOf(GeneratorFunction));
|
||||
assertTrue(g instanceof Function);
|
||||
|
||||
assertEq("function* g() { yield 1; }", g.toString());
|
||||
|
||||
// Not all functions are generators.
|
||||
assertTrue(f instanceof Function); // Sanity check.
|
||||
assertFalse(f instanceof GeneratorFunction);
|
||||
|
||||
assertTrue((new GeneratorFunction()) instanceof GeneratorFunction);
|
||||
assertTrue(GeneratorFunction() instanceof GeneratorFunction);
|
||||
|
||||
assertTrue(GeneratorFunction('yield 1') instanceof GeneratorFunction);
|
||||
assertTrue(GeneratorFunction('return 1') instanceof GeneratorFunction);
|
||||
assertTrue(GeneratorFunction('a', 'yield a') instanceof GeneratorFunction);
|
||||
assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction);
|
||||
assertTrue(GeneratorFunction('a', 'return a') instanceof GeneratorFunction);
|
||||
assertSyntaxError("GeneratorFunction('yield', 'return yield')");
|
||||
assertTrue(GeneratorFunction('with (x) return foo;') instanceof GeneratorFunction);
|
||||
assertSyntaxError("GeneratorFunction('\"use strict\"; with (x) return foo;')");
|
||||
|
||||
// Doesn't matter particularly what string gets serialized, as long
|
||||
// as it contains "function*" and "yield 10".
|
||||
assertEq(GeneratorFunction('yield 10').toString(),
|
||||
"function* anonymous() {\nyield 10\n}");
|
||||
}
|
||||
TestGeneratorFunction();
|
||||
|
||||
|
||||
function TestPerGeneratorPrototype() {
|
||||
assertNotEq((function*(){}).prototype, (function*(){}).prototype);
|
||||
assertNotEq((function*(){}).prototype, g.prototype);
|
||||
assertEq(typeof GeneratorFunctionPrototype, "object");
|
||||
assertEq(g.prototype.__proto__.constructor, GeneratorFunctionPrototype, "object");
|
||||
assertEq(Object.getPrototypeOf(g.prototype), GeneratorObjectPrototype);
|
||||
assertFalse(g.prototype instanceof Function);
|
||||
assertEq(typeof (g.prototype), "object");
|
||||
|
||||
assertArrayEq(Object.getOwnPropertyNames(g.prototype), []);
|
||||
}
|
||||
TestPerGeneratorPrototype();
|
||||
|
||||
|
||||
if (typeof reportCompare == "function")
|
||||
reportCompare(true, true);
|
@ -26,34 +26,35 @@ function test()
|
||||
|
||||
try {
|
||||
proto.next();
|
||||
throw "generatorProto.next() does not throw StopIteration";
|
||||
throw "generatorProto.next() does not throw TypeError";
|
||||
} catch (e) {
|
||||
if (!(e instanceof StopIteration))
|
||||
if (!(e instanceof TypeError))
|
||||
throw "generatorProto.next() throws unexpected exception: "+uneval(e);
|
||||
}
|
||||
|
||||
try {
|
||||
proto.send();
|
||||
throw "generatorProto.send() does not throw StopIteration";
|
||||
throw "generatorProto.send() does not throw TypeError";
|
||||
} catch (e) {
|
||||
if (!(e instanceof StopIteration))
|
||||
if (!(e instanceof TypeError))
|
||||
throw "generatorProto.send() throws unexpected exception: "+uneval(e);
|
||||
}
|
||||
|
||||
var obj = {};
|
||||
try {
|
||||
proto.throw(obj);
|
||||
throw "generatorProto.throw(obj) does not throw obj";
|
||||
throw "generatorProto.throw(obj) does not throw TypeError";
|
||||
} catch (e) {
|
||||
if (e !== obj)
|
||||
if (!(e instanceof TypeError))
|
||||
throw "generatorProto.throw() throws unexpected exception: "+uneval(e);
|
||||
}
|
||||
|
||||
var obj = {};
|
||||
try {
|
||||
proto.close();
|
||||
throw "generatorProto.close() does not throw TypeError";
|
||||
} catch (e) {
|
||||
throw "generatorProto.throw() throws exception: "+uneval(e);
|
||||
if (!(e instanceof TypeError))
|
||||
throw "generatorProto.close() throws unexpected exception: "+uneval(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -90,8 +90,9 @@ class GlobalObject : public JSObject
|
||||
|
||||
/* One-off properties stored after slots for built-ins. */
|
||||
static const unsigned ELEMENT_ITERATOR_PROTO = FROM_BUFFER_UINT8CLAMPED + 1;
|
||||
static const unsigned GENERATOR_PROTO = ELEMENT_ITERATOR_PROTO + 1;
|
||||
static const unsigned MAP_ITERATOR_PROTO = GENERATOR_PROTO + 1;
|
||||
static const unsigned LEGACY_GENERATOR_OBJECT_PROTO = ELEMENT_ITERATOR_PROTO + 1;
|
||||
static const unsigned STAR_GENERATOR_OBJECT_PROTO = LEGACY_GENERATOR_OBJECT_PROTO + 1;
|
||||
static const unsigned MAP_ITERATOR_PROTO = STAR_GENERATOR_OBJECT_PROTO + 1;
|
||||
static const unsigned SET_ITERATOR_PROTO = MAP_ITERATOR_PROTO + 1;
|
||||
static const unsigned COLLATOR_PROTO = SET_ITERATOR_PROTO + 1;
|
||||
static const unsigned NUMBER_FORMAT_PROTO = COLLATOR_PROTO + 1;
|
||||
@ -364,8 +365,21 @@ class GlobalObject : public JSObject
|
||||
return getOrCreateObject(cx, ELEMENT_ITERATOR_PROTO, initIteratorClasses);
|
||||
}
|
||||
|
||||
JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
|
||||
return getOrCreateObject(cx, GENERATOR_PROTO, initIteratorClasses);
|
||||
JSObject *getOrCreateLegacyGeneratorObjectPrototype(JSContext *cx) {
|
||||
return getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO, initIteratorClasses);
|
||||
}
|
||||
|
||||
JSObject *getOrCreateStarGeneratorObjectPrototype(JSContext *cx) {
|
||||
return getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initIteratorClasses);
|
||||
}
|
||||
|
||||
JSObject *getOrCreateStarGeneratorFunctionPrototype(JSContext *cx) {
|
||||
return getOrCreateObject(cx, JSProto_LIMIT + JSProto_GeneratorFunction,
|
||||
initIteratorClasses);
|
||||
}
|
||||
|
||||
JSObject *getOrCreateStarGeneratorFunction(JSContext *cx) {
|
||||
return getOrCreateObject(cx, JSProto_GeneratorFunction, initIteratorClasses);
|
||||
}
|
||||
|
||||
JSObject *getOrCreateMapIteratorPrototype(JSContext *cx) {
|
||||
|
Loading…
Reference in New Issue
Block a user