From 58a8f963cc6854a149ef9c92ac1d459accdce37e Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Thu, 7 Mar 2024 09:15:31 -0800 Subject: [PATCH 01/21] Fix export typing Summary: `ReferencerOptions` is a type, so it has to be exported by `export type`. It will be caught in Flow 0.230.0 Changelog: [internal] Reviewed By: alexmckenley Differential Revision: D54617796 fbshipit-source-id: f143c942cb9a10fac7843812ac8ab274dc0e2355 --- .../js/hermes-eslint/src/scope-manager/referencer/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js b/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js index 1556ce413a3..271b0c3ea4b 100644 --- a/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js +++ b/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js @@ -10,4 +10,5 @@ 'use strict'; -export {Referencer, ReferencerOptions} from './Referencer'; +export {Referencer} from './Referencer'; +export type {ReferencerOptions} from './Referencer'; From 3c3d80302993e626152b0cae737e6f484a7c0cc2 Mon Sep 17 00:00:00 2001 From: Michael Leon Date: Thu, 7 Mar 2024 10:33:47 -0800 Subject: [PATCH 02/21] Prevent regex native stack overflow (#1191) Summary: Pull Request resolved: https://github.com/facebook/hermes/pull/1191 Currently, the regex executor has no bounds on the amount of recursive calls it can make. This means that the regex executor can overflow. This diff refactors the logic that the Runtime class already has for guarding against overflow into a separate class, and then uses that class in the executor. This overflow checking class uses either real native stack checking or a simple depth counter configured with a max depth allowed. The variant is chosen based on `HERMES_CHECK_NATIVE_STACK`. When stack checking is off, the depth counter is inherited from the runtime. That's defined using a set of heuristics based on platform and build mode. However, we set the max limit of the depth counter to a larger value, since the stack size of the regex executor is less than the size of the interpreter stack. Reviewed By: dannysu Differential Revision: D50425266 fbshipit-source-id: 6434e4320268e39a6857f73abc6f643dcbf12a8d --- include/hermes/Regex/Executor.h | 29 +++- include/hermes/Support/StackOverflowGuard.h | 131 ++++++++++++++++++ include/hermes/VM/Runtime.h | 111 +++++---------- lib/Regex/Executor.cpp | 52 +++++-- lib/VM/JSLib/JSLibInternal.cpp | 6 +- lib/VM/JSRegExp.cpp | 3 +- lib/VM/Runtime.cpp | 21 +-- test/hermes/regress-regexp-native-overflow.js | 17 +++ utils/testsuite/testsuite_skiplist.py | 3 + 9 files changed, 265 insertions(+), 108 deletions(-) create mode 100644 include/hermes/Support/StackOverflowGuard.h create mode 100644 test/hermes/regress-regexp-native-overflow.js diff --git a/include/hermes/Regex/Executor.h b/include/hermes/Regex/Executor.h index de6ace6f46f..e713c6eb7d7 100644 --- a/include/hermes/Regex/Executor.h +++ b/include/hermes/Regex/Executor.h @@ -10,6 +10,7 @@ #include "hermes/Regex/RegexBytecode.h" #include "hermes/Regex/RegexTypes.h" +#include "hermes/Support/StackOverflowGuard.h" // This file contains the machinery for executing a regexp compiled to bytecode. @@ -57,16 +58,25 @@ struct CapturedRange { /// Search using the compiled regex represented by \p bytecode with the flags \p /// matchFlags. If the search succeeds, populate \p captures with the capture /// groups. -/// \return true if some portion of the string matched the regex represented by -/// the bytecode, false otherwise. -/// This is the char16_t overload. +/// \param guard is used to implement stack overflow prevention. +/// \return true if some portion of the string matched the regex +/// represented by the bytecode, false otherwise. This is the char16_t overload. MatchRuntimeResult searchWithBytecode( llvh::ArrayRef bytecode, const char16_t *first, uint32_t start, uint32_t length, std::vector *captures, - constants::MatchFlagType matchFlags); + constants::MatchFlagType matchFlags, + StackOverflowGuard guard = +#ifdef HERMES_CHECK_NATIVE_STACK + StackOverflowGuard::nativeStackGuard( + 512 * 1024) // this is a conservative gap that should work in + // sanitizer builds +#else + StackOverflowGuard::depthCounterGuard(128) +#endif +); /// This is the ASCII overload. MatchRuntimeResult searchWithBytecode( @@ -75,7 +85,16 @@ MatchRuntimeResult searchWithBytecode( uint32_t start, uint32_t length, std::vector *captures, - constants::MatchFlagType matchFlags); + constants::MatchFlagType matchFlags, + StackOverflowGuard guard = +#ifdef HERMES_CHECK_NATIVE_STACK + StackOverflowGuard::nativeStackGuard( + 512 * 1024) // this is a conservative gap that should work in + // sanitizer builds +#else + StackOverflowGuard::depthCounterGuard(128) +#endif +); } // namespace regex } // namespace hermes diff --git a/include/hermes/Support/StackOverflowGuard.h b/include/hermes/Support/StackOverflowGuard.h new file mode 100644 index 00000000000..dd4561d1b9b --- /dev/null +++ b/include/hermes/Support/StackOverflowGuard.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +//===----------------------------------------------------------------------===// +/// \file +/// +/// OverflowGuard is designed to catch imminent native stack overflow. It does +/// this using two different heuristics, depending on HERMES_CHECK_NATIVE_STACK. +/// +/// If HERMES_CHECK_NATIVE_STACK is defined, it will use real stack checking. It +/// calls into platform-specific APIs to obtain the upper stack bound of the +/// currently executing thread. It will then check the current stack address +/// against the upper limit, along with some user-defined gap. Overflow is +/// reported if the current stack address is close enough to the upper bound, +/// accounting for the supplied gap value. +/// +/// If HERMES_CHECK_NATIVE_STACK is not defined, a simple depth counter is used. +/// Every time a recursive call is made, the counter should be bumped. Overflow +/// is reported if the counter reaches some user-defined max. +/// +//===----------------------------------------------------------------------===// + +#ifndef HERMES_SUPPORT_STACKOVERFLOWGUARD_H +#define HERMES_SUPPORT_STACKOVERFLOWGUARD_H + +#include +#include "hermes/Support/OSCompat.h" +#include "llvh/Support/Compiler.h" +#include "llvh/Support/raw_ostream.h" + +namespace hermes { + +#ifdef HERMES_CHECK_NATIVE_STACK + +class StackOverflowGuard { + explicit StackOverflowGuard(unsigned stackGap) : nativeStackGap(stackGap) {} + + public: + /// Upper bound on the stack, nullptr if currently unknown. + const void *nativeStackHigh{nullptr}; + /// This has already taken \c nativeStackGap into account, + /// so any stack outside [nativeStackHigh-nativeStackSize, nativeStackHigh] + /// is overflowing. + size_t nativeStackSize{0}; + /// Native stack remaining before assuming overflow. + unsigned nativeStackGap; + + static StackOverflowGuard nativeStackGuard(unsigned stackGap) { + return StackOverflowGuard(stackGap); + } + + /// \return true if the native stack is overflowing the bounds of the + /// current thread. Updates the stack bounds if the thread which Runtime + /// is executing on changes. + inline bool isOverflowing() { + // Check for overflow by subtracting the sp from the high pointer. + // If the sp is outside the valid stack range, the difference will + // be greater than the known stack size. + // This is clearly true when 0 < sp < nativeStackHigh_ - size. + // If nativeStackHigh_ < sp, then the subtraction will wrap around. + // We know that nativeStackSize_ <= nativeStackHigh_ + // (because otherwise the stack wouldn't fit in the memory), + // so the overflowed difference will be greater than nativeStackSize_. + if (LLVM_LIKELY(!( + (uintptr_t)nativeStackHigh - (uintptr_t)__builtin_frame_address(0) > + nativeStackSize))) { + // Fast path: quickly check the stored stack bounds. + // NOTE: It is possible to have a false negative here (highly unlikely). + // If the program creates many threads and destroys them, a new + // thread's stack could overlap the saved stack so we'd be checking + // against the wrong bounds. + return false; + } + // Slow path: might be overflowing, but update the stack bounds first + // in case execution has changed threads. + return isStackOverflowingSlowPath(); + } + + /// Clear the native stack bounds and force recomputation. + inline void clearStackBounds() { + nativeStackHigh = nullptr; + nativeStackSize = 0; + } + + private: + /// Slow path for \c isOverflowing. + /// Sets \c stackLow_ \c stackHigh_. + /// \return true if the native stack is overflowing the bounds of the + /// current thread. + bool isStackOverflowingSlowPath() { + auto [highPtr, size] = oscompat::thread_stack_bounds(nativeStackGap); + nativeStackHigh = (const char *)highPtr; + nativeStackSize = size; + return LLVM_UNLIKELY( + (uintptr_t)nativeStackHigh - (uintptr_t)__builtin_frame_address(0) > + nativeStackSize); + } +}; + +#else + +class StackOverflowGuard { + explicit StackOverflowGuard(size_t max) : maxCallDepth(max) {} + + public: + /// This is how many recursive calls have already been made. + /// This grows towards maxCallDepth. + size_t callDepth{0}; + /// If callDepth exceeds this value, it is considered overflow. + size_t maxCallDepth; + + static StackOverflowGuard depthCounterGuard(unsigned stackGap) { + return StackOverflowGuard(stackGap); + } + + /// \return true if \c callDepth has exceeded the budget set by \c + /// maxCallDepth. + inline bool isOverflowing() { + return callDepth > maxCallDepth; + } +}; + +#endif + +} // namespace hermes + +#endif // HERMES_SUPPORT_STACKOVERFLOWGUARD_H diff --git a/include/hermes/VM/Runtime.h b/include/hermes/VM/Runtime.h index 2d539a39c59..4342e40b744 100644 --- a/include/hermes/VM/Runtime.h +++ b/include/hermes/VM/Runtime.h @@ -12,6 +12,7 @@ #include "hermes/Public/RuntimeConfig.h" #include "hermes/Support/Compiler.h" #include "hermes/Support/ErrorHandling.h" +#include "hermes/Support/StackOverflowGuard.h" #include "hermes/VM/AllocOptions.h" #include "hermes/VM/AllocResult.h" #include "hermes/VM/BasicBlockExecutionInfo.h" @@ -549,14 +550,12 @@ class HERMES_EMPTY_BASES Runtime : public PointerBase, /// \return true if the native stack is overflowing the bounds of the /// current thread. Updates the stack bounds if the thread which Runtime - /// is executing on changes. Will use nativeCallFrameDepth_ if Runtime + /// is executing on changes. Will use simple depth counter if Runtime /// has been compiled without \c HERMES_CHECK_NATIVE_STACK. - inline bool isNativeStackOverflowing(); + inline bool isStackOverflowing(); -#ifdef HERMES_CHECK_NATIVE_STACK - /// Clear the native stack bounds and force recomputation. - inline void clearStackBounds(); -#endif + /// \return the overflow guard to be used in the regex engine. + inline StackOverflowGuard getOverflowGuardForRegex(); /// \return `thrownValue`. HermesValue getThrownValue() const { @@ -1138,13 +1137,6 @@ class HERMES_EMPTY_BASES Runtime : public PointerBase, /// Write a JS stack trace as part of a \c crashCallback() run. void crashWriteCallStack(JSONEmitter &json); - /// Out-of-line slow path for \c isNativeStackOverflowing. - /// Sets \c stackLow_ \c stackHigh_. - /// \return true if the native stack is overflowing the bounds of the - /// current thread. Will always return false when Runtime - /// has been compiled without \c HERMES_CHECK_NATIVE_STACK. - bool isNativeStackOverflowingSlowPath(); - private: GCStorage heapStorage_; @@ -1263,22 +1255,9 @@ class HERMES_EMPTY_BASES Runtime : public PointerBase, /// including \c stackPointer_. StackFramePtr currentFrame_{nullptr}; -#ifdef HERMES_CHECK_NATIVE_STACK - /// Native stack remaining before assuming overflow. - unsigned nativeStackGap_; - - /// Upper bound on the stack, nullptr if currently unknown. - const void *nativeStackHigh_{nullptr}; - - /// This has already taken \c nativeStackGap_ into account, - /// so any stack outside [nativeStackHigh_-nativeStackSize_, nativeStackHigh_] - /// is overflowing. - size_t nativeStackSize_{0}; -#else - /// Current depth of native call frames, including recursive interpreter - /// calls. - unsigned nativeCallFrameDepth_{0}; -#endif + /// Used to guard against stack overflow. Either uses real stack checking or + /// call depth counter checking. + StackOverflowGuard overflowGuard_; /// rootClazzes_[i] is a PinnedHermesValue pointing to a hidden class with /// its i first slots pre-reserved. @@ -1613,13 +1592,13 @@ class ScopedNativeDepthTracker { explicit ScopedNativeDepthTracker(Runtime &runtime) : runtime_(runtime) { (void)runtime_; #ifndef HERMES_CHECK_NATIVE_STACK - ++runtime.nativeCallFrameDepth_; + ++runtime.overflowGuard_.callDepth; #endif - overflowed_ = runtime.isNativeStackOverflowing(); + overflowed_ = runtime.isStackOverflowing(); } ~ScopedNativeDepthTracker() { #ifndef HERMES_CHECK_NATIVE_STACK - --runtime_.nativeCallFrameDepth_; + --runtime_.overflowGuard_.callDepth; #endif } @@ -1653,31 +1632,32 @@ class ScopedNativeDepthReducer { public: #ifdef HERMES_CHECK_NATIVE_STACK explicit ScopedNativeDepthReducer(Runtime &runtime) - : runtime_(runtime), nativeStackGapOld(runtime.nativeStackGap_) { + : runtime_(runtime), + nativeStackGapOld(runtime.overflowGuard_.nativeStackGap) { // Temporarily reduce the gap to use that headroom for gathering the error. // If overflow is detected, the recomputation of the stack bounds will // result in no gap for the duration of the ScopedNativeDepthReducer's // lifetime. - runtime_.nativeStackGap_ = kReducedNativeStackGap; + runtime_.overflowGuard_.nativeStackGap = kReducedNativeStackGap; } ~ScopedNativeDepthReducer() { assert( - runtime_.nativeStackGap_ == kReducedNativeStackGap && + runtime_.overflowGuard_.nativeStackGap == kReducedNativeStackGap && "ScopedNativeDepthReducer gap was overridden"); - runtime_.nativeStackGap_ = nativeStackGapOld; + runtime_.overflowGuard_.nativeStackGap = nativeStackGapOld; // Force the bounds to be recomputed the next time. - runtime_.clearStackBounds(); + runtime_.overflowGuard_.clearStackBounds(); } #else explicit ScopedNativeDepthReducer(Runtime &runtime) : runtime_(runtime) { - if (runtime.nativeCallFrameDepth_ >= kDepthAdjustment) { - runtime.nativeCallFrameDepth_ -= kDepthAdjustment; + if (runtime.overflowGuard_.callDepth >= kDepthAdjustment) { + runtime.overflowGuard_.callDepth -= kDepthAdjustment; undo = true; } } ~ScopedNativeDepthReducer() { if (undo) { - runtime_.nativeCallFrameDepth_ += kDepthAdjustment; + runtime_.overflowGuard_.callDepth += kDepthAdjustment; } } #endif @@ -1721,7 +1701,7 @@ class ScopedNativeCallFrame { Runtime &runtime, uint32_t registersNeeded) { return runtime.checkAvailableStack(registersNeeded) && - !runtime.isNativeStackOverflowing(); + !runtime.isStackOverflowing(); } public: @@ -1743,7 +1723,7 @@ class ScopedNativeCallFrame { HermesValue thisArg) : runtime_(runtime), savedSP_(runtime.getStackPointer()) { #ifndef HERMES_CHECK_NATIVE_STACK - runtime.nativeCallFrameDepth_++; + runtime.overflowGuard_.callDepth++; #endif uint32_t registersNeeded = StackFrameLayout::callerOutgoingRegisters(argCount); @@ -1799,7 +1779,7 @@ class ScopedNativeCallFrame { // Note that we unconditionally increment the native call frame depth and // save the SP to avoid branching in the dtor. #ifndef HERMES_CHECK_NATIVE_STACK - runtime_.nativeCallFrameDepth_--; + runtime_.overflowGuard_.callDepth--; #endif runtime_.popToSavedStackPointer(savedSP_); #ifndef NDEBUG @@ -2130,41 +2110,24 @@ inline llvh::iterator_range Runtime::getStackFrames() ConstStackFrameIterator{registerStackStart_}}; }; -inline bool Runtime::isNativeStackOverflowing() { -#ifdef HERMES_CHECK_NATIVE_STACK - // Check for overflow by subtracting the sp from the high pointer. - // If the sp is outside the valid stack range, the difference will - // be greater than the known stack size. - // This is clearly true when 0 < sp < nativeStackHigh_ - size. - // If nativeStackHigh_ < sp, then the subtraction will wrap around. - // We know that nativeStackSize_ <= nativeStackHigh_ - // (because otherwise the stack wouldn't fit in the memory), - // so the overflowed difference will be greater than nativeStackSize_. - bool overflowing = - (uintptr_t)nativeStackHigh_ - (uintptr_t)__builtin_frame_address(0) > - nativeStackSize_; - if (LLVM_LIKELY(!overflowing)) { - // Fast path: quickly check the stored stack bounds. - // NOTE: It is possible to have a false negative here (highly unlikely). - // If the program creates many threads and destroys them, a new - // thread's stack could overlap the saved stack so we'd be checking - // against the wrong bounds. - return false; - } - // Slow path: might be overflowing, but update the stack bounds first - // in case execution has changed threads. - return isNativeStackOverflowingSlowPath(); -#else - return nativeCallFrameDepth_ > Runtime::MAX_NATIVE_CALL_FRAME_DEPTH; +inline StackOverflowGuard Runtime::getOverflowGuardForRegex() { + StackOverflowGuard copy = overflowGuard_; +#ifndef HERMES_CHECK_NATIVE_STACK + // We should take into account the approximate difference in sizes between the + // stack size of the interpreter vs the regex executor. The max call depth in + // use here was calculated using call stack sizes of the interpreter. Since + // the executor has a smaller stack size, it should be allowed to recurse more + // than the interpreter could have. So we multiply the max allowed depth by + // some number larger than 1. + constexpr uint32_t kRegexMaxDepthMult = 5; + copy.maxCallDepth *= kRegexMaxDepthMult; #endif + return copy; } -#ifdef HERMES_CHECK_NATIVE_STACK -inline void Runtime::clearStackBounds() { - nativeStackHigh_ = nullptr; - nativeStackSize_ = 0; +inline bool Runtime::isStackOverflowing() { + return overflowGuard_.isOverflowing(); } -#endif inline ExecutionStatus Runtime::setThrownValue(HermesValue value) { thrownValue_ = value; diff --git a/lib/Regex/Executor.cpp b/lib/Regex/Executor.cpp index fb73b8e5658..55d6364455b 100644 --- a/lib/Regex/Executor.cpp +++ b/lib/Regex/Executor.cpp @@ -11,6 +11,7 @@ #include "llvh/ADT/SmallVector.h" #include "llvh/Support/TrailingObjects.h" +#include "llvh/Support/raw_ostream.h" // This file contains the machinery for executing a regexp compiled to bytecode. @@ -400,6 +401,10 @@ struct Context { /// This is effectively a timeout on the regexp execution. uint32_t backtracksRemaining_ = kBacktrackLimit; + /// Used to guard against stack overflow. Either uses real stack + /// checking or call depth counter checking. + StackOverflowGuard overflowGuard_; + Context( llvh::ArrayRef bytecodeStream, constants::MatchFlagType flags, @@ -407,14 +412,16 @@ struct Context { const CodeUnit *first, const CodeUnit *last, uint32_t markedCount, - uint32_t loopCount) + uint32_t loopCount, + StackOverflowGuard guard) : bytecodeStream_(bytecodeStream), flags_(flags), syntaxFlags_(syntaxFlags), first_(first), last_(last), markedCount_(markedCount), - loopCount_(loopCount) {} + loopCount_(loopCount), + overflowGuard_(guard) {} /// Run the given State \p state, by starting at its cursor and acting on its /// ip_ until the match succeeds or fails. If \p onlyAtStart is set, only @@ -467,6 +474,15 @@ struct Context { BacktrackStack &bts); private: + /// \return true if the native stack is overflowing. This will either use real + /// stack checking or a simple depth counter. + bool isStackOverflowing() { +#ifndef HERMES_CHECK_NATIVE_STACK + overflowGuard_.callDepth++; +#endif + return overflowGuard_.isOverflowing(); + } + /// Do initialization of the given state before it enters the loop body /// described by the LoopInsn \p loop, including setting up any backtracking /// state. @@ -990,6 +1006,12 @@ auto Context::match(State *s, bool onlyAtStart) (c.forwards() || locsToCheckCount == 1) && "Can only check one location when cursor is backwards"); + // Make sure we are not exceeding the set limit of the amount of times we can + // recurse. + if (isStackOverflowing()) { + return ExecutionStatus::STACK_OVERFLOW; + } + // Macro used when a state fails to match. #define BACKTRACK() \ do { \ @@ -1317,9 +1339,13 @@ auto Context::match(State *s, bool onlyAtStart) // anything. s->ip_ += sizeof(LookaroundInsn); auto match = this->match(s, true /* onlyAtStart */); - // There were no errors and we matched something (so non-null - // return) - matched = match && match.getValue(); + // If the match errored out due to stack overflow, then we need to + // return an error here as well. + if (LLVM_UNLIKELY(!match)) { + return match.getStatus(); + } + // We got a match if the value is non-null. + matched = match.getValue() != nullptr; c.setCurrentPointer(savedState.cursor_.currentPointer()); c.setForwards(savedState.cursor_.forwards()); @@ -1514,7 +1540,8 @@ MatchRuntimeResult searchWithBytecodeImpl( uint32_t start, uint32_t length, std::vector *m, - constants::MatchFlagType matchFlags) { + constants::MatchFlagType matchFlags, + StackOverflowGuard guard) { assert( bytecode.size() >= sizeof(RegexBytecodeHeader) && "Bytecode too small"); auto header = reinterpret_cast(bytecode.data()); @@ -1535,7 +1562,8 @@ MatchRuntimeResult searchWithBytecodeImpl( first, first + length, header->markedCount, - header->loopCount); + header->loopCount, + guard); State state{cursor, markedCount, loopCount}; // We check only one location if either the regex pattern constrains us to, or @@ -1571,9 +1599,10 @@ MatchRuntimeResult searchWithBytecode( uint32_t start, uint32_t length, std::vector *m, - constants::MatchFlagType matchFlags) { + constants::MatchFlagType matchFlags, + StackOverflowGuard guard) { return searchWithBytecodeImpl( - bytecode, first, start, length, m, matchFlags); + bytecode, first, start, length, m, matchFlags, guard); } MatchRuntimeResult searchWithBytecode( @@ -1582,9 +1611,10 @@ MatchRuntimeResult searchWithBytecode( uint32_t start, uint32_t length, std::vector *m, - constants::MatchFlagType matchFlags) { + constants::MatchFlagType matchFlags, + StackOverflowGuard guard) { return searchWithBytecodeImpl( - bytecode, first, start, length, m, matchFlags); + bytecode, first, start, length, m, matchFlags, guard); } } // namespace regex diff --git a/lib/VM/JSLib/JSLibInternal.cpp b/lib/VM/JSLib/JSLibInternal.cpp index 4ea3d7400cb..7e558d7675d 100644 --- a/lib/VM/JSLib/JSLibInternal.cpp +++ b/lib/VM/JSLib/JSLibInternal.cpp @@ -290,7 +290,8 @@ static bool isReturnThis(Handle str, Runtime &runtime) { 0, input.length(), nullptr, - regex::constants::matchDefault | regex::constants::matchInputAllAscii); + regex::constants::matchDefault | regex::constants::matchInputAllAscii, + runtime.getOverflowGuardForRegex()); } else { const char16_t *begin = input.castToChar16Ptr(); result = regex::searchWithBytecode( @@ -299,7 +300,8 @@ static bool isReturnThis(Handle str, Runtime &runtime) { 0, input.length(), nullptr, - regex::constants::matchDefault); + regex::constants::matchDefault, + runtime.getOverflowGuardForRegex()); } return result == regex::MatchRuntimeResult::Match; } diff --git a/lib/VM/JSRegExp.cpp b/lib/VM/JSRegExp.cpp index 3f66da3ffad..1ac3ec96c7a 100644 --- a/lib/VM/JSRegExp.cpp +++ b/lib/VM/JSRegExp.cpp @@ -272,7 +272,8 @@ CallResult performSearch( searchStartOffset, stringLength, &nativeMatchRanges, - matchFlags); + matchFlags, + runtime.getOverflowGuardForRegex()); if (matchResult == regex::MatchRuntimeResult::StackOverflow) { return runtime.raiseRangeError("Maximum regex stack depth reached"); } else if (matchResult == regex::MatchRuntimeResult::NoMatch) { diff --git a/lib/VM/Runtime.cpp b/lib/VM/Runtime.cpp index 2b44b6a91ab..e002d546e0a 100644 --- a/lib/VM/Runtime.cpp +++ b/lib/VM/Runtime.cpp @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +#include "hermes/Support/StackOverflowGuard.h" #define DEBUG_TYPE "vm" #include "hermes/VM/Runtime.h" @@ -265,9 +266,12 @@ Runtime::Runtime( stackPointer_(), crashMgr_(runtimeConfig.getCrashMgr()), #ifdef HERMES_CHECK_NATIVE_STACK - nativeStackGap_(std::max( + overflowGuard_(StackOverflowGuard::nativeStackGuard(std::max( runtimeConfig.getNativeStackGap(), - kMinSupportedNativeStackGap)), + kMinSupportedNativeStackGap))), +#else + overflowGuard_(StackOverflowGuard::depthCounterGuard( + Runtime::MAX_NATIVE_CALL_FRAME_DEPTH)), #endif crashCallbackKey_( crashMgr_->registerCallback([this](int fd) { crashCallback(fd); })), @@ -1962,19 +1966,6 @@ static std::string &llvmStreamableToString(const T &v) { return buf; } -bool Runtime::isNativeStackOverflowingSlowPath() { -#ifdef HERMES_CHECK_NATIVE_STACK - auto [highPtr, size] = oscompat::thread_stack_bounds(nativeStackGap_); - nativeStackHigh_ = (const char *)highPtr; - nativeStackSize_ = size; - return LLVM_UNLIKELY( - (uintptr_t)nativeStackHigh_ - (uintptr_t)__builtin_frame_address(0) > - nativeStackSize_); -#else - return false; -#endif -} - /**************************************************************************** * WARNING: This code is run after a crash. Avoid walking data structures, * doing memory allocation, or using libc etc. as much as possible diff --git a/test/hermes/regress-regexp-native-overflow.js b/test/hermes/regress-regexp-native-overflow.js new file mode 100644 index 00000000000..082ee1da9d3 --- /dev/null +++ b/test/hermes/regress-regexp-native-overflow.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// RUN: %hermes -O %s | %FileCheck --match-full-lines %s + +// Check Regex doesn't crash due to recursing too many times in native code. + +try { + var res = new RegExp("(?=".repeat(100000) + "A" + ")".repeat(100000)).exec('b'); +} catch (e) { + print(e); + // CHECK: RangeError: Maximum regex stack depth reached +} diff --git a/utils/testsuite/testsuite_skiplist.py b/utils/testsuite/testsuite_skiplist.py index f1ce1ec8123..5a696d0d089 100644 --- a/utils/testsuite/testsuite_skiplist.py +++ b/utils/testsuite/testsuite_skiplist.py @@ -1885,6 +1885,9 @@ "mjsunit/regress/regress-585775.js", "test262/test/annexB/built-ins/RegExp/prototype/compile/", "test262/test/annexB/built-ins/RegExp/prototype/flags/order-after-compile", + # Tests long regexes that exceed our regex stack limit + "mjsunit/regress/regress-231.js", + "mjsunit/third_party/regexp-pcre/regexp-pcre.js", # annexB "mjsunit/es6/string-html.js", "mjsunit/function-names.js", From d86a2713f0d8355aaf1acc960d1effea9430a89d Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Thu, 7 Mar 2024 14:35:29 -0800 Subject: [PATCH 03/21] Speedup flow check Summary: This diff does various tricks to speed up flow checking of the JS codebase of hermes parser. The check time goes from 14s to 4s on my machine as a result. 1. Inline all the predicate checks in predicates.js. Somehow the import checking is very expensive. This causes the biggest drop in checking time, since 13s is spent on this file before. 2. Replaced some suppressions of some hopelessly typed code with just cast to $FlowFixMe Unrelated to perf improvements, I also re-run the generator and added the missing AsConstExpression type. I also converted the predicates.js file to use type guards. Changelog: [internal] Reviewed By: pieterv Differential Revision: D54646080 fbshipit-source-id: 4edac6078caac57a57b39906adcf7fb89821d2bf --- .../hermes-estree/src/generated/predicates.js | 18 +- .../js/hermes-estree/src/predicates.js | 336 ++-- .../js/hermes-estree/src/types.js | 5 + .../src/HermesParserNodeDeserializers.js | 10 +- .../src/generated/ESTreeVisitorKeys.js | 3 +- .../src/generated/ParserVisitorKeys.js | 5 +- .../src/transform/astNodeMutationHelpers.js | 9 +- .../js/hermes-transform/src/detachedNode.js | 15 +- .../src/generated/node-types.js | 1750 ++++++++++------- .../src/transform/TransformContext.js | 55 +- .../src/transform/mutations/ReplaceNode.js | 16 +- .../hermes-transform/src/traverse/traverse.js | 4 +- .../js/scripts/genPredicateFunctions.js | 10 +- .../js/scripts/genTransformNodeTypes.js | 8 +- .../js/scripts/utils/scriptUtils.js | 2 +- 15 files changed, 1223 insertions(+), 1023 deletions(-) diff --git a/tools/hermes-parser/js/hermes-estree/src/generated/predicates.js b/tools/hermes-parser/js/hermes-estree/src/generated/predicates.js index b9d5680455d..3aac466955c 100644 --- a/tools/hermes-parser/js/hermes-estree/src/generated/predicates.js +++ b/tools/hermes-parser/js/hermes-estree/src/generated/predicates.js @@ -16,7 +16,7 @@ // lint directives to let us do some basic validation of generated files /* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */ -/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray */ +/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */ 'use strict'; @@ -24,6 +24,14 @@ import type { ESNode, Token, + AFunction, + ClassMember, + BigIntLiteral, + BooleanLiteral, + NullLiteral, + NumericLiteral, + RegExpLiteral, + StringLiteral, Identifier, JSXIdentifier, JSXText, @@ -32,6 +40,7 @@ import type { ArrayPattern, ArrayTypeAnnotation, ArrowFunctionExpression, + AsConstExpression, AsExpression, AssignmentExpression, AssignmentPattern, @@ -199,7 +208,7 @@ import type { LineComment, BlockComment, MostTokens, -} from 'hermes-estree'; +} from '../types'; */ @@ -243,6 +252,11 @@ export function isArrowFunctionExpression(node /*: ESNode | Token */) /*: node i } +export function isAsConstExpression(node /*: ESNode | Token */) /*: node is AsConstExpression */ { + return node.type === 'AsConstExpression'; +} + + export function isAsExpression(node /*: ESNode | Token */) /*: node is AsExpression */ { return node.type === 'AsExpression'; } diff --git a/tools/hermes-parser/js/hermes-estree/src/predicates.js b/tools/hermes-parser/js/hermes-estree/src/predicates.js index 2dac40707a6..76d6cae5cbd 100644 --- a/tools/hermes-parser/js/hermes-estree/src/predicates.js +++ b/tools/hermes-parser/js/hermes-estree/src/predicates.js @@ -5,254 +5,198 @@ * LICENSE file in the root directory of this source tree. * * @flow strict-local - * @format */ 'use strict'; -import type {ESNode, Token} from './types'; - -import { - isArrayExpression, - isArrowFunctionExpression, - isAsExpression, - isAssignmentExpression, - isAwaitExpression, - isBinaryExpression, - isBlockComment, - isBlockStatement, - isBreakStatement, - isCallExpression, - isChainExpression, - isClassDeclaration, - isClassExpression, - isConditionalExpression, - isContinueStatement, - isDebuggerStatement, - isDeclareClass, - isDeclareFunction, - isDeclareInterface, - isDeclareModule, - isDeclareOpaqueType, - isDeclareTypeAlias, - isDeclareVariable, - isDoWhileStatement, - isEmptyStatement, - isEnumDeclaration, - isExpressionStatement, - isForInStatement, - isForOfStatement, - isForStatement, - isFunctionDeclaration, - isFunctionExpression, - isIdentifier, - isIfStatement, - isImportExpression, - isInterfaceDeclaration, - isJSXElement, - isJSXFragment, - isLabeledStatement, - isLineComment, - isLiteral, - isLogicalExpression, - isMemberExpression, - isMetaProperty, - isMethodDefinition, - isNewExpression, - isObjectExpression, - isOpaqueType, - isProperty, - isPropertyDefinition, - isReturnStatement, - isSequenceExpression, - isSwitchStatement, - isTaggedTemplateExpression, - isTemplateLiteral, - isThisExpression, - isThrowStatement, - isTryStatement, - isTypeAlias, - isTypeCastExpression, - isUnaryExpression, - isUpdateExpression, - isVariableDeclaration, - isWhileStatement, - isWithStatement, - isYieldExpression, -} from './generated/predicates'; +/*:: +import type { + ESNode, + Token, + MostTokens, + BlockComment, + LineComment, + AFunction, + PropertyDefinition, + PropertyDefinitionWithNonComputedName, + MethodDefinition, + MethodDefinitionConstructor, + MethodDefinitionWithNonComputedName, + MemberExpression, + MemberExpressionWithNonComputedName, + ObjectPropertyWithShorthandStaticName, + ObjectPropertyWithNonShorthandStaticName, + DestructuringObjectPropertyWithShorthandStaticName, + DestructuringObjectPropertyWithNonShorthandStaticName, + ClassMember, + ClassDeclaration, + ClassExpression, + Literal, + BigIntLiteral, + BooleanLiteral, + NullLiteral, + NumericLiteral, + RegExpLiteral, + StringLiteral, + Identifier, + EnumDefaultedMember, + Expression, + Statement, +} from './types'; +*/ export * from './generated/predicates'; -// $FlowFixMe[deprecated-type] -export function isClass(node: ESNode): boolean %checks { - return isClassDeclaration(node) || isClassExpression(node); +export function isClass(node /*: ESNode */) /*: node is (ClassDeclaration | ClassExpression) */ { + return node.type === 'ClassDeclaration' || node.type === 'ClassExpression'; } export function isPropertyDefinitionWithNonComputedName( - node: ESNode, - // $FlowFixMe[deprecated-type] -): boolean %checks { - return isPropertyDefinition(node) && node.computed === false; + node /*: ESNode */, +) /*: node is PropertyDefinitionWithNonComputedName */ { + return node.type === 'PropertyDefinition' && node.computed === false; } -// $FlowFixMe[deprecated-type] -export function isClassMember(node: ESNode): boolean %checks { - return isPropertyDefinition(node) || isMethodDefinition(node); +export function isClassMember(node /*: ESNode */) /*: node is ClassMember */ { + return node.type === 'PropertyDefinition' || node.type === 'MethodDefinition'; } export function isClassMemberWithNonComputedName( - node: ESNode, - // $FlowFixMe[deprecated-type] -): boolean %checks { - return isClassMember(node) && node.computed === false; + node /*: ESNode */, +) /*: node is (PropertyDefinitionWithNonComputedName | MethodDefinitionConstructor | MethodDefinitionWithNonComputedName) */ { + return (node.type === 'PropertyDefinition' || node.type === 'MethodDefinition') && node.computed === false; } -// $FlowFixMe[deprecated-type] -export function isComment(node: ESNode | Token): boolean %checks { - return isBlockComment(node) || isLineComment(node); +export function isComment(node /*: ESNode | Token */) /*: node is (MostTokens | BlockComment | LineComment) */ { + return node.type === 'Block' || node.type === 'Line'; } -// $FlowFixMe[deprecated-type] -export function isFunction(node: ESNode): boolean %checks { +export function isFunction(node /*: ESNode */) /*: node is AFunction */ { return ( - isArrowFunctionExpression(node) || - isFunctionDeclaration(node) || - isFunctionExpression(node) + node.type === 'ArrowFunctionExpression' || + node.type === 'FunctionDeclaration' || + node.type === 'FunctionExpression' ); } export function isMethodDefinitionWithNonComputedName( - node: ESNode, - // $FlowFixMe[deprecated-type] -): boolean %checks { - return isMethodDefinition(node) && node.computed === false; + node /*: ESNode */, +) /*: node is (MethodDefinitionConstructor | MethodDefinitionWithNonComputedName) */ { + return node.type === 'MethodDefinition' && node.computed === false; } export function isMemberExpressionWithNonComputedProperty( - node: ESNode, - // $FlowFixMe[deprecated-type] -): boolean %checks { - return isMemberExpression(node) && node.computed === false; + node /*: ESNode */, +) /*: node is MemberExpressionWithNonComputedName */ { + return node.type === 'MemberExpression' && node.computed === false; } export function isOptionalMemberExpressionWithNonComputedProperty( - node: ESNode, - // $FlowFixMe[deprecated-type] -): boolean %checks { - return isMemberExpression(node) && node.computed === false; + node /*: ESNode */, +) /*: node is MemberExpressionWithNonComputedName */ { + return node.type === 'MemberExpression' && node.computed === false; } -// $FlowFixMe[deprecated-type] -export function isObjectPropertyWithShorthand(node: ESNode): boolean %checks { - return isProperty(node) && node.shorthand === true; +export function isObjectPropertyWithShorthand(node /*: ESNode */) /*: node is (ObjectPropertyWithShorthandStaticName | DestructuringObjectPropertyWithShorthandStaticName) */ { + return node.type === 'Property' && node.shorthand === true; } -export function isObjectPropertyWithNonComputedName( - node: ESNode, - // $FlowFixMe[deprecated-type] -): boolean %checks { - return isProperty(node) && node.computed === false; +export function isObjectPropertyWithNonComputedName(node /*: ESNode */) /*: node is (ObjectPropertyWithNonShorthandStaticName | ObjectPropertyWithShorthandStaticName | DestructuringObjectPropertyWithNonShorthandStaticName | DestructuringObjectPropertyWithShorthandStaticName) */ { + return node.type === 'Property' && node.computed === false; } -// $FlowFixMe[deprecated-type] -export function isBigIntLiteral(node: ESNode): boolean %checks { - return isLiteral(node) && node.literalType === 'bigint'; +export function isBigIntLiteral(node /*: ESNode */) /*: node is BigIntLiteral */ { + return node.type === 'Literal' && node.literalType === 'bigint'; } -// $FlowFixMe[deprecated-type] -export function isBooleanLiteral(node: ESNode): boolean %checks { - return isLiteral(node) && node.literalType === 'boolean'; +export function isBooleanLiteral(node /*: ESNode */) /*: node is BooleanLiteral */ { + return node.type === 'Literal' && node.literalType === 'boolean'; } -// $FlowFixMe[deprecated-type] -export function isNullLiteral(node: ESNode): boolean %checks { - return isLiteral(node) && node.literalType === 'null'; +export function isNullLiteral(node /*: ESNode */) /*: node is NullLiteral */ { + return node.type === 'Literal' && node.literalType === 'null'; } -// $FlowFixMe[deprecated-type] -export function isNumericLiteral(node: ESNode): boolean %checks { - return isLiteral(node) && node.literalType === 'numeric'; +export function isNumericLiteral(node /*: ESNode */) /*: node is NumericLiteral */ { + return node.type === 'Literal' && node.literalType === 'numeric'; } -// $FlowFixMe[deprecated-type] -export function isRegExpLiteral(node: ESNode): boolean %checks { - return isLiteral(node) && node.literalType === 'regexp'; +export function isRegExpLiteral(node /*: ESNode */) /*: node is RegExpLiteral */ { + return node.type === 'Literal' && node.literalType === 'regexp'; } -// $FlowFixMe[deprecated-type] -export function isStringLiteral(node: ESNode): boolean %checks { - return isLiteral(node) && node.literalType === 'string'; +export function isStringLiteral(node /*: ESNode */) /*: node is StringLiteral */ { + return node.type === 'Literal' && node.literalType === 'string'; } -// $FlowFixMe[deprecated-type] -export function isExpression(node: ESNode): boolean %checks { +export function isExpression(node /*: ESNode */) /*: node is Expression */ { return ( - isThisExpression(node) || - isArrayExpression(node) || - isObjectExpression(node) || - isFunctionExpression(node) || - isArrowFunctionExpression(node) || - isYieldExpression(node) || - isLiteral(node) || - isUnaryExpression(node) || - isUpdateExpression(node) || - isBinaryExpression(node) || - isAssignmentExpression(node) || - isLogicalExpression(node) || - isMemberExpression(node) || - isConditionalExpression(node) || - isCallExpression(node) || - isNewExpression(node) || - isSequenceExpression(node) || - isTemplateLiteral(node) || - isTaggedTemplateExpression(node) || - isClassExpression(node) || - isMetaProperty(node) || - isIdentifier(node) || - isAwaitExpression(node) || - isImportExpression(node) || - isChainExpression(node) || - isTypeCastExpression(node) || - isAsExpression(node) || - isJSXFragment(node) || - isJSXElement(node) + node.type === 'ThisExpression' || + node.type === 'ArrayExpression' || + node.type === 'ObjectExpression' || + node.type === 'ObjectExpression' || + node.type === 'FunctionExpression' || + node.type === 'ArrowFunctionExpression' || + node.type === 'YieldExpression' || + node.type === 'Literal' || + node.type === 'UnaryExpression' || + node.type === 'UpdateExpression' || + node.type === 'BinaryExpression' || + node.type === 'AssignmentExpression' || + node.type === 'LogicalExpression' || + node.type === 'MemberExpression' || + node.type === 'ConditionalExpression' || + node.type === 'CallExpression' || + node.type === 'NewExpression' || + node.type === 'SequenceExpression' || + node.type === 'TemplateLiteral' || + node.type === 'TaggedTemplateExpression' || + node.type === 'ClassExpression' || + node.type === 'MetaProperty' || + node.type === 'Identifier' || + node.type === 'AwaitExpression' || + node.type === 'ImportExpression' || + node.type === 'ChainExpression' || + node.type === 'TypeCastExpression' || + node.type === 'AsExpression' || + node.type === 'JSXFragment' || + node.type === 'JSXElement' ); } -// $FlowFixMe[deprecated-type] -export function isStatement(node: ESNode): boolean %checks { +export function isStatement(node /*: ESNode */) /*: node is Statement */ { return ( - isBlockStatement(node) || - isBreakStatement(node) || - isClassDeclaration(node) || - isContinueStatement(node) || - isDebuggerStatement(node) || - isDeclareClass(node) || - isDeclareVariable(node) || - isDeclareFunction(node) || - isDeclareInterface(node) || - isDeclareModule(node) || - isDeclareOpaqueType(node) || - isDeclareTypeAlias(node) || - isDoWhileStatement(node) || - isEmptyStatement(node) || - isEnumDeclaration(node) || - isExpressionStatement(node) || - isForInStatement(node) || - isForOfStatement(node) || - isForStatement(node) || - isFunctionDeclaration(node) || - isIfStatement(node) || - isInterfaceDeclaration(node) || - isLabeledStatement(node) || - isOpaqueType(node) || - isReturnStatement(node) || - isSwitchStatement(node) || - isThrowStatement(node) || - isTryStatement(node) || - isTypeAlias(node) || - isVariableDeclaration(node) || - isWhileStatement(node) || - isWithStatement(node) + node.type === 'BlockStatement' || + node.type === 'BreakStatement' || + node.type === 'ClassDeclaration' || + node.type === 'ContinueStatement' || + node.type === 'DebuggerStatement' || + node.type === 'DeclareClass' || + node.type === 'DeclareVariable' || + node.type === 'DeclareFunction' || + node.type === 'DeclareInterface' || + node.type === 'DeclareModule' || + node.type === 'DeclareOpaqueType' || + node.type === 'DeclareTypeAlias' || + node.type === 'DoWhileStatement' || + node.type === 'EmptyStatement' || + node.type === 'EnumDeclaration' || + node.type === 'ExpressionStatement' || + node.type === 'ForInStatement' || + node.type === 'ForOfStatement' || + node.type === 'ForStatement' || + node.type === 'FunctionDeclaration' || + node.type === 'IfStatement' || + node.type === 'InterfaceDeclaration' || + node.type === 'LabeledStatement' || + node.type === 'OpaqueType' || + node.type === 'ReturnStatement' || + node.type === 'SwitchStatement' || + node.type === 'ThrowStatement' || + node.type === 'TryStatement' || + node.type === 'TypeAlias' || + node.type === 'VariableDeclaration' || + node.type === 'WhileStatement' || + node.type === 'WithStatement' ); } diff --git a/tools/hermes-parser/js/hermes-estree/src/types.js b/tools/hermes-parser/js/hermes-estree/src/types.js index 431990b0b99..ada4aad4346 100644 --- a/tools/hermes-parser/js/hermes-estree/src/types.js +++ b/tools/hermes-parser/js/hermes-estree/src/types.js @@ -465,6 +465,7 @@ export type Expression = | ChainExpression | TypeCastExpression | AsExpression + | AsConstExpression | JSXFragment | JSXElement; @@ -1536,6 +1537,10 @@ export interface AsExpression extends BaseNode { +expression: Expression; +typeAnnotation: TypeAnnotationType; } +export interface AsConstExpression extends BaseNode { + +type: 'AsConstExpression'; + +expression: AsConstExpression; +} interface BaseInterfaceNode extends BaseNode { +body: ObjectTypeAnnotation; diff --git a/tools/hermes-parser/js/hermes-parser/src/HermesParserNodeDeserializers.js b/tools/hermes-parser/js/hermes-parser/src/HermesParserNodeDeserializers.js index 9a8ae6fb074..75e8d462a75 100644 --- a/tools/hermes-parser/js/hermes-parser/src/HermesParserNodeDeserializers.js +++ b/tools/hermes-parser/js/hermes-parser/src/HermesParserNodeDeserializers.js @@ -17,7 +17,7 @@ // lint directives to let us do some basic validation of generated files /* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */ -/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray */ +/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */ 'use strict'; @@ -1468,6 +1468,13 @@ function deserializeAsExpression() { typeAnnotation: this.deserializeNode(), }; } +function deserializeAsConstExpression() { + return { + type: 'AsConstExpression', + loc: this.addEmptyLoc(), + expression: this.deserializeNode(), + }; +} function deserializeInferredPredicate() { return {type: 'InferredPredicate', loc: this.addEmptyLoc()}; } @@ -2132,6 +2139,7 @@ module.exports = [ deserializeTypeParameterInstantiation, deserializeTypeCastExpression, deserializeAsExpression, + deserializeAsConstExpression, deserializeInferredPredicate, deserializeDeclaredPredicate, deserializeEnumDeclaration, diff --git a/tools/hermes-parser/js/hermes-parser/src/generated/ESTreeVisitorKeys.js b/tools/hermes-parser/js/hermes-parser/src/generated/ESTreeVisitorKeys.js index 655ee1a0bf2..0e748459ba9 100644 --- a/tools/hermes-parser/js/hermes-parser/src/generated/ESTreeVisitorKeys.js +++ b/tools/hermes-parser/js/hermes-parser/src/generated/ESTreeVisitorKeys.js @@ -17,7 +17,7 @@ // lint directives to let us do some basic validation of generated files /* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */ -/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray */ +/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */ 'use strict'; @@ -34,6 +34,7 @@ module.exports = { 'returnType', 'predicate', ], + AsConstExpression: ['expression'], AsExpression: ['expression', 'typeAnnotation'], AssignmentExpression: ['left', 'right'], AssignmentPattern: ['left', 'right'], diff --git a/tools/hermes-parser/js/hermes-parser/src/generated/ParserVisitorKeys.js b/tools/hermes-parser/js/hermes-parser/src/generated/ParserVisitorKeys.js index 2367155e278..ee400321fd7 100644 --- a/tools/hermes-parser/js/hermes-parser/src/generated/ParserVisitorKeys.js +++ b/tools/hermes-parser/js/hermes-parser/src/generated/ParserVisitorKeys.js @@ -17,7 +17,7 @@ // lint directives to let us do some basic validation of generated files /* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */ -/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray */ +/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */ 'use strict'; @@ -43,6 +43,9 @@ export const HERMES_AST_VISITOR_KEYS = { returnType: 'Node', predicate: 'Node', }, + AsConstExpression: { + expression: 'Node', + }, AsExpression: { expression: 'Node', typeAnnotation: 'Node', diff --git a/tools/hermes-parser/js/hermes-parser/src/transform/astNodeMutationHelpers.js b/tools/hermes-parser/js/hermes-parser/src/transform/astNodeMutationHelpers.js index eb3b51bd391..1786a9a52d4 100644 --- a/tools/hermes-parser/js/hermes-parser/src/transform/astNodeMutationHelpers.js +++ b/tools/hermes-parser/js/hermes-parser/src/transform/astNodeMutationHelpers.js @@ -171,14 +171,13 @@ export function nodeWith( // Check if this will actually result in a change, maintaining referential equality is important. const willBeUnchanged = Object.entries(overrideProps).every( ([key, value]) => { + const node_: $FlowFixMe = node; if (Array.isArray(value)) { - // $FlowExpectedError[prop-missing] - return Array.isArray(node[key]) - ? arrayIsEqual(node[key], value) + return Array.isArray(node_[key]) + ? arrayIsEqual(node_[key], value) : false; } - // $FlowExpectedError[prop-missing] - return node[key] === value; + return node_[key] === value; }, ); if (willBeUnchanged) { diff --git a/tools/hermes-parser/js/hermes-transform/src/detachedNode.js b/tools/hermes-parser/js/hermes-transform/src/detachedNode.js index 0f39cca3e9e..4a047e784ce 100644 --- a/tools/hermes-parser/js/hermes-transform/src/detachedNode.js +++ b/tools/hermes-parser/js/hermes-transform/src/detachedNode.js @@ -65,7 +65,7 @@ export const asDetachedNode: { return null; } - if (isDetachedNode(node)) { + if (isDetachedNode((node: MaybeDetachedNode))) { return node; } @@ -77,7 +77,7 @@ export const asDetachedNode: { // used by the node type function codegen export function detachedProps( parent: ?ESNode, - props: $ReadOnly>, + props: {...}, config: DetachConfig = {}, ): DetachedNode { // $FlowExpectedError[incompatible-type] @@ -144,13 +144,12 @@ export function detachedProps( */ export function shallowCloneNode( node: T, - newProps: $ReadOnly>, + newProps: {...}, config?: DetachConfig = {}, ): DetachedNode { - return detachedProps( + return detachedProps( null, - // $FlowFixMe[cannot-spread-interface] - {...node, ...newProps}, + {...(node: $FlowFixMe), ...newProps}, { preserveLocation: config.preserveLocation ?? true, originalNode: config.originalNode ?? node, @@ -163,7 +162,7 @@ export function shallowCloneNode( */ export function deepCloneNode( node: T, - newProps: $ReadOnly>, + newProps: {...}, ): DetachedNode { const clone: DetachedNode = Object.assign( JSON.parse( @@ -181,7 +180,7 @@ export function deepCloneNode( updateAllParentPointers(clone); // $FlowExpectedError[class-object-subtyping] - return detachedProps(null, clone); + return detachedProps(null, clone); } /** diff --git a/tools/hermes-parser/js/hermes-transform/src/generated/node-types.js b/tools/hermes-parser/js/hermes-transform/src/generated/node-types.js index e918f9a9dd6..2447deacc8b 100644 --- a/tools/hermes-parser/js/hermes-transform/src/generated/node-types.js +++ b/tools/hermes-parser/js/hermes-transform/src/generated/node-types.js @@ -17,7 +17,7 @@ // lint directives to let us do some basic validation of generated files /* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */ -/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray */ +/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */ 'use strict'; @@ -27,6 +27,7 @@ import type { ArrayExpression as ArrayExpressionType, ArrayPattern as ArrayPatternType, ArrayTypeAnnotation as ArrayTypeAnnotationType, + AsConstExpression as AsConstExpressionType, AsExpression as AsExpressionType, AssignmentExpression as AssignmentExpressionType, AssignmentPattern as AssignmentPatternType, @@ -213,6 +214,10 @@ export type ArrayTypeAnnotationProps = { +elementType: MaybeDetachedNode, }; +export type AsConstExpressionProps = { + +expression: MaybeDetachedNode, +}; + export type AsExpressionProps = { +expression: MaybeDetachedNode, +typeAnnotation: MaybeDetachedNode, @@ -1144,110 +1149,137 @@ export function AnyTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'AnyTypeAnnotation', }); } export function ArrayExpression(props: { - ...$ReadOnly, + ...ArrayExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ArrayExpression', elements: props.elements.map(n => asDetachedNodeForCodeGen(n)), trailingComma: props.trailingComma, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ArrayPattern(props: { - ...$ReadOnly, + ...ArrayPatternProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ArrayPattern', elements: props.elements.map(n => asDetachedNodeForCodeGen(n)), typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ArrayTypeAnnotation(props: { - ...$ReadOnly, + ...ArrayTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ArrayTypeAnnotation', - elementType: asDetachedNodeForCodeGen(props.elementType), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ArrayTypeAnnotation', + elementType: asDetachedNodeForCodeGen(props.elementType), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); + return node; +} + +export function AsConstExpression(props: { + ...AsConstExpressionProps, + +parent?: ESNode, +}): DetachedNode { + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'AsConstExpression', + expression: asDetachedNodeForCodeGen(props.expression), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function AsExpression(props: { - ...$ReadOnly, + ...AsExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'AsExpression', expression: asDetachedNodeForCodeGen(props.expression), typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function AssignmentExpression(props: { - ...$ReadOnly, + ...AssignmentExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'AssignmentExpression', - operator: props.operator, - left: asDetachedNodeForCodeGen(props.left), - right: asDetachedNodeForCodeGen(props.right), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'AssignmentExpression', + operator: props.operator, + left: asDetachedNodeForCodeGen(props.left), + right: asDetachedNodeForCodeGen(props.right), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function AssignmentPattern(props: { - ...$ReadOnly, + ...AssignmentPatternProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'AssignmentPattern', - left: asDetachedNodeForCodeGen(props.left), - right: asDetachedNodeForCodeGen(props.right), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'AssignmentPattern', + left: asDetachedNodeForCodeGen(props.left), + right: asDetachedNodeForCodeGen(props.right), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function AwaitExpression(props: { - ...$ReadOnly, + ...AwaitExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'AwaitExpression', argument: asDetachedNodeForCodeGen(props.argument), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function BigIntLiteralTypeAnnotation(props: { - ...$ReadOnly, + ...BigIntLiteralTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'BigIntLiteralTypeAnnotation', - raw: props.raw, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'BigIntLiteralTypeAnnotation', + raw: props.raw, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -1256,47 +1288,50 @@ export function BigIntTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'BigIntTypeAnnotation', }); } export function BinaryExpression(props: { - ...$ReadOnly, + ...BinaryExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'BinaryExpression', left: asDetachedNodeForCodeGen(props.left), right: asDetachedNodeForCodeGen(props.right), operator: props.operator, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function BlockStatement(props: { - ...$ReadOnly, + ...BlockStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'BlockStatement', body: props.body.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function BooleanLiteralTypeAnnotation(props: { - ...$ReadOnly, + ...BooleanLiteralTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'BooleanLiteralTypeAnnotation', - value: props.value, - raw: props.raw, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'BooleanLiteralTypeAnnotation', + value: props.value, + raw: props.raw, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -1305,79 +1340,79 @@ export function BooleanTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'BooleanTypeAnnotation', }); } export function BreakStatement(props: { - ...$ReadOnly, + ...BreakStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'BreakStatement', label: asDetachedNodeForCodeGen(props.label), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function CallExpression(props: { - ...$ReadOnly, + ...CallExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'CallExpression', callee: asDetachedNodeForCodeGen(props.callee), typeArguments: asDetachedNodeForCodeGen(props.typeArguments), arguments: props.arguments.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function CatchClause(props: { - ...$ReadOnly, + ...CatchClauseProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'CatchClause', param: asDetachedNodeForCodeGen(props.param), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ChainExpression(props: { - ...$ReadOnly, + ...ChainExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ChainExpression', expression: asDetachedNodeForCodeGen(props.expression), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ClassBody(props: { - ...$ReadOnly, + ...ClassBodyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ClassBody', body: props.body.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ClassExpression(props: { - ...$ReadOnly, + ...ClassExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ClassExpression', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), @@ -1387,120 +1422,141 @@ export function ClassExpression(props: { decorators: props.decorators.map(n => asDetachedNodeForCodeGen(n)), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ClassImplements(props: { - ...$ReadOnly, + ...ClassImplementsProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ClassImplements', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ComponentDeclaration(props: { - ...$ReadOnly, + ...ComponentDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ComponentDeclaration', - id: asDetachedNodeForCodeGen(props.id), - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - body: asDetachedNodeForCodeGen(props.body), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - rendersType: asDetachedNodeForCodeGen(props.rendersType), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ComponentDeclaration', + id: asDetachedNodeForCodeGen(props.id), + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + body: asDetachedNodeForCodeGen(props.body), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + rendersType: asDetachedNodeForCodeGen(props.rendersType), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ComponentParameter(props: { - ...$ReadOnly, + ...ComponentParameterProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ComponentParameter', - name: asDetachedNodeForCodeGen(props.name), - local: asDetachedNodeForCodeGen(props.local), - shorthand: props.shorthand, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ComponentParameter', + name: asDetachedNodeForCodeGen(props.name), + local: asDetachedNodeForCodeGen(props.local), + shorthand: props.shorthand, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ComponentTypeAnnotation(props: { - ...$ReadOnly, + ...ComponentTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ComponentTypeAnnotation', - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - rest: asDetachedNodeForCodeGen(props.rest), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - rendersType: asDetachedNodeForCodeGen(props.rendersType), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ComponentTypeAnnotation', + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + rest: asDetachedNodeForCodeGen(props.rest), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + rendersType: asDetachedNodeForCodeGen(props.rendersType), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ComponentTypeParameter(props: { - ...$ReadOnly, + ...ComponentTypeParameterProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ComponentTypeParameter', - name: asDetachedNodeForCodeGen(props.name), - typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), - optional: props.optional, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ComponentTypeParameter', + name: asDetachedNodeForCodeGen(props.name), + typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), + optional: props.optional, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ConditionalExpression(props: { - ...$ReadOnly, + ...ConditionalExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ConditionalExpression', - test: asDetachedNodeForCodeGen(props.test), - alternate: asDetachedNodeForCodeGen(props.alternate), - consequent: asDetachedNodeForCodeGen(props.consequent), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ConditionalExpression', + test: asDetachedNodeForCodeGen(props.test), + alternate: asDetachedNodeForCodeGen(props.alternate), + consequent: asDetachedNodeForCodeGen(props.consequent), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ConditionalTypeAnnotation(props: { - ...$ReadOnly, + ...ConditionalTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ConditionalTypeAnnotation', - checkType: asDetachedNodeForCodeGen(props.checkType), - extendsType: asDetachedNodeForCodeGen(props.extendsType), - trueType: asDetachedNodeForCodeGen(props.trueType), - falseType: asDetachedNodeForCodeGen(props.falseType), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ConditionalTypeAnnotation', + checkType: asDetachedNodeForCodeGen(props.checkType), + extendsType: asDetachedNodeForCodeGen(props.extendsType), + trueType: asDetachedNodeForCodeGen(props.trueType), + falseType: asDetachedNodeForCodeGen(props.falseType), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ContinueStatement(props: { - ...$ReadOnly, + ...ContinueStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ContinueStatement', - label: asDetachedNodeForCodeGen(props.label), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ContinueStatement', + label: asDetachedNodeForCodeGen(props.label), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -1509,16 +1565,16 @@ export function DebuggerStatement( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'DebuggerStatement', }); } export function DeclareClass(props: { - ...$ReadOnly, + ...DeclareClassProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareClass', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), @@ -1527,15 +1583,15 @@ export function DeclareClass(props: { mixins: props.mixins.map(n => asDetachedNodeForCodeGen(n)), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareComponent(props: { - ...$ReadOnly, + ...DeclareComponentProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareComponent', id: asDetachedNodeForCodeGen(props.id), params: props.params.map(n => asDetachedNodeForCodeGen(n)), @@ -1543,152 +1599,164 @@ export function DeclareComponent(props: { typeParameters: asDetachedNodeForCodeGen(props.typeParameters), rendersType: asDetachedNodeForCodeGen(props.rendersType), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclaredPredicate(props: { - ...$ReadOnly, + ...DeclaredPredicateProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'DeclaredPredicate', - value: asDetachedNodeForCodeGen(props.value), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'DeclaredPredicate', + value: asDetachedNodeForCodeGen(props.value), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareEnum(props: { - ...$ReadOnly, + ...DeclareEnumProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareEnum', id: asDetachedNodeForCodeGen(props.id), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareExportAllDeclaration(props: { - ...$ReadOnly, + ...DeclareExportAllDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'DeclareExportAllDeclaration', - source: asDetachedNodeForCodeGen(props.source), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'DeclareExportAllDeclaration', + source: asDetachedNodeForCodeGen(props.source), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareInterface(props: { - ...$ReadOnly, + ...DeclareInterfaceProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareInterface', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), extends: props.extends.map(n => asDetachedNodeForCodeGen(n)), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareModule(props: { - ...$ReadOnly, + ...DeclareModuleProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareModule', id: asDetachedNodeForCodeGen(props.id), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareModuleExports(props: { - ...$ReadOnly, + ...DeclareModuleExportsProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'DeclareModuleExports', - typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'DeclareModuleExports', + typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareNamespace(props: { - ...$ReadOnly, + ...DeclareNamespaceProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareNamespace', id: asDetachedNodeForCodeGen(props.id), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareOpaqueType(props: { - ...$ReadOnly, + ...DeclareOpaqueTypeProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'DeclareOpaqueType', - id: asDetachedNodeForCodeGen(props.id), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - impltype: asDetachedNodeForCodeGen(props.impltype), - supertype: asDetachedNodeForCodeGen(props.supertype), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'DeclareOpaqueType', + id: asDetachedNodeForCodeGen(props.id), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + impltype: asDetachedNodeForCodeGen(props.impltype), + supertype: asDetachedNodeForCodeGen(props.supertype), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareTypeAlias(props: { - ...$ReadOnly, + ...DeclareTypeAliasProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareTypeAlias', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), right: asDetachedNodeForCodeGen(props.right), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DeclareVariable(props: { - ...$ReadOnly, + ...DeclareVariableProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DeclareVariable', id: asDetachedNodeForCodeGen(props.id), kind: props.kind, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function DoWhileStatement(props: { - ...$ReadOnly, + ...DoWhileStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'DoWhileStatement', body: asDetachedNodeForCodeGen(props.body), test: asDetachedNodeForCodeGen(props.test), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -1697,7 +1765,7 @@ export function EmptyStatement( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'EmptyStatement', }); } @@ -1707,127 +1775,133 @@ export function EmptyTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'EmptyTypeAnnotation', }); } export function EnumBooleanBody(props: { - ...$ReadOnly, + ...EnumBooleanBodyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'EnumBooleanBody', members: props.members.map(n => asDetachedNodeForCodeGen(n)), explicitType: props.explicitType, hasUnknownMembers: props.hasUnknownMembers, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumBooleanMember(props: { - ...$ReadOnly, + ...EnumBooleanMemberProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'EnumBooleanMember', - id: asDetachedNodeForCodeGen(props.id), - init: asDetachedNodeForCodeGen(props.init), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'EnumBooleanMember', + id: asDetachedNodeForCodeGen(props.id), + init: asDetachedNodeForCodeGen(props.init), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumDeclaration(props: { - ...$ReadOnly, + ...EnumDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'EnumDeclaration', id: asDetachedNodeForCodeGen(props.id), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumDefaultedMember(props: { - ...$ReadOnly, + ...EnumDefaultedMemberProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'EnumDefaultedMember', - id: asDetachedNodeForCodeGen(props.id), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'EnumDefaultedMember', + id: asDetachedNodeForCodeGen(props.id), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumNumberBody(props: { - ...$ReadOnly, + ...EnumNumberBodyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'EnumNumberBody', members: props.members.map(n => asDetachedNodeForCodeGen(n)), explicitType: props.explicitType, hasUnknownMembers: props.hasUnknownMembers, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumNumberMember(props: { - ...$ReadOnly, + ...EnumNumberMemberProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'EnumNumberMember', id: asDetachedNodeForCodeGen(props.id), init: asDetachedNodeForCodeGen(props.init), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumStringBody(props: { - ...$ReadOnly, + ...EnumStringBodyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'EnumStringBody', members: props.members.map(n => asDetachedNodeForCodeGen(n)), explicitType: props.explicitType, hasUnknownMembers: props.hasUnknownMembers, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumStringMember(props: { - ...$ReadOnly, + ...EnumStringMemberProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'EnumStringMember', id: asDetachedNodeForCodeGen(props.id), init: asDetachedNodeForCodeGen(props.init), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function EnumSymbolBody(props: { - ...$ReadOnly, + ...EnumSymbolBodyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'EnumSymbolBody', members: props.members.map(n => asDetachedNodeForCodeGen(n)), hasUnknownMembers: props.hasUnknownMembers, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -1836,193 +1910,217 @@ export function ExistsTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'ExistsTypeAnnotation', }); } export function ExportAllDeclaration(props: { - ...$ReadOnly, + ...ExportAllDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ExportAllDeclaration', - exported: asDetachedNodeForCodeGen(props.exported), - source: asDetachedNodeForCodeGen(props.source), - exportKind: props.exportKind, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ExportAllDeclaration', + exported: asDetachedNodeForCodeGen(props.exported), + source: asDetachedNodeForCodeGen(props.source), + exportKind: props.exportKind, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ExportDefaultDeclaration(props: { - ...$ReadOnly, + ...ExportDefaultDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ExportDefaultDeclaration', - declaration: asDetachedNodeForCodeGen(props.declaration), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ExportDefaultDeclaration', + declaration: asDetachedNodeForCodeGen(props.declaration), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ExportSpecifier(props: { - ...$ReadOnly, + ...ExportSpecifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ExportSpecifier', exported: asDetachedNodeForCodeGen(props.exported), local: asDetachedNodeForCodeGen(props.local), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ExpressionStatement(props: { - ...$ReadOnly, + ...ExpressionStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ExpressionStatement', - expression: asDetachedNodeForCodeGen(props.expression), - directive: props.directive, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ExpressionStatement', + expression: asDetachedNodeForCodeGen(props.expression), + directive: props.directive, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ForInStatement(props: { - ...$ReadOnly, + ...ForInStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ForInStatement', left: asDetachedNodeForCodeGen(props.left), right: asDetachedNodeForCodeGen(props.right), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ForOfStatement(props: { - ...$ReadOnly, + ...ForOfStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ForOfStatement', left: asDetachedNodeForCodeGen(props.left), right: asDetachedNodeForCodeGen(props.right), body: asDetachedNodeForCodeGen(props.body), await: props.await, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ForStatement(props: { - ...$ReadOnly, + ...ForStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ForStatement', init: asDetachedNodeForCodeGen(props.init), test: asDetachedNodeForCodeGen(props.test), update: asDetachedNodeForCodeGen(props.update), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function FunctionDeclaration(props: { - ...$ReadOnly, + ...FunctionDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'FunctionDeclaration', - id: asDetachedNodeForCodeGen(props.id), - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - body: asDetachedNodeForCodeGen(props.body), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - returnType: asDetachedNodeForCodeGen(props.returnType), - predicate: asDetachedNodeForCodeGen(props.predicate), - generator: props.generator, - async: props.async, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'FunctionDeclaration', + id: asDetachedNodeForCodeGen(props.id), + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + body: asDetachedNodeForCodeGen(props.body), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + returnType: asDetachedNodeForCodeGen(props.returnType), + predicate: asDetachedNodeForCodeGen(props.predicate), + generator: props.generator, + async: props.async, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function FunctionExpression(props: { - ...$ReadOnly, + ...FunctionExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'FunctionExpression', - id: asDetachedNodeForCodeGen(props.id), - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - body: asDetachedNodeForCodeGen(props.body), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - returnType: asDetachedNodeForCodeGen(props.returnType), - predicate: asDetachedNodeForCodeGen(props.predicate), - generator: props.generator, - async: props.async, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'FunctionExpression', + id: asDetachedNodeForCodeGen(props.id), + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + body: asDetachedNodeForCodeGen(props.body), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + returnType: asDetachedNodeForCodeGen(props.returnType), + predicate: asDetachedNodeForCodeGen(props.predicate), + generator: props.generator, + async: props.async, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function FunctionTypeAnnotation(props: { - ...$ReadOnly, + ...FunctionTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'FunctionTypeAnnotation', - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - this: asDetachedNodeForCodeGen(props.this), - returnType: asDetachedNodeForCodeGen(props.returnType), - rest: asDetachedNodeForCodeGen(props.rest), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'FunctionTypeAnnotation', + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + this: asDetachedNodeForCodeGen(props.this), + returnType: asDetachedNodeForCodeGen(props.returnType), + rest: asDetachedNodeForCodeGen(props.rest), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function FunctionTypeParam(props: { - ...$ReadOnly, + ...FunctionTypeParamProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'FunctionTypeParam', - name: asDetachedNodeForCodeGen(props.name), - typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), - optional: props.optional, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'FunctionTypeParam', + name: asDetachedNodeForCodeGen(props.name), + typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), + optional: props.optional, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function GenericTypeAnnotation(props: { - ...$ReadOnly, + ...GenericTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'GenericTypeAnnotation', - id: asDetachedNodeForCodeGen(props.id), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'GenericTypeAnnotation', + id: asDetachedNodeForCodeGen(props.id), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function HookDeclaration(props: { - ...$ReadOnly, + ...HookDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'HookDeclaration', id: asDetachedNodeForCodeGen(props.id), params: props.params.map(n => asDetachedNodeForCodeGen(n)), @@ -2030,128 +2128,143 @@ export function HookDeclaration(props: { typeParameters: asDetachedNodeForCodeGen(props.typeParameters), returnType: asDetachedNodeForCodeGen(props.returnType), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function HookTypeAnnotation(props: { - ...$ReadOnly, + ...HookTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'HookTypeAnnotation', - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - returnType: asDetachedNodeForCodeGen(props.returnType), - rest: asDetachedNodeForCodeGen(props.rest), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'HookTypeAnnotation', + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + returnType: asDetachedNodeForCodeGen(props.returnType), + rest: asDetachedNodeForCodeGen(props.rest), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function IfStatement(props: { - ...$ReadOnly, + ...IfStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'IfStatement', test: asDetachedNodeForCodeGen(props.test), consequent: asDetachedNodeForCodeGen(props.consequent), alternate: asDetachedNodeForCodeGen(props.alternate), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ImportAttribute(props: { - ...$ReadOnly, + ...ImportAttributeProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ImportAttribute', key: asDetachedNodeForCodeGen(props.key), value: asDetachedNodeForCodeGen(props.value), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ImportDeclaration(props: { - ...$ReadOnly, + ...ImportDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ImportDeclaration', - specifiers: props.specifiers.map(n => asDetachedNodeForCodeGen(n)), - source: asDetachedNodeForCodeGen(props.source), - assertions: props.assertions?.map(n => asDetachedNodeForCodeGen(n)), - importKind: props.importKind, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ImportDeclaration', + specifiers: props.specifiers.map(n => asDetachedNodeForCodeGen(n)), + source: asDetachedNodeForCodeGen(props.source), + assertions: props.assertions?.map(n => asDetachedNodeForCodeGen(n)), + importKind: props.importKind, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ImportDefaultSpecifier(props: { - ...$ReadOnly, + ...ImportDefaultSpecifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ImportDefaultSpecifier', - local: asDetachedNodeForCodeGen(props.local), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ImportDefaultSpecifier', + local: asDetachedNodeForCodeGen(props.local), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ImportExpression(props: { - ...$ReadOnly, + ...ImportExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ImportExpression', source: asDetachedNodeForCodeGen(props.source), attributes: asDetachedNodeForCodeGen(props.attributes), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ImportNamespaceSpecifier(props: { - ...$ReadOnly, + ...ImportNamespaceSpecifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ImportNamespaceSpecifier', - local: asDetachedNodeForCodeGen(props.local), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ImportNamespaceSpecifier', + local: asDetachedNodeForCodeGen(props.local), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ImportSpecifier(props: { - ...$ReadOnly, + ...ImportSpecifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ImportSpecifier', imported: asDetachedNodeForCodeGen(props.imported), local: asDetachedNodeForCodeGen(props.local), importKind: props.importKind, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function IndexedAccessType(props: { - ...$ReadOnly, + ...IndexedAccessTypeProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'IndexedAccessType', - objectType: asDetachedNodeForCodeGen(props.objectType), - indexType: asDetachedNodeForCodeGen(props.indexType), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'IndexedAccessType', + objectType: asDetachedNodeForCodeGen(props.objectType), + indexType: asDetachedNodeForCodeGen(props.indexType), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2160,98 +2273,113 @@ export function InferredPredicate( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'InferredPredicate', }); } export function InferTypeAnnotation(props: { - ...$ReadOnly, + ...InferTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'InferTypeAnnotation', - typeParameter: asDetachedNodeForCodeGen(props.typeParameter), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'InferTypeAnnotation', + typeParameter: asDetachedNodeForCodeGen(props.typeParameter), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function InterfaceDeclaration(props: { - ...$ReadOnly, + ...InterfaceDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'InterfaceDeclaration', - id: asDetachedNodeForCodeGen(props.id), - typeParameters: asDetachedNodeForCodeGen(props.typeParameters), - extends: props.extends.map(n => asDetachedNodeForCodeGen(n)), - body: asDetachedNodeForCodeGen(props.body), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'InterfaceDeclaration', + id: asDetachedNodeForCodeGen(props.id), + typeParameters: asDetachedNodeForCodeGen(props.typeParameters), + extends: props.extends.map(n => asDetachedNodeForCodeGen(n)), + body: asDetachedNodeForCodeGen(props.body), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function InterfaceExtends(props: { - ...$ReadOnly, + ...InterfaceExtendsProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'InterfaceExtends', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function InterfaceTypeAnnotation(props: { - ...$ReadOnly, + ...InterfaceTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'InterfaceTypeAnnotation', - extends: props.extends.map(n => asDetachedNodeForCodeGen(n)), - body: asDetachedNodeForCodeGen(props.body), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'InterfaceTypeAnnotation', + extends: props.extends.map(n => asDetachedNodeForCodeGen(n)), + body: asDetachedNodeForCodeGen(props.body), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function IntersectionTypeAnnotation(props: { - ...$ReadOnly, + ...IntersectionTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'IntersectionTypeAnnotation', - types: props.types.map(n => asDetachedNodeForCodeGen(n)), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'IntersectionTypeAnnotation', + types: props.types.map(n => asDetachedNodeForCodeGen(n)), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXAttribute(props: { - ...$ReadOnly, + ...JSXAttributeProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'JSXAttribute', name: asDetachedNodeForCodeGen(props.name), value: asDetachedNodeForCodeGen(props.value), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXClosingElement(props: { - ...$ReadOnly, + ...JSXClosingElementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'JSXClosingElement', - name: asDetachedNodeForCodeGen(props.name), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'JSXClosingElement', + name: asDetachedNodeForCodeGen(props.name), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2260,22 +2388,22 @@ export function JSXClosingFragment( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'JSXClosingFragment', }); } export function JSXElement(props: { - ...$ReadOnly, + ...JSXElementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'JSXElement', openingElement: asDetachedNodeForCodeGen(props.openingElement), children: props.children.map(n => asDetachedNodeForCodeGen(n)), closingElement: asDetachedNodeForCodeGen(props.closingElement), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2284,87 +2412,99 @@ export function JSXEmptyExpression( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'JSXEmptyExpression', }); } export function JSXExpressionContainer(props: { - ...$ReadOnly, + ...JSXExpressionContainerProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'JSXExpressionContainer', - expression: asDetachedNodeForCodeGen(props.expression), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'JSXExpressionContainer', + expression: asDetachedNodeForCodeGen(props.expression), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXFragment(props: { - ...$ReadOnly, + ...JSXFragmentProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'JSXFragment', openingFragment: asDetachedNodeForCodeGen(props.openingFragment), children: props.children.map(n => asDetachedNodeForCodeGen(n)), closingFragment: asDetachedNodeForCodeGen(props.closingFragment), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXIdentifier(props: { - ...$ReadOnly, + ...JSXIdentifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'JSXIdentifier', name: props.name, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXMemberExpression(props: { - ...$ReadOnly, + ...JSXMemberExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'JSXMemberExpression', - object: asDetachedNodeForCodeGen(props.object), - property: asDetachedNodeForCodeGen(props.property), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'JSXMemberExpression', + object: asDetachedNodeForCodeGen(props.object), + property: asDetachedNodeForCodeGen(props.property), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXNamespacedName(props: { - ...$ReadOnly, + ...JSXNamespacedNameProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'JSXNamespacedName', - namespace: asDetachedNodeForCodeGen(props.namespace), - name: asDetachedNodeForCodeGen(props.name), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'JSXNamespacedName', + namespace: asDetachedNodeForCodeGen(props.namespace), + name: asDetachedNodeForCodeGen(props.name), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXOpeningElement(props: { - ...$ReadOnly, + ...JSXOpeningElementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'JSXOpeningElement', - name: asDetachedNodeForCodeGen(props.name), - attributes: props.attributes.map(n => asDetachedNodeForCodeGen(n)), - selfClosing: props.selfClosing, - typeArguments: asDetachedNodeForCodeGen(props.typeArguments), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'JSXOpeningElement', + name: asDetachedNodeForCodeGen(props.name), + attributes: props.attributes.map(n => asDetachedNodeForCodeGen(n)), + selfClosing: props.selfClosing, + typeArguments: asDetachedNodeForCodeGen(props.typeArguments), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2373,105 +2513,114 @@ export function JSXOpeningFragment( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'JSXOpeningFragment', }); } export function JSXSpreadAttribute(props: { - ...$ReadOnly, + ...JSXSpreadAttributeProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'JSXSpreadAttribute', - argument: asDetachedNodeForCodeGen(props.argument), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'JSXSpreadAttribute', + argument: asDetachedNodeForCodeGen(props.argument), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXSpreadChild(props: { - ...$ReadOnly, + ...JSXSpreadChildProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'JSXSpreadChild', expression: asDetachedNodeForCodeGen(props.expression), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function JSXText(props: { - ...$ReadOnly, + ...JSXTextProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'JSXText', value: props.value, raw: props.raw, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function KeyofTypeAnnotation(props: { - ...$ReadOnly, + ...KeyofTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'KeyofTypeAnnotation', - argument: asDetachedNodeForCodeGen(props.argument), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'KeyofTypeAnnotation', + argument: asDetachedNodeForCodeGen(props.argument), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function LabeledStatement(props: { - ...$ReadOnly, + ...LabeledStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'LabeledStatement', label: asDetachedNodeForCodeGen(props.label), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function LogicalExpression(props: { - ...$ReadOnly, + ...LogicalExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'LogicalExpression', - left: asDetachedNodeForCodeGen(props.left), - right: asDetachedNodeForCodeGen(props.right), - operator: props.operator, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'LogicalExpression', + left: asDetachedNodeForCodeGen(props.left), + right: asDetachedNodeForCodeGen(props.right), + operator: props.operator, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function MetaProperty(props: { - ...$ReadOnly, + ...MetaPropertyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'MetaProperty', meta: asDetachedNodeForCodeGen(props.meta), property: asDetachedNodeForCodeGen(props.property), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function MethodDefinition(props: { - ...$ReadOnly, + ...MethodDefinitionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'MethodDefinition', key: asDetachedNodeForCodeGen(props.key), value: asDetachedNodeForCodeGen(props.value), @@ -2479,7 +2628,7 @@ export function MethodDefinition(props: { computed: props.computed, static: props.static, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2488,34 +2637,37 @@ export function MixedTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'MixedTypeAnnotation', }); } export function NewExpression(props: { - ...$ReadOnly, + ...NewExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'NewExpression', callee: asDetachedNodeForCodeGen(props.callee), typeArguments: asDetachedNodeForCodeGen(props.typeArguments), arguments: props.arguments.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function NullableTypeAnnotation(props: { - ...$ReadOnly, + ...NullableTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'NullableTypeAnnotation', - typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'NullableTypeAnnotation', + typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2524,21 +2676,27 @@ export function NullLiteralTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { - type: 'NullLiteralTypeAnnotation', - }); + return detachedProps( + (props.parent: $FlowFixMe), + { + type: 'NullLiteralTypeAnnotation', + }, + ); } export function NumberLiteralTypeAnnotation(props: { - ...$ReadOnly, + ...NumberLiteralTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'NumberLiteralTypeAnnotation', - value: props.value, - raw: props.raw, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'NumberLiteralTypeAnnotation', + value: props.value, + raw: props.raw, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2547,172 +2705,198 @@ export function NumberTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'NumberTypeAnnotation', }); } export function ObjectExpression(props: { - ...$ReadOnly, + ...ObjectExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ObjectExpression', properties: props.properties.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ObjectPattern(props: { - ...$ReadOnly, + ...ObjectPatternProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ObjectPattern', properties: props.properties.map(n => asDetachedNodeForCodeGen(n)), typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ObjectTypeAnnotation(props: { - ...$ReadOnly, + ...ObjectTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ObjectTypeAnnotation', - properties: props.properties.map(n => asDetachedNodeForCodeGen(n)), - indexers: props.indexers.map(n => asDetachedNodeForCodeGen(n)), - callProperties: props.callProperties.map(n => asDetachedNodeForCodeGen(n)), - internalSlots: props.internalSlots.map(n => asDetachedNodeForCodeGen(n)), - inexact: props.inexact, - exact: props.exact, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ObjectTypeAnnotation', + properties: props.properties.map(n => asDetachedNodeForCodeGen(n)), + indexers: props.indexers.map(n => asDetachedNodeForCodeGen(n)), + callProperties: props.callProperties.map(n => + asDetachedNodeForCodeGen(n), + ), + internalSlots: props.internalSlots.map(n => asDetachedNodeForCodeGen(n)), + inexact: props.inexact, + exact: props.exact, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ObjectTypeCallProperty(props: { - ...$ReadOnly, + ...ObjectTypeCallPropertyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ObjectTypeCallProperty', - value: asDetachedNodeForCodeGen(props.value), - static: props.static, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ObjectTypeCallProperty', + value: asDetachedNodeForCodeGen(props.value), + static: props.static, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ObjectTypeIndexer(props: { - ...$ReadOnly, + ...ObjectTypeIndexerProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ObjectTypeIndexer', - id: asDetachedNodeForCodeGen(props.id), - key: asDetachedNodeForCodeGen(props.key), - value: asDetachedNodeForCodeGen(props.value), - static: props.static, - variance: asDetachedNodeForCodeGen(props.variance), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ObjectTypeIndexer', + id: asDetachedNodeForCodeGen(props.id), + key: asDetachedNodeForCodeGen(props.key), + value: asDetachedNodeForCodeGen(props.value), + static: props.static, + variance: asDetachedNodeForCodeGen(props.variance), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ObjectTypeInternalSlot(props: { - ...$ReadOnly, + ...ObjectTypeInternalSlotProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ObjectTypeInternalSlot', - id: asDetachedNodeForCodeGen(props.id), - value: asDetachedNodeForCodeGen(props.value), - optional: props.optional, - static: props.static, - method: props.method, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ObjectTypeInternalSlot', + id: asDetachedNodeForCodeGen(props.id), + value: asDetachedNodeForCodeGen(props.value), + optional: props.optional, + static: props.static, + method: props.method, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ObjectTypeMappedTypeProperty(props: { - ...$ReadOnly, + ...ObjectTypeMappedTypePropertyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ObjectTypeMappedTypeProperty', - keyTparam: asDetachedNodeForCodeGen(props.keyTparam), - propType: asDetachedNodeForCodeGen(props.propType), - sourceType: asDetachedNodeForCodeGen(props.sourceType), - variance: asDetachedNodeForCodeGen(props.variance), - optional: props.optional, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ObjectTypeMappedTypeProperty', + keyTparam: asDetachedNodeForCodeGen(props.keyTparam), + propType: asDetachedNodeForCodeGen(props.propType), + sourceType: asDetachedNodeForCodeGen(props.sourceType), + variance: asDetachedNodeForCodeGen(props.variance), + optional: props.optional, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ObjectTypeSpreadProperty(props: { - ...$ReadOnly, + ...ObjectTypeSpreadPropertyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'ObjectTypeSpreadProperty', - argument: asDetachedNodeForCodeGen(props.argument), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'ObjectTypeSpreadProperty', + argument: asDetachedNodeForCodeGen(props.argument), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function OpaqueType(props: { - ...$ReadOnly, + ...OpaqueTypeProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'OpaqueType', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), impltype: asDetachedNodeForCodeGen(props.impltype), supertype: asDetachedNodeForCodeGen(props.supertype), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function OptionalIndexedAccessType(props: { - ...$ReadOnly, + ...OptionalIndexedAccessTypeProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'OptionalIndexedAccessType', - objectType: asDetachedNodeForCodeGen(props.objectType), - indexType: asDetachedNodeForCodeGen(props.indexType), - optional: props.optional, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'OptionalIndexedAccessType', + objectType: asDetachedNodeForCodeGen(props.objectType), + indexType: asDetachedNodeForCodeGen(props.indexType), + optional: props.optional, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function PrivateIdentifier(props: { - ...$ReadOnly, + ...PrivateIdentifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'PrivateIdentifier', - name: props.name, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'PrivateIdentifier', + name: props.name, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function Property(props: { - ...$ReadOnly, + ...PropertyProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'Property', key: asDetachedNodeForCodeGen(props.key), value: asDetachedNodeForCodeGen(props.value), @@ -2721,113 +2905,128 @@ export function Property(props: { method: props.method, shorthand: props.shorthand, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function PropertyDefinition(props: { - ...$ReadOnly, + ...PropertyDefinitionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'PropertyDefinition', - key: asDetachedNodeForCodeGen(props.key), - value: asDetachedNodeForCodeGen(props.value), - computed: props.computed, - static: props.static, - declare: props.declare, - optional: props.optional, - variance: asDetachedNodeForCodeGen(props.variance), - typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'PropertyDefinition', + key: asDetachedNodeForCodeGen(props.key), + value: asDetachedNodeForCodeGen(props.value), + computed: props.computed, + static: props.static, + declare: props.declare, + optional: props.optional, + variance: asDetachedNodeForCodeGen(props.variance), + typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function QualifiedTypeIdentifier(props: { - ...$ReadOnly, + ...QualifiedTypeIdentifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'QualifiedTypeIdentifier', - qualification: asDetachedNodeForCodeGen(props.qualification), - id: asDetachedNodeForCodeGen(props.id), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'QualifiedTypeIdentifier', + qualification: asDetachedNodeForCodeGen(props.qualification), + id: asDetachedNodeForCodeGen(props.id), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function QualifiedTypeofIdentifier(props: { - ...$ReadOnly, + ...QualifiedTypeofIdentifierProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'QualifiedTypeofIdentifier', - qualification: asDetachedNodeForCodeGen(props.qualification), - id: asDetachedNodeForCodeGen(props.id), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'QualifiedTypeofIdentifier', + qualification: asDetachedNodeForCodeGen(props.qualification), + id: asDetachedNodeForCodeGen(props.id), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function RestElement(props: { - ...$ReadOnly, + ...RestElementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'RestElement', argument: asDetachedNodeForCodeGen(props.argument), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function ReturnStatement(props: { - ...$ReadOnly, + ...ReturnStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ReturnStatement', argument: asDetachedNodeForCodeGen(props.argument), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function SequenceExpression(props: { - ...$ReadOnly, + ...SequenceExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'SequenceExpression', - expressions: props.expressions.map(n => asDetachedNodeForCodeGen(n)), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'SequenceExpression', + expressions: props.expressions.map(n => asDetachedNodeForCodeGen(n)), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function SpreadElement(props: { - ...$ReadOnly, + ...SpreadElementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'SpreadElement', argument: asDetachedNodeForCodeGen(props.argument), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function StringLiteralTypeAnnotation(props: { - ...$ReadOnly, + ...StringLiteralTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'StringLiteralTypeAnnotation', - value: props.value, - raw: props.raw, - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'StringLiteralTypeAnnotation', + value: props.value, + raw: props.raw, + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2836,7 +3035,7 @@ export function StringTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'StringTypeAnnotation', }); } @@ -2846,34 +3045,34 @@ export function Super( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'Super', }); } export function SwitchCase(props: { - ...$ReadOnly, + ...SwitchCaseProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'SwitchCase', test: asDetachedNodeForCodeGen(props.test), consequent: props.consequent.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function SwitchStatement(props: { - ...$ReadOnly, + ...SwitchStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'SwitchStatement', discriminant: asDetachedNodeForCodeGen(props.discriminant), cases: props.cases.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2882,34 +3081,37 @@ export function SymbolTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'SymbolTypeAnnotation', }); } export function TaggedTemplateExpression(props: { - ...$ReadOnly, + ...TaggedTemplateExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TaggedTemplateExpression', - tag: asDetachedNodeForCodeGen(props.tag), - quasi: asDetachedNodeForCodeGen(props.quasi), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TaggedTemplateExpression', + tag: asDetachedNodeForCodeGen(props.tag), + quasi: asDetachedNodeForCodeGen(props.quasi), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TemplateLiteral(props: { - ...$ReadOnly, + ...TemplateLiteralProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'TemplateLiteral', quasis: props.quasis.map(n => asDetachedNodeForCodeGen(n)), expressions: props.expressions.map(n => asDetachedNodeForCodeGen(n)), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -2918,7 +3120,7 @@ export function ThisExpression( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'ThisExpression', }); } @@ -2928,147 +3130,162 @@ export function ThisTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'ThisTypeAnnotation', }); } export function ThrowStatement(props: { - ...$ReadOnly, + ...ThrowStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'ThrowStatement', argument: asDetachedNodeForCodeGen(props.argument), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TryStatement(props: { - ...$ReadOnly, + ...TryStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'TryStatement', block: asDetachedNodeForCodeGen(props.block), handler: asDetachedNodeForCodeGen(props.handler), finalizer: asDetachedNodeForCodeGen(props.finalizer), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TupleTypeAnnotation(props: { - ...$ReadOnly, + ...TupleTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TupleTypeAnnotation', - types: props.types.map(n => asDetachedNodeForCodeGen(n)), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TupleTypeAnnotation', + types: props.types.map(n => asDetachedNodeForCodeGen(n)), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TupleTypeLabeledElement(props: { - ...$ReadOnly, + ...TupleTypeLabeledElementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TupleTypeLabeledElement', - label: asDetachedNodeForCodeGen(props.label), - elementType: asDetachedNodeForCodeGen(props.elementType), - optional: props.optional, - variance: asDetachedNodeForCodeGen(props.variance), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TupleTypeLabeledElement', + label: asDetachedNodeForCodeGen(props.label), + elementType: asDetachedNodeForCodeGen(props.elementType), + optional: props.optional, + variance: asDetachedNodeForCodeGen(props.variance), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TupleTypeSpreadElement(props: { - ...$ReadOnly, + ...TupleTypeSpreadElementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TupleTypeSpreadElement', - label: asDetachedNodeForCodeGen(props.label), - typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TupleTypeSpreadElement', + label: asDetachedNodeForCodeGen(props.label), + typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeAlias(props: { - ...$ReadOnly, + ...TypeAliasProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'TypeAlias', id: asDetachedNodeForCodeGen(props.id), typeParameters: asDetachedNodeForCodeGen(props.typeParameters), right: asDetachedNodeForCodeGen(props.right), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeAnnotation(props: { - ...$ReadOnly, + ...TypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'TypeAnnotation', typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeCastExpression(props: { - ...$ReadOnly, + ...TypeCastExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TypeCastExpression', - expression: asDetachedNodeForCodeGen(props.expression), - typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TypeCastExpression', + expression: asDetachedNodeForCodeGen(props.expression), + typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeofTypeAnnotation(props: { - ...$ReadOnly, + ...TypeofTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TypeofTypeAnnotation', - argument: asDetachedNodeForCodeGen(props.argument), - typeArguments: asDetachedNodeForCodeGen(props.typeArguments), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TypeofTypeAnnotation', + argument: asDetachedNodeForCodeGen(props.argument), + typeArguments: asDetachedNodeForCodeGen(props.typeArguments), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeOperator(props: { - ...$ReadOnly, + ...TypeOperatorProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'TypeOperator', operator: props.operator, typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeParameter(props: { - ...$ReadOnly, + ...TypeParameterProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'TypeParameter', name: props.name, bound: asDetachedNodeForCodeGen(props.bound), @@ -3076,123 +3293,138 @@ export function TypeParameter(props: { default: asDetachedNodeForCodeGen(props.default), usesExtendsBound: props.usesExtendsBound, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeParameterDeclaration(props: { - ...$ReadOnly, + ...TypeParameterDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TypeParameterDeclaration', - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TypeParameterDeclaration', + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypeParameterInstantiation(props: { - ...$ReadOnly, + ...TypeParameterInstantiationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'TypeParameterInstantiation', - params: props.params.map(n => asDetachedNodeForCodeGen(n)), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'TypeParameterInstantiation', + params: props.params.map(n => asDetachedNodeForCodeGen(n)), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function TypePredicate(props: { - ...$ReadOnly, + ...TypePredicateProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'TypePredicate', parameterName: asDetachedNodeForCodeGen(props.parameterName), typeAnnotation: asDetachedNodeForCodeGen(props.typeAnnotation), asserts: props.asserts, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function UnaryExpression(props: { - ...$ReadOnly, + ...UnaryExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'UnaryExpression', operator: props.operator, argument: asDetachedNodeForCodeGen(props.argument), prefix: props.prefix, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function UnionTypeAnnotation(props: { - ...$ReadOnly, + ...UnionTypeAnnotationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'UnionTypeAnnotation', - types: props.types.map(n => asDetachedNodeForCodeGen(n)), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'UnionTypeAnnotation', + types: props.types.map(n => asDetachedNodeForCodeGen(n)), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function UpdateExpression(props: { - ...$ReadOnly, + ...UpdateExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'UpdateExpression', operator: props.operator, argument: asDetachedNodeForCodeGen(props.argument), prefix: props.prefix, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function VariableDeclaration(props: { - ...$ReadOnly, + ...VariableDeclarationProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'VariableDeclaration', - kind: props.kind, - declarations: props.declarations.map(n => asDetachedNodeForCodeGen(n)), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'VariableDeclaration', + kind: props.kind, + declarations: props.declarations.map(n => asDetachedNodeForCodeGen(n)), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function VariableDeclarator(props: { - ...$ReadOnly, + ...VariableDeclaratorProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { - type: 'VariableDeclarator', - init: asDetachedNodeForCodeGen(props.init), - id: asDetachedNodeForCodeGen(props.id), - }); - setParentPointersInDirectChildren(node); + const node = detachedProps( + (props.parent: $FlowFixMe), + { + type: 'VariableDeclarator', + init: asDetachedNodeForCodeGen(props.init), + id: asDetachedNodeForCodeGen(props.id), + }, + ); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function Variance(props: { - ...$ReadOnly, + ...VarianceProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'Variance', kind: props.kind, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } @@ -3201,47 +3433,47 @@ export function VoidTypeAnnotation( +parent?: ESNode, } = {...null}, ): DetachedNode { - return detachedProps(props.parent, { + return detachedProps((props.parent: $FlowFixMe), { type: 'VoidTypeAnnotation', }); } export function WhileStatement(props: { - ...$ReadOnly, + ...WhileStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'WhileStatement', body: asDetachedNodeForCodeGen(props.body), test: asDetachedNodeForCodeGen(props.test), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function WithStatement(props: { - ...$ReadOnly, + ...WithStatementProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'WithStatement', object: asDetachedNodeForCodeGen(props.object), body: asDetachedNodeForCodeGen(props.body), }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } export function YieldExpression(props: { - ...$ReadOnly, + ...YieldExpressionProps, +parent?: ESNode, }): DetachedNode { - const node = detachedProps(props.parent, { + const node = detachedProps((props.parent: $FlowFixMe), { type: 'YieldExpression', argument: asDetachedNodeForCodeGen(props.argument), delegate: props.delegate, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } diff --git a/tools/hermes-parser/js/hermes-transform/src/transform/TransformContext.js b/tools/hermes-parser/js/hermes-transform/src/transform/TransformContext.js index cd77ab47c02..bf48030466e 100644 --- a/tools/hermes-parser/js/hermes-transform/src/transform/TransformContext.js +++ b/tools/hermes-parser/js/hermes-transform/src/transform/TransformContext.js @@ -331,72 +331,64 @@ export function getTransformContext(): TransformContextAdditions { const cloneAPIs: TransformCloneAPIs = { // $FlowFixMe[incompatible-exact] - shallowCloneNode: (( - node: ?ESNode, - ): // $FlowExpectedError[incompatible-cast] - ?DetachedNode => { + shallowCloneNode: (((node: ?ESNode): ?DetachedNode => { if (node == null) { return null; } return shallowCloneNode(node, {}); - }: TransformCloneAPIs['shallowCloneNode']), + }: $FlowFixMe): TransformCloneAPIs['shallowCloneNode']), - shallowCloneNodeWithOverrides: (( + shallowCloneNodeWithOverrides: ((( node: ?ESNode, newProps?: $ReadOnly<{...}> = {}, - ): // $FlowExpectedError[incompatible-cast] - // $FlowExpectedError[prop-missing] + ): // $FlowExpectedError[prop-missing] ?DetachedNode => { if (node == null) { return null; } return shallowCloneNode(node, newProps); - }: TransformCloneAPIs['shallowCloneNodeWithOverrides']), + }: $FlowFixMe): TransformCloneAPIs['shallowCloneNodeWithOverrides']), // $FlowFixMe[incompatible-exact] - shallowCloneArray: (( + shallowCloneArray: ((( nodes: ?$ReadOnlyArray, - ): // $FlowExpectedError[incompatible-cast] - ?$ReadOnlyArray> => { + ): ?$ReadOnlyArray> => { if (nodes == null) { return null; } - return nodes.map((node): DetachedNode => { + return nodes.map((node_: ?T): DetachedNode => { + const node: ?ESNode = node_; if (node == null) { // $FlowExpectedError[incompatible-return] return node; } - return shallowCloneNode(node, {}); + return shallowCloneNode(node, {}); }); - }: TransformCloneAPIs['shallowCloneArray']), + }: $FlowFixMe): TransformCloneAPIs['shallowCloneArray']), // $FlowFixMe[incompatible-exact] - deepCloneNode: (( - node: ?ESNode, - ): // $FlowExpectedError[incompatible-cast] - ?DetachedNode => { + deepCloneNode: (((node: ?ESNode): ?DetachedNode => { if (node == null) { return null; } return deepCloneNode(node, {}); - }: TransformCloneAPIs['deepCloneNode']), + }: $FlowFixMe): TransformCloneAPIs['deepCloneNode']), - deepCloneNodeWithOverrides: (( + deepCloneNodeWithOverrides: ((( node: ?ESNode, newProps?: $ReadOnly<{...}> = {}, - ): // $FlowExpectedError[incompatible-cast] - // $FlowExpectedError[prop-missing] + ): // $FlowExpectedError[prop-missing] ?DetachedNode => { if (node == null) { return null; } return deepCloneNode(node, newProps); - }: TransformCloneAPIs['deepCloneNodeWithOverrides']), + }: $FlowFixMe): TransformCloneAPIs['deepCloneNodeWithOverrides']), }; const commentAPIs: TransformCommentAPIs = { getComments: ((node): Array => { @@ -504,8 +496,7 @@ export function getTransformContext(): TransformContextAdditions { }: TransformRemoveAPIs['removeStatement']), }; const replaceAPIs: TransformReplaceAPIs = { - // $FlowFixMe[incompatible-exact] - replaceNode: (( + replaceNode: ((( target: ESNode, nodeToReplaceWith: MaybeDetachedNode, options?: ReplaceNodeOptions, @@ -517,7 +508,7 @@ export function getTransformContext(): TransformContextAdditions { options, ), ); - }: TransformReplaceAPIs['replaceNode']), + }: $FlowFixMe): TransformReplaceAPIs['replaceNode']), replaceStatementWithMany: (( target, @@ -544,8 +535,11 @@ export function getTransformContext(): TransformContextAdditions { } const cloned = shallowCloneNode(node, newProps, {preserveLocation: true}); - // $FlowExpectedError[incompatible-call] - replaceAPIs.replaceNode(node, cloned, options); + replaceAPIs.replaceNode( + (node: $FlowFixMe), + (cloned: $FlowFixMe), + options, + ); }: TransformModifyAPIs['modifyNodeInPlace']), }; @@ -568,8 +562,7 @@ export function getTransformContext(): TransformContextAdditions { function toArray(thing: SingleOrArray): $ReadOnlyArray { if (Array.isArray(thing)) { - // $FlowExpectedError[incompatible-return] - return thing; + return (thing: $FlowFixMe); } return [thing]; } diff --git a/tools/hermes-parser/js/hermes-transform/src/transform/mutations/ReplaceNode.js b/tools/hermes-parser/js/hermes-transform/src/transform/mutations/ReplaceNode.js index 907b61bbbee..38a12d7fc37 100644 --- a/tools/hermes-parser/js/hermes-transform/src/transform/mutations/ReplaceNode.js +++ b/tools/hermes-parser/js/hermes-transform/src/transform/mutations/ReplaceNode.js @@ -88,18 +88,14 @@ function getParentKey(target: ESNode): $ReadOnly< > { const parent = target.parent; for (const key of getVisitorKeys(parent)) { - if ( - isNode( - // $FlowExpectedError[prop-missing] - parent[key], - ) - ) { - if (parent[key] === target) { + const child = (parent: $FlowFixMe)[key]; + if (isNode(child)) { + if (child === target) { return {type: 'single', parent, key}; } - } else if (Array.isArray(parent[key])) { - for (let i = 0; i < parent[key].length; i += 1) { - const current = parent[key][i]; + } else if (Array.isArray(child)) { + for (let i = 0; i < child.length; i += 1) { + const current = child[i]; const originalNode = getOriginalNode(current); if (current === target || originalNode === target) { return {type: 'array', parent, key, targetIndex: i}; diff --git a/tools/hermes-parser/js/hermes-transform/src/traverse/traverse.js b/tools/hermes-parser/js/hermes-transform/src/traverse/traverse.js index f548a33c7c7..353dace9d6e 100644 --- a/tools/hermes-parser/js/hermes-transform/src/traverse/traverse.js +++ b/tools/hermes-parser/js/hermes-transform/src/traverse/traverse.js @@ -175,9 +175,7 @@ export function traverseWithContext( // add all the selectors from the visitor as listeners Object.keys(selectors).forEach(selector => { // flow doesn't want us to be general here - but it's safe - // $FlowExpectedError[incompatible-type] - // $FlowExpectedError[prop-missing] - const listener: ?EmitterListener = selectors[selector]; + const listener: ?EmitterListener = (selectors: $FlowFixMe)[selector]; if (listener) { emitter.on(selector, listener); } diff --git a/tools/hermes-parser/js/scripts/genPredicateFunctions.js b/tools/hermes-parser/js/scripts/genPredicateFunctions.js index ddcc223bbc7..df9429f9bcf 100644 --- a/tools/hermes-parser/js/scripts/genPredicateFunctions.js +++ b/tools/hermes-parser/js/scripts/genPredicateFunctions.js @@ -241,8 +241,16 @@ const fileContents = `\ import type { ESNode, Token, + AFunction, + ClassMember, + BigIntLiteral, + BooleanLiteral, + NullLiteral, + NumericLiteral, + RegExpLiteral, + StringLiteral, ${nodeTypesToImport.map(n => ` ${n}`).join(',\n')}, -} from 'hermes-estree'; +} from '../types'; */ ${predicateFunctions.join('\n')} diff --git a/tools/hermes-parser/js/scripts/genTransformNodeTypes.js b/tools/hermes-parser/js/scripts/genTransformNodeTypes.js index 9cea1a0affc..e2d3458ad1e 100644 --- a/tools/hermes-parser/js/scripts/genTransformNodeTypes.js +++ b/tools/hermes-parser/js/scripts/genTransformNodeTypes.js @@ -62,7 +62,7 @@ export type ${node.name}Props = {}; export function ${node.name}(props: { +parent?: ESNode, } = {...null}): DetachedNode<${node.name}Type> { - return detachedProps<${node.name}Type>(props.parent, { + return detachedProps<${node.name}Type>((props.parent: $FlowFixMe), { type: '${type}', }); } @@ -98,10 +98,10 @@ export type ${node.name}Props = { nodeTypeFunctions.push( `\ export function ${node.name}(props: { - ...$ReadOnly<${node.name}Props>, + ...${node.name}Props, +parent?: ESNode, }): DetachedNode<${node.name}Type> { - const node = detachedProps<${node.name}Type>(props.parent, { + const node = detachedProps<${node.name}Type>((props.parent: $FlowFixMe), { type: '${type}', ${node.arguments .map(arg => { @@ -122,7 +122,7 @@ export function ${node.name}(props: { .filter(Boolean) .join(',\n')}, }); - setParentPointersInDirectChildren(node); + setParentPointersInDirectChildren((node: $FlowFixMe)); return node; } `, diff --git a/tools/hermes-parser/js/scripts/utils/scriptUtils.js b/tools/hermes-parser/js/scripts/utils/scriptUtils.js index 406ea2fda56..7cbbe4d6f77 100644 --- a/tools/hermes-parser/js/scripts/utils/scriptUtils.js +++ b/tools/hermes-parser/js/scripts/utils/scriptUtils.js @@ -81,7 +81,7 @@ function HEADER(flow: FlowStyle, skipFormat: boolean): string { // lint directives to let us do some basic validation of generated files /* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */ -/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray */ +/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */ 'use strict'; From c72e2d2b0ec670c1a65145d2f1a45604bdf4beb3 Mon Sep 17 00:00:00 2001 From: Danny Su Date: Thu, 7 Mar 2024 14:51:05 -0800 Subject: [PATCH 04/21] Re-enable ProfilerBasicOperation (#1326) Summary: Pull Request resolved: https://github.com/facebook/hermes/pull/1326 The old code had several issues. First, the `registerForProfiling()` and `unregisterForProfiling()` functions should be called on the runtime thread. CircleCI doesn't support privileged mode for their docker environment. This means when the test hangs, I'm unable to attach lldb to it to debug. But I was able to reproduce the issue outside of CircleCI in a Linux VM with docker. It looked like the `unregisterForProfiling()` call might have messed up states that then caused the `SamplingProfiler::resume()` to not complete. This means the runtime thread is blocked. SerialExecutor's destructor will wait for the runtime thread to finish and so once the runtime thread is blocked, the test will never finish due to being stuck in TearDown. Second, the code to set `stopFlag_` needs to be guaranteed to run in all situations. Otherwise the same issue might occur where SerialExecutor cannot finish its destructor. Lastly, the test had checks that aren't deterministic. The sampling profiler doesn't guarantee it will gather any particular samples within the short run of the test, so certain checks need to be removed. Reviewed By: mattbfb Differential Revision: D54276786 fbshipit-source-id: 99b5644c4cddbc3bde8244a7d9e4abaa9b977b2f --- unittests/API/CDPAgentTest.cpp | 75 +++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index 14c3fbd9074..5bd2e0f4204 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -525,6 +525,10 @@ TEST_F(CDPAgentTest, DebuggerScriptsOnEnable) { } TEST_F(CDPAgentTest, DebuggerEnableWhenAlreadyPaused) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; // This needs to be a while-loop because Explicit AsyncBreak will only happen @@ -580,9 +584,6 @@ TEST_F(CDPAgentTest, DebuggerEnableWhenAlreadyPaused) { }); ensureNotification(waitForMessage("Debugger.resumed"), "Debugger.resumed"); - - // break out of loop - stopFlag_.store(true); } TEST_F(CDPAgentTest, DebuggerScriptsOrdering) { @@ -637,6 +638,10 @@ TEST_F(CDPAgentTest, DebuggerBytecodeScript) { } TEST_F(CDPAgentTest, DebuggerAsyncPauseWhileRunning) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; scheduleScript(R"( @@ -668,9 +673,6 @@ TEST_F(CDPAgentTest, DebuggerAsyncPauseWhileRunning) { sendAndCheckResponse("Debugger.resume", msgId++); ensureNotification(waitForMessage(), "Debugger.resumed"); } - - // break out of loop - stopFlag_.store(true); } TEST_F(CDPAgentTest, DebuggerTestDebuggerStatement) { @@ -1587,6 +1589,10 @@ TEST_F(CDPAgentTest, RuntimeRefuseOperationsWithoutEnable) { } TEST_F(CDPAgentTest, RuntimeGetHeapUsage) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; sendAndCheckResponse("Runtime.enable", msgId++); @@ -1621,12 +1627,13 @@ TEST_F(CDPAgentTest, RuntimeGetHeapUsage) { // more than 0. EXPECT_GT(jsonScope_.getNumber(resp, {"result", "usedSize"}), 0); EXPECT_GT(jsonScope_.getNumber(resp, {"result", "totalSize"}), 0); - - // Let the script terminate - stopFlag_.store(true); } TEST_F(CDPAgentTest, RuntimeGlobalLexicalScopeNames) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; sendAndCheckResponse("Runtime.enable", msgId++); @@ -1679,9 +1686,6 @@ TEST_F(CDPAgentTest, RuntimeGlobalLexicalScopeNames) { resp, {"result", "names", std::to_string(index++)}); EXPECT_EQ(name, expectedName); } - - // Let the script terminate - stopFlag_.store(true); } TEST_F(CDPAgentTest, RuntimeCompileScript) { @@ -1876,6 +1880,10 @@ TEST_F(CDPAgentTest, RuntimeGetPropertiesOnlyOwn) { } TEST_F(CDPAgentTest, RuntimeEvaluate) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; // Start a script @@ -1939,9 +1947,6 @@ TEST_F(CDPAgentTest, RuntimeEvaluate) { {"str", PropInfo("string").setValue("\"string\"")}, {"__proto__", PropInfo("object")}}, true); - - // Let the script terminate - stopFlag_.store(true); } TEST_F(CDPAgentTest, RuntimeEvaluateWhilePaused) { @@ -1988,6 +1993,10 @@ TEST_F(CDPAgentTest, RuntimeEvaluateWhilePaused) { } TEST_F(CDPAgentTest, RuntimeEvaluateReturnByValue) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; // Start a script @@ -2018,12 +2027,13 @@ TEST_F(CDPAgentTest, RuntimeEvaluateReturnByValue) { EXPECT_TRUE(jsonValsEQ( jsonScope_.getObject(resp, {"result", "result", "value"}), jsonScope_.parseObject(object))); - - // Let the script terminate - stopFlag_.store(true); } TEST_F(CDPAgentTest, RuntimeEvaluateException) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; // Start a script @@ -2046,9 +2056,6 @@ TEST_F(CDPAgentTest, RuntimeEvaluateException) { EXPECT_GT( jsonScope_.getString(resp, {"result", "exceptionDetails", "text"}).size(), 0); - - // Let the script terminate - stopFlag_.store(true); } TEST_F(CDPAgentTest, RuntimeCallFunctionOnObject) { @@ -2436,12 +2443,20 @@ TEST_F(CDPAgentTest, RuntimeConsoleBuffer) { } } -TEST_F(CDPAgentTest, DISABLED_ProfilerBasicOperation) { - runtime_->registerForProfiling(); - auto clearInDidPause = - llvh::make_scope_exit([this] { runtime_->unregisterForProfiling(); }); +TEST_F(CDPAgentTest, ProfilerBasicOperation) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); int msgId = 1; + waitFor([this](auto promise) { + runtimeThread_->add([this, promise]() { + runtime_->registerForProfiling(); + promise->set_value(true); + }); + }); + scheduleScript(R"( while(!shouldStop()); )"); @@ -2459,17 +2474,9 @@ TEST_F(CDPAgentTest, DISABLED_ProfilerBasicOperation) { auto resp = expectResponse(std::nullopt, msgId++); auto nodes = jsonScope_.getArray(resp, {"result", "profile", "nodes"}); EXPECT_GT(nodes->size(), 0); - EXPECT_LT( + EXPECT_LE( jsonScope_.getNumber(resp, {"result", "profile", "startTime"}), jsonScope_.getNumber(resp, {"result", "profile", "endTime"})); - auto samples = jsonScope_.getArray(resp, {"result", "profile", "samples"}); - auto timeDeltas = - jsonScope_.getArray(resp, {"result", "profile", "timeDeltas"}); - EXPECT_GT(samples->size(), 0); - EXPECT_EQ(samples->size(), timeDeltas->size()); - - // break out of loop - stopFlag_.store(true); } #endif // HERMES_ENABLE_DEBUGGER From dad13216439e82c685417610985dbb047a83376f Mon Sep 17 00:00:00 2001 From: "Alex Taylor (alta)" Date: Thu, 7 Mar 2024 15:27:50 -0800 Subject: [PATCH 05/21] Deploy 0.230.0 to xplat Summary: X-link: https://github.com/facebook/react-native/pull/43373 Changelog: [Internal] Reviewed By: pieterv Differential Revision: D54640661 fbshipit-source-id: 6d09ec126ea55bbe0d7a6b3a2823b1a0ffef7d21 --- tools/hermes-parser/js/.flowconfig | 2 +- .../hermes-eslint/src/scope-manager/referencer/index.js | 1 + tools/hermes-parser/js/package.json | 2 +- tools/hermes-parser/js/yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/hermes-parser/js/.flowconfig b/tools/hermes-parser/js/.flowconfig index 808cd88a1b7..3179d635d6b 100644 --- a/tools/hermes-parser/js/.flowconfig +++ b/tools/hermes-parser/js/.flowconfig @@ -28,7 +28,7 @@ experimental.global_find_ref=true enums=true [version] -^0.229.2 +^0.230.0 [lints] untyped-type-import=error diff --git a/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js b/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js index 271b0c3ea4b..702565501d6 100644 --- a/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js +++ b/tools/hermes-parser/js/hermes-eslint/src/scope-manager/referencer/index.js @@ -10,5 +10,6 @@ 'use strict'; +// $FlowFixMe[prop-missing] export {Referencer} from './Referencer'; export type {ReferencerOptions} from './Referencer'; diff --git a/tools/hermes-parser/js/package.json b/tools/hermes-parser/js/package.json index 213fe14e8b1..307116b0557 100644 --- a/tools/hermes-parser/js/package.json +++ b/tools/hermes-parser/js/package.json @@ -17,7 +17,7 @@ "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-jest": "^25.2.4", "eslint-plugin-prettier": "^4.2.1", - "flow-bin": "^0.229.2", + "flow-bin": "^0.230.0", "glob": "^8.0.3", "jest": "^29.2.2", "jest-specific-snapshot": "^5.0.0", diff --git a/tools/hermes-parser/js/yarn.lock b/tools/hermes-parser/js/yarn.lock index 1b6e0e80d19..f2e2b6981f8 100644 --- a/tools/hermes-parser/js/yarn.lock +++ b/tools/hermes-parser/js/yarn.lock @@ -2947,10 +2947,10 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== -flow-bin@^0.229.2: - version "0.229.2" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.229.2.tgz#6ce5575c8d6fe93390430f9e13da4c06a9a250f4" - integrity sha512-4hjnaApj/BUhosbStw9Tqk0Muf3WCxAnauPOEHvxIfvHWBHJH3wPCCWryPkLPoifjvvcRYYEKPNWHrYF3PkErA== +flow-bin@^0.230.0: + version "0.230.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.230.0.tgz#eefb67215270ab144cd1fa0203061d1687d1cfa6" + integrity sha512-Yx/AnKXR+D4Ieb7xlanfe7cNamJEsuXG6RLWCOl7OSzehMDmrZUuGh2Ycvf4tCKdM1nYt2569p3kkNELiBonaw== flow-enums-runtime@^0.0.6: version "0.0.6" From 4be4c33de06ad8dbbd4d93401b1996a0bff86016 Mon Sep 17 00:00:00 2001 From: Skander Ellouze Date: Thu, 7 Mar 2024 17:03:28 -0800 Subject: [PATCH 06/21] Update test262 tests (#1335) Summary: Upgrade tests262 to the most recent commit. This incorporates tests for recently implemented functions, such as those introduced in ES2023. Pull Request resolved: https://github.com/facebook/hermes/pull/1335 Test Plan: No test plan, since this PR only updates tests. Reviewed By: neildhar Differential Revision: D54544243 Pulled By: avp fbshipit-source-id: 3ed7f0066119b8f743e582f12b3de8cbe951ce3d --- .circleci/config.yml | 4 +- utils/testsuite/testsuite.py | 4 +- utils/testsuite/testsuite_skiplist.py | 112 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 50fdf763784..905c60b4070 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -664,7 +664,7 @@ jobs: # Check out test262 at a pinned revision to reduce flakiness git clone https://github.com/tc39/test262 cd test262 - git checkout 19da3ca0757248f7595ee09d532bb83dd438f2b5 + git checkout 62626e083bd506124aac6c799464d76c2c42851b - run: name: Build Hermes Compiler command: | @@ -691,7 +691,7 @@ jobs: # Check out test262 at a pinned revision to reduce flakiness git clone https://github.com/tc39/test262 cd test262 - git checkout 19da3ca0757248f7595ee09d532bb83dd438f2b5 + git checkout 62626e083bd506124aac6c799464d76c2c42851b - run: name: Run Hermes tests and test262 with Intl command: | diff --git a/utils/testsuite/testsuite.py b/utils/testsuite/testsuite.py index 394b8dff8d9..114e8745b9f 100644 --- a/utils/testsuite/testsuite.py +++ b/utils/testsuite/testsuite.py @@ -292,7 +292,9 @@ def generateSource(content, strict, suite, flags): evalMatcher = re.compile(r"\beval\s*\(") indirectEvalMatcher = re.compile(r"\(.*,\s*eval\)\s*\(") assignEvalMatcher = re.compile(r"=\s*eval\s*;") -withMatcher = re.compile(r"\bwith\s*\(") +withMatcher = re.compile( + r"(? Date: Fri, 8 Mar 2024 10:00:09 -0800 Subject: [PATCH 07/21] Fix lint Summary: Fix a lint by adding the `_` suffix to a protected member. Reviewed By: dannysu Differential Revision: D54655804 fbshipit-source-id: 2af5c5442c766b6027e8e8701f350e7768577c98 --- unittests/API/CDPAgentTest.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index 5bd2e0f4204..974fd12434f 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -87,7 +87,7 @@ class CDPAgentTest : public ::testing::Test { } protected: - static constexpr int32_t kTestExecutionContextId = 1; + static constexpr int32_t kTestExecutionContextId_ = 1; void SetUp() override; void TearDown() override; @@ -173,7 +173,7 @@ void CDPAgentTest::SetUp() { setupRuntimeTestInfra(); cdpAgent_ = CDPAgent::create( - kTestExecutionContextId, + kTestExecutionContextId_, *cdpDebugAPI_, std::bind(&CDPAgentTest::handleRuntimeTask, this, _1), std::bind(&CDPAgentTest::handleResponse, this, _1)); @@ -394,7 +394,7 @@ TEST_F(CDPAgentTest, CDPAgentIssuesStartupTask) { // Trigger the startup task auto cdpAgent = CDPAgent::create( - kTestExecutionContextId, *cdpDebugAPI_, handleTask, handleMessage); + kTestExecutionContextId_, *cdpDebugAPI_, handleTask, handleMessage); ASSERT_TRUE(gotTask); } @@ -408,7 +408,7 @@ TEST_F(CDPAgentTest, CDPAgentIssuesShutdownTask) { OutboundMessageFunc handleMessage = [](const std::string &message) {}; auto cdpAgent = CDPAgent::create( - kTestExecutionContextId, *cdpDebugAPI_, handleTask, handleMessage); + kTestExecutionContextId_, *cdpDebugAPI_, handleTask, handleMessage); // Ignore the startup task gotTask = false; @@ -428,7 +428,7 @@ TEST_F(CDPAgentTest, CDPAgentIssuesCommandHandlingTask) { OutboundMessageFunc handleMessage = [](const std::string &message) {}; auto cdpAgent = CDPAgent::create( - kTestExecutionContextId, *cdpDebugAPI_, handleTask, handleMessage); + kTestExecutionContextId_, *cdpDebugAPI_, handleTask, handleMessage); // Ignore the startup task gotTask = false; @@ -454,7 +454,7 @@ TEST_F(CDPAgentTest, CDPAgentRejectsMalformedMethods) { runtimeThread_->add([this, task]() { task(*runtime_); }); }; cdpAgent = CDPAgent::create( - kTestExecutionContextId, *cdpDebugAPI_, handleTask, handleMessage); + kTestExecutionContextId_, *cdpDebugAPI_, handleTask, handleMessage); // Send a command with no domain delimiter in the method. Just format the // JSON manually, as there is no Request object for this fake method. @@ -479,7 +479,7 @@ TEST_F(CDPAgentTest, CDPAgentRejectsUnknownDomains) { runtimeThread_->add([this, task]() { task(*runtime_); }); }; cdpAgent = CDPAgent::create( - kTestExecutionContextId, *cdpDebugAPI_, handleTask, handleMessage); + kTestExecutionContextId_, *cdpDebugAPI_, handleTask, handleMessage); // Send a command with a properly-formatted domain, but unrecognized by the // CDP Agent. Just format the JSON manually, as there is no Request object @@ -1445,7 +1445,7 @@ TEST_F(CDPAgentTest, DebuggerRestoreState) { // CDPAgent. setupRuntimeTestInfra(); cdpAgent_ = CDPAgent::create( - kTestExecutionContextId, + kTestExecutionContextId_, *cdpDebugAPI_, std::bind(&CDPAgentTest::handleRuntimeTask, this, _1), std::bind(&CDPAgentTest::handleResponse, this, _1), @@ -1673,7 +1673,7 @@ TEST_F(CDPAgentTest, RuntimeGlobalLexicalScopeNames) { "Runtime.globalLexicalScopeNames", msgId, [](::hermes::JSONEmitter &json) { - json.emitKeyValue("executionContextId", kTestExecutionContextId); + json.emitKeyValue("executionContextId", kTestExecutionContextId_); }); auto resp = expectResponse(std::nullopt, msgId++); @@ -2243,7 +2243,7 @@ TEST_F(CDPAgentTest, RuntimeCallFunctionOnExecutionContext) { // Don't have an easy way to copy these, so... req.arguments = std::vector{}; req.arguments->push_back(std::move(ca)); - req.executionContextId = kTestExecutionContextId; + req.executionContextId = kTestExecutionContextId_; cdpAgent_->handleCommand(serializeRuntimeCallFunctionOnRequest(req)); expectResponse(std::nullopt, msgId++); @@ -2331,7 +2331,7 @@ TEST_F(CDPAgentTest, RuntimeConsoleLog) { EXPECT_EQ(jsonScope_.getNumber(note, {"params", "timestamp"}), kTimestamp); EXPECT_EQ( jsonScope_.getNumber(note, {"params", "executionContextId"}), - kTestExecutionContextId); + kTestExecutionContextId_); EXPECT_EQ(jsonScope_.getString(note, {"params", "type"}), "warning"); EXPECT_EQ(jsonScope_.getArray(note, {"params", "args"})->size(), 3); From 370b612d9ea8a155e661778527a58668fc4aa6a6 Mon Sep 17 00:00:00 2001 From: Matt Blagden Date: Fri, 8 Mar 2024 13:29:41 -0800 Subject: [PATCH 08/21] Fix message ID type in tests Summary: Make the JSON helpers use the same type for message IDs as the message structures, preventing integer narrowing. Reviewed By: dannysu Differential Revision: D54653766 fbshipit-source-id: e506eae74999edabcfca917cb5c075a514569fe4 --- unittests/API/CDPJSONHelpers.cpp | 13 ++++++++----- unittests/API/CDPJSONHelpers.h | 16 +++++++++++----- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/unittests/API/CDPJSONHelpers.cpp b/unittests/API/CDPJSONHelpers.cpp index dab92657b66..bcf7f465e70 100644 --- a/unittests/API/CDPJSONHelpers.cpp +++ b/unittests/API/CDPJSONHelpers.cpp @@ -20,7 +20,7 @@ using namespace hermes::parser; namespace facebook { namespace hermes { -void ensureErrorResponse(const std::string &message, int id) { +void ensureErrorResponse(const std::string &message, long long id) { JSLexer::Allocator allocator; JSONFactory factory(allocator); auto response = @@ -28,7 +28,7 @@ void ensureErrorResponse(const std::string &message, int id) { EXPECT_EQ(response.id, id); } -void ensureOkResponse(const std::string &message, int id) { +void ensureOkResponse(const std::string &message, long long id) { JSLexer::Allocator allocator; JSONFactory factory(allocator); auto response = @@ -215,7 +215,7 @@ std::unordered_map ensureProps( void ensureEvalResponse( const std::string &message, - int id, + long long id, const char *expectedValue) { JSLexer::Allocator allocator; JSONFactory factory(allocator); @@ -232,7 +232,7 @@ void ensureEvalResponse( void ensureEvalResponse( const std::string &message, - int id, + long long id, bool expectedValue) { JSLexer::Allocator allocator; JSONFactory factory(allocator); @@ -245,7 +245,10 @@ void ensureEvalResponse( EXPECT_FALSE(resp.exceptionDetails.has_value()); } -void ensureEvalResponse(const std::string &message, int id, int expectedValue) { +void ensureEvalResponse( + const std::string &message, + long long id, + int expectedValue) { JSLexer::Allocator allocator; JSONFactory factory(allocator); auto resp = mustMake( diff --git a/unittests/API/CDPJSONHelpers.h b/unittests/API/CDPJSONHelpers.h index a2f9959e2ec..505a21ab692 100644 --- a/unittests/API/CDPJSONHelpers.h +++ b/unittests/API/CDPJSONHelpers.h @@ -118,8 +118,8 @@ struct PropInfo { std::optional unserializableValue; }; -void ensureErrorResponse(const std::string &message, int id); -void ensureOkResponse(const std::string &message, int id); +void ensureErrorResponse(const std::string &message, long long id); +void ensureOkResponse(const std::string &message, long long id); void ensureNotification(const std::string &message, const std::string &method); @@ -130,10 +130,16 @@ m::debugger::PausedNotification ensurePaused( void ensureEvalResponse( const std::string &message, - int id, + long long id, const char *expectedValue); -void ensureEvalResponse(const std::string &message, int id, bool expectedValue); -void ensureEvalResponse(const std::string &message, int id, int expectedValue); +void ensureEvalResponse( + const std::string &message, + long long id, + bool expectedValue); +void ensureEvalResponse( + const std::string &message, + long long id, + int expectedValue); void ensureEvalException( const std::string &message, From db072fa7544c2a3d08e79c74c758dad04f931f49 Mon Sep 17 00:00:00 2001 From: Matt Blagden Date: Fri, 8 Mar 2024 13:29:41 -0800 Subject: [PATCH 09/21] Validate execution context ID Summary: Validate the execution context ID in messages that contain it. Reviewed By: dannysu Differential Revision: D54649943 fbshipit-source-id: e8f5081a7f0129e96bf7ac08a907c7cc854aa6a0 --- API/hermes/cdp/RuntimeDomainAgent.cpp | 35 +++++++++++++--- API/hermes/cdp/RuntimeDomainAgent.h | 8 ++++ unittests/API/CDPAgentTest.cpp | 57 +++++++++++++++++++++++++++ unittests/API/CDPJSONHelpers.cpp | 3 +- unittests/API/CDPJSONHelpers.h | 4 +- 5 files changed, 99 insertions(+), 8 deletions(-) diff --git a/API/hermes/cdp/RuntimeDomainAgent.cpp b/API/hermes/cdp/RuntimeDomainAgent.cpp index 7c3996bdc74..c1340043b16 100644 --- a/API/hermes/cdp/RuntimeDomainAgent.cpp +++ b/API/hermes/cdp/RuntimeDomainAgent.cpp @@ -338,6 +338,11 @@ void RuntimeDomainAgent::globalLexicalScopeNames( const m::runtime::GlobalLexicalScopeNamesRequest &req) { // Allow this message even if domain is not enabled to match V8 behavior. + if (req.executionContextId.has_value() && + !validateExecutionContextId(*req.executionContextId, req.id)) { + return; + } + const debugger::ProgramState &state = runtime_.getDebugger().getProgramState(); const debugger::LexicalInfo &lexicalInfo = state.getLexicalInfo(0); @@ -368,6 +373,10 @@ void RuntimeDomainAgent::compileScript( if (!checkRuntimeEnabled(req)) { return; } + if (req.executionContextId.has_value() && + !validateExecutionContextId(*req.executionContextId, req.id)) { + return; + } m::runtime::CompileScriptResponse resp; resp.id = req.id; @@ -420,6 +429,11 @@ void RuntimeDomainAgent::getProperties( void RuntimeDomainAgent::evaluate(const m::runtime::EvaluateRequest &req) { // Allow this to be used when domain is not enabled to match V8 behavior. + if (req.contextId.has_value() && + !validateExecutionContextId(*req.contextId, req.id)) { + return; + } + m::runtime::EvaluateResponse resp; resp.id = req.id; @@ -469,12 +483,7 @@ void RuntimeDomainAgent::callFunctionOn( assert( req.executionContextId && "should not be here if both object id and execution context id are missing"); - if (*req.executionContextId != executionContextID_) { - sendResponseToClient(m::makeErrorResponse( - req.id, - m::ErrorCode::InvalidRequest, - "unknown execution context id " + - std::to_string(*req.executionContextId))); + if (!validateExecutionContextId(*req.executionContextId, req.id)) { return; } } @@ -526,6 +535,20 @@ bool RuntimeDomainAgent::checkRuntimeEnabled(const m::Request &req) { return true; } +bool RuntimeDomainAgent::validateExecutionContextId( + m::runtime::ExecutionContextId executionContextId, + long long commandId) { + if (executionContextId == executionContextID_) { + return true; + } + + sendResponseToClient(m::makeErrorResponse( + commandId, + m::ErrorCode::InvalidRequest, + "Unknown execution context id: " + std::to_string(executionContextId))); + return false; +} + std::vector RuntimeDomainAgent::makePropsFromScope( std::pair frameAndScopeIndex, diff --git a/API/hermes/cdp/RuntimeDomainAgent.h b/API/hermes/cdp/RuntimeDomainAgent.h index edff0b436c7..975de7e6762 100644 --- a/API/hermes/cdp/RuntimeDomainAgent.h +++ b/API/hermes/cdp/RuntimeDomainAgent.h @@ -67,6 +67,14 @@ class RuntimeDomainAgent : public DomainAgent { private: bool checkRuntimeEnabled(const m::Request &req); + /// Ensure the provided \p executionContextId matches the one + /// indicated via the constructor. Returns true if they match. + /// Sends an error message with the specified \p commandId + /// and returns false otherwise. + bool validateExecutionContextId( + m::runtime::ExecutionContextId executionContextId, + long long commandId); + std::vector makePropsFromScope( std::pair frameAndScopeIndex, const std::string &objectGroup, diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index 974fd12434f..19b8b6800cd 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -119,6 +119,12 @@ class CDPAgentTest : public ::testing::Test { void expectNothing(); JSONObject *expectNotification(const std::string &method); JSONObject *expectResponse(const std::optional &method, int id); + /// Wait for a message, validate that it is an error with the specified + /// \p messageID, and assert that the error description contains the + /// specified \p substring. + void expectErrorMessageContaining( + const std::string &substring, + long long messageID); void sendRequest( const std::string &method, @@ -289,6 +295,13 @@ JSONObject *CDPAgentTest::expectResponse( return response; } +void CDPAgentTest::expectErrorMessageContaining( + const std::string &substring, + long long messageID) { + std::string errorMessage = ensureErrorResponse(waitForMessage(), messageID); + ASSERT_NE(errorMessage.find(substring), std::string::npos); +} + jsi::Value CDPAgentTest::shouldStop( jsi::Runtime &runtime, const jsi::Value &thisVal, @@ -2479,4 +2492,48 @@ TEST_F(CDPAgentTest, ProfilerBasicOperation) { jsonScope_.getNumber(resp, {"result", "profile", "endTime"})); } +TEST_F(CDPAgentTest, RuntimeValidatesExecutionContextId) { + auto setStopFlag = llvh::make_scope_exit([this] { + // break out of loop + stopFlag_.store(true); + }); + + int msgId = 1; + + // Start a script + sendAndCheckResponse("Runtime.enable", msgId++); + scheduleScript(R"(while(!shouldStop());)"); + + constexpr auto kExecutionContextSubstring = "execution context id"; + + sendRequest( + "Runtime.globalLexicalScopeNames", + msgId, + [](::hermes::JSONEmitter &json) { + json.emitKeyValue("executionContextId", kTestExecutionContextId_ + 1); + }); + expectErrorMessageContaining(kExecutionContextSubstring, msgId++); + + sendRequest("Runtime.compileScript", msgId, [](::hermes::JSONEmitter &json) { + json.emitKeyValue("persistScript", true); + json.emitKeyValue("sourceURL", "none"); + json.emitKeyValue("expression", "1+1"); + json.emitKeyValue("executionContextId", kTestExecutionContextId_ + 1); + }); + expectErrorMessageContaining(kExecutionContextSubstring, msgId++); + + sendRequest("Runtime.evaluate", msgId, [](::hermes::JSONEmitter ¶ms) { + params.emitKeyValue("expression", R"("0: " + globalVar)"); + params.emitKeyValue("contextId", kTestExecutionContextId_ + 1); + }); + expectErrorMessageContaining(kExecutionContextSubstring, msgId++); + + m::runtime::CallFunctionOnRequest req; + req.id = msgId; + req.functionDeclaration = std::string("function(){}"); + req.executionContextId = kTestExecutionContextId_ + 1; + cdpAgent_->handleCommand(serializeRuntimeCallFunctionOnRequest(req)); + expectErrorMessageContaining(kExecutionContextSubstring, msgId++); +} + #endif // HERMES_ENABLE_DEBUGGER diff --git a/unittests/API/CDPJSONHelpers.cpp b/unittests/API/CDPJSONHelpers.cpp index bcf7f465e70..6ffbb253ae2 100644 --- a/unittests/API/CDPJSONHelpers.cpp +++ b/unittests/API/CDPJSONHelpers.cpp @@ -20,12 +20,13 @@ using namespace hermes::parser; namespace facebook { namespace hermes { -void ensureErrorResponse(const std::string &message, long long id) { +std::string ensureErrorResponse(const std::string &message, long long id) { JSLexer::Allocator allocator; JSONFactory factory(allocator); auto response = mustMake(mustParseStrAsJsonObj(message, factory)); EXPECT_EQ(response.id, id); + return response.message; } void ensureOkResponse(const std::string &message, long long id) { diff --git a/unittests/API/CDPJSONHelpers.h b/unittests/API/CDPJSONHelpers.h index 505a21ab692..5a2f6c3cfd1 100644 --- a/unittests/API/CDPJSONHelpers.h +++ b/unittests/API/CDPJSONHelpers.h @@ -118,7 +118,9 @@ struct PropInfo { std::optional unserializableValue; }; -void ensureErrorResponse(const std::string &message, long long id); +/// Ensure that \p message is a an error response with the given \p id, +/// and return the error description. +std::string ensureErrorResponse(const std::string &message, long long id); void ensureOkResponse(const std::string &message, long long id); void ensureNotification(const std::string &message, const std::string &method); From ddd8cd2976892e89e88102e088c1345ef27560f4 Mon Sep 17 00:00:00 2001 From: Aakash Patel Date: Fri, 8 Mar 2024 16:08:35 -0800 Subject: [PATCH 10/21] Fix Array functions with large lengths Summary: `toLength` can return numbers that are greater than uint32_t, so that needs to be returned and checked. SH version: D54667592 Reviewed By: tmikov Differential Revision: D54667593 fbshipit-source-id: 403fed67f962578a105ec339edbab86b62ea5083 --- lib/VM/JSLib/Array.cpp | 20 ++++++++++++++++---- test/hermes/array-functions.js | 3 +++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/VM/JSLib/Array.cpp b/lib/VM/JSLib/Array.cpp index ae480f90c6c..d2c357aa77c 100644 --- a/lib/VM/JSLib/Array.cpp +++ b/lib/VM/JSLib/Array.cpp @@ -567,7 +567,7 @@ arrayPrototypeToLocaleString(void *, Runtime &runtime, NativeArgs args) { return HermesValue::encodeStringValue(*builder->getStringPrimitive()); } -static inline CallResult +static inline CallResult lengthOfArrayLike(Runtime &runtime, Handle O, Handle jsArr) { if (LLVM_LIKELY(jsArr)) { // Fast path for getting the length. @@ -3562,7 +3562,11 @@ arrayPrototypeToReversed(void *, Runtime &runtime, NativeArgs args) { auto len = lenRes.getValue(); // 3. Let A be ArrayCreate(len). - auto ARes = JSArray::create(runtime, len, len); + uint32_t len32 = truncateToUInt32(len); + if (LLVM_UNLIKELY(len32 != len)) { + return runtime.raiseRangeError("invalid array length"); + } + auto ARes = JSArray::create(runtime, len32, len32); if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } @@ -3766,7 +3770,11 @@ arrayPrototypeToSpliced(void *, Runtime &runtime, NativeArgs args) { } // 13. Let A be ArrayCreate(len). - auto ARes = JSArray::create(runtime, lenAfterInsert, lenAfterInsert); + uint32_t lenAfterInsert32 = truncateToUInt32(lenAfterInsert); + if (LLVM_UNLIKELY(lenAfterInsert32 != lenAfterInsert)) { + return runtime.raiseRangeError("invalid array length"); + } + auto ARes = JSArray::create(runtime, lenAfterInsert32, lenAfterInsert32); if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } @@ -3873,7 +3881,11 @@ arrayPrototypeWith(void *, Runtime &runtime, NativeArgs args) { } // 7. Let A be ArrayCreate(len). - auto ARes = JSArray::create(runtime, len, len); + uint32_t len32 = truncateToUInt32(len); + if (LLVM_UNLIKELY(len32 != len)) { + return runtime.raiseRangeError("invalid array length"); + } + auto ARes = JSArray::create(runtime, len32, len32); if (LLVM_UNLIKELY(ARes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } diff --git a/test/hermes/array-functions.js b/test/hermes/array-functions.js index 1f44a0c4e16..f699e04587c 100644 --- a/test/hermes/array-functions.js +++ b/test/hermes/array-functions.js @@ -1193,3 +1193,6 @@ try { print(e.name) } // CHECK-NEXT: RangeError +var obj = {length: 2 ** 32}; +try { Array.prototype.with.call(obj, 0, 123) } catch (e) { print(e.name) } +// CHECK-NEXT: RangeError From 774d0160c4bae9630a0df7533542662a300a0dd2 Mon Sep 17 00:00:00 2001 From: Aakash Patel Date: Mon, 11 Mar 2024 09:44:19 -0700 Subject: [PATCH 11/21] Update intl test skiplist (#1338) Summary: Pull Request resolved: https://github.com/facebook/hermes/pull/1338 Reviewed By: neildhar Differential Revision: D54703576 fbshipit-source-id: cdd126e8ad0c9df20ff4ee919c376182e54be68d --- .../hermes/test/HermesIntlTest262.java | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/android/intltest/java/com/facebook/hermes/test/HermesIntlTest262.java b/android/intltest/java/com/facebook/hermes/test/HermesIntlTest262.java index 7fc7f96f8fb..2b491751f76 100644 --- a/android/intltest/java/com/facebook/hermes/test/HermesIntlTest262.java +++ b/android/intltest/java/com/facebook/hermes/test/HermesIntlTest262.java @@ -152,6 +152,7 @@ private Set getSkipList() { "test262/test/intl402/Collator/constructor-options-throwing-getters.js", "test262/test/intl402/Collator/subclassing.js", "test262/test/intl402/Collator/proto-from-ctor-realm.js", + "test262/test/intl402/Collator/prototype/compare/ignorePunctuation.js", "test262/test/intl402/Collator/prototype/resolvedOptions/order.js")); // Intl.DateTimeFormat @@ -193,7 +194,17 @@ private Set getSkipList() { "test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/hourCycle-timeStyle.js", "test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/order-dayPeriod.js", "test262/test/intl402/DateTimeFormat/prototype/formatRange", - "test262/test/intl402/DateTimeFormat/prototype/formatRangeToParts")); + "test262/test/intl402/DateTimeFormat/prototype/formatRangeToParts", + "test262/test/intl402/DateTimeFormat/prototype/format/offset-timezone-gmt-same.js", + "test262/test/intl402/DateTimeFormat/prototype/format/temporal-zoneddatetime-not-supported.js", + "test262/test/intl402/DateTimeFormat/prototype/formatToParts/offset-timezone-correct.js", + "test262/test/intl402/DateTimeFormat/prototype/formatToParts/temporal-zoneddatetime-not-supported.js", + "test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/hourCycle-default.js", + "test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/offset-timezone-basic.js", + "test262/test/intl402/DateTimeFormat/prototype/resolvedOptions/offset-timezone-change.js", + "test262/test/intl402/DateTimeFormat/timezone-case-insensitive.js", + "test262/test/intl402/Intl/DateTimeFormat/prototype/formatRange/fails-on-distinct-temporal-types.js", + "test262/test/intl402/Intl/DateTimeFormat/prototype/formatRangeToParts/fails-on-distinct-temporal-types.js")); // Intl.NumberFormat skipList.addAll( @@ -294,7 +305,81 @@ private Set getSkipList() { "test262/test/intl402/NumberFormat/prototype/formatToParts/unit-zh-TW.js", "test262/test/intl402/NumberFormat/prototype/formatToParts/percent-en-US.js", "test262/test/intl402/NumberFormat/prototype/formatToParts/engineering-scientific-zh-TW.js", - "test262/test/intl402/NumberFormat/prototype/formatToParts/notation-compact-zh-TW.js")); + "test262/test/intl402/NumberFormat/prototype/formatToParts/notation-compact-zh-TW.js", + "test262/test/intl402/NumberFormat/constructor-option-read-order.js", + "test262/test/intl402/NumberFormat/constructor-options-throwing-getters-rounding-increment.js", + "test262/test/intl402/NumberFormat/constructor-options-throwing-getters-rounding-priority.js", + "test262/test/intl402/NumberFormat/constructor-options-throwing-getters-trailing-zero-display.js", + "test262/test/intl402/NumberFormat/constructor-roundingIncrement-invalid.js", + "test262/test/intl402/NumberFormat/constructor-roundingIncrement.js", + "test262/test/intl402/NumberFormat/constructor-signDisplay-negative.js", + "test262/test/intl402/NumberFormat/constructor-trailingZeroDisplay-invalid.js", + "test262/test/intl402/NumberFormat/constructor-trailingZeroDisplay.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-10.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-100.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-1000.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-2.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-20.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-200.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-2000.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-25.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-250.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-2500.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-5.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-50.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-500.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-increment-5000.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-priority-less-precision.js", + "test262/test/intl402/NumberFormat/prototype/format/format-rounding-priority-more-precision.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-currency-de-DE.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-currency-en-US.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-currency-ja-JP.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-currency-ko-KR.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-currency-zh-TW.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-de-DE.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-en-US.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-ja-JP.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-ko-KR.js", + "test262/test/intl402/NumberFormat/prototype/format/signDisplay-negative-zh-TW.js", + "test262/test/intl402/NumberFormat/prototype/format/useGrouping-extended-de-DE.js", + "test262/test/intl402/NumberFormat/prototype/format/useGrouping-extended-en-IN.js", + "test262/test/intl402/NumberFormat/prototype/format/useGrouping-extended-en-US.js", + "test262/test/intl402/NumberFormat/prototype/format/value-decimal-string.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/builtin.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/en-US.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/invoked-as-func.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/length.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/name.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/nan-arguments-throws.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/prop-desc.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/pt-PT.js", + "test262/test/intl402/NumberFormat/prototype/formatRange/x-greater-than-y-not-throws.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/builtin.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/en-US.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/invoked-as-func.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/length.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/name.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/nan-arguments-throws.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/prop-desc.js", + "test262/test/intl402/NumberFormat/prototype/formatRangeToParts/x-greater-than-y-not-throws.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-de-DE.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-en-US.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-ja-JP.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-ko-KR.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-currency-zh-TW.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-de-DE.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-en-US.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-ja-JP.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-ko-KR.js", + "test262/test/intl402/NumberFormat/prototype/formatToParts/signDisplay-negative-zh-TW.js", + "test262/test/intl402/NumberFormat/prototype/resolvedOptions/basic.js", + "test262/test/intl402/NumberFormat/prototype/resolvedOptions/return-keys-order-default.js", + "test262/test/intl402/NumberFormat/test-option-roundingPriority-mixed-options.js", + "test262/test/intl402/NumberFormat/test-option-roundingPriority.js", + "test262/test/intl402/NumberFormat/test-option-useGrouping-extended.js", + "test262/test/intl402/NumberFormat/test-option-useGrouping.js", + "test262/test/intl402/NumberFormat/throws-for-maximumFractionDigits-over-limit.js", + "test262/test/intl402/NumberFormat/throws-for-minimumFractionDigits-over-limit.js")); // Misc skipList.addAll( From 8c5dae926b77bd063268d6487d7a5170dc2c6cd8 Mon Sep 17 00:00:00 2001 From: Matt Blagden Date: Mon, 11 Mar 2024 12:03:03 -0700 Subject: [PATCH 12/21] Remove duplicates from getLoadedScripts Summary: Track files that have already been reported and skip them. Reviewed By: dannysu Differential Revision: D54686192 fbshipit-source-id: 37ba2f30baafbee0a3c357988fe34a923e8d27f2 --- lib/VM/Debugger/Debugger.cpp | 4 ++++ unittests/API/DebuggerTest.cpp | 15 +++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/VM/Debugger/Debugger.cpp b/lib/VM/Debugger/Debugger.cpp index 55b8545711c..55ffdc0e660 100644 --- a/lib/VM/Debugger/Debugger.cpp +++ b/lib/VM/Debugger/Debugger.cpp @@ -1421,6 +1421,10 @@ auto Debugger::getLoadedScripts() const -> std::vector { // Uninitialized module. continue; } + // Only include a RuntimeModule if it's the root module + if (runtimeModule.getLazyRootModule() != &runtimeModule) { + continue; + } auto *debugInfo = runtimeModule.getBytecode()->getDebugInfo(); if (!debugInfo) { diff --git a/unittests/API/DebuggerTest.cpp b/unittests/API/DebuggerTest.cpp index e6eee3f52cd..102ad357636 100644 --- a/unittests/API/DebuggerTest.cpp +++ b/unittests/API/DebuggerTest.cpp @@ -44,9 +44,12 @@ struct DebuggerAPITest : public ::testing::Test { TestEventObserver observer; DebuggerAPITest() - : rt(makeHermesRuntime(((hermes::vm::RuntimeConfig::Builder()) - .withEnableBlockScoping(true) - .build()))) { + : rt(makeHermesRuntime( + ((hermes::vm::RuntimeConfig::Builder()) + .withEnableBlockScoping(true) + .withCompilationMode( + hermes::vm::CompilationMode::ForceLazyCompilation) + .build()))) { rt->getDebugger().setEventObserver(&observer); } }; @@ -144,7 +147,11 @@ TEST_F(DebuggerAPITest, GetLoadedScriptsTest) { bool foundJavaScript = false; bool foundTestJs = false; - rt->debugJavaScript("var x = 2;", "Test.js", {}); + // Use a script containing a function (in combination with forced lazy + // compilation in the test setup) to cause multiple runtime modules for this + // single script, allowing this test to verify we don't get duplicate + // results. + rt->debugJavaScript("(function(){var x = 2;})()", "Test.js", {}); scripts = rt->getDebugger().getLoadedScripts(); EXPECT_EQ(scripts.size(), 2); for (auto script : scripts) { From 74e09b6aa143160f2055866ae862bf6a9d8826c7 Mon Sep 17 00:00:00 2001 From: Danny Su Date: Mon, 11 Mar 2024 14:09:18 -0700 Subject: [PATCH 13/21] Verify object type response for evalOnCallFrame Summary: Verifying the object response requires using `Runtime.getProperties` so it was left as a TODO when Runtime wasn't implemented yet. Now put the validation back in. Reviewed By: mattbfb Differential Revision: D54702444 fbshipit-source-id: afc303584230c75a1bd7f6daf815a42cd91182d8 --- unittests/API/CDPAgentTest.cpp | 16 ++++++---------- unittests/API/CDPJSONHelpers.cpp | 17 +++++++++++++++++ unittests/API/CDPJSONHelpers.h | 2 ++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index 19b8b6800cd..7fd49f22fd9 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -961,23 +961,19 @@ TEST_F(CDPAgentTest, DebuggerEvalOnCallFrame) { ensureEvalResponse(waitForMessage(), msgId + 1, 42); msgId += 2; - /* TODO: This needs Runtime domain capability // [2.2] run eval statement that returns object frame = 0; - sendEvalRequest(msgId + 0, frame, "objectVar"); - ensureEvalResponse( - waitForMessage(), - msgId + 0, + sendEvalRequest(msgId, frame, "objectVar"); + auto objectId = ensureObjectEvalResponse(waitForMessage(), msgId++); + + getAndEnsureProps( + msgId++, + objectId, {{"number", PropInfo("number").setValue("1")}, {"bool", PropInfo("boolean").setValue("false")}, {"str", PropInfo("string").setValue("\"string\"")}, {"__proto__", PropInfo("object")}}); - // msgId is increased by 2 because expectEvalResponse will make additional - // request with expectProps. - msgId += 2; - */ - // [3] resume sendAndCheckResponse("Debugger.resume", msgId++); ensureNotification(waitForMessage(), "Debugger.resumed"); diff --git a/unittests/API/CDPJSONHelpers.cpp b/unittests/API/CDPJSONHelpers.cpp index 6ffbb253ae2..a5ac7469916 100644 --- a/unittests/API/CDPJSONHelpers.cpp +++ b/unittests/API/CDPJSONHelpers.cpp @@ -261,6 +261,23 @@ void ensureEvalResponse( EXPECT_FALSE(resp.exceptionDetails.has_value()); } +std::string ensureObjectEvalResponse(const std::string &message, int id) { + JSLexer::Allocator allocator; + JSONFactory factory(allocator); + auto resp = mustMake( + mustParseStrAsJsonObj(message, factory)); + + EXPECT_EQ(resp.id, id); + EXPECT_EQ(resp.result.type, "object"); + EXPECT_FALSE(resp.exceptionDetails.has_value()); + + EXPECT_TRUE(resp.result.objectId.has_value()); + EXPECT_TRUE(resp.result.preview.has_value()); + EXPECT_EQ(resp.result.preview->type, "object"); + + return resp.result.objectId.value(); +} + void ensureEvalException( const std::string &message, int id, diff --git a/unittests/API/CDPJSONHelpers.h b/unittests/API/CDPJSONHelpers.h index 5a2f6c3cfd1..ad23f4da807 100644 --- a/unittests/API/CDPJSONHelpers.h +++ b/unittests/API/CDPJSONHelpers.h @@ -143,6 +143,8 @@ void ensureEvalResponse( long long id, int expectedValue); +std::string ensureObjectEvalResponse(const std::string &message, int id); + void ensureEvalException( const std::string &message, int id, From 8e930b0e83e40ed86d3a7d6634684d211d99b985 Mon Sep 17 00:00:00 2001 From: Fumihiko Tanuma Date: Mon, 11 Mar 2024 17:09:37 -0700 Subject: [PATCH 14/21] Use open address for collision resolution in OrderedHashMap (#1345) Summary: Pull Request resolved: https://github.com/facebook/hermes/pull/1345 Change the way OrderedHashMap handles collision; Use linear probing instead of chaining. Couple of things are also modified to make it work. 1. In case for erasing an entry, mark the bucket as deleted and remove it during rehash. We cannot simply make the bucket empty because it will cause "find" to fail when there was collision and do linear probing. 2. rehashIfNecessary is changed to rehash, which now always run rehash even if the new capacity ends up the same size. This is necessary to wipe the deleted buckets. And so, we also use the deleted count to decide whether to rehash or not. 3. Instead, introduce shouldShrink, shouldRehash to decide whether to invoke rehash. 4. In insert(), run rehash before actually inserting the new entry. This is necessary to make sure that there is a room for insert. 5. The inner loop of rehash function iterates on the linked list instead of the hash array. This not only is necessary for reusing the same hash array when the size is the same, but also reduces the iteration count. This can reduce the memory access locality during rehash, but still faster than accessing the entire array. Reviewed By: avp Differential Revision: D53447832 fbshipit-source-id: ce333b8ac829ca11bda99d32bb68fba25f0da541 --- test/hermes/set_regress.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hermes/set_regress.js b/test/hermes/set_regress.js index 0c99f15729b..6fba0c771b5 100644 --- a/test/hermes/set_regress.js +++ b/test/hermes/set_regress.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -// RUN: %hermes -O -gc-sanitize-handles=0 -gc-max-heap=12M %s | %FileCheck --match-full-lines %s +// RUN: %hermes -O -gc-sanitize-handles=0 -gc-max-heap=13M %s | %FileCheck --match-full-lines %s function testCompact() { print("testCompact"); From a1c9acc7ab84e49759e51cd1e309ffa604fbed5b Mon Sep 17 00:00:00 2001 From: Danny Su Date: Mon, 11 Mar 2024 18:16:41 -0700 Subject: [PATCH 15/21] Make sure to catch JSIException in all try/catch cases Summary: We should always make sure to catch JSIException because it is the base JSI class for exceptions. Reviewed By: mattbfb Differential Revision: D54761643 fbshipit-source-id: 9896de276115e06fa0bc2d33471af718a88ae6dc --- API/hermes/cdp/RuntimeDomainAgent.cpp | 11 +++++++---- unittests/API/CDPAgentTest.cpp | 12 ++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/API/hermes/cdp/RuntimeDomainAgent.cpp b/API/hermes/cdp/RuntimeDomainAgent.cpp index c1340043b16..00f5cc1a0c6 100644 --- a/API/hermes/cdp/RuntimeDomainAgent.cpp +++ b/API/hermes/cdp/RuntimeDomainAgent.cpp @@ -385,7 +385,7 @@ void RuntimeDomainAgent::compileScript( std::shared_ptr preparedScript; try { preparedScript = runtime_.prepareJavaScript(source, req.sourceURL); - } catch (const facebook::jsi::JSIException &err) { + } catch (const jsi::JSIException &err) { resp.exceptionDetails = m::runtime::ExceptionDetails(); resp.exceptionDetails->text = err.what(); sendResponseToClient(resp); @@ -452,11 +452,14 @@ void RuntimeDomainAgent::evaluate(const m::runtime::EvaluateRequest &req) { auto remoteObjPtr = m::runtime::makeRemoteObject( runtime_, result, *objTable_, objectGroup, byValue, generatePreview); resp.result = std::move(remoteObjPtr); - } catch (const facebook::jsi::JSError &error) { + } catch (const jsi::JSError &error) { resp.exceptionDetails = m::runtime::ExceptionDetails(); resp.exceptionDetails->text = error.getMessage() + "\n" + error.getStack(); resp.exceptionDetails->exception = m::runtime::makeRemoteObject( runtime_, error.value(), *objTable_, objectGroup, false, false); + } catch (const jsi::JSIException &err) { + resp.exceptionDetails = m::runtime::ExceptionDetails(); + resp.exceptionDetails->text = err.what(); } sendResponseToClient(resp); @@ -502,7 +505,7 @@ void RuntimeDomainAgent::callFunctionOn( evalResult = runtime_.evaluateJavaScript( std::unique_ptr(new jsi::StringBuffer(expression)), kEvaluatedCodeUrl); - } catch (const facebook::jsi::JSError &error) { + } catch (const jsi::JSIException &error) { sendResponseToClient(m::makeErrorResponse( req.id, m::ErrorCode::InternalError, @@ -646,7 +649,7 @@ RuntimeDomainAgent::makePropsFromValue( objectGroup, false, generatePreview); - } catch (const jsi::JSError &err) { + } catch (const jsi::JSIException &err) { // We fetched a property with a getter that threw. Show a placeholder. // We could have added additional info, but the UI quickly gets messy. desc.value = m::runtime::makeRemoteObject( diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index 7fd49f22fd9..5f9a7b9e7bf 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -2065,6 +2065,18 @@ TEST_F(CDPAgentTest, RuntimeEvaluateException) { EXPECT_GT( jsonScope_.getString(resp, {"result", "exceptionDetails", "text"}).size(), 0); + + // Evaluate something that isn't valid JavaScript syntax + sendRequest("Runtime.evaluate", msgId, [](::hermes::JSONEmitter ¶ms) { + params.emitKeyValue("expression", R"(*ptr));)"); + }); + resp = expectResponse(std::nullopt, msgId++); + + // Ensure that we catch parse exception as well + EXPECT_NE( + jsonScope_.getString(resp, {"result", "exceptionDetails", "text"}) + .find("Compiling JS failed"), + std::string::npos); } TEST_F(CDPAgentTest, RuntimeCallFunctionOnObject) { From 1952e59f95385b268f50e82516150c48fc1e8736 Mon Sep 17 00:00:00 2001 From: Danny Su Date: Mon, 11 Mar 2024 18:18:58 -0700 Subject: [PATCH 16/21] Fix duplicate execution bug in RuntimeTaskRunner Summary: RuntimeTaskRunner sets the `alreadyRan` flag after it executes the given task. However, if the given task triggers interrupts, then we get a situation where the same task could be executed twice. Fixing by setting the `alreadyRan` flag first. Reviewed By: fbmal7 Differential Revision: D54771697 fbshipit-source-id: a6ccf21639d62937527eaffe1b6c9f17af831f3a --- API/hermes/RuntimeTaskRunner.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/API/hermes/RuntimeTaskRunner.cpp b/API/hermes/RuntimeTaskRunner.cpp index 23be970ae0e..52b2294c470 100644 --- a/API/hermes/RuntimeTaskRunner.cpp +++ b/API/hermes/RuntimeTaskRunner.cpp @@ -24,11 +24,15 @@ void RuntimeTaskRunner::enqueueTask(RuntimeTask task) { // access to the runtime, implying they are never concurrently executed. std::shared_ptr alreadyRan = std::make_shared(false); - // Ask the integrator to run the task whenenver JavaScript is not running. + // Ask the integrator to run the task whenever JavaScript is not running. enqueueRuntimeTask_([alreadyRan, task](HermesRuntime &runtime) { if (!*alreadyRan) { - task(runtime); + // Make sure to set alreadyRan first before executing the RuntimeTask. The + // task could potentially trigger interrupts by calling into + // HermesRuntime, which might cause the callback queued with + // `triggerInterrupt_TS()` to run. *alreadyRan = true; + task(runtime); } }); @@ -36,8 +40,8 @@ void RuntimeTaskRunner::enqueueTask(RuntimeTask task) { // be interrupted. debugger_.triggerInterrupt_TS([alreadyRan, task](HermesRuntime &runtime) { if (!*alreadyRan) { - task(runtime); *alreadyRan = true; + task(runtime); } }); } From 0335e69ddfc817dea48dc7e6b8b963588b20215c Mon Sep 17 00:00:00 2001 From: Danny Su Date: Mon, 11 Mar 2024 18:26:18 -0700 Subject: [PATCH 17/21] Message response should be at the end Summary: Per https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/public/devtools_protocol/ > If a command invocation results in sending some events (for example, the enable command of certain domains may result in sending of previously buffered events), these are guaranteed to be emitted before the method returns. The response to a command should always be at the very end after sending any notifications. The way V8 has implemented their interfaces guarantees this. The response is always a return value of whatever function that processes it. It is then up to a separate code to take the response and craft the actual response payload. For us, a better guarantee might be easier to tackle when we solve the dispatch logic TODO. For now, just ensure we send the response at the end. Reviewed By: mattbfb Differential Revision: D54758755 fbshipit-source-id: 447888d0040b02cbdec1f4cbab12fdb286c9e964 --- API/hermes/cdp/DebuggerDomainAgent.cpp | 3 ++- API/hermes/cdp/RuntimeDomainAgent.cpp | 2 +- unittests/API/CDPAgentTest.cpp | 20 ++++++++++++++------ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/API/hermes/cdp/DebuggerDomainAgent.cpp b/API/hermes/cdp/DebuggerDomainAgent.cpp index df527cf7dc9..7d9986a62e1 100644 --- a/API/hermes/cdp/DebuggerDomainAgent.cpp +++ b/API/hermes/cdp/DebuggerDomainAgent.cpp @@ -111,7 +111,6 @@ void DebuggerDomainAgent::enable(const m::debugger::EnableRequest &req) { return; } enabled_ = true; - sendResponseToClient(m::makeOkResponse(req.id)); // The debugger just got enabled; inform the client about all scripts. for (auto &srcLoc : runtime_.getDebugger().getLoadedScripts()) { @@ -163,6 +162,8 @@ void DebuggerDomainAgent::enable(const m::debugger::EnableRequest &req) { paused_ = true; sendPausedNotificationToClient(); } + + sendResponseToClient(m::makeOkResponse(req.id)); } void DebuggerDomainAgent::disable(const m::debugger::DisableRequest &req) { diff --git a/API/hermes/cdp/RuntimeDomainAgent.cpp b/API/hermes/cdp/RuntimeDomainAgent.cpp index 00f5cc1a0c6..7d57b4e0dcf 100644 --- a/API/hermes/cdp/RuntimeDomainAgent.cpp +++ b/API/hermes/cdp/RuntimeDomainAgent.cpp @@ -312,8 +312,8 @@ void RuntimeDomainAgent::enable() { void RuntimeDomainAgent::enable(const m::runtime::EnableRequest &req) { // Match V8 behavior of returning success even if domain is already enabled - sendResponseToClient(m::makeOkResponse(req.id)); enable(); + sendResponseToClient(m::makeOkResponse(req.id)); } void RuntimeDomainAgent::disable(const m::runtime::DisableRequest &req) { diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index 5f9a7b9e7bf..f9be46162fd 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -528,13 +528,15 @@ TEST_F(CDPAgentTest, DebuggerScriptsOnEnable) { scheduleScript("true"); // Verify that upon enable, we get notification of existing scripts - sendAndCheckResponse("Debugger.enable", msgId++); + sendParameterlessRequest("Debugger.enable", msgId); ensureNotification(waitForMessage(), "Debugger.scriptParsed"); + ensureOkResponse(waitForMessage(), msgId++); sendAndCheckResponse("Debugger.disable", msgId++); - sendAndCheckResponse("Debugger.enable", msgId++); + sendParameterlessRequest("Debugger.enable", msgId); ensureNotification(waitForMessage(), "Debugger.scriptParsed"); + ensureOkResponse(waitForMessage(), msgId++); } TEST_F(CDPAgentTest, DebuggerEnableWhenAlreadyPaused) { @@ -573,7 +575,7 @@ TEST_F(CDPAgentTest, DebuggerEnableWhenAlreadyPaused) { // we'll test if we can perform Debugger.enable while the runtime is in that // state. - sendAndCheckResponse("Debugger.enable", msgId++); + sendParameterlessRequest("Debugger.enable", msgId); ensureNotification( waitForMessage("Debugger.scriptParsed"), "Debugger.scriptParsed"); @@ -584,6 +586,8 @@ TEST_F(CDPAgentTest, DebuggerEnableWhenAlreadyPaused) { "other", {FrameInfo("global", 0, 1).setLineNumberMax(9)}); + ensureOkResponse(waitForMessage(), msgId++); + // After removing this callback, AsyncDebuggerAPI will still have another // callback registered by CDPAgent. Therefore, JS will not continue by itself. asyncDebuggerAPI.removeDebuggerEventCallback_TS(eventCallbackID); @@ -618,12 +622,13 @@ TEST_F(CDPAgentTest, DebuggerScriptsOrdering) { // Make sure the same ordering is retained after a disable request sendAndCheckResponse("Debugger.disable", msgId++); - sendAndCheckResponse("Debugger.enable", msgId++); + sendParameterlessRequest("Debugger.enable", msgId); for (int i = 0; i < kNumScriptParsed; i++) { std::string notification = waitForMessage(); ensureNotification(notification, "Debugger.scriptParsed"); EXPECT_EQ(notifications[i], notification); } + ensureOkResponse(waitForMessage(), msgId++); } TEST_F(CDPAgentTest, DebuggerBytecodeScript) { @@ -671,8 +676,9 @@ TEST_F(CDPAgentTest, DebuggerAsyncPauseWhileRunning) { var d = -accum; )"); - sendAndCheckResponse("Debugger.enable", msgId++); + sendParameterlessRequest("Debugger.enable", msgId); ensureNotification(waitForMessage(), "Debugger.scriptParsed"); + ensureOkResponse(waitForMessage(), msgId++); // send some number of async pauses, make sure that we always stop before // the end of the loop on line 9 @@ -2418,7 +2424,7 @@ TEST_F(CDPAgentTest, RuntimeConsoleBuffer) { receivedWarning = false; received.fill(false); - sendAndCheckResponse("Runtime.enable", msgId++); + sendParameterlessRequest("Runtime.enable", msgId); // Loop for 1 iteration more than kExpectedMaxBufferSize because there is a // warning message given when buffer is exceeded @@ -2449,6 +2455,8 @@ TEST_F(CDPAgentTest, RuntimeConsoleBuffer) { } } + ensureOkResponse(waitForMessage(), msgId++); + // Make sure no more log messages arrive expectNothing(); From e2caed25fcaea8160eca1545ea8c69401eee31f2 Mon Sep 17 00:00:00 2001 From: Matt Blagden Date: Mon, 11 Mar 2024 21:29:53 -0700 Subject: [PATCH 18/21] Report invalid JSON Summary: Report invalid JSON without a command ID. Reviewed By: dannysu Differential Revision: D54772688 fbshipit-source-id: 7203d935338ac44cd78c285c84854e7358a6aa0e --- API/hermes/cdp/CDPAgent.cpp | 7 ++++--- API/hermes/cdp/MessageInterfaces.h | 3 ++- unittests/API/CDPAgentTest.cpp | 24 ++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/API/hermes/cdp/CDPAgent.cpp b/API/hermes/cdp/CDPAgent.cpp index b5226fdbbb8..d45db458e26 100644 --- a/API/hermes/cdp/CDPAgent.cpp +++ b/API/hermes/cdp/CDPAgent.cpp @@ -198,9 +198,10 @@ void CDPAgentImpl::initializeDomainAgents(State state) { void CDPAgentImpl::handleCommand(std::string json) { std::shared_ptr command = message::Request::fromJson(json); if (!command) { - // Can't even parse the command to get the command ID, so there's no ID - // to respond to with an error message. - // TODO: return an error message + m::ErrorResponse resp; + resp.code = static_cast(message::ErrorCode::ParseError); + resp.message = "Malformed JSON"; + messageCallback_(resp.toJsonStr()); return; } diff --git a/API/hermes/cdp/MessageInterfaces.h b/API/hermes/cdp/MessageInterfaces.h index b462daeff1e..f19418f5794 100644 --- a/API/hermes/cdp/MessageInterfaces.h +++ b/API/hermes/cdp/MessageInterfaces.h @@ -9,6 +9,7 @@ #define HERMES_CDP_MESSAGEINTERFACES_H #include +#include #include #include #include @@ -53,7 +54,7 @@ struct Request : public Serializable { struct Response : public Serializable { Response() = default; - long long id = 0; + std::optional id = std::nullopt; }; /// Notifications are sent from the target to the debugger. This is used to diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index f9be46162fd..a1997e64aae 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -452,6 +452,30 @@ TEST_F(CDPAgentTest, CDPAgentIssuesCommandHandlingTask) { ASSERT_TRUE(gotTask); } +TEST_F(CDPAgentTest, CDPAgentRejectsMalformedJson) { + std::unique_ptr cdpAgent; + + waitFor([this, &cdpAgent](auto promise) { + OutboundMessageFunc handleMessage = [this, + promise](const std::string &message) { + // Ensure the invalid JSON is reported + JSONObject *resp = jsonScope_.parseObject(message); + EXPECT_EQ( + jsonScope_.getString(resp, {"error", "message"}), "Malformed JSON"); + promise->set_value(true); + }; + + EnqueueRuntimeTaskFunc handleTask = [this](RuntimeTask task) { + runtimeThread_->add([this, task]() { task(*runtime_); }); + }; + cdpAgent = CDPAgent::create( + kTestExecutionContextId_, *cdpDebugAPI_, handleTask, handleMessage); + + // Send a command that's not valid JSON + cdpAgent->handleCommand("_"); + }); +} + TEST_F(CDPAgentTest, CDPAgentRejectsMalformedMethods) { int commandID = 1; std::unique_ptr cdpAgent; From 5f3fbb0661e148ea895e80130acd2499a8d14a24 Mon Sep 17 00:00:00 2001 From: Matt Blagden Date: Mon, 11 Mar 2024 21:51:56 -0700 Subject: [PATCH 19/21] Avoid querying non-existent frames Summary: Don't call `getLexicalInfo` with an invalid frame index. Reviewed By: dannysu Differential Revision: D54770056 fbshipit-source-id: 3d42bc1bfe247022607041b8da703f7492f347d5 --- API/hermes/AsyncDebuggerAPI.cpp | 4 +++ API/hermes/AsyncDebuggerAPI.h | 5 ++++ API/hermes/cdp/CDPAgent.cpp | 1 + API/hermes/cdp/RuntimeDomainAgent.cpp | 41 +++++++++++++++++++++++++-- API/hermes/cdp/RuntimeDomainAgent.h | 6 +++- unittests/API/CDPAgentTest.cpp | 16 +++++++++++ 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/API/hermes/AsyncDebuggerAPI.cpp b/API/hermes/AsyncDebuggerAPI.cpp index 4d47f719b4b..c997b094426 100644 --- a/API/hermes/AsyncDebuggerAPI.cpp +++ b/API/hermes/AsyncDebuggerAPI.cpp @@ -91,6 +91,10 @@ bool AsyncDebuggerAPI::isWaitingForCommand() { return isWaitingForCommand_; } +bool AsyncDebuggerAPI::isPaused() { + return inDidPause_; +} + bool AsyncDebuggerAPI::resumeFromPaused(AsyncDebugCommand command) { if (!isWaitingForCommand_) { return false; diff --git a/API/hermes/AsyncDebuggerAPI.h b/API/hermes/AsyncDebuggerAPI.h index 991521b7344..9143834a0c3 100644 --- a/API/hermes/AsyncDebuggerAPI.h +++ b/API/hermes/AsyncDebuggerAPI.h @@ -117,6 +117,11 @@ class HERMES_EXPORT AsyncDebuggerAPI : private debugger::EventObserver { /// Should only be called from the runtime thread. bool isWaitingForCommand(); + /// Whether the runtime is currently paused for any reason (e.g. script + /// parsed, running interrupts, or waiting for a command). + /// Should only be called from the runtime thread. + bool isPaused(); + /// Provide the next action to perform. Should only be called from the runtime /// thread and only if the next command is expected to be set. bool resumeFromPaused(AsyncDebugCommand command); diff --git a/API/hermes/cdp/CDPAgent.cpp b/API/hermes/cdp/CDPAgent.cpp index d45db458e26..10e577e72c4 100644 --- a/API/hermes/cdp/CDPAgent.cpp +++ b/API/hermes/cdp/CDPAgent.cpp @@ -259,6 +259,7 @@ void CDPAgentImpl::DomainAgents::initialize(State state) { runtimeAgent_ = std::make_unique( executionContextID_, runtime_, + asyncDebuggerAPI_, messageCallback_, objTable_, consoleMessageStorage_, diff --git a/API/hermes/cdp/RuntimeDomainAgent.cpp b/API/hermes/cdp/RuntimeDomainAgent.cpp index 7d57b4e0dcf..b2bddd373a7 100644 --- a/API/hermes/cdp/RuntimeDomainAgent.cpp +++ b/API/hermes/cdp/RuntimeDomainAgent.cpp @@ -254,6 +254,7 @@ class CallFunctionOnBuilder { RuntimeDomainAgent::RuntimeDomainAgent( int32_t executionContextID, HermesRuntime &runtime, + debugger::AsyncDebuggerAPI &asyncDebuggerAPI, SynchronizedOutboundCallback messageCallback, std::shared_ptr objTable, ConsoleMessageStorage &consoleMessageStorage, @@ -263,6 +264,7 @@ RuntimeDomainAgent::RuntimeDomainAgent( std::move(messageCallback), std::move(objTable)), runtime_(runtime), + asyncDebuggerAPI_(asyncDebuggerAPI), consoleMessageStorage_(consoleMessageStorage), consoleMessageDispatcher_(consoleMessageDispatcher), enabled_(false) { @@ -343,11 +345,24 @@ void RuntimeDomainAgent::globalLexicalScopeNames( return; } + if (!asyncDebuggerAPI_.isPaused()) { + sendResponseToClient(m::makeErrorResponse( + req.id, + m::ErrorCode::InvalidRequest, + "Cannot get global scope names unless execution is paused")); + return; + } + const debugger::ProgramState &state = runtime_.getDebugger().getProgramState(); + assert( + state.getStackTrace().callFrameCount() > 0 && + "Paused with no call frames"); const debugger::LexicalInfo &lexicalInfo = state.getLexicalInfo(0); debugger::ScopeDepth scopeCount = lexicalInfo.getScopesCount(); if (scopeCount == 0) { + sendResponseToClient(m::makeErrorResponse( + req.id, m::ErrorCode::InvalidRequest, "No scope descriptor")); return; } const debugger::ScopeDepth globalScopeIndex = scopeCount - 1; @@ -415,10 +430,26 @@ void RuntimeDomainAgent::getProperties( m::runtime::GetPropertiesResponse resp; resp.id = req.id; if (scopePtr != nullptr) { + if (!asyncDebuggerAPI_.isPaused()) { + sendResponseToClient(m::makeErrorResponse( + req.id, + m::ErrorCode::InvalidRequest, + "Cannot get scope properties unless execution is paused")); + return; + } const debugger::ProgramState &state = runtime_.getDebugger().getProgramState(); - resp.result = + auto result = makePropsFromScope(*scopePtr, objGroup, state, generatePreview); + if (!result) { + sendResponseToClient(m::makeErrorResponse( + req.id, + m::ErrorCode::InvalidRequest, + "Could not inspect specified scope")); + return; + } + resp.result = std::move(*result); + } else if (valuePtr != nullptr) { resp.result = makePropsFromValue(*valuePtr, objGroup, ownProperties, generatePreview); @@ -552,7 +583,7 @@ bool RuntimeDomainAgent::validateExecutionContextId( return false; } -std::vector +std::optional> RuntimeDomainAgent::makePropsFromScope( std::pair frameAndScopeIndex, const std::string &objectGroup, @@ -566,7 +597,13 @@ RuntimeDomainAgent::makePropsFromScope( uint32_t frameIndex = frameAndScopeIndex.first; uint32_t scopeIndex = frameAndScopeIndex.second; + if (frameIndex >= state.getStackTrace().callFrameCount()) { + return std::nullopt; + } debugger::LexicalInfo lexicalInfo = state.getLexicalInfo(frameIndex); + if (scopeIndex >= lexicalInfo.getScopesCount()) { + return std::nullopt; + } uint32_t varCount = lexicalInfo.getVariablesCountInScope(scopeIndex); // If this is the frame's local scope, include 'this'. diff --git a/API/hermes/cdp/RuntimeDomainAgent.h b/API/hermes/cdp/RuntimeDomainAgent.h index 975de7e6762..b4065ff9c32 100644 --- a/API/hermes/cdp/RuntimeDomainAgent.h +++ b/API/hermes/cdp/RuntimeDomainAgent.h @@ -8,6 +8,8 @@ #ifndef HERMES_CDP_RUNTIMEDOMAINAGENT_H #define HERMES_CDP_RUNTIMEDOMAINAGENT_H +#include + #include "CDPDebugAPI.h" #include "DomainAgent.h" @@ -26,6 +28,7 @@ class RuntimeDomainAgent : public DomainAgent { RuntimeDomainAgent( int32_t executionContextID, HermesRuntime &runtime, + debugger::AsyncDebuggerAPI &asyncDebuggerAPI, SynchronizedOutboundCallback messageCallback, std::shared_ptr objTable, ConsoleMessageStorage &consoleMessageStorage, @@ -75,7 +78,7 @@ class RuntimeDomainAgent : public DomainAgent { m::runtime::ExecutionContextId executionContextId, long long commandId); - std::vector makePropsFromScope( + std::optional> makePropsFromScope( std::pair frameAndScopeIndex, const std::string &objectGroup, const debugger::ProgramState &state, @@ -87,6 +90,7 @@ class RuntimeDomainAgent : public DomainAgent { bool generatePreview); HermesRuntime &runtime_; + debugger::AsyncDebuggerAPI &asyncDebuggerAPI_; ConsoleMessageStorage &consoleMessageStorage_; ConsoleMessageDispatcher &consoleMessageDispatcher_; diff --git a/unittests/API/CDPAgentTest.cpp b/unittests/API/CDPAgentTest.cpp index a1997e64aae..be72d274847 100644 --- a/unittests/API/CDPAgentTest.cpp +++ b/unittests/API/CDPAgentTest.cpp @@ -1727,6 +1727,22 @@ TEST_F(CDPAgentTest, RuntimeGlobalLexicalScopeNames) { } } +TEST_F(CDPAgentTest, RuntimeGlobalLexicalScopeNamesOnEmptyStack) { + int msgId = 1; + + sendAndCheckResponse("Runtime.enable", msgId++); + + sendRequest( + "Runtime.globalLexicalScopeNames", + msgId, + [](::hermes::JSONEmitter &json) { + json.emitKeyValue("executionContextId", kTestExecutionContextId_); + }); + + // Can't get lexical scopes on an empty stack. + ensureErrorResponse(waitForMessage(), msgId); +} + TEST_F(CDPAgentTest, RuntimeCompileScript) { int msgId = 1; From b0fb9c957446522bfbe0392a6f8a6ccfd7fc6b0c Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Tue, 12 Mar 2024 08:13:54 -0700 Subject: [PATCH 20/21] CDPAgent::enableDebuggerDomain Summary: Adds the `CDPAgent::enableDebuggerDomain` as a direct parallel of the existing `CDPAgent::enableRuntimeDomain`. Reviewed By: robhogan Differential Revision: D54712524 fbshipit-source-id: efbfd0775d59803847d5478f355c197cbfeafae6 --- API/hermes/cdp/CDPAgent.cpp | 32 ++++++++++++++++++++++---- API/hermes/cdp/CDPAgent.h | 8 +++++-- API/hermes/cdp/DebuggerDomainAgent.cpp | 7 ++++-- API/hermes/cdp/DebuggerDomainAgent.h | 3 +++ API/hermes/cdp/RuntimeDomainAgent.h | 2 +- 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/API/hermes/cdp/CDPAgent.cpp b/API/hermes/cdp/CDPAgent.cpp index 10e577e72c4..db4cb5975e5 100644 --- a/API/hermes/cdp/CDPAgent.cpp +++ b/API/hermes/cdp/CDPAgent.cpp @@ -63,10 +63,14 @@ class CDPAgentImpl { /// Process a CDP command encoded in \p json. void handleCommand(std::string json); - /// Enable the Runtime domain without processing a CDP command or send a CDP - /// response. + /// Enable the Runtime domain without processing a CDP command or sending a + /// CDP response. void enableRuntimeDomain(); + /// Enable the Debugger domain without processing a CDP command or sending a + /// CDP response. + void enableDebuggerDomain(); + /// Extract state to be persisted across reloads. State getState(); @@ -92,10 +96,14 @@ class CDPAgentImpl { /// handler. void handleCommand(std::shared_ptr command); - /// Enable the Runtime domain without processing a CDP command or send a CDP - /// response. + /// Enable the Runtime domain without processing a CDP command or sending a + /// CDP response. void enableRuntimeDomain(); + /// Enable the Debugger domain without processing a CDP command or sending a + /// CDP response. + void enableDebuggerDomain(); + /// Get the Debugger domain state to be persisted. std::unique_ptr getDebuggerDomainState(); @@ -220,6 +228,13 @@ void CDPAgentImpl::enableRuntimeDomain() { }); } +void CDPAgentImpl::enableDebuggerDomain() { + runtimeTaskRunner_.enqueueTask( + [domainAgents = domainAgents_](HermesRuntime &) { + domainAgents->enableDebuggerDomain(); + }); +} + State CDPAgentImpl::getState() { // This function might not be called on the runtime thread. Functions on // DomainAgents expect to be called on the runtime thread because they @@ -366,6 +381,11 @@ void CDPAgentImpl::DomainAgents::enableRuntimeDomain() { runtimeAgent_->enable(); } +void CDPAgentImpl::DomainAgents::enableDebuggerDomain() { + std::lock_guard lock(mutex_); + debuggerAgent_->enable(); +} + std::unique_ptr CDPAgentImpl::DomainAgents::getDebuggerDomainState() { std::lock_guard lock(mutex_); @@ -413,6 +433,10 @@ void CDPAgent::enableRuntimeDomain() { impl_->enableRuntimeDomain(); } +void CDPAgent::enableDebuggerDomain() { + impl_->enableDebuggerDomain(); +} + State CDPAgent::getState() { return impl_->getState(); } diff --git a/API/hermes/cdp/CDPAgent.h b/API/hermes/cdp/CDPAgent.h index 386208269d8..56b03874ff5 100644 --- a/API/hermes/cdp/CDPAgent.h +++ b/API/hermes/cdp/CDPAgent.h @@ -101,10 +101,14 @@ class HERMES_EXPORT CDPAgent { /// arbitrary threads. void handleCommand(std::string json); - /// Enable the Runtime domain without processing a CDP command or send a CDP - /// response. This can be called from arbitrary threads. + /// Enable the Runtime domain without processing a CDP command or sending a + /// CDP response. This can be called from arbitrary threads. void enableRuntimeDomain(); + /// Enable the Debugger domain without processing a CDP command or sending a + /// CDP response. This can be called from arbitrary threads. + void enableDebuggerDomain(); + /// Extract state to be persisted across reloads. This can be called from /// arbitrary threads. State getState(); diff --git a/API/hermes/cdp/DebuggerDomainAgent.cpp b/API/hermes/cdp/DebuggerDomainAgent.cpp index 7d9986a62e1..79e33819442 100644 --- a/API/hermes/cdp/DebuggerDomainAgent.cpp +++ b/API/hermes/cdp/DebuggerDomainAgent.cpp @@ -105,9 +105,8 @@ std::unique_ptr DebuggerDomainAgent::getState() { return state; } -void DebuggerDomainAgent::enable(const m::debugger::EnableRequest &req) { +void DebuggerDomainAgent::enable() { if (enabled_) { - sendResponseToClient(m::makeOkResponse(req.id)); return; } enabled_ = true; @@ -162,7 +161,11 @@ void DebuggerDomainAgent::enable(const m::debugger::EnableRequest &req) { paused_ = true; sendPausedNotificationToClient(); } +} +void DebuggerDomainAgent::enable(const m::debugger::EnableRequest &req) { + // Match V8 behavior of returning success even if domain is already enabled + enable(); sendResponseToClient(m::makeOkResponse(req.id)); } diff --git a/API/hermes/cdp/DebuggerDomainAgent.h b/API/hermes/cdp/DebuggerDomainAgent.h index 3d2e8f7bb65..37da0cdd08b 100644 --- a/API/hermes/cdp/DebuggerDomainAgent.h +++ b/API/hermes/cdp/DebuggerDomainAgent.h @@ -90,6 +90,9 @@ class DebuggerDomainAgent : public DomainAgent { /// Extract state to be persisted across reloads. std::unique_ptr getState(); + /// Enables the Debugger domain without processing CDP message or sending a + /// CDP response. It will still send CDP notifications if needed. + void enable(); /// Handles Debugger.enable request /// @cdp Debugger.enable If domain is already enabled, will return success. void enable(const m::debugger::EnableRequest &req); diff --git a/API/hermes/cdp/RuntimeDomainAgent.h b/API/hermes/cdp/RuntimeDomainAgent.h index b4065ff9c32..4a69e143baf 100644 --- a/API/hermes/cdp/RuntimeDomainAgent.h +++ b/API/hermes/cdp/RuntimeDomainAgent.h @@ -35,7 +35,7 @@ class RuntimeDomainAgent : public DomainAgent { ConsoleMessageDispatcher &consoleMessageDispatcher); ~RuntimeDomainAgent(); - /// Enables the Runtime domain without processing CDP message or send a CDP + /// Enables the Runtime domain without processing CDP message or sending a CDP /// response. It will still send CDP notifications if needed. void enable(); /// Handles Runtime.enable request From b8cd4ffb935d9f60e3b1da8c28f60c9e093b40ae Mon Sep 17 00:00:00 2001 From: Matt Blagden Date: Tue, 12 Mar 2024 10:32:54 -0700 Subject: [PATCH 21/21] Fix getLoadedScripts test Summary: The `getLoadedScripts` test was invoking the observer that was installed in the `DebuggerAPITest` setup, and inspecting program state at an inopportune time. Make the test construct its own runtime instead. Reviewed By: ftanuma Differential Revision: D54800717 fbshipit-source-id: c2a438e3744601857792e770c63523cc3b914ee4 --- unittests/API/DebuggerTest.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/unittests/API/DebuggerTest.cpp b/unittests/API/DebuggerTest.cpp index 102ad357636..044f4523c32 100644 --- a/unittests/API/DebuggerTest.cpp +++ b/unittests/API/DebuggerTest.cpp @@ -44,12 +44,9 @@ struct DebuggerAPITest : public ::testing::Test { TestEventObserver observer; DebuggerAPITest() - : rt(makeHermesRuntime( - ((hermes::vm::RuntimeConfig::Builder()) - .withEnableBlockScoping(true) - .withCompilationMode( - hermes::vm::CompilationMode::ForceLazyCompilation) - .build()))) { + : rt(makeHermesRuntime(((hermes::vm::RuntimeConfig::Builder()) + .withEnableBlockScoping(true) + .build()))) { rt->getDebugger().setEventObserver(&observer); } }; @@ -135,11 +132,21 @@ TEST_F(DebuggerAPITest, SingleFrameStackTraceTest) { } TEST_F(DebuggerAPITest, GetLoadedScriptsTest) { - auto scripts = rt->getDebugger().getLoadedScripts(); + // Don't use the member runtime, as we don't want to + // trigger the observer on script loads. + std::unique_ptr runtime = makeHermesRuntime( + ((hermes::vm::RuntimeConfig::Builder()) + .withCompilationMode( + hermes::vm::CompilationMode::ForceLazyCompilation) + .build())); + + auto scripts = runtime->getDebugger().getLoadedScripts(); EXPECT_EQ(scripts.size(), 0); - eval("var x = 1;"); - scripts = rt->getDebugger().getLoadedScripts(); + runtime->global() + .getPropertyAsFunction(*runtime, "eval") + .call(*runtime, "var x = 1;"); + scripts = runtime->getDebugger().getLoadedScripts(); EXPECT_EQ(scripts.size(), 1); EXPECT_EQ(scripts[0].line, 1); EXPECT_EQ(scripts[0].column, 1); @@ -151,8 +158,8 @@ TEST_F(DebuggerAPITest, GetLoadedScriptsTest) { // compilation in the test setup) to cause multiple runtime modules for this // single script, allowing this test to verify we don't get duplicate // results. - rt->debugJavaScript("(function(){var x = 2;})()", "Test.js", {}); - scripts = rt->getDebugger().getLoadedScripts(); + runtime->debugJavaScript("(function(){var x = 2;})()", "Test.js", {}); + scripts = runtime->getDebugger().getLoadedScripts(); EXPECT_EQ(scripts.size(), 2); for (auto script : scripts) { if (script.fileName == "JavaScript") {