Coverage Report

Created: 2025-08-28 06:48

/src/hermes/lib/VM/JSLib/Function.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
//===----------------------------------------------------------------------===//
9
/// \file
10
/// ES5.1 15.3 Initialize the Function constructor.
11
//===----------------------------------------------------------------------===//
12
#include "JSLibInternal.h"
13
14
#include "hermes/Regex/Executor.h"
15
#include "hermes/Regex/RegexTraits.h"
16
#include "hermes/VM/ArrayLike.h"
17
#include "hermes/VM/Callable.h"
18
#include "hermes/VM/Operations.h"
19
#include "hermes/VM/StringBuilder.h"
20
#include "hermes/VM/StringView.h"
21
22
namespace hermes {
23
namespace vm {
24
25
//===----------------------------------------------------------------------===//
26
/// Function.
27
28
93
Handle<JSObject> createFunctionConstructor(Runtime &runtime) {
29
93
  auto functionPrototype = Handle<Callable>::vmcast(&runtime.functionPrototype);
30
31
93
  auto cons = defineSystemConstructor<JSFunction>(
32
93
      runtime,
33
93
      Predefined::getSymbolID(Predefined::Function),
34
93
      functionConstructor,
35
93
      functionPrototype,
36
93
      1,
37
93
      CellKind::JSFunctionKind);
38
39
  // Function.prototype.xxx() methods.
40
93
  defineMethod(
41
93
      runtime,
42
93
      functionPrototype,
43
93
      Predefined::getSymbolID(Predefined::toString),
44
93
      nullptr,
45
93
      functionPrototypeToString,
46
93
      0);
47
93
  defineMethod(
48
93
      runtime,
49
93
      functionPrototype,
50
93
      Predefined::getSymbolID(Predefined::apply),
51
93
      nullptr,
52
93
      functionPrototypeApply,
53
93
      2);
54
93
  defineMethod(
55
93
      runtime,
56
93
      functionPrototype,
57
93
      Predefined::getSymbolID(Predefined::call),
58
93
      nullptr,
59
93
      functionPrototypeCall,
60
93
      1);
61
93
  defineMethod(
62
93
      runtime,
63
93
      functionPrototype,
64
93
      Predefined::getSymbolID(Predefined::bind),
65
93
      nullptr,
66
93
      functionPrototypeBind,
67
93
      1);
68
69
93
  DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
70
93
  dpf.writable = 0;
71
93
  dpf.enumerable = 0;
72
93
  dpf.configurable = 0;
73
93
  (void)defineMethod(
74
93
      runtime,
75
93
      functionPrototype,
76
93
      Predefined::getSymbolID(Predefined::SymbolHasInstance),
77
93
      Predefined::getSymbolID(Predefined::squareSymbolHasInstance),
78
93
      nullptr,
79
93
      functionPrototypeSymbolHasInstance,
80
93
      1,
81
93
      dpf);
82
83
93
  return cons;
84
93
}
85
86
CallResult<HermesValue>
87
10
functionConstructor(void *, Runtime &runtime, NativeArgs args) {
88
10
  return createDynamicFunction(runtime, args, DynamicFunctionKind::Normal);
89
10
}
90
91
CallResult<HermesValue>
92
7
functionPrototypeToString(void *, Runtime &runtime, NativeArgs args) {
93
7
  GCScope gcScope{runtime};
94
95
7
  auto func = args.dyncastThis<Callable>();
96
7
  if (!func) {
97
0
    return runtime.raiseTypeError(
98
0
        "Can't call Function.prototype.toString() on non-callable");
99
0
  }
100
101
  /// Append the current function name to the \p strBuf.
102
7
  auto appendFunctionName = [&func, &runtime](SmallU16String<64> &strBuf) {
103
    // Extract the name.
104
7
    auto propRes = JSObject::getNamed_RJS(
105
7
        func, runtime, Predefined::getSymbolID(Predefined::name));
106
7
    if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
107
0
      return ExecutionStatus::EXCEPTION;
108
0
    }
109
110
    // Convert the name to string, unless it is undefined.
111
7
    if (!(*propRes)->isUndefined()) {
112
7
      auto strRes =
113
7
          toString_RJS(runtime, runtime.makeHandle(std::move(*propRes)));
114
7
      if (LLVM_UNLIKELY(strRes == ExecutionStatus::EXCEPTION)) {
115
0
        return ExecutionStatus::EXCEPTION;
116
0
      }
117
7
      strRes->get()->appendUTF16String(strBuf);
118
7
    }
119
7
    return ExecutionStatus::RETURNED;
120
7
  };
121
122
  // Deal with JSFunctions that has a source String ID. That implies this
123
  // function need a non-default toString implementation.
124
7
  if (auto jsFunc = dyn_vmcast<JSFunction>(*func)) {
125
7
    if (auto sourceID = jsFunc->getCodeBlock(runtime)->getFunctionSourceID()) {
126
0
      StringPrimitive *source =
127
0
          jsFunc->getCodeBlock(runtime)
128
0
              ->getRuntimeModule()
129
0
              ->getLazyRootModule()
130
0
              ->getStringPrimFromStringIDMayAllocate(*sourceID);
131
      // Empty source marks implementation-hidden function, fabricate a source
132
      // code string that imitate a NativeFunction.
133
0
      if (source->getStringLength() == 0) {
134
0
        SmallU16String<64> strBuf{};
135
0
        strBuf.append("function ");
136
0
        if (LLVM_UNLIKELY(
137
0
                appendFunctionName(strBuf) == ExecutionStatus::EXCEPTION)) {
138
0
          return ExecutionStatus::EXCEPTION;
139
0
        }
140
0
        strBuf.append("() { [native code] }");
141
0
        return StringPrimitive::create(runtime, strBuf);
142
0
      } else {
143
        // Otherwise, it's the preserved source code.
144
0
        return HermesValue::encodeStringValue(source);
145
0
      }
146
7
    };
147
7
  }
148
149
7
  SmallU16String<64> strBuf{};
150
7
  if (vmisa<JSAsyncFunction>(*func)) {
151
0
    strBuf.append("async function ");
152
7
  } else if (vmisa<JSGeneratorFunction>(*func)) {
153
1
    strBuf.append("function *");
154
6
  } else {
155
6
    strBuf.append("function ");
156
6
  }
157
158
7
  if (LLVM_UNLIKELY(appendFunctionName(strBuf) == ExecutionStatus::EXCEPTION)) {
159
0
    return ExecutionStatus::EXCEPTION;
160
0
  }
161
162
  // Formal parameters and the rest of the body.
163
7
  if (vmisa<NativeFunction>(*func)) {
164
    // Use [native code] here because we want to work with tools like Babel
165
    // which detect the string "[native code]" and use it to alter behavior
166
    // during the class transform.
167
    // Also print without synthesized formal parameters to avoid breaking
168
    // heuristics that detect the string "() { [native code] }".
169
    // \see https://github.com/facebook/hermes/issues/471
170
0
    strBuf.append("() { [native code] }");
171
7
  } else {
172
    // Append the synthesized formal parameters.
173
7
    strBuf.append('(');
174
175
    // Extract ".length".
176
7
    auto lengthProp = Callable::extractOwnLengthProperty_RJS(func, runtime);
177
7
    if (lengthProp == ExecutionStatus::EXCEPTION)
178
0
      return ExecutionStatus::EXCEPTION;
179
180
    // The value of the property is not guaranteed to be meaningful, so clamp it
181
    // to [0..65535] for sanity.
182
7
    uint32_t paramCount =
183
7
        (uint32_t)std::min(65535.0, std::max(0.0, *lengthProp));
184
185
7
    for (uint32_t i = 0; i < paramCount; ++i) {
186
0
      if (i != 0)
187
0
        strBuf.append(", ");
188
0
      char buf[16];
189
0
      ::snprintf(buf, sizeof(buf), "a%u", i);
190
0
      strBuf.append(buf);
191
0
    }
192
193
    // Avoid using the [native code] string to prevent extra wrapping overhead
194
    // in, e.g., Babel's class extension mechanism.
195
7
    strBuf.append(") { [bytecode] }");
196
7
  }
197
198
  // Finally allocate a StringPrimitive.
199
7
  return StringPrimitive::create(runtime, strBuf);
200
7
} // namespace vm
201
202
CallResult<HermesValue>
203
0
functionPrototypeApply(void *, Runtime &runtime, NativeArgs args) {
204
0
  GCScope gcScope(runtime);
205
0
  auto func = args.dyncastThis<Callable>();
206
0
  if (LLVM_UNLIKELY(!func)) {
207
0
    return runtime.raiseTypeError("Can't apply() to non-callable");
208
0
  }
209
210
0
  if (args.getArg(1).isNull() || args.getArg(1).isUndefined()) {
211
0
    ScopedNativeCallFrame newFrame{runtime, 0, *func, false, args.getArg(0)};
212
0
    if (LLVM_UNLIKELY(newFrame.overflowed()))
213
0
      return runtime.raiseStackOverflow(
214
0
          Runtime::StackOverflowKind::NativeStack);
215
0
    return Callable::call(func, runtime).toCallResultHermesValue();
216
0
  }
217
218
0
  auto argObj = Handle<JSObject>::dyn_vmcast(args.getArgHandle(1));
219
0
  if (LLVM_UNLIKELY(!argObj)) {
220
0
    return runtime.raiseTypeError(
221
0
        "Can't apply() with non-object arguments list");
222
0
  }
223
224
0
  return Callable::executeCall(
225
0
             func,
226
0
             runtime,
227
0
             Runtime::getUndefinedValue(),
228
0
             args.getArgHandle(0),
229
0
             argObj)
230
0
      .toCallResultHermesValue();
231
0
}
232
233
CallResult<HermesValue>
234
0
functionPrototypeCall(void *, Runtime &runtime, NativeArgs args) {
235
0
  auto func = args.dyncastThis<Callable>();
236
0
  if (LLVM_UNLIKELY(!func)) {
237
0
    return runtime.raiseTypeError("Can't call() non-callable");
238
0
  }
239
240
0
  uint32_t argCount = args.getArgCount();
241
0
  ScopedNativeCallFrame newFrame{
242
0
      runtime, argCount ? argCount - 1 : 0, *func, false, args.getArg(0)};
243
0
  if (LLVM_UNLIKELY(newFrame.overflowed()))
244
0
    return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
245
0
  for (uint32_t i = 1; i < argCount; ++i) {
246
0
    newFrame->getArgRef(i - 1) = args.getArg(i);
247
0
  }
248
0
  auto res = Callable::call(func, runtime);
249
0
  if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) {
250
0
    return ExecutionStatus::EXCEPTION;
251
0
  }
252
0
  return res->getHermesValue();
253
0
}
254
255
CallResult<HermesValue>
256
93
functionPrototypeBind(void *, Runtime &runtime, NativeArgs args) {
257
93
  auto target = Handle<Callable>::dyn_vmcast(args.getThisHandle());
258
93
  if (!target) {
259
0
    return runtime.raiseTypeError("Can't bind() a non-callable");
260
0
  }
261
262
93
  return BoundFunction::create(
263
93
      runtime, target, args.getArgCount(), args.begin());
264
93
}
265
266
CallResult<HermesValue>
267
0
functionPrototypeSymbolHasInstance(void *, Runtime &runtime, NativeArgs args) {
268
  /// 1. Let F be the this value.
269
0
  auto F = args.getThisHandle();
270
  /// 2. Return OrdinaryHasInstance(F, V).
271
0
  auto result = ordinaryHasInstance(runtime, F, args.getArgHandle(0));
272
0
  if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
273
0
    return ExecutionStatus::EXCEPTION;
274
0
  }
275
0
  return HermesValue::encodeBoolValue(*result);
276
0
}
277
278
} // namespace vm
279
} // namespace hermes