diff --git a/external/llvh/lib/Support/SourceMgr.cpp b/external/llvh/lib/Support/SourceMgr.cpp index d661ce46941..4da26fb2241 100644 --- a/external/llvh/lib/Support/SourceMgr.cpp +++ b/external/llvh/lib/Support/SourceMgr.cpp @@ -126,7 +126,7 @@ std::pair SourceMgr::SrcBuffer::getLineNumber( EOL != Offsets->end() ? BufStart + *EOL + 1 : Buffer->getBufferEnd(); // Lines count from 1, so add 1 to the distance from the 0th line. - return {StringRef(LineStart, LineEnd - LineStart), (1 + (EOL - Offsets->begin()))}; + return {StringRef(LineStart, LineEnd - LineStart), static_cast((1 + (EOL - Offsets->begin())))}; } template @@ -219,7 +219,7 @@ std::pair SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const { auto LineRefAndNo = FindLine(Loc, BufferID); return std::make_pair(LineRefAndNo.second, - Loc.getPointer() - LineRefAndNo.first.data() + 1); + static_cast(Loc.getPointer() - LineRefAndNo.first.data() + 1)); } void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const { @@ -285,8 +285,8 @@ SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind, // Translate from SMLoc ranges to column ranges. // FIXME: Handle multibyte characters. - ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart, - R.End.getPointer()-LineStart)); + ColRanges.push_back(std::make_pair(static_cast(R.Start.getPointer()-LineStart), + static_cast(R.End.getPointer()-LineStart))); } LineAndCol = getLineAndColumn(Loc, CurBuf); diff --git a/external/llvh/patches/SourceMgr-msvc-warnings.patch b/external/llvh/patches/SourceMgr-msvc-warnings.patch new file mode 100644 index 00000000000..f3c4ab8ac21 --- /dev/null +++ b/external/llvh/patches/SourceMgr-msvc-warnings.patch @@ -0,0 +1,32 @@ +diff --git a/xplat/hermes/external/llvh/lib/Support/SourceMgr.cpp b/xplat/hermes/external/llvh/lib/Support/SourceMgr.cpp +--- a/xplat/hermes/external/llvh/lib/Support/SourceMgr.cpp ++++ b/xplat/hermes/external/llvh/lib/Support/SourceMgr.cpp +@@ -126,7 +126,7 @@ + EOL != Offsets->end() ? BufStart + *EOL + 1 : Buffer->getBufferEnd(); + + // Lines count from 1, so add 1 to the distance from the 0th line. +- return {StringRef(LineStart, LineEnd - LineStart), (1 + (EOL - Offsets->begin()))}; ++ return {StringRef(LineStart, LineEnd - LineStart), static_cast((1 + (EOL - Offsets->begin())))}; + } + + template +@@ -219,7 +219,7 @@ + SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const { + auto LineRefAndNo = FindLine(Loc, BufferID); + return std::make_pair(LineRefAndNo.second, +- Loc.getPointer() - LineRefAndNo.first.data() + 1); ++ static_cast(Loc.getPointer() - LineRefAndNo.first.data() + 1)); + } + + void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const { +@@ -285,8 +285,8 @@ + + // Translate from SMLoc ranges to column ranges. + // FIXME: Handle multibyte characters. +- ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart, +- R.End.getPointer()-LineStart)); ++ ColRanges.push_back(std::make_pair(static_cast(R.Start.getPointer()-LineStart), ++ static_cast(R.End.getPointer()-LineStart))); + } + + LineAndCol = getLineAndColumn(Loc, CurBuf); diff --git a/external/zip/src/zip.h b/external/zip/src/zip.h index 1ef60eb0c79..e5909ae0142 100644 --- a/external/zip/src/zip.h +++ b/external/zip/src/zip.h @@ -313,7 +313,7 @@ extern int zip_entries_total(struct zip_t *zip); * @return the number of deleted entries, or negative number (< 0) on error. */ extern int zip_entries_delete(struct zip_t *zip, char *const entries[], - size_t len); + const size_t len); /** * Extracts a zip archive stream into directory. diff --git a/include/hermes/BCGen/HBC/BytecodeProviderFromSrc.h b/include/hermes/BCGen/HBC/BytecodeProviderFromSrc.h index 070af43dcff..5a5020ff690 100644 --- a/include/hermes/BCGen/HBC/BytecodeProviderFromSrc.h +++ b/include/hermes/BCGen/HBC/BytecodeProviderFromSrc.h @@ -219,7 +219,7 @@ class BCProviderLazy final : public BCProviderBase { /// No debug information will be available without compiling it. void createDebugInfo() override { - llvm_unreachable("Accessing debug info from a lazy module"); + hermes_fatal("Accessing debug info from a lazy module"); } public: @@ -234,20 +234,20 @@ class BCProviderLazy final : public BCProviderBase { } StringTableEntry getStringTableEntry(uint32_t index) const override { - llvm_unreachable("Accessing string table from a lazy module"); + hermes_fatal("Accessing string table from a lazy module"); } const uint8_t *getBytecode(uint32_t) const override { - llvm_unreachable("Accessing bytecode from a lazy module"); + hermes_fatal("Accessing bytecode from a lazy module"); } llvh::ArrayRef getExceptionTable( uint32_t) const override { - llvm_unreachable("Accessing exception info from a lazy module"); + hermes_fatal("Accessing exception info from a lazy module"); } const hbc::DebugOffsets *getDebugOffsets(uint32_t) const override { - llvm_unreachable("Accessing debug offsets from a lazy module"); + hermes_fatal("Accessing debug offsets from a lazy module"); } bool isFunctionLazy(uint32_t) const override { diff --git a/include/hermes/Support/JSON.h b/include/hermes/Support/JSON.h index 9fe24863576..bbfc9d1d147 100644 --- a/include/hermes/Support/JSON.h +++ b/include/hermes/Support/JSON.h @@ -8,8 +8,50 @@ #ifndef HERMES_SUPPORT_JSON_H #define HERMES_SUPPORT_JSON_H +#include "hermes/Platform/Unicode/CharacterProperties.h" + namespace hermes { +template +void appendUTF16Escaped(Output &output, char16_t cp) { + auto toLowerHex = [](uint8_t u) { + assert(u <= 0xF); + return u"0123456789abcdef"[u]; + }; + output.append({u'\\', u'u'}); + output.push_back(toLowerHex(cp >> 12)); + output.push_back(toLowerHex((cp >> 8) & 0xF)); + output.push_back(toLowerHex((cp >> 4) & 0xF)); + output.push_back(toLowerHex(cp & 0xF)); +} + +// If there is a valid surrogate pair at position \p i in \p view, then write +// both the high and low surrogate into \p output. Otherwise, write an escaped +// UTF16 value into \p output. \return true if a pair was found. +template +bool handleSurrogate(Output &output, StringView view, size_t i) { + char16_t ch = view[i]; + assert( + ch >= UNICODE_SURROGATE_FIRST && ch <= UNICODE_SURROGATE_LAST && + "charcter should be a surrogate character"); + // Handle well-formed-ness here: Represent unpaired surrogate code points as + // JSON escape sequences. + if (isHighSurrogate(ch) && i + 1 < view.length()) { + char16_t next = view[i + 1]; + if (isLowSurrogate(next)) { + // We found a surrogate pair. Simply write both of them unescaped to the + // output. + output.push_back(ch); + output.push_back(next); + return true; + } + } + // We did not find a valid pair, so the current surrogate character must be + // written as an escaped JSON sequence. + appendUTF16Escaped(output, ch); + return false; +} + /// Quotes a string given by \p view and puts the quoted version into \p output. /// \p view should be utf16-encoded, and \p output will be as well. /// \post output is a container that has a sequential list of utf16 characters @@ -19,7 +61,8 @@ void quoteStringForJSON(Output &output, StringView view) { // Quote.1. output.push_back(u'"'); // Quote.2. - for (char16_t ch : view) { + for (size_t i = 0; i < view.length(); i++) { + char16_t ch = view[i]; #define ESCAPE(ch, replace) \ case ch: \ output.push_back(u'\\'); \ @@ -47,8 +90,15 @@ void quoteStringForJSON(Output &output, StringView view) { output.push_back(u'a' + (ch % 16 - 10)); } } else { - // Quote.2.d. - output.push_back(ch); + if (ch >= UNICODE_SURROGATE_FIRST && ch <= UNICODE_SURROGATE_LAST) { + if (handleSurrogate(output, view, i)) { + // Found a valid surrogate pair, so skip over the next character. + i++; + } + } else { + // Quote.2.d. + output.push_back(ch); + } } } } diff --git a/include/hermes/VM/SmallHermesValue-inline.h b/include/hermes/VM/SmallHermesValue-inline.h index 4db396ee129..a9c13a5776e 100644 --- a/include/hermes/VM/SmallHermesValue-inline.h +++ b/include/hermes/VM/SmallHermesValue-inline.h @@ -24,10 +24,14 @@ namespace hermes { namespace vm { +#ifndef HERMESVM_ALLOW_COMPRESSED_POINTERS + void SmallHermesValueAdaptor::setInGC(SmallHermesValueAdaptor hv, GC &gc) { HermesValue::setInGC(hv, gc); } +#else // #ifndef HERMESVM_ALLOW_COMPRESSED_POINTERS + void HermesValue32::setInGC(HermesValue32 hv, GC &gc) { setNoBarrier(hv); assert(gc.calledByGC()); @@ -148,6 +152,9 @@ double HermesValue32::getNumber(PointerBase &pb) const { "Strings must use encodeStringValue; BigInts, encodeBigIntValue"); return encodePointerImpl(ptr, Tag::Object, pb); } + +#endif // #ifndef HERMESVM_ALLOW_COMPRESSED_POINTERS + } // namespace vm } // namespace hermes #pragma GCC diagnostic pop diff --git a/include/hermes/VM/SmallHermesValue.h b/include/hermes/VM/SmallHermesValue.h index e1a561a8f3b..ea95e0d448c 100644 --- a/include/hermes/VM/SmallHermesValue.h +++ b/include/hermes/VM/SmallHermesValue.h @@ -19,11 +19,7 @@ #include #include #include -#pragma GCC diagnostic push -#ifdef HERMES_COMPILER_SUPPORTS_WSHORTEN_64_TO_32 -#pragma GCC diagnostic ignored "-Wshorten-64-to-32" -#endif namespace hermes { namespace vm { @@ -32,6 +28,13 @@ class StringPrimitive; class GCCell; class Runtime; +/// If compressed pointers are allowed, then we should also compress +/// HermesValues. This means that when compressed pointers are allowed, +/// SmallHermesValue will almost always be 32 bits, except with MallocGC, which +/// does not support compressed pointers. Depending on the compressed pointers +/// flag, SmallHermesValue will alias SmallHermesValueAdaptor or HermesValue32. + +#ifndef HERMESVM_ALLOW_COMPRESSED_POINTERS /// An adaptor class that provides the API of a SmallHermesValue is internally /// just a HermesValue. class SmallHermesValueAdaptor : protected HermesValue { @@ -168,6 +171,9 @@ class SmallHermesValueAdaptor : protected HermesValue { return SmallHermesValueAdaptor{HermesValue::encodeEmptyValue()}; } }; +using SmallHermesValue = SmallHermesValueAdaptor; + +#else // #ifndef HERMESVM_ALLOW_COMPRESSED_POINTERS /// A compressed HermesValue that is always equal to the size of a /// CompressedPointer. It uses the least significant bits (guaranteed to be zero @@ -476,18 +482,9 @@ class HermesValue32 { raw_ = other.raw_; } }; +using SmallHermesValue = HermesValue32; -/// If compressed pointers are allowed, then we should also compress -/// HermesValues. This means that when compressed pointers are allowed, -/// SmallHermesValue will almost always be 32 bits, except with MallocGC, which -/// does not support compressed pointers. -using SmallHermesValue = -#ifdef HERMESVM_ALLOW_COMPRESSED_POINTERS - HermesValue32 -#else - SmallHermesValueAdaptor -#endif - ; +#endif // #ifndef HERMESVM_ALLOW_COMPRESSED_POINTERS static_assert( std::is_trivial::value, @@ -498,5 +495,4 @@ using GCSmallHermesValue = GCHermesValueBase; } // end namespace vm } // end namespace hermes -#pragma GCC diagnostic pop #endif // HERMES_VM_HERMESVALUE_H diff --git a/lib/BCGen/HBC/BytecodeStream.cpp b/lib/BCGen/HBC/BytecodeStream.cpp index ec1540f08a5..f8dc61782ae 100644 --- a/lib/BCGen/HBC/BytecodeStream.cpp +++ b/lib/BCGen/HBC/BytecodeStream.cpp @@ -190,7 +190,8 @@ void BytecodeSerializer::serializeFunctionsBytecode(BytecodeModule &BM) { if (isLayout_) { // Deduplicate the bytecode during layout phase. DedupKey key = entry->getOpcodeArray(); - auto pair = bcMap.insert(std::make_pair(key, loc_)); + auto pair = + bcMap.insert(std::make_pair(key, static_cast(loc_))); if (!pair.second) { reuse = true; entry->setOffset(pair.first->second); diff --git a/lib/IR/IR.cpp b/lib/IR/IR.cpp index ccac80713a6..ae973d4a2f8 100644 --- a/lib/IR/IR.cpp +++ b/lib/IR/IR.cpp @@ -102,7 +102,7 @@ void Value::removeUse(Use U) { // If we've changed the location of a use in the use list then we need to // update the operand in the user. if (U.second != Users.size()) { - Use oldUse = {this, Users.size()}; + Use oldUse = {this, static_cast(Users.size())}; auto &operands = Users[U.second]->Operands; for (int i = 0, e = operands.size(); i < e; i++) { if (operands[i] == oldUse) { @@ -116,7 +116,7 @@ void Value::removeUse(Use U) { Value::Use Value::addUser(Instruction *Inst) { Users.push_back(Inst); - return {this, Users.size() - 1}; + return {this, static_cast(Users.size() - 1)}; } void Value::replaceAllUsesWith(Value *Other) { diff --git a/lib/Support/BigIntSupport.cpp b/lib/Support/BigIntSupport.cpp index 24dc7c1619d..b4fac6586fd 100644 --- a/lib/Support/BigIntSupport.cpp +++ b/lib/Support/BigIntSupport.cpp @@ -2015,7 +2015,8 @@ static std::tuple getShiftAmountAndSign( // shiftAmnt is outside of the // [MinNegativeShiftAmountInBits, MaxPositiveShiftAmountInBits]; thus return // a really large shift amount. - return std::make_tuple(reallyLargeShiftAmount, shiftAmntIsNeg); + return std::make_tuple( + static_cast(reallyLargeShiftAmount), shiftAmntIsNeg); } const SignedBigIntDigitType sa = (shiftAmnt.numDigits == 0) @@ -2026,7 +2027,8 @@ static std::tuple getShiftAmountAndSign( shiftAmnt.digits[0] != std::numeric_limits::min()) && "shiftAmnt is MIN_INT, hence -signedShiftAmnt is MIN_INT"); // Always return a positive result -- thus negate sa if shiftAmnt is negative. - return std::make_tuple(shiftAmntIsNeg ? -sa : sa, shiftAmntIsNeg); + return std::make_tuple( + static_cast(shiftAmntIsNeg ? -sa : sa), shiftAmntIsNeg); } } // namespace diff --git a/lib/Support/OSCompatWindows.cpp b/lib/Support/OSCompatWindows.cpp index a2df13be46f..7c9268d3034 100644 --- a/lib/Support/OSCompatWindows.cpp +++ b/lib/Support/OSCompatWindows.cpp @@ -16,7 +16,9 @@ // Include windows.h first because other includes from windows API need it. // The blank line after the include is necessary to avoid lint error. #define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX #define NOMINMAX // do not define min/max macros +#endif #include #include diff --git a/lib/VM/Instrumentation/ProcessStats.cpp b/lib/VM/Instrumentation/ProcessStats.cpp index 72dbc9b9e92..021afbc30ae 100644 --- a/lib/VM/Instrumentation/ProcessStats.cpp +++ b/lib/VM/Instrumentation/ProcessStats.cpp @@ -11,7 +11,9 @@ // Include windows.h first because other includes from windows API need it. // The blank line after the include is necessary to avoid lint error. #define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX #define NOMINMAX // do not define min/max macros +#endif #include #include diff --git a/lib/VM/JSLib/HermesInternal.cpp b/lib/VM/JSLib/HermesInternal.cpp index d957660cd73..3d8c308a624 100644 --- a/lib/VM/JSLib/HermesInternal.cpp +++ b/lib/VM/JSLib/HermesInternal.cpp @@ -574,14 +574,18 @@ static CallResult getCodeBlockFileName( const CodeBlock *codeBlock, OptValue location) { RuntimeModule *runtimeModule = codeBlock->getRuntimeModule(); - if (location) { - auto debugInfo = runtimeModule->getBytecode()->getDebugInfo(); - return StringPrimitive::createEfficient( - runtime, debugInfo->getFilenameByID(location->filenameId)); - } else { - llvh::StringRef sourceURL = runtimeModule->getSourceURL(); - if (!sourceURL.empty()) { - return StringPrimitive::createEfficient(runtime, sourceURL); + if (!runtimeModule->getBytecode()->isLazy()) { + // Lazy code blocks do not have debug information (and will hermes_fatal if + // you try to access it), so only touch it for non-lazy blocks. + if (location) { + auto debugInfo = runtimeModule->getBytecode()->getDebugInfo(); + return StringPrimitive::createEfficient( + runtime, debugInfo->getFilenameByID(location->filenameId)); + } else { + llvh::StringRef sourceURL = runtimeModule->getSourceURL(); + if (!sourceURL.empty()) { + return StringPrimitive::createEfficient(runtime, sourceURL); + } } } return HermesValue::encodeUndefinedValue(); @@ -803,6 +807,24 @@ hermesInternalFuzzilli(void *, Runtime &runtime, NativeArgs args) { } #endif // HERMES_ENABLE_FUZZILLI +static CallResult +hermesInternalIsLazy(void *, Runtime &runtime, NativeArgs args) { + auto callable = args.dyncastArg(0); + if (!callable) { + return HermesValue::encodeBoolValue(false); + } + + auto codeBlock = getLeafCodeBlock(callable, runtime); + if (!codeBlock) { + // Native function is never lazy. + return HermesValue::encodeBoolValue(false); + } + + RuntimeModule *runtimeModule = codeBlock->getRuntimeModule(); + return HermesValue::encodeBoolValue( + runtimeModule && runtimeModule->getBytecode()->isLazy()); +} + Handle createHermesInternalObject( Runtime &runtime, const JSLibFlags &flags) { @@ -912,6 +934,7 @@ Handle createHermesInternalObject( defineInternMethod( P::copyDataProperties, hermesBuiltinCopyDataProperties, 3); defineInternMethodAndSymbol("isProxy", hermesInternalIsProxy); + defineInternMethodAndSymbol("isLazy", hermesInternalIsLazy); defineInternMethod(P::drainJobs, hermesInternalDrainJobs); } diff --git a/lib/VM/JSLib/Math.cpp b/lib/VM/JSLib/Math.cpp index f6cbf00d350..21fddd219db 100644 --- a/lib/VM/JSLib/Math.cpp +++ b/lib/VM/JSLib/Math.cpp @@ -17,7 +17,9 @@ #include "hermes/VM/SingleObject.h" #include "hermes/VM/StringPrimitive.h" +#ifndef _USE_MATH_DEFINES #define _USE_MATH_DEFINES +#endif #include #include #include diff --git a/lib/VM/Operations.cpp b/lib/VM/Operations.cpp index f677bfe1de3..21b422e3fa9 100644 --- a/lib/VM/Operations.cpp +++ b/lib/VM/Operations.cpp @@ -1104,11 +1104,11 @@ abstractEqualityTest_RJS(Runtime &runtime, Handle<> xHandle, Handle<> yHandle) { // ToNumber(x) == y. CASE_S_S(Bool, NUMBER_TAG) { // Do both conversions and check numerical equality. - return x->getBool() == y->getNumber(); + return static_cast(x->getBool()) == y->getNumber(); } CASE_S_M(Bool, Str) { // Do string parsing and check double equality. - return x->getBool() == + return static_cast(x->getBool()) == stringToNumber(runtime, Handle::vmcast(y)); } CASE_S_M(Bool, BigInt) { @@ -1121,11 +1121,11 @@ abstractEqualityTest_RJS(Runtime &runtime, Handle<> xHandle, Handle<> yHandle) { // 9. If Type(y) is Boolean, return the result of the comparison x == ! // ToNumber(y). CASE_S_S(NUMBER_TAG, Bool) { - return x->getNumber() == y->getBool(); + return x->getNumber() == static_cast(y->getBool()); } CASE_M_S(Str, Bool) { return stringToNumber(runtime, Handle::vmcast(x)) == - y->getBool(); + static_cast(y->getBool()); } CASE_M_S(BigInt, Bool) { return x->getBigInt()->compare(static_cast(y->getBool())) == 0; diff --git a/lib/VM/Profiler/SamplingProfiler.cpp b/lib/VM/Profiler/SamplingProfiler.cpp index 48bb811d5cd..3e7d3da2fdd 100644 --- a/lib/VM/Profiler/SamplingProfiler.cpp +++ b/lib/VM/Profiler/SamplingProfiler.cpp @@ -225,6 +225,10 @@ void SamplingProfiler::clear() { } void SamplingProfiler::suspend(std::string_view extraInfo) { + // Need to check whether the profiler is enabled without holding the + // runtimeDataLock_. Otherwise, we'd have a lock inversion. + bool enabled = sampling_profiler::Sampler::get()->enabled(); + std::lock_guard lk(runtimeDataLock_); if (++suspendCount_ > 1 || extraInfo.empty()) { // If there are multiple nested suspend calls use a default "suspended" @@ -234,8 +238,7 @@ void SamplingProfiler::suspend(std::string_view extraInfo) { } // Only record the stack trace for the first suspend() call. - if (LLVM_UNLIKELY( - sampling_profiler::Sampler::get()->enabled() && suspendCount_ == 1)) { + if (LLVM_UNLIKELY(enabled && suspendCount_ == 1)) { recordPreSuspendStack(extraInfo); } } diff --git a/lib/VM/Profiler/SamplingProfilerWindows.cpp b/lib/VM/Profiler/SamplingProfilerWindows.cpp index 65467904537..2e11124d81b 100644 --- a/lib/VM/Profiler/SamplingProfilerWindows.cpp +++ b/lib/VM/Profiler/SamplingProfilerWindows.cpp @@ -9,6 +9,14 @@ #if defined(HERMESVM_SAMPLING_PROFILER_WINDOWS) +#ifdef HERMES_FACEBOOK_BUILD +#define HERMESVM_ENABLE_LOOM_WINDOWS +#endif // defined(HERMES_FACEBOOK_BUILD) + +#if defined(HERMESVM_ENABLE_LOOM_WINDOWS) +#include +#endif // defined(HERMESVM_ENABLE_LOOM_WINDOWS) + #include "hermes/VM/Profiler/SamplingProfiler.h" #ifndef NOMINMAX @@ -26,6 +34,14 @@ struct SamplingProfilerWindows : SamplingProfiler { THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION, false, GetCurrentThreadId()); + +#if defined(HERMESVM_ENABLE_LOOM_WINDOWS) + fbloom_profilo_api()->fbloom_register_enable_for_loom_callback( + FBLoomTracerType::JAVASCRIPT, enable); + fbloom_profilo_api()->fbloom_register_disable_for_loom_callback( + FBLoomTracerType::JAVASCRIPT, disable); + loomDataPushEnabled_ = true; +#endif // defined(HERMESVM_ENABLE_LOOM_WINDOWS) } ~SamplingProfilerWindows() override { @@ -35,9 +51,80 @@ struct SamplingProfilerWindows : SamplingProfiler { CloseHandle(currentThread_); } - /// Thread that this profiler instance represents. This can currently only be - /// set from the constructor of SamplingProfiler, so we need to construct a - /// new SamplingProfiler every time the runtime is moved to a different +#if defined(HERMESVM_ENABLE_LOOM_WINDOWS) + bool shouldPushDataToLoom() const { + auto now = std::chrono::system_clock::now(); + constexpr auto kLoomDelay = std::chrono::milliseconds(50); + // The default sample stack interval in timerLoop() is between 5-15ms + // which is too often for loom. + return loomDataPushEnabled_ && (now - previousPushTs > kLoomDelay); + } + + void collectStackForLoomCommon( + const StackFrame &frame, + int64_t *frames, + uint32_t index) { + constexpr uint64_t kNativeFrameMask = ((uint64_t)1 << 63); + switch (frame.kind) { + case StackFrame::FrameKind::JSFunction: { + auto *bcProvider = frame.jsFrame.module->getBytecode(); + uint32_t virtualOffset = + bcProvider->getVirtualOffsetForFunction(frame.jsFrame.functionId) + + frame.jsFrame.offset; + uint32_t segmentID = bcProvider->getSegmentID(); + uint64_t frameAddress = ((uint64_t)segmentID << 32) + virtualOffset; + assert( + (frameAddress & kNativeFrameMask) == 0 && + "Module id should take less than 32 bits"); + frames[(index)] = static_cast(frameAddress); + break; + } + + case StackFrame::FrameKind::NativeFunction: + case StackFrame::FrameKind::FinalizableNativeFunction: { + NativeFunctionPtr nativeFrame = getNativeFunctionPtr(frame); + frames[(index)] = ((uint64_t)nativeFrame | kNativeFrameMask); + break; + } + case StackFrame::FrameKind::SuspendFrame: + break; + default: + llvm_unreachable("Loom: unknown frame kind"); + } + } + + void pushLastSampledStackToLoom() { + constexpr uint16_t maxDepth = 512; + int64_t frames[maxDepth]; + uint16_t depth = 0; + // Each element in sampledStacks_ is one call stack, access the last one + // to get the latest stack trace. + auto sample = sampledStacks_.back(); + for (auto iter = sample.stack.rbegin(); iter != sample.stack.rend(); + ++iter) { + const StackFrame &frame = *iter; + collectStackForLoomCommon(frame, frames, depth); + depth++; + if (depth > maxDepth) { + return; + } + } + fbloom_profilo_api()->fbloom_write_stack_to_loom( + FBLoomTracerType::JAVASCRIPT, frames, depth); + previousPushTs = std::chrono::system_clock::now(); + clear(); + } + + /// Previous timestamp when a push to loom occurred. The Loom API does not + /// rate limit its callers, and thus care must be taken to not overload it. + std::chrono::time_point previousPushTs; + + bool loomDataPushEnabled_{false}; +#endif // defined(HERMESVM_ENABLE_LOOM_WINDOWS) + + /// Thread that this profiler instance represents. This can currently only + /// be set from the constructor of SamplingProfiler, so we need to construct + /// a new SamplingProfiler every time the runtime is moved to a different /// thread. HANDLE currentThread_; }; @@ -73,7 +160,14 @@ void Sampler::platformRegisterRuntime(SamplingProfiler *profiler) {} void Sampler::platformUnregisterRuntime(SamplingProfiler *profiler) {} -void Sampler::platformPostSampleStack(SamplingProfiler *localProfiler) {} +void Sampler::platformPostSampleStack(SamplingProfiler *localProfiler) { +#if defined(HERMESVM_ENABLE_LOOM_WINDOWS) + auto *windowsProfiler = static_cast(localProfiler); + if (windowsProfiler->shouldPushDataToLoom()) { + windowsProfiler->pushLastSampledStackToLoom(); + } +#endif // defined(HERMESVM_ENABLE_LOOM_WINDOWS) +} bool Sampler::platformSuspendVMAndWalkStack(SamplingProfiler *profiler) { auto *winProfiler = static_cast(profiler); @@ -83,7 +177,6 @@ bool Sampler::platformSuspendVMAndWalkStack(SamplingProfiler *profiler) { if (prevSuspendCount == static_cast(-1)) { return true; } - assert(prevSuspendCount == 0 && "JS thread should not be suspended"); // Get the JS thread context. This ensures that the thread suspension is // completed. @@ -95,7 +188,10 @@ bool Sampler::platformSuspendVMAndWalkStack(SamplingProfiler *profiler) { // Resume the thread. prevSuspendCount = ResumeThread(winProfiler->currentThread_); - assert(prevSuspendCount == 1); + assert( + prevSuspendCount != static_cast(-1) && + "couldn't resume js thread"); + (void)prevSuspendCount; return true; } diff --git a/lib/VM/StorageProvider.cpp b/lib/VM/StorageProvider.cpp index 49bc0173848..f961b1d4755 100644 --- a/lib/VM/StorageProvider.cpp +++ b/lib/VM/StorageProvider.cpp @@ -129,7 +129,7 @@ llvh::ErrorOr VMAllocateStorageProvider::newStorageImpl( } void *mem = *result; assert(isAligned(mem)); - (void)isAligned; + (void)&isAligned; #ifdef HERMESVM_ALLOW_HUGE_PAGES oscompat::vm_hugepage(mem, AlignedStorage::size()); #endif diff --git a/lib/VM/StringPrimitive.cpp b/lib/VM/StringPrimitive.cpp index b2ea7377924..3ead6af707a 100644 --- a/lib/VM/StringPrimitive.cpp +++ b/lib/VM/StringPrimitive.cpp @@ -97,7 +97,15 @@ CallResult StringPrimitive::createEfficientImpl( } auto output = runtime.makeHandle(*result); // Copy directly into the StringPrimitive storage. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + std::copy(str.begin(), str.end(), output->castToASCIIPointerForWrite()); +#ifdef _MSC_VER +#pragma warning(pop) +#endif return output.getHermesValue(); } @@ -200,8 +208,15 @@ CallResult StringPrimitive::createDynamic( return ExecutionStatus::EXCEPTION; } // Copy directly into the StringPrimitive storage. +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif std::copy( str.begin(), str.end(), res->getString()->castToASCIIPointerForWrite()); +#ifdef _MSC_VER +#pragma warning(pop) +#endif return res; } else { return DynamicUTF16StringPrimitive::create(runtime, str); diff --git a/test/IRGen/warnings-disabled.js b/test/IRGen/warnings-disabled.js index ae2b2b7893b..952ead58f85 100644 --- a/test/IRGen/warnings-disabled.js +++ b/test/IRGen/warnings-disabled.js @@ -6,6 +6,7 @@ */ // RUN: (echo "START" && %hermes -dump-ir -w %s 2>&1 >/dev/null && echo "END" ) | %FileCheck %s --match-full-lines +// REQUIRES: !fbcode_coverage // CHECK: START "use strict"; diff --git a/test/SourceMap/translator/sourcemap-translator.sh b/test/SourceMap/translator/sourcemap-translator.sh index b3873f6b47e..637efce8188 100644 --- a/test/SourceMap/translator/sourcemap-translator.sh +++ b/test/SourceMap/translator/sourcemap-translator.sh @@ -4,6 +4,7 @@ # LICENSE file in the root directory of this source tree. # RUN: bash %s %S %T %hermes +# REQUIRES: !fbcode_coverage # shellcheck shell=bash disable=SC2086 # This test is an end-to-end merge input source-map with hermes debuginfo symbolication test. diff --git a/test/hermes/json-stringify.js b/test/hermes/json-stringify.js new file mode 100644 index 00000000000..d7c1d8609e9 --- /dev/null +++ b/test/hermes/json-stringify.js @@ -0,0 +1,110 @@ +/** + * 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 -emit-binary -out %t.hbc %s && %hermes %t.hbc | %FileCheck --match-full-lines %s + +print('json.stringify'); +//CHECK-LABEL: json.stringify + +print(JSON.stringify('𝌆')); +// CHECK-NEXT "𝌆" +print(JSON.stringify('\uD834\uDF06')); +// CHECK-NEXT "𝌆" +print(JSON.stringify('\uD834')); +// CHECK-NEXT "\\ud834" +print(JSON.stringify('\uDF06')); +// CHECK-NEXT "\\udf06" +print(JSON.stringify('\uDF06\uD834')); +// CHECK-NEXT "\\udf06\\ud834" +print(JSON.stringify('\uDEAD')); +// CHECK-NEXT "\\udead" +print(JSON.stringify('\uD834\uD834\uDF06')); +// CHECK-NEXT "\\ud834𝌆" +print(JSON.stringify('\uD834a')); +// CHECK-NEXT "\\ud834a" +print(JSON.stringify('\uD834\u0400')); +// CHECK-NEXT "\\ud834Ѐ" +print(JSON.stringify("\ud7ff")); +// CHECK-NEXT "\ud7ff" +print(JSON.stringify("\ud800")); +// CHECK-NEXT "\\ud800" +print(JSON.stringify("\ud937")); +// CHECK-NEXT "\\ud937" +print(JSON.stringify("\uda20")); +// CHECK-NEXT "\\uda20" +print(JSON.stringify("\udbff")); +// CHECK-NEXT "\\udbff" +print(JSON.stringify("\udc00")); +// CHECK-NEXT "\\udc00" +print(JSON.stringify("\udddd")); +// CHECK-NEXT "\\udddd" +print(JSON.stringify("\udeaf")); +// CHECK-NEXT "\\udeaf" +print(JSON.stringify("\udfff")); +// CHECK-NEXT "\\udfff" +print(JSON.stringify("\ue000")); +// CHECK-NEXT "\ue000" +print(JSON.stringify("\ud7ffa")); +// CHECK-NEXT "\ud7ffa" +print(JSON.stringify("\ud800a")); +// CHECK-NEXT "\\ud800a" +print(JSON.stringify("\ud937a")); +// CHECK-NEXT "\\ud937a" +print(JSON.stringify("\uda20a")); +// CHECK-NEXT "\\uda20a" +print(JSON.stringify("\udbffa")); +// CHECK-NEXT "\\udbffa" +print(JSON.stringify("\udc00a")); +// CHECK-NEXT "\\udc00a" +print(JSON.stringify("\udddda")); +// CHECK-NEXT "\\udddda" +print(JSON.stringify("\udeafa")); +// CHECK-NEXT "\\udeafa" +print(JSON.stringify("\udfffa")); +// CHECK-NEXT "\\udfffa" +print(JSON.stringify("\ue000a")); +// CHECK-NEXT "\ue000a" +print(JSON.stringify("\ud7ff\ud800")); +// CHECK-NEXT "\ud7ff\\ud800" +print(JSON.stringify("\ud800\ud800")); +// CHECK-NEXT "\\ud800\\ud800" +print(JSON.stringify("\ud937\ud800")); +// CHECK-NEXT "\\ud937\\ud800" +print(JSON.stringify("\uda20\ud800")); +// CHECK-NEXT "\\uda20\\ud800" +print(JSON.stringify("\udbff\ud800")); +// CHECK-NEXT "\\udbff\\ud800" +print(JSON.stringify("\udc00\ud800")); +// CHECK-NEXT "\\udc00\\ud800" +print(JSON.stringify("\udddd\ud800")); +// CHECK-NEXT "\\udddd\\ud800" +print(JSON.stringify("\udeaf\ud800")); +// CHECK-NEXT "\\udeaf\\ud800" +print(JSON.stringify("\udfff\ud800")); +// CHECK-NEXT "\\udfff\\ud800" +print(JSON.stringify("\ue000\ud800")); +// CHECK-NEXT "\ue000\\ud800" +print(JSON.stringify("\ud7ff\udc00")); +// CHECK-NEXT "\ud7ff\\udc00" +print(JSON.stringify("\ud800\udc00")); +// CHECK-NEXT "\ud800\udc00" +print(JSON.stringify("\ud937\udc00")); +// CHECK-NEXT "\ud937\udc00" +print(JSON.stringify("\uda20\udc00")); +// CHECK-NEXT "\uda20\udc00" +print(JSON.stringify("\udbff\udc00")); +// CHECK-NEXT "\udbff\udc00" +print(JSON.stringify("\udc00\udc00")); +// CHECK-NEXT "\\udc00\\udc00" +print(JSON.stringify("\udddd\udc00")); +// CHECK-NEXT "\\udddd\\udc00" +print(JSON.stringify("\udeaf\udc00")); +// CHECK-NEXT "\\udeaf\\udc00" +print(JSON.stringify("\udfff\udc00")); +// CHECK-NEXT "\\udfff\\udc00" +print(JSON.stringify("\ue000\udc00")); +// CHECK-NEXT "\ue000\\udc00" diff --git a/test/hermes/regress-hermes-internal-get-lazy-function-location.js b/test/hermes/regress-hermes-internal-get-lazy-function-location.js new file mode 100644 index 00000000000..3507d51c3dd --- /dev/null +++ b/test/hermes/regress-hermes-internal-get-lazy-function-location.js @@ -0,0 +1,35 @@ +/** + * 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 -g --lazy --Xhermes-internal-test-methods -O %s | %FileCheck %s +// RUN: %hermes -g --lazy --Xhermes-internal-test-methods -O0 %s | %FileCheck %s + +"use strict"; + +// isLazy should be able to handle native functions. +print(HermesInternal.isLazy(print)); +// CHECK: false + +// isLazy should be able to handle non-callables. +print(HermesInternal.isLazy(10)); +// CHECK: false + +function lazyFunc() {} + +print(HermesInternal.isLazy(lazyFunc)); +// CHECK: true + +print(JSON.stringify(HermesInternal.getFunctionLocation(lazyFunc))); +// CHECK: {"isNative":false,"lineNumber":21,"columnNumber":1} + +lazyFunc(); + +print(HermesInternal.isLazy(lazyFunc)); +// CHECK: false + +print(JSON.stringify(HermesInternal.getFunctionLocation(lazyFunc))); +// CHECK: {"isNative":false,"lineNumber":21,"columnNumber":1,"fileName":"{{.*}}regress-hermes-internal-get-lazy-function-location.js"} diff --git a/test/lit.cfg b/test/lit.cfg index b49713e7b19..1009bd7ff59 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -56,6 +56,9 @@ if lit_config.params.get("build_mode") != "opt": if isTrue(lit_config.params.get("is_fbcode")): config.available_features.add("fbcode") +if lit_config.params.get("is_fbcode_coverage"): + config.available_features.add("fbcode_coverage") + if isTrue(lit_config.params.get("ubsan")): config.available_features.add("ubsan") diff --git a/tools/hermes-parser/js/.flowconfig b/tools/hermes-parser/js/.flowconfig index d37e828f77b..953b961b917 100644 --- a/tools/hermes-parser/js/.flowconfig +++ b/tools/hermes-parser/js/.flowconfig @@ -26,7 +26,7 @@ enums=true inference_mode=constrain_writes [version] -^0.198.1 +^0.198.2 [lints] untyped-type-import=error diff --git a/tools/hermes-parser/js/package.json b/tools/hermes-parser/js/package.json index eea2579f5cb..3946bd42e53 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.0.0", - "flow-bin": "^0.198.1", + "flow-bin": "^0.198.2", "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 5a369f1e454..30da670499f 100644 --- a/tools/hermes-parser/js/yarn.lock +++ b/tools/hermes-parser/js/yarn.lock @@ -2722,10 +2722,10 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== -flow-bin@^0.198.1: - version "0.198.1" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.198.1.tgz#5fdad4d572bdc76f9ab24890c335e1f7addcecd4" - integrity sha512-9jWC1GJgV5QyeBxvT0GtTQtaw55imDRIh//C5WaS/dijl7IP34CrNY2NgBSwzif516SktkG8KylQWJaslZI2QA== +flow-bin@^0.198.2: + version "0.198.2" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.198.2.tgz#e601121a411b4ec5889cc3567d2706369cc510a9" + integrity sha512-PML2zhAZVd8EgzDqS9oSJF+OxUtJ+YLEdVVsVO7d1BwnrXCgiVrLjxiQP8/hur820grzC51Xb2CI3J+ohH/bMg== flow-enums-runtime@^0.0.6: version "0.0.6" diff --git a/utils/testsuite/buck_run_testsuite.py b/utils/testsuite/buck_run_testsuite.py index d21d83d9cae..5d9dc948fc3 100644 --- a/utils/testsuite/buck_run_testsuite.py +++ b/utils/testsuite/buck_run_testsuite.py @@ -34,7 +34,8 @@ def main(): ] for rsc, out_name in resources: with open(os.path.join(binary_dir, out_name), "w+b") as f: - shutil.copyfileobj(pkg_resources.resource_stream(rsc_package, rsc), f) + with pkg_resources.resource_stream(rsc_package, rsc) as t: + shutil.copyfileobj(t, f) st = os.stat(f.name) os.chmod(f.name, st.st_mode | stat.S_IEXEC) return run( diff --git a/utils/testsuite/testsuite.py b/utils/testsuite/testsuite.py index fee1af35b1c..ef9c1e8db19 100644 --- a/utils/testsuite/testsuite.py +++ b/utils/testsuite/testsuite.py @@ -551,7 +551,8 @@ def runTest( 0, ) - content = open(filename, "rb").read().decode("utf-8") + with open(filename, "rb") as test_contents: + content = test_contents.read().decode("utf-8") shouldRun, skipReason, permanent, flags, strictModes = testShouldRun( filename, content diff --git a/utils/testsuite/testsuite_skiplist.py b/utils/testsuite/testsuite_skiplist.py index c9bfa48e716..6396caa22f9 100644 --- a/utils/testsuite/testsuite_skiplist.py +++ b/utils/testsuite/testsuite_skiplist.py @@ -2028,7 +2028,6 @@ "ShadowRealm", "tail-call-optimization", "Temporal", - "well-formed-json-stringify", "u180e", ]