Bug 1082672, part 1 - Add JSAPI macros JS_SYM_FN etc. to support defining functions with well-known symbol keys. r=Waldo.

--HG--
extra : rebase_source : 9791c940599844802c9a262fe8b1610a0de3ef40
This commit is contained in:
Jason Orendorff 2014-07-25 18:50:48 -05:00
parent 2f541b83f6
commit f07de76ea5
7 changed files with 151 additions and 66 deletions

View File

@ -1685,7 +1685,7 @@ InitIds(JSContext* cx, const Prefable<Spec>* prefableSpecs, jsid* ids)
// because this is only done once per application runtime.
Spec* spec = prefableSpecs->specs;
do {
if (!InternJSString(cx, *ids, spec->name)) {
if (!JS::PropertySpecNameToPermanentId(cx, spec->name, ids)) {
return false;
}
} while (++ids, (++spec)->name);

View File

@ -2781,7 +2781,8 @@ JS_AlreadyHasOwnUCProperty(JSContext *cx, HandleObject obj, const char16_t *name
return JS_AlreadyHasOwnPropertyById(cx, obj, id, foundp);
}
/* Wrapper functions to create wrappers with no corresponding JSJitInfo from API
/*
* Wrapper functions to create wrappers with no corresponding JSJitInfo from API
* function arguments.
*/
static JSPropertyOpWrapper
@ -2825,8 +2826,6 @@ DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue val
MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
JSFunction::Flags zeroFlags = JSAPIToJSFunctionFlags(0);
// We can't just use JS_NewFunctionById here because it assumes a
// string id.
RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : nullptr);
attrs &= ~JSPROP_NATIVE_ACCESSORS;
if (getter) {
@ -2863,12 +2862,12 @@ DefinePropertyById(JSContext *cx, HandleObject obj, HandleId id, HandleValue val
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj, id, value,
(attrs & JSPROP_GETTER)
? JS_FUNC_TO_DATA_PTR(JSObject *, getter)
: nullptr,
(attrs & JSPROP_SETTER)
? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
: nullptr);
(attrs & JSPROP_GETTER)
? JS_FUNC_TO_DATA_PTR(JSObject *, getter)
: nullptr,
(attrs & JSPROP_SETTER)
? JS_FUNC_TO_DATA_PTR(JSObject *, setter)
: nullptr);
return JSObject::defineGeneric(cx, obj, id, value, getter, setter, attrs);
}
@ -3012,30 +3011,22 @@ DefineProperty(JSContext *cx, HandleObject obj, const char *name, HandleValue va
return DefinePropertyById(cx, obj, id, value, getter, setter, attrs, flags);
}
static bool
DefineSelfHostedProperty(JSContext *cx,
HandleObject obj,
const char *name,
const char *getterName,
const char *setterName,
unsigned attrs,
unsigned flags)
DefineSelfHostedProperty(JSContext *cx, HandleObject obj, HandleId id,
const char *getterName, const char *setterName,
unsigned attrs, unsigned flags)
{
RootedAtom nameAtom(cx, Atomize(cx, name, strlen(name)));
if (!nameAtom)
return false;
RootedAtom getterNameAtom(cx, Atomize(cx, getterName, strlen(getterName)));
if (!getterNameAtom)
return false;
RootedValue getterValue(cx);
if (!cx->global()->getSelfHostedFunction(cx, getterNameAtom, nameAtom,
0, &getterValue))
{
RootedAtom name(cx, IdToFunctionName(cx, id));
if (!name)
return false;
RootedValue getterValue(cx);
if (!cx->global()->getSelfHostedFunction(cx, getterNameAtom, name, 0, &getterValue))
return false;
}
MOZ_ASSERT(getterValue.isObject() && getterValue.toObject().is<JSFunction>());
RootedFunction getterFunc(cx, &getterValue.toObject().as<JSFunction>());
JSPropertyOp getterOp = JS_DATA_TO_FUNC_PTR(PropertyOp, getterFunc.get());
@ -3047,19 +3038,16 @@ DefineSelfHostedProperty(JSContext *cx,
return false;
RootedValue setterValue(cx);
if (!cx->global()->getSelfHostedFunction(cx, setterNameAtom, nameAtom,
0, &setterValue))
{
if (!cx->global()->getSelfHostedFunction(cx, setterNameAtom, name, 0, &setterValue))
return false;
}
MOZ_ASSERT(setterValue.isObject() && setterValue.toObject().is<JSFunction>());
setterFunc = &getterValue.toObject().as<JSFunction>();
}
JSStrictPropertyOp setterOp = JS_DATA_TO_FUNC_PTR(StrictPropertyOp, setterFunc.get());
return DefineProperty(cx, obj, name, JS::UndefinedHandleValue,
GetterWrapper(getterOp), SetterWrapper(setterOp),
attrs, flags);
return DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
GetterWrapper(getterOp), SetterWrapper(setterOp),
attrs, flags);
}
JS_PUBLIC_API(bool)
@ -3256,37 +3244,73 @@ JS_DefineConstIntegers(JSContext *cx, HandleObject obj, const JSConstIntegerSpec
return DefineConstScalar(cx, obj, cis);
}
static bool
PropertySpecNameToId(JSContext *cx, const char *name, MutableHandleId id,
js::InternBehavior ib = js::DoNotInternAtom)
{
if (JS::PropertySpecNameIsSymbol(name)) {
uintptr_t u = reinterpret_cast<uintptr_t>(name);
id.set(SYMBOL_TO_JSID(cx->wellKnownSymbols().get(u - 1)));
} else {
JSAtom *atom = Atomize(cx, name, strlen(name), ib);
if (!atom)
return false;
id.set(AtomToId(atom));
}
return true;
}
JS_PUBLIC_API(bool)
JS::PropertySpecNameToPermanentId(JSContext *cx, const char *name, jsid *idp)
{
// We are calling fromMarkedLocation(idp) even though idp points to a
// location that will never be marked. This is OK because the whole point
// of this API is to populate *idp with a jsid that does not need to be
// marked.
return PropertySpecNameToId(cx, name, MutableHandleId::fromMarkedLocation(idp),
js::InternAtom);
}
JS_PUBLIC_API(bool)
JS_DefineProperties(JSContext *cx, HandleObject obj, const JSPropertySpec *ps)
{
bool ok;
for (ok = true; ps->name; ps++) {
RootedId id(cx);
for (; ps->name; ps++) {
if (!PropertySpecNameToId(cx, ps->name, &id))
return false;
if (ps->flags & JSPROP_NATIVE_ACCESSORS) {
// If you declare native accessors, then you should have a native
// getter.
MOZ_ASSERT(ps->getter.propertyOp.op);
// If you do not have a self-hosted getter, you should not have a
// self-hosted setter. This is the closest approximation to that
// assertion we can have with our setup.
MOZ_ASSERT_IF(ps->setter.propertyOp.info, ps->setter.propertyOp.op);
ok = DefineProperty(cx, obj, ps->name, JS::UndefinedHandleValue,
ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0);
if (!DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
ps->getter.propertyOp, ps->setter.propertyOp, ps->flags, 0))
{
return false;
}
} else {
// If you have self-hosted getter/setter, you can't have a
// native one.
MOZ_ASSERT(!ps->getter.propertyOp.op && !ps->setter.propertyOp.op);
MOZ_ASSERT(ps->flags & JSPROP_GETTER);
ok = DefineSelfHostedProperty(cx, obj, ps->name,
if (!DefineSelfHostedProperty(cx, obj, id,
ps->getter.selfHosted.funname,
ps->setter.selfHosted.funname,
ps->flags, 0);
ps->flags, 0))
{
return false;
}
}
if (!ok)
break;
}
return ok;
return true;
}
JS_PUBLIC_API(bool)
@ -3768,12 +3792,14 @@ JS_NewFunctionById(JSContext *cx, JSNative native, unsigned nargs, unsigned flag
JS_PUBLIC_API(JSFunction *)
JS::GetSelfHostedFunction(JSContext *cx, const char *selfHostedName, HandleId id, unsigned nargs)
{
MOZ_ASSERT(JSID_IS_STRING(id));
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RootedAtom name(cx, JSID_TO_ATOM(id));
RootedAtom name(cx, IdToFunctionName(cx, id));
if (!name)
return nullptr;
RootedAtom shName(cx, Atomize(cx, selfHostedName, strlen(selfHostedName)));
if (!shName)
return nullptr;
@ -3937,21 +3963,11 @@ JS_DefineFunctions(JSContext *cx, HandleObject obj, const JSFunctionSpec *fs)
CHECK_REQUEST(cx);
assertSameCompartment(cx, obj);
RootedId id(cx);
for (; fs->name; fs++) {
RootedAtom atom(cx);
// If the name starts with "@@", it must be a well-known symbol.
if (fs->name[0] != '@' || fs->name[1] != '@')
atom = Atomize(cx, fs->name, strlen(fs->name));
else if (strcmp(fs->name, "@@iterator") == 0)
// FIXME: This atom should be a symbol: bug 918828.
atom = cx->names().std_iterator;
else
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_SYMBOL, fs->name);
if (!atom)
if (!PropertySpecNameToId(cx, fs->name, &id))
return false;
Rooted<jsid> id(cx, AtomToId(atom));
/*
* Define a generic arity N+1 static method for the arity N prototype
* method if flags contains JSFUN_GENERIC_NATIVE.
@ -3993,8 +4009,11 @@ JS_DefineFunctions(JSContext *cx, HandleObject obj, const JSFunctionSpec *fs)
RootedAtom shName(cx, Atomize(cx, fs->selfHostedName, strlen(fs->selfHostedName)));
if (!shName)
return false;
RootedAtom name(cx, IdToFunctionName(cx, id));
if (!name)
return false;
RootedValue funVal(cx);
if (!cx->global()->getSelfHostedFunction(cx, shName, atom, fs->nargs, &funVal))
if (!cx->global()->getSelfHostedFunction(cx, shName, name, fs->nargs, &funVal))
return false;
if (!JSObject::defineGeneric(cx, obj, id, funVal, nullptr, nullptr, flags))
return false;

View File

@ -2475,15 +2475,27 @@ struct JSFunctionSpec {
* JSFUN_STUB_GSOPS. JS_FNINFO allows the simple adding of
* JSJitInfos. JS_SELF_HOSTED_FN declares a self-hosted function. Finally
* JS_FNSPEC has slots for all the fields.
*
* The _SYM variants allow defining a function with a symbol key rather than a
* string key. For example, use JS_SYM_FN(iterator, ...) to define an
* @@iterator method.
*/
#define JS_FS(name,call,nargs,flags) \
JS_FNSPEC(name, call, nullptr, nargs, flags, nullptr)
#define JS_FN(name,call,nargs,flags) \
JS_FNSPEC(name, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
#define JS_SYM_FN(name,call,nargs,flags) \
JS_SYM_FNSPEC(symbol, call, nullptr, nargs, (flags) | JSFUN_STUB_GSOPS, nullptr)
#define JS_FNINFO(name,call,info,nargs,flags) \
JS_FNSPEC(name, call, info, nargs, flags, nullptr)
#define JS_SELF_HOSTED_FN(name,selfHostedName,nargs,flags) \
JS_FNSPEC(name, nullptr, nullptr, nargs, flags, selfHostedName)
#define JS_SELF_HOSTED_SYM_FN(symbol, selfHostedName, nargs, flags) \
JS_SYM_FNSPEC(symbol, nullptr, nullptr, nargs, flags, selfHostedName)
#define JS_SYM_FNSPEC(symbol, call, info, nargs, flags, selfHostedName) \
JS_FNSPEC(reinterpret_cast<const char *>( \
uint32_t(::JS::SymbolCode::symbol) + 1), \
call, info, nargs, flags, selfHostedName)
#define JS_FNSPEC(name,call,info,nargs,flags,selfHostedName) \
{name, {call, info}, nargs, flags, selfHostedName}
@ -4494,6 +4506,28 @@ GetSymbolCode(Handle<Symbol*> symbol);
JS_PUBLIC_API(Symbol *)
GetWellKnownSymbol(JSContext *cx, SymbolCode which);
/*
* Return true if the given JSPropertySpec::name or JSFunctionSpec::name value
* is actually a symbol code and not a string. See JS_SYM_FN.
*/
inline bool
PropertySpecNameIsSymbol(const char *name)
{
uintptr_t u = reinterpret_cast<uintptr_t>(name);
return u != 0 && u - 1 < WellKnownSymbolLimit;
}
/*
* Create a jsid that does not need to be marked for GC.
*
* 'name' is a JSPropertySpec::name or JSFunctionSpec::name value. The
* resulting jsid, on success, is either an interned string or a well-known
* symbol; either way it is immune to GC so there is no need to visit *idp
* during GC marking.
*/
JS_PUBLIC_API(bool)
PropertySpecNameToPermanentId(JSContext *cx, const char *name, jsid *idp);
} /* namespace JS */
/************************************************************************/

View File

@ -284,6 +284,7 @@ struct ThreadSafeContext : ContextFriendFields,
JSAtomState &names() { return *runtime_->commonNames; }
StaticStrings &staticStrings() { return *runtime_->staticStrings; }
AtomSet &permanentAtoms() { return *runtime_->permanentAtoms; }
WellKnownSymbols &wellKnownSymbols() { return *runtime_->wellKnownSymbols; }
const JS::AsmJSCacheOps &asmJSCacheOps() { return runtime_->asmJSCacheOps; }
PropertyName *emptyString() { return runtime_->emptyString; }
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }

View File

@ -2063,6 +2063,33 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
return cloneRoot;
}
/*
* Return an atom for use as the name of a builtin method with the given
* property id.
*
* Function names are always strings. If id is the well-known @@iterator
* symbol, this returns "[Symbol.iterator]".
*
* Implements step 4 of SetFunctionName in ES6 draft rev 27 (24 Aug 2014).
*/
JSAtom *
js::IdToFunctionName(JSContext *cx, HandleId id)
{
if (JSID_IS_ATOM(id))
return JSID_TO_ATOM(id);
if (JSID_IS_SYMBOL(id)) {
RootedAtom desc(cx, JSID_TO_SYMBOL(id)->description());
StringBuffer sb(cx);
if (!sb.append('[') || !sb.append(desc) || !sb.append(']'))
return nullptr;
return sb.finishAtom();
}
RootedValue idv(cx, IdToValue(id));
return ToAtom<CanGC>(cx, idv);
}
JSFunction *
js::DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
unsigned nargs, unsigned flags, AllocKind allocKind /* = FinalizeKind */,
@ -2070,9 +2097,6 @@ js::DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
{
PropertyOp gop;
StrictPropertyOp sop;
RootedFunction fun(cx);
if (flags & JSFUN_STUB_GSOPS) {
/*
* JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
@ -2093,8 +2117,13 @@ js::DefineFunction(JSContext *cx, HandleObject obj, HandleId id, Native native,
funFlags = JSFunction::INTERPRETED_LAZY;
else
funFlags = JSAPIToJSFunctionFlags(flags);
RootedAtom atom(cx, JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : nullptr);
fun = NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom, allocKind, newKind);
RootedAtom atom(cx, IdToFunctionName(cx, id));
if (!atom)
return nullptr;
RootedFunction fun(cx, NewFunction(cx, NullPtr(), native, nargs, funFlags, obj, atom,
allocKind, newKind));
if (!fun)
return nullptr;

View File

@ -521,6 +521,9 @@ NewFunctionWithProto(ExclusiveContext *cx, HandleObject funobj, JSNative native,
JSObject *proto, gc::AllocKind allocKind = JSFunction::FinalizeKind,
NewObjectKind newKind = GenericObject);
extern JSAtom *
IdToFunctionName(JSContext *cx, HandleId id);
extern JSFunction *
DefineFunction(JSContext *cx, HandleObject obj, HandleId id, JSNative native,
unsigned nargs, unsigned flags,
@ -572,7 +575,6 @@ CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent,
gc::AllocKind kind = JSFunction::FinalizeKind,
NewObjectKind newKindArg = GenericObject);
extern bool
FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *bodyStart,
size_t *bodyEnd);
@ -664,7 +666,7 @@ js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp);
extern bool
js_fun_call(JSContext *cx, unsigned argc, js::Value *vp);
extern JSObject*
extern JSObject *
js_fun_bind(JSContext *cx, js::HandleObject target, js::HandleValue thisArg,
js::Value *boundArgs, unsigned argslen);

View File

@ -414,7 +414,7 @@ namespace js {
* Storage for well-known symbols. It's a separate struct from the Runtime so
* that it can be shared across multiple runtimes. As in JSAtomState, each
* field is a smart pointer that's immutable once initialized.
* `rt->wellKnownSymbols.iterator` is convertible to Handle<Symbol*>.
* `rt->wellKnownSymbols->iterator` is convertible to Handle<Symbol*>.
*
* Well-known symbols are never GC'd. The description() of each well-known
* symbol is a permanent atom.