Bug 896116 - Morph the dependent-JSProtoKey system to explicitly store a JSProtoKey, rather than relying on a system where a single class represents the parent and dependent both. Also convert error objects (Error, RangeError, &c.) to be represented by multiple classes, not a single one. r=bholley, r=till

--HG--
extra : rebase_source : 1d1913c49ae58be86e991077bda96684c6afb941
This commit is contained in:
Jeff Walden 2014-09-18 20:06:32 -07:00
parent 053427c423
commit f5477c2a39
10 changed files with 144 additions and 73 deletions

View File

@ -286,6 +286,8 @@ typedef JSObject *(*ClassObjectCreationOp)(JSContext *cx, JSProtoKey key);
typedef bool (*FinishClassInitOp)(JSContext *cx, JS::HandleObject ctor,
JS::HandleObject proto);
const size_t JSCLASS_CACHED_PROTO_WIDTH = 6;
struct ClassSpec
{
ClassObjectCreationOp createConstructor;
@ -294,7 +296,29 @@ struct ClassSpec
const JSFunctionSpec *prototypeFunctions;
const JSPropertySpec *prototypeProperties;
FinishClassInitOp finishInit;
uintptr_t flags;
static const size_t ParentKeyWidth = JSCLASS_CACHED_PROTO_WIDTH;
static const uintptr_t ParentKeyMask = (1 << ParentKeyWidth) - 1;
static const uintptr_t DontDefineConstructor = 1 << ParentKeyWidth;
bool defined() const { return !!createConstructor; }
bool dependent() const {
MOZ_ASSERT(defined());
return (flags & ParentKeyMask);
}
JSProtoKey parentKey() const {
static_assert(JSProto_Null == 0, "zeroed key must be null");
return JSProtoKey(flags & ParentKeyMask);
}
bool shouldDefineConstructor() const {
MOZ_ASSERT(defined());
return !(flags & DontDefineConstructor);
}
};
struct ClassExtension
@ -373,7 +397,7 @@ typedef void (*JSClassInternal)();
struct JSClass {
JS_CLASS_MEMBERS(JSFinalizeOp);
void *reserved[32];
void *reserved[33];
};
#define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot
@ -446,7 +470,6 @@ struct JSClass {
// Fast access to the original value of each standard class's prototype.
#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 10)
#define JSCLASS_CACHED_PROTO_WIDTH 6
#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH)
#define JSCLASS_HAS_CACHED_PROTO(key) (uint32_t(key) << JSCLASS_CACHED_PROTO_SHIFT)
#define JSCLASS_CACHED_PROTO_KEY(clasp) ((JSProtoKey) \

View File

@ -3032,7 +3032,7 @@ const Class DateObject::class_ = {
nullptr, /* trace */
{
GenericCreateConstructor<js_Date, MAXARGS, JSFunction::FinalizeKind>,
GenericCreatePrototype<&DateObject::class_>,
GenericCreatePrototype,
date_static_methods,
date_methods,
nullptr,

View File

@ -59,30 +59,69 @@ static const JSFunctionSpec exception_methods[] = {
JS_FS_END
};
const Class ErrorObject::class_ = {
js_Error_str,
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Error) |
JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
JS_PropertyStub, /* addProperty */
JS_DeletePropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
exn_finalize,
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
{
ErrorObject::createConstructor,
ErrorObject::createProto,
nullptr,
exception_methods
#define IMPLEMENT_ERROR_SUBCLASS(name) \
{ \
js_Error_str, /* yes, really */ \
JSCLASS_IMPLEMENTS_BARRIERS | \
JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \
JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), \
JS_PropertyStub, /* addProperty */ \
JS_DeletePropertyStub, /* delProperty */ \
JS_PropertyStub, /* getProperty */ \
JS_StrictPropertyStub, /* setProperty */ \
JS_EnumerateStub, \
JS_ResolveStub, \
JS_ConvertStub, \
exn_finalize, \
nullptr, /* call */ \
nullptr, /* hasInstance */ \
nullptr, /* construct */ \
nullptr, /* trace */ \
{ \
ErrorObject::createConstructor, \
ErrorObject::createProto, \
nullptr, \
exception_methods, \
nullptr, \
nullptr, \
JSProto_Error \
} \
}
const Class
ErrorObject::classes[JSEXN_LIMIT] = {
{
js_Error_str,
JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Error) |
JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS),
JS_PropertyStub, /* addProperty */
JS_DeletePropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
JS_StrictPropertyStub, /* setProperty */
JS_EnumerateStub,
JS_ResolveStub,
JS_ConvertStub,
exn_finalize,
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
nullptr, /* trace */
{
ErrorObject::createConstructor,
ErrorObject::createProto,
nullptr,
exception_methods,
0
}
},
IMPLEMENT_ERROR_SUBCLASS(InternalError),
IMPLEMENT_ERROR_SUBCLASS(EvalError),
IMPLEMENT_ERROR_SUBCLASS(RangeError),
IMPLEMENT_ERROR_SUBCLASS(ReferenceError),
IMPLEMENT_ERROR_SUBCLASS(SyntaxError),
IMPLEMENT_ERROR_SUBCLASS(TypeError),
IMPLEMENT_ERROR_SUBCLASS(URIError)
};
JSErrorReport *
@ -465,7 +504,7 @@ JS_STATIC_ASSERT(JSProto_Error + JSEXN_URIERR == JSProto_URIError);
/* static */ JSObject *
ErrorObject::createProto(JSContext *cx, JSProtoKey key)
{
RootedObject errorProto(cx, GenericCreatePrototype<&ErrorObject::class_>(cx, key));
RootedObject errorProto(cx, GenericCreatePrototype(cx, key));
if (!errorProto)
return nullptr;

View File

@ -659,17 +659,14 @@ JS_FRIEND_API(const Class *)
ProtoKeyToClass(JSProtoKey key);
// Returns true if the standard class identified by |key| inherits from
// another standard class with the same js::Class. This basically means
// that the various properties described by our js::Class are intended
// to live higher up on the proto chain.
// another standard class (in addition to Object) along its proto chain.
//
// In practice, this only returns true for Error subtypes.
inline bool
StandardClassIsDependent(JSProtoKey key)
{
JSProtoKey keyFromClass = JSCLASS_CACHED_PROTO_KEY(ProtoKeyToClass(key));
MOZ_ASSERT(keyFromClass);
return key != keyFromClass;
const Class *clasp = ProtoKeyToClass(key);
return clasp->spec.defined() && clasp->spec.dependent();
}
// Returns the key for the class inherited by a given standard class (that
@ -686,10 +683,9 @@ ParentKeyForStandardClass(JSProtoKey key)
if (key == JSProto_Object)
return JSProto_Null;
// If we're dependent (i.e. an Error subtype), return the key of the class
// we depend on.
// If we're dependent, return the key of the class we depend on.
if (StandardClassIsDependent(key))
return JSCLASS_CACHED_PROTO_KEY(ProtoKeyToClass(key));
return ProtoKeyToClass(key)->spec.parentKey();
// Otherwise, we inherit [Object].
return JSProto_Object;

View File

@ -35,6 +35,7 @@
#define CLASP(name) (&name##Class)
#define OCLASP(name) (&name##Object::class_)
#define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[Scalar::type])
#define ERROR_CLASP(type) (&ErrorObject::classes[type])
#define SHARED_TYPED_ARRAY_CLASP(type) (&SharedTypedArrayObject::classes[Scalar::type])
#ifdef ENABLE_PARALLEL_JS
@ -79,14 +80,14 @@
real(Number, 8, js_InitNumberClass, OCLASP(Number)) \
real(String, 9, js_InitStringClass, OCLASP(String)) \
real(RegExp, 10, js_InitRegExpClass, OCLASP(RegExp)) \
real(Error, 11, js_InitViaClassSpec, OCLASP(Error)) \
real(InternalError, 12, js_InitViaClassSpec, OCLASP(Error)) \
real(EvalError, 13, js_InitViaClassSpec, OCLASP(Error)) \
real(RangeError, 14, js_InitViaClassSpec, OCLASP(Error)) \
real(ReferenceError, 15, js_InitViaClassSpec, OCLASP(Error)) \
real(SyntaxError, 16, js_InitViaClassSpec, OCLASP(Error)) \
real(TypeError, 17, js_InitViaClassSpec, OCLASP(Error)) \
real(URIError, 18, js_InitViaClassSpec, OCLASP(Error)) \
real(Error, 11, js_InitViaClassSpec, ERROR_CLASP(JSEXN_ERR)) \
real(InternalError, 12, js_InitViaClassSpec, ERROR_CLASP(JSEXN_INTERNALERR)) \
real(EvalError, 13, js_InitViaClassSpec, ERROR_CLASP(JSEXN_EVALERR)) \
real(RangeError, 14, js_InitViaClassSpec, ERROR_CLASP(JSEXN_RANGEERR)) \
real(ReferenceError, 15, js_InitViaClassSpec, ERROR_CLASP(JSEXN_REFERENCEERR)) \
real(SyntaxError, 16, js_InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR)) \
real(TypeError, 17, js_InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR)) \
real(URIError, 18, js_InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR)) \
real(Iterator, 19, js_InitIteratorClasses, OCLASP(PropertyIterator)) \
real(StopIteration, 20, js_InitIteratorClasses, OCLASP(StopIteration)) \
real(ArrayBuffer, 21, js_InitArrayBufferClass, &js::ArrayBufferObject::protoClass) \

