Coverage Report

Created: 2025-12-12 07:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hermes/include/hermes/AST/Context.h
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
#ifndef HERMES_AST_CONTEXT_H
9
#define HERMES_AST_CONTEXT_H
10
11
#include "hermes/Parser/PreParser.h"
12
#include "hermes/Regex/RegexSerialization.h"
13
#include "hermes/Support/Allocator.h"
14
#include "hermes/Support/SourceErrorManager.h"
15
#include "hermes/Support/StringTable.h"
16
17
#include "llvh/ADT/DenseSet.h"
18
#include "llvh/ADT/StringRef.h"
19
20
namespace hermes {
21
22
namespace hbc {
23
class BackendContext;
24
}
25
26
#ifdef HERMES_RUN_WASM
27
class EmitWasmIntrinsicsContext;
28
#endif // HERMES_RUN_WASM
29
30
struct CodeGenerationSettings_DumpSettings {
31
  bool all{false};
32
  llvh::SmallDenseSet<llvh::StringRef> passes;
33
  llvh::SmallDenseSet<llvh::StringRef> functions;
34
};
35
36
struct CodeGenerationSettings {
37
  using DumpSettings = CodeGenerationSettings_DumpSettings;
38
39
  /// Whether we should emit TDZ checks.
40
  bool const enableTDZ{false};
41
  /// Whether we can assume there are unlimited number of registers.
42
  /// This affects how we generate the IR, as we can decide whether
43
  /// to hold as many temporary values as we like.
44
  bool unlimitedRegisters{true};
45
  /// Dump registers assigned to instruction operands.
46
  bool dumpOperandRegisters{false};
47
  /// Print source location information in IR dumps.
48
  bool dumpSourceLocation{false};
49
  /// Print the original scope for each instruction.
50
  bool dumpSourceLevelScope{false};
51
  /// Print the textified callee of call instructions.
52
  bool dumpTextifiedCallee{false};
53
  /// Print the use list if the instruction has any users.
54
  bool dumpUseList{false};
55
  /// Instrument IR for dynamic checking (if support is compiled in).
56
  bool instrumentIR{false};
57
  /// Instructs IR Generation to use synthetic names for unnamed functions.
58
  bool generateNameForUnnamedFunctions{false};
59
  /// Whether block scoping is enabled.
60
  bool enableBlockScoping{false};
61
62
  /// Dump IR before each pass (if holds boolean), or the given passes (if holds
63
  /// DensetSet).
64
  DumpSettings dumpBefore;
65
66
  /// Dump IR after each pass (if holds boolean), or the given passes (if holds
67
  /// DensetSet).
68
  DumpSettings dumpAfter;
69
70
  /// Restricts inter-pass dump to the given functions. If empty, all functions
71
  /// are dumped.
72
  llvh::SmallDenseSet<llvh::StringRef> functionsToDump;
73
};
74
75
struct OptimizationSettings {
76
  /// Enable aggressive non-strict mode optimizations. These optimizations
77
  /// assume that:
78
  ///   - function arguments are never modified indirectly
79
  ///   - local "eval()" or "with" are not used.
80
  bool aggressiveNonStrictModeOptimizations{true};
81
82
  /// Enable any inlining of functions.
83
  bool inlining{true};
84
85
  /// Reuse property cache entries for same property name.
86
  bool reusePropCache{true};
87
88
  /// Recognize calls to global functions like Object.keys() and turn them
89
  /// into builtin calls.
90
  bool staticBuiltins{false};
91
92
  /// Attempt to resolve CommonJS require() calls at compile time.
93
  bool staticRequire{false};
94
95
  /// Recognize and emit Asm.js/Wasm unsafe compiler intrinsics.
96
  bool useUnsafeIntrinsics{false};
97
};
98
99
enum class DebugInfoSetting {
100
  /// Only emit source locations for instructions that may throw, as required
101
  /// for generating error stack traces.
102
  THROWING,
103
104
  /// Emit source locations for all instructions, as required for generating
105
  /// a source map.
106
  SOURCE_MAP,
107
108
  /// Emit full debug info, including source locations for all instructions,
109
  /// lexical scope info, async break check instructions, etc.
110
  ALL,
111
};
112
113
enum class ParseFlowSetting {
114
  /// Do not parse any Flow type syntax.
115
  NONE,
116
117
  /// Parse all Flow type syntax.
118
  ALL,
119
120
  /// Parse all unambiguous Flow type syntax. Syntax that can be intepreted as
121
  /// either Flow types or standard JavaScript is parsed as if it were standard
122
  /// JavaScript.
123
  ///
124
  /// For example, `foo<T>(x)` is parsed as if it were standard JavaScript
125
  /// containing two comparisons, even though it could otherwise be interpreted
126
  /// as a call expression with Flow type arguments.
127
  UNAMBIGUOUS,
128
};
129
130
/// An enum to track the "source visibility" of functions. This notion is coined
131
/// to implement "directives" such as 'hide source' and 'sensitive' defined by
132
/// https://github.com/tc39/proposal-function-implementation-hiding, as well as
133
/// 'show source' Hermes proposed to explicitly preserve source for `toString`.
134
///
135
/// Members are ordered in an increasingly stronger manner, where only later
136
/// source visibility can override the earlier but not vice versa.
137
enum class SourceVisibility {
138
  /// The implementation-default behavior, e.g. `toString` prints
139
  /// `{ [bytecode] }` in Hermes.
140
  Default,
141
142
  /// Enforce the source code text to be available for the `toString` use.
143
  ShowSource,
144
145
  /// Enforce to have the syntax of NativeFunction, e.g. `toString` prints
146
  /// `{ [native code] }`.
147
  HideSource,
148
149
  /// Considered security-sensitive, e.g. `toString` printed as NativeFunction;
150
  /// hidden from error stack trace to protect from leaking its existence.
151
  Sensitive,
152
};
153
154
/// Holds shared dependencies and state.
155
class Context {
156
 public:
157
  using Allocator = hermes::BumpPtrAllocator;
158
159
  /// Mapping from require() arguments to actual resolved paths.
160
  /// Strings owned by a JSON Parser allocator that the user of Context should
161
  /// manage.
162
  using ResolutionTableEntry = llvh::DenseMap<llvh::StringRef, llvh::StringRef>;
163
164
  /// Mapping from file names to the dictionary to use to look up resolutions.
165
  /// Strings owned by a JSON Parser allocator that the user of Context should
166
  /// manage.
167
  using ResolutionTable = llvh::DenseMap<llvh::StringRef, ResolutionTableEntry>;
168
169
 private:
170
  /// The allocator for AST nodes, which may be rolled back to parse subtrees
171
  /// during pre-parsing (for lazy parsing).
172
  Allocator allocator_{};
173
174
  /// String/identifier table allocator. It's separate from the AST allocator
175
  /// because we don't want to revert the strings when we revert subtrees.
176
  Allocator identifierAllocator_{};
177
178
  /// Preparsed function spans and similar used during lazy parsing.
179
  std::unique_ptr<parser::PreParsedData> preParsed_{};
180
181
  /// The global string table.
182
  StringTable stringTable_{identifierAllocator_};
183
184
  std::map<std::pair<UniqueString *, UniqueString *>, CompiledRegExp>
185
      compiledRegExps_{};
186
187
  /// If an external SourceErrorManager was not supplied to us, we allocate out
188
  /// private one here.
189
  std::unique_ptr<SourceErrorManager> ownSm_;
190
191
  /// A reference to the manager which we are using.
192
  SourceErrorManager &sm_;
193
194
  /// Whether we are running in script mode. Default to strict.
195
  bool strictMode_{false};
196
197
  /// Is 'eval()' is enabled.
198
  bool enableEval_{true};
199
200
  /// If true, every function will be compiled lazily when invoked for the
201
  /// first time.
202
  bool lazyCompilation_{false};
203
204
  /// Even if lazily compiling, eagerly compile any functions under this size in
205
  /// bytes.
206
  unsigned preemptiveFunctionCompilationThreshold_{0};
207
208
  /// Even if lazily compiling, eagerly compile any files under this size in
209
  /// bytes.
210
  unsigned preemptiveFileCompilationThreshold_{0};
211
212
  /// If true, do not error on return statements that are not within functions.
213
  bool allowReturnOutsideFunction_{false};
214
215
  /// Allows generator functions to be compiled.
216
  bool generatorEnabled_{true};
217
218
  /// If true, wrap each file in the CommonJS module wrapper function,
219
  /// and use that for requiring modules.
220
  bool useCJSModules_{false};
221
222
  /// If true, allow parsing JSX as a primary expression.
223
  bool parseJSX_{false};
224
225
  /// If true, allow parsing component syntax when also using Flow syntax.
226
  bool parseFlowComponentSyntax_{false};
227
228
  /// If true, allow parsing match statements and expressions when
229
  /// also using Flow syntax.
230
  bool parseFlowMatch_{false};
231
232
  /// Whether to parse Flow type syntax.
233
  ParseFlowSetting parseFlow_{ParseFlowSetting::NONE};
234
235
  /// Whether to parse TypeScript syntax.
236
  bool parseTS_{false};
237
238
  /// Whether to convert ES6 classes to ES5 functions
239
  bool convertES6Classes_{false};
240
241
  /// If non-null, the resolution table which resolves static require().
242
  const std::unique_ptr<ResolutionTable> resolutionTable_;
243
244
  /// The list of segment IDs, based on the user's metadata input to the
245
  /// compiler when splitting the bundle.
246
  const std::vector<uint32_t> segments_;
247
248
  /// The level of debug information we should emit. Defaults to
249
  /// DebugInfoSetting::THROWING.
250
  DebugInfoSetting debugInfoSetting_{DebugInfoSetting::THROWING};
251
252
  /// Whether to emit async break check instruction or not.
253
  bool emitAsyncBreakCheck_{false};
254
255
  CodeGenerationSettings codeGenerationSettings_;
256
257
  OptimizationSettings optimizationSettings_;
258
259
  /// The HBC backend context. We use a shared pointer to avoid any dependencies
260
  /// on its destructor.
261
  std::shared_ptr<hbc::BackendContext> hbcBackendContext_{};
262
263
#ifdef HERMES_RUN_WASM
264
  std::shared_ptr<EmitWasmIntrinsicsContext> wasmIntrinsicsContext_{};
265
#endif // HERMES_RUN_WASM
266
267
 public:
268
  explicit Context(
269
      SourceErrorManager &sm,
270
      CodeGenerationSettings codeGenOpts = CodeGenerationSettings(),
271
      OptimizationSettings optimizationOpts = OptimizationSettings(),
272
      std::unique_ptr<ResolutionTable> resolutionTable = nullptr,
273
      std::vector<uint32_t> segments = {})
274
      : sm_(sm),
275
        resolutionTable_(std::move(resolutionTable)),
276
        segments_(std::move(segments)),
277
        codeGenerationSettings_(std::move(codeGenOpts)),
278
0
        optimizationSettings_(std::move(optimizationOpts)) {}
279
280
  explicit Context(
281
      CodeGenerationSettings codeGenOpts = CodeGenerationSettings(),
282
      OptimizationSettings optimizationOpts = OptimizationSettings(),
283
      std::unique_ptr<ResolutionTable> resolutionTable = nullptr,
284
      std::vector<uint32_t> segments = {})
285
196
      : ownSm_(new SourceErrorManager()),
286
196
        sm_(*ownSm_),
287
196
        resolutionTable_(std::move(resolutionTable)),
288
196
        segments_(std::move(segments)),
289
196
        codeGenerationSettings_(std::move(codeGenOpts)),
290
196
        optimizationSettings_(std::move(optimizationOpts)) {}
291
292
  Context(const Context &) = delete;
293
  void operator=(const Context &) = delete;
294
295
807
  Allocator &getAllocator() {
296
807
    return allocator_;
297
807
  }
298
299
93.1k
  StringTable &getStringTable() {
300
93.1k
    return stringTable_;
301
93.1k
  }
302
303
  void addCompiledRegExp(
304
      UniqueString *pattern,
305
      UniqueString *flags,
306
1.82k
      CompiledRegExp &&compiled) {
307
1.82k
    compiledRegExps_.emplace(
308
1.82k
        std::make_pair(pattern, flags), std::move(compiled));
309
1.82k
  }
310
311
  CompiledRegExp &getCompiledRegExp(
312
      UniqueString *pattern,
313
2.86k
      UniqueString *flags) {
314
2.86k
    auto it = compiledRegExps_.find(std::make_pair(pattern, flags));
315
2.86k
    assert(it != compiledRegExps_.end() && "Regex hasn't been compiled");
316
2.86k
    return it->second;
317
2.86k
  }
318
319
350
  parser::PreParsedBufferInfo *getPreParsedBufferInfo(uint32_t bufferId) {
320
350
    if (!preParsed_)
321
196
      preParsed_ = std::make_unique<parser::PreParsedData>();
322
350
    return preParsed_->getBufferInfo(bufferId);
323
350
  }
324
325
25.1k
  SourceErrorManager &getSourceErrorManager() {
326
25.1k
    return sm_;
327
25.1k
  }
328
329
0
  const SourceErrorManager &getSourceErrorManager() const {
330
0
    return sm_;
331
0
  }
332
333
  /// \return the table for static require resolution, nullptr if not supplied.
334
0
  const std::vector<uint32_t> &getSegments() const {
335
0
    return segments_;
336
0
  }
337
338
  /// \return the table for static require resolution, nullptr if not supplied.
339
0
  const ResolutionTable *getResolutionTable() const {
340
0
    return resolutionTable_.get();
341
0
  }
342
343
  /// Get or create a new identifier for the string \p str. The method copies
344
  /// the content of the string.
345
377k
  Identifier getIdentifier(llvh::StringRef str) {
346
377k
    return stringTable_.getIdentifier(str);
347
377k
  }
348
349
  /// Return the textual representation of the identifier.
350
0
  llvh::StringRef toString(Identifier iden) {
351
0
    return iden.str();
352
0
  }
353
354
196
  void setStrictMode(bool strictMode) {
355
196
    strictMode_ = strictMode;
356
196
  }
357
617
  bool isStrictMode() const {
358
617
    return strictMode_;
359
617
  }
360
361
0
  bool getEnableEval() const {
362
0
    return enableEval_;
363
0
  }
364
196
  void setEnableEval(bool enableEval) {
365
196
    enableEval_ = enableEval;
366
196
  }
367
368
196
  void setDebugInfoSetting(DebugInfoSetting debugInfoSetting) {
369
196
    debugInfoSetting_ = debugInfoSetting;
370
196
  }
371
6.56M
  DebugInfoSetting getDebugInfoSetting() const {
372
6.56M
    return debugInfoSetting_;
373
6.56M
  }
374
375
196
  void setEmitAsyncBreakCheck(bool check) {
376
196
    emitAsyncBreakCheck_ = check;
377
196
  }
378
0
  bool getEmitAsyncBreakCheck() const {
379
0
    return emitAsyncBreakCheck_;
380
0
  }
381
382
  /// A hack to disable CJS modules while preserving the same interface.
383
0
  void setUseCJSModules(bool useCJSModules) {}
384
0
  bool getUseCJSModules() const {
385
0
    return false;
386
0
  }
387
  /// SemanticValidator performs some AST transformations when CommonJS modules
388
  /// are enabled. This attribute allows us to continue supporting those, while
389
  /// code generation for CJS modules has been disabled.
390
0
  void setTransformCJSModules(bool useCJSModules) {
391
0
    useCJSModules_ = useCJSModules;
392
0
  }
393
0
  bool getTransformCJSModules() const {
394
0
    return useCJSModules_;
395
0
  }
396
397
0
  void setParseJSX(bool parseJSX) {
398
0
    parseJSX_ = parseJSX;
399
0
  }
400
2
  bool getParseJSX() const {
401
2
    return parseJSX_;
402
2
  }
403
404
0
  void setParseFlow(ParseFlowSetting parseFlow) {
405
0
    parseFlow_ = parseFlow;
406
0
  }
407
22.6M
  bool getParseFlow() const {
408
22.6M
    return parseFlow_ != ParseFlowSetting::NONE;
409
22.6M
  }
410
4.91M
  bool getParseFlowAmbiguous() const {
411
4.91M
    return parseFlow_ == ParseFlowSetting::ALL;
412
4.91M
  }
413
414
0
  void setParseFlowComponentSyntax(bool parseFlowComponentSyntax) {
415
0
    parseFlowComponentSyntax_ = parseFlowComponentSyntax;
416
0
  }
417
0
  bool getParseFlowComponentSyntax() const {
418
0
    return parseFlowComponentSyntax_;
419
0
  }
420
421
0
  void setParseFlowMatch(bool parseFlowMatch) {
422
0
    parseFlowMatch_ = parseFlowMatch;
423
0
  }
424
0
  bool getParseFlowMatch() const {
425
0
    return parseFlowMatch_;
426
0
  }
427
428
0
  void setParseTS(bool parseTS) {
429
0
    parseTS_ = parseTS;
430
0
  }
431
15.3M
  bool getParseTS() const {
432
15.3M
    return parseTS_;
433
15.3M
  }
434
435
196
  void setConvertES6Classes(bool convertES6Classes) {
436
196
    convertES6Classes_ = convertES6Classes;
437
196
  }
438
439
154
  bool getConvertES6Classes() const {
440
154
#ifndef HERMES_FACEBOOK_BUILD
441
154
    return convertES6Classes_;
442
#else
443
    return false;
444
#endif
445
154
  }
446
447
  /// \return true if either TS or Flow is being parsed.
448
4.25M
  bool getParseTypes() const {
449
4.25M
    return getParseFlow() || getParseTS();
450
4.25M
  }
451
452
196
  bool isLazyCompilation() const {
453
196
    return lazyCompilation_;
454
196
  }
455
456
196
  void setLazyCompilation(bool lazyCompilation) {
457
196
    lazyCompilation_ = lazyCompilation;
458
196
  }
459
460
80
  unsigned getPreemptiveFunctionCompilationThreshold() {
461
80
    return preemptiveFunctionCompilationThreshold_;
462
80
  }
463
464
196
  void setPreemptiveFunctionCompilationThreshold(unsigned byteCount) {
465
196
    preemptiveFunctionCompilationThreshold_ = byteCount;
466
196
  };
467
468
196
  unsigned getPreemptiveFileCompilationThreshold() {
469
196
    return preemptiveFileCompilationThreshold_;
470
196
  }
471
472
196
  void setPreemptiveFileCompilationThreshold(unsigned byteCount) {
473
196
    preemptiveFileCompilationThreshold_ = byteCount;
474
196
  };
475
476
0
  bool allowReturnOutsideFunction() const {
477
0
    return allowReturnOutsideFunction_;
478
0
  }
479
480
0
  void setAllowReturnOutsideFunction(bool allowReturnOutsideFunction) {
481
0
    allowReturnOutsideFunction_ = allowReturnOutsideFunction;
482
0
  }
483
484
0
  bool isGeneratorEnabled() const {
485
0
    return generatorEnabled_;
486
0
  }
487
488
196
  void setGeneratorEnabled(bool v) {
489
196
    generatorEnabled_ = v;
490
196
  }
491
492
147
  void setStaticBuiltinOptimization(bool staticBuiltins) {
493
147
    optimizationSettings_.staticBuiltins = staticBuiltins;
494
147
  }
495
496
0
  bool getStaticBuiltinOptimization() const {
497
0
    return optimizationSettings_.staticBuiltins;
498
0
  }
499
500
0
  bool getUseUnsafeIntrinsics() const {
501
0
    return optimizationSettings_.useUnsafeIntrinsics;
502
0
  }
503
504
208k
  const CodeGenerationSettings &getCodeGenerationSettings() const {
505
208k
    return codeGenerationSettings_;
506
208k
  }
507
508
313k
  const OptimizationSettings &getOptimizationSettings() const {
509
313k
    return optimizationSettings_;
510
313k
  }
511
512
  /// Allocates AST nodes. Should not be used for non-AST data because
513
  /// the memory may be freed during parsing.
514
  template <typename T>
515
  T *allocateNode(size_t num = 1) {
516
    return allocator_.template Allocate<T>(num);
517
  }
518
11.5M
  void *allocateNode(size_t size, size_t alignment) {
519
11.5M
    return allocator_.Allocate(size, alignment);
520
11.5M
  }
521
522
46.3k
  hbc::BackendContext *getHBCBackendContext() {
523
46.3k
    return hbcBackendContext_.get();
524
46.3k
  }
525
526
  void setHBCBackendContext(
527
147
      std::shared_ptr<hbc::BackendContext> hbcBackendContext) {
528
147
    hbcBackendContext_ = std::move(hbcBackendContext);
529
147
  }
530
531
#ifdef HERMES_RUN_WASM
532
  EmitWasmIntrinsicsContext *getWasmIntrinsicsContext() {
533
    return wasmIntrinsicsContext_.get();
534
  }
535
536
  void setWasmIntrinsicsContext(
537
      std::shared_ptr<EmitWasmIntrinsicsContext> wasmIntrinsicsContext) {
538
    wasmIntrinsicsContext_ = std::move(wasmIntrinsicsContext);
539
  }
540
#endif // HERMES_RUN_WASM
541
};
542
543
} // namespace hermes
544
545
#endif // HERMES_AST_CONTEXT_H