Coverage Report

Created: 2025-01-28 06:38

/src/hermes/lib/VM/JSLib/JSLibInternal.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 "JSLibInternal.h"
9
10
#include "hermes/Regex/Executor.h"
11
#include "hermes/Regex/Regex.h"
12
#include "hermes/Regex/RegexTraits.h"
13
#include "hermes/VM/PropertyAccessor.h"
14
#include "hermes/VM/Runtime.h"
15
#include "hermes/VM/StringBuilder.h"
16
#include "hermes/VM/StringPrimitive.h"
17
#include "hermes/VM/StringView.h"
18
#pragma GCC diagnostic push
19
20
#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32
21
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
22
#endif
23
namespace hermes {
24
namespace vm {
25
26
Handle<NativeConstructor> defineSystemConstructor(
27
    Runtime &runtime,
28
    SymbolID name,
29
    NativeFunctionPtr nativeFunctionPtr,
30
    Handle<JSObject> prototypeObjectHandle,
31
    Handle<JSObject> constructorProtoObjectHandle,
32
    unsigned paramCount,
33
    NativeConstructor::CreatorFunction *creator,
34
6.24k
    CellKind targetKind) {
35
6.24k
  auto constructor = runtime.makeHandle(NativeConstructor::create(
36
6.24k
      runtime,
37
6.24k
      constructorProtoObjectHandle,
38
6.24k
      nullptr,
39
6.24k
      nativeFunctionPtr,
40
6.24k
      paramCount,
41
6.24k
      creator,
42
6.24k
      targetKind));
43
44
6.24k
  auto st = Callable::defineNameLengthAndPrototype(
45
6.24k
      constructor,
46
6.24k
      runtime,
47
6.24k
      name,
48
6.24k
      paramCount,
49
6.24k
      prototypeObjectHandle,
50
6.24k
      Callable::WritablePrototype::No,
51
6.24k
      false);
52
6.24k
  (void)st;
53
6.24k
  assert(
54
6.24k
      st != ExecutionStatus::EXCEPTION && "defineLengthAndPrototype() failed");
55
56
  // Define the global.
57
6.24k
  DefinePropertyFlags dpf = DefinePropertyFlags::getNewNonEnumerableFlags();
58
59
6.24k
  auto res = JSObject::defineOwnProperty(
60
6.24k
      runtime.getGlobal(), runtime, name, dpf, constructor);
61
6.24k
  assert(
62
6.24k
      res != ExecutionStatus::EXCEPTION && *res &&
63
6.24k
      "defineOwnProperty() failed");
64
6.24k
  (void)res;
65
66
6.24k
  return constructor;
67
6.24k
}
68
69
CallResult<HermesValue> defineMethod(
70
    Runtime &runtime,
71
    Handle<JSObject> objectHandle,
72
    SymbolID propertyName,
73
    SymbolID methodName,
74
    void *context,
75
    NativeFunctionPtr nativeFunctionPtr,
76
    unsigned paramCount,
77
57.6k
    DefinePropertyFlags dpf) {
78
57.6k
  GCScope gcScope{runtime};
79
80
57.6k
  auto method = NativeFunction::create(
81
57.6k
      runtime,
82
57.6k
      Handle<JSObject>::vmcast(&runtime.functionPrototype),
83
57.6k
      context,
84
57.6k
      nativeFunctionPtr,
85
57.6k
      methodName,
86
57.6k
      paramCount,
87
57.6k
      Runtime::makeNullHandle<JSObject>());
88
89
57.6k
  auto res = JSObject::defineOwnProperty(
90
57.6k
      objectHandle, runtime, propertyName, dpf, method);
91
57.6k
  (void)res;
92
57.6k
  assert(
93
57.6k
      res != ExecutionStatus::EXCEPTION && *res &&
94
57.6k
      "defineOwnProperty() failed");
95
96
57.6k
  return method.getHermesValue();
97
57.6k
}
98
99
Handle<NativeConstructor> defineSystemConstructor(
100
    Runtime &runtime,
101
    SymbolID name,
102
    NativeFunctionPtr nativeFunctionPtr,
103
    Handle<JSObject> prototypeObjectHandle,
104
    unsigned paramCount,
105
    NativeConstructor::CreatorFunction *creator,
106
3.04k
    CellKind targetKind) {
107
3.04k
  return defineSystemConstructor(
108
3.04k
      runtime,
109
3.04k
      name,
110
3.04k
      nativeFunctionPtr,
111
3.04k
      prototypeObjectHandle,
112
3.04k
      Handle<JSObject>::vmcast(&runtime.functionPrototype),
113
3.04k
      paramCount,
114
3.04k
      creator,
115
3.04k
      targetKind);
116
3.04k
}
117
118
void defineMethod(
119
    Runtime &runtime,
120
    Handle<JSObject> objectHandle,
121
    SymbolID name,
122
    void *context,
123
    NativeFunctionPtr nativeFunctionPtr,
124
53.9k
    unsigned paramCount) {
125
53.9k
  DefinePropertyFlags dpf = DefinePropertyFlags::getNewNonEnumerableFlags();
126
53.9k
  (void)defineMethod(
127
53.9k
      runtime, objectHandle, name, context, nativeFunctionPtr, paramCount, dpf);
128
53.9k
}
129
130
void defineAccessor(
131
    Runtime &runtime,
132
    Handle<JSObject> objectHandle,
133
    SymbolID propertyName,
134
    SymbolID methodName,
135
    void *context,
136
    NativeFunctionPtr getterFunc,
137
    NativeFunctionPtr setterFunc,
138
    bool enumerable,
139
7.04k
    bool configurable) {
140
7.04k
  assert(
141
7.04k
      (getterFunc || setterFunc) &&
142
7.04k
      "at least a getter or a setter must be specified");
143
7.04k
  ExecutionStatus status{};
144
7.04k
  (void)status;
145
146
7.04k
  GCScope gcScope{runtime};
147
148
7.04k
  StringView nameView =
149
7.04k
      runtime.getIdentifierTable().getStringView(runtime, methodName);
150
7.04k
  assert(nameView.isASCII() && "Only ASCII accessors are supported");
151
152
7.04k
  MutableHandle<NativeFunction> getter{runtime};
153
7.04k
  if (getterFunc) {
154
    // Set the name by prepending "get ".
155
7.04k
    llvh::SmallString<32> getterName{"get "};
156
7.04k
    llvh::raw_svector_ostream os{getterName};
157
7.04k
    os << nameView;
158
159
7.04k
    auto strRes = runtime.ignoreAllocationFailure(
160
7.04k
        StringPrimitive::create(runtime, getterName));
161
7.04k
    SymbolID getterFuncName =
162
7.04k
        runtime
163
7.04k
            .ignoreAllocationFailure(
164
7.04k
                runtime.getIdentifierTable().getSymbolHandleFromPrimitive(
165
7.04k
                    runtime,
166
7.04k
                    createPseudoHandle(vmcast<StringPrimitive>(strRes))))
167
7.04k
            .get();
168
169
7.04k
    auto funcRes = NativeFunction::create(
170
7.04k
        runtime,
171
7.04k
        Handle<JSObject>::vmcast(&runtime.functionPrototype),
172
7.04k
        context,
173
7.04k
        getterFunc,
174
7.04k
        getterFuncName,
175
7.04k
        0,
176
7.04k
        Runtime::makeNullHandle<JSObject>());
177
7.04k
    getter = funcRes.get();
178
7.04k
  }
179
180
7.04k
  MutableHandle<NativeFunction> setter{runtime};
181
7.04k
  if (setterFunc) {
182
    // Set the name by prepending "set ".
183
160
    llvh::SmallString<32> setterName{"set "};
184
160
    llvh::raw_svector_ostream os{setterName};
185
160
    os << nameView;
186
187
160
    auto strRes = runtime.ignoreAllocationFailure(
188
160
        StringPrimitive::create(runtime, setterName));
189
160
    SymbolID setterFuncName =
190
160
        runtime
191
160
            .ignoreAllocationFailure(
192
160
                runtime.getIdentifierTable().getSymbolHandleFromPrimitive(
193
160
                    runtime,
194
160
                    createPseudoHandle(vmcast<StringPrimitive>(strRes))))
195
160
            .get();
196
197
160
    auto funcRes = NativeFunction::create(
198
160
        runtime,
199
160
        Handle<JSObject>::vmcast(&runtime.functionPrototype),
200
160
        context,
201
160
        setterFunc,
202
160
        setterFuncName,
203
160
        1,
204
160
        Runtime::makeNullHandle<JSObject>());
205
160
    setter = funcRes.get();
206
160
  }
207
208
7.04k
  auto accessor = runtime.makeHandle<PropertyAccessor>(
209
7.04k
      PropertyAccessor::create(runtime, getter, setter));
210
211
7.04k
  DefinePropertyFlags dpf{};
212
7.04k
  dpf.setEnumerable = 1;
213
7.04k
  dpf.setConfigurable = 1;
214
7.04k
  dpf.setGetter = 1;
215
7.04k
  dpf.setSetter = 1;
216
7.04k
  dpf.enumerable = enumerable;
217
7.04k
  dpf.configurable = configurable;
218
219
7.04k
  auto res = JSObject::defineOwnProperty(
220
7.04k
      objectHandle, runtime, propertyName, dpf, accessor);
221
7.04k
  (void)res;
222
7.04k
  assert(
223
7.04k
      res != ExecutionStatus::EXCEPTION && *res &&
224
7.04k
      "defineOwnProperty() failed");
225
7.04k
}
226
227
void defineProperty(
228
    Runtime &runtime,
229
    Handle<JSObject> objectHandle,
230
    SymbolID name,
231
    Handle<> value,
232
13.6k
    DefinePropertyFlags dpf) {
233
13.6k
  auto res = JSObject::defineOwnProperty(
234
13.6k
      objectHandle, runtime, name, dpf, value, PropOpFlags());
235
13.6k
  (void)res;
236
13.6k
  assert(
237
13.6k
      res != ExecutionStatus::EXCEPTION && *res &&
238
13.6k
      "defineOwnProperty() failed");
239
13.6k
}
240
241
void defineProperty(
242
    Runtime &runtime,
243
    Handle<JSObject> objectHandle,
244
    SymbolID name,
245
4.48k
    Handle<> value) {
246
4.48k
  DefinePropertyFlags dpf = DefinePropertyFlags::getNewNonEnumerableFlags();
247
248
4.48k
  return defineProperty(runtime, objectHandle, name, value, dpf);
249
4.48k
}
250
251
ExecutionStatus iteratorCloseAndRethrow(
252
    Runtime &runtime,
253
0
    Handle<JSObject> iterator) {
254
0
  auto completion = runtime.makeHandle(runtime.getThrownValue());
255
0
  if (isUncatchableError(completion.getHermesValue())) {
256
    // If an uncatchable exception was raised, do not swallow it, but instead
257
    // propagate it.
258
0
    return ExecutionStatus::EXCEPTION;
259
0
  }
260
0
  runtime.clearThrownValue();
261
0
  auto status = iteratorClose(runtime, iterator, completion);
262
0
  (void)status;
263
0
  assert(
264
0
      status == ExecutionStatus::EXCEPTION && "exception swallowed mistakenly");
265
0
  return ExecutionStatus::EXCEPTION;
266
0
}
267
268
1
static std::vector<uint8_t> getReturnThisRegexBytecode() {
269
1
  const char16_t *returnThisRE = uR"X(^\s*return[ \t]+this\s*;?\s*$)X";
270
1
  return regex::Regex<regex::UTF16RegexTraits>(returnThisRE).compile();
271
1
}
272
273
86
static bool isReturnThis(Handle<StringPrimitive> str, Runtime &runtime) {
274
  // Fast check for minimal version.
275
86
  {
276
86
    auto minimal = runtime.getPredefinedString(Predefined::returnThis);
277
86
    if (str->equals(minimal)) {
278
0
      return true;
279
0
    }
280
86
  }
281
  // Regex match for variants.
282
86
  auto input = StringPrimitive::createStringView(runtime, str);
283
86
  static auto bytecode = getReturnThisRegexBytecode();
284
86
  auto result = regex::MatchRuntimeResult::NoMatch;
285
86
  if (input.isASCII()) {
286
0
    const char *begin = input.castToCharPtr();
287
0
    result = regex::searchWithBytecode(
288
0
        bytecode,
289
0
        begin,
290
0
        0,
291
0
        input.length(),
292
0
        nullptr,
293
0
        regex::constants::matchDefault | regex::constants::matchInputAllAscii,
294
0
        runtime.getOverflowGuardForRegex());
295
86
  } else {
296
86
    const char16_t *begin = input.castToChar16Ptr();
297
86
    result = regex::searchWithBytecode(
298
86
        bytecode,
299
86
        begin,
300
86
        0,
301
86
        input.length(),
302
86
        nullptr,
303
86
        regex::constants::matchDefault,
304
86
        runtime.getOverflowGuardForRegex());
305
86
  }
306
86
  return result == regex::MatchRuntimeResult::Match;
307
86
}
308
309
CallResult<HermesValue> createDynamicFunction(
310
    Runtime &runtime,
311
    NativeArgs args,
312
87
    DynamicFunctionKind kind) {
313
87
  GCScope gcScope(runtime);
314
315
  // Number of arguments supplied to Function().
316
87
  uint32_t argCount = args.getArgCount();
317
318
  // Number of parameters in the resultant function.
319
87
  uint32_t paramCount = argCount > 0 ? argCount - 1 : 0;
320
321
  // List of the parameter strings for the resultant function..
322
87
  auto arrRes = JSArray::create(runtime, paramCount, paramCount);
323
87
  if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
324
0
    return ExecutionStatus::EXCEPTION;
325
0
  }
326
87
  auto params = *arrRes;
327
328
  // Body of the resultant function.
329
87
  MutableHandle<StringPrimitive> body{runtime};
330
331
  // String length of the function in its entirety.
332
  // Account for commas in the argument list initially.
333
  // If at least two arguments to the function (3 in total), there's a comma.
334
87
  SafeUInt32 size{paramCount > 0 ? paramCount - 1 : 0};
335
336
  // es2020 19.2.1.1.1 Runtime Semantics: CreateDynamicFunction: If
337
  // NewTarget is given, such as with Reflect.construct, use
338
  // NewParent.prototype as the parent.  The prototype on NewParent
339
  // has already been looked up to use as the parent of 'this', so
340
  // instead of looking it up again, just use this's parent.  If
341
  // NewTarget isn't given, fall back to a default.
342
87
  MutableHandle<JSObject> fallbackProto{runtime};
343
87
  switch (kind) {
344
87
    case DynamicFunctionKind::Normal:
345
87
      fallbackProto = Handle<JSObject>::vmcast(&runtime.functionPrototype);
346
87
      break;
347
0
    case DynamicFunctionKind::Generator:
348
0
      fallbackProto =
349
0
          Handle<JSObject>::vmcast(&runtime.generatorFunctionPrototype);
350
0
      break;
351
0
    case DynamicFunctionKind::Async:
352
0
      fallbackProto = Handle<JSObject>::vmcast(&runtime.asyncFunctionPrototype);
353
0
      break;
354
0
    default:
355
0
      llvm_unreachable("unknown kind for CreateDynamicFunction.");
356
87
  }
357
358
87
  Handle<JSObject> parent = !args.getNewTarget().isUndefined()
359
87
      ? runtime.makeHandle(
360
0
            vmcast<JSObject>(args.getThisArg())->getParent(runtime))
361
87
      : fallbackProto;
362
363
87
  if (argCount == 0) {
364
    // No arguments, just set body to be the empty string.
365
0
    body = runtime.getPredefinedString(Predefined::emptyString);
366
87
  } else {
367
    // If there's arguments, store the parameters and the provided body.
368
87
    auto marker = gcScope.createMarker();
369
13.3k
    for (uint32_t i = 0; i < paramCount; ++i) {
370
13.2k
      gcScope.flushToMarker(marker);
371
13.2k
      auto strRes = toString_RJS(runtime, args.getArgHandle(i));
372
13.2k
      if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
373
0
        return ExecutionStatus::EXCEPTION;
374
0
      }
375
13.2k
      auto param = runtime.makeHandle(std::move(*strRes));
376
13.2k
      JSArray::setElementAt(params, runtime, i, param);
377
13.2k
      size.add(param->getStringLength());
378
13.2k
    }
379
380
    // Last parameter is the body.
381
87
    auto strRes = toString_RJS(runtime, args.getArgHandle(paramCount));
382
87
    if (strRes == ExecutionStatus::EXCEPTION) {
383
0
      return ExecutionStatus::EXCEPTION;
384
0
    }
385
87
    body = strRes->get();
386
87
    size.add(body->getStringLength());
387
388
87
    if (kind == DynamicFunctionKind::Normal && argCount == 1 &&
389
87
        isReturnThis(body, runtime)) {
390
      // If this raises an exception, we still return immediately.
391
0
      return JSFunction::create(
392
0
                 runtime,
393
0
                 runtime.makeHandle(Domain::create(runtime)),
394
0
                 parent,
395
0
                 Handle<Environment>(runtime, nullptr),
396
0
                 runtime.getReturnThisCodeBlock())
397
0
          .getHermesValue();
398
0
    }
399
87
  }
400
401
  // Constant parts of the function.
402
87
  ASCIIRef functionHeader;
403
87
  switch (kind) {
404
87
    case DynamicFunctionKind::Normal:
405
87
      functionHeader = createASCIIRef("(function (");
406
87
      break;
407
0
    case DynamicFunctionKind::Generator:
408
0
      functionHeader = createASCIIRef("(function*(");
409
0
      break;
410
0
    case DynamicFunctionKind::Async:
411
0
      functionHeader = createASCIIRef("(async function (");
412
0
      break;
413
0
    default:
414
0
      llvm_unreachable("unknown kind for CreateDynamicFunction.");
415
87
  };
416
87
  size.add(functionHeader.size());
417
418
87
  auto bodyHeader = createASCIIRef("){");
419
87
  size.add(bodyHeader.size());
420
421
  // Note: add a \n before the closing '}' in case the
422
  // function body ends with a line comment.
423
87
  auto functionFooter = createASCIIRef("\n})");
424
87
  size.add(functionFooter.size());
425
426
87
  auto separator = createASCIIRef(",");
427
428
87
  auto builder = StringBuilder::createStringBuilder(runtime, size);
429
87
  if (builder == ExecutionStatus::EXCEPTION) {
430
0
    return ExecutionStatus::EXCEPTION;
431
0
  }
432
87
  builder->appendASCIIRef(functionHeader);
433
87
  MutableHandle<StringPrimitive> element{runtime};
434
13.3k
  for (uint32_t i = 0; i < paramCount; ++i) {
435
    // Copy params into str.
436
13.2k
    element = params->at(runtime, i).getString(runtime);
437
13.2k
    builder->appendStringPrim(element);
438
13.2k
    if (i < paramCount - 1) {
439
      // If there's more params left to put, need to add a comma.
440
      // We wouldn't have entered the loop if paramCount == 0,
441
      // so there's no overflow here.
442
13.2k
      builder->appendASCIIRef(separator);
443
13.2k
    }
444
13.2k
  }
445
87
  builder->appendASCIIRef(bodyHeader);
446
87
  builder->appendStringPrim(body);
447
87
  builder->appendASCIIRef(functionFooter);
448
449
87
  auto evalRes =
450
87
      directEval(runtime, builder->getStringPrimitive(), {}, false, true);
451
87
  if (evalRes == ExecutionStatus::EXCEPTION) {
452
5
    return ExecutionStatus::EXCEPTION;
453
5
  }
454
455
82
  Handle<JSFunction> function =
456
82
      runtime.makeHandle(vmcast<JSFunction>(evalRes.getValue()));
457
458
82
  DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
459
82
  dpf.enumerable = 0;
460
82
  dpf.writable = 0;
461
462
  // Define the `name` correctly.
463
82
  if (JSObject::defineOwnProperty(
464
82
          function,
465
82
          runtime,
466
82
          Predefined::getSymbolID(Predefined::name),
467
82
          dpf,
468
82
          runtime.makeHandle(runtime.getStringPrimFromSymbolID(
469
82
              Predefined::getSymbolID(Predefined::anonymous)))) ==
470
82
      ExecutionStatus::EXCEPTION) {
471
0
    return ExecutionStatus::EXCEPTION;
472
0
  }
473
474
  // Set the parent.  This could be done by threading the argument
475
  // through to Runtime::runBytecode where the object is actually
476
  // created, but this is the only place we need to do this so it
477
  // keeps the code simpler.
478
82
  CallResult<bool> parentRes = JSObject::setParent(*function, runtime, *parent);
479
82
  if (LLVM_UNLIKELY(parentRes == ExecutionStatus::EXCEPTION)) {
480
0
    return ExecutionStatus::EXCEPTION;
481
0
  }
482
82
  assert(
483
82
      *parentRes && "Setting prototype on new dynamic function returned false");
484
485
82
  return function.getHermesValue();
486
82
}
487
488
} // namespace vm
489
} // namespace hermes