View File

@ -93,7 +93,8 @@ js::ErrorObject::create(JSContext *cx, JSExnType errorType, HandleString stack,
Rooted<ErrorObject*> errObject(cx);
{
JSObject* obj = NewObjectWithGivenProto(cx, &ErrorObject::class_, proto, nullptr);
const Class *clasp = ErrorObject::classForType(errorType);
JSObject* obj = NewObjectWithGivenProto(cx, clasp, proto, nullptr);
if (!obj)
return nullptr;
errObject = &obj->as<ErrorObject>();

View File

@ -7,6 +7,8 @@
#ifndef vm_ErrorObject_h_
#define vm_ErrorObject_h_
#include "mozilla/ArrayUtils.h"
#include "jsobj.h"
#include "vm/Shape.h"
@ -63,7 +65,17 @@ class ErrorObject : public JSObject
static const uint32_t RESERVED_SLOTS = MESSAGE_SLOT + 1;
public:
static const Class class_;
static const Class classes[JSEXN_LIMIT];
static const Class * classForType(JSExnType type) {
MOZ_ASSERT(type != JSEXN_NONE);
MOZ_ASSERT(type < JSEXN_LIMIT);
return &classes[type];
}
static bool isErrorClass(const Class *clasp) {
return &classes[0] <= clasp && clasp < &classes[0] + mozilla::ArrayLength(classes);
}
// Create an error of the given type corresponding to the provided location
// info. If |message| is non-null, then the error will have a .message
@ -100,4 +112,11 @@ class ErrorObject : public JSObject
} // namespace js
template<>
inline bool
JSObject::is<js::ErrorObject>() const
{
return js::ErrorObject::isErrorClass(getClass());
}
#endif // vm_ErrorObject_h_

View File

@ -161,8 +161,10 @@ GlobalObject::resolveConstructor(JSContext *cx, Handle<GlobalObject*> global, JS
return false;
RootedId id(cx, NameToId(ClassName(key, cx)));
if (!global->addDataProperty(cx, id, constructorPropertySlot(key), 0))
return false;
if (clasp->spec.shouldDefineConstructor()) {
if (!global->addDataProperty(cx, id, constructorPropertySlot(key), 0))
return false;
}
global->setConstructor(key, ObjectValue(*ctor));
global->setConstructorPropertySlot(key, ObjectValue(*ctor));
@ -192,9 +194,11 @@ GlobalObject::resolveConstructor(JSContext *cx, Handle<GlobalObject*> global, JS
if (clasp->spec.finishInit && !clasp->spec.finishInit(cx, ctor, proto))
return false;
// Stash type information, so that what we do here is equivalent to
// initBuiltinConstructor.
types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
if (clasp->spec.shouldDefineConstructor()) {
// Stash type information, so that what we do here is equivalent to
// initBuiltinConstructor.
types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
}
return true;
}

View File

@ -795,11 +795,11 @@ GenericCreateConstructor(JSContext *cx, JSProtoKey key)
return cx->global()->createConstructor(cx, ctor, name, length, kind);
}
template<const Class *clasp>
JSObject *
inline JSObject *
GenericCreatePrototype(JSContext *cx, JSProtoKey key)
{
MOZ_ASSERT(key != JSProto_Object);
const Class *clasp = ProtoKeyToClass(key);
JSProtoKey parentKey = ParentKeyForStandardClass(key);
if (!GlobalObject::ensureConstructor(cx, cx->global(), parentKey))
return nullptr;

View File

@ -421,17 +421,12 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper,
return true;
}
// Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
const js::Class *clasp = js::GetObjectClass(target);
MOZ_ASSERT(clasp->spec.defined());
JSProtoKey protoKey = getProtoKey(holder);
// Handle the 'constructor' property.
if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) {
RootedObject constructor(cx);
{
JSAutoCompartment ac(cx, target);
if (!JS_GetClassObject(cx, protoKey, &constructor))
if (!JS_GetClassObject(cx, key, &constructor))
return false;
}
if (!JS_WrapObject(cx, &constructor))
@ -452,11 +447,6 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper,
return JS_IdToValue(cx, className, desc.value());
}
// Bail out for dependent classes, since all the rest of the properties we'll
// resolve here will live on the parent prototype.
if (js::StandardClassIsDependent(protoKey))
return true;
// Compute the property name we're looking for. Indexed array properties
// are handled above. We'll handle well-known symbols when we start
// supporting Symbol.iterator in bug 918828.
@ -464,6 +454,10 @@ JSXrayTraits::resolveOwnProperty(JSContext *cx, const Wrapper &jsWrapper,
return true;
Rooted<JSFlatString*> str(cx, JSID_TO_FLAT_STRING(id));
// Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
const js::Class *clasp = js::GetObjectClass(target);
MOZ_ASSERT(clasp->spec.defined());
// Scan through the functions.
const JSFunctionSpec *fsMatch = nullptr;
for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) {
@ -694,11 +688,6 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags
return true;
}
// Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
const js::Class *clasp = js::GetObjectClass(target);
MOZ_ASSERT(clasp->spec.defined());
JSProtoKey protoKey = getProtoKey(holder);
// Add the 'constructor' property.
if (!props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)))
return false;
@ -707,10 +696,9 @@ JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags
if (IsErrorObjectKey(key) && !props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_NAME)))
return false;
// Bail out for dependent classes, since all the rest of the properties we'll
// resolve here will live on the parent prototype.
if (js::StandardClassIsDependent(protoKey))
return true;
// Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
const js::Class *clasp = js::GetObjectClass(target);
MOZ_ASSERT(clasp->spec.defined());
// Intern all the strings, and pass theme to the caller.
for (const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; fs && fs->name; ++fs) {