lib/VM/PrimitiveBox.cpp (264 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. */ #include "hermes/VM/PrimitiveBox.h" #include "hermes/VM/BuildMetadata.h" #include "hermes/VM/Runtime-inline.h" #include "hermes/VM/StringPrimitive.h" namespace hermes { namespace vm { //===----------------------------------------------------------------------===// // class JSString const ObjectVTable JSString::vt{ VTable(CellKind::JSStringKind, cellSize<JSString>()), JSString::_getOwnIndexedRangeImpl, JSString::_haveOwnIndexedImpl, JSString::_getOwnIndexedPropertyFlagsImpl, JSString::_getOwnIndexedImpl, JSString::_setOwnIndexedImpl, JSString::_deleteOwnIndexedImpl, JSString::_checkAllOwnIndexedImpl, }; void JSStringBuildMeta(const GCCell *cell, Metadata::Builder &mb) { mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSString>()); JSObjectBuildMeta(cell, mb); const auto *self = static_cast<const JSString *>(cell); mb.setVTable(&JSString::vt); mb.addField(&self->primitiveValue_); } CallResult<Handle<JSString>> JSString::create( Runtime &runtime, Handle<StringPrimitive> value, Handle<JSObject> parentHandle) { auto clazzHandle = runtime.getHiddenClassForPrototype( *parentHandle, numOverlapSlots<JSString>()); auto obj = runtime.makeAFixed<JSString>(runtime, value, parentHandle, clazzHandle); auto selfHandle = JSObjectInit::initToHandle(runtime, obj); PropertyFlags pf; pf.writable = 0; pf.enumerable = 0; pf.configurable = 0; if (LLVM_UNLIKELY( JSObject::defineNewOwnProperty( selfHandle, runtime, Predefined::getSymbolID(Predefined::length), pf, runtime.makeHandle(HermesValue::encodeDoubleValue( value->getStringLength()))) == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return selfHandle; } void JSString::setPrimitiveString( Handle<JSString> selfHandle, Runtime &runtime, Handle<StringPrimitive> string) { NamedPropertyDescriptor desc; bool res = JSObject::getOwnNamedDescriptor( selfHandle, runtime, Predefined::getSymbolID(Predefined::length), desc); assert(res && "cannot find 'length' property"); (void)res; // This is definitely not a proxy because we know strings have lengths. auto shv = SmallHermesValue::encodeNumberValue(string->getStringLength(), runtime); JSObject::setNamedSlotValueUnsafe(*selfHandle, runtime, desc, shv); selfHandle->primitiveValue_.set(runtime, *string, &runtime.getHeap()); } bool JSString::_haveOwnIndexedImpl( JSObject *self, Runtime &runtime, uint32_t index) { auto *str = getPrimitiveString(vmcast<JSString>(self), runtime); return index < str->getStringLength(); } OptValue<PropertyFlags> JSString::_getOwnIndexedPropertyFlagsImpl( JSObject *self, Runtime &runtime, uint32_t index) { auto *str = getPrimitiveString(vmcast<JSString>(self), runtime); if (index < str->getStringLength()) { PropertyFlags flags; flags.enumerable = 1; return flags; } return llvh::None; } std::pair<uint32_t, uint32_t> JSString::_getOwnIndexedRangeImpl( JSObject *selfObj, Runtime &runtime) { auto *str = getPrimitiveString(vmcast<JSString>(selfObj), runtime); return {0, str->getStringLength()}; } HermesValue JSString::_getOwnIndexedImpl(JSObject *self, Runtime &runtime, uint32_t index) { auto *str = getPrimitiveString(vmcast<JSString>(self), runtime); return LLVM_LIKELY(index < str->getStringLength()) ? runtime.getCharacterString(str->at(index)).getHermesValue() : HermesValue::encodeEmptyValue(); } CallResult<bool> JSString::_setOwnIndexedImpl( Handle<JSObject> selfHandle, Runtime &runtime, uint32_t index, Handle<> valueHandle) { auto *str = getPrimitiveString(vmcast<JSString>(selfHandle.get()), runtime); if (index < str->getStringLength()) return false; // Property indexes beyond the end of the string must be added as named // properties. auto vr = valueToSymbolID( runtime, runtime.makeHandle(HermesValue::encodeNumberValue(index))); assert( vr != ExecutionStatus::EXCEPTION && "valueToIdentifier() failed for uint32_t value"); // Can't call defineOwnComputedPrimitive because it would infinitely recurse // calling JSString::_setOwnIndexedImpl. auto dr = JSObject::defineOwnPropertyInternal( selfHandle, runtime, **vr, DefinePropertyFlags::getDefaultNewPropertyFlags(), valueHandle); assert( dr != ExecutionStatus::EXCEPTION && "defineOwnProperty() threw in JSString::_setOwnIndexedImpl()"); return *dr; } bool JSString::_deleteOwnIndexedImpl( Handle<JSObject> selfHandle, Runtime &runtime, uint32_t index) { auto *str = getPrimitiveString(vmcast<JSString>(selfHandle.get()), runtime); // Only characters past the end of the string can be deleted (since they // already are). return index >= str->getStringLength(); } //===----------------------------------------------------------------------===// // class JSStringIterator const ObjectVTable JSStringIterator::vt{ VTable(CellKind::JSStringIteratorKind, cellSize<JSStringIterator>()), JSStringIterator::_getOwnIndexedRangeImpl, JSStringIterator::_haveOwnIndexedImpl, JSStringIterator::_getOwnIndexedPropertyFlagsImpl, JSStringIterator::_getOwnIndexedImpl, JSStringIterator::_setOwnIndexedImpl, JSStringIterator::_deleteOwnIndexedImpl, JSStringIterator::_checkAllOwnIndexedImpl, }; void JSStringIteratorBuildMeta(const GCCell *cell, Metadata::Builder &mb) { mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSStringIterator>()); JSObjectBuildMeta(cell, mb); const auto *self = static_cast<const JSStringIterator *>(cell); mb.setVTable(&JSStringIterator::vt); mb.addField("iteratedString", &self->iteratedString_); } /// ES6.0 21.1.5.1 CreateStringIterator Abstract Operation PseudoHandle<JSStringIterator> JSStringIterator::create( Runtime &runtime, Handle<StringPrimitive> string) { auto proto = Handle<JSObject>::vmcast(&runtime.stringIteratorPrototype); auto clazzHandle = runtime.getHiddenClassForPrototype( *proto, numOverlapSlots<JSStringIterator>()); auto obj = runtime.makeAFixed<JSStringIterator>(runtime, proto, clazzHandle, string); return JSObjectInit::initToPseudoHandle(runtime, obj); } /// ES6.0 21.1.5.2.1 %StringIteratorPrototype%.next ( ) 4-14 CallResult<HermesValue> JSStringIterator::nextElement( Handle<JSStringIterator> self, Runtime &runtime) { // 4. Let s be the value of the [[IteratedString]] internal slot of O. auto s = runtime.makeHandle(self->iteratedString_); if (!s) { // 5. If s is undefined, return CreateIterResultObject(undefined, true). return createIterResultObject(runtime, Runtime::getUndefinedValue(), true) .getHermesValue(); } // 6. Let position be the value of the [[StringIteratorNextIndex]] internal // slot of O. uint32_t position = self->nextIndex_; // 7. Let len be the number of elements in s. uint32_t len = s->getStringLength(); if (position >= len) { // 8a. Set the value of the [[IteratedString]] internal slot of O to // undefined. self->iteratedString_.setNull(&runtime.getHeap()); // 8b. Return CreateIterResultObject(undefined, true). return createIterResultObject(runtime, Runtime::getUndefinedValue(), true) .getHermesValue(); } MutableHandle<StringPrimitive> resultString{runtime}; // 9. Let first be the code unit value at index position in s. char16_t first = s->at(position); if (first < 0xd800 || first > 0xdbff || position + 1 == len) { // 10. If first < 0xD800 or first > 0xDBFF or position+1 = len, // let resultString be the string consisting of the single code unit first. resultString = runtime.getCharacterString(first).get(); } else { // 11a. Let second the code unit value at index position+1 in the String S. char16_t second = s->at(position + 1); if (second < 0xdc00 || second > 0xdfff) { // 11b. If second < 0xDC00 or second > 0xDFFF, let resultString be the // string consisting of the single code unit first. resultString = runtime.getCharacterString(first).get(); } else { // 11c. Let resultString be the string consisting of the code unit first // followed by the code unit second. char16_t charArr[2]{first, second}; auto strRes = StringPrimitive::create(runtime, UTF16Ref{charArr, 2}); if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } resultString = vmcast<StringPrimitive>(*strRes); } } // 13. Set the value of the [[StringIteratorNextIndex]] internal slot of O to // position+resultSize. self->nextIndex_ = position + resultString->getStringLength(); // 14. Return CreateIterResultObject(resultString, false). return createIterResultObject(runtime, resultString, false).getHermesValue(); } //===----------------------------------------------------------------------===// // class JSNumber const ObjectVTable JSNumber::vt{ VTable(CellKind::JSNumberKind, cellSize<JSNumber>()), JSNumber::_getOwnIndexedRangeImpl, JSNumber::_haveOwnIndexedImpl, JSNumber::_getOwnIndexedPropertyFlagsImpl, JSNumber::_getOwnIndexedImpl, JSNumber::_setOwnIndexedImpl, JSNumber::_deleteOwnIndexedImpl, JSNumber::_checkAllOwnIndexedImpl, }; void JSNumberBuildMeta(const GCCell *cell, Metadata::Builder &mb) { mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSNumber>()); JSObjectBuildMeta(cell, mb); mb.setVTable(&JSNumber::vt); } PseudoHandle<JSNumber> JSNumber::create( Runtime &runtime, double value, Handle<JSObject> parentHandle) { auto clazzHandle = runtime.getHiddenClassForPrototype( *parentHandle, numOverlapSlots<JSNumber>()); auto obj = runtime.makeAFixed<JSNumber>(runtime, value, parentHandle, clazzHandle); return JSObjectInit::initToPseudoHandle(runtime, obj); } //===----------------------------------------------------------------------===// // class JSBoolean const ObjectVTable JSBoolean::vt{ VTable(CellKind::JSBooleanKind, cellSize<JSBoolean>()), JSBoolean::_getOwnIndexedRangeImpl, JSBoolean::_haveOwnIndexedImpl, JSBoolean::_getOwnIndexedPropertyFlagsImpl, JSBoolean::_getOwnIndexedImpl, JSBoolean::_setOwnIndexedImpl, JSBoolean::_deleteOwnIndexedImpl, JSBoolean::_checkAllOwnIndexedImpl, }; void JSBooleanBuildMeta(const GCCell *cell, Metadata::Builder &mb) { mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSBoolean>()); JSObjectBuildMeta(cell, mb); mb.setVTable(&JSBoolean::vt); } PseudoHandle<JSBoolean> JSBoolean::create(Runtime &runtime, bool value, Handle<JSObject> parentHandle) { auto clazzHandle = runtime.getHiddenClassForPrototype( *parentHandle, numOverlapSlots<JSBoolean>()); auto obj = runtime.makeAFixed<JSBoolean>(runtime, value, parentHandle, clazzHandle); return JSObjectInit::initToPseudoHandle(runtime, obj); } //===----------------------------------------------------------------------===// // class JSSymbol const ObjectVTable JSSymbol::vt{ VTable(CellKind::JSSymbolKind, cellSize<JSSymbol>()), _getOwnIndexedRangeImpl, _haveOwnIndexedImpl, _getOwnIndexedPropertyFlagsImpl, _getOwnIndexedImpl, _setOwnIndexedImpl, _deleteOwnIndexedImpl, _checkAllOwnIndexedImpl, }; void JSSymbolBuildMeta(const GCCell *cell, Metadata::Builder &mb) { mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSSymbol>()); JSObjectBuildMeta(cell, mb); const auto *self = static_cast<const JSSymbol *>(cell); mb.setVTable(&JSSymbol::vt); mb.addField(&self->primitiveValue_); } PseudoHandle<JSSymbol> JSSymbol::create( Runtime &runtime, SymbolID value, Handle<JSObject> parentHandle) { auto clazzHandle = runtime.getHiddenClassForPrototype( *parentHandle, numOverlapSlots<JSSymbol>()); auto *obj = runtime.makeAFixed<JSSymbol>(runtime, value, parentHandle, clazzHandle); return JSObjectInit::initToPseudoHandle(runtime, obj); } } // namespace vm } // namespace hermes