Coverage Report

Created: 2025-12-11 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/lib/VM/JSLib/HermesInternal.cpp
Line
Count
Source
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 "JSLibInternal.h"
9
10
#include "hermes/BCGen/HBC/BytecodeFileFormat.h"
11
#include "hermes/Support/Base64vlq.h"
12
#include "hermes/Support/OSCompat.h"
13
#include "hermes/VM/Callable.h"
14
#include "hermes/VM/JSArray.h"
15
#include "hermes/VM/JSArrayBuffer.h"
16
#include "hermes/VM/JSLib.h"
17
#include "hermes/VM/JSTypedArray.h"
18
#include "hermes/VM/JSWeakMapImpl.h"
19
#include "hermes/VM/Operations.h"
20
#include "hermes/VM/StackFrame-inline.h"
21
#include "hermes/VM/StringView.h"
22
23
#include <cstring>
24
#include <random>
25
#pragma GCC diagnostic push
26
27
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
28
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
29
#endif
30
namespace hermes {
31
namespace vm {
32
33
/// \return a SymbolID  for a given C string \p s.
34
static inline CallResult<Handle<SymbolID>> symbolForCStr(
35
    Runtime &rt,
36
0
    const char *s) {
37
0
  return rt.getIdentifierTable().getSymbolHandle(rt, ASCIIRef{s, strlen(s)});
38
0
}
39
40
// ES7 24.1.1.3
41
CallResult<HermesValue>
42
0
hermesInternalDetachArrayBuffer(void *, Runtime &runtime, NativeArgs args) {
43
0
  auto buffer = args.dyncastArg<JSArrayBuffer>(0);
44
0
  if (!buffer) {
45
0
    return runtime.raiseTypeError(
46
0
        "Cannot use detachArrayBuffer on something which "
47
0
        "is not an ArrayBuffer foo");
48
0
  }
49
0
  if (LLVM_UNLIKELY(
50
0
          JSArrayBuffer::detach(runtime, buffer) == ExecutionStatus::EXCEPTION))
51
0
    return ExecutionStatus::EXCEPTION;
52
  // "void" return
53
0
  return HermesValue::encodeUndefinedValue();
54
0
}
55
56
CallResult<HermesValue>
57
0
hermesInternalGetEpilogues(void *, Runtime &runtime, NativeArgs args) {
58
  // Create outer array with one element per module.
59
0
  auto eps = runtime.getEpilogues();
60
0
  auto outerLen = eps.size();
61
0
  auto outerResult = JSArray::create(runtime, outerLen, outerLen);
62
63
0
  if (outerResult == ExecutionStatus::EXCEPTION) {
64
0
    return ExecutionStatus::EXCEPTION;
65
0
  }
66
0
  auto outer = *outerResult;
67
0
  if (outer->setStorageEndIndex(outer, runtime, outerLen) ==
68
0
      ExecutionStatus::EXCEPTION) {
69
0
    return ExecutionStatus::EXCEPTION;
70
0
  }
71
  // Set each element to a Uint8Array holding the epilogue for that module.
72
0
  for (unsigned i = 0; i < outerLen; ++i) {
73
0
    auto innerLen = eps[i].size();
74
0
    if (innerLen != 0) {
75
0
      auto result = Uint8Array::allocate(runtime, innerLen);
76
0
      if (result == ExecutionStatus::EXCEPTION) {
77
0
        return ExecutionStatus::EXCEPTION;
78
0
      }
79
0
      auto ta = result.getValue();
80
0
      std::memcpy(ta->begin(runtime), eps[i].begin(), innerLen);
81
0
      const auto shv = SmallHermesValue::encodeObjectValue(*ta, runtime);
82
0
      JSArray::unsafeSetExistingElementAt(*outer, runtime, i, shv);
83
0
    }
84
0
  }
85
0
  return HermesValue::encodeObjectValue(*outer);
86
0
}
87
88
/// Used for testing, determines how many live values
89
/// are in the given WeakMap or WeakSet.
90
CallResult<HermesValue>
91
0
hermesInternalGetWeakSize(void *, Runtime &runtime, NativeArgs args) {
92
0
  if (auto M = args.dyncastArg<JSWeakMap>(0)) {
93
0
    return HermesValue::encodeUntrustedNumberValue(
94
0
        JSWeakMap::debugFreeSlotsAndGetSize(runtime, *M));
95
0
  }
96
97
0
  if (auto S = args.dyncastArg<JSWeakSet>(0)) {
98
0
    return HermesValue::encodeUntrustedNumberValue(
99
0
        JSWeakSet::debugFreeSlotsAndGetSize(runtime, *S));
100
0
  }
101
102
0
  return runtime.raiseTypeError(
103
0
      "getWeakSize can only be called on a WeakMap/WeakSet");
104
0
}
105
106
/// \return an object containing various instrumented statistics.
107
CallResult<HermesValue>
108
0
hermesInternalGetInstrumentedStats(void *, Runtime &runtime, NativeArgs args) {
109
0
  GCScope gcScope(runtime);
110
0
  auto resultHandle = runtime.makeHandle(JSObject::create(runtime));
111
0
  MutableHandle<> valHandle{runtime};
112
113
  /// Adds \p key with \p val to the resultHandle object.
114
0
  auto addToResultHandle =
115
0
      [&gcScope, &runtime](
116
0
          Handle<JSObject> obj, llvh::StringRef key, Handle<> val) {
117
0
        GCScopeMarkerRAII marker{gcScope};
118
0
        auto keySym = symbolForCStr(runtime, key.data());
119
0
        if (LLVM_UNLIKELY(keySym == ExecutionStatus::EXCEPTION)) {
120
0
          return ExecutionStatus::EXCEPTION;
121
0
        }
122
123
0
        return JSObject::defineNewOwnProperty(
124
0
            obj,
125
0
            runtime,
126
0
            **keySym,
127
0
            PropertyFlags::defaultNewNamedPropertyFlags(),
128
0
            val);
129
0
      };
130
131
  /// Adds a property to resultHandle. \p key provides its name, and \p val,
132
  /// its value.
133
0
#define ADD_PROP(obj, name, value)                            \
134
0
  valHandle = HermesValue::encodeUntrustedNumberValue(value); \
135
0
  if (LLVM_UNLIKELY(                                          \
136
0
          addToResultHandle(obj, name, valHandle) ==          \
137
0
          ExecutionStatus::EXCEPTION)) {                      \
138
0
    return ExecutionStatus::EXCEPTION;                        \
139
0
  }
140
141
0
  auto &heap = runtime.getHeap();
142
0
  GCBase::HeapInfo info;
143
0
  heap.getHeapInfo(info);
144
145
0
  ADD_PROP(resultHandle, "js_VMExperiments", runtime.getVMExperimentFlags());
146
0
  ADD_PROP(resultHandle, "js_numGCs", heap.getNumGCs());
147
0
  ADD_PROP(resultHandle, "js_gcCPUTime", heap.getGCCPUTime());
148
0
  ADD_PROP(
149
0
      resultHandle, "js_avgGCCPUTime", info.generalStats.gcCPUTime.average());
150
0
  ADD_PROP(resultHandle, "js_maxGCCPUTime", info.generalStats.gcCPUTime.max());
151
0
  ADD_PROP(resultHandle, "js_gcTime", heap.getGCTime());
152
0
  ADD_PROP(
153
0
      resultHandle, "js_avgGCTime", info.generalStats.gcWallTime.average());
154
0
  ADD_PROP(resultHandle, "js_maxGCTime", info.generalStats.gcWallTime.max());
155
0
  ADD_PROP(resultHandle, "js_totalAllocatedBytes", info.totalAllocatedBytes);
156
0
  ADD_PROP(resultHandle, "js_allocatedBytes", info.allocatedBytes);
157
0
  ADD_PROP(resultHandle, "js_heapSize", info.heapSize);
158
0
  ADD_PROP(resultHandle, "js_mallocSizeEstimate", info.mallocSizeEstimate);
159
0
  ADD_PROP(resultHandle, "js_vaSize", info.va);
160
0
  ADD_PROP(resultHandle, "js_externalBytes", info.externalBytes);
161
0
  ADD_PROP(
162
0
      resultHandle,
163
0
      "js_peakAllocatedBytes",
164
0
      info.generalStats.usedBefore.max());
165
0
  ADD_PROP(
166
0
      resultHandle, "js_peakLiveAfterGC", info.generalStats.usedAfter.max());
167
168
0
#ifdef HERMESVM_GC_HADES
169
0
  Handle<JSObject> specificStatsHandle =
170
0
      runtime.makeHandle(JSObject::create(runtime));
171
0
  auto res =
172
0
      addToResultHandle(resultHandle, "js_gcSpecific", specificStatsHandle);
173
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
174
0
    return ExecutionStatus::EXCEPTION;
175
0
  }
176
177
0
  ADD_PROP(
178
0
      specificStatsHandle,
179
0
      "js_numYGCollections",
180
0
      info.youngGenStats.numCollections);
181
0
  ADD_PROP(
182
0
      specificStatsHandle,
183
0
      "js_numOGCollections",
184
0
      info.fullStats.numCollections);
185
0
  ADD_PROP(specificStatsHandle, "js_numCompactions", info.numCompactions);
186
0
#endif
187
0
#undef ADD_PROP
188
189
0
  return resultHandle.getHermesValue();
190
0
}
191
192
/// \return a static string summarising the presence and resolution type of
193
/// CommonJS modules across all RuntimeModules that have been loaded into \c
194
/// runtime.
195
0
static const char *getCJSModuleModeDescription(Runtime &runtime) {
196
0
  bool hasCJSModulesDynamic = false;
197
0
  bool hasCJSModulesStatic = false;
198
0
  for (const auto &runtimeModule : runtime.getRuntimeModules()) {
199
0
    if (runtimeModule.hasCJSModules()) {
200
0
      hasCJSModulesDynamic = true;
201
0
    }
202
0
    if (runtimeModule.hasCJSModulesStatic()) {
203
0
      hasCJSModulesStatic = true;
204
0
    }
205
0
  }
206
0
  if (hasCJSModulesDynamic && hasCJSModulesStatic) {
207
0
    return "Mixed dynamic/static";
208
0
  }
209
0
  if (hasCJSModulesDynamic) {
210
0
    return "Dynamically resolved";
211
0
  }
212
0
  if (hasCJSModulesStatic) {
213
0
    return "Statically resolved";
214
0
  }
215
0
  return "None";
216
0
}
217
218
/// \return an object mapping keys to runtime property values.
219
CallResult<HermesValue>
220
0
hermesInternalGetRuntimeProperties(void *, Runtime &runtime, NativeArgs args) {
221
0
  GCScope gcScope(runtime);
222
0
  auto resultHandle = runtime.makeHandle(JSObject::create(runtime));
223
0
  MutableHandle<> tmpHandle{runtime};
224
225
  /// Add a property \p value keyed under \p key to resultHandle.
226
  /// Return an ExecutionStatus.
227
0
  auto addProperty = [&](Handle<> value, const char *key) {
228
0
    auto keySym = symbolForCStr(runtime, key);
229
0
    if (LLVM_UNLIKELY(keySym == ExecutionStatus::EXCEPTION)) {
230
0
      return ExecutionStatus::EXCEPTION;
231
0
    }
232
0
    return JSObject::defineNewOwnProperty(
233
0
        resultHandle,
234
0
        runtime,
235
0
        **keySym,
236
0
        PropertyFlags::defaultNewNamedPropertyFlags(),
237
0
        value);
238
0
  };
239
240
#ifdef HERMES_FACEBOOK_BUILD
241
  tmpHandle =
242
      HermesValue::encodeBoolValue(std::strstr(__FILE__, "hermes-snapshot"));
243
  if (LLVM_UNLIKELY(
244
          addProperty(tmpHandle, "Snapshot VM") ==
245
          ExecutionStatus::EXCEPTION)) {
246
    return ExecutionStatus::EXCEPTION;
247
  }
248
#endif
249
250
0
  tmpHandle =
251
0
      HermesValue::encodeUntrustedNumberValue(::hermes::hbc::BYTECODE_VERSION);
252
0
  if (LLVM_UNLIKELY(
253
0
          addProperty(tmpHandle, "Bytecode Version") ==
254
0
          ExecutionStatus::EXCEPTION)) {
255
0
    return ExecutionStatus::EXCEPTION;
256
0
  }
257
258
0
  tmpHandle = HermesValue::encodeBoolValue(runtime.builtinsAreFrozen());
259
0
  if (LLVM_UNLIKELY(
260
0
          addProperty(tmpHandle, "Builtins Frozen") ==
261
0
          ExecutionStatus::EXCEPTION)) {
262
0
    return ExecutionStatus::EXCEPTION;
263
0
  }
264
265
0
  tmpHandle =
266
0
      HermesValue::encodeUntrustedNumberValue(runtime.getVMExperimentFlags());
267
0
  if (LLVM_UNLIKELY(
268
0
          addProperty(tmpHandle, "VM Experiments") ==
269
0
          ExecutionStatus::EXCEPTION)) {
270
0
    return ExecutionStatus::EXCEPTION;
271
0
  }
272
273
0
  const char buildMode[] =
274
0
#ifdef HERMES_SLOW_DEBUG
275
0
      "SlowDebug"
276
#elif !defined(NDEBUG)
277
      "Debug"
278
#else
279
      "Release"
280
#endif
281
0
      ;
282
0
  auto buildModeRes = StringPrimitive::create(
283
0
      runtime, ASCIIRef(buildMode, sizeof(buildMode) - 1));
284
0
  if (LLVM_UNLIKELY(buildModeRes == ExecutionStatus::EXCEPTION)) {
285
0
    return ExecutionStatus::EXCEPTION;
286
0
  }
287
0
  tmpHandle = *buildModeRes;
288
0
  if (LLVM_UNLIKELY(
289
0
          addProperty(tmpHandle, "Build") == ExecutionStatus::EXCEPTION)) {
290
0
    return ExecutionStatus::EXCEPTION;
291
0
  }
292
293
0
  std::string gcKind = runtime.getHeap().getKindAsStr();
294
0
  auto gcKindRes = StringPrimitive::create(
295
0
      runtime, ASCIIRef(gcKind.c_str(), gcKind.length()));
296
0
  if (LLVM_UNLIKELY(gcKindRes == ExecutionStatus::EXCEPTION)) {
297
0
    return ExecutionStatus::EXCEPTION;
298
0
  }
299
0
  tmpHandle = *gcKindRes;
300
0
  if (LLVM_UNLIKELY(
301
0
          addProperty(tmpHandle, "GC") == ExecutionStatus::EXCEPTION)) {
302
0
    return ExecutionStatus::EXCEPTION;
303
0
  }
304
305
0
#ifdef HERMES_RELEASE_VERSION
306
0
  auto relVerRes =
307
0
      StringPrimitive::create(runtime, createASCIIRef(HERMES_RELEASE_VERSION));
308
0
  if (LLVM_UNLIKELY(relVerRes == ExecutionStatus::EXCEPTION)) {
309
0
    return ExecutionStatus::EXCEPTION;
310
0
  }
311
0
  tmpHandle = *relVerRes;
312
0
  if (LLVM_UNLIKELY(
313
0
          addProperty(tmpHandle, "OSS Release Version") ==
314
0
          ExecutionStatus::EXCEPTION)) {
315
0
    return ExecutionStatus::EXCEPTION;
316
0
  }
317
0
#endif
318
319
0
  const bool debuggerEnabled =
320
0
#ifdef HERMES_ENABLE_DEBUGGER
321
0
      true
322
#else
323
      false
324
#endif
325
0
      ;
326
327
0
  tmpHandle = HermesValue::encodeBoolValue(debuggerEnabled);
328
0
  if (LLVM_UNLIKELY(
329
0
          addProperty(tmpHandle, "Debugger Enabled") ==
330
0
          ExecutionStatus::EXCEPTION)) {
331
0
    return ExecutionStatus::EXCEPTION;
332
0
  }
333
334
0
  const char *cjsModuleMode = getCJSModuleModeDescription(runtime);
335
0
  auto cjsModuleModeRes =
336
0
      StringPrimitive::create(runtime, createASCIIRef(cjsModuleMode));
337
0
  if (LLVM_UNLIKELY(cjsModuleModeRes == ExecutionStatus::EXCEPTION)) {
338
0
    return ExecutionStatus::EXCEPTION;
339
0
  }
340
0
  tmpHandle = *cjsModuleModeRes;
341
0
  if (LLVM_UNLIKELY(
342
0
          addProperty(tmpHandle, "CommonJS Modules") ==
343
0
          ExecutionStatus::EXCEPTION)) {
344
0
    return ExecutionStatus::EXCEPTION;
345
0
  }
346
347
0
  return resultHandle.getHermesValue();
348
0
}
349
350
#ifdef HERMESVM_PLATFORM_LOGGING
351
static void logGCStats(Runtime &runtime, const char *msg) {
352
  // The GC stats can exceed the android logcat length limit, of
353
  // 1024 bytes.  Break it up.
354
  std::string stats;
355
  {
356
    llvh::raw_string_ostream os(stats);
357
    runtime.printHeapStats(os);
358
  }
359
  auto copyRegionFrom = [&stats](size_t from) -> size_t {
360
    size_t rBrace = stats.find("},", from);
361
    if (rBrace == std::string::npos) {
362
      std::string portion = stats.substr(from);
363
      hermesLog("HermesVM", "%s", portion.c_str());
364
      return stats.size();
365
    }
366
367
    // Add 2 for the length of the search string, to get to the end.
368
    const size_t to = rBrace + 2;
369
    std::string portion = stats.substr(from, to - from);
370
    hermesLog("HermesVM", "%s", portion.c_str());
371
    return to;
372
  };
373
374
  hermesLog("HermesVM", "%s:", msg);
375
  for (size_t ind = 0; ind < stats.size(); ind = copyRegionFrom(ind))
376
    ;
377
}
378
#endif
379
380
CallResult<HermesValue>
381
0
hermesInternalTTIReached(void *, Runtime &runtime, NativeArgs args) {
382
0
  runtime.ttiReached();
383
#ifdef HERMESVM_LLVM_PROFILE_DUMP
384
  __llvm_profile_dump();
385
  throw jsi::JSINativeException("TTI reached; profiling done");
386
#endif
387
#ifdef HERMESVM_PLATFORM_LOGGING
388
  logGCStats(runtime, "TTI call");
389
#endif
390
0
  return HermesValue::encodeUndefinedValue();
391
0
}
392
393
CallResult<HermesValue>
394
0
hermesInternalTTRCReached(void *, Runtime &runtime, NativeArgs args) {
395
  // Currently does nothing, but could change in the future.
396
0
  return HermesValue::encodeUndefinedValue();
397
0
}
398
399
CallResult<HermesValue>
400
0
hermesInternalIsProxy(void *, Runtime &runtime, NativeArgs args) {
401
0
  Handle<JSObject> obj = args.dyncastArg<JSObject>(0);
402
0
  return HermesValue::encodeBoolValue(obj && obj->isProxyObject());
403
0
}
404
405
CallResult<HermesValue>
406
188
hermesInternalHasPromise(void *, Runtime &runtime, NativeArgs args) {
407
188
  return HermesValue::encodeBoolValue(runtime.hasES6Promise());
408
188
}
409
410
CallResult<HermesValue>
411
94
hermesInternalHasES6Class(void *, Runtime &runtime, NativeArgs args) {
412
94
  return HermesValue::encodeBoolValue(runtime.hasES6Class());
413
94
}
414
415
CallResult<HermesValue>
416
94
hermesInternalUseEngineQueue(void *, Runtime &runtime, NativeArgs args) {
417
94
  return HermesValue::encodeBoolValue(runtime.hasMicrotaskQueue());
418
94
}
419
420
/// \code
421
///   HermesInternal.enqueueJob = function (func) {}
422
/// \endcode
423
CallResult<HermesValue>
424
0
hermesInternalEnqueueJob(void *, Runtime &runtime, NativeArgs args) {
425
0
  auto callable = args.dyncastArg<Callable>(0);
426
0
  if (!callable) {
427
0
    return runtime.raiseTypeError(
428
0
        "Argument to HermesInternal.enqueueJob must be callable");
429
0
  }
430
0
  runtime.enqueueJob(callable.get());
431
0
  return HermesValue::encodeUndefinedValue();
432
0
}
433
434
/// \code
435
///   HermesInternal.drainJobs = function () {}
436
/// \endcode
437
/// Throw if the drainJobs throws.
438
CallResult<HermesValue>
439
0
hermesInternalDrainJobs(void *, Runtime &runtime, NativeArgs args) {
440
0
  auto drainRes = runtime.drainJobs();
441
0
  if (drainRes == ExecutionStatus::EXCEPTION) {
442
    // No need to rethrow since it's already throw.
443
0
    return ExecutionStatus::EXCEPTION;
444
0
  }
445
0
  return HermesValue::encodeUndefinedValue();
446
0
}
447
448
#ifdef HERMESVM_EXCEPTION_ON_OOM
449
/// Gets the current call stack as a JS String value.  Intended (only)
450
/// to allow testing of Runtime::callStack() from JS code.
451
CallResult<HermesValue>
452
hermesInternalGetCallStack(void *, Runtime &runtime, NativeArgs args) {
453
  std::string stack = runtime.getCallStackNoAlloc();
454
  return StringPrimitive::create(runtime, ASCIIRef(stack.data(), stack.size()));
455
}
456
#endif // HERMESVM_EXCEPTION_ON_OOM
457
458
/// \return the code block associated with \p callableHandle if it is a
459
/// (possibly bound) JS function, or nullptr otherwise.
460
static const CodeBlock *getLeafCodeBlock(
461
    Handle<Callable> callableHandle,
462
0
    Runtime &runtime) {
463
0
  const Callable *callable = callableHandle.get();
464
0
  while (auto *bound = dyn_vmcast<BoundFunction>(callable)) {
465
0
    callable = bound->getTarget(runtime);
466
0
  }
467
0
  if (auto *asFunction = dyn_vmcast<const JSFunction>(callable)) {
468
0
    return asFunction->getCodeBlock(runtime);
469
0
  }
470
0
  return nullptr;
471
0
}
472
473
/// \return the file name associated with \p codeBlock, if any.
474
/// This mirrors the way we print file names for code blocks in JSError.
475
static CallResult<HermesValue> getCodeBlockFileName(
476
    Runtime &runtime,
477
    const CodeBlock *codeBlock,
478
0
    OptValue<hbc::DebugSourceLocation> location) {
479
0
  RuntimeModule *runtimeModule = codeBlock->getRuntimeModule();
480
0
  if (!runtimeModule->getBytecode()->isLazy()) {
481
    // Lazy code blocks do not have debug information (and will hermes_fatal if
482
    // you try to access it), so only touch it for non-lazy blocks.
483
0
    if (location) {
484
0
      auto debugInfo = runtimeModule->getBytecode()->getDebugInfo();
485
0
      return StringPrimitive::createEfficient(
486
0
          runtime, debugInfo->getFilenameByID(location->filenameId));
487
0
    } else {
488
0
      llvh::StringRef sourceURL = runtimeModule->getSourceURL();
489
0
      if (!sourceURL.empty()) {
490
0
        return StringPrimitive::createEfficient(runtime, sourceURL);
491
0
      }
492
0
    }
493
0
  }
494
0
  return HermesValue::encodeUndefinedValue();
495
0
}
496
497
/// \code
498
///   HermesInternal.getFunctionLocation function (func) {}
499
/// \endcode
500
/// Returns an object describing the source location of func.
501
/// The following properties may be present:
502
/// * fileName (string)
503
/// * lineNumber (number) - 1 based
504
/// * columnNumber (number) - 1 based
505
/// * segmentID (number) - 0 based
506
/// * virtualOffset (number) - 0 based
507
/// * isNative (boolean)
508
/// TypeError if func is not a function.
509
CallResult<HermesValue>
510
0
hermesInternalGetFunctionLocation(void *, Runtime &runtime, NativeArgs args) {
511
0
  GCScope gcScope(runtime);
512
513
0
  auto callable = args.dyncastArg<Callable>(0);
514
0
  if (!callable) {
515
0
    return runtime.raiseTypeError(
516
0
        "Argument to HermesInternal.getFunctionLocation must be callable");
517
0
  }
518
0
  auto resultHandle = runtime.makeHandle(JSObject::create(runtime));
519
0
  MutableHandle<> tmpHandle{runtime};
520
521
0
  auto codeBlock = getLeafCodeBlock(callable, runtime);
522
0
  bool isNative = !codeBlock;
523
0
  auto res = JSObject::defineOwnProperty(
524
0
      resultHandle,
525
0
      runtime,
526
0
      Predefined::getSymbolID(Predefined::isNative),
527
0
      DefinePropertyFlags::getDefaultNewPropertyFlags(),
528
0
      runtime.getBoolValue(isNative));
529
0
  assert(res != ExecutionStatus::EXCEPTION && "Failed to set isNative");
530
0
  (void)res;
531
532
0
  if (codeBlock) {
533
0
    OptValue<hbc::DebugSourceLocation> location =
534
0
        codeBlock->getSourceLocation();
535
0
    if (location) {
536
0
      tmpHandle = HermesValue::encodeUntrustedNumberValue(location->line);
537
0
      res = JSObject::defineOwnProperty(
538
0
          resultHandle,
539
0
          runtime,
540
0
          Predefined::getSymbolID(Predefined::lineNumber),
541
0
          DefinePropertyFlags::getDefaultNewPropertyFlags(),
542
0
          tmpHandle);
543
0
      assert(res != ExecutionStatus::EXCEPTION && "Failed to set lineNumber");
544
0
      (void)res;
545
546
0
      tmpHandle = HermesValue::encodeUntrustedNumberValue(location->column);
547
0
      res = JSObject::defineOwnProperty(
548
0
          resultHandle,
549
0
          runtime,
550
0
          Predefined::getSymbolID(Predefined::columnNumber),
551
0
          DefinePropertyFlags::getDefaultNewPropertyFlags(),
552
0
          tmpHandle);
553
0
      assert(res != ExecutionStatus::EXCEPTION && "Failed to set columnNumber");
554
0
      (void)res;
555
0
    } else {
556
0
      tmpHandle = HermesValue::encodeUntrustedNumberValue(
557
0
          codeBlock->getRuntimeModule()->getBytecode()->getSegmentID());
558
0
      res = JSObject::defineOwnProperty(
559
0
          resultHandle,
560
0
          runtime,
561
0
          Predefined::getSymbolID(Predefined::segmentID),
562
0
          DefinePropertyFlags::getDefaultNewPropertyFlags(),
563
0
          tmpHandle);
564
0
      assert(res != ExecutionStatus::EXCEPTION && "Failed to set segmentID");
565
0
      (void)res;
566
567
0
      tmpHandle = HermesValue::encodeUntrustedNumberValue(
568
0
          codeBlock->getVirtualOffset());
569
0
      res = JSObject::defineOwnProperty(
570
0
          resultHandle,
571
0
          runtime,
572
0
          Predefined::getSymbolID(Predefined::virtualOffset),
573
0
          DefinePropertyFlags::getDefaultNewPropertyFlags(),
574
0
          tmpHandle);
575
0
      assert(
576
0
          res != ExecutionStatus::EXCEPTION && "Failed to set virtualOffset");
577
0
      (void)res;
578
0
    }
579
580
0
    auto fileNameRes = getCodeBlockFileName(runtime, codeBlock, location);
581
0
    if (LLVM_UNLIKELY(fileNameRes == ExecutionStatus::EXCEPTION)) {
582
0
      return ExecutionStatus::EXCEPTION;
583
0
    }
584
0
    tmpHandle = *fileNameRes;
585
0
    res = JSObject::defineOwnProperty(
586
0
        resultHandle,
587
0
        runtime,
588
0
        Predefined::getSymbolID(Predefined::fileName),
589
0
        DefinePropertyFlags::getDefaultNewPropertyFlags(),
590
0
        tmpHandle);
591
0
    assert(res != ExecutionStatus::EXCEPTION && "Failed to set fileName");
592
0
    (void)res;
593
0
  }
594
0
  JSObject::preventExtensions(*resultHandle);
595
0
  return resultHandle.getHermesValue();
596
0
}
597
598
/// \code
599
///   HermesInternal.setPromiseRejectionTrackingHook = function (func) {}
600
/// \endcode
601
/// Register the function which can be used to *enable* Promise rejection
602
/// tracking when the user calls it.
603
/// For example, when using the npm `promise` polyfill:
604
/// \code
605
///   HermesInternal.setPromiseRejectionTrackingHook(
606
///     require('./rejection-tracking.js').enable
607
///   );
608
/// \endcode
609
CallResult<HermesValue> hermesInternalSetPromiseRejectionTrackingHook(
610
    void *,
611
    Runtime &runtime,
612
94
    NativeArgs args) {
613
94
  runtime.promiseRejectionTrackingHook_ = args.getArg(0);
614
94
  return HermesValue::encodeUndefinedValue();
615
94
}
616
617
/// \code
618
///   HermesInternal.enablePromiseRejectionTracker = function (opts) {}
619
/// \endcode
620
/// Enable promise rejection tracking with the given opts.
621
CallResult<HermesValue> hermesInternalEnablePromiseRejectionTracker(
622
    void *,
623
    Runtime &runtime,
624
0
    NativeArgs args) {
625
0
  auto opts = args.getArgHandle(0);
626
0
  auto func = Handle<Callable>::dyn_vmcast(
627
0
      Handle<>(&runtime.promiseRejectionTrackingHook_));
628
0
  if (!func) {
629
0
    return runtime.raiseTypeError(
630
0
        "Promise rejection tracking hook was not registered");
631
0
  }
632
0
  return Callable::executeCall1(
633
0
             func, runtime, Runtime::getUndefinedValue(), opts.getHermesValue())
634
0
      .toCallResultHermesValue();
635
0
}
636
637
#ifdef HERMES_ENABLE_FUZZILLI
638
639
/// Internal "fuzzilli" function used by the Fuzzilli fuzzer
640
/// (https://github.com/googleprojectzero/fuzzilli) to sanity-check the engine.
641
/// This function is conditionally defined in Hermes internal VM code rather
642
/// than in an external fuzzing module so to catch build misconfigurations, e.g.
643
/// we want to make sure that the VM is compiled with assertions enabled and
644
/// doing this check out of the VM (e.g. in the fuzzing harness) doesn't
645
/// guarantee that the VM has asserts on.
646
///
647
/// This function is defined as follow:
648
/// \code
649
/// HermesInternal.fuzzilli = function(op, arg) {}
650
/// \endcode
651
/// The first argument "op", is a string specifying the operation to be
652
/// performed. Currently supported values of "op" are "FUZZILLI_CRASH", used to
653
/// simulate a crash, and "FUZZILLI_PRINT", used to send data over Fuzzilli's
654
/// ata write file decriptor (REPRL_DWFD). The secong argument "arg" can be an
655
/// integer specifying the type of crash (if op is "FUZZILLI_CRASH") or a string
656
/// which value will be sent to fuzzilli (if op is "FUZZILLI_PRINT")
657
CallResult<HermesValue>
658
hermesInternalFuzzilli(void *, Runtime &runtime, NativeArgs args) {
659
  // REPRL = read-eval-print-reset-loop
660
  // This file descriptor is being opened by Fuzzilli
661
  constexpr int REPRL_DWFD = 103; // Data write file decriptor
662
663
  auto operationRes = toString_RJS(runtime, args.getArgHandle(0));
664
  if (LLVM_UNLIKELY(operationRes == ExecutionStatus::EXCEPTION)) {
665
    return ExecutionStatus::EXCEPTION;
666
  }
667
  auto operation = StringPrimitive::createStringView(
668
      runtime, runtime.makeHandle(std::move(*operationRes)));
669
670
  if (operation.equals(createUTF16Ref(u"FUZZILLI_CRASH"))) {
671
    auto crashTypeRes = toIntegerOrInfinity(runtime, args.getArgHandle(1));
672
    if (LLVM_UNLIKELY(crashTypeRes == ExecutionStatus::EXCEPTION)) {
673
      return ExecutionStatus::EXCEPTION;
674
    }
675
    switch (crashTypeRes->getNumberAs<int>()) {
676
      case 0:
677
        *((int *)0x41414141) = 0x1337;
678
        break;
679
      case 1:
680
        assert(0);
681
        break;
682
      case 2:
683
        std::abort();
684
        break;
685
    }
686
  } else if (operation.equals(createUTF16Ref(u"FUZZILLI_PRINT"))) {
687
    static FILE *fzliout = fdopen(REPRL_DWFD, "w");
688
    if (!fzliout) {
689
      fprintf(
690
          stderr,
691
          "Fuzzer output channel not available, printing to stdout instead\n");
692
      fzliout = stdout;
693
    }
694
695
    auto printRes = toString_RJS(runtime, args.getArgHandle(1));
696
    if (LLVM_UNLIKELY(printRes == ExecutionStatus::EXCEPTION)) {
697
      return ExecutionStatus::EXCEPTION;
698
    }
699
    auto print = StringPrimitive::createStringView(
700
        runtime, runtime.makeHandle(std::move(*printRes)));
701
702
    vm::SmallU16String<32> allocator;
703
    std::string outputString;
704
    ::hermes::convertUTF16ToUTF8WithReplacements(
705
        outputString, print.getUTF16Ref(allocator));
706
    fprintf(fzliout, "%s\n", outputString.c_str());
707
    fflush(fzliout);
708
  }
709
710
  return HermesValue::encodeUndefinedValue();
711
}
712
#endif // HERMES_ENABLE_FUZZILLI
713
714
static CallResult<HermesValue>
715
0
hermesInternalIsLazy(void *, Runtime &runtime, NativeArgs args) {
716
0
  auto callable = args.dyncastArg<Callable>(0);
717
0
  if (!callable) {
718
0
    return HermesValue::encodeBoolValue(false);
719
0
  }
720
721
0
  auto codeBlock = getLeafCodeBlock(callable, runtime);
722
0
  if (!codeBlock) {
723
    // Native function is never lazy.
724
0
    return HermesValue::encodeBoolValue(false);
725
0
  }
726
727
0
  RuntimeModule *runtimeModule = codeBlock->getRuntimeModule();
728
0
  return HermesValue::encodeBoolValue(
729
0
      runtimeModule && runtimeModule->getBytecode()->isLazy());
730
0
}
731
732
Handle<JSObject> createHermesInternalObject(
733
    Runtime &runtime,
734
94
    const JSLibFlags &flags) {
735
94
  namespace P = Predefined;
736
94
  Handle<JSObject> intern = runtime.makeHandle(JSObject::create(runtime));
737
94
  GCScope gcScope{runtime};
738
739
94
  DefinePropertyFlags constantDPF =
740
94
      DefinePropertyFlags::getDefaultNewPropertyFlags();
741
94
  constantDPF.enumerable = 0;
742
94
  constantDPF.writable = 0;
743
94
  constantDPF.configurable = 0;
744
745
94
  auto defineInternMethod =
746
1.12k
      [&](Predefined::Str symID, NativeFunctionPtr func, uint8_t count = 0) {
747
1.12k
        (void)defineMethod(
748
1.12k
            runtime,
749
1.12k
            intern,
750
1.12k
            Predefined::getSymbolID(symID),
751
1.12k
            nullptr /* context */,
752
1.12k
            func,
753
1.12k
            count,
754
1.12k
            constantDPF);
755
1.12k
      };
756
757
94
  auto defineInternMethodAndSymbol =
758
94
      [&](const char *name, NativeFunctionPtr func, uint8_t count = 0) {
759
0
        ASCIIRef ref = createASCIIRef(name);
760
0
        Handle<SymbolID> symHandle = runtime.ignoreAllocationFailure(
761
0
            runtime.getIdentifierTable().getSymbolHandle(runtime, ref));
762
0
        (void)defineMethod(
763
0
            runtime,
764
0
            intern,
765
0
            *symHandle,
766
0
            nullptr /* context */,
767
0
            func,
768
0
            count,
769
0
            constantDPF);
770
0
      };
771
772
  // suppress unused-variable warning
773
94
  (void)defineInternMethodAndSymbol;
774
775
  // Make a copy of the original String.prototype.concat implementation that we
776
  // can use internally.
777
  // TODO: we can't make HermesInternal.concat a static builtin method now
778
  // because this method should be called with a meaningful `this`, but
779
  // CallBuiltin instruction does not support it.
780
94
  auto propRes = JSObject::getNamed_RJS(
781
94
      runtime.makeHandle<JSObject>(runtime.stringPrototype),
782
94
      runtime,
783
94
      Predefined::getSymbolID(Predefined::concat));
784
94
  assert(
785
94
      propRes != ExecutionStatus::EXCEPTION && !(*propRes)->isUndefined() &&
786
94
      "Failed to get String.prototype.concat.");
787
94
  auto putRes = JSObject::defineOwnProperty(
788
94
      intern,
789
94
      runtime,
790
94
      Predefined::getSymbolID(Predefined::concat),
791
94
      constantDPF,
792
94
      runtime.makeHandle(std::move(*propRes)));
793
94
  assert(
794
94
      putRes != ExecutionStatus::EXCEPTION && *putRes &&
795
94
      "Failed to set HermesInternal.concat.");
796
94
  (void)putRes;
797
798
  // HermesInternal functions that are known to be safe and are required to be
799
  // present by the VM internals even under a security-sensitive environment
800
  // where HermesInternal might be explicitly disabled.
801
94
  defineInternMethod(P::hasPromise, hermesInternalHasPromise);
802
94
  defineInternMethod(P::hasES6Class, hermesInternalHasES6Class);
803
94
  defineInternMethod(P::enqueueJob, hermesInternalEnqueueJob);
804
94
  defineInternMethod(
805
94
      P::setPromiseRejectionTrackingHook,
806
94
      hermesInternalSetPromiseRejectionTrackingHook);
807
94
  defineInternMethod(
808
94
      P::enablePromiseRejectionTracker,
809
94
      hermesInternalEnablePromiseRejectionTracker);
810
94
  defineInternMethod(P::useEngineQueue, hermesInternalUseEngineQueue);
811
812
#ifdef HERMES_ENABLE_FUZZILLI
813
  defineInternMethod(P::fuzzilli, hermesInternalFuzzilli);
814
#endif
815
816
  // All functions are known to be safe can be defined above this flag check.
817
94
  if (!flags.enableHermesInternal) {
818
0
    JSObject::preventExtensions(*intern);
819
0
    return intern;
820
0
  }
821
822
  // HermesInternal functions that are not necessarily required but are
823
  // generally considered harmless to be exposed by default.
824
94
  defineInternMethod(P::getEpilogues, hermesInternalGetEpilogues);
825
94
  defineInternMethod(
826
94
      P::getRuntimeProperties, hermesInternalGetRuntimeProperties);
827
94
  defineInternMethod(P::ttiReached, hermesInternalTTIReached);
828
94
  defineInternMethod(P::ttrcReached, hermesInternalTTRCReached);
829
94
  defineInternMethod(P::getFunctionLocation, hermesInternalGetFunctionLocation);
830
831
94
  if (LLVM_UNLIKELY(runtime.traceMode != SynthTraceMode::None)) {
832
    // Use getNewNonEnumerableFlags() so that getInstrumentedStats can be
833
    // overridden for synth trace case. See TracingRuntime.cpp.
834
0
    (void)defineMethod(
835
0
        runtime,
836
0
        intern,
837
0
        Predefined::getSymbolID(P::getInstrumentedStats),
838
0
        nullptr /* context */,
839
0
        hermesInternalGetInstrumentedStats,
840
0
        0,
841
0
        DefinePropertyFlags::getNewNonEnumerableFlags());
842
94
  } else {
843
94
    defineInternMethod(
844
94
        P::getInstrumentedStats, hermesInternalGetInstrumentedStats);
845
94
  }
846
847
  // HermesInternal function that are only meant to be used for testing purpose.
848
  // They can change language semantics and are security risks.
849
94
  if (flags.enableHermesInternalTestMethods) {
850
0
    defineInternMethod(
851
0
        P::detachArrayBuffer, hermesInternalDetachArrayBuffer, 1);
852
0
    defineInternMethod(P::getWeakSize, hermesInternalGetWeakSize);
853
0
    defineInternMethod(
854
0
        P::copyDataProperties, hermesBuiltinCopyDataProperties, 3);
855
0
    defineInternMethodAndSymbol("isProxy", hermesInternalIsProxy);
856
0
    defineInternMethodAndSymbol("isLazy", hermesInternalIsLazy);
857
0
    defineInternMethod(P::drainJobs, hermesInternalDrainJobs);
858
0
  }
859
860
#ifdef HERMESVM_EXCEPTION_ON_OOM
861
  defineInternMethodAndSymbol("getCallStack", hermesInternalGetCallStack, 0);
862
#endif // HERMESVM_EXCEPTION_ON_OOM
863
864
94
  JSObject::preventExtensions(*intern);
865
866
94
  return intern;
867
94
}
868
869
} // namespace vm
870
} // namespace hermes