Coverage Report

Created: 2025-09-04 06:34

/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