/src/hermes/lib/VM/PrimitiveBox.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #include "hermes/VM/PrimitiveBox.h" |
9 | | |
10 | | #include "hermes/VM/BigIntPrimitive.h" |
11 | | #include "hermes/VM/BuildMetadata.h" |
12 | | #include "hermes/VM/Runtime-inline.h" |
13 | | #include "hermes/VM/StringPrimitive.h" |
14 | | |
15 | | namespace hermes { |
16 | | namespace vm { |
17 | | |
18 | | //===----------------------------------------------------------------------===// |
19 | | // class JSString |
20 | | |
21 | | const ObjectVTable JSString::vt{ |
22 | | VTable(CellKind::JSStringKind, cellSize<JSString>()), |
23 | | JSString::_getOwnIndexedRangeImpl, |
24 | | JSString::_haveOwnIndexedImpl, |
25 | | JSString::_getOwnIndexedPropertyFlagsImpl, |
26 | | JSString::_getOwnIndexedImpl, |
27 | | JSString::_setOwnIndexedImpl, |
28 | | JSString::_deleteOwnIndexedImpl, |
29 | | JSString::_checkAllOwnIndexedImpl, |
30 | | }; |
31 | | |
32 | 1 | void JSStringBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
33 | 1 | mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSString>()); |
34 | 1 | JSObjectBuildMeta(cell, mb); |
35 | 1 | const auto *self = static_cast<const JSString *>(cell); |
36 | 1 | mb.setVTable(&JSString::vt); |
37 | 1 | mb.addField(&self->primitiveValue_); |
38 | 1 | } |
39 | | |
40 | | CallResult<Handle<JSString>> JSString::create( |
41 | | Runtime &runtime, |
42 | | Handle<StringPrimitive> value, |
43 | 53 | Handle<JSObject> parentHandle) { |
44 | 53 | auto clazzHandle = runtime.getHiddenClassForPrototype( |
45 | 53 | *parentHandle, numOverlapSlots<JSString>()); |
46 | 53 | auto obj = |
47 | 53 | runtime.makeAFixed<JSString>(runtime, value, parentHandle, clazzHandle); |
48 | | |
49 | 53 | auto selfHandle = JSObjectInit::initToHandle(runtime, obj); |
50 | | |
51 | 53 | PropertyFlags pf; |
52 | 53 | pf.writable = 0; |
53 | 53 | pf.enumerable = 0; |
54 | 53 | pf.configurable = 0; |
55 | | |
56 | 53 | if (LLVM_UNLIKELY( |
57 | 53 | JSObject::defineNewOwnProperty( |
58 | 53 | selfHandle, |
59 | 53 | runtime, |
60 | 53 | Predefined::getSymbolID(Predefined::length), |
61 | 53 | pf, |
62 | 53 | runtime.makeHandle(HermesValue::encodeUntrustedNumberValue( |
63 | 53 | value->getStringLength()))) == ExecutionStatus::EXCEPTION)) { |
64 | 0 | return ExecutionStatus::EXCEPTION; |
65 | 0 | } |
66 | | |
67 | 53 | return selfHandle; |
68 | 53 | } |
69 | | |
70 | | void JSString::setPrimitiveString( |
71 | | Handle<JSString> selfHandle, |
72 | | Runtime &runtime, |
73 | 0 | Handle<StringPrimitive> string) { |
74 | 0 | NamedPropertyDescriptor desc; |
75 | 0 | bool res = JSObject::getOwnNamedDescriptor( |
76 | 0 | selfHandle, runtime, Predefined::getSymbolID(Predefined::length), desc); |
77 | 0 | assert(res && "cannot find 'length' property"); |
78 | 0 | (void)res; |
79 | | |
80 | | // This is definitely not a proxy because we know strings have lengths. |
81 | 0 | auto shv = |
82 | 0 | SmallHermesValue::encodeNumberValue(string->getStringLength(), runtime); |
83 | 0 | JSObject::setNamedSlotValueUnsafe(*selfHandle, runtime, desc, shv); |
84 | 0 | selfHandle->primitiveValue_.set(runtime, *string, runtime.getHeap()); |
85 | 0 | } |
86 | | |
87 | | bool JSString::_haveOwnIndexedImpl( |
88 | | JSObject *self, |
89 | | Runtime &runtime, |
90 | 0 | uint32_t index) { |
91 | 0 | auto *str = getPrimitiveString(vmcast<JSString>(self), runtime); |
92 | 0 | return index < str->getStringLength(); |
93 | 0 | } |
94 | | |
95 | | OptValue<PropertyFlags> JSString::_getOwnIndexedPropertyFlagsImpl( |
96 | | JSObject *self, |
97 | | Runtime &runtime, |
98 | 914k | uint32_t index) { |
99 | 914k | auto *str = getPrimitiveString(vmcast<JSString>(self), runtime); |
100 | 914k | if (index < str->getStringLength()) { |
101 | 914k | PropertyFlags flags; |
102 | 914k | flags.enumerable = 1; |
103 | 914k | return flags; |
104 | 914k | } |
105 | | |
106 | 0 | return llvh::None; |
107 | 914k | } |
108 | | |
109 | | std::pair<uint32_t, uint32_t> JSString::_getOwnIndexedRangeImpl( |
110 | | JSObject *selfObj, |
111 | 8 | Runtime &runtime) { |
112 | 8 | auto *str = getPrimitiveString(vmcast<JSString>(selfObj), runtime); |
113 | 8 | return {0, str->getStringLength()}; |
114 | 8 | } |
115 | | |
116 | | HermesValue JSString::_getOwnIndexedImpl( |
117 | | PseudoHandle<JSObject> self, |
118 | | Runtime &runtime, |
119 | 0 | uint32_t index) { |
120 | 0 | auto *str = getPrimitiveString(vmcast<JSString>(self.get()), runtime); |
121 | |
|
122 | 0 | NoAllocScope noAllocs{runtime}; |
123 | |
|
124 | 0 | if (LLVM_LIKELY(index < str->getStringLength())) { |
125 | 0 | auto chr = str->at(index); |
126 | 0 | noAllocs.release(); |
127 | 0 | return runtime.getCharacterString(chr).getHermesValue(); |
128 | 0 | } |
129 | | |
130 | 0 | return HermesValue::encodeEmptyValue(); |
131 | 0 | } |
132 | | |
133 | | CallResult<bool> JSString::_setOwnIndexedImpl( |
134 | | Handle<JSObject> selfHandle, |
135 | | Runtime &runtime, |
136 | | uint32_t index, |
137 | 0 | Handle<> valueHandle) { |
138 | 0 | auto *str = getPrimitiveString(vmcast<JSString>(selfHandle.get()), runtime); |
139 | |
|
140 | 0 | if (index < str->getStringLength()) |
141 | 0 | return false; |
142 | | |
143 | | // Property indexes beyond the end of the string must be added as named |
144 | | // properties. |
145 | 0 | auto vr = valueToSymbolID( |
146 | 0 | runtime, |
147 | 0 | runtime.makeHandle(HermesValue::encodeUntrustedNumberValue(index))); |
148 | 0 | assert( |
149 | 0 | vr != ExecutionStatus::EXCEPTION && |
150 | 0 | "valueToIdentifier() failed for uint32_t value"); |
151 | | |
152 | | // Can't call defineOwnComputedPrimitive because it would infinitely recurse |
153 | | // calling JSString::_setOwnIndexedImpl. |
154 | 0 | return JSObject::defineOwnPropertyInternal( |
155 | 0 | selfHandle, |
156 | 0 | runtime, |
157 | 0 | **vr, |
158 | 0 | DefinePropertyFlags::getDefaultNewPropertyFlags(), |
159 | 0 | valueHandle); |
160 | 0 | } |
161 | | |
162 | | bool JSString::_deleteOwnIndexedImpl( |
163 | | Handle<JSObject> selfHandle, |
164 | | Runtime &runtime, |
165 | 0 | uint32_t index) { |
166 | 0 | auto *str = getPrimitiveString(vmcast<JSString>(selfHandle.get()), runtime); |
167 | | |
168 | | // Only characters past the end of the string can be deleted (since they |
169 | | // already are). |
170 | 0 | return index >= str->getStringLength(); |
171 | 0 | } |
172 | | |
173 | | //===----------------------------------------------------------------------===// |
174 | | // class JSStringIterator |
175 | | |
176 | | const ObjectVTable JSStringIterator::vt{ |
177 | | VTable(CellKind::JSStringIteratorKind, cellSize<JSStringIterator>()), |
178 | | JSStringIterator::_getOwnIndexedRangeImpl, |
179 | | JSStringIterator::_haveOwnIndexedImpl, |
180 | | JSStringIterator::_getOwnIndexedPropertyFlagsImpl, |
181 | | JSStringIterator::_getOwnIndexedImpl, |
182 | | JSStringIterator::_setOwnIndexedImpl, |
183 | | JSStringIterator::_deleteOwnIndexedImpl, |
184 | | JSStringIterator::_checkAllOwnIndexedImpl, |
185 | | }; |
186 | | |
187 | 1 | void JSStringIteratorBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
188 | 1 | mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSStringIterator>()); |
189 | 1 | JSObjectBuildMeta(cell, mb); |
190 | 1 | const auto *self = static_cast<const JSStringIterator *>(cell); |
191 | 1 | mb.setVTable(&JSStringIterator::vt); |
192 | 1 | mb.addField("iteratedString", &self->iteratedString_); |
193 | 1 | } |
194 | | |
195 | | /// ES6.0 21.1.5.1 CreateStringIterator Abstract Operation |
196 | | PseudoHandle<JSStringIterator> JSStringIterator::create( |
197 | | Runtime &runtime, |
198 | 2 | Handle<StringPrimitive> string) { |
199 | 2 | auto proto = Handle<JSObject>::vmcast(&runtime.stringIteratorPrototype); |
200 | 2 | auto clazzHandle = runtime.getHiddenClassForPrototype( |
201 | 2 | *proto, numOverlapSlots<JSStringIterator>()); |
202 | 2 | auto obj = |
203 | 2 | runtime.makeAFixed<JSStringIterator>(runtime, proto, clazzHandle, string); |
204 | 2 | return JSObjectInit::initToPseudoHandle(runtime, obj); |
205 | 2 | } |
206 | | |
207 | | /// ES6.0 21.1.5.2.1 %StringIteratorPrototype%.next ( ) 4-14 |
208 | | CallResult<HermesValue> JSStringIterator::nextElement( |
209 | | Handle<JSStringIterator> self, |
210 | 397k | Runtime &runtime) { |
211 | | // 4. Let s be the value of the [[IteratedString]] internal slot of O. |
212 | 397k | auto s = runtime.makeHandle(self->iteratedString_); |
213 | 397k | if (!s) { |
214 | | // 5. If s is undefined, return CreateIterResultObject(undefined, true). |
215 | 0 | return createIterResultObject(runtime, Runtime::getUndefinedValue(), true) |
216 | 0 | .getHermesValue(); |
217 | 0 | } |
218 | | |
219 | | // 6. Let position be the value of the [[StringIteratorNextIndex]] internal |
220 | | // slot of O. |
221 | 397k | uint32_t position = self->nextIndex_; |
222 | | // 7. Let len be the number of elements in s. |
223 | 397k | uint32_t len = s->getStringLength(); |
224 | | |
225 | 397k | if (position >= len) { |
226 | | // 8a. Set the value of the [[IteratedString]] internal slot of O to |
227 | | // undefined. |
228 | 2 | self->iteratedString_.setNull(runtime.getHeap()); |
229 | | // 8b. Return CreateIterResultObject(undefined, true). |
230 | 2 | return createIterResultObject(runtime, Runtime::getUndefinedValue(), true) |
231 | 2 | .getHermesValue(); |
232 | 2 | } |
233 | | |
234 | 397k | MutableHandle<StringPrimitive> resultString{runtime}; |
235 | | |
236 | | // 9. Let first be the code unit value at index position in s. |
237 | 397k | char16_t first = s->at(position); |
238 | 397k | if (first < 0xd800 || first > 0xdbff || position + 1 == len) { |
239 | | // 10. If first < 0xD800 or first > 0xDBFF or position+1 = len, |
240 | | // let resultString be the string consisting of the single code unit first. |
241 | 397k | resultString = runtime.getCharacterString(first).get(); |
242 | 397k | } else { |
243 | | // 11a. Let second the code unit value at index position+1 in the String S. |
244 | 0 | char16_t second = s->at(position + 1); |
245 | 0 | if (second < 0xdc00 || second > 0xdfff) { |
246 | | // 11b. If second < 0xDC00 or second > 0xDFFF, let resultString be the |
247 | | // string consisting of the single code unit first. |
248 | 0 | resultString = runtime.getCharacterString(first).get(); |
249 | 0 | } else { |
250 | | // 11c. Let resultString be the string consisting of the code unit first |
251 | | // followed by the code unit second. |
252 | 0 | char16_t charArr[2]{first, second}; |
253 | 0 | auto strRes = StringPrimitive::create(runtime, UTF16Ref{charArr, 2}); |
254 | 0 | if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) { |
255 | 0 | return ExecutionStatus::EXCEPTION; |
256 | 0 | } |
257 | 0 | resultString = vmcast<StringPrimitive>(*strRes); |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | // 13. Set the value of the [[StringIteratorNextIndex]] internal slot of O to |
262 | | // position+resultSize. |
263 | 397k | self->nextIndex_ = position + resultString->getStringLength(); |
264 | | |
265 | | // 14. Return CreateIterResultObject(resultString, false). |
266 | 397k | return createIterResultObject(runtime, resultString, false).getHermesValue(); |
267 | 397k | } |
268 | | |
269 | | //===----------------------------------------------------------------------===// |
270 | | // class JSBigInt |
271 | | |
272 | | const ObjectVTable JSBigInt::vt{ |
273 | | VTable(CellKind::JSBigIntKind, cellSize<JSBigInt>()), |
274 | | JSBigInt::_getOwnIndexedRangeImpl, |
275 | | JSBigInt::_haveOwnIndexedImpl, |
276 | | JSBigInt::_getOwnIndexedPropertyFlagsImpl, |
277 | | JSBigInt::_getOwnIndexedImpl, |
278 | | JSBigInt::_setOwnIndexedImpl, |
279 | | JSBigInt::_deleteOwnIndexedImpl, |
280 | | JSBigInt::_checkAllOwnIndexedImpl, |
281 | | }; |
282 | | |
283 | 1 | void JSBigIntBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
284 | 1 | mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSBigInt>()); |
285 | 1 | JSObjectBuildMeta(cell, mb); |
286 | 1 | const auto *self = static_cast<const JSBigInt *>(cell); |
287 | 1 | mb.setVTable(&JSBigInt::vt); |
288 | 1 | mb.addField(&self->primitiveValue_); |
289 | 1 | } |
290 | | |
291 | | Handle<JSBigInt> JSBigInt::create( |
292 | | Runtime &runtime, |
293 | | Handle<BigIntPrimitive> value, |
294 | 0 | Handle<JSObject> parentHandle) { |
295 | 0 | auto clazzHandle = runtime.getHiddenClassForPrototype( |
296 | 0 | *parentHandle, numOverlapSlots<JSBigInt>()); |
297 | 0 | auto obj = |
298 | 0 | runtime.makeAFixed<JSBigInt>(runtime, value, parentHandle, clazzHandle); |
299 | |
|
300 | 0 | return JSObjectInit::initToHandle(runtime, obj); |
301 | 0 | } |
302 | | |
303 | | //===----------------------------------------------------------------------===// |
304 | | // class JSNumber |
305 | | |
306 | | const ObjectVTable JSNumber::vt{ |
307 | | VTable(CellKind::JSNumberKind, cellSize<JSNumber>()), |
308 | | JSNumber::_getOwnIndexedRangeImpl, |
309 | | JSNumber::_haveOwnIndexedImpl, |
310 | | JSNumber::_getOwnIndexedPropertyFlagsImpl, |
311 | | JSNumber::_getOwnIndexedImpl, |
312 | | JSNumber::_setOwnIndexedImpl, |
313 | | JSNumber::_deleteOwnIndexedImpl, |
314 | | JSNumber::_checkAllOwnIndexedImpl, |
315 | | }; |
316 | | |
317 | 1 | void JSNumberBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
318 | 1 | mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSNumber>()); |
319 | 1 | JSObjectBuildMeta(cell, mb); |
320 | 1 | mb.setVTable(&JSNumber::vt); |
321 | 1 | } |
322 | | |
323 | | PseudoHandle<JSNumber> JSNumber::create( |
324 | | Runtime &runtime, |
325 | | double value, |
326 | 47 | Handle<JSObject> parentHandle) { |
327 | 47 | auto clazzHandle = runtime.getHiddenClassForPrototype( |
328 | 47 | *parentHandle, numOverlapSlots<JSNumber>()); |
329 | 47 | auto obj = |
330 | 47 | runtime.makeAFixed<JSNumber>(runtime, value, parentHandle, clazzHandle); |
331 | 47 | return JSObjectInit::initToPseudoHandle(runtime, obj); |
332 | 47 | } |
333 | | |
334 | | //===----------------------------------------------------------------------===// |
335 | | // class JSBoolean |
336 | | |
337 | | const ObjectVTable JSBoolean::vt{ |
338 | | VTable(CellKind::JSBooleanKind, cellSize<JSBoolean>()), |
339 | | JSBoolean::_getOwnIndexedRangeImpl, |
340 | | JSBoolean::_haveOwnIndexedImpl, |
341 | | JSBoolean::_getOwnIndexedPropertyFlagsImpl, |
342 | | JSBoolean::_getOwnIndexedImpl, |
343 | | JSBoolean::_setOwnIndexedImpl, |
344 | | JSBoolean::_deleteOwnIndexedImpl, |
345 | | JSBoolean::_checkAllOwnIndexedImpl, |
346 | | }; |
347 | | |
348 | 1 | void JSBooleanBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
349 | 1 | mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSBoolean>()); |
350 | 1 | JSObjectBuildMeta(cell, mb); |
351 | 1 | mb.setVTable(&JSBoolean::vt); |
352 | 1 | } |
353 | | |
354 | | PseudoHandle<JSBoolean> |
355 | 47 | JSBoolean::create(Runtime &runtime, bool value, Handle<JSObject> parentHandle) { |
356 | 47 | auto clazzHandle = runtime.getHiddenClassForPrototype( |
357 | 47 | *parentHandle, numOverlapSlots<JSBoolean>()); |
358 | 47 | auto obj = |
359 | 47 | runtime.makeAFixed<JSBoolean>(runtime, value, parentHandle, clazzHandle); |
360 | 47 | return JSObjectInit::initToPseudoHandle(runtime, obj); |
361 | 47 | } |
362 | | |
363 | | //===----------------------------------------------------------------------===// |
364 | | // class JSSymbol |
365 | | |
366 | | const ObjectVTable JSSymbol::vt{ |
367 | | VTable(CellKind::JSSymbolKind, cellSize<JSSymbol>()), |
368 | | _getOwnIndexedRangeImpl, |
369 | | _haveOwnIndexedImpl, |
370 | | _getOwnIndexedPropertyFlagsImpl, |
371 | | _getOwnIndexedImpl, |
372 | | _setOwnIndexedImpl, |
373 | | _deleteOwnIndexedImpl, |
374 | | _checkAllOwnIndexedImpl, |
375 | | }; |
376 | | |
377 | 1 | void JSSymbolBuildMeta(const GCCell *cell, Metadata::Builder &mb) { |
378 | 1 | mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSSymbol>()); |
379 | 1 | JSObjectBuildMeta(cell, mb); |
380 | 1 | const auto *self = static_cast<const JSSymbol *>(cell); |
381 | 1 | mb.setVTable(&JSSymbol::vt); |
382 | 1 | mb.addField(&self->primitiveValue_); |
383 | 1 | } |
384 | | |
385 | | PseudoHandle<JSSymbol> JSSymbol::create( |
386 | | Runtime &runtime, |
387 | | SymbolID value, |
388 | 0 | Handle<JSObject> parentHandle) { |
389 | 0 | auto clazzHandle = runtime.getHiddenClassForPrototype( |
390 | 0 | *parentHandle, numOverlapSlots<JSSymbol>()); |
391 | 0 | auto *obj = |
392 | 0 | runtime.makeAFixed<JSSymbol>(runtime, value, parentHandle, clazzHandle); |
393 | 0 | return JSObjectInit::initToPseudoHandle(runtime, obj); |
394 | 0 | } |
395 | | |
396 | | } // namespace vm |
397 | | } // namespace hermes |