mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Bug 686900 - Rewrite js_InitFunctionAndObject. r=jorendorff
--HG-- extra : rebase_source : 89ab3d2a5ae21c0b56ca58c7e602c0b6b18a3c87
This commit is contained in:
parent
8ec0fd4c8c
commit
18cac613d7
@ -1580,8 +1580,8 @@ StdNameToAtom(JSContext *cx, JSStdName *stdn)
|
||||
* If you add a "standard" class, remember to update this table.
|
||||
*/
|
||||
static JSStdName standard_class_atoms[] = {
|
||||
{js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)},
|
||||
{js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)},
|
||||
{js_InitFunctionClass, EAGER_ATOM_AND_CLASP(Function)},
|
||||
{js_InitObjectClass, EAGER_ATOM_AND_CLASP(Object)},
|
||||
{js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)},
|
||||
{js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)},
|
||||
{js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)},
|
||||
|
@ -56,8 +56,8 @@
|
||||
* wrapping the inclusion with JS_BEGIN_EXTERN_C and JS_END_EXTERN_C.
|
||||
*/
|
||||
JS_PROTO(Null, 0, js_InitNullClass)
|
||||
JS_PROTO(Object, 1, js_InitFunctionAndObjectClasses)
|
||||
JS_PROTO(Function, 2, js_InitFunctionAndObjectClasses)
|
||||
JS_PROTO(Object, 1, js_InitObjectClass)
|
||||
JS_PROTO(Function, 2, js_InitFunctionClass)
|
||||
JS_PROTO(Array, 3, js_InitArrayClass)
|
||||
JS_PROTO(Boolean, 4, js_InitBooleanClass)
|
||||
JS_PROTO(JSON, 5, js_InitJSONClass)
|
||||
|
@ -54,23 +54,26 @@ using namespace js;
|
||||
JSObject *
|
||||
js_InitObjectClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *proto = js_InitClass(cx, obj, NULL, &ObjectClass, js_Object, 1,
|
||||
object_props, object_methods, NULL, object_static_methods);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
JS_ASSERT(obj->isNative());
|
||||
|
||||
/* The default 'new' object for Object.prototype has unknown properties. */
|
||||
proto->getNewType(cx, NULL, /* markUnknown = */ true);
|
||||
GlobalObject *global = obj->asGlobal();
|
||||
if (!global->functionObjectClassesInitialized()) {
|
||||
if (!global->initFunctionAndObjectClasses(cx))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
|
||||
JSObject *evalobj = js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS);
|
||||
if (!evalobj)
|
||||
return NULL;
|
||||
if (obj->isGlobal())
|
||||
obj->asGlobal()->setOriginalEval(evalobj);
|
||||
return global->getObjectPrototype();
|
||||
}
|
||||
|
||||
return proto;
|
||||
JSObject *
|
||||
js_InitFunctionClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(obj->isNative());
|
||||
|
||||
GlobalObject *global = obj->asGlobal();
|
||||
return global->functionObjectClassesInitialized()
|
||||
? global->getFunctionPrototype()
|
||||
: global->initFunctionAndObjectClasses(cx);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
@ -81,106 +84,168 @@ ThrowTypeError(JSContext *cx, uintN argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_InitFunctionClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *proto = js_InitClass(cx, obj, NULL, &FunctionClass, Function, 1,
|
||||
NULL, function_methods, NULL, NULL);
|
||||
if (!proto)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* The default 'new' object for Function.prototype has unknown properties.
|
||||
* This will be used for generic scripted functions, e.g. from
|
||||
* non-compileAndGo code.
|
||||
*/
|
||||
proto->getNewType(cx, NULL, /* markUnknown = */ true);
|
||||
|
||||
JSFunction *fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
fun->flags |= JSFUN_PROTOTYPE;
|
||||
|
||||
JSScript *script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
|
||||
if (!script)
|
||||
return NULL;
|
||||
script->noScriptRval = true;
|
||||
script->code[0] = JSOP_STOP;
|
||||
script->code[1] = SRC_NULL;
|
||||
fun->u.i.script = script;
|
||||
fun->getType(cx)->interpretedFunction = fun;
|
||||
script->hasFunction = true;
|
||||
script->setOwnerObject(fun);
|
||||
js_CallNewScriptHook(cx, script, fun);
|
||||
|
||||
if (obj->isGlobal()) {
|
||||
/* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
|
||||
JSFunction *throwTypeError = js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, obj, NULL);
|
||||
if (!throwTypeError)
|
||||
return NULL;
|
||||
|
||||
obj->asGlobal()->setThrowTypeError(throwTypeError);
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
namespace js {
|
||||
|
||||
JSObject *
|
||||
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
|
||||
GlobalObject::initFunctionAndObjectClasses(JSContext *cx)
|
||||
{
|
||||
JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
|
||||
JS_ASSERT(isNative());
|
||||
|
||||
/* If cx has no global object, use obj so prototypes can be found. */
|
||||
/*
|
||||
* Calling a function from a cleared global triggers this (yeah, I know).
|
||||
* Uncomment this once bug 470510 is fixed (if that bug doesn't remove
|
||||
* isCleared entirely).
|
||||
*/
|
||||
// JS_ASSERT(!isCleared());
|
||||
|
||||
/* If cx has no global object, make this the global object. */
|
||||
if (!cx->globalObject)
|
||||
JS_SetGlobalObject(cx, obj);
|
||||
JS_SetGlobalObject(cx, this);
|
||||
|
||||
/* Record Function and Object in cx->resolvingList. */
|
||||
JSAtom **classAtoms = cx->runtime->atomState.classAtoms;
|
||||
AutoResolving resolving1(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Function]));
|
||||
AutoResolving resolving2(cx, obj, ATOM_TO_JSID(classAtoms[JSProto_Object]));
|
||||
|
||||
/* Initialize the function class first so constructors can be made. */
|
||||
JSObject *fun_proto;
|
||||
if (!js_GetClassPrototype(cx, obj, JSProto_Function, &fun_proto))
|
||||
/*
|
||||
* Create |Object.prototype| first, mirroring CreateBlankProto but for the
|
||||
* prototype of the created object.
|
||||
*/
|
||||
JSObject *objectProto = NewNonFunction<WithProto::Given>(cx, &ObjectClass, NULL, this);
|
||||
if (!objectProto || !objectProto->setSingletonType(cx))
|
||||
return NULL;
|
||||
if (!fun_proto) {
|
||||
fun_proto = js_InitFunctionClass(cx, obj);
|
||||
if (!fun_proto)
|
||||
types::TypeObject *objectType = objectProto->getNewType(cx, NULL, /* markUnknown = */ true);
|
||||
if (!objectType || !objectType->getEmptyShape(cx, &ObjectClass, gc::FINALIZE_OBJECT0))
|
||||
return NULL;
|
||||
|
||||
/* Create |Function.prototype| next so we can create other functions. */
|
||||
JSFunction *functionProto;
|
||||
{
|
||||
JSObject *proto = NewObject<WithProto::Given>(cx, &FunctionClass, objectProto, this);
|
||||
if (!proto || !proto->setSingletonType(cx))
|
||||
return NULL;
|
||||
} else {
|
||||
JSObject *ctor = JS_GetConstructor(cx, fun_proto);
|
||||
if (!ctor)
|
||||
types::TypeObject *functionType = proto->getNewType(cx, NULL, /* markUnknown = */ true);
|
||||
if (!functionType || !functionType->getEmptyShape(cx, &FunctionClass, gc::FINALIZE_OBJECT0))
|
||||
return NULL;
|
||||
if (!obj->defineProperty(cx, ATOM_TO_JSID(CLASS_ATOM(cx, Function)),
|
||||
ObjectValue(*ctor), 0, 0, 0)) {
|
||||
|
||||
/*
|
||||
* Bizarrely, |Function.prototype| must be an interpreted function, so
|
||||
* give it the guts to be one.
|
||||
*/
|
||||
functionProto = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, this, NULL);
|
||||
if (!functionProto)
|
||||
return NULL;
|
||||
}
|
||||
JS_ASSERT(proto == functionProto);
|
||||
functionProto->flags |= JSFUN_PROTOTYPE;
|
||||
|
||||
JSScript *script =
|
||||
JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, JSVERSION_DEFAULT);
|
||||
if (!script)
|
||||
return NULL;
|
||||
script->noScriptRval = true;
|
||||
script->code[0] = JSOP_STOP;
|
||||
script->code[1] = SRC_NULL;
|
||||
functionProto->u.i.script = script;
|
||||
functionProto->getType(cx)->interpretedFunction = functionProto;
|
||||
script->hasFunction = true;
|
||||
script->setOwnerObject(functionProto);
|
||||
}
|
||||
|
||||
/* Initialize the object class next so Object.prototype works. */
|
||||
JSObject *obj_proto;
|
||||
if (!js_GetClassPrototype(cx, obj, JSProto_Object, &obj_proto))
|
||||
/* Create the Object function now that we have a [[Prototype]] for it. */
|
||||
jsid objectId = ATOM_TO_JSID(CLASS_ATOM(cx, Object));
|
||||
JSFunction *objectCtor;
|
||||
{
|
||||
JSObject *ctor = NewObject<WithProto::Given>(cx, &FunctionClass, functionProto, this);
|
||||
if (!ctor)
|
||||
return NULL;
|
||||
objectCtor = js_NewFunction(cx, ctor, js_Object, 1, JSFUN_CONSTRUCTOR, this,
|
||||
JSID_TO_ATOM(objectId));
|
||||
if (!objectCtor)
|
||||
return NULL;
|
||||
JS_ASSERT(ctor == objectCtor);
|
||||
|
||||
objectCtor->setConstructorClass(&ObjectClass);
|
||||
}
|
||||
|
||||
/*
|
||||
* Install |Object| and |Object.prototype| for the benefit of subsequent
|
||||
* code that looks for them.
|
||||
*/
|
||||
setObjectClassDetails(objectCtor, objectProto);
|
||||
|
||||
/* Create |Function| so it and |Function.prototype| can be installed. */
|
||||
jsid functionId = ATOM_TO_JSID(CLASS_ATOM(cx, Function));
|
||||
JSFunction *functionCtor;
|
||||
{
|
||||
JSObject *ctor =
|
||||
NewObject<WithProto::Given>(cx, &FunctionClass, functionProto, this);
|
||||
if (!ctor)
|
||||
return NULL;
|
||||
functionCtor = js_NewFunction(cx, ctor, Function, 1, JSFUN_CONSTRUCTOR, this,
|
||||
JSID_TO_ATOM(functionId));
|
||||
if (!functionCtor)
|
||||
return NULL;
|
||||
JS_ASSERT(ctor == functionCtor);
|
||||
|
||||
functionCtor->setConstructorClass(&FunctionClass);
|
||||
}
|
||||
|
||||
/*
|
||||
* Install |Function| and |Function.prototype| so that we can freely create
|
||||
* functions and objects without special effort.
|
||||
*/
|
||||
setFunctionClassDetails(functionCtor, functionProto);
|
||||
|
||||
/*
|
||||
* The hard part's done: now go back and add all the properties these
|
||||
* primordial values have.
|
||||
*/
|
||||
if (!LinkConstructorAndPrototype(cx, objectCtor, objectProto) ||
|
||||
!DefinePropertiesAndBrand(cx, objectProto, object_props, object_methods) ||
|
||||
!DefinePropertiesAndBrand(cx, objectCtor, NULL, object_static_methods) ||
|
||||
!LinkConstructorAndPrototype(cx, functionCtor, functionProto) ||
|
||||
!DefinePropertiesAndBrand(cx, functionProto, NULL, function_methods) ||
|
||||
!DefinePropertiesAndBrand(cx, functionCtor, NULL, NULL))
|
||||
{
|
||||
return NULL;
|
||||
if (!obj_proto)
|
||||
obj_proto = js_InitObjectClass(cx, obj);
|
||||
if (!obj_proto)
|
||||
}
|
||||
|
||||
/* Add the global Function and Object properties now. */
|
||||
if (!addDataProperty(cx, objectId, JSProto_Object + JSProto_LIMIT * 2, 0))
|
||||
return false;
|
||||
if (!addDataProperty(cx, functionId, JSProto_Function + JSProto_LIMIT * 2, 0))
|
||||
return false;
|
||||
|
||||
/* Heavy lifting done, but lingering tasks remain. */
|
||||
|
||||
/* ES5 15.1.2.1. */
|
||||
jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
|
||||
JSObject *evalobj = js_DefineFunction(cx, this, id, eval, 1, JSFUN_STUB_GSOPS);
|
||||
if (!evalobj)
|
||||
return NULL;
|
||||
setOriginalEval(evalobj);
|
||||
|
||||
/* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
|
||||
JSFunction *throwTypeError = js_NewFunction(cx, NULL, ThrowTypeError, 0, 0, this, NULL);
|
||||
if (!throwTypeError)
|
||||
return NULL;
|
||||
setThrowTypeError(throwTypeError);
|
||||
|
||||
/*
|
||||
* The global object should have |Object.prototype| as its [[Prototype]].
|
||||
* Eventually we'd like to have standard classes be there from the start,
|
||||
* and thus we would know we were always setting what had previously been a
|
||||
* null [[Prototype]], but right now some code assumes it can set the
|
||||
* [[Prototype]] before standard classes have been initialized. For now,
|
||||
* only set the [[Prototype]] if it hasn't already been set.
|
||||
*/
|
||||
if (shouldSplicePrototype(cx) && !splicePrototype(cx, objectProto))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Function.prototype and the global object delegate to Object.prototype.
|
||||
* Don't update the prototype if the __proto__ of either object was cleared
|
||||
* after the objects started getting used.
|
||||
* Notify any debuggers about the creation of the script for
|
||||
* |Function.prototype| -- after all initialization, for simplicity.
|
||||
*/
|
||||
if (fun_proto->shouldSplicePrototype(cx) && !fun_proto->splicePrototype(cx, obj_proto))
|
||||
return NULL;
|
||||
if (obj->shouldSplicePrototype(cx) && !obj->splicePrototype(cx, obj_proto))
|
||||
return NULL;
|
||||
|
||||
return fun_proto;
|
||||
js_CallNewScriptHook(cx, functionProto->script(), functionProto);
|
||||
return functionProto;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
GlobalObject *
|
||||
GlobalObject::create(JSContext *cx, Class *clasp)
|
||||
{
|
||||
@ -219,7 +284,7 @@ GlobalObject::initStandardClasses(JSContext *cx)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!js_InitFunctionAndObjectClasses(cx, this))
|
||||
if (!initFunctionAndObjectClasses(cx))
|
||||
return false;
|
||||
|
||||
/* Initialize the rest of the standard objects and functions. */
|
||||
@ -257,6 +322,14 @@ GlobalObject::clear(JSContext *cx)
|
||||
/* Clear the runtime-codegen-enabled cache. */
|
||||
setSlot(RUNTIME_CODEGEN_ENABLED, UndefinedValue());
|
||||
|
||||
/*
|
||||
* Clear the original-eval and [[ThrowTypeError]] slots, in case throwing
|
||||
* trying to execute a script for this global must reinitialize standard
|
||||
* classes. See bug 470150.
|
||||
*/
|
||||
setSlot(EVAL, UndefinedValue());
|
||||
setSlot(THROWTYPEERROR, UndefinedValue());
|
||||
|
||||
/*
|
||||
* Mark global as cleared. If we try to execute any compile-and-go
|
||||
* scripts from here on, we will throw.
|
||||
|
@ -51,9 +51,6 @@ js_InitObjectClass(JSContext *cx, JSObject *obj);
|
||||
extern JSObject *
|
||||
js_InitFunctionClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JSObject *
|
||||
js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj);
|
||||
|
||||
namespace js {
|
||||
|
||||
class Debugger;
|
||||
@ -120,6 +117,47 @@ class GlobalObject : public ::JSObject {
|
||||
setSlot(FLAGS, Int32Value(flags));
|
||||
}
|
||||
|
||||
friend JSObject *
|
||||
::js_InitObjectClass(JSContext *cx, JSObject *obj);
|
||||
friend JSObject *
|
||||
::js_InitFunctionClass(JSContext *cx, JSObject *obj);
|
||||
|
||||
/* Initialize the Function and Object classes. Must only be called once! */
|
||||
JSObject *
|
||||
initFunctionAndObjectClasses(JSContext *cx);
|
||||
|
||||
void setDetailsForKey(JSProtoKey key, JSObject *ctor, JSObject *proto) {
|
||||
Value &ctorVal = getSlotRef(key);
|
||||
Value &protoVal = getSlotRef(JSProto_LIMIT + key);
|
||||
Value &visibleVal = getSlotRef(2 * JSProto_LIMIT + key);
|
||||
JS_ASSERT(ctorVal.isUndefined());
|
||||
JS_ASSERT(protoVal.isUndefined());
|
||||
JS_ASSERT(visibleVal.isUndefined());
|
||||
ctorVal = ObjectValue(*ctor);
|
||||
protoVal = ObjectValue(*proto);
|
||||
visibleVal = ctorVal;
|
||||
}
|
||||
|
||||
void setObjectClassDetails(JSFunction *ctor, JSObject *proto) {
|
||||
setDetailsForKey(JSProto_Object, ctor, proto);
|
||||
}
|
||||
|
||||
void setFunctionClassDetails(JSFunction *ctor, JSObject *proto) {
|
||||
setDetailsForKey(JSProto_Function, ctor, proto);
|
||||
}
|
||||
|
||||
void setThrowTypeError(JSFunction *fun) {
|
||||
Value &v = getSlotRef(THROWTYPEERROR);
|
||||
JS_ASSERT(v.isUndefined());
|
||||
v.setObject(*fun);
|
||||
}
|
||||
|
||||
void setOriginalEval(JSObject *evalobj) {
|
||||
Value &v = getSlotRef(EVAL);
|
||||
JS_ASSERT(v.isUndefined());
|
||||
v.setObject(*evalobj);
|
||||
}
|
||||
|
||||
public:
|
||||
static GlobalObject *create(JSContext *cx, Class *clasp);
|
||||
|
||||
@ -146,15 +184,26 @@ class GlobalObject : public ::JSObject {
|
||||
*/
|
||||
JSObject *createBlankPrototypeInheriting(JSContext *cx, js::Class *clasp, JSObject &proto);
|
||||
|
||||
void setThrowTypeError(JSFunction *fun) {
|
||||
// Our bootstrapping code is currently too convoluted to correctly and
|
||||
// confidently assert this.
|
||||
// JS_ASSERT(v.isUndefined());
|
||||
// JS_ASSERT(getSlot(THROWTYPEERROR).isUndefined());
|
||||
setSlot(THROWTYPEERROR, ObjectValue(*fun));
|
||||
bool functionObjectClassesInitialized() const {
|
||||
bool inited = !getSlot(JSProto_Function).isUndefined();
|
||||
JS_ASSERT(inited == !getSlot(JSProto_LIMIT + JSProto_Function).isUndefined());
|
||||
JS_ASSERT(inited == !getSlot(JSProto_Object).isUndefined());
|
||||
JS_ASSERT(inited == !getSlot(JSProto_LIMIT + JSProto_Object).isUndefined());
|
||||
return inited;
|
||||
}
|
||||
|
||||
JSObject *getFunctionPrototype() const {
|
||||
JS_ASSERT(functionObjectClassesInitialized());
|
||||
return &getSlot(JSProto_LIMIT + JSProto_Function).toObject();
|
||||
}
|
||||
|
||||
JSObject *getObjectPrototype() const {
|
||||
JS_ASSERT(functionObjectClassesInitialized());
|
||||
return &getSlot(JSProto_LIMIT + JSProto_Object).toObject();
|
||||
}
|
||||
|
||||
JSObject *getThrowTypeError() const {
|
||||
JS_ASSERT(functionObjectClassesInitialized());
|
||||
return &getSlot(THROWTYPEERROR).toObject();
|
||||
}
|
||||
|
||||
@ -179,17 +228,10 @@ class GlobalObject : public ::JSObject {
|
||||
bool isRuntimeCodeGenEnabled(JSContext *cx);
|
||||
|
||||
const Value &getOriginalEval() const {
|
||||
JS_ASSERT(getSlot(EVAL).isObject());
|
||||
return getSlot(EVAL);
|
||||
}
|
||||
|
||||
void setOriginalEval(JSObject *evalobj) {
|
||||
// Our bootstrapping code is currently too convoluted to correctly and
|
||||
// confidently assert this.
|
||||
// JS_ASSERT(v.isUndefined());
|
||||
// JS_ASSERT(getSlot(EVAL).isUndefined());
|
||||
setSlot(EVAL, ObjectValue(*evalobj));
|
||||
}
|
||||
|
||||
bool getFunctionNamespace(JSContext *cx, Value *vp);
|
||||
|
||||
bool initGeneratorClass(JSContext *cx);
|
||||
|
Loading…
Reference in New Issue
Block a user