diff options
Diffstat (limited to 'js/src/builtin/SymbolObject.cpp')
-rw-r--r-- | js/src/builtin/SymbolObject.cpp | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/js/src/builtin/SymbolObject.cpp b/js/src/builtin/SymbolObject.cpp new file mode 100644 index 000000000..cf48402d6 --- /dev/null +++ b/js/src/builtin/SymbolObject.cpp @@ -0,0 +1,234 @@ +/* -*- 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/SymbolObject.h" + +#include "vm/StringBuffer.h" +#include "vm/Symbol.h" + +#include "jsobjinlines.h" + +#include "vm/NativeObject-inl.h" + +using JS::Symbol; +using namespace js; + +const Class SymbolObject::class_ = { + "Symbol", + JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol) +}; + +SymbolObject* +SymbolObject::create(JSContext* cx, JS::HandleSymbol symbol) +{ + JSObject* obj = NewBuiltinClassInstance(cx, &class_); + if (!obj) + return nullptr; + SymbolObject& symobj = obj->as<SymbolObject>(); + symobj.setPrimitiveValue(symbol); + return &symobj; +} + +const JSPropertySpec SymbolObject::properties[] = { + JS_PS_END +}; + +const JSFunctionSpec SymbolObject::methods[] = { + JS_FN(js_toString_str, toString, 0, 0), + JS_FN(js_valueOf_str, valueOf, 0, 0), + JS_SYM_FN(toPrimitive, toPrimitive, 1, JSPROP_READONLY), + JS_FS_END +}; + +const JSFunctionSpec SymbolObject::staticMethods[] = { + JS_FN("for", for_, 1, 0), + JS_FN("keyFor", keyFor, 1, 0), + JS_FS_END +}; + +JSObject* +SymbolObject::initClass(JSContext* cx, HandleObject obj) +{ + Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>()); + + // This uses &JSObject::class_ because: "The Symbol prototype object is an + // ordinary object. It is not a Symbol instance and does not have a + // [[SymbolData]] internal slot." (ES6 rev 24, 19.4.3) + RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx)); + if (!proto) + return nullptr; + + RootedFunction ctor(cx, global->createConstructor(cx, construct, + ClassName(JSProto_Symbol, cx), 0)); + if (!ctor) + return nullptr; + + // Define the well-known symbol properties, such as Symbol.iterator. + ImmutablePropertyNamePtr* names = cx->names().wellKnownSymbolNames(); + RootedValue value(cx); + unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT; + WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols; + for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) { + value.setSymbol(wks->get(i)); + if (!NativeDefineProperty(cx, ctor, names[i], value, nullptr, nullptr, attrs)) + return nullptr; + } + + if (!LinkConstructorAndPrototype(cx, ctor, proto) || + !DefinePropertiesAndFunctions(cx, proto, properties, methods) || + !DefineToStringTag(cx, proto, cx->names().Symbol) || + !DefinePropertiesAndFunctions(cx, ctor, nullptr, staticMethods) || + !GlobalObject::initBuiltinConstructor(cx, global, JSProto_Symbol, ctor, proto)) + { + return nullptr; + } + return proto; +} + +// ES6 rev 24 (2014 Apr 27) 19.4.1.1 and 19.4.1.2 +bool +SymbolObject::construct(JSContext* cx, unsigned argc, Value* vp) +{ + // According to a note in the draft standard, "Symbol has ordinary + // [[Construct]] behaviour but the definition of its @@create method causes + // `new Symbol` to throw a TypeError exception." We do not support @@create + // yet, so just throw a TypeError. + CallArgs args = CallArgsFromVp(argc, vp); + if (args.isConstructing()) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR, "Symbol"); + return false; + } + + // steps 1-3 + RootedString desc(cx); + if (!args.get(0).isUndefined()) { + desc = ToString(cx, args.get(0)); + if (!desc) + return false; + } + + // step 4 + RootedSymbol symbol(cx, JS::Symbol::new_(cx, JS::SymbolCode::UniqueSymbol, desc)); + if (!symbol) + return false; + args.rval().setSymbol(symbol); + return true; +} + +// ES6 rev 24 (2014 Apr 27) 19.4.2.2 +bool +SymbolObject::for_(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // steps 1-2 + RootedString stringKey(cx, ToString(cx, args.get(0))); + if (!stringKey) + return false; + + // steps 3-7 + JS::Symbol* symbol = JS::Symbol::for_(cx, stringKey); + if (!symbol) + return false; + args.rval().setSymbol(symbol); + return true; +} + +// ES6 rev 25 (2014 May 22) 19.4.2.7 +bool +SymbolObject::keyFor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // step 1 + HandleValue arg = args.get(0); + if (!arg.isSymbol()) { + ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK, + arg, nullptr, "not a symbol", nullptr); + return false; + } + + // step 2 + if (arg.toSymbol()->code() == JS::SymbolCode::InSymbolRegistry) { +#ifdef DEBUG + RootedString desc(cx, arg.toSymbol()->description()); + MOZ_ASSERT(Symbol::for_(cx, desc) == arg.toSymbol()); +#endif + args.rval().setString(arg.toSymbol()->description()); + return true; + } + + // step 3: omitted + // step 4 + args.rval().setUndefined(); + return true; +} + +MOZ_ALWAYS_INLINE bool +IsSymbol(HandleValue v) +{ + return v.isSymbol() || (v.isObject() && v.toObject().is<SymbolObject>()); +} + +// ES6 rev 27 (2014 Aug 24) 19.4.3.2 +bool +SymbolObject::toString_impl(JSContext* cx, const CallArgs& args) +{ + // steps 1-3 + HandleValue thisv = args.thisv(); + MOZ_ASSERT(IsSymbol(thisv)); + Rooted<Symbol*> sym(cx, thisv.isSymbol() + ? thisv.toSymbol() + : thisv.toObject().as<SymbolObject>().unbox()); + + // step 4 + return SymbolDescriptiveString(cx, sym, args.rval()); +} + +bool +SymbolObject::toString(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod<IsSymbol, toString_impl>(cx, args); +} + +//ES6 rev 24 (2014 Apr 27) 19.4.3.3 +bool +SymbolObject::valueOf_impl(JSContext* cx, const CallArgs& args) +{ + // Step 3, the error case, is handled by CallNonGenericMethod. + HandleValue thisv = args.thisv(); + MOZ_ASSERT(IsSymbol(thisv)); + if (thisv.isSymbol()) + args.rval().set(thisv); + else + args.rval().setSymbol(thisv.toObject().as<SymbolObject>().unbox()); + return true; +} + +bool +SymbolObject::valueOf(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args); +} + +// ES6 19.4.3.4 +bool +SymbolObject::toPrimitive(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // The specification gives exactly the same algorithm for @@toPrimitive as + // for valueOf, so reuse the valueOf implementation. + return CallNonGenericMethod<IsSymbol, valueOf_impl>(cx, args); +} + +JSObject* +js::InitSymbolClass(JSContext* cx, HandleObject obj) +{ + return SymbolObject::initClass(cx, obj); +} |