From 3252341ae4f499ed9dcfa96b9af27f1c3ac729a5 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 7 Feb 2012 11:45:18 -0800 Subject: [PATCH] Bug 725888 - Remove some js_GetClassPrototype calls, using faster methods on GlobalObject instead. Also introduce JS_GetObjectPrototype to retrieve Object.prototype so that a friend API can be made un-friendly. r=dmandelin --HG-- extra : rebase_source : d68d7f1fdcc1bc9a1a027c08ea62bebd8e9d0178 --- js/src/jsapi.cpp | 8 ++++++++ js/src/jsapi.h | 7 +++++++ js/src/jsexn.cpp | 16 ++++------------ js/src/jsexn.h | 8 ++++++++ js/src/jsfriendapi.h | 8 -------- js/src/jsfun.cpp | 4 ++-- js/src/jsinfer.cpp | 8 ++++---- js/src/jsinterp.cpp | 2 +- js/src/jsinterpinlines.h | 29 ++++++++++++----------------- js/src/jsobj.h | 11 +++++++++++ js/src/methodjit/Compiler.cpp | 4 ++-- js/src/methodjit/FastOps.cpp | 4 ++-- js/src/vm/Debugger.cpp | 7 ++++--- js/src/vm/GlobalObject.h | 15 +++++++++++++++ js/xpconnect/src/dombindings.cpp | 5 +---- 15 files changed, 81 insertions(+), 55 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8652b96c09c..375f1371975 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2172,6 +2172,14 @@ JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp) return js_GetClassObject(cx, obj, key, objp); } +JS_PUBLIC_API(JSObject *) +JS_GetObjectPrototype(JSContext *cx, JSObject *forObj) +{ + CHECK_REQUEST(cx); + assertSameCompartment(cx, forObj); + return forObj->global().getOrCreateObjectPrototype(cx); +} + JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 3a3647c226a..4663d11d1dd 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2728,6 +2728,13 @@ extern JS_PUBLIC_API(JSBool) JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); +/* + * Returns the original value of |Object.prototype| from the global object in + * which |forObj| was created. + */ +extern JS_PUBLIC_API(JSObject *) +JS_GetObjectPrototype(JSContext *cx, JSObject *forObj); + extern JS_PUBLIC_API(JSObject *) JS_GetGlobalForObject(JSContext *cx, JSObject *obj); diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 876913de810..5ee3aaf47a9 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -971,14 +971,6 @@ JS_STATIC_ASSERT(JSProto_Error + JSEXN_SYNTAXERR == JSProto_SyntaxError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_TYPEERR == JSProto_TypeError); JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError); -static JS_INLINE JSProtoKey -GetExceptionProtoKey(intN exn) -{ - JS_ASSERT(JSEXN_ERR <= exn); - JS_ASSERT(exn < JSEXN_LIMIT); - return JSProtoKey(JSProto_Error + exn); -} - static JSObject * InitErrorClass(JSContext *cx, GlobalObject *global, intN type, JSObject &proto) { @@ -1031,8 +1023,8 @@ js_InitExceptionClasses(JSContext *cx, JSObject *obj) GlobalObject *global = &obj->asGlobal(); - JSObject *objectProto; - if (!js_GetClassPrototype(cx, global, JSProto_Object, &objectProto)) + JSObject *objectProto = global->getOrCreateObjectPrototype(cx); + if (!objectProto) return NULL; /* Initialize the base Error class first. */ @@ -1331,8 +1323,8 @@ js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope) copy->exnType = priv->exnType; // Create the Error object. - JSObject *proto; - if (!js_GetClassPrototype(cx, &scope->global(), GetExceptionProtoKey(copy->exnType), &proto)) + JSObject *proto = scope->global().getOrCreateCustomErrorPrototype(cx, copy->exnType); + if (!proto) return NULL; JSObject *copyobj = NewObjectWithGivenProto(cx, &ErrorClass, proto, NULL); SetExnPrivate(cx, copyobj, copy); diff --git a/js/src/jsexn.h b/js/src/jsexn.h index db26f399479..4e47d3062d3 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -101,4 +101,12 @@ js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, extern JSObject * js_CopyErrorObject(JSContext *cx, JSObject *errobj, JSObject *scope); +static JS_INLINE JSProtoKey +GetExceptionProtoKey(intN exn) +{ + JS_ASSERT(JSEXN_ERR <= exn); + JS_ASSERT(exn < JSEXN_LIMIT); + return JSProtoKey(JSProto_Error + exn); +} + #endif /* jsexn_h___ */ diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index aaaaf0852a1..495b4006817 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -755,14 +755,6 @@ class ObjectPtr } /* namespace js */ -/* - * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is - * JSProto_Null, clasp must non-null. - */ -extern JS_FRIEND_API(JSBool) -js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, - JSObject **protop, js::Class *clasp = NULL); - #endif /* Implemented in jsdate.cpp. */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 989fd62c7fd..1c57cb9b42a 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1244,8 +1244,8 @@ ResolveInterpretedFunctionPrototype(JSContext *cx, JSObject *obj) * Make the prototype object an instance of Object with the same parent * as the function object itself. */ - JSObject *objProto; - if (!js_GetClassPrototype(cx, obj->getParent(), JSProto_Object, &objProto)) + JSObject *objProto = obj->global().getOrCreateObjectPrototype(cx); + if (!objProto) return NULL; JSObject *proto = NewObjectWithGivenProto(cx, &ObjectClass, objProto, NULL); if (!proto || !proto->setSingletonType(cx)) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 6c49497da9e..707a6bfcb68 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2094,11 +2094,11 @@ types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script) if (!cx->typeInferenceEnabled() || !script->hasGlobal()) return true; - JSObject *proto; - if (!js_GetClassPrototype(cx, NULL, JSProto_Array, &proto, NULL)) + JSObject *proto = script->global()->getOrCreateArrayPrototype(cx); + if (!proto) return true; - while (proto) { + do { TypeObject *type = proto->getType(cx); if (type->unknownProperties()) return true; @@ -2106,7 +2106,7 @@ types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script) if (!indexTypes || indexTypes->isOwnProperty(cx, type, true) || indexTypes->knownNonEmpty(cx)) return true; proto = proto->getProto(); - } + } while (proto); return false; } diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index a704f7a2cdb..26bf4d7e3ea 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2612,7 +2612,7 @@ BEGIN_CASE(JSOP_CALLELEM) { /* Find the object on which to look for |this|'s properties. */ Value thisv = regs.sp[-2]; - JSObject *thisObj = ValuePropertyBearer(cx, thisv, -2); + JSObject *thisObj = ValuePropertyBearer(cx, regs.fp(), thisv, -2); if (!thisObj) goto error; diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 44c1b09485a..dd8cd8445dc 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -153,28 +153,23 @@ ComputeThis(JSContext *cx, StackFrame *fp) * problem to the value at |spindex| on the stack. */ JS_ALWAYS_INLINE JSObject * -ValuePropertyBearer(JSContext *cx, const Value &v, int spindex) +ValuePropertyBearer(JSContext *cx, StackFrame *fp, const Value &v, int spindex) { if (v.isObject()) return &v.toObject(); - JSProtoKey protoKey; - if (v.isString()) { - protoKey = JSProto_String; - } else if (v.isNumber()) { - protoKey = JSProto_Number; - } else if (v.isBoolean()) { - protoKey = JSProto_Boolean; - } else { - JS_ASSERT(v.isNull() || v.isUndefined()); - js_ReportIsNullOrUndefined(cx, spindex, v, NULL); - return NULL; - } + GlobalObject &global = fp->scopeChain().global(); - JSObject *pobj; - if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj)) - return NULL; - return pobj; + if (v.isString()) + return global.getOrCreateStringPrototype(cx); + if (v.isNumber()) + return global.getOrCreateNumberPrototype(cx); + if (v.isBoolean()) + return global.getOrCreateBooleanPrototype(cx); + + JS_ASSERT(v.isNull() || v.isUndefined()); + js_ReportIsNullOrUndefined(cx, spindex, v, NULL); + return NULL; } inline bool diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 7cab0a8504d..3f9a322e581 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -2000,6 +2000,17 @@ js_InferFlags(JSContext *cx, uintN defaultFlags); JSBool js_Object(JSContext *cx, uintN argc, js::Value *vp); +/* + * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is + * JSProto_Null, clasp must non-null. + * + * If protoKey is constant and scope is non-null, use GlobalObject's prototype + * methods instead. + */ +extern JS_FRIEND_API(JSBool) +js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, + JSObject **protop, js::Class *clasp = NULL); + namespace js { extern bool diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 5a817ffbf51..025f194b3df 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -4507,8 +4507,8 @@ mjit::Compiler::callArrayBuiltin(uint32_t argc, bool callingNew) if (!js_GetClassObject(cx, globalObj, JSProto_Array, &arrayObj)) return Compile_Error; - JSObject *arrayProto; - if (!js_GetClassPrototype(cx, globalObj, JSProto_Array, &arrayProto)) + JSObject *arrayProto = globalObj->global().getOrCreateArrayPrototype(cx); + if (!arrayProto) return Compile_Error; if (argc > 1) diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 5bf65a7185f..011507b07fe 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -1518,8 +1518,8 @@ GetDenseArrayShape(JSContext *cx, JSObject *globalObj) { JS_ASSERT(globalObj); - JSObject *proto; - if (!js_GetClassPrototype(cx, globalObj, JSProto_Array, &proto, NULL)) + JSObject *proto = globalObj->global().getOrCreateArrayPrototype(cx); + if (!proto) return NULL; return EmptyShape::getInitialShape(cx, &ArrayClass, proto, diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 60b35da1e6e..d0e707c5537 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2633,8 +2633,8 @@ DebuggerFrame_getArguments(JSContext *cx, uintN argc, Value *vp) /* Create an arguments object. */ RootedVar global(cx); global = &args.callee().global(); - JSObject *proto; - if (!js_GetClassPrototype(cx, global, JSProto_Array, &proto)) + JSObject *proto = global->getOrCreateArrayPrototype(cx); + if (!proto) return false; argsobj = NewObjectWithGivenProto(cx, &DebuggerArguments_class, proto, global); if (!argsobj) @@ -3791,7 +3791,8 @@ JS_DefineDebuggerObject(JSContext *cx, JSObject *obj) scriptProto(cx), objectProto(cx); - if (!js_GetClassPrototype(cx, obj, JSProto_Object, objProto.address())) + objProto = obj->asGlobal().getOrCreateObjectPrototype(cx); + if (!objProto) return false; diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 5341ebbf9a1..a13ac836052 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -45,6 +45,7 @@ #include "jsarray.h" #include "jsbool.h" +#include "jsexn.h" #include "jsfun.h" #include "jsiter.h" #include "jsnum.h" @@ -186,6 +187,9 @@ class GlobalObject : public JSObject { bool arrayBufferClassInitialized() const { return classIsInitialized(JSProto_ArrayBuffer); } + bool errorClassesInitialized() const { + return classIsInitialized(JSProto_Error); + } public: static GlobalObject *create(JSContext *cx, Class *clasp); @@ -294,6 +298,17 @@ class GlobalObject : public JSObject { return &self->getPrototype(JSProto_ArrayBuffer).toObject(); } + JSObject *getOrCreateCustomErrorPrototype(JSContext *cx, intN exnType) { + GlobalObject *self = this; + JSProtoKey key = GetExceptionProtoKey(exnType); + if (!errorClassesInitialized()) { + Root root(cx, &self); + if (!js_InitExceptionClasses(cx, this)) + return NULL; + } + return &self->getPrototype(key).toObject(); + } + JSObject *getOrCreateGeneratorPrototype(JSContext *cx) { GlobalObject *self = this; Value v = getSlotRef(GENERATOR_PROTO); diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index c5241216148..2ed5f3b75dd 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -1312,10 +1312,7 @@ NoBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) // We need to pass the object prototype to JS_NewObject. If we pass NULL then the JS engine // will look up a prototype on the global by using the class' name and we'll recurse into // getPrototype. - JSObject* proto; - if (!js_GetClassPrototype(cx, scope->GetGlobalJSObject(), JSProto_Object, &proto)) - return NULL; - return proto; + return JS_GetObjectPrototype(cx, scope->GetGlobalJSObject()); }