# HG changeset patch # User Jason Orendorff # Date 1415158903 21600 # Tue Nov 04 21:41:43 2014 -0600 # Node ID f4eb7930ddebb329ee959c284454ce3eb0c7602c # Parent 7ba37b8dd17328f4300c076f7cd4c99336e5d6cb Bug 987514, part 3 - Make every global have a (usually empty) Reflect object; rename JS_InitReflect -> JS_InitReflectParse. diff --git a/js/src/builtin/Reflect.cpp b/js/src/builtin/Reflect.cpp new file mode 100644 --- /dev/null +++ b/js/src/builtin/Reflect.cpp @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "builtin/Reflect.h" + +using namespace js; + +JSObject * +js_InitReflect(JSContext *cx, HandleObject obj) +{ + static const JSFunctionSpec static_methods[] = { + JS_FS_END + }; + + RootedObject proto(cx, obj->as().getOrCreateObjectPrototype(cx)); + if (!proto) + return nullptr; + + RootedObject Reflect(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, + obj, SingletonObject)); + if (!Reflect) + return nullptr; + if (!JS_DefineFunctions(cx, Reflect, static_methods)) + return nullptr; + + RootedValue value(cx, ObjectValue(*Reflect)); + if (!JSObject::defineProperty(cx, obj, cx->names().Reflect, value, + JS_PropertyStub, JS_StrictPropertyStub, 0)) + { + return nullptr; + } + + obj->as().setConstructor(JSProto_Reflect, value); + + return Reflect; +} diff --git a/js/src/builtin/Reflect.h b/js/src/builtin/Reflect.h new file mode 100644 --- /dev/null +++ b/js/src/builtin/Reflect.h @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef builtin_Reflect_h +#define builtin_Reflect_h + +#include "jsobj.h" + +extern JSObject * +js_InitReflect(JSContext *cx, js::HandleObject obj); + +#endif /* builtin_Reflect_h */ diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -11,16 +11,17 @@ #include #include "jsarray.h" #include "jsatom.h" #include "jsobj.h" #include "jspubtd.h" +#include "builtin/Reflect.h" #include "frontend/Parser.h" #include "frontend/TokenStream.h" #include "js/CharacterEncoding.h" #include "vm/RegExpObject.h" #include "jsobjinlines.h" #include "frontend/ParseNode-inl.h" @@ -3592,34 +3593,27 @@ reflect_parse(JSContext *cx, uint32_t ar args.rval().setNull(); return false; } args.rval().set(val); return true; } -JS_PUBLIC_API(JSObject *) -JS_InitReflect(JSContext *cx, HandleObject obj) +JS_PUBLIC_API(bool) +JS_InitReflectParse(JSContext *cx, HandleObject global) { static const JSFunctionSpec static_methods[] = { JS_FN("parse", reflect_parse, 1, 0), JS_FS_END }; - RootedObject proto(cx, obj->as().getOrCreateObjectPrototype(cx)); - if (!proto) + RootedValue reflectVal(cx); + if (!JSObject::getProperty(cx, global, global, cx->names().Reflect, &reflectVal)) return nullptr; - RootedObject Reflect(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, - obj, SingletonObject)); - if (!Reflect) - return nullptr; - - if (!JS_DefineProperty(cx, obj, "Reflect", Reflect, 0, - JS_STUBGETTER, JS_STUBSETTER)) { + if (!reflectVal.isObject()) { + JS_ReportError(cx, "JS_InitReflectParse must be called during global initialization"); return nullptr; } - if (!JS_DefineFunctions(cx, Reflect, static_methods)) - return nullptr; - - return Reflect; + RootedObject reflectObj(cx, &reflectVal.toObject()); + return JS_DefineFunctions(cx, reflectObj, static_methods); } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1262,22 +1262,25 @@ JS_ResolveStandardClass(JSContext *cx, H /* Try less frequently used top-level functions and constants. */ if (!stdnm) stdnm = LookupStdName(rt, idstr, builtin_property_names); // If this class is anonymous, then it doesn't exist as a global // property, so we won't resolve anything. JSProtoKey key = stdnm ? stdnm->key : JSProto_Null; - if (key != JSProto_Null && !(ProtoKeyToClass(key)->flags & JSCLASS_IS_ANONYMOUS)) { - if (!GlobalObject::ensureConstructor(cx, global, stdnm->key)) - return false; - - *resolved = true; - return true; + if (key != JSProto_Null) { + const Class *clasp = ProtoKeyToClass(key); + if (!clasp || !(clasp->flags & JSCLASS_IS_ANONYMOUS)) { + if (!GlobalObject::ensureConstructor(cx, global, key)) + return false; + + *resolved = true; + return true; + } } // There is no such property to resolve. An ordinary resolve hook would // just return true at this point. But the global object is special in one // more way: its prototype chain is lazily initialized. That is, // global->getProto() might be null right now because we haven't created // Object.prototype yet. Force it now. if (!global->getOrCreateObjectPrototype(cx)) diff --git a/js/src/jsapi.h b/js/src/jsapi.h --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1860,20 +1860,21 @@ JS_GetGlobalForCompartmentOrNull(JSConte namespace JS { extern JS_PUBLIC_API(JSObject *) CurrentGlobalOrNull(JSContext *cx); } /* - * Initialize the 'Reflect' object on a global object. + * Add 'Reflect.parse', a SpiderMonkey extension, to the Reflect object on the + * given global. */ -extern JS_PUBLIC_API(JSObject *) -JS_InitReflect(JSContext *cx, JS::HandleObject global); +extern JS_PUBLIC_API(bool) +JS_InitReflectParse(JSContext *cx, JS::HandleObject global); /* * Add various profiling-related functions as properties of the given object. * Defined in builtin/Profilers.cpp. */ extern JS_PUBLIC_API(bool) JS_DefineProfilingFunctions(JSContext *cx, JS::HandleObject obj); diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -639,17 +639,17 @@ ProtoKeyToClass(JSProtoKey key); // Returns true if the standard class identified by |key| inherits from // 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) { const Class *clasp = ProtoKeyToClass(key); - return clasp->spec.defined() && clasp->spec.dependent(); + return clasp && clasp->spec.defined() && clasp->spec.dependent(); } // Returns the key for the class inherited by a given standard class (that // is to say, the prototype of this standard class's prototype). // // You must be sure that this corresponds to a standard class with a cached // JSProtoKey before calling this function. In general |key| will match the // cached proto key, except in cases where multiple JSProtoKeys share a diff --git a/js/src/jsprototypes.h b/js/src/jsprototypes.h --- a/js/src/jsprototypes.h +++ b/js/src/jsprototypes.h @@ -118,12 +118,13 @@ IF_SAB(real,imaginary)(SharedInt16Array, IF_SAB(real,imaginary)(SharedUint16Array, 46, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Uint16)) \ IF_SAB(real,imaginary)(SharedInt32Array, 47, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Int32)) \ IF_SAB(real,imaginary)(SharedUint32Array, 48, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Uint32)) \ IF_SAB(real,imaginary)(SharedFloat32Array, 49, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Float32)) \ IF_SAB(real,imaginary)(SharedFloat64Array, 50, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Float64)) \ IF_SAB(real,imaginary)(SharedUint8ClampedArray, 51, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Uint8Clamped)) \ real(TypedArray, 52, js_InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \ IF_SAB(real,imaginary)(Atomics, 53, js_InitAtomicsClass, OCLASP(Atomics)) \ + real(Reflect, 54, js_InitReflect, nullptr) \ #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro) #endif /* jsprototypes_h */ diff --git a/js/src/moz.build b/js/src/moz.build --- a/js/src/moz.build +++ b/js/src/moz.build @@ -99,16 +99,17 @@ UNIFIED_SOURCES += [ 'asmjs/AsmJSSignalHandlers.cpp', 'asmjs/AsmJSValidate.cpp', 'builtin/AtomicsObject.cpp', 'builtin/Eval.cpp', 'builtin/Intl.cpp', 'builtin/MapObject.cpp', 'builtin/Object.cpp', 'builtin/Profilers.cpp', + 'builtin/Reflect.cpp', 'builtin/ReflectParse.cpp', 'builtin/SIMD.cpp', 'builtin/SymbolObject.cpp', 'builtin/TestingFunctions.cpp', 'builtin/TypedObject.cpp', 'builtin/WeakSetObject.cpp', 'devtools/sharkctl.cpp', 'ds/LifoAlloc.cpp', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -5264,17 +5264,17 @@ NewGlobalObject(JSContext *cx, JS::Compa if (!JS_InitStandardClasses(cx, glob)) return nullptr; #endif #ifdef JS_HAS_CTYPES if (!JS_InitCTypesClass(cx, glob)) return nullptr; #endif - if (!JS_InitReflect(cx, glob)) + if (!JS_InitReflectParse(cx, glob)) return nullptr; if (!JS_DefineDebuggerObject(cx, glob)) return nullptr; if (!JS::RegisterPerfMeasurement(cx, glob)) return nullptr; if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) || !JS_DefineProfilingFunctions(cx, glob)) { diff --git a/js/src/tests/ecma_6/Reflect/browser.js b/js/src/tests/ecma_6/Reflect/browser.js new file mode 100644 diff --git a/js/src/tests/ecma_6/Reflect/shell.js b/js/src/tests/ecma_6/Reflect/shell.js new file mode 100644 diff --git a/js/src/tests/ecma_6/Reflect/surfaces.js b/js/src/tests/ecma_6/Reflect/surfaces.js new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/Reflect/surfaces.js @@ -0,0 +1,20 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +assertEq(typeof Reflect, 'object'); +assertEq(Object.getPrototypeOf(Reflect), Object.prototype); +assertEq(Reflect.toString(), '[object Object]'); + +var desc = Object.getOwnPropertyDescriptor(this, "Reflect"); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, true); +assertEq(desc.writable, true); + +// Assert that the SpiderMonkey "resolve hook" mechanism does not resurrect the +// Reflect property once it is deleted. +delete this.Reflect; +assertEq("Reflect" in this, false); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -104,16 +104,18 @@ GlobalObject::resolveConstructor(JSConte // the class js_InitFoo hook, defined in a JSProtoKey-keyed table at the // top of this file. The other lives in the ClassSpec for classes that // define it. Classes may use one or the other, but not both. ClassInitializerOp init = protoTable[key].init; if (init == js_InitViaClassSpec) init = nullptr; const Class *clasp = ProtoKeyToClass(key); + if (!init && !clasp) + return true; // JSProto_Null or a compile-time-disabled feature. // Some classes have no init routine, which means that they're disabled at // compile-time. We could try to enforce that callers never pass such keys // to resolveConstructor, but that would cramp the style of consumers like // GlobalObject::initStandardClasses that want to just carpet-bomb-call // ensureConstructor with every JSProtoKey. So it's easier to just handle // it here. bool haveSpec = clasp && clasp->spec.defined(); diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -817,16 +817,17 @@ GenericCreateConstructor(JSContext *cx, return cx->global()->createConstructor(cx, ctor, name, length, kind); } inline JSObject * GenericCreatePrototype(JSContext *cx, JSProtoKey key) { MOZ_ASSERT(key != JSProto_Object); const Class *clasp = ProtoKeyToClass(key); + MOZ_ASSERT(clasp); JSProtoKey parentKey = ParentKeyForStandardClass(key); if (!GlobalObject::ensureConstructor(cx, cx->global(), parentKey)) return nullptr; JSObject *parentProto = &cx->global()->getPrototype(parentKey).toObject(); return cx->global()->createBlankPrototypeInheriting(cx, clasp, *parentProto); } inline JSProtoKey diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp --- a/js/xpconnect/src/XPCShellImpl.cpp +++ b/js/xpconnect/src/XPCShellImpl.cpp @@ -1477,17 +1477,17 @@ XRE_XPCShellMain(int argc, char **argv, // discarded, there's no reason to do that on XPCShell, and doing so // might break various automation scripts. JS::CompartmentOptionsRef(glob).setDiscardSource(false); backstagePass->SetGlobalObject(glob); JSAutoCompartment ac(cx, glob); - if (!JS_InitReflect(cx, glob)) { + if (!JS_InitReflectParse(cx, glob)) { return 1; } if (!JS_DefineFunctions(cx, glob, glob_functions) || !JS_DefineProfilingFunctions(cx, glob)) { return 1; } diff --git a/toolkit/components/reflect/reflect.cpp b/toolkit/components/reflect/reflect.cpp --- a/toolkit/components/reflect/reflect.cpp +++ b/toolkit/components/reflect/reflect.cpp @@ -43,17 +43,17 @@ Module::Call(nsIXPConnectWrappedNative* JSObject* obj, const JS::CallArgs& args, bool* _retval) { JS::Rooted global(cx, JS::CurrentGlobalOrNull(cx)); if (!global) return NS_ERROR_NOT_AVAILABLE; - *_retval = !!JS_InitReflect(cx, global); + *_retval = JS_InitReflectParse(cx, global); return NS_OK; } } } NS_DEFINE_NAMED_CID(JSREFLECT_CID);