From 09f9f51b84db418dbb9a7a1ee4886292f415a66b Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 23 May 2014 16:53:03 -0700 Subject: [PATCH] Bug 992958 - Add support for prototype JSPropertySpecs on ClassSpec. r=luke --- js/public/Class.h | 5 +- js/src/jsdate.cpp | 1 + js/src/vm/GlobalObject.cpp | 7 ++- js/xpconnect/wrappers/XrayWrapper.cpp | 90 ++++++++++++++++++--------- 4 files changed, 72 insertions(+), 31 deletions(-) diff --git a/js/public/Class.h b/js/public/Class.h index 387e39f52b7b..4d7e23a7f0a3 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -292,6 +292,7 @@ struct ClassSpec ClassObjectCreationOp createPrototype; const JSFunctionSpec *constructorFunctions; const JSFunctionSpec *prototypeFunctions; + const JSPropertySpec *prototypeProperties; FinishClassInitOp finishInit; bool defined() const { return !!createConstructor; } }; @@ -322,7 +323,7 @@ struct ClassExtension JSWeakmapKeyDelegateOp weakmapKeyDelegateOp; }; -#define JS_NULL_CLASS_SPEC {nullptr,nullptr,nullptr,nullptr,nullptr} +#define JS_NULL_CLASS_SPEC {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr} #define JS_NULL_CLASS_EXT {nullptr,nullptr,nullptr,false,nullptr} struct ObjectOps @@ -365,7 +366,7 @@ typedef void (*JSClassInternal)(); struct JSClass { JS_CLASS_MEMBERS(JSFinalizeOp); - void *reserved[31]; + void *reserved[32]; }; #define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index ebf66240454a..efcb12cfde64 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -3049,6 +3049,7 @@ const Class DateObject::class_ = { GenericCreatePrototype<&DateObject::class_>, date_static_methods, date_methods, + nullptr, FinishDateClassInit } }; diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index e2ad88238438..d0c727932d8b 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -383,7 +383,8 @@ GlobalObject::resolveConstructor(JSContext *cx, Handle global, JS } // We don't always have a prototype (i.e. Math and JSON). If we don't, - // |createPrototype| and |prototypeFunctions| should both be null. + // |createPrototype|, |prototypeFunctions|, and |prototypeProperties| + // should all be null. RootedObject proto(cx); if (clasp->spec.createPrototype) { proto = clasp->spec.createPrototype(cx, key); @@ -394,6 +395,10 @@ GlobalObject::resolveConstructor(JSContext *cx, Handle global, JS if (!JS_DefineFunctions(cx, proto, funs)) return false; } + if (const JSPropertySpec *props = clasp->spec.prototypeProperties) { + if (!JS_DefineProperties(cx, proto, props)) + return false; + } // If the prototype exists, link it with the constructor. if (proto && !LinkConstructorAndPrototype(cx, ctor, proto)) diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index c2594d951803..829f447db288 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -443,41 +443,68 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, return true; } - // Find the properties available, if any. - const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; - if (!fs) - return true; - // Compute the property name we're looking for. We'll handle indexed // properties when we start supporting arrays. if (!JSID_IS_STRING(id)) return true; Rooted str(cx, JSID_TO_FLAT_STRING(id)); - // Scan through the properties. If we don't find anything, we're done. - for (; fs->name; ++fs) { + // Scan through the functions. + const JSFunctionSpec *fsMatch = nullptr; + for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) { // We don't support self-hosted functions yet. See bug 972987. if (fs->selfHostedName) continue; - if (JS_FlatStringEqualsAscii(str, fs->name)) + if (JS_FlatStringEqualsAscii(str, fs->name)) { + fsMatch = fs; break; + } } - if (!fs->name) - return true; + if (fsMatch) { + // Generate an Xrayed version of the method. + Rooted fun(cx, JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs, + 0, wrapper, id)); + if (!fun) + return false; - // Generate an Xrayed version of the method. - Rooted fun(cx, JS_NewFunctionById(cx, fs->call.op, fs->nargs, - 0, wrapper, id)); - if (!fun) - return false; + // The generic Xray machinery only defines non-own properties on the holder. + // This is broken, and will be fixed at some point, but for now we need to + // cache the value explicitly. See the corresponding call to + // JS_GetPropertyById at the top of this function. + RootedObject funObj(cx, JS_GetFunctionObject(fun)); + return JS_DefinePropertyById(cx, holder, id, funObj, 0) && + JS_GetPropertyDescriptorById(cx, holder, id, desc); + } - // The generic Xray machinery only defines non-own properties on the holder. - // This is broken, and will be fixed at some point, but for now we need to - // cache the value explicitly. See the corresponding call to - // JS_GetPropertyById at the top of this function. - RootedValue value(cx, ObjectValue(*JS_GetFunctionObject(fun))); - return JS_DefinePropertyById(cx, holder, id, value, 0) && - JS_GetPropertyDescriptorById(cx, holder, id, desc); + // Scan through the properties. + const JSPropertySpec *psMatch = nullptr; + for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) { + // We don't support self-hosted accessors yet (see bug 972987). And given + // the confusion outlined in bug 992977, we can't support JSPropertyOp- + // backed entries either (which in practice is fine). + if (!(ps->flags & JSPROP_NATIVE_ACCESSORS)) + continue; + if (JS_FlatStringEqualsAscii(str, ps->name)) { + psMatch = ps; + break; + } + } + if (psMatch) { + // The generic Xray machinery only defines non-own properties on the holder. + // This is broken, and will be fixed at some point, but for now we need to + // cache the value explicitly. See the corresponding call to + // JS_GetPropertyById at the top of this function. + // + // Note also that the public-facing API here doesn't give us a way to + // pass along JITInfo. It's probably ok though, since Xrays are already + // pretty slow. + return JS_DefinePropertyById(cx, holder, id, + UndefinedHandleValue, psMatch->flags, + psMatch->getter.propertyOp.op, psMatch->setter.propertyOp.op) && + JS_GetPropertyDescriptorById(cx, holder, id, desc); + } + + return true; } bool @@ -498,13 +525,8 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == getProtoKey(holder)); MOZ_ASSERT(clasp->spec.defined()); - // Find the properties available, if any. - const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; - if (!fs) - return true; - // Intern all the strings, and pass theme to the caller. - for (; fs->name; ++fs) { + for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) { // We don't support self-hosted functions yet. See bug 972987. if (fs->selfHostedName) continue; @@ -514,6 +536,18 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags if (!props.append(INTERNED_STRING_TO_JSID(cx, str))) return false; } + for (const JSPropertySpec *ps = clasp->spec.prototypeProperties; ps && ps->name; ++ps) { + // We don't support self-hosted functions yet. See bug 972987. + // Note that this is also kind of an abuse of JSPROP_NATIVE_ACCESSORS. + // See bug 992977. + if (!(ps->flags & JSPROP_NATIVE_ACCESSORS)) + continue; + RootedString str(cx, JS_InternString(cx, ps->name)); + if (!str) + return false; + if (!props.append(INTERNED_STRING_TO_JSID(cx, str))) + return false; + } // Add the 'constructor' property. return props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR));