diff --git a/NativeScript/include/cppgc/common.h b/NativeScript/include/cppgc/common.h new file mode 100644 index 00000000..228b9abb --- /dev/null +++ b/NativeScript/include/cppgc/common.h @@ -0,0 +1,26 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_COMMON_H_ +#define INCLUDE_CPPGC_COMMON_H_ + +// TODO(chromium:1056170): Remove dependency on v8. +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +// Indicator for the stack state of the embedder. +enum class EmbedderStackState { + kMayContainHeapPointers, + kNoHeapPointers, + kUnknown V8_ENUM_DEPRECATE_SOON("Use kMayContainHeapPointers") = + kMayContainHeapPointers, + kNonEmpty V8_ENUM_DEPRECATE_SOON("Use kMayContainHeapPointers") = + kMayContainHeapPointers, + kEmpty V8_ENUM_DEPRECATE_SOON("Use kNoHeapPointers") = kNoHeapPointers, +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_COMMON_H_ diff --git a/NativeScript/include/inspector/Debugger.h b/NativeScript/include/inspector/Debugger.h index 6b8c6afe..d984c6a4 100644 --- a/NativeScript/include/inspector/Debugger.h +++ b/NativeScript/include/inspector/Debugger.h @@ -31,6 +31,7 @@ namespace Paused { namespace ReasonEnum { V8_EXPORT extern const char* Ambiguous; V8_EXPORT extern const char* Assert; +V8_EXPORT extern const char* CSPViolation; V8_EXPORT extern const char* DebugCommand; V8_EXPORT extern const char* DOM; V8_EXPORT extern const char* EventListener; diff --git a/NativeScript/include/libplatform/libplatform.h b/NativeScript/include/libplatform/libplatform.h index c7ea4c2b..00de81df 100644 --- a/NativeScript/include/libplatform/libplatform.h +++ b/NativeScript/include/libplatform/libplatform.h @@ -43,6 +43,27 @@ V8_PLATFORM_EXPORT std::unique_ptr NewDefaultPlatform( InProcessStackDumping::kDisabled, std::unique_ptr tracing_controller = {}); +/** + * The same as NewDefaultPlatform but disables the worker thread pool. + * It must be used with the --single-threaded V8 flag. + */ +V8_PLATFORM_EXPORT std::unique_ptr +NewSingleThreadedDefaultPlatform( + IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, + InProcessStackDumping in_process_stack_dumping = + InProcessStackDumping::kDisabled, + std::unique_ptr tracing_controller = {}); + +/** + * Returns a new instance of the default v8::JobHandle implementation. + * + * The job will be executed by spawning up to |num_worker_threads| many worker + * threads on the provided |platform| with the given |priority|. + */ +V8_PLATFORM_EXPORT std::unique_ptr NewDefaultJobHandle( + v8::Platform* platform, v8::TaskPriority priority, + std::unique_ptr job_task, size_t num_worker_threads); + /** * Pumps the message loop for the given isolate. * @@ -79,6 +100,17 @@ V8_PLATFORM_EXPORT void SetTracingController( v8::Platform* platform, v8::platform::tracing::TracingController* tracing_controller); +/** + * Notifies the given platform about the Isolate getting deleted soon. Has to be + * called for all Isolates which are deleted - unless we're shutting down the + * platform. + * + * The |platform| has to be created using |NewDefaultPlatform|. + * + */ +V8_PLATFORM_EXPORT void NotifyIsolateShutdown(v8::Platform* platform, + Isolate* isolate); + } // namespace platform } // namespace v8 diff --git a/NativeScript/include/v8-inspector.h b/NativeScript/include/v8-inspector.h index 6573940e..86fcf518 100644 --- a/NativeScript/include/v8-inspector.h +++ b/NativeScript/include/v8-inspector.h @@ -181,6 +181,10 @@ class V8_EXPORT V8InspectorClient { virtual std::unique_ptr valueSubtype(v8::Local) { return nullptr; } + virtual std::unique_ptr descriptionForValueSubtype( + v8::Local, v8::Local) { + return nullptr; + } virtual bool formatAccessorsAsProperties(v8::Local) { return false; } diff --git a/NativeScript/include/v8-internal.h b/NativeScript/include/v8-internal.h index fa5ee747..06846d70 100644 --- a/NativeScript/include/v8-internal.h +++ b/NativeScript/include/v8-internal.h @@ -120,6 +120,23 @@ constexpr bool HeapSandboxIsEnabled() { using ExternalPointer_t = Address; +// If the heap sandbox is enabled, these tag values will be XORed with the +// external pointers in the external pointer table to prevent use of pointers of +// the wrong type. +enum ExternalPointerTag : Address { + kExternalPointerNullTag = static_cast
(0ULL), + kArrayBufferBackingStoreTag = static_cast
(1ULL << 48), + kTypedArrayExternalPointerTag = static_cast
(2ULL << 48), + kDataViewDataPointerTag = static_cast
(3ULL << 48), + kExternalStringResourceTag = static_cast
(4ULL << 48), + kExternalStringResourceDataTag = static_cast
(5ULL << 48), + kForeignForeignAddressTag = static_cast
(6ULL << 48), + kNativeContextMicrotaskQueueTag = static_cast
(7ULL << 48), + // TODO(v8:10391, saelo): Currently has to be zero so that raw zero values are + // also nullptr + kEmbedderDataSlotPayloadTag = static_cast
(0ULL << 48), +}; + #ifdef V8_31BIT_SMIS_ON_64BIT_ARCH using PlatformSmiTagging = SmiTagging; #else @@ -140,6 +157,20 @@ V8_INLINE static constexpr internal::Address IntToSmi(int value) { kSmiTag; } +// Converts encoded external pointer to address. +V8_EXPORT Address DecodeExternalPointerImpl(const Isolate* isolate, + ExternalPointer_t pointer, + ExternalPointerTag tag); + +// {obj} must be the raw tagged pointer representation of a HeapObject +// that's guaranteed to never be in ReadOnlySpace. +V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); + +// Returns if we need to throw when an error occurs. This infers the language +// mode based on the current context and the closure. This returns true if the +// language mode is strict. +V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate); + /** * This class exports constants and functionality from within v8 that * is necessary to implement inline functions in the v8 api. Don't @@ -159,6 +190,9 @@ class Internals { static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; static const int kEmbedderDataSlotSize = kApiSystemPointerSize; +#ifdef V8_HEAP_SANDBOX + static const int kEmbedderDataSlotRawPayloadOffset = kApiTaggedSize; +#endif static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize; static const int kFullStringRepresentationMask = 0x0f; static const int kStringEncodingMask = 0x8; @@ -169,14 +203,8 @@ class Internals { // IsolateData layout guarantees. static const int kIsolateEmbedderDataOffset = 0; - static const int kExternalMemoryOffset = - kNumIsolateDataSlots * kApiSystemPointerSize; - static const int kExternalMemoryLimitOffset = - kExternalMemoryOffset + kApiInt64Size; - static const int kExternalMemoryLowSinceMarkCompactOffset = - kExternalMemoryLimitOffset + kApiInt64Size; static const int kIsolateFastCCallCallerFpOffset = - kExternalMemoryLowSinceMarkCompactOffset + kApiInt64Size; + kNumIsolateDataSlots * kApiSystemPointerSize; static const int kIsolateFastCCallCallerPcOffset = kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize; static const int kIsolateStackGuardOffset = @@ -184,6 +212,12 @@ class Internals { static const int kIsolateRootsOffset = kIsolateStackGuardOffset + 7 * kApiSystemPointerSize; + static const int kExternalPointerTableBufferOffset = 0; + static const int kExternalPointerTableLengthOffset = + kExternalPointerTableBufferOffset + kApiSystemPointerSize; + static const int kExternalPointerTableCapacityOffset = + kExternalPointerTableLengthOffset + kApiInt32Size; + static const int kUndefinedValueRootIndex = 4; static const int kTheHoleValueRootIndex = 5; static const int kNullValueRootIndex = 6; @@ -339,16 +373,37 @@ class Internals { #endif } + V8_INLINE static internal::Isolate* GetIsolateForHeapSandbox( + internal::Address obj) { +#ifdef V8_HEAP_SANDBOX + return internal::IsolateFromNeverReadOnlySpaceObject(obj); +#else + // Not used in non-sandbox mode. + return nullptr; +#endif + } + + V8_INLINE static Address DecodeExternalPointer( + const Isolate* isolate, ExternalPointer_t encoded_pointer, + ExternalPointerTag tag) { +#ifdef V8_HEAP_SANDBOX + return internal::DecodeExternalPointerImpl(isolate, encoded_pointer, tag); +#else + return encoded_pointer; +#endif + } + V8_INLINE static internal::Address ReadExternalPointerField( - internal::Isolate* isolate, internal::Address heap_object_ptr, - int offset) { -#ifdef V8_COMPRESS_POINTERS - internal::Address value = ReadRawField
(heap_object_ptr, offset); + internal::Isolate* isolate, internal::Address heap_object_ptr, int offset, + ExternalPointerTag tag) { +#ifdef V8_HEAP_SANDBOX + internal::ExternalPointer_t encoded_value = + ReadRawField(heap_object_ptr, offset); // We currently have to treat zero as nullptr in embedder slots. - if (value) value = DecodeExternalPointer(isolate, value); - return value; + return encoded_value ? DecodeExternalPointer(isolate, encoded_value, tag) + : 0; #else - return ReadRawField(heap_object_ptr, offset); + return ReadRawField
(heap_object_ptr, offset); #endif } @@ -357,10 +412,6 @@ class Internals { static constexpr size_t kPtrComprHeapReservationSize = size_t{1} << 32; static constexpr size_t kPtrComprIsolateRootAlignment = size_t{1} << 32; - // See v8:10391 for details about V8 heap sandbox. - static constexpr uint32_t kExternalPointerSalt = - 0x7fffffff & ~static_cast(kHeapObjectTagMask); - V8_INLINE static internal::Address GetRootFromOnHeapAddress( internal::Address addr) { return addr & -static_cast(kPtrComprIsolateRootAlignment); @@ -372,14 +423,6 @@ class Internals { return root + static_cast(static_cast(value)); } - V8_INLINE static Address DecodeExternalPointer( - const Isolate* isolate, ExternalPointer_t encoded_pointer) { -#ifndef V8_HEAP_SANDBOX - return encoded_pointer; -#else - return encoded_pointer ^ kExternalPointerSalt; -#endif - } #endif // V8_COMPRESS_POINTERS }; @@ -403,18 +446,10 @@ void CastCheck::Perform(T* data) {} template V8_INLINE void PerformCastCheck(T* data) { - CastCheck::value>::Perform(data); + CastCheck::value && + !std::is_same>::value>::Perform(data); } -// {obj} must be the raw tagged pointer representation of a HeapObject -// that's guaranteed to never be in ReadOnlySpace. -V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); - -// Returns if we need to throw when an error occurs. This infers the language -// mode based on the current context and the closure. This returns true if the -// language mode is strict. -V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate); - // A base class for backing stores, which is needed due to vagaries of // how static casts work with std::shared_ptr. class BackingStoreBase {}; diff --git a/NativeScript/include/v8-platform.h b/NativeScript/include/v8-platform.h index 7cfd18b5..1f1497f6 100644 --- a/NativeScript/include/v8-platform.h +++ b/NativeScript/include/v8-platform.h @@ -80,6 +80,14 @@ class TaskRunner { * implementation takes ownership of |task|. The |task| cannot be nested * within other task executions. * + * Tasks which shouldn't be interleaved with JS execution must be posted with + * |PostNonNestableTask| or |PostNonNestableDelayedTask|. This is because the + * embedder may process tasks in a callback which is called during JS + * execution. + * + * In particular, tasks which execute JS must be non-nestable, since JS + * execution is not allowed to nest. + * * Requires that |TaskRunner::NonNestableTasksEnabled()| is true. */ virtual void PostNonNestableTask(std::unique_ptr task) {} @@ -98,6 +106,14 @@ class TaskRunner { * implementation takes ownership of |task|. The |task| cannot be nested * within other task executions. * + * Tasks which shouldn't be interleaved with JS execution must be posted with + * |PostNonNestableTask| or |PostNonNestableDelayedTask|. This is because the + * embedder may process tasks in a callback which is called during JS + * execution. + * + * In particular, tasks which execute JS must be non-nestable, since JS + * execution is not allowed to nest. + * * Requires that |TaskRunner::NonNestableDelayedTasksEnabled()| is true. */ virtual void PostNonNestableDelayedTask(std::unique_ptr task, @@ -154,6 +170,20 @@ class JobDelegate { * details. */ virtual void NotifyConcurrencyIncrease() = 0; + + /** + * Returns a task_id unique among threads currently running this job, such + * that GetTaskId() < worker count. To achieve this, the same task_id may be + * reused by a different thread after a worker_task returns. + */ + virtual uint8_t GetTaskId() = 0; + + /** + * Returns true if the current task is called from the thread currently + * running JobHandle::Join(). + * TODO(etiennep): Make pure virtual once custom embedders implement it. + */ + virtual bool IsJoiningThread() const { return false; } }; /** @@ -186,11 +216,41 @@ class JobHandle { */ virtual void Cancel() = 0; + /* + * Forces all existing workers to yield ASAP but doesn’t wait for them. + * Warning, this is dangerous if the Job's callback is bound to or has access + * to state which may be deleted after this call. + * TODO(etiennep): Cleanup once implemented by all embedders. + */ + virtual void CancelAndDetach() { Cancel(); } + + /** + * Returns true if there's currently no work pending and no worker running. + * TODO(etiennep): Deprecate IsCompleted in favor of IsActive once implemented + * by all embedders. + */ + virtual bool IsCompleted() = 0; + virtual bool IsActive() { return !IsCompleted(); } + /** * Returns true if associated with a Job and other methods may be called. - * Returns false after Join() or Cancel() was called. + * Returns false after Join() or Cancel() was called. This may return true + * even if no workers are running and IsCompleted() returns true + * TODO(etiennep): Deprecate IsRunning in favor of IsValid once implemented by + * all embedders. */ virtual bool IsRunning() = 0; + virtual bool IsValid() { return IsRunning(); } + + /** + * Returns true if job priority can be changed. + */ + virtual bool UpdatePriorityEnabled() const { return false; } + + /** + * Update this Job's priority. + */ + virtual void UpdatePriority(TaskPriority new_priority) {} }; /** @@ -203,12 +263,17 @@ class JobTask { virtual void Run(JobDelegate* delegate) = 0; /** - * Controls the maximum number of threads calling Run() concurrently. Run() is - * only invoked if the number of threads previously running Run() was less - * than the value returned. Since GetMaxConcurrency() is a leaf function, it - * must not call back any JobHandle methods. + * Controls the maximum number of threads calling Run() concurrently, given + * the number of threads currently assigned to this job and executing Run(). + * Run() is only invoked if the number of threads previously running Run() was + * less than the value returned. Since GetMaxConcurrency() is a leaf function, + * it must not call back any JobHandle methods. */ - virtual size_t GetMaxConcurrency() const = 0; + virtual size_t GetMaxConcurrency(size_t worker_count) const = 0; + + // TODO(1114823): Clean up once all overrides are removed. + V8_DEPRECATED("Use the version that takes |worker_count|.") + virtual size_t GetMaxConcurrency() const { return 0; } }; /** @@ -343,7 +408,13 @@ class PageAllocator { kReadWrite, // TODO(hpayer): Remove this flag. Memory should never be rwx. kReadWriteExecute, - kReadExecute + kReadExecute, + // Set this when reserving memory that will later require kReadWriteExecute + // permissions. The resulting behavior is platform-specific, currently + // this is used to set the MAP_JIT flag on Apple Silicon. + // TODO(jkummerow): Remove this when Wasm has a platform-independent + // w^x implementation. + kNoAccessWillJitLater }; /** @@ -375,6 +446,69 @@ class PageAllocator { * memory area brings the memory transparently back. */ virtual bool DiscardSystemPages(void* address, size_t size) { return true; } + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + */ + class SharedMemoryMapping { + public: + // Implementations are expected to free the shared memory mapping in the + // destructor. + virtual ~SharedMemoryMapping() = default; + virtual void* GetMemory() const = 0; + }; + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + */ + class SharedMemory { + public: + // Implementations are expected to free the shared memory in the destructor. + virtual ~SharedMemory() = default; + virtual std::unique_ptr RemapTo( + void* new_address) const = 0; + virtual void* GetMemory() const = 0; + virtual size_t GetSize() const = 0; + }; + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + * + * Reserve pages at a fixed address returning whether the reservation is + * possible. The reserved memory is detached from the PageAllocator and so + * should not be freed by it. It's intended for use with + * SharedMemory::RemapTo, where ~SharedMemoryMapping would free the memory. + */ + virtual bool ReserveForSharedMemoryMapping(void* address, size_t size) { + return false; + } + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + * + * Allocates shared memory pages. Not all PageAllocators need support this and + * so this method need not be overridden. + * Allocates a new read-only shared memory region of size |length| and copies + * the memory at |original_address| into it. + */ + virtual std::unique_ptr AllocateSharedPages( + size_t length, const void* original_address) { + return {}; + } + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + * + * If not overridden and changed to return true, V8 will not attempt to call + * AllocateSharedPages or RemapSharedPages. If overridden, AllocateSharedPages + * and RemapSharedPages must also be overridden. + */ + virtual bool CanAllocateSharedPages() { return false; } }; /** @@ -519,15 +653,12 @@ class Platform { * libplatform looks like: * std::unique_ptr PostJob( * TaskPriority priority, std::unique_ptr job_task) override { - * return std::make_unique( - * std::make_shared( - * this, std::move(job_task), kNumThreads)); + * return v8::platform::NewDefaultJobHandle( + * this, priority, std::move(job_task), NumberOfWorkerThreads()); * } */ virtual std::unique_ptr PostJob( - TaskPriority priority, std::unique_ptr job_task) { - return nullptr; - } + TaskPriority priority, std::unique_ptr job_task) = 0; /** * Monotonically increasing time in seconds from an arbitrary fixed point in diff --git a/NativeScript/include/v8-profiler.h b/NativeScript/include/v8-profiler.h index c3b25e8d..74b6df88 100644 --- a/NativeScript/include/v8-profiler.h +++ b/NativeScript/include/v8-profiler.h @@ -6,6 +6,7 @@ #define V8_V8_PROFILER_H_ #include + #include #include #include @@ -248,6 +249,15 @@ enum CpuProfilingLoggingMode { kEagerLogging, }; +// Enum for returning profiling status. Once StartProfiling is called, +// we want to return to clients whether the profiling was able to start +// correctly, or return a descriptive error. +enum class CpuProfilingStatus { + kStarted, + kAlreadyStarted, + kErrorTooManyProfilers +}; + /** * Optional profiling attributes. */ @@ -268,6 +278,8 @@ class V8_EXPORT CpuProfilingOptions { * interval, set via SetSamplingInterval(). If * zero, the sampling interval will be equal to * the profiler's sampling interval. + * \param filter_context Deprecated option to filter by context, currently a + * no-op. */ CpuProfilingOptions( CpuProfilingMode mode = kLeafNodeLineNumbers, @@ -281,13 +293,9 @@ class V8_EXPORT CpuProfilingOptions { private: friend class internal::CpuProfile; - bool has_filter_context() const { return !filter_context_.IsEmpty(); } - void* raw_filter_context() const; - CpuProfilingMode mode_; unsigned max_samples_; int sampling_interval_us_; - CopyablePersistentTraits::CopyablePersistent filter_context_; }; /** @@ -338,7 +346,8 @@ class V8_EXPORT CpuProfiler { * profiles may be collected at once. Attempts to start collecting several * profiles with the same title are silently ignored. */ - void StartProfiling(Local title, CpuProfilingOptions options); + CpuProfilingStatus StartProfiling(Local title, + CpuProfilingOptions options); /** * Starts profiling with the same semantics as above, except with expanded @@ -351,7 +360,7 @@ class V8_EXPORT CpuProfiler { * recorded by the profiler. Samples obtained after this limit will be * discarded. */ - void StartProfiling( + CpuProfilingStatus StartProfiling( Local title, CpuProfilingMode mode, bool record_samples = false, unsigned max_samples = CpuProfilingOptions::kNoSampleLimit); /** @@ -359,7 +368,8 @@ class V8_EXPORT CpuProfiler { * kLeafNodeLineNumbers mode, which was the previous default behavior of the * profiler. */ - void StartProfiling(Local title, bool record_samples = false); + CpuProfilingStatus StartProfiling(Local title, + bool record_samples = false); /** * Stops collecting CPU profile with a given title and returns it. @@ -712,6 +722,19 @@ class V8_EXPORT EmbedderGraph { public: class Node { public: + /** + * Detachedness specifies whether an object is attached or detached from the + * main application state. While unkown in general, there may be objects + * that specifically know their state. V8 passes this information along in + * the snapshot. Users of the snapshot may use it to annotate the object + * graph. + */ + enum class Detachedness : uint8_t { + kUnknown = 0, + kAttached = 1, + kDetached = 2, + }; + Node() = default; virtual ~Node() = default; virtual const char* Name() = 0; @@ -736,6 +759,14 @@ class V8_EXPORT EmbedderGraph { */ virtual NativeObject GetNativeObject() { return nullptr; } + /** + * Detachedness state of a given object. While unkown in general, there may + * be objects that specifically know their state. V8 passes this information + * along in the snapshot. Users of the snapshot may use it to annotate the + * object graph. + */ + virtual Detachedness GetDetachedness() { return Detachedness::kUnknown; } + Node(const Node&) = delete; Node& operator=(const Node&) = delete; }; @@ -786,6 +817,18 @@ class V8_EXPORT HeapProfiler { v8::EmbedderGraph* graph, void* data); + /** + * Callback function invoked during heap snapshot generation to retrieve + * the detachedness state of an object referenced by a TracedReference. + * + * The callback takes Local as parameter to allow the embedder to + * unpack the TracedReference into a Local and reuse that Local for different + * purposes. + */ + using GetDetachednessCallback = EmbedderGraph::Node::Detachedness (*)( + v8::Isolate* isolate, const v8::Local& v8_value, + uint16_t class_id, void* data); + /** Returns the number of snapshots taken. */ int GetSnapshotCount(); @@ -936,6 +979,8 @@ class V8_EXPORT HeapProfiler { void RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback, void* data); + void SetGetDetachednessCallback(GetDetachednessCallback callback, void* data); + /** * Default value of persistent handle class ID. Must not be used to * define a class. Can be used to reset a class of a persistent diff --git a/NativeScript/include/v8-version.h b/NativeScript/include/v8-version.h index 0ae05ed4..0659df6e 100644 --- a/NativeScript/include/v8-version.h +++ b/NativeScript/include/v8-version.h @@ -9,7 +9,7 @@ // NOTE these macros are used by some of the tool scripts and the build // system so their names cannot be changed without changing the scripts. #define V8_MAJOR_VERSION 8 -#define V8_MINOR_VERSION 4 +#define V8_MINOR_VERSION 9 #define V8_BUILD_NUMBER 0 #define V8_PATCH_LEVEL 0 diff --git a/NativeScript/include/v8.h b/NativeScript/include/v8.h index 2fdd86ef..f3a62b72 100644 --- a/NativeScript/include/v8.h +++ b/NativeScript/include/v8.h @@ -18,12 +18,15 @@ #include #include #include + +#include #include #include #include #include #include +#include "cppgc/common.h" #include "v8-internal.h" // NOLINT(build/include_directory) #include "v8-version.h" // NOLINT(build/include_directory) #include "v8config.h" // NOLINT(build/include_directory) @@ -43,7 +46,10 @@ class BigInt; class BigIntObject; class Boolean; class BooleanObject; +class CFunction; class Context; +class CppHeap; +struct CppHeapCreateParams; class Data; class Date; class External; @@ -79,6 +85,7 @@ class String; class StringObject; class Symbol; class SymbolObject; +class TracedReferenceBase; class PrimitiveArray; class Private; class Uint32; @@ -100,7 +107,7 @@ class TracedGlobal; template class TracedReference; template -class TracedReferenceBase; +class BasicTracedReference; template class PersistentValueMap; template class PersistentValueMapBase; @@ -123,9 +130,9 @@ namespace internal { enum class ArgumentsType; template class Arguments; +class BasicTracedReferenceExtractor; template class CustomArguments; -class DeferredHandles; class FunctionCallbackArguments; class GlobalHandles; class Heap; @@ -147,6 +154,10 @@ class StreamingDecoder; } // namespace internal +namespace metrics { +class Recorder; +} // namespace metrics + namespace debug { class ConsoleCallArguments; } // namespace debug @@ -294,9 +305,10 @@ class Local { V8_INLINE static Local New(Isolate* isolate, const PersistentBase& that); V8_INLINE static Local New(Isolate* isolate, - const TracedReferenceBase& that); + const BasicTracedReference& that); private: + friend class TracedReferenceBase; friend class Utils; template friend class Eternal; template friend class PersistentBase; @@ -328,7 +340,7 @@ class Local { template friend class TracedGlobal; template - friend class TracedReferenceBase; + friend class BasicTracedReference; template friend class TracedReference; @@ -818,27 +830,11 @@ using UniquePersistent = Global; template struct TracedGlobalTrait {}; -/** - * A traced handle with copy and move semantics. The handle is to be used - * together with |v8::EmbedderHeapTracer| and specifies edges from the embedder - * into V8's heap. - * - * The exact semantics are: - * - Tracing garbage collections use |v8::EmbedderHeapTracer|. - * - Non-tracing garbage collections refer to - * |v8::EmbedderHeapTracer::IsRootForNonTracingGC()| whether the handle should - * be treated as root or not. - * - * Note that the base class cannot be instantiated itself. Choose from - * - TracedGlobal - * - TracedReference - */ -template class TracedReferenceBase { public: /** - * Returns true if this TracedReferenceBase is empty, i.e., has not been - * assigned an object. + * Returns true if the reference is empty, i.e., has not been assigned + * object. */ bool IsEmpty() const { return val_ == nullptr; } @@ -849,36 +845,16 @@ class TracedReferenceBase { V8_INLINE void Reset(); /** - * Construct a Local from this handle. + * Construct a Local from this handle. */ - Local Get(Isolate* isolate) const { return Local::New(isolate, *this); } - - template - V8_INLINE bool operator==(const TracedReferenceBase& that) const { - internal::Address* a = reinterpret_cast(val_); - internal::Address* b = reinterpret_cast(that.val_); - if (a == nullptr) return b == nullptr; - if (b == nullptr) return false; - return *a == *b; - } - - template - V8_INLINE bool operator==(const Local& that) const { - internal::Address* a = reinterpret_cast(val_); - internal::Address* b = reinterpret_cast(that.val_); - if (a == nullptr) return b == nullptr; - if (b == nullptr) return false; - return *a == *b; - } + V8_INLINE v8::Local Get(v8::Isolate* isolate) const; - template - V8_INLINE bool operator!=(const TracedReferenceBase& that) const { - return !operator==(that); - } - - template - V8_INLINE bool operator!=(const Local& that) const { - return !operator==(that); + /** + * Returns true if this TracedReference is empty, i.e., has not been + * assigned an object. This version of IsEmpty is thread-safe. + */ + bool IsEmptyThreadSafe() const { + return this->GetSlotThreadSafe() == nullptr; } /** @@ -892,24 +868,77 @@ class TracedReferenceBase { */ V8_INLINE uint16_t WrapperClassId() const; + protected: + /** + * Update this reference in a thread-safe way. + */ + void SetSlotThreadSafe(void* new_val) { + reinterpret_cast*>(&val_)->store( + new_val, std::memory_order_relaxed); + } + + /** + * Get this reference in a thread-safe way + */ + const void* GetSlotThreadSafe() const { + return reinterpret_cast const*>(&val_)->load( + std::memory_order_relaxed); + } + + // val_ points to a GlobalHandles node. + internal::Address* val_ = nullptr; + + friend class internal::BasicTracedReferenceExtractor; + template + friend class Local; + template + friend bool operator==(const TracedReferenceBase&, const Local&); + friend bool operator==(const TracedReferenceBase&, + const TracedReferenceBase&); +}; + +/** + * A traced handle with copy and move semantics. The handle is to be used + * together with |v8::EmbedderHeapTracer| or as part of GarbageCollected objects + * (see v8-cppgc.h) and specifies edges from C++ objects to JavaScript. + * + * The exact semantics are: + * - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc. + * - Non-tracing garbage collections refer to + * |v8::EmbedderHeapTracer::IsRootForNonTracingGC()| whether the handle should + * be treated as root or not. + * + * Note that the base class cannot be instantiated itself. Choose from + * - TracedGlobal + * - TracedReference + */ +template +class BasicTracedReference : public TracedReferenceBase { + public: + /** + * Construct a Local from this handle. + */ + Local Get(Isolate* isolate) const { return Local::New(isolate, *this); } + template - V8_INLINE TracedReferenceBase& As() const { - return reinterpret_cast&>( - const_cast&>(*this)); + V8_INLINE BasicTracedReference& As() const { + return reinterpret_cast&>( + const_cast&>(*this)); } + T* operator->() const { return reinterpret_cast(val_); } + T* operator*() const { return reinterpret_cast(val_); } + private: enum DestructionMode { kWithDestructor, kWithoutDestructor }; /** - * An empty TracedReferenceBase without storage cell. + * An empty BasicTracedReference without storage cell. */ - TracedReferenceBase() = default; + BasicTracedReference() = default; - V8_INLINE static T* New(Isolate* isolate, T* that, void* slot, - DestructionMode destruction_mode); - - T* val_ = nullptr; + V8_INLINE static internal::Address* New(Isolate* isolate, T* that, void* slot, + DestructionMode destruction_mode); friend class EmbedderHeapTracer; template @@ -920,27 +949,29 @@ class TracedReferenceBase { template friend class TracedReference; template + friend class BasicTracedReference; + template friend class ReturnValue; }; /** * A traced handle with destructor that clears the handle. For more details see - * TracedReferenceBase. + * BasicTracedReference. */ template -class TracedGlobal : public TracedReferenceBase { +class TracedGlobal : public BasicTracedReference { public: - using TracedReferenceBase::Reset; + using BasicTracedReference::Reset; /** - * Destructor resetting the handle. + * Destructor resetting the handle.Is */ ~TracedGlobal() { this->Reset(); } /** * An empty TracedGlobal without storage cell. */ - TracedGlobal() : TracedReferenceBase() {} + TracedGlobal() : BasicTracedReference() {} /** * Construct a TracedGlobal from a Local. @@ -949,9 +980,9 @@ class TracedGlobal : public TracedReferenceBase { * pointing to the same object. */ template - TracedGlobal(Isolate* isolate, Local that) : TracedReferenceBase() { + TracedGlobal(Isolate* isolate, Local that) : BasicTracedReference() { this->val_ = this->New(isolate, that.val_, &this->val_, - TracedReferenceBase::kWithDestructor); + BasicTracedReference::kWithDestructor); static_assert(std::is_base_of::value, "type check"); } @@ -1048,7 +1079,7 @@ class TracedGlobal : public TracedReferenceBase { * A traced handle without destructor that clears the handle. The embedder needs * to ensure that the handle is not accessed once the V8 object has been * reclaimed. This can happen when the handle is not passed through the - * EmbedderHeapTracer. For more details see TracedReferenceBase. + * EmbedderHeapTracer. For more details see BasicTracedReference. * * The reference assumes the embedder has precise knowledge about references at * all times. In case V8 needs to separately handle on-stack references, the @@ -1056,14 +1087,14 @@ class TracedGlobal : public TracedReferenceBase { * |EmbedderHeapTracer::SetStackStart|. */ template -class TracedReference : public TracedReferenceBase { +class TracedReference : public BasicTracedReference { public: - using TracedReferenceBase::Reset; + using BasicTracedReference::Reset; /** * An empty TracedReference without storage cell. */ - TracedReference() : TracedReferenceBase() {} + TracedReference() : BasicTracedReference() {} /** * Construct a TracedReference from a Local. @@ -1072,9 +1103,9 @@ class TracedReference : public TracedReferenceBase { * pointing to the same object. */ template - TracedReference(Isolate* isolate, Local that) : TracedReferenceBase() { + TracedReference(Isolate* isolate, Local that) : BasicTracedReference() { this->val_ = this->New(isolate, that.val_, &this->val_, - TracedReferenceBase::kWithoutDestructor); + BasicTracedReference::kWithoutDestructor); static_assert(std::is_base_of::value, "type check"); } @@ -1287,6 +1318,32 @@ class V8_EXPORT SealHandleScope { * The superclass of objects that can reside on V8's heap. */ class V8_EXPORT Data { + public: + /** + * Returns true if this data is a |v8::Value|. + */ + bool IsValue() const; + + /** + * Returns true if this data is a |v8::Module|. + */ + bool IsModule() const; + + /** + * Returns true if this data is a |v8::Private|. + */ + bool IsPrivate() const; + + /** + * Returns true if this data is a |v8::ObjectTemplate|. + */ + bool IsObjectTemplate() const; + + /** + * Returns true if this data is a |v8::FunctionTemplate|. + */ + bool IsFunctionTemplate() const; + private: Data(); }; @@ -1543,6 +1600,31 @@ class V8_EXPORT Module : public Data { */ Local GetUnboundModuleScript(); + /** + * Returns the underlying script's id. + * + * The module must be a SourceTextModule and must not have a kErrored status. + */ + int ScriptId(); + + /** + * Returns whether this module or any of its requested modules is async, + * i.e. contains top-level await. + * + * The module's status must be at least kInstantiated. + */ + bool IsGraphAsync() const; + + /** + * Returns whether the module is a SourceTextModule. + */ + bool IsSourceTextModule() const; + + /** + * Returns whether the module is a SyntheticModule. + */ + bool IsSyntheticModule() const; + /* * Callback defined in the embedder. This is responsible for setting * the module's exported values with calls to SetSyntheticModuleExport(). @@ -1574,13 +1656,18 @@ class V8_EXPORT Module : public Data { */ V8_WARN_UNUSED_RESULT Maybe SetSyntheticModuleExport( Isolate* isolate, Local export_name, Local export_value); - V8_DEPRECATE_SOON( + V8_DEPRECATED( "Use the preceding SetSyntheticModuleExport with an Isolate parameter, " "instead of the one that follows. The former will throw a runtime " "error if called for an export that doesn't exist (as per spec); " "the latter will crash with a failed CHECK().") void SetSyntheticModuleExport(Local export_name, Local export_value); + + V8_INLINE static Module* Cast(Data* data); + + private: + static void CheckCast(Data* obj); }; /** @@ -1754,7 +1841,7 @@ class V8_EXPORT ScriptCompiler { public: enum Encoding { ONE_BYTE, TWO_BYTE, UTF8 }; - V8_DEPRECATE_SOON( + V8_DEPRECATED( "This class takes ownership of source_stream, so use the constructor " "taking a unique_ptr to make these semantics clearer") StreamedSource(ExternalSourceStream* source_stream, Encoding encoding); @@ -1774,7 +1861,7 @@ class V8_EXPORT ScriptCompiler { /** * A streaming task which the embedder must run on a background thread to - * stream scripts into V8. Returned by ScriptCompiler::StartStreamingScript. + * stream scripts into V8. Returned by ScriptCompiler::StartStreaming. */ class V8_EXPORT ScriptStreamingTask final { public: @@ -1861,9 +1948,12 @@ class V8_EXPORT ScriptCompiler { * This API allows to start the streaming with as little data as possible, and * the remaining data (for example, the ScriptOrigin) is passed to Compile. */ + V8_DEPRECATED("Use ScriptCompiler::StartStreamingScript instead.") static ScriptStreamingTask* StartStreamingScript( Isolate* isolate, StreamedSource* source, CompileOptions options = kNoCompileOptions); + static ScriptStreamingTask* StartStreaming(Isolate* isolate, + StreamedSource* source); /** * Compiles a streamed script (bound to current context). @@ -2184,14 +2274,25 @@ enum StateTag { IDLE }; +// Holds the callee saved registers needed for the stack unwinder. It is the +// empty struct if no registers are required. Implemented in +// include/v8-unwinder-state.h. +struct CalleeSavedRegisters; + // A RegisterState represents the current state of registers used // by the sampling profiler API. -struct RegisterState { - RegisterState() : pc(nullptr), sp(nullptr), fp(nullptr), lr(nullptr) {} +struct V8_EXPORT RegisterState { + RegisterState(); + ~RegisterState(); + RegisterState(const RegisterState& other); + RegisterState& operator=(const RegisterState& other); + void* pc; // Instruction pointer. void* sp; // Stack pointer. void* fp; // Frame pointer. void* lr; // Link register (or nullptr on platforms without a link register). + // Callee saved registers (or null if no callee saved registers were stored) + std::unique_ptr callee_saved; }; // The output structure filled up by GetStackSample API function. @@ -2212,14 +2313,6 @@ struct JSEntryStub { MemoryRange code; }; -struct UnwindState { - MemoryRange code_range; - MemoryRange embedded_code_range; - JSEntryStub js_entry_stub; - JSEntryStub js_construct_entry_stub; - JSEntryStub js_run_microtasks_entry_stub; -}; - struct JSEntryStubs { JSEntryStub js_entry_stub; JSEntryStub js_construct_entry_stub; @@ -2884,6 +2977,8 @@ class V8_EXPORT Value : public Data { bool FullIsUndefined() const; bool FullIsNull() const; bool FullIsString() const; + + static void CheckCast(Data* that); }; @@ -3039,10 +3134,18 @@ class V8_EXPORT String : public Name { V8_INLINE static Local Empty(Isolate* isolate); /** - * Returns true if the string is external + * Returns true if the string is external two-byte. + * */ + V8_DEPRECATED( + "Use String::IsExternalTwoByte() or String::IsExternalOneByte()") bool IsExternal() const; + /** + * Returns true if the string is both external and two-byte. + */ + bool IsExternalTwoByte() const; + /** * Returns true if the string is both external and one-byte. */ @@ -3351,7 +3454,7 @@ class V8_EXPORT Symbol : public Name { */ Local Description() const; - V8_DEPRECATE_SOON("Use Symbol::Description()") + V8_DEPRECATED("Use Symbol::Description()") Local Name() const { return Description(); } /** @@ -3614,11 +3717,11 @@ enum PropertyFilter { /** * Options for marking whether callbacks may trigger JS-observable side effects. - * Side-effect-free callbacks are whitelisted during debug evaluation with + * Side-effect-free callbacks are allowlisted during debug evaluation with * throwOnSideEffect. It applies when calling a Function, FunctionTemplate, * or an Accessor callback. For Interceptors, please see * PropertyHandlerFlags's kHasNoSideEffect. - * Callbacks that only cause side effects to the receiver are whitelisted if + * Callbacks that only cause side effects to the receiver are allowlisted if * invoked on receiver objects that are created within the same debug-evaluate * call, as these objects are temporary and the side effect does not escape. */ @@ -3891,10 +3994,10 @@ class V8_EXPORT Object : public Value { return object.val_->InternalFieldCount(); } - /** Same as above, but works for TracedReferenceBase. */ + /** Same as above, but works for BasicTracedReference. */ V8_INLINE static int InternalFieldCount( - const TracedReferenceBase& object) { - return object.val_->InternalFieldCount(); + const BasicTracedReference& object) { + return object->InternalFieldCount(); } /** Gets the value from an internal field. */ @@ -3918,8 +4021,8 @@ class V8_EXPORT Object : public Value { /** Same as above, but works for TracedGlobal. */ V8_INLINE static void* GetAlignedPointerFromInternalField( - const TracedReferenceBase& object, int index) { - return object.val_->GetAlignedPointerFromInternalField(index); + const BasicTracedReference& object, int index) { + return object->GetAlignedPointerFromInternalField(index); } /** @@ -4102,6 +4205,16 @@ class V8_EXPORT Object : public Value { V8_INLINE static Object* Cast(Value* obj); + /** + * Support for TC39 "dynamic code brand checks" proposal. + * + * This API allows to query whether an object was constructed from a + * "code like" ObjectTemplate. + * + * See also: v8::ObjectTemplate::SetCodeLike + */ + bool IsCodeLike(Isolate* isolate); + private: Object(); static void CheckCast(Value* obj); @@ -4215,7 +4328,7 @@ class ReturnValue { template V8_INLINE void Set(const Global& handle); template - V8_INLINE void Set(const TracedReferenceBase& handle); + V8_INLINE void Set(const BasicTracedReference& handle); template V8_INLINE void Set(const Local handle); // Fast primitive setters @@ -4510,6 +4623,15 @@ class V8_EXPORT Function : public Object { */ Local GetBoundFunction() const; + /** + * Calls builtin Function.prototype.toString on this function. + * This is different from Value::ToString() that may call a user-defined + * toString() function, and different than Object::ObjectProtoToString() which + * always serializes "[object Function]". + */ + V8_WARN_UNUSED_RESULT MaybeLocal FunctionProtoToString( + Local context); + ScriptOrigin GetScriptOrigin() const; V8_INLINE static Function* Cast(Value* obj); static const int kLineOffsetNotFound; @@ -4970,6 +5092,13 @@ class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase { */ bool IsShared() const; + /** + * Prevent implicit instantiation of operator delete with size_t argument. + * The size_t argument would be incorrect because ptr points to the + * internal BackingStore object. + */ + void operator delete(void* ptr) { ::operator delete(ptr); } + /** * Wrapper around ArrayBuffer::Allocator::Reallocate that preserves IsShared. * Assumes that the backing_store was allocated by the ArrayBuffer allocator @@ -5346,9 +5475,10 @@ class V8_EXPORT TypedArray : public ArrayBufferView { /* * The largest typed array size that can be constructed using New. */ - static constexpr size_t kMaxLength = internal::kApiSystemPointerSize == 4 - ? internal::kSmiMaxValue - : 0xFFFFFFFF; + static constexpr size_t kMaxLength = + internal::kApiSystemPointerSize == 4 + ? internal::kSmiMaxValue + : static_cast(uint64_t{1} << 32); /** * Number of elements in this typed array @@ -5874,6 +6004,10 @@ class V8_EXPORT RegExp : public Object { /** * Regular expression flag bits. They can be or'ed to enable a set * of flags. + * The kLinear value ('l') is experimental and can only be used with + * --enable-experimental-regexp-engine. RegExps with kLinear flag are + * guaranteed to be executed in asymptotic linear time wrt. the length of + * the subject string. */ enum Flags { kNone = 0, @@ -5883,9 +6017,10 @@ class V8_EXPORT RegExp : public Object { kSticky = 1 << 3, kUnicode = 1 << 4, kDotAll = 1 << 5, + kLinear = 1 << 6, }; - static constexpr int kFlagCount = 6; + static constexpr int kFlagCount = 7; /** * Creates a regular expression from the given pattern string and @@ -5954,13 +6089,14 @@ class V8_EXPORT External : public Value { static void CheckCast(v8::Value* obj); }; -#define V8_INTRINSICS_LIST(F) \ - F(ArrayProto_entries, array_entries_iterator) \ - F(ArrayProto_forEach, array_for_each_iterator) \ - F(ArrayProto_keys, array_keys_iterator) \ - F(ArrayProto_values, array_values_iterator) \ - F(ErrorPrototype, initial_error_prototype) \ - F(IteratorPrototype, initial_iterator_prototype) \ +#define V8_INTRINSICS_LIST(F) \ + F(ArrayProto_entries, array_entries_iterator) \ + F(ArrayProto_forEach, array_for_each_iterator) \ + F(ArrayProto_keys, array_keys_iterator) \ + F(ArrayProto_values, array_values_iterator) \ + F(AsyncIteratorPrototype, initial_async_iterator_prototype) \ + F(ErrorPrototype, initial_error_prototype) \ + F(IteratorPrototype, initial_iterator_prototype) \ F(ObjProto_valueOf, object_value_of_function) enum Intrinsic { @@ -6026,7 +6162,6 @@ class V8_EXPORT Template : public Data { void SetNativeDataProperty( Local name, AccessorGetterCallback getter, AccessorSetterCallback setter = nullptr, - // TODO(dcarney): gcc can't handle Local below Local data = Local(), PropertyAttribute attribute = None, Local signature = Local(), AccessControl settings = DEFAULT, @@ -6035,7 +6170,6 @@ class V8_EXPORT Template : public Data { void SetNativeDataProperty( Local name, AccessorNameGetterCallback getter, AccessorNameSetterCallback setter = nullptr, - // TODO(dcarney): gcc can't handle Local below Local data = Local(), PropertyAttribute attribute = None, Local signature = Local(), AccessControl settings = DEFAULT, @@ -6307,7 +6441,6 @@ typedef bool (*AccessCheckCallback)(Local accessing_context, Local accessed_object, Local data); -class CFunction; /** * A FunctionTemplate is used to create functions at runtime. There * can only be one function created from a FunctionTemplate in a @@ -6882,6 +7015,18 @@ class V8_EXPORT ObjectTemplate : public Template { */ void SetImmutableProto(); + /** + * Support for TC39 "dynamic code brand checks" proposal. + * + * This API allows to mark (& query) objects as "code like", which causes + * them to be treated like Strings in the context of eval and function + * constructor. + * + * Reference: https://github.com/tc39/proposal-dynamic-code-brand-checks + */ + void SetCodeLike(); + bool IsCodeLike(); + V8_INLINE static ObjectTemplate* Cast(Data* data); private: @@ -7085,36 +7230,11 @@ class V8_EXPORT ResourceConstraints { initial_young_generation_size_ = initial_size; } - /** - * Deprecated functions. Do not use in new code. - */ - V8_DEPRECATE_SOON("Use code_range_size_in_bytes.") - size_t code_range_size() const { return code_range_size_ / kMB; } - V8_DEPRECATE_SOON("Use set_code_range_size_in_bytes.") - void set_code_range_size(size_t limit_in_mb) { - code_range_size_ = limit_in_mb * kMB; - } - V8_DEPRECATE_SOON("Use max_young_generation_size_in_bytes.") - size_t max_semi_space_size_in_kb() const; - V8_DEPRECATE_SOON("Use set_max_young_generation_size_in_bytes.") - void set_max_semi_space_size_in_kb(size_t limit_in_kb); - V8_DEPRECATE_SOON("Use max_old_generation_size_in_bytes.") - size_t max_old_space_size() const { return max_old_generation_size_ / kMB; } - V8_DEPRECATE_SOON("Use set_max_old_generation_size_in_bytes.") - void set_max_old_space_size(size_t limit_in_mb) { - max_old_generation_size_ = limit_in_mb * kMB; - } - V8_DEPRECATE_SOON("Zone does not pool memory any more.") - size_t max_zone_pool_size() const { return max_zone_pool_size_; } - V8_DEPRECATE_SOON("Zone does not pool memory any more.") - void set_max_zone_pool_size(size_t bytes) { max_zone_pool_size_ = bytes; } - private: static constexpr size_t kMB = 1048576u; size_t code_range_size_ = 0; size_t max_old_generation_size_ = 0; size_t max_young_generation_size_ = 0; - size_t max_zone_pool_size_ = 0; size_t initial_old_generation_size_ = 0; size_t initial_young_generation_size_ = 0; uint32_t* stack_limit_ = nullptr; @@ -7290,7 +7410,7 @@ class PromiseRejectMessage { typedef void (*PromiseRejectCallback)(PromiseRejectMessage message); // --- Microtasks Callbacks --- -V8_DEPRECATE_SOON("Use *WithData version.") +V8_DEPRECATED("Use *WithData version.") typedef void (*MicrotasksCompletedCallback)(Isolate*); typedef void (*MicrotasksCompletedCallbackWithData)(Isolate*, void*); typedef void (*MicrotaskCallback)(void* data); @@ -7456,11 +7576,15 @@ struct ModifyCodeGenerationFromStringsResult { /** * Callback to check if codegen is allowed from a source object, and convert - * the source to string if necessary.See ModifyCodeGenerationFromStrings. + * the source to string if necessary. See: ModifyCodeGenerationFromStrings. */ typedef ModifyCodeGenerationFromStringsResult ( *ModifyCodeGenerationFromStringsCallback)(Local context, Local source); +typedef ModifyCodeGenerationFromStringsResult ( + *ModifyCodeGenerationFromStringsCallback2)(Local context, + Local source, + bool is_code_like); // --- WebAssembly compilation callbacks --- typedef bool (*ExtensionCallback)(const FunctionCallbackInfo&); @@ -7482,6 +7606,9 @@ typedef bool (*WasmThreadsEnabledCallback)(Local context); typedef Local (*WasmLoadSourceMapCallback)(Isolate* isolate, const char* name); +// --- Callback for checking if WebAssembly Simd is enabled --- +typedef bool (*WasmSimdEnabledCallback)(Local context); + // --- Garbage Collection Callbacks --- /** @@ -7839,19 +7966,14 @@ enum class MemoryPressureLevel { kNone, kModerate, kCritical }; */ class V8_EXPORT EmbedderHeapTracer { public: + using EmbedderStackState = cppgc::EmbedderStackState; + enum TraceFlags : uint64_t { kNoFlags = 0, kReduceMemory = 1 << 0, kForced = 1 << 2, }; - // Indicator for the stack state of the embedder. - enum EmbedderStackState { - kUnknown, - kNonEmpty, - kEmpty, - }; - /** * Interface for iterating through TracedGlobal handles. */ @@ -7908,7 +8030,7 @@ class V8_EXPORT EmbedderHeapTracer { virtual void RegisterV8References( const std::vector >& embedder_fields) = 0; - void RegisterEmbedderReference(const TracedReferenceBase& ref); + void RegisterEmbedderReference(const BasicTracedReference& ref); /** * Called at the beginning of a GC cycle. @@ -8067,10 +8189,11 @@ enum class MeasureMemoryMode { kSummary, kDetailed }; /** * Controls how promptly a memory measurement request is executed. * By default the measurement is folded with the next scheduled GC which may - * happen after a while. The kEager starts increment GC right away and - * is useful for testing. + * happen after a while and is forced after some timeout. + * The kEager mode starts incremental GC right away and is useful for testing. + * The kLazy mode does not force GC. */ -enum class MeasureMemoryExecution { kDefault, kEager }; +enum class MeasureMemoryExecution { kDefault, kEager, kLazy }; /** * The delegate is used in Isolate::MeasureMemory API. @@ -8130,26 +8253,15 @@ class V8_EXPORT Isolate { /** * Initial configuration parameters for a new Isolate. */ - struct CreateParams { - CreateParams() - : code_event_handler(nullptr), - snapshot_blob(nullptr), - counter_lookup_callback(nullptr), - create_histogram_callback(nullptr), - add_histogram_sample_callback(nullptr), - array_buffer_allocator(nullptr), - array_buffer_allocator_shared(), - external_references(nullptr), - allow_atomics_wait(true), - only_terminate_in_safe_scope(false), - embedder_wrapper_type_index(-1), - embedder_wrapper_object_index(-1) {} + struct V8_EXPORT CreateParams { + CreateParams(); + ~CreateParams(); /** * Allows the host application to provide the address of a function that is * notified each time code is added, moved or removed. */ - JitCodeEventHandler code_event_handler; + JitCodeEventHandler code_event_handler = nullptr; /** * ResourceConstraints to use for the new Isolate. @@ -8159,14 +8271,13 @@ class V8_EXPORT Isolate { /** * Explicitly specify a startup snapshot blob. The embedder owns the blob. */ - StartupData* snapshot_blob; - + StartupData* snapshot_blob = nullptr; /** * Enables the host application to provide a mechanism for recording * statistics counters. */ - CounterLookupCallback counter_lookup_callback; + CounterLookupCallback counter_lookup_callback = nullptr; /** * Enables the host application to provide a mechanism for recording @@ -8174,8 +8285,8 @@ class V8_EXPORT Isolate { * histogram which will later be passed to the AddHistogramSample * function. */ - CreateHistogramCallback create_histogram_callback; - AddHistogramSampleCallback add_histogram_sample_callback; + CreateHistogramCallback create_histogram_callback = nullptr; + AddHistogramSampleCallback add_histogram_sample_callback = nullptr; /** * The ArrayBuffer::Allocator to use for allocating and freeing the backing @@ -8186,7 +8297,7 @@ class V8_EXPORT Isolate { * to the allocator, in order to facilitate lifetime * management for the allocator instance. */ - ArrayBuffer::Allocator* array_buffer_allocator; + ArrayBuffer::Allocator* array_buffer_allocator = nullptr; std::shared_ptr array_buffer_allocator_shared; /** @@ -8195,28 +8306,37 @@ class V8_EXPORT Isolate { * deserialization. This array and its content must stay valid for the * entire lifetime of the isolate. */ - const intptr_t* external_references; + const intptr_t* external_references = nullptr; /** * Whether calling Atomics.wait (a function that may block) is allowed in * this isolate. This can also be configured via SetAllowAtomicsWait. */ - bool allow_atomics_wait; + bool allow_atomics_wait = true; /** * Termination is postponed when there is no active SafeForTerminationScope. */ - bool only_terminate_in_safe_scope; + bool only_terminate_in_safe_scope = false; /** * The following parameters describe the offsets for addressing type info * for wrapped API objects and are used by the fast C API * (for details see v8-fast-api-calls.h). */ - int embedder_wrapper_type_index; - int embedder_wrapper_object_index; - }; + int embedder_wrapper_type_index = -1; + int embedder_wrapper_object_index = -1; + /** + * If parameters are set, V8 creates a managed C++ heap as extension to its + * JavaScript heap. + * + * See v8::Isolate::GetCppHeap() for working with the heap. + * + * This is an experimental feature and may still change significantly. + */ + std::shared_ptr cpp_heap_params; + }; /** * Stack-allocated class which sets the isolate for all operations @@ -8307,7 +8427,7 @@ class V8_EXPORT Isolate { /** * This scope allows terminations inside direct V8 API calls and forbid them - * inside any recursice API calls without explicit SafeForTerminationScope. + * inside any recursive API calls without explicit SafeForTerminationScope. */ class V8_EXPORT SafeForTerminationScope { public: @@ -8390,8 +8510,8 @@ class V8_EXPORT Isolate { kFunctionTokenOffsetTooLongForToString = 49, kWasmSharedMemory = 50, kWasmThreadOpcodes = 51, - kAtomicsNotify = 52, - kAtomicsWake = 53, + kAtomicsNotify = 52, // Unused. + kAtomicsWake = 53, // Unused. kCollator = 54, kNumberFormat = 55, kDateTimeFormat = 56, @@ -8444,6 +8564,11 @@ class V8_EXPORT Isolate { kInvalidatedStringIteratorLookupChainProtector = 103, kInvalidatedStringLengthOverflowLookupChainProtector = 104, kInvalidatedTypedArraySpeciesLookupChainProtector = 105, + kWasmSimdOpcodes = 106, + kVarRedeclaredCatchBinding = 107, + kWasmRefTypes = 108, + kWasmBulkMemory = 109, + kWasmMultiValue = 110, // If you add new values here, you'll also need to update Chromium's: // web_feature.mojom, use_counter_callback.cc, and enums.xml. V8 changes to @@ -8540,7 +8665,7 @@ class V8_EXPORT Isolate { HostImportModuleDynamicallyCallback callback); /** - * This specifies the callback called by the upcoming importa.meta + * This specifies the callback called by the upcoming import.meta * language feature to retrieve host-defined meta data for a module. */ void SetHostInitializeImportMetaObjectCallback( @@ -8692,7 +8817,7 @@ class V8_EXPORT Isolate { std::unique_ptr delegate, MeasureMemoryExecution execution = MeasureMemoryExecution::kDefault); - V8_DEPRECATE_SOON("Use the version with a delegate") + V8_DEPRECATED("Use the version with a delegate") MaybeLocal MeasureMemory(Local context, MeasureMemoryMode mode); @@ -8724,8 +8849,7 @@ class V8_EXPORT Isolate { * kept alive by JavaScript objects. * \returns the adjusted value. */ - V8_INLINE int64_t - AdjustAmountOfExternalAllocatedMemory(int64_t change_in_bytes); + int64_t AdjustAmountOfExternalAllocatedMemory(int64_t change_in_bytes); /** * Returns the number of phantom handles without callbacks that were reset @@ -8756,10 +8880,6 @@ class V8_EXPORT Isolate { */ Local GetCurrentContext(); - /** Returns the last context entered through V8's C++ API. */ - V8_DEPRECATED("Use GetEnteredOrMicrotaskContext().") - Local GetEnteredContext(); - /** * Returns either the last context entered through V8's C++ API, or the * context of the currently running microtask while processing microtasks. @@ -8818,6 +8938,12 @@ class V8_EXPORT Isolate { */ EmbedderHeapTracer* GetEmbedderHeapTracer(); + /** + * \returns the C++ heap managed by V8. Only available if the Isolate was + * created with proper CreatePrams::cpp_heap_params option. + */ + CppHeap* GetCppHeap() const; + /** * Use for |AtomicsWaitCallback| to indicate the type of event it receives. */ @@ -8975,6 +9101,13 @@ class V8_EXPORT Isolate { */ void RequestInterrupt(InterruptCallback callback, void* data); + /** + * Returns true if there is ongoing background work within V8 that will + * eventually post a foreground task, like asynchronous WebAssembly + * compilation. + */ + bool HasPendingBackgroundTasks(); + /** * Request garbage collection in this Isolate. It is only valid to call this * function if --expose_gc was specified. @@ -9031,12 +9164,6 @@ class V8_EXPORT Isolate { */ void SetPromiseRejectCallback(PromiseRejectCallback callback); - /** - * An alias for PerformMicrotaskCheckpoint. - */ - V8_DEPRECATE_SOON("Use PerformMicrotaskCheckpoint.") - void RunMicrotasks() { PerformMicrotaskCheckpoint(); } - /** * Runs the default MicrotaskQueue until it gets empty and perform other * microtask checkpoint steps, such as calling ClearKeptObjects. Asserts that @@ -9078,7 +9205,7 @@ class V8_EXPORT Isolate { * Executing scripts inside the callback will not re-trigger microtasks and * the callback. */ - V8_DEPRECATE_SOON("Use *WithData version.") + V8_DEPRECATED("Use *WithData version.") void AddMicrotasksCompletedCallback(MicrotasksCompletedCallback callback); void AddMicrotasksCompletedCallback( MicrotasksCompletedCallbackWithData callback, void* data = nullptr); @@ -9086,7 +9213,7 @@ class V8_EXPORT Isolate { /** * Removes callback that was installed by AddMicrotasksCompletedCallback. */ - V8_DEPRECATE_SOON("Use *WithData version.") + V8_DEPRECATED("Use *WithData version.") void RemoveMicrotasksCompletedCallback(MicrotasksCompletedCallback callback); void RemoveMicrotasksCompletedCallback( MicrotasksCompletedCallbackWithData callback, void* data = nullptr); @@ -9111,6 +9238,18 @@ class V8_EXPORT Isolate { void SetCreateHistogramFunction(CreateHistogramCallback); void SetAddHistogramSampleFunction(AddHistogramSampleCallback); + /** + * Enables the host application to provide a mechanism for recording + * event based metrics. In order to use this interface + * include/v8-metrics.h + * needs to be included and the recorder needs to be derived from the + * Recorder base class defined there. + * This method can only be called once per isolate and must happen during + * isolate initialization before background threads are spawned. + */ + void SetMetricsRecorder( + const std::shared_ptr& metrics_recorder); + /** * Enables the host application to provide a mechanism for recording a * predefined set of data as crash keys to be used in postmortem debugging in @@ -9255,11 +9394,10 @@ class V8_EXPORT Isolate { void GetCodeRange(void** start, size_t* length_in_bytes); /** - * Returns the UnwindState necessary for use with the Unwinder API. + * As GetCodeRange, but for embedded builtins (these live in a distinct + * memory region from other V8 Code objects). */ - // TODO(petermarshall): Remove this API. - V8_DEPRECATED("Use entry_stubs + code_pages version.") - UnwindState GetUnwindState(); + void GetEmbeddedCodeRange(const void** start, size_t* length_in_bytes); /** * Returns the JSEntryStubs necessary for use with the Unwinder API. @@ -9319,12 +9457,14 @@ class V8_EXPORT Isolate { * strings should be allowed. */ V8_DEPRECATED( - "Use Isolate::SetModifyCodeGenerationFromStringsCallback instead. " - "See http://crbug.com/v8/10096.") - void SetAllowCodeGenerationFromStringsCallback( - AllowCodeGenerationFromStringsCallback callback); + "Use Isolate::SetModifyCodeGenerationFromStringsCallback with " + "ModifyCodeGenerationFromStringsCallback2 instead. See " + "http://crbug.com/1096017 and TC39 Dynamic Code Brand Checks proposal " + "at https://github.com/tc39/proposal-dynamic-code-brand-checks.") void SetModifyCodeGenerationFromStringsCallback( ModifyCodeGenerationFromStringsCallback callback); + void SetModifyCodeGenerationFromStringsCallback( + ModifyCodeGenerationFromStringsCallback2 callback); /** * Set the callback to invoke to check if wasm code generation should @@ -9346,6 +9486,8 @@ class V8_EXPORT Isolate { void SetWasmLoadSourceMapCallback(WasmLoadSourceMapCallback callback); + void SetWasmSimdEnabledCallback(WasmSimdEnabledCallback callback); + /** * Check if V8 is dead and therefore unusable. This is the case after * fatal errors such as out-of-memory situations. @@ -9494,12 +9636,16 @@ class V8_EXPORT StartupData { * Only valid for StartupData returned by SnapshotCreator::CreateBlob(). */ bool CanBeRehashed() const; + /** + * Allows embedders to verify whether the data is valid for the current + * V8 instance. + */ + bool IsValid() const; const char* data; int raw_size; }; - /** * EntropySource is used as a callback function when v8 needs a source * of entropy. @@ -9675,7 +9821,7 @@ class V8_EXPORT V8 { * \param context The third argument passed to the Linux signal handler, which * points to a ucontext_t structure. */ - V8_DEPRECATE_SOON("Use TryHandleWebAssemblyTrapPosix") + V8_DEPRECATED("Use TryHandleWebAssemblyTrapPosix") static bool TryHandleSignal(int signal_number, void* info, void* context); #endif // V8_OS_POSIX @@ -9706,6 +9852,12 @@ class V8_EXPORT V8 { */ static void GetSharedMemoryStatistics(SharedMemoryStatistics* statistics); + /** + * Notifies V8 that the process is cross-origin-isolated, which enables + * defining the SharedArrayBuffer function on the global object of Contexts. + */ + static void SetIsCrossOriginIsolated(); + private: V8(); @@ -9755,6 +9907,8 @@ class V8_EXPORT V8 { static void ToLocalEmpty(); static void InternalFieldOutOfBounds(int index); template + friend class BasicTracedReference; + template friend class Global; template friend class Local; template @@ -9762,9 +9916,8 @@ class V8_EXPORT V8 { template friend class Maybe; template - friend class TracedReferenceBase; - template friend class TracedGlobal; + friend class TracedReferenceBase; template friend class TracedReference; template @@ -10058,8 +10211,6 @@ class V8_EXPORT TryCatch { /** * Returns the exception caught by this try/catch block. If no exception has * been caught an empty handle is returned. - * - * The returned handle is valid until this TryCatch block has been destroyed. */ Local Exception() const; @@ -10081,9 +10232,6 @@ class V8_EXPORT TryCatch { /** * Returns the message associated with this exception. If there is * no message associated an empty handle is returned. - * - * The returned handle is valid until this TryCatch block has been - * destroyed. */ Local Message() const; @@ -10617,12 +10765,14 @@ class V8_EXPORT Unwinder { * * The unwinder also needs the virtual memory range of all possible V8 code * objects. There are two ranges required - the heap code range and the range - * for code embedded in the binary. The V8 API provides all required inputs - * via an UnwindState object through the Isolate::GetUnwindState() API. These - * values will not change after Isolate initialization, so the same - * |unwind_state| can be used for multiple calls. + * for code embedded in the binary. * - * \param unwind_state Input state for the Isolate that the stack comes from. + * Available on x64, ARM64 and ARM32. + * + * \param code_pages A list of all of the ranges in which V8 has allocated + * executable code. The caller should obtain this list by calling + * Isolate::CopyCodePages() during the same interrupt/thread suspension that + * captures the stack. * \param register_state The current registers. This is an in-out param that * will be overwritten with the register values after unwinding, on success. * \param stack_base The resulting stack pointer and frame pointer values are @@ -10633,20 +10783,6 @@ class V8_EXPORT Unwinder { * * \return True on success. */ - // TODO(petermarshall): Remove this API - V8_DEPRECATED("Use entry_stubs + code_pages version.") - static bool TryUnwindV8Frames(const UnwindState& unwind_state, - RegisterState* register_state, - const void* stack_base); - - /** - * The same as above, but is available on x64, ARM64 and ARM32. - * - * \param code_pages A list of all of the ranges in which V8 has allocated - * executable code. The caller should obtain this list by calling - * Isolate::CopyCodePages() during the same interrupt/thread suspension that - * captures the stack. - */ static bool TryUnwindV8Frames(const JSEntryStubs& entry_stubs, size_t code_pages_length, const MemoryRange* code_pages, @@ -10654,20 +10790,13 @@ class V8_EXPORT Unwinder { const void* stack_base); /** - * Whether the PC is within the V8 code range represented by code_range or - * embedded_code_range in |unwind_state|. + * Whether the PC is within the V8 code range represented by code_pages. * * If this returns false, then calling UnwindV8Frames() with the same PC * and unwind_state will always fail. If it returns true, then unwinding may * (but not necessarily) be successful. - */ - // TODO(petermarshall): Remove this API - V8_DEPRECATED("Use code_pages version.") - static bool PCIsInV8(const UnwindState& unwind_state, void* pc); - - /** - * The same as above, but is available on x64, ARM64 and ARM32. See the - * comment on TryUnwindV8Frames. + * + * Available on x64, ARM64 and ARM32 */ static bool PCIsInV8(size_t code_pages_length, const MemoryRange* code_pages, void* pc); @@ -10686,8 +10815,8 @@ Local Local::New(Isolate* isolate, const PersistentBase& that) { } template -Local Local::New(Isolate* isolate, const TracedReferenceBase& that) { - return New(isolate, that.val_); +Local Local::New(Isolate* isolate, const BasicTracedReference& that) { + return New(isolate, *that); } template @@ -10799,8 +10928,15 @@ V8_INLINE void PersistentBase::SetWeak( P* parameter, typename WeakCallbackInfo

::Callback callback, WeakCallbackType type) { typedef typename WeakCallbackInfo::Callback Callback; +#if (__GNUC__ >= 8) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif V8::MakeWeak(reinterpret_cast(this->val_), parameter, reinterpret_cast(callback), type); +#if (__GNUC__ >= 8) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif } template @@ -10867,21 +11003,67 @@ Global& Global::operator=(Global&& rhs) { } template -T* TracedReferenceBase::New(Isolate* isolate, T* that, void* slot, - DestructionMode destruction_mode) { +internal::Address* BasicTracedReference::New( + Isolate* isolate, T* that, void* slot, DestructionMode destruction_mode) { if (that == nullptr) return nullptr; internal::Address* p = reinterpret_cast(that); - return reinterpret_cast(V8::GlobalizeTracedReference( + return V8::GlobalizeTracedReference( reinterpret_cast(isolate), p, reinterpret_cast(slot), - destruction_mode == kWithDestructor)); + destruction_mode == kWithDestructor); } -template -void TracedReferenceBase::Reset() { +void TracedReferenceBase::Reset() { if (IsEmpty()) return; V8::DisposeTracedGlobal(reinterpret_cast(val_)); - val_ = nullptr; + SetSlotThreadSafe(nullptr); +} + +v8::Local TracedReferenceBase::Get(v8::Isolate* isolate) const { + if (IsEmpty()) return Local(); + return Local::New(isolate, reinterpret_cast(val_)); +} + +V8_INLINE bool operator==(const TracedReferenceBase& lhs, + const TracedReferenceBase& rhs) { + v8::internal::Address* a = reinterpret_cast(lhs.val_); + v8::internal::Address* b = reinterpret_cast(rhs.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; +} + +template +V8_INLINE bool operator==(const TracedReferenceBase& lhs, + const v8::Local& rhs) { + v8::internal::Address* a = reinterpret_cast(lhs.val_); + v8::internal::Address* b = reinterpret_cast(*rhs); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; +} + +template +V8_INLINE bool operator==(const v8::Local& lhs, + const TracedReferenceBase& rhs) { + return rhs == lhs; +} + +V8_INLINE bool operator!=(const TracedReferenceBase& lhs, + const TracedReferenceBase& rhs) { + return !(lhs == rhs); +} + +template +V8_INLINE bool operator!=(const TracedReferenceBase& lhs, + const v8::Local& rhs) { + return !(lhs == rhs); +} + +template +V8_INLINE bool operator!=(const v8::Local& lhs, + const TracedReferenceBase& rhs) { + return !(rhs == lhs); } template @@ -10891,7 +11073,7 @@ void TracedGlobal::Reset(Isolate* isolate, const Local& other) { Reset(); if (other.IsEmpty()) return; this->val_ = this->New(isolate, other.val_, &this->val_, - TracedReferenceBase::kWithDestructor); + BasicTracedReference::kWithDestructor); } template @@ -10937,10 +11119,11 @@ template template void TracedReference::Reset(Isolate* isolate, const Local& other) { static_assert(std::is_base_of::value, "type check"); - Reset(); + this->Reset(); if (other.IsEmpty()) return; - this->val_ = this->New(isolate, other.val_, &this->val_, - TracedReferenceBase::kWithoutDestructor); + this->SetSlotThreadSafe( + this->New(isolate, other.val_, &this->val_, + BasicTracedReference::kWithoutDestructor)); } template @@ -10983,8 +11166,7 @@ TracedReference& TracedReference::operator=(const TracedReference& rhs) { return *this; } -template -void TracedReferenceBase::SetWrapperClassId(uint16_t class_id) { +void TracedReferenceBase::SetWrapperClassId(uint16_t class_id) { typedef internal::Internals I; if (IsEmpty()) return; internal::Address* obj = reinterpret_cast(val_); @@ -10992,8 +11174,7 @@ void TracedReferenceBase::SetWrapperClassId(uint16_t class_id) { *reinterpret_cast(addr) = class_id; } -template -uint16_t TracedReferenceBase::WrapperClassId() const { +uint16_t TracedReferenceBase::WrapperClassId() const { typedef internal::Internals I; if (IsEmpty()) return 0; internal::Address* obj = reinterpret_cast(val_); @@ -11024,7 +11205,7 @@ void ReturnValue::Set(const Global& handle) { template template -void ReturnValue::Set(const TracedReferenceBase& handle) { +void ReturnValue::Set(const BasicTracedReference& handle) { static_assert(std::is_base_of::value, "type check"); if (V8_UNLIKELY(handle.IsEmpty())) { *value_ = GetDefaultValue(); @@ -11144,22 +11325,14 @@ template Local FunctionCallbackInfo::operator[](int i) const { // values_ points to the first argument (not the receiver). if (i < 0 || length_ <= i) return Local(*Undefined(GetIsolate())); -#ifdef V8_REVERSE_JSARGS return Local(reinterpret_cast(values_ + i)); -#else - return Local(reinterpret_cast(values_ - i)); -#endif } template Local FunctionCallbackInfo::This() const { // values_ points to the first argument (not the receiver). -#ifdef V8_REVERSE_JSARGS return Local(reinterpret_cast(values_ - 1)); -#else - return Local(reinterpret_cast(values_ + 1)); -#endif } @@ -11354,9 +11527,12 @@ void* Object::GetAlignedPointerFromInternalField(int index) { instance_type == I::kJSApiObjectType || instance_type == I::kJSSpecialApiObjectType)) { int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index); - internal::Isolate* isolate = - internal::IsolateFromNeverReadOnlySpaceObject(obj); - A value = I::ReadExternalPointerField(isolate, obj, offset); +#ifdef V8_HEAP_SANDBOX + offset += I::kEmbedderDataSlotRawPayloadOffset; +#endif + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); + A value = I::ReadExternalPointerField( + isolate, obj, offset, internal::kEmbedderDataSlotPayloadTag); return reinterpret_cast(value); } #endif @@ -11387,10 +11563,10 @@ String::ExternalStringResource* String::GetExternalStringResource() const { ExternalStringResource* result; if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) { - internal::Isolate* isolate = - internal::IsolateFromNeverReadOnlySpaceObject(obj); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); A value = - I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset); + I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset, + internal::kExternalStringResourceTag); result = reinterpret_cast(value); } else { result = GetExternalStringResourceSlow(); @@ -11412,10 +11588,10 @@ String::ExternalStringResourceBase* String::GetExternalStringResourceBase( ExternalStringResourceBase* resource; if (type == I::kExternalOneByteRepresentationTag || type == I::kExternalTwoByteRepresentationTag) { - internal::Isolate* isolate = - internal::IsolateFromNeverReadOnlySpaceObject(obj); + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(obj); A value = - I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset); + I::ReadExternalPointerField(isolate, obj, I::kStringResourceOffset, + internal::kExternalStringResourceTag); resource = reinterpret_cast(value); } else { resource = GetExternalStringResourceBaseSlow(encoding_out); @@ -11501,6 +11677,13 @@ template Value* Value::Cast(T* value) { return static_cast(value); } +template <> +V8_INLINE Value* Value::Cast(Data* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} Boolean* Boolean::Cast(v8::Value* value) { #ifdef V8_ENABLE_CHECKS @@ -11533,6 +11716,12 @@ Private* Private::Cast(Data* data) { return reinterpret_cast(data); } +Module* Module::Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return reinterpret_cast(data); +} Number* Number::Cast(v8::Value* value) { #ifdef V8_ENABLE_CHECKS @@ -11929,37 +12118,6 @@ MaybeLocal Isolate::GetDataFromSnapshotOnce(size_t index) { return Local(data); } -int64_t Isolate::AdjustAmountOfExternalAllocatedMemory( - int64_t change_in_bytes) { - typedef internal::Internals I; - int64_t* external_memory = reinterpret_cast( - reinterpret_cast(this) + I::kExternalMemoryOffset); - int64_t* external_memory_limit = reinterpret_cast( - reinterpret_cast(this) + I::kExternalMemoryLimitOffset); - int64_t* external_memory_low_since_mc = - reinterpret_cast(reinterpret_cast(this) + - I::kExternalMemoryLowSinceMarkCompactOffset); - - // Embedders are weird: we see both over- and underflows here. Perform the - // addition with unsigned types to avoid undefined behavior. - const int64_t amount = - static_cast(static_cast(change_in_bytes) + - static_cast(*external_memory)); - *external_memory = amount; - - if (amount < *external_memory_low_since_mc) { - *external_memory_low_since_mc = amount; - *external_memory_limit = amount + I::kExternalAllocationSoftLimit; - } - - if (change_in_bytes <= 0) return *external_memory; - - if (amount > *external_memory_limit) { - ReportExternalAllocationLimitReached(); - } - return *external_memory; -} - Local Context::GetEmbedderData(int index) { #ifndef V8_ENABLE_CHECKS typedef internal::Address A; @@ -11995,10 +12153,13 @@ void* Context::GetAlignedPointerFromEmbedderData(int index) { I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); int value_offset = I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); - internal::Isolate* isolate = internal::IsolateFromNeverReadOnlySpaceObject( - *reinterpret_cast(this)); +#ifdef V8_HEAP_SANDBOX + value_offset += I::kEmbedderDataSlotRawPayloadOffset; +#endif + internal::Isolate* isolate = I::GetIsolateForHeapSandbox(ctx); return reinterpret_cast( - I::ReadExternalPointerField(isolate, embedder_data, value_offset)); + I::ReadExternalPointerField(isolate, embedder_data, value_offset, + internal::kEmbedderDataSlotPayloadTag)); #else return SlowGetAlignedPointerFromEmbedderData(index); #endif diff --git a/NativeScript/include/v8config.h b/NativeScript/include/v8config.h index 40d23c35..ae89edb2 100644 --- a/NativeScript/include/v8config.h +++ b/NativeScript/include/v8config.h @@ -70,6 +70,7 @@ // V8_OS_POSIX - POSIX compatible (mostly everything except Windows) // V8_OS_QNX - QNX Neutrino // V8_OS_SOLARIS - Sun Solaris and OpenSolaris +// V8_OS_STARBOARD - Starboard (platform abstraction for Cobalt) // V8_OS_AIX - AIX // V8_OS_WIN - Microsoft Windows @@ -93,6 +94,8 @@ #elif defined(__sun) # define V8_OS_POSIX 1 # define V8_OS_SOLARIS 1 +#elif defined(STARBOARD) +# define V8_OS_STARBOARD 1 #elif defined(_AIX) #define V8_OS_POSIX 1 #define V8_OS_AIX 1 @@ -405,6 +408,15 @@ #endif +#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 6) +# define V8_ENUM_DEPRECATED(message) +# define V8_ENUM_DEPRECATE_SOON(message) +#else +# define V8_ENUM_DEPRECATED(message) V8_DEPRECATED(message) +# define V8_ENUM_DEPRECATE_SOON(message) V8_DEPRECATE_SOON(message) +#endif + + // A macro to provide the compiler with branch prediction information. #if V8_HAS_BUILTIN_EXPECT # define V8_UNLIKELY(condition) (__builtin_expect(!!(condition), 0)) @@ -424,6 +436,16 @@ #define V8_WARN_UNUSED_RESULT /* NOT SUPPORTED */ #endif +// Helper macro to define no_sanitize attributes only with clang. +#if defined(__clang__) && defined(__has_attribute) +#if __has_attribute(no_sanitize) +#define V8_CLANG_NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#endif +#if !defined(V8_CLANG_NO_SANITIZE) +#define V8_CLANG_NO_SANITIZE(what) +#endif + #if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED) #error Inconsistent build configuration: To build the V8 shared library \ set BUILDING_V8_SHARED, to include its headers for linking against the \ diff --git a/NativeScript/inspector/base/trace_event/common/trace_event_common.h b/NativeScript/inspector/base/trace_event/common/trace_event_common.h index a7bffbdb..120481f3 100644 --- a/NativeScript/inspector/base/trace_event/common/trace_event_common.h +++ b/NativeScript/inspector/base/trace_event/common/trace_event_common.h @@ -837,6 +837,14 @@ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, category_group, name, id, \ TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END0(category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_COPY) #define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0( \ category_group, name, id, timestamp) \ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ @@ -961,6 +969,7 @@ #define TRACE_TASK_EXECUTION(run_function, task) \ INTERNAL_TRACE_TASK_EXECUTION(run_function, task) +// Special trace event macro to trace log messages. #define TRACE_LOG_MESSAGE(file, message, line) \ INTERNAL_TRACE_LOG_MESSAGE(file, message, line) diff --git a/NativeScript/inspector/src/base/address-region.h b/NativeScript/inspector/src/base/address-region.h index 44151606..010a123d 100644 --- a/NativeScript/inspector/src/base/address-region.h +++ b/NativeScript/inspector/src/base/address-region.h @@ -25,9 +25,9 @@ class AddressRegion { using Address = uintptr_t; - AddressRegion() = default; + constexpr AddressRegion() = default; - AddressRegion(Address address, size_t size) + constexpr AddressRegion(Address address, size_t size) : address_(address), size_(size) {} Address begin() const { return address_; } diff --git a/NativeScript/inspector/src/base/atomicops.h b/NativeScript/inspector/src/base/atomicops.h index 11c41545..5d6422be 100644 --- a/NativeScript/inspector/src/base/atomicops.h +++ b/NativeScript/inspector/src/base/atomicops.h @@ -36,9 +36,25 @@ #include "src/base/base-export.h" #include "src/base/build_config.h" +#if defined(V8_OS_STARBOARD) +#include "starboard/atomic.h" + +#if SB_API_VERSION < 10 +#error Your version of Starboard must support SbAtomic8 in order to use V8. +#endif // SB_API_VERSION < 10 +#endif // V8_OS_STARBOARD + namespace v8 { namespace base { +#ifdef V8_OS_STARBOARD +using Atomic8 = SbAtomic8; +using Atomic16 = int16_t; +using Atomic32 = SbAtomic32; +#if SB_IS_64_BIT +using Atomic64 = SbAtomic64; +#endif +#else using Atomic8 = char; using Atomic16 = int16_t; using Atomic32 = int32_t; @@ -51,10 +67,15 @@ using Atomic64 = int64_t; using Atomic64 = intptr_t; #endif // defined(__ILP32__) #endif // defined(V8_HOST_ARCH_64_BIT) +#endif // V8_OS_STARBOARD // Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or // Atomic64 routines below, depending on your architecture. +#if defined(V8_OS_STARBOARD) +using AtomicWord = SbAtomicPtr; +#else using AtomicWord = intptr_t; +#endif // Atomically execute: // result = *ptr; @@ -65,6 +86,8 @@ using AtomicWord = intptr_t; // I.e. replace |*ptr| with |new_value| if |*ptr| used to be |old_value|. // Always return the value of |*ptr| before the operation. // Acquire, Relaxed, Release correspond to standard C++ memory orders. +Atomic8 Relaxed_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value, + Atomic8 new_value); Atomic16 Relaxed_CompareAndSwap(volatile Atomic16* ptr, Atomic16 old_value, Atomic16 new_value); Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, Atomic32 old_value, @@ -124,7 +147,7 @@ Atomic64 Acquire_Load(volatile const Atomic64* ptr); } // namespace base } // namespace v8 -#if defined(V8_OS_WIN) +#if defined(V8_OS_WIN) || defined(V8_OS_STARBOARD) #include "src/base/atomicops_internals_std.h" #else // TODO(ulan): Switch to std version after performance regression with Wheezy diff --git a/NativeScript/inspector/src/base/atomicops_internals_portable.h b/NativeScript/inspector/src/base/atomicops_internals_portable.h index 1f89f0a6..ac162e2a 100644 --- a/NativeScript/inspector/src/base/atomicops_internals_portable.h +++ b/NativeScript/inspector/src/base/atomicops_internals_portable.h @@ -50,6 +50,14 @@ inline void SeqCst_MemoryFence() { #endif } +inline Atomic8 Relaxed_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value, + Atomic8 new_value) { + bool result = __atomic_compare_exchange_n(ptr, &old_value, new_value, false, + __ATOMIC_RELAXED, __ATOMIC_RELAXED); + USE(result); // Make gcc compiler happy. + return old_value; +} + inline Atomic16 Relaxed_CompareAndSwap(volatile Atomic16* ptr, Atomic16 old_value, Atomic16 new_value) { __atomic_compare_exchange_n(ptr, &old_value, new_value, false, @@ -116,6 +124,10 @@ inline void Relaxed_Store(volatile Atomic32* ptr, Atomic32 value) { __atomic_store_n(ptr, value, __ATOMIC_RELAXED); } +inline void Release_Store(volatile Atomic8* ptr, Atomic8 value) { + __atomic_store_n(ptr, value, __ATOMIC_RELEASE); +} + inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { __atomic_store_n(ptr, value, __ATOMIC_RELEASE); } @@ -132,6 +144,10 @@ inline Atomic32 Relaxed_Load(volatile const Atomic32* ptr) { return __atomic_load_n(ptr, __ATOMIC_RELAXED); } +inline Atomic8 Acquire_Load(volatile const Atomic8* ptr) { + return __atomic_load_n(ptr, __ATOMIC_ACQUIRE); +} + inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { return __atomic_load_n(ptr, __ATOMIC_ACQUIRE); } diff --git a/NativeScript/inspector/src/base/bits.h b/NativeScript/inspector/src/base/bits.h index b74b98e1..cf4b77fa 100644 --- a/NativeScript/inspector/src/base/bits.h +++ b/NativeScript/inspector/src/base/bits.h @@ -32,22 +32,27 @@ constexpr inline return sizeof(T) == 8 ? __builtin_popcountll(static_cast(value)) : __builtin_popcount(static_cast(value)); #else + // Fall back to divide-and-conquer popcount (see "Hacker's Delight" by Henry + // S. Warren, Jr.), chapter 5-1. constexpr uint64_t mask[] = {0x5555555555555555, 0x3333333333333333, 0x0f0f0f0f0f0f0f0f}; - // Start with 1 bit wide buckets of [0,1]. + // Start with 64 buckets of 1 bits, holding values from [0,1]. value = ((value >> 1) & mask[0]) + (value & mask[0]); - // Having 2 bit wide buckets of [0,2] now. + // Having 32 buckets of 2 bits, holding values from [0,2] now. value = ((value >> 2) & mask[1]) + (value & mask[1]); - // Having 4 bit wide buckets of [0,4] now. - value = (value >> 4) + value; - // Having 4 bit wide buckets of [0,8] now. - if (sizeof(T) > 1) - value = ((value >> (sizeof(T) > 1 ? 8 : 0)) & mask[2]) + (value & mask[2]); - // Having 8 bit wide buckets of [0,16] now. + // Having 16 buckets of 4 bits, holding values from [0,4] now. + value = ((value >> 4) & mask[2]) + (value & mask[2]); + // Having 8 buckets of 8 bits, holding values from [0,8] now. + // From this point on, the buckets are bigger than the number of bits + // required to hold the values, and the buckets are bigger the maximum + // result, so there's no need to mask value anymore, since there's no + // more risk of overflow between buckets. + if (sizeof(T) > 1) value = (value >> (sizeof(T) > 1 ? 8 : 0)) + value; + // Having 4 buckets of 16 bits, holding values from [0,16] now. if (sizeof(T) > 2) value = (value >> (sizeof(T) > 2 ? 16 : 0)) + value; - // Having 8 bit wide buckets of [0,32] now. + // Having 2 buckets of 32 bits, holding values from [0,32] now. if (sizeof(T) > 4) value = (value >> (sizeof(T) > 4 ? 32 : 0)) + value; - // Having 8 bit wide buckets of [0,64] now. + // Having 1 buckets of 64 bits, holding values from [0,64] now. return static_cast(value & 0xff); #endif } @@ -140,9 +145,7 @@ constexpr inline bool IsPowerOfTwo(T value) { template ::value>::type> inline constexpr int WhichPowerOfTwo(T value) { -#if V8_HAS_CXX14_CONSTEXPR - DCHECK(IsPowerOfTwo(value)); -#endif + CONSTEXPR_DCHECK(IsPowerOfTwo(value)); #if V8_HAS_BUILTIN_CTZ STATIC_ASSERT(sizeof(T) <= 8); return sizeof(T) == 8 ? __builtin_ctzll(static_cast(value)) diff --git a/NativeScript/inspector/src/base/build_config.h b/NativeScript/inspector/src/base/build_config.h index 8d142c45..2bfbe1ba 100644 --- a/NativeScript/inspector/src/base/build_config.h +++ b/NativeScript/inspector/src/base/build_config.h @@ -199,12 +199,18 @@ #else #define V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK false #endif +constexpr int kReturnAddressStackSlotCount = + V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK ? 1 : 0; // Number of bits to represent the page size for paged spaces. #if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) // PPC has large (64KB) physical pages. const int kPageSizeBits = 19; #else +// Arm64 supports up to 64k OS pages on Linux, however 4k pages are more common +// so we keep the V8 page size at 256k. Nonetheless, we need to make sure we +// don't decrease it further in the future due to reserving 3 OS pages for every +// executable V8 page. const int kPageSizeBits = 18; #endif diff --git a/NativeScript/inspector/src/base/compiler-specific.h b/NativeScript/inspector/src/base/compiler-specific.h index 5d68f7e1..49ce128a 100644 --- a/NativeScript/inspector/src/base/compiler-specific.h +++ b/NativeScript/inspector/src/base/compiler-specific.h @@ -105,4 +105,31 @@ #define V8_NOEXCEPT #endif +// Specify memory alignment for structs, classes, etc. +// Use like: +// class ALIGNAS(16) MyClass { ... } +// ALIGNAS(16) int array[4]; +// +// In most places you can use the C++11 keyword "alignas", which is preferred. +// +// But compilers have trouble mixing __attribute__((...)) syntax with +// alignas(...) syntax. +// +// Doesn't work in clang or gcc: +// struct alignas(16) __attribute__((packed)) S { char c; }; +// Works in clang but not gcc: +// struct __attribute__((packed)) alignas(16) S2 { char c; }; +// Works in clang and gcc: +// struct alignas(16) S3 { char c; } __attribute__((packed)); +// +// There are also some attributes that must be specified *before* a class +// definition: visibility (used for exporting functions/classes) is one of +// these attributes. This means that it is not possible to use alignas() with a +// class that is marked as exported. +#if defined(V8_CC_MSVC) +#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) +#else +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#endif + #endif // V8_BASE_COMPILER_SPECIFIC_H_ diff --git a/NativeScript/inspector/src/base/flags.h b/NativeScript/inspector/src/base/flags.h index c2b79522..bbc20456 100644 --- a/NativeScript/inspector/src/base/flags.h +++ b/NativeScript/inspector/src/base/flags.h @@ -89,50 +89,40 @@ class Flags final { mask_type mask_; }; -#define DEFINE_OPERATORS_FOR_FLAGS(Type) \ - inline Type operator&( \ - Type::flag_type lhs, \ - Type::flag_type rhs)ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT; \ - inline Type operator&(Type::flag_type lhs, Type::flag_type rhs) { \ - return Type(lhs) & rhs; \ - } \ - inline Type operator&( \ - Type::flag_type lhs, \ - const Type& rhs)ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT; \ - inline Type operator&(Type::flag_type lhs, const Type& rhs) { \ - return rhs & lhs; \ - } \ - inline void operator&(Type::flag_type lhs, \ - Type::mask_type rhs)ALLOW_UNUSED_TYPE; \ - inline void operator&(Type::flag_type lhs, Type::mask_type rhs) {} \ - inline Type operator|(Type::flag_type lhs, Type::flag_type rhs) \ - ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT; \ - inline Type operator|(Type::flag_type lhs, Type::flag_type rhs) { \ - return Type(lhs) | rhs; \ - } \ - inline Type operator|(Type::flag_type lhs, const Type& rhs) \ - ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT; \ - inline Type operator|(Type::flag_type lhs, const Type& rhs) { \ - return rhs | lhs; \ - } \ - inline void operator|(Type::flag_type lhs, Type::mask_type rhs) \ - ALLOW_UNUSED_TYPE; \ - inline void operator|(Type::flag_type lhs, Type::mask_type rhs) {} \ - inline Type operator^(Type::flag_type lhs, Type::flag_type rhs) \ - ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT; \ - inline Type operator^(Type::flag_type lhs, Type::flag_type rhs) { \ - return Type(lhs) ^ rhs; \ - } \ - inline Type operator^(Type::flag_type lhs, const Type& rhs) \ - ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT; \ - inline Type operator^(Type::flag_type lhs, const Type& rhs) { \ - return rhs ^ lhs; \ - } \ - inline void operator^(Type::flag_type lhs, Type::mask_type rhs) \ - ALLOW_UNUSED_TYPE; \ - inline void operator^(Type::flag_type lhs, Type::mask_type rhs) {} \ - inline Type operator~(Type::flag_type val)ALLOW_UNUSED_TYPE; \ - inline Type operator~(Type::flag_type val) { return ~Type(val); } +#define DEFINE_OPERATORS_FOR_FLAGS(Type) \ + ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT inline constexpr Type operator&( \ + Type::flag_type lhs, Type::flag_type rhs) { \ + return Type(lhs) & rhs; \ + } \ + ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT inline constexpr Type operator&( \ + Type::flag_type lhs, const Type& rhs) { \ + return rhs & lhs; \ + } \ + ALLOW_UNUSED_TYPE inline void operator&(Type::flag_type lhs, \ + Type::mask_type rhs) {} \ + ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT inline constexpr Type operator|( \ + Type::flag_type lhs, Type::flag_type rhs) { \ + return Type(lhs) | rhs; \ + } \ + ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT inline constexpr Type operator|( \ + Type::flag_type lhs, const Type& rhs) { \ + return rhs | lhs; \ + } \ + ALLOW_UNUSED_TYPE inline void operator|(Type::flag_type lhs, \ + Type::mask_type rhs) {} \ + ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT inline constexpr Type operator^( \ + Type::flag_type lhs, Type::flag_type rhs) { \ + return Type(lhs) ^ rhs; \ + } \ + ALLOW_UNUSED_TYPE V8_WARN_UNUSED_RESULT inline constexpr Type operator^( \ + Type::flag_type lhs, const Type& rhs) { \ + return rhs ^ lhs; \ + } \ + ALLOW_UNUSED_TYPE inline void operator^(Type::flag_type lhs, \ + Type::mask_type rhs) {} \ + ALLOW_UNUSED_TYPE inline constexpr Type operator~(Type::flag_type val) { \ + return ~Type(val); \ + } } // namespace base } // namespace v8 diff --git a/NativeScript/inspector/src/base/lazy-instance.h b/NativeScript/inspector/src/base/lazy-instance.h index 3ea5fc95..75e5b060 100644 --- a/NativeScript/inspector/src/base/lazy-instance.h +++ b/NativeScript/inspector/src/base/lazy-instance.h @@ -235,12 +235,13 @@ class LeakyObject { new (&storage_) T(std::forward(args)...); } + LeakyObject(const LeakyObject&) = delete; + LeakyObject& operator=(const LeakyObject&) = delete; + T* get() { return reinterpret_cast(&storage_); } private: typename std::aligned_storage::type storage_; - - DISALLOW_COPY_AND_ASSIGN(LeakyObject); }; // Define a function which returns a pointer to a lazily initialized and never diff --git a/NativeScript/inspector/src/base/logging.h b/NativeScript/inspector/src/base/logging.h index 790018c9..fe39f988 100644 --- a/NativeScript/inspector/src/base/logging.h +++ b/NativeScript/inspector/src/base/logging.h @@ -134,6 +134,12 @@ V8_BASE_EXPORT void SetDcheckFunction(void (*dcheck_Function)(const char*, int, #endif +#if V8_HAS_CXX14_CONSTEXPR +#define CONSTEXPR_DCHECK(cond) DCHECK(cond) +#else +#define CONSTEXPR_DCHECK(cond) +#endif + // Define PrintCheckOperand for each T which defines operator<< for ostream. template typename std::enable_if< diff --git a/NativeScript/inspector/src/base/macros.h b/NativeScript/inspector/src/base/macros.h index e22dd008..15e2924b 100644 --- a/NativeScript/inspector/src/base/macros.h +++ b/NativeScript/inspector/src/base/macros.h @@ -10,6 +10,7 @@ #include "src/base/compiler-specific.h" #include "src/base/logging.h" +#include "src/base/platform/wrappers.h" // No-op macro which is used to work around MSVC's funky VA_ARGS support. #define EXPAND(x) x @@ -104,16 +105,20 @@ V8_INLINE Dest bit_cast(Source const& source) { static_assert(sizeof(Dest) == sizeof(Source), "source and dest must be same size"); Dest dest; - memcpy(&dest, &source, sizeof(dest)); + v8::base::Memcpy(&dest, &source, sizeof(dest)); return dest; } // Explicitly declare the assignment operator as deleted. +// Note: This macro is deprecated and will be removed soon. Please explicitly +// delete the assignment operator instead. #define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete // Explicitly declare the copy constructor and assignment operator as deleted. // This also deletes the implicit move constructor and implicit move assignment // operator, but still allows to manually define them. +// Note: This macro is deprecated and will be removed soon. Please explicitly +// delete the copy constructor and assignment operator instead. #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&) = delete; \ DISALLOW_ASSIGN(TypeName) @@ -171,22 +176,19 @@ V8_INLINE Dest bit_cast(Source const& source) { #endif #endif -// Helper macro to define no_sanitize attributes only with clang. -#if defined(__clang__) && defined(__has_attribute) -#if __has_attribute(no_sanitize) -#define CLANG_NO_SANITIZE(what) __attribute__((no_sanitize(what))) -#endif -#endif -#if !defined(CLANG_NO_SANITIZE) -#define CLANG_NO_SANITIZE(what) -#endif - // DISABLE_CFI_PERF -- Disable Control Flow Integrity checks for Perf reasons. -#define DISABLE_CFI_PERF CLANG_NO_SANITIZE("cfi") +#define DISABLE_CFI_PERF V8_CLANG_NO_SANITIZE("cfi") // DISABLE_CFI_ICALL -- Disable Control Flow Integrity indirect call checks, // useful because calls into JITed code can not be CFI verified. -#define DISABLE_CFI_ICALL CLANG_NO_SANITIZE("cfi-icall") +#ifdef V8_OS_WIN +// On Windows, also needs __declspec(guard(nocf)) for CFG. +#define DISABLE_CFI_ICALL \ + V8_CLANG_NO_SANITIZE("cfi-icall") \ + __declspec(guard(nocf)) +#else +#define DISABLE_CFI_ICALL V8_CLANG_NO_SANITIZE("cfi-icall") +#endif #if V8_CC_GNU #define V8_IMMEDIATE_CRASH() __builtin_trap() @@ -328,10 +330,10 @@ V8_INLINE A implicit_cast(A x) { #define V8PRIuPTR "lxu" #endif -// The following macro works on both 32 and 64-bit platforms. -// Usage: instead of writing 0x1234567890123456 -// write V8_2PART_UINT64_C(0x12345678,90123456); -#define V8_2PART_UINT64_C(a, b) (((static_cast(a) << 32) + 0x##b##u)) +// Make a uint64 from two uint32_t halves. +inline uint64_t make_uint64(uint32_t high, uint32_t low) { + return (uint64_t{high} << 32) + low; +} // Return the largest multiple of m which is <= x. template @@ -339,14 +341,14 @@ inline T RoundDown(T x, intptr_t m) { STATIC_ASSERT(std::is_integral::value); // m must be a power of two. DCHECK(m != 0 && ((m & (m - 1)) == 0)); - return x & -m; + return x & static_cast(-m); } template constexpr inline T RoundDown(T x) { STATIC_ASSERT(std::is_integral::value); // m must be a power of two. STATIC_ASSERT(m != 0 && ((m & (m - 1)) == 0)); - return x & -m; + return x & static_cast(-m); } // Return the smallest multiple of m which is >= x. diff --git a/NativeScript/inspector/src/base/memory.h b/NativeScript/inspector/src/base/memory.h index 087f6729..8da39911 100644 --- a/NativeScript/inspector/src/base/memory.h +++ b/NativeScript/inspector/src/base/memory.h @@ -6,6 +6,7 @@ #define V8_BASE_MEMORY_H_ #include "src/base/macros.h" +#include "src/base/platform/wrappers.h" namespace v8 { namespace base { @@ -31,14 +32,14 @@ template static inline V ReadUnalignedValue(Address p) { ASSERT_TRIVIALLY_COPYABLE(V); V r; - memcpy(&r, reinterpret_cast(p), sizeof(V)); + base::Memcpy(&r, reinterpret_cast(p), sizeof(V)); return r; } template static inline void WriteUnalignedValue(Address p, V value) { ASSERT_TRIVIALLY_COPYABLE(V); - memcpy(reinterpret_cast(p), &value, sizeof(V)); + base::Memcpy(reinterpret_cast(p), &value, sizeof(V)); } template @@ -76,6 +77,9 @@ static inline V ReadLittleEndianValue(V* p) { template static inline void WriteLittleEndianValue(V* p, V value) { + static_assert( + !std::is_array::value, + "Passing an array decays to pointer, causing unexpected results."); WriteLittleEndianValue(reinterpret_cast
(p), value); } diff --git a/NativeScript/inspector/src/base/optional.h b/NativeScript/inspector/src/base/optional.h index 6610c7ff..3c13e654 100644 --- a/NativeScript/inspector/src/base/optional.h +++ b/NativeScript/inspector/src/base/optional.h @@ -557,33 +557,33 @@ class OPTIONAL_DECLSPEC_EMPTY_BASES Optional return *this; } - const T* operator->() const { - DCHECK(storage_.is_populated_); + constexpr const T* operator->() const { + CONSTEXPR_DCHECK(storage_.is_populated_); return &storage_.value_; } - T* operator->() { - DCHECK(storage_.is_populated_); + constexpr T* operator->() { + CONSTEXPR_DCHECK(storage_.is_populated_); return &storage_.value_; } - const T& operator*() const & { - DCHECK(storage_.is_populated_); + constexpr const T& operator*() const& { + CONSTEXPR_DCHECK(storage_.is_populated_); return storage_.value_; } - T& operator*() & { - DCHECK(storage_.is_populated_); + constexpr T& operator*() & { + CONSTEXPR_DCHECK(storage_.is_populated_); return storage_.value_; } - const T&& operator*() const && { - DCHECK(storage_.is_populated_); + constexpr const T&& operator*() const&& { + CONSTEXPR_DCHECK(storage_.is_populated_); return std::move(storage_.value_); } - T&& operator*() && { - DCHECK(storage_.is_populated_); + constexpr T&& operator*() && { + CONSTEXPR_DCHECK(storage_.is_populated_); return std::move(storage_.value_); } diff --git a/NativeScript/inspector/src/base/platform/mutex.h b/NativeScript/inspector/src/base/platform/mutex.h index c3144f7c..2db68b8f 100644 --- a/NativeScript/inspector/src/base/platform/mutex.h +++ b/NativeScript/inspector/src/base/platform/mutex.h @@ -7,6 +7,7 @@ #include "src/base/base-export.h" #include "src/base/lazy-instance.h" +#include "src/base/optional.h" #if V8_OS_WIN #include "src/base/win32-headers.h" #endif @@ -16,6 +17,12 @@ #include // NOLINT #endif +#if V8_OS_STARBOARD +#include "starboard/common/mutex.h" +#include "starboard/common/recursive_mutex.h" +#include "starboard/common/rwlock.h" +#endif + namespace v8 { namespace base { @@ -37,6 +44,8 @@ namespace base { class V8_BASE_EXPORT Mutex final { public: Mutex(); + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; ~Mutex(); // Locks the given mutex. If the mutex is currently unlocked, it becomes @@ -58,6 +67,8 @@ class V8_BASE_EXPORT Mutex final { using NativeHandle = pthread_mutex_t; #elif V8_OS_WIN using NativeHandle = SRWLOCK; +#elif V8_OS_STARBOARD + using NativeHandle = SbMutex; #endif NativeHandle& native_handle() { @@ -91,8 +102,6 @@ class V8_BASE_EXPORT Mutex final { } friend class ConditionVariable; - - DISALLOW_COPY_AND_ASSIGN(Mutex); }; // POD Mutex initialized lazily (i.e. the first time Pointer() is called). @@ -132,6 +141,8 @@ using LazyMutex = LazyStaticInstance, class V8_BASE_EXPORT RecursiveMutex final { public: RecursiveMutex(); + RecursiveMutex(const RecursiveMutex&) = delete; + RecursiveMutex& operator=(const RecursiveMutex&) = delete; ~RecursiveMutex(); // Locks the mutex. If another thread has already locked the mutex, a call to @@ -159,14 +170,14 @@ class V8_BASE_EXPORT RecursiveMutex final { using NativeHandle = pthread_mutex_t; #elif V8_OS_WIN using NativeHandle = CRITICAL_SECTION; +#elif V8_OS_STARBOARD + using NativeHandle = starboard::RecursiveMutex; #endif NativeHandle native_handle_; #ifdef DEBUG int level_; #endif - - DISALLOW_COPY_AND_ASSIGN(RecursiveMutex); }; @@ -203,6 +214,8 @@ using LazyRecursiveMutex = class V8_BASE_EXPORT SharedMutex final { public: SharedMutex(); + SharedMutex(const SharedMutex&) = delete; + SharedMutex& operator=(const SharedMutex&) = delete; ~SharedMutex(); // Acquires shared ownership of the {SharedMutex}. If another thread is @@ -247,11 +260,11 @@ class V8_BASE_EXPORT SharedMutex final { using NativeHandle = pthread_rwlock_t; #elif V8_OS_WIN using NativeHandle = SRWLOCK; +#elif V8_OS_STARBOARD + using NativeHandle = starboard::RWLock; #endif NativeHandle native_handle_; - - DISALLOW_COPY_AND_ASSIGN(SharedMutex); }; // ----------------------------------------------------------------------------- @@ -274,6 +287,8 @@ class LockGuard final { explicit LockGuard(Mutex* mutex) : mutex_(mutex) { if (has_mutex()) mutex_->Lock(); } + LockGuard(const LockGuard&) = delete; + LockGuard& operator=(const LockGuard&) = delete; ~LockGuard() { if (has_mutex()) mutex_->Unlock(); } @@ -286,8 +301,6 @@ class LockGuard final { mutex_ != nullptr); return Behavior == NullBehavior::kRequireNotNull || mutex_ != nullptr; } - - DISALLOW_COPY_AND_ASSIGN(LockGuard); }; using MutexGuard = LockGuard; @@ -307,6 +320,8 @@ class SharedMutexGuard final { mutex_->LockExclusive(); } } + SharedMutexGuard(const SharedMutexGuard&) = delete; + SharedMutexGuard& operator=(const SharedMutexGuard&) = delete; ~SharedMutexGuard() { if (!has_mutex()) return; if (kIsShared) { @@ -324,8 +339,20 @@ class SharedMutexGuard final { mutex_ != nullptr); return Behavior == NullBehavior::kRequireNotNull || mutex_ != nullptr; } +}; - DISALLOW_COPY_AND_ASSIGN(SharedMutexGuard); +template +class SharedMutexGuardIf final { + public: + SharedMutexGuardIf(SharedMutex* mutex, bool enable_mutex) { + if (enable_mutex) mutex_.emplace(mutex); + } + SharedMutexGuardIf(const SharedMutexGuardIf&) = delete; + SharedMutexGuardIf& operator=(const SharedMutexGuardIf&) = delete; + + private: + base::Optional> mutex_; }; } // namespace base diff --git a/NativeScript/inspector/src/base/platform/platform.h b/NativeScript/inspector/src/base/platform/platform.h index af55036a..7beefbe5 100644 --- a/NativeScript/inspector/src/base/platform/platform.h +++ b/NativeScript/inspector/src/base/platform/platform.h @@ -22,6 +22,7 @@ #define V8_BASE_PLATFORM_PLATFORM_H_ #include +#include #include #include @@ -74,6 +75,9 @@ inline intptr_t InternalGetExistingThreadLocal(intptr_t index) { #elif defined(__APPLE__) && (V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64) +// tvOS simulator does not use intptr_t as TLS key. +#if !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR) + #define V8_FAST_TLS_SUPPORTED 1 extern V8_BASE_EXPORT intptr_t kMacTlsBaseOffset; @@ -94,6 +98,8 @@ inline intptr_t InternalGetExistingThreadLocal(intptr_t index) { return result; } +#endif // !defined(V8_OS_STARBOARD) || !defined(TARGET_OS_SIMULATOR) + #endif #endif // V8_NO_FAST_TLS @@ -167,7 +173,10 @@ class V8_BASE_EXPORT OS { kReadWrite, // TODO(hpayer): Remove this flag. Memory should never be rwx. kReadWriteExecute, - kReadExecute + kReadExecute, + // TODO(jkummerow): Remove this when Wasm has a platform-independent + // w^x implementation. + kNoAccessWillJitLater }; static bool HasLazyCommits(); @@ -254,7 +263,7 @@ class V8_BASE_EXPORT OS { static void AdjustSchedulingParams(); - static void ExitProcess(int exit_code); + [[noreturn]] static void ExitProcess(int exit_code); private: // These classes use the private memory management API below. @@ -274,6 +283,13 @@ class V8_BASE_EXPORT OS { size_t alignment, MemoryPermission access); + V8_WARN_UNUSED_RESULT static void* AllocateShared(size_t size, + MemoryPermission access); + + V8_WARN_UNUSED_RESULT static void* RemapShared(void* old_address, + void* new_address, + size_t size); + V8_WARN_UNUSED_RESULT static bool Free(void* address, const size_t size); V8_WARN_UNUSED_RESULT static bool Release(void* address, size_t size); @@ -316,7 +332,11 @@ inline void EnsureConsoleOutput() { class V8_BASE_EXPORT Thread { public: // Opaque data type for thread-local storage keys. +#if V8_OS_STARBOARD + using LocalStorageKey = SbThreadLocalKey; +#else using LocalStorageKey = int32_t; +#endif class Options { public: @@ -334,6 +354,8 @@ class V8_BASE_EXPORT Thread { // Create new thread. explicit Thread(const Options& options); + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; virtual ~Thread(); // Start new thread by calling the Run() method on the new thread. @@ -363,13 +385,7 @@ class V8_BASE_EXPORT Thread { static LocalStorageKey CreateThreadLocalKey(); static void DeleteThreadLocalKey(LocalStorageKey key); static void* GetThreadLocal(LocalStorageKey key); - static int GetThreadLocalInt(LocalStorageKey key) { - return static_cast(reinterpret_cast(GetThreadLocal(key))); - } static void SetThreadLocal(LocalStorageKey key, void* value); - static void SetThreadLocalInt(LocalStorageKey key, int value) { - SetThreadLocal(key, reinterpret_cast(static_cast(value))); - } static bool HasThreadLocal(LocalStorageKey key) { return GetThreadLocal(key) != nullptr; } @@ -407,37 +423,48 @@ class V8_BASE_EXPORT Thread { char name_[kMaxThreadNameLength]; int stack_size_; Semaphore* start_semaphore_; - - DISALLOW_COPY_AND_ASSIGN(Thread); }; // TODO(v8:10354): Make use of the stack utilities here in V8. class V8_BASE_EXPORT Stack { public: + // Convenience wrapper to use stack slots as unsigned values or void* + // pointers. + struct StackSlot { + // NOLINTNEXTLINE + StackSlot(void* value) : value(reinterpret_cast(value)) {} + StackSlot(uintptr_t value) : value(value) {} // NOLINT + + // NOLINTNEXTLINE + operator void*() const { return reinterpret_cast(value); } + operator uintptr_t() const { return value; } // NOLINT + + uintptr_t value; + }; + // Gets the start of the stack of the current thread. - static void* GetStackStart(); + static StackSlot GetStackStart(); // Returns the current stack top. Works correctly with ASAN and SafeStack. // GetCurrentStackPosition() should not be inlined, because it works on stack // frames if it were inlined into a function with a huge stack frame it would // return an address significantly above the actual current stack position. - static V8_NOINLINE void* GetCurrentStackPosition(); + static V8_NOINLINE StackSlot GetCurrentStackPosition(); - // Translates an ASAN-based slot to a real stack slot if necessary. - static void* GetStackSlot(void* slot) { + // Returns the real stack frame if slot is part of a fake frame, and slot + // otherwise. + static StackSlot GetRealStackAddressForSlot(StackSlot slot) { #ifdef V8_USE_ADDRESS_SANITIZER - void* fake_stack = __asan_get_current_fake_stack(); - if (fake_stack) { - void* fake_frame_start; - void* real_frame = __asan_addr_is_in_fake_stack( - fake_stack, slot, &fake_frame_start, nullptr); - if (real_frame) { - return reinterpret_cast( - reinterpret_cast(real_frame) + - (reinterpret_cast(slot) - - reinterpret_cast(fake_frame_start))); - } - } + // ASAN fetches the real stack deeper in the __asan_addr_is_in_fake_stack() + // call (precisely, deeper in __asan_stack_malloc_()), which results in a + // real frame that could be outside of stack bounds. Adjust for this + // impreciseness here. + constexpr size_t kAsanRealFrameOffsetBytes = 32; + void* real_frame = __asan_addr_is_in_fake_stack( + __asan_get_current_fake_stack(), slot, nullptr, nullptr); + return real_frame + ? (static_cast(real_frame) + kAsanRealFrameOffsetBytes) + : slot; #endif // V8_USE_ADDRESS_SANITIZER return slot; } diff --git a/NativeScript/inspector/src/base/platform/semaphore.h b/NativeScript/inspector/src/base/platform/semaphore.h index c4937aca..83a7a339 100644 --- a/NativeScript/inspector/src/base/platform/semaphore.h +++ b/NativeScript/inspector/src/base/platform/semaphore.h @@ -17,6 +17,10 @@ #include // NOLINT #endif +#if V8_OS_STARBOARD +#include "starboard/common/semaphore.h" +#endif + namespace v8 { namespace base { @@ -35,6 +39,8 @@ class TimeDelta; class V8_BASE_EXPORT Semaphore final { public: explicit Semaphore(int count); + Semaphore(const Semaphore&) = delete; + Semaphore& operator=(const Semaphore&) = delete; ~Semaphore(); // Increments the semaphore counter. @@ -55,6 +61,8 @@ class V8_BASE_EXPORT Semaphore final { using NativeHandle = sem_t; #elif V8_OS_WIN using NativeHandle = HANDLE; +#elif V8_OS_STARBOARD + using NativeHandle = starboard::Semaphore; #endif NativeHandle& native_handle() { @@ -66,8 +74,6 @@ class V8_BASE_EXPORT Semaphore final { private: NativeHandle native_handle_; - - DISALLOW_COPY_AND_ASSIGN(Semaphore); }; diff --git a/NativeScript/inspector/src/base/platform/v8-time.h b/NativeScript/inspector/src/base/platform/v8-time.h index 5f69129e..2fc7859d 100644 --- a/NativeScript/inspector/src/base/platform/v8-time.h +++ b/NativeScript/inspector/src/base/platform/v8-time.h @@ -14,6 +14,7 @@ #include "src/base/base-export.h" #include "src/base/bits.h" #include "src/base/macros.h" +#include "src/base/safe_conversions.h" #if V8_OS_WIN #include "src/base/win32-headers.h" #endif @@ -36,7 +37,7 @@ class TimeTicks; namespace time_internal { template class TimeBase; -} +} // namespace time_internal class TimeConstants { public: @@ -90,6 +91,14 @@ class V8_BASE_EXPORT TimeDelta final { return TimeDelta(nanoseconds / TimeConstants::kNanosecondsPerMicrosecond); } + static TimeDelta FromSecondsD(double seconds) { + return FromDouble(seconds * TimeConstants::kMicrosecondsPerSecond); + } + static TimeDelta FromMillisecondsD(double milliseconds) { + return FromDouble(milliseconds * + TimeConstants::kMicrosecondsPerMillisecond); + } + // Returns the maximum time delta, which should be greater than any reasonable // time delta we might compare it to. Adding or subtracting the maximum time // delta to a time or another time delta has an undefined result. @@ -201,6 +210,9 @@ class V8_BASE_EXPORT TimeDelta final { } private: + // TODO(v8:10620): constexpr requires constexpr saturated_cast. + static inline TimeDelta FromDouble(double value); + template friend class time_internal::TimeBase; // Constructs a delta given the duration in microseconds. This is private // to avoid confusion by callers with an integer constructor. Use @@ -211,6 +223,11 @@ class V8_BASE_EXPORT TimeDelta final { int64_t delta_; }; +// static +TimeDelta TimeDelta::FromDouble(double value) { + return TimeDelta(saturated_cast(value)); +} + // static constexpr TimeDelta TimeDelta::Max() { return TimeDelta(std::numeric_limits::max()); diff --git a/NativeScript/inspector/src/base/platform/wrappers.h b/NativeScript/inspector/src/base/platform/wrappers.h new file mode 100644 index 00000000..5d07cec2 --- /dev/null +++ b/NativeScript/inspector/src/base/platform/wrappers.h @@ -0,0 +1,73 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_BASE_PLATFORM_WRAPPERS_H_ +#define V8_BASE_PLATFORM_WRAPPERS_H_ + +#include +#include +#include +#include + +#include "src/base/base-export.h" + +#if defined(V8_OS_STARBOARD) +#include "starboard/memory.h" +#endif + +namespace v8 { +namespace base { + +#if !defined(V8_OS_STARBOARD) + +// Common libstd implementations. +// inline implementations are preferred here due to performance concerns. +inline void* Malloc(size_t size) { return malloc(size); } + +inline void* Realloc(void* memory, size_t size) { + return realloc(memory, size); +} + +inline void Free(void* memory) { return free(memory); } + +inline void* Calloc(size_t count, size_t size) { return calloc(count, size); } + +inline void* Memcpy(void* dest, const void* source, size_t count) { + return memcpy(dest, source, count); +} + +inline FILE* Fopen(const char* filename, const char* mode) { + return fopen(filename, mode); +} + +inline int Fclose(FILE* stream) { return fclose(stream); } + +#else // V8_OS_STARBOARD + +inline void* Malloc(size_t size) { return SbMemoryAlloc(size); } + +inline void* Realloc(void* memory, size_t size) { + return SbMemoryReallocate(memory, size); +} + +inline void Free(void* memory) { return SbMemoryDeallocate(memory); } + +inline void* Calloc(size_t count, size_t size) { + return SbMemoryCalloc(count, size); +} + +inline void* Memcpy(void* dest, const void* source, size_t count) { + return SbMemoryCopy(dest, source, count); +} + +inline FILE* Fopen(const char* filename, const char* mode) { return NULL; } + +inline int Fclose(FILE* stream) { return -1; } + +#endif // V8_OS_STARBOARD + +} // namespace base +} // namespace v8 + +#endif // V8_BASE_PLATFORM_WRAPPERS_H_ diff --git a/NativeScript/inspector/src/base/safe_conversions.h b/NativeScript/inspector/src/base/safe_conversions.h index f63f1ad9..38aa7b9a 100644 --- a/NativeScript/inspector/src/base/safe_conversions.h +++ b/NativeScript/inspector/src/base/safe_conversions.h @@ -4,59 +4,383 @@ // Slightly adapted for inclusion in V8. // Copyright 2014 the V8 project authors. All rights reserved. +// List of adaptations: +// - include guard names +// - wrap in v8 namespace +// - formatting (git cl format) +// - include paths #ifndef V8_BASE_SAFE_CONVERSIONS_H_ #define V8_BASE_SAFE_CONVERSIONS_H_ +#include + +#include #include +#include #include "src/base/safe_conversions_impl.h" +#if defined(__ARMEL__) && !defined(__native_client__) +#include "src/base/safe_conversions_arm_impl.h" +#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1) +#else +#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0) +#endif + +#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS +#include +#endif + namespace v8 { namespace base { +namespace internal { + +#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS +template +struct SaturateFastAsmOp { + static constexpr bool is_supported = false; + static constexpr Dst Do(Src) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure(); + } +}; +#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS +#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS + +// The following special case a few specific integer conversions where we can +// eke out better performance than range checking. +template +struct IsValueInRangeFastOp { + static constexpr bool is_supported = false; + static constexpr bool Do(Src value) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure(); + } +}; + +// Signed to signed range comparison. +template +struct IsValueInRangeFastOp< + Dst, Src, + typename std::enable_if< + std::is_integral::value && std::is_integral::value && + std::is_signed::value && std::is_signed::value && + !IsTypeInRangeForNumericType::value>::type> { + static constexpr bool is_supported = true; + + static constexpr bool Do(Src value) { + // Just downcast to the smaller type, sign extend it back to the original + // type, and then see if it matches the original value. + return value == static_cast(value); + } +}; + +// Signed to unsigned range comparison. +template +struct IsValueInRangeFastOp< + Dst, Src, + typename std::enable_if< + std::is_integral::value && std::is_integral::value && + !std::is_signed::value && std::is_signed::value && + !IsTypeInRangeForNumericType::value>::type> { + static constexpr bool is_supported = true; + + static constexpr bool Do(Src value) { + // We cast a signed as unsigned to overflow negative values to the top, + // then compare against whichever maximum is smaller, as our upper bound. + return as_unsigned(value) <= as_unsigned(CommonMax()); + } +}; // Convenience function that returns true if the supplied value is in range // for the destination type. template -inline bool IsValueInRangeForNumericType(Src value) { - return internal::DstRangeRelationToSrcRange(value) == - internal::RANGE_VALID; +constexpr bool IsValueInRangeForNumericType(Src value) { + using SrcType = typename internal::UnderlyingType::type; + return internal::IsValueInRangeFastOp::is_supported + ? internal::IsValueInRangeFastOp::Do( + static_cast(value)) + : internal::DstRangeRelationToSrcRange( + static_cast(value)) + .IsValid(); } // checked_cast<> is analogous to static_cast<> for numeric types, // except that it CHECKs that the specified numeric conversion will not // overflow or underflow. NaN source will always trigger a CHECK. -template -inline Dst checked_cast(Src value) { - CHECK(IsValueInRangeForNumericType(value)); - return static_cast(value); +template +constexpr Dst checked_cast(Src value) { + // This throws a compile-time error on evaluating the constexpr if it can be + // determined at compile-time as failing, otherwise it will CHECK at runtime. + using SrcType = typename internal::UnderlyingType::type; + return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType(value))) + ? static_cast(static_cast(value)) + : CheckHandler::template HandleFailure(); } +// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. +// You may provide your own limits (e.g. to saturated_cast) so long as you +// implement all of the static constexpr member functions in the class below. +template +struct SaturationDefaultLimits : public std::numeric_limits { + static constexpr T NaN() { + return std::numeric_limits::has_quiet_NaN + ? std::numeric_limits::quiet_NaN() + : T(); + } + using std::numeric_limits::max; + static constexpr T Overflow() { + return std::numeric_limits::has_infinity + ? std::numeric_limits::infinity() + : std::numeric_limits::max(); + } + using std::numeric_limits::lowest; + static constexpr T Underflow() { + return std::numeric_limits::has_infinity + ? std::numeric_limits::infinity() * -1 + : std::numeric_limits::lowest(); + } +}; + +template class S, typename Src> +constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { + // For some reason clang generates much better code when the branch is + // structured exactly this way, rather than a sequence of checks. + return !constraint.IsOverflowFlagSet() + ? (!constraint.IsUnderflowFlagSet() ? static_cast(value) + : S::Underflow()) + // Skip this check for integral Src, which cannot be NaN. + : (std::is_integral::value || !constraint.IsUnderflowFlagSet() + ? S::Overflow() + : S::NaN()); +} + +// We can reduce the number of conditions and get slightly better performance +// for normal signed and unsigned integer ranges. And in the specific case of +// Arm, we can use the optimized saturation instructions. +template +struct SaturateFastOp { + static constexpr bool is_supported = false; + static constexpr Dst Do(Src value) { + // Force a compile failure if instantiated. + return CheckOnFailure::template HandleFailure(); + } +}; + +template +struct SaturateFastOp< + Dst, Src, + typename std::enable_if::value && + std::is_integral::value && + SaturateFastAsmOp::is_supported>::type> { + static constexpr bool is_supported = true; + static constexpr Dst Do(Src value) { + return SaturateFastAsmOp::Do(value); + } +}; + +template +struct SaturateFastOp< + Dst, Src, + typename std::enable_if::value && + std::is_integral::value && + !SaturateFastAsmOp::is_supported>::type> { + static constexpr bool is_supported = true; + static constexpr Dst Do(Src value) { + // The exact order of the following is structured to hit the correct + // optimization heuristics across compilers. Do not change without + // checking the emitted code. + const Dst saturated = CommonMaxOrMin( + IsMaxInRangeForNumericType() || + (!IsMinInRangeForNumericType() && IsValueNegative(value))); + return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType(value)) + ? static_cast(value) + : saturated; + } +}; + // saturated_cast<> is analogous to static_cast<> for numeric types, except -// that the specified numeric conversion will saturate rather than overflow or -// underflow. NaN assignment to an integral will trigger a CHECK condition. +// that the specified numeric conversion will saturate by default rather than +// overflow or underflow, and NaN assignment to an integral will return 0. +// All boundary condition behaviors can be overriden with a custom handler. +template class SaturationHandler = SaturationDefaultLimits, + typename Src> +constexpr Dst saturated_cast(Src value) { + using SrcType = typename UnderlyingType::type; + return !IsCompileTimeConstant(value) && + SaturateFastOp::is_supported && + std::is_same, + SaturationDefaultLimits>::value + ? SaturateFastOp::Do(static_cast(value)) + : saturated_cast_impl( + static_cast(value), + DstRangeRelationToSrcRange( + static_cast(value))); +} + +// strict_cast<> is analogous to static_cast<> for numeric types, except that +// it will cause a compile failure if the destination type is not large enough +// to contain any value in the source type. It performs no runtime checking. template -inline Dst saturated_cast(Src value) { - // Optimization for floating point values, which already saturate. - if (std::numeric_limits::is_iec559) - return static_cast(value); +constexpr Dst strict_cast(Src value) { + using SrcType = typename UnderlyingType::type; + static_assert(UnderlyingType::is_numeric, "Argument must be numeric."); + static_assert(std::is_arithmetic::value, "Result must be numeric."); + + // If you got here from a compiler error, it's because you tried to assign + // from a source type to a destination type that has insufficient range. + // The solution may be to change the destination type you're assigning to, + // and use one large enough to represent the source. + // Alternatively, you may be better served with the checked_cast<> or + // saturated_cast<> template functions for your particular use case. + static_assert(StaticDstRangeRelationToSrcRange::value == + NUMERIC_RANGE_CONTAINED, + "The source type is out of range for the destination type. " + "Please see strict_cast<> comments for more information."); + + return static_cast(static_cast(value)); +} + +// Some wrappers to statically check that a type is in range. +template +struct IsNumericRangeContained { + static constexpr bool value = false; +}; + +template +struct IsNumericRangeContained< + Dst, Src, + typename std::enable_if::value && + ArithmeticOrUnderlyingEnum::value>::type> { + static constexpr bool value = + StaticDstRangeRelationToSrcRange::value == + NUMERIC_RANGE_CONTAINED; +}; + +// StrictNumeric implements compile time range checking between numeric types by +// wrapping assignment operations in a strict_cast. This class is intended to be +// used for function arguments and return types, to ensure the destination type +// can always contain the source type. This is essentially the same as enforcing +// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied +// incrementally at API boundaries, making it easier to convert code so that it +// compiles cleanly with truncation warnings enabled. +// This template should introduce no runtime overhead, but it also provides no +// runtime checking of any of the associated mathematical operations. Use +// CheckedNumeric for runtime range checks of the actual value being assigned. +template +class StrictNumeric { + public: + using type = T; - switch (internal::DstRangeRelationToSrcRange(value)) { - case internal::RANGE_VALID: - return static_cast(value); + constexpr StrictNumeric() : value_(0) {} - case internal::RANGE_UNDERFLOW: - return std::numeric_limits::min(); + // Copy constructor. + template + constexpr StrictNumeric(const StrictNumeric& rhs) + : value_(strict_cast(rhs.value_)) {} - case internal::RANGE_OVERFLOW: - return std::numeric_limits::max(); + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to StrictNumerics to make them easier to use. + template + constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit) + : value_(strict_cast(value)) {} - // Should fail only on attempting to assign NaN to a saturated integer. - case internal::RANGE_INVALID: - UNREACHABLE(); + // If you got here from a compiler error, it's because you tried to assign + // from a source type to a destination type that has insufficient range. + // The solution may be to change the destination type you're assigning to, + // and use one large enough to represent the source. + // If you're assigning from a CheckedNumeric<> class, you may be able to use + // the AssignIfValid() member function, specify a narrower destination type to + // the member value functions (e.g. val.template ValueOrDie()), use one + // of the value helper functions (e.g. ValueOrDieForType(val)). + // If you've encountered an _ambiguous overload_ you can use a static_cast<> + // to explicitly cast the result to the destination type. + // If none of that works, you may be better served with the checked_cast<> or + // saturated_cast<> template functions for your particular use case. + template ::value>::type* = nullptr> + constexpr operator Dst() const { + return static_cast::type>(value_); } - UNREACHABLE(); + private: + const T value_; +}; + +// Convience wrapper returns a StrictNumeric from the provided arithmetic type. +template +constexpr StrictNumeric::type> MakeStrictNum( + const T value) { + return value; +} + +#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS +// Overload the ostream output operator to make logging work nicely. +template +std::ostream& operator<<(std::ostream& os, const StrictNumeric& value) { + os << static_cast(value); + return os; +} +#endif + +#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \ + template ::value>::type* = nullptr> \ + constexpr bool operator OP(const L lhs, const R rhs) { \ + return SafeCompare::type, \ + typename UnderlyingType::type>(lhs, rhs); \ + } + +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==) +BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=) + +} // namespace internal + +using internal::as_signed; +using internal::as_unsigned; +using internal::checked_cast; +using internal::IsTypeInRangeForNumericType; +using internal::IsValueInRangeForNumericType; +using internal::IsValueNegative; +using internal::MakeStrictNum; +using internal::SafeUnsignedAbs; +using internal::saturated_cast; +using internal::strict_cast; +using internal::StrictNumeric; + +// Explicitly make a shorter size_t alias for convenience. +using SizeT = StrictNumeric; + +// floating -> integral conversions that saturate and thus can actually return +// an integral type. In most cases, these should be preferred over the std:: +// versions. +template ::value && + std::is_floating_point::value>> +Dst ClampFloor(Src value) { + return saturated_cast(std::floor(value)); +} +template ::value && + std::is_floating_point::value>> +Dst ClampCeil(Src value) { + return saturated_cast(std::ceil(value)); +} +template ::value && + std::is_floating_point::value>> +Dst ClampRound(Src value) { + const Src rounded = + (value >= 0.0f) ? std::floor(value + 0.5f) : std::ceil(value - 0.5f); + return saturated_cast(rounded); } } // namespace base diff --git a/NativeScript/inspector/src/base/safe_conversions_impl.h b/NativeScript/inspector/src/base/safe_conversions_impl.h index 90c8e193..5d9277df 100644 --- a/NativeScript/inspector/src/base/safe_conversions_impl.h +++ b/NativeScript/inspector/src/base/safe_conversions_impl.h @@ -4,28 +4,130 @@ // Slightly adapted for inclusion in V8. // Copyright 2014 the V8 project authors. All rights reserved. +// List of adaptations: +// - include guard names +// - wrap in v8 namespace +// - formatting (git cl format) #ifndef V8_BASE_SAFE_CONVERSIONS_IMPL_H_ #define V8_BASE_SAFE_CONVERSIONS_IMPL_H_ +#include + #include +#include -#include "src/base/logging.h" -#include "src/base/macros.h" +#if defined(__GNUC__) || defined(__clang__) +#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1) +#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define BASE_NUMERICS_LIKELY(x) (x) +#define BASE_NUMERICS_UNLIKELY(x) (x) +#endif namespace v8 { namespace base { namespace internal { // The std library doesn't provide a binary max_exponent for integers, however -// we can compute one by adding one to the number of non-sign bits. This allows -// for accurate range comparisons between floating point and integer types. +// we can compute an analog using std::numeric_limits<>::digits. template struct MaxExponent { - static const int value = std::numeric_limits::is_iec559 + static const int value = std::is_floating_point::value ? std::numeric_limits::max_exponent - : (sizeof(NumericType) * 8 + 1 - - std::numeric_limits::is_signed); + : std::numeric_limits::digits + 1; +}; + +// The number of bits (including the sign) in an integer. Eliminates sizeof +// hacks. +template +struct IntegerBitsPlusSign { + static const int value = std::numeric_limits::digits + + std::is_signed::value; +}; + +// Helper templates for integer manipulations. + +template +struct PositionOfSignBit { + static const size_t value = IntegerBitsPlusSign::value - 1; +}; + +// Determines if a numeric value is negative without throwing compiler +// warnings on: unsigned(value) < 0. +template ::value>::type* = nullptr> +constexpr bool IsValueNegative(T value) { + static_assert(std::is_arithmetic::value, "Argument must be numeric."); + return value < 0; +} + +template ::value>::type* = nullptr> +constexpr bool IsValueNegative(T) { + static_assert(std::is_arithmetic::value, "Argument must be numeric."); + return false; +} + +// This performs a fast negation, returning a signed value. It works on unsigned +// arguments, but probably doesn't do what you want for any unsigned value +// larger than max / 2 + 1 (i.e. signed min cast to unsigned). +template +constexpr typename std::make_signed::type ConditionalNegate( + T x, bool is_negative) { + static_assert(std::is_integral::value, "Type must be integral"); + using SignedT = typename std::make_signed::type; + using UnsignedT = typename std::make_unsigned::type; + return static_cast( + (static_cast(x) ^ -SignedT(is_negative)) + is_negative); +} + +// This performs a safe, absolute value via unsigned overflow. +template +constexpr typename std::make_unsigned::type SafeUnsignedAbs(T value) { + static_assert(std::is_integral::value, "Type must be integral"); + using UnsignedT = typename std::make_unsigned::type; + return IsValueNegative(value) + ? static_cast(0u - static_cast(value)) + : static_cast(value); +} + +// This allows us to switch paths on known compile-time constants. +#if defined(__clang__) || defined(__GNUC__) +constexpr bool CanDetectCompileTimeConstant() { return true; } +template +constexpr bool IsCompileTimeConstant(const T v) { + return __builtin_constant_p(v); +} +#else +constexpr bool CanDetectCompileTimeConstant() { return false; } +template +constexpr bool IsCompileTimeConstant(const T) { + return false; +} +#endif +template +constexpr bool MustTreatAsConstexpr(const T v) { + // Either we can't detect a compile-time constant, and must always use the + // constexpr path, or we know we have a compile-time constant. + return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v); +} + +// Forces a crash, like a CHECK(false). Used for numeric boundary errors. +// Also used in a constexpr template to trigger a compilation failure on +// an error condition. +struct CheckOnFailure { + template + static T HandleFailure() { +#if defined(_MSC_VER) + __debugbreak(); +#elif defined(__GNUC__) || defined(__clang__) + __builtin_trap(); +#else + ((void)(*(volatile char*)0 = 0)); +#endif + return T(); + } }; enum IntegerRepresentation { @@ -35,7 +137,7 @@ enum IntegerRepresentation { // A range for a given nunmeric Src type is contained for a given numeric Dst // type if both numeric_limits::max() <= numeric_limits::max() and -// numeric_limits::min() >= numeric_limits::min() are true. +// numeric_limits::lowest() >= numeric_limits::lowest() are true. // We implement this as template specializations rather than simple static // comparisons to ensure type correctness in our comparisons. enum NumericRangeRepresentation { @@ -46,16 +148,13 @@ enum NumericRangeRepresentation { // Helper templates to statically determine if our destination type can contain // maximum and minimum values represented by the source type. -template < - typename Dst, - typename Src, - IntegerRepresentation DstSign = std::numeric_limits::is_signed - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED, - IntegerRepresentation SrcSign = - std::numeric_limits::is_signed - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED > +template ::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = std::is_signed::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED> struct StaticDstRangeRelationToSrcRange; // Same sign: Dst is guaranteed to contain Src only if its range is equal or @@ -90,127 +189,630 @@ struct StaticDstRangeRelationToSrcRange= RANGE_VALID && - integer_range_constraint <= RANGE_INVALID); - return static_cast(integer_range_constraint); -} +// The following helper template addresses a corner case in range checks for +// conversion from a floating-point type to an integral type of smaller range +// but larger precision (e.g. float -> unsigned). The problem is as follows: +// 1. Integral maximum is always one less than a power of two, so it must be +// truncated to fit the mantissa of the floating point. The direction of +// rounding is implementation defined, but by default it's always IEEE +// floats, which round to nearest and thus result in a value of larger +// magnitude than the integral value. +// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX +// // is 4294967295u. +// 2. If the floating point value is equal to the promoted integral maximum +// value, a range check will erroneously pass. +// Example: (4294967296f <= 4294967295u) // This is true due to a precision +// // loss in rounding up to float. +// 3. When the floating point value is then converted to an integral, the +// resulting value is out of range for the target integral type and +// thus is implementation defined. +// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. +// To fix this bug we manually truncate the maximum value when the destination +// type is an integral of larger precision than the source floating-point type, +// such that the resulting maximum is represented exactly as a floating point. +template class Bounds> +struct NarrowingRange { + using SrcLimits = std::numeric_limits; + using DstLimits = typename std::numeric_limits; -// This function creates a RangeConstraint from an upper and lower bound -// check by taking advantage of the fact that only NaN can be out of range in -// both directions at once. -inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, - bool is_in_lower_bound) { - return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) | - (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); -} + // Computes the mask required to make an accurate comparison between types. + static const int kShift = + (MaxExponent::value > MaxExponent::value && + SrcLimits::digits < DstLimits::digits) + ? (DstLimits::digits - SrcLimits::digits) + : 0; + template ::value>::type* = nullptr> -template < - typename Dst, - typename Src, - IntegerRepresentation DstSign = std::numeric_limits::is_signed - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED, - IntegerRepresentation SrcSign = std::numeric_limits::is_signed - ? INTEGER_REPRESENTATION_SIGNED - : INTEGER_REPRESENTATION_UNSIGNED, - NumericRangeRepresentation DstRange = - StaticDstRangeRelationToSrcRange::value > + // Masks out the integer bits that are beyond the precision of the + // intermediate type used for comparison. + static constexpr T Adjust(T value) { + static_assert(std::is_same::value, ""); + static_assert(kShift < DstLimits::digits, ""); + return static_cast( + ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)), + IsValueNegative(value))); + } + + template ::value>::type* = nullptr> + static constexpr T Adjust(T value) { + static_assert(std::is_same::value, ""); + static_assert(kShift == 0, ""); + return value; + } + + static constexpr Dst max() { return Adjust(Bounds::max()); } + static constexpr Dst lowest() { return Adjust(Bounds::lowest()); } +}; + +template class Bounds, + IntegerRepresentation DstSign = std::is_signed::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + IntegerRepresentation SrcSign = std::is_signed::value + ? INTEGER_REPRESENTATION_SIGNED + : INTEGER_REPRESENTATION_UNSIGNED, + NumericRangeRepresentation DstRange = + StaticDstRangeRelationToSrcRange::value> struct DstRangeRelationToSrcRangeImpl; // The following templates are for ranges that must be verified at runtime. We // split it into checks based on signedness to avoid confusing casts and // compiler warnings on signed an unsigned comparisons. -// Dst range is statically determined to contain Src: Nothing to check. -template -struct DstRangeRelationToSrcRangeImpl class Bounds, + IntegerRepresentation DstSign, IntegerRepresentation SrcSign> +struct DstRangeRelationToSrcRangeImpl { - static RangeConstraint Check(Src value) { return RANGE_VALID; } + static constexpr RangeCheck Check(Src value) { + using SrcLimits = std::numeric_limits; + using DstLimits = NarrowingRange; + return RangeCheck( + static_cast(SrcLimits::lowest()) >= DstLimits::lowest() || + static_cast(value) >= DstLimits::lowest(), + static_cast(SrcLimits::max()) <= DstLimits::max() || + static_cast(value) <= DstLimits::max()); + } }; // Signed to signed narrowing: Both the upper and lower boundaries may be -// exceeded. -template -struct DstRangeRelationToSrcRangeImpl { - static RangeConstraint Check(Src value) { - return std::numeric_limits::is_iec559 - ? GetRangeConstraint(value <= std::numeric_limits::max(), - value >= -std::numeric_limits::max()) - : GetRangeConstraint(value <= std::numeric_limits::max(), - value >= std::numeric_limits::min()); +// exceeded for standard limits. +template class Bounds> +struct DstRangeRelationToSrcRangeImpl< + Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using DstLimits = NarrowingRange; + return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); } }; -// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded. -template -struct DstRangeRelationToSrcRangeImpl { - static RangeConstraint Check(Src value) { - return GetRangeConstraint(value <= std::numeric_limits::max(), true); +// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for +// standard limits. +template class Bounds> +struct DstRangeRelationToSrcRangeImpl< + Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using DstLimits = NarrowingRange; + return RangeCheck( + DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), + value <= DstLimits::max()); } }; -// Unsigned to signed: The upper boundary may be exceeded. -template -struct DstRangeRelationToSrcRangeImpl { - static RangeConstraint Check(Src value) { - return sizeof(Dst) > sizeof(Src) - ? RANGE_VALID - : GetRangeConstraint( - value <= static_cast(std::numeric_limits::max()), - true); +// Unsigned to signed: Only the upper bound can be exceeded for standard limits. +template class Bounds> +struct DstRangeRelationToSrcRangeImpl< + Dst, Src, Bounds, INTEGER_REPRESENTATION_SIGNED, + INTEGER_REPRESENTATION_UNSIGNED, NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using DstLimits = NarrowingRange; + using Promotion = decltype(Src() + Dst()); + return RangeCheck(DstLimits::lowest() <= Dst(0) || + static_cast(value) >= + static_cast(DstLimits::lowest()), + static_cast(value) <= + static_cast(DstLimits::max())); } }; // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, -// and any negative value exceeds the lower boundary. +// and any negative value exceeds the lower boundary for standard limits. +template class Bounds> +struct DstRangeRelationToSrcRangeImpl< + Dst, Src, Bounds, INTEGER_REPRESENTATION_UNSIGNED, + INTEGER_REPRESENTATION_SIGNED, NUMERIC_RANGE_NOT_CONTAINED> { + static constexpr RangeCheck Check(Src value) { + using SrcLimits = std::numeric_limits; + using DstLimits = NarrowingRange; + using Promotion = decltype(Src() + Dst()); + bool ge_zero = false; + // Converting floating-point to integer will discard fractional part, so + // values in (-1.0, -0.0) will truncate to 0 and fit in Dst. + if (std::is_floating_point::value) { + ge_zero = value > Src(-1); + } else { + ge_zero = value >= Src(0); + } + return RangeCheck( + ge_zero && (DstLimits::lowest() == 0 || + static_cast(value) >= DstLimits::lowest()), + static_cast(SrcLimits::max()) <= + static_cast(DstLimits::max()) || + static_cast(value) <= + static_cast(DstLimits::max())); + } +}; + +// Simple wrapper for statically checking if a type's range is contained. template -struct DstRangeRelationToSrcRangeImpl { - static RangeConstraint Check(Src value) { - return (MaxExponent::value >= MaxExponent::value) - ? GetRangeConstraint(true, value >= static_cast(0)) - : GetRangeConstraint( - value <= static_cast(std::numeric_limits::max()), - value >= static_cast(0)); +struct IsTypeInRangeForNumericType { + static const bool value = StaticDstRangeRelationToSrcRange::value == + NUMERIC_RANGE_CONTAINED; +}; + +template class Bounds = std::numeric_limits, + typename Src> +constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { + static_assert(std::is_arithmetic::value, "Argument must be numeric."); + static_assert(std::is_arithmetic::value, "Result must be numeric."); + static_assert(Bounds::lowest() < Bounds::max(), ""); + return DstRangeRelationToSrcRangeImpl::Check(value); +} + +// Integer promotion templates used by the portable checked integer arithmetic. +template +struct IntegerForDigitsAndSign; + +#define INTEGER_FOR_DIGITS_AND_SIGN(I) \ + template <> \ + struct IntegerForDigitsAndSign::value, \ + std::is_signed::value> { \ + using type = I; \ + } + +INTEGER_FOR_DIGITS_AND_SIGN(int8_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint8_t); +INTEGER_FOR_DIGITS_AND_SIGN(int16_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint16_t); +INTEGER_FOR_DIGITS_AND_SIGN(int32_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint32_t); +INTEGER_FOR_DIGITS_AND_SIGN(int64_t); +INTEGER_FOR_DIGITS_AND_SIGN(uint64_t); +#undef INTEGER_FOR_DIGITS_AND_SIGN + +// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to +// support 128-bit math, then the ArithmeticPromotion template below will need +// to be updated (or more likely replaced with a decltype expression). +static_assert(IntegerBitsPlusSign::value == 64, + "Max integer size not supported for this toolchain."); + +template ::value> +struct TwiceWiderInteger { + using type = + typename IntegerForDigitsAndSign::value * 2, + IsSigned>::type; +}; + +enum ArithmeticPromotionCategory { + LEFT_PROMOTION, // Use the type of the left-hand argument. + RIGHT_PROMOTION // Use the type of the right-hand argument. +}; + +// Determines the type that can represent the largest positive value. +template ::value > MaxExponent::value) + ? LEFT_PROMOTION + : RIGHT_PROMOTION> +struct MaxExponentPromotion; + +template +struct MaxExponentPromotion { + using type = Lhs; +}; + +template +struct MaxExponentPromotion { + using type = Rhs; +}; + +// Determines the type that can represent the lowest arithmetic value. +template ::value + ? (std::is_signed::value + ? (MaxExponent::value > MaxExponent::value + ? LEFT_PROMOTION + : RIGHT_PROMOTION) + : LEFT_PROMOTION) + : (std::is_signed::value + ? RIGHT_PROMOTION + : (MaxExponent::value < MaxExponent::value + ? LEFT_PROMOTION + : RIGHT_PROMOTION))> +struct LowestValuePromotion; + +template +struct LowestValuePromotion { + using type = Lhs; +}; + +template +struct LowestValuePromotion { + using type = Rhs; +}; + +// Determines the type that is best able to represent an arithmetic result. +template < + typename Lhs, typename Rhs = Lhs, + bool is_intmax_type = + std::is_integral::type>::value&& + IntegerBitsPlusSign::type>:: + value == IntegerBitsPlusSign::value, + bool is_max_exponent = + StaticDstRangeRelationToSrcRange< + typename MaxExponentPromotion::type, Lhs>::value == + NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< + typename MaxExponentPromotion::type, Rhs>::value == + NUMERIC_RANGE_CONTAINED> +struct BigEnoughPromotion; + +// The side with the max exponent is big enough. +template +struct BigEnoughPromotion { + using type = typename MaxExponentPromotion::type; + static const bool is_contained = true; +}; + +// We can use a twice wider type to fit. +template +struct BigEnoughPromotion { + using type = + typename TwiceWiderInteger::type, + std::is_signed::value || + std::is_signed::value>::type; + static const bool is_contained = true; +}; + +// No type is large enough. +template +struct BigEnoughPromotion { + using type = typename MaxExponentPromotion::type; + static const bool is_contained = false; +}; + +// We can statically check if operations on the provided types can wrap, so we +// can skip the checked operations if they're not needed. So, for an integer we +// care if the destination type preserves the sign and is twice the width of +// the source. +template +struct IsIntegerArithmeticSafe { + static const bool value = + !std::is_floating_point::value && + !std::is_floating_point::value && + !std::is_floating_point::value && + std::is_signed::value >= std::is_signed::value && + IntegerBitsPlusSign::value >= (2 * IntegerBitsPlusSign::value) && + std::is_signed::value >= std::is_signed::value && + IntegerBitsPlusSign::value >= (2 * IntegerBitsPlusSign::value); +}; + +// Promotes to a type that can represent any possible result of a binary +// arithmetic operation with the source types. +template ::value || + std::is_signed::value, + intmax_t, uintmax_t>::type, + typename MaxExponentPromotion::type>::value> +struct FastIntegerArithmeticPromotion; + +template +struct FastIntegerArithmeticPromotion { + using type = + typename TwiceWiderInteger::type, + std::is_signed::value || + std::is_signed::value>::type; + static_assert(IsIntegerArithmeticSafe::value, ""); + static const bool is_contained = true; +}; + +template +struct FastIntegerArithmeticPromotion { + using type = typename BigEnoughPromotion::type; + static const bool is_contained = false; +}; + +// Extracts the underlying type from an enum. +template ::value> +struct ArithmeticOrUnderlyingEnum; + +template +struct ArithmeticOrUnderlyingEnum { + using type = typename std::underlying_type::type; + static const bool value = std::is_arithmetic::value; +}; + +template +struct ArithmeticOrUnderlyingEnum { + using type = T; + static const bool value = std::is_arithmetic::value; +}; + +// The following are helper templates used in the CheckedNumeric class. +template +class CheckedNumeric; + +template +class ClampedNumeric; + +template +class StrictNumeric; + +// Used to treat CheckedNumeric and arithmetic underlying types the same. +template +struct UnderlyingType { + using type = typename ArithmeticOrUnderlyingEnum::type; + static const bool is_numeric = std::is_arithmetic::value; + static const bool is_checked = false; + static const bool is_clamped = false; + static const bool is_strict = false; +}; + +template +struct UnderlyingType> { + using type = T; + static const bool is_numeric = true; + static const bool is_checked = true; + static const bool is_clamped = false; + static const bool is_strict = false; +}; + +template +struct UnderlyingType> { + using type = T; + static const bool is_numeric = true; + static const bool is_checked = false; + static const bool is_clamped = true; + static const bool is_strict = false; +}; + +template +struct UnderlyingType> { + using type = T; + static const bool is_numeric = true; + static const bool is_checked = false; + static const bool is_clamped = false; + static const bool is_strict = true; +}; + +template +struct IsCheckedOp { + static const bool value = + UnderlyingType::is_numeric && UnderlyingType::is_numeric && + (UnderlyingType::is_checked || UnderlyingType::is_checked); +}; + +template +struct IsClampedOp { + static const bool value = + UnderlyingType::is_numeric && UnderlyingType::is_numeric && + (UnderlyingType::is_clamped || UnderlyingType::is_clamped) && + !(UnderlyingType::is_checked || UnderlyingType::is_checked); +}; + +template +struct IsStrictOp { + static const bool value = + UnderlyingType::is_numeric && UnderlyingType::is_numeric && + (UnderlyingType::is_strict || UnderlyingType::is_strict) && + !(UnderlyingType::is_checked || UnderlyingType::is_checked) && + !(UnderlyingType::is_clamped || UnderlyingType::is_clamped); +}; + +// as_signed<> returns the supplied integral value (or integral castable +// Numeric template) cast as a signed integral of equivalent precision. +// I.e. it's mostly an alias for: static_cast::type>(t) +template +constexpr typename std::make_signed< + typename base::internal::UnderlyingType::type>::type +as_signed(const Src value) { + static_assert(std::is_integral::value, + "Argument must be a signed or unsigned integer type."); + return static_cast(value); +} + +// as_unsigned<> returns the supplied integral value (or integral castable +// Numeric template) cast as an unsigned integral of equivalent precision. +// I.e. it's mostly an alias for: static_cast::type>(t) +template +constexpr typename std::make_unsigned< + typename base::internal::UnderlyingType::type>::type +as_unsigned(const Src value) { + static_assert(std::is_integral::value, + "Argument must be a signed or unsigned integer type."); + return static_cast(value); +} + +template +constexpr bool IsLessImpl(const L lhs, const R rhs, const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsUnderflow() || r_range.IsOverflow() || + (l_range == r_range && static_cast(lhs) < + static_cast(rhs)); +} + +template +struct IsLess { + static_assert(std::is_arithmetic::value && std::is_arithmetic::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange(lhs), + DstRangeRelationToSrcRange(rhs)); + } +}; + +template +constexpr bool IsLessOrEqualImpl(const L lhs, const R rhs, + const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsUnderflow() || r_range.IsOverflow() || + (l_range == r_range && static_cast(lhs) <= + static_cast(rhs)); +} + +template +struct IsLessOrEqual { + static_assert(std::is_arithmetic::value && std::is_arithmetic::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange(lhs), + DstRangeRelationToSrcRange(rhs)); + } +}; + +template +constexpr bool IsGreaterImpl(const L lhs, const R rhs, const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsOverflow() || r_range.IsUnderflow() || + (l_range == r_range && static_cast(lhs) > + static_cast(rhs)); +} + +template +struct IsGreater { + static_assert(std::is_arithmetic::value && std::is_arithmetic::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange(lhs), + DstRangeRelationToSrcRange(rhs)); } }; +template +constexpr bool IsGreaterOrEqualImpl(const L lhs, const R rhs, + const RangeCheck l_range, + const RangeCheck r_range) { + return l_range.IsOverflow() || r_range.IsUnderflow() || + (l_range == r_range && static_cast(lhs) >= + static_cast(rhs)); +} + +template +struct IsGreaterOrEqual { + static_assert(std::is_arithmetic::value && std::is_arithmetic::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange(lhs), + DstRangeRelationToSrcRange(rhs)); + } +}; + +template +struct IsEqual { + static_assert(std::is_arithmetic::value && std::is_arithmetic::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return DstRangeRelationToSrcRange(lhs) == + DstRangeRelationToSrcRange(rhs) && + static_cast(lhs) == + static_cast(rhs); + } +}; + +template +struct IsNotEqual { + static_assert(std::is_arithmetic::value && std::is_arithmetic::value, + "Types must be numeric."); + static constexpr bool Test(const L lhs, const R rhs) { + return DstRangeRelationToSrcRange(lhs) != + DstRangeRelationToSrcRange(rhs) || + static_cast(lhs) != + static_cast(rhs); + } +}; + +// These perform the actual math operations on the CheckedNumerics. +// Binary arithmetic operations. +template