lib/VM/JSLib/Symbol.cpp (237 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ //===----------------------------------------------------------------------===// /// \file /// ES6.0 19.4 Initialize the Symbol constructor. //===----------------------------------------------------------------------===// #include "JSLibInternal.h" #include "hermes/VM/Operations.h" #include "hermes/VM/PrimitiveBox.h" #include "hermes/VM/Runtime.h" #include "hermes/VM/StringBuilder.h" #include "hermes/VM/StringPrimitive.h" namespace hermes { namespace vm { Handle<JSObject> createSymbolConstructor(Runtime &runtime) { auto symbolPrototype = Handle<JSObject>::vmcast(&runtime.symbolPrototype); auto cons = defineSystemConstructor<JSSymbol>( runtime, Predefined::getSymbolID(Predefined::Symbol), symbolConstructor, symbolPrototype, 0, CellKind::JSSymbolKind); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::predefinedFor), nullptr, symbolFor, 1); defineMethod( runtime, cons, Predefined::getSymbolID(Predefined::keyFor), nullptr, symbolKeyFor, 1); // Well-known symbols. DefinePropertyFlags dpf{}; dpf.writable = 0; dpf.enumerable = 0; dpf.configurable = 0; dpf.setWritable = 0; dpf.setEnumerable = 0; dpf.setConfigurable = 0; dpf.setValue = 1; defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::hasInstance), runtime.makeHandle( Predefined::getSymbolID(Predefined::SymbolHasInstance)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::iterator), runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolIterator)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::isConcatSpreadable), runtime.makeHandle( Predefined::getSymbolID(Predefined::SymbolIsConcatSpreadable)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::toPrimitive), runtime.makeHandle( Predefined::getSymbolID(Predefined::SymbolToPrimitive)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::toStringTag), runtime.makeHandle( Predefined::getSymbolID(Predefined::SymbolToStringTag)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::match), runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolMatch)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::matchAll), runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolMatchAll)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::search), runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolSearch)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::replace), runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolReplace)), dpf); defineProperty( runtime, cons, Predefined::getSymbolID(Predefined::split), runtime.makeHandle(Predefined::getSymbolID(Predefined::SymbolSplit)), dpf); // Symbol.prototype.xxx methods. void *ctx = nullptr; defineAccessor( runtime, symbolPrototype, Predefined::getSymbolID(Predefined::description), ctx, symbolPrototypeDescriptionGetter, nullptr, false, true); defineMethod( runtime, symbolPrototype, Predefined::getSymbolID(Predefined::toString), ctx, symbolPrototypeToString, 0); defineMethod( runtime, symbolPrototype, Predefined::getSymbolID(Predefined::valueOf), ctx, symbolPrototypeValueOf, 0); dpf = DefinePropertyFlags::getDefaultNewPropertyFlags(); dpf.writable = 0; dpf.enumerable = 0; defineProperty( runtime, symbolPrototype, Predefined::getSymbolID(Predefined::SymbolToStringTag), runtime.getPredefinedStringHandle(Predefined::Symbol), dpf); (void)defineMethod( runtime, symbolPrototype, Predefined::getSymbolID(Predefined::SymbolToPrimitive), Predefined::getSymbolID(Predefined::squareSymbolToPrimitive), nullptr, symbolPrototypeValueOf, 1, dpf); return cons; } CallResult<HermesValue> symbolConstructor(void *, Runtime &runtime, NativeArgs args) { if (args.isConstructorCall()) { return runtime.raiseTypeError("Symbol is not a constructor"); } MutableHandle<StringPrimitive> descString{runtime}; if (args.getArg(0).isUndefined()) { // If description is undefined, the descString will eventually be "". descString = runtime.getPredefinedString(Predefined::emptyString); } else { auto descStringRes = toString_RJS(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(descStringRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } descString = descStringRes->get(); } auto symbolRes = runtime.getIdentifierTable().createNotUniquedSymbol(runtime, descString); if (LLVM_UNLIKELY(symbolRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return HermesValue::encodeSymbolValue(*symbolRes); } CallResult<HermesValue> symbolFor(void *, Runtime &runtime, NativeArgs args) { auto cr = toString_RJS(runtime, args.getArgHandle(0)); if (LLVM_UNLIKELY(cr == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto key = runtime.makeHandle(std::move(*cr)); auto symbolRes = runtime.getSymbolRegistry().getSymbolForKey(runtime, key); if (LLVM_UNLIKELY(symbolRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return HermesValue::encodeSymbolValue(*symbolRes); } CallResult<HermesValue> symbolKeyFor(void *, Runtime &runtime, NativeArgs args) { if (LLVM_UNLIKELY(!args.getArg(0).isSymbol())) { return runtime.raiseTypeError("Symbol.keyFor() requires a symbol argument"); } auto sym = Handle<SymbolID>::vmcast(args.getArgHandle(0)); if (runtime.getSymbolRegistry().hasSymbol(sym.get())) { return HermesValue::encodeStringValue( runtime.getStringPrimFromSymbolID(*sym)); } return HermesValue::encodeUndefinedValue(); } /// ES10.0 19.4.3.2 get Symbol.prototype.description /// TODO(T79770380): make the Symbol(undefined) case spec-conformant. CallResult<HermesValue> symbolPrototypeDescriptionGetter(void *, Runtime &runtime, NativeArgs args) { MutableHandle<SymbolID> sym{runtime}; // 1. Let s be the this value. // 2. Let sym be ? thisSymbolValue(s). if (args.getThisArg().isSymbol()) { sym = args.vmcastThis<SymbolID>().get(); } else if (auto symHandle = args.dyncastThis<JSSymbol>()) { sym = symHandle->getPrimitiveSymbol(); } else { return runtime.raiseTypeError( "Symbol.prototype.description can only be called on Symbol"); } // 3. Return sym.[[Description]]. StringPrimitive *desc = runtime.getStringPrimFromSymbolID(*sym); return HermesValue::encodeStringValue(desc); } /// ES10 19.4.3.3 Symbol.prototype.toString ( ) CallResult<HermesValue> symbolPrototypeToString(void *, Runtime &runtime, NativeArgs args) { // 1. Let sym be ? thisSymbolValue(this value). MutableHandle<SymbolID> sym{runtime}; if (args.getThisArg().isSymbol()) { sym = args.vmcastThis<SymbolID>().get(); } else if (auto symHandle = args.dyncastThis<JSSymbol>()) { sym = symHandle->getPrimitiveSymbol(); } else { return runtime.raiseTypeError( "Symbol.prototype.toString can only be called on Symbol"); } // 2. Return SymbolDescriptiveString(sym). auto str = symbolDescriptiveString(runtime, sym); if (LLVM_UNLIKELY(str == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return str->getHermesValue(); } /// ES10 19.4.3.4 Symbol.prototype.valueOf ( ) CallResult<HermesValue> symbolPrototypeValueOf(void *, Runtime &runtime, NativeArgs args) { // 1. Return ? thisSymbolValue(this value). if (args.getThisArg().isSymbol()) { return args.getThisArg(); } if (auto jsSymbol = args.dyncastThis<JSSymbol>()) { return jsSymbol->getPrimitiveSymbol().getHermesValue(); } return runtime.raiseTypeError( "Symbol.prototype.valueOf can only be called on Symbol"); } } // namespace vm } // namespace hermes