Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

135
deps/v8/src/DEPS vendored Normal file
View File

@ -0,0 +1,135 @@
include_rules = [
"+src",
"-src/asmjs",
"+src/asmjs/asm-js.h",
"-src/baseline",
"+src/baseline/baseline.h",
"+src/baseline/baseline-batch-compiler.h",
"+src/baseline/bytecode-offset-iterator.h",
"-src/bigint",
"+src/bigint/bigint.h",
"-src/compiler",
"+src/compiler/code-assembler.h",
"+src/compiler/turbofan.h",
"+src/compiler/turboshaft/wasm-turboshaft-compiler.h",
"+src/compiler/wasm-compiler-definitions.h",
"+src/compiler/wasm-compiler.h",
"-src/flags/flags-impl.h",
"-src/heap",
"+src/heap/memory-chunk-constants.h",
"+src/heap/memory-chunk-metadata.h",
"+src/heap/code-range.h",
"+src/heap/read-only-spaces.h",
"+src/heap/trusted-range.h",
"+src/heap/combined-heap.h",
"+src/heap/factory.h",
"+src/heap/factory-inl.h",
# TODO(v8:10496): Don't expose so much (through transitive includes) outside
# of heap/.
"+src/heap/gc-tracer.h",
"+src/heap/gc-tracer-inl.h",
"+src/heap/heap.h",
"+src/heap/heap-verifier.h",
"+src/heap/heap-inl.h",
"+src/heap/heap-layout-inl.h",
"+src/heap/heap-layout.h",
"+src/heap/heap-write-barrier-inl.h",
"+src/heap/heap-write-barrier.h",
"+src/heap/local-factory-inl.h",
"+src/heap/local-factory.h",
"+src/heap/local-heap.h",
"+src/heap/local-heap-inl.h",
"+src/heap/pretenuring-handler-inl.h",
# TODO(v8:10496): Don't expose memory chunk outside of heap/.
"+src/heap/mutable-page-metadata.h",
"+src/heap/mutable-page-metadata-inl.h",
"+src/heap/memory-chunk.h",
"+src/heap/page-metadata-inl.h",
"+src/heap/page-pool.h",
"+src/heap/paged-spaces-inl.h",
"+src/heap/parked-scope-inl.h",
"+src/heap/parked-scope.h",
"+src/heap/read-only-heap-inl.h",
"+src/heap/read-only-heap.h",
"+src/heap/reference-summarizer.h",
"+src/heap/safepoint.h",
"+src/heap/base/stack.h",
"+src/heap/conservative-stack-visitor.h",
"+src/heap/visit-object.h",
"-src/inspector",
"-src/interpreter",
"+src/interpreter/bytecode-array-iterator.h",
"+src/interpreter/bytecode-array-random-iterator.h",
"+src/interpreter/bytecode-decoder.h",
"+src/interpreter/bytecode-flags-and-tokens.h",
"+src/interpreter/bytecode-register.h",
"+src/interpreter/bytecodes.h",
"+src/interpreter/interpreter.h",
"+src/interpreter/interpreter-generator.h",
"+src/interpreter/setup-interpreter.h",
"-src/maglev",
"+src/maglev/maglev.h",
"+src/maglev/maglev-concurrent-dispatcher.h",
"-src/regexp",
"+src/regexp/regexp.h",
"+src/regexp/regexp-flags.h",
"+src/regexp/regexp-result-vector.h",
"+src/regexp/regexp-stack.h",
"+src/regexp/regexp-utils.h",
"+src/tracing/trace-event-no-perfetto.h",
"-src/trap-handler",
"+src/trap-handler/handler-inside-posix.h",
"+src/trap-handler/handler-inside-win.h",
"+src/trap-handler/trap-handler.h",
"+src/trap-handler/trap-handler-simulator.h",
"+testing/gtest/include/gtest/gtest_prod.h",
"-src/libplatform",
"-include/libplatform",
"+builtins-generated",
"+torque-generated",
"+starboard",
]
specific_include_rules = {
"d8\.cc": [
"+include/libplatform/libplatform.h",
"+include/libplatform/v8-tracing.h",
"+perfetto/tracing/track_event.h",
"+perfetto/tracing/track_event_legacy.h"
],
"d8-platforms\.cc": [
"+include/libplatform/libplatform.h",
],
"builtins-trace\.cc": [
"+protos/perfetto",
],
"etw-jit-win\.cc": [
"+src/libplatform/tracing/recorder.h",
],
"etw-jit-metadata-win\.h": [
"+src/libplatform/etw/etw-provider-win.h",
],
"external-pointer-table\.cc": [
"+src/heap/read-only-spaces.h",
],
# keep the includes to a minimum since this header will be included via write barriers.
"memory-chunk-header\.h": [
"-src",
"+src/base/build_config.h",
"+src/flags/flags.h",
],
"script\.h": [
"+src/heap/factory.h",
"+src/heap/factory-base.h",
"+src/heap/local-factory.h",
],
"setup-builtins-internal\.cc": [
"+src/compiler/pipeline.h",
],
"snapshot\.cc": [
"+src/heap/read-only-promotion.h",
],
"string-hasher-inl\.h": [
"+third_party/rapidhash-v8/rapidhash.h",
]
}

11
deps/v8/src/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,11 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript"
}

3
deps/v8/src/OWNERS vendored Normal file
View File

@ -0,0 +1,3 @@
per-file *DEPS=file:../COMMON_OWNERS
per-file intl-*=file:../INTL_OWNERS
per-file *-intl*=file:../INTL_OWNERS

14
deps/v8/src/api/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>API"
}
buganizer_public: {
component_id: 1456124
}

10
deps/v8/src/api/OWNERS vendored Normal file
View File

@ -0,0 +1,10 @@
file:../../include/OWNERS
clemensb@chromium.org
ishell@chromium.org
jkummerow@chromium.org
leszeks@chromium.org
mlippautz@chromium.org
verwaest@chromium.org
# For v8-debug.h implementations.
per-file api.cc=file:../debug/OWNERS

489
deps/v8/src/api/api-arguments-inl.h vendored Normal file
View File

@ -0,0 +1,489 @@
// Copyright 2016 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_API_API_ARGUMENTS_INL_H_
#define V8_API_API_ARGUMENTS_INL_H_
#include "src/api/api-arguments.h"
// Include the non-inl header before the rest of the headers.
#include "src/api/api-inl.h"
#include "src/debug/debug.h"
#include "src/execution/vm-state-inl.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/instance-type.h"
#include "src/objects/slots-inl.h"
namespace v8 {
namespace internal {
CustomArgumentsBase::CustomArgumentsBase(Isolate* isolate)
: Relocatable(isolate) {}
template <typename T>
CustomArguments<T>::~CustomArguments() {
slot_at(kReturnValueIndex).store(Tagged<Object>(kHandleZapValue));
}
template <typename T>
template <typename V>
Handle<V> CustomArguments<T>::GetReturnValue(Isolate* isolate) const {
// Check the ReturnValue.
FullObjectSlot slot = slot_at(kReturnValueIndex);
DCHECK(Is<JSAny>(*slot));
return Cast<V>(Handle<Object>(slot.location()));
}
inline Tagged<JSObject> PropertyCallbackArguments::holder() const {
return Cast<JSObject>(*slot_at(T::kHolderIndex));
}
inline Tagged<Object> PropertyCallbackArguments::receiver() const {
return *slot_at(T::kThisIndex);
}
#define DCHECK_NAME_COMPATIBLE(interceptor, name) \
DCHECK(interceptor->is_named()); \
DCHECK(!name->IsPrivate()); \
DCHECK_IMPLIES(IsSymbol(*name), interceptor->can_intercept_symbols());
#define PREPARE_CALLBACK_INFO_ACCESSOR(ISOLATE, F, API_RETURN_TYPE, \
ACCESSOR_INFO, RECEIVER, ACCESSOR_KIND, \
EXCEPTION_CONTEXT) \
if (ISOLATE->should_check_side_effects() && \
!ISOLATE->debug()->PerformSideEffectCheckForAccessor( \
ACCESSOR_INFO, RECEIVER, ACCESSOR_KIND)) { \
return {}; \
} \
const PropertyCallbackInfo<API_RETURN_TYPE>& callback_info = \
GetPropertyCallbackInfo<API_RETURN_TYPE>(); \
ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F), \
EXCEPTION_CONTEXT, &callback_info);
#define PREPARE_CALLBACK_INFO_INTERCEPTOR(ISOLATE, F, API_RETURN_TYPE, \
INTERCEPTOR_INFO, EXCEPTION_CONTEXT) \
if (ISOLATE->should_check_side_effects() && \
!ISOLATE->debug()->PerformSideEffectCheckForInterceptor( \
INTERCEPTOR_INFO)) { \
return {}; \
} \
const PropertyCallbackInfo<API_RETURN_TYPE>& callback_info = \
GetPropertyCallbackInfo<API_RETURN_TYPE>(); \
ExternalCallbackScope call_scope(ISOLATE, FUNCTION_ADDR(F), \
EXCEPTION_CONTEXT, &callback_info);
DirectHandle<Object> FunctionCallbackArguments::CallOrConstruct(
Tagged<FunctionTemplateInfo> function, bool is_construct) {
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kFunctionCallback);
v8::FunctionCallback f =
reinterpret_cast<v8::FunctionCallback>(function->callback(isolate));
if (isolate->should_check_side_effects() &&
!isolate->debug()->PerformSideEffectCheckForCallback(
handle(function, isolate))) {
return {};
}
FunctionCallbackInfo<v8::Value> info(values_, argv_, argc_);
ExternalCallbackScope call_scope(isolate, FUNCTION_ADDR(f),
is_construct ? ExceptionContext::kConstructor
: ExceptionContext::kOperation,
&info);
f(info);
return GetReturnValue<Object>(isolate);
}
PropertyCallbackArguments::~PropertyCallbackArguments(){
#ifdef DEBUG
// TODO(chromium:1310062): enable this check.
// if (javascript_execution_counter_) {
// CHECK_WITH_MSG(javascript_execution_counter_ ==
// isolate()->javascript_execution_counter(),
// "Unexpected side effect detected");
// }
#endif // DEBUG
}
Maybe<InterceptorResult> PropertyCallbackArguments::GetBooleanReturnValue(
v8::Intercepted intercepted, const char* callback_kind_for_error_message,
bool ignore_return_value) {
Isolate* isolate = this->isolate();
if (isolate->has_exception()) {
// TODO(ishell, 328490288): fix Node.js which has Setter/Definer
// interceptor callbacks not returning v8::Intercepted::kYes on exceptions.
if ((false) && DEBUG_BOOL && (intercepted == v8::Intercepted::kNo)) {
FATAL(
"Check failed: %s interceptor callback has thrown an "
"exception but hasn't returned v8::Intercepted::kYes.",
callback_kind_for_error_message);
}
return Nothing<InterceptorResult>();
}
if (intercepted == v8::Intercepted::kNo) {
// Not intercepted, there must be no side effects including exceptions.
DCHECK(!isolate->has_exception());
return Just(InterceptorResult::kNotIntercepted);
}
DCHECK_EQ(intercepted, v8::Intercepted::kYes);
AcceptSideEffects();
if (ignore_return_value) return Just(InterceptorResult::kTrue);
bool result = IsTrue(*GetReturnValue<Boolean>(isolate), isolate);
// TODO(ishell, 348688196): ensure callbacks comply with this and
// enable the check.
if ((false) && DEBUG_BOOL && !result && ShouldThrowOnError()) {
FATAL(
"Check failed: %s interceptor callback hasn't thrown an "
"exception on failure as requested.",
callback_kind_for_error_message);
}
return Just(result ? InterceptorResult::kTrue : InterceptorResult::kFalse);
}
// -------------------------------------------------------------------------
// Named Interceptor callbacks.
DirectHandle<JSObjectOrUndefined>
PropertyCallbackArguments::CallNamedEnumerator(
DirectHandle<InterceptorInfo> interceptor) {
DCHECK(interceptor->is_named());
RCS_SCOPE(isolate(), RuntimeCallCounterId::kNamedEnumeratorCallback);
return CallPropertyEnumerator(interceptor);
}
// TODO(ishell): return std::optional<PropertyAttributes>.
DirectHandle<Object> PropertyCallbackArguments::CallNamedQuery(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
DCHECK_NAME_COMPATIBLE(interceptor, name);
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedQueryCallback);
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(Smi::FromInt(v8::None));
NamedPropertyQueryCallback f =
ToCData<NamedPropertyQueryCallback, kApiNamedPropertyQueryCallbackTag>(
isolate, interceptor->query());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Integer, interceptor,
ExceptionContext::kNamedQuery);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<Object>(isolate);
}
DirectHandle<JSAny> PropertyCallbackArguments::CallNamedGetter(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
DCHECK_NAME_COMPATIBLE(interceptor, name);
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
NamedPropertyGetterCallback f =
ToCData<NamedPropertyGetterCallback, kApiNamedPropertyGetterCallbackTag>(
isolate, interceptor->getter());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kNamedGetter);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>(isolate);
}
Handle<JSAny> PropertyCallbackArguments::CallNamedDescriptor(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
DCHECK_NAME_COMPATIBLE(interceptor, name);
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDescriptorCallback);
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
NamedPropertyDescriptorCallback f =
ToCData<NamedPropertyDescriptorCallback,
kApiNamedPropertyDescriptorCallbackTag>(
isolate, interceptor->descriptor());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kNamedDescriptor);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>(isolate);
}
v8::Intercepted PropertyCallbackArguments::CallNamedSetter(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name,
DirectHandle<Object> value) {
DCHECK_NAME_COMPATIBLE(interceptor, name);
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedSetterCallback);
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
NamedPropertySetterCallback f =
ToCData<NamedPropertySetterCallback, kApiNamedPropertySetterCallbackTag>(
isolate, interceptor->setter());
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, has_side_effects,
ExceptionContext::kNamedSetter);
v8::Intercepted intercepted =
f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallNamedDefiner(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name,
const v8::PropertyDescriptor& desc) {
DCHECK_NAME_COMPATIBLE(interceptor, name);
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDefinerCallback);
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
NamedPropertyDefinerCallback f = ToCData<NamedPropertyDefinerCallback,
kApiNamedPropertyDefinerCallbackTag>(
isolate, interceptor->definer());
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, has_side_effects,
ExceptionContext::kNamedDefiner);
v8::Intercepted intercepted =
f(v8::Utils::ToLocal(name), desc, callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallNamedDeleter(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name) {
DCHECK_NAME_COMPATIBLE(interceptor, name);
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedDeleterCallback);
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
NamedPropertyDeleterCallback f = ToCData<NamedPropertyDeleterCallback,
kApiNamedPropertyDeleterCallbackTag>(
isolate, interceptor->deleter());
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, has_side_effects,
ExceptionContext::kNamedDeleter);
v8::Intercepted intercepted = f(v8::Utils::ToLocal(name), callback_info);
return intercepted;
}
// -------------------------------------------------------------------------
// Indexed Interceptor callbacks.
DirectHandle<JSObjectOrUndefined>
PropertyCallbackArguments::CallIndexedEnumerator(
DirectHandle<InterceptorInfo> interceptor) {
DCHECK(!interceptor->is_named());
RCS_SCOPE(isolate(), RuntimeCallCounterId::kIndexedEnumeratorCallback);
return CallPropertyEnumerator(interceptor);
}
// TODO(ishell): return std::optional<PropertyAttributes>.
DirectHandle<Object> PropertyCallbackArguments::CallIndexedQuery(
DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
DCHECK(!interceptor->is_named());
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedQueryCallback);
index_ = index;
slot_at(kPropertyKeyIndex).store(Smi::zero()); // indexed callback marker
slot_at(kReturnValueIndex).store(Smi::FromInt(v8::None));
IndexedPropertyQueryCallbackV2 f =
ToCData<IndexedPropertyQueryCallbackV2,
kApiIndexedPropertyQueryCallbackTag>(isolate,
interceptor->query());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Integer, interceptor,
ExceptionContext::kIndexedQuery);
v8::Intercepted intercepted = f(index, callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<Object>(isolate);
}
DirectHandle<JSAny> PropertyCallbackArguments::CallIndexedGetter(
DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
DCHECK(!interceptor->is_named());
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kNamedGetterCallback);
index_ = index;
slot_at(kPropertyKeyIndex).store(Smi::zero()); // indexed callback marker
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
IndexedPropertyGetterCallbackV2 f =
ToCData<IndexedPropertyGetterCallbackV2,
kApiIndexedPropertyGetterCallbackTag>(isolate,
interceptor->getter());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kIndexedGetter);
v8::Intercepted intercepted = f(index, callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>(isolate);
}
Handle<JSAny> PropertyCallbackArguments::CallIndexedDescriptor(
DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
DCHECK(!interceptor->is_named());
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDescriptorCallback);
index_ = index;
slot_at(kPropertyKeyIndex).store(Smi::zero()); // indexed callback marker
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
IndexedPropertyDescriptorCallbackV2 f =
ToCData<IndexedPropertyDescriptorCallbackV2,
kApiIndexedPropertyDescriptorCallbackTag>(
isolate, interceptor->descriptor());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Value, interceptor,
ExceptionContext::kIndexedDescriptor);
v8::Intercepted intercepted = f(index, callback_info);
if (intercepted == v8::Intercepted::kNo) return {};
return GetReturnValue<JSAny>(isolate);
}
v8::Intercepted PropertyCallbackArguments::CallIndexedSetter(
DirectHandle<InterceptorInfo> interceptor, uint32_t index,
DirectHandle<Object> value) {
DCHECK(!interceptor->is_named());
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedSetterCallback);
index_ = index;
slot_at(kPropertyKeyIndex).store(Smi::zero()); // indexed callback marker
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
IndexedPropertySetterCallbackV2 f =
ToCData<IndexedPropertySetterCallbackV2,
kApiIndexedPropertySetterCallbackTag>(isolate,
interceptor->setter());
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, has_side_effects,
ExceptionContext::kIndexedSetter);
v8::Intercepted intercepted =
f(index, v8::Utils::ToLocal(value), callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallIndexedDefiner(
DirectHandle<InterceptorInfo> interceptor, uint32_t index,
const v8::PropertyDescriptor& desc) {
DCHECK(!interceptor->is_named());
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDefinerCallback);
index_ = index;
slot_at(kPropertyKeyIndex).store(Smi::zero()); // indexed callback marker
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
IndexedPropertyDefinerCallbackV2 f =
ToCData<IndexedPropertyDefinerCallbackV2,
kApiIndexedPropertyDefinerCallbackTag>(isolate,
interceptor->definer());
DirectHandle<InterceptorInfo> has_side_effects;
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, void, has_side_effects,
ExceptionContext::kIndexedDefiner);
v8::Intercepted intercepted = f(index, desc, callback_info);
return intercepted;
}
v8::Intercepted PropertyCallbackArguments::CallIndexedDeleter(
DirectHandle<InterceptorInfo> interceptor, uint32_t index) {
DCHECK(!interceptor->is_named());
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kIndexedDeleterCallback);
index_ = index;
slot_at(kPropertyKeyIndex).store(Smi::zero()); // indexed callback marker
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
IndexedPropertyDeleterCallbackV2 f =
ToCData<IndexedPropertyDeleterCallbackV2,
kApiIndexedPropertyDeleterCallbackTag>(isolate,
interceptor->deleter());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Boolean, interceptor,
ExceptionContext::kIndexedDeleter);
v8::Intercepted intercepted = f(index, callback_info);
return intercepted;
}
DirectHandle<JSObjectOrUndefined>
PropertyCallbackArguments::CallPropertyEnumerator(
DirectHandle<InterceptorInfo> interceptor) {
// Named and indexed enumerator callbacks have same signatures.
static_assert(std::is_same<NamedPropertyEnumeratorCallback,
IndexedPropertyEnumeratorCallback>::value);
Isolate* isolate = this->isolate();
slot_at(kPropertyKeyIndex).store(Smi::zero()); // not relevant
// Enumerator callback's return value is initialized with undefined even
// though it's supposed to return v8::Array.
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
// TODO(ishell): consider making it return v8::Intercepted to indicate
// whether the result was set or not.
IndexedPropertyEnumeratorCallback f =
v8::ToCData<IndexedPropertyEnumeratorCallback,
kApiIndexedPropertyEnumeratorCallbackTag>(
isolate, interceptor->enumerator());
PREPARE_CALLBACK_INFO_INTERCEPTOR(isolate, f, v8::Array, interceptor,
ExceptionContext::kNamedEnumerator);
f(callback_info);
DirectHandle<JSAny> result = GetReturnValue<JSAny>(isolate);
DCHECK(IsUndefined(*result) || IsJSObject(*result));
return Cast<JSObjectOrUndefined>(result);
}
// -------------------------------------------------------------------------
// Accessors
DirectHandle<JSAny> PropertyCallbackArguments::CallAccessorGetter(
DirectHandle<AccessorInfo> info, DirectHandle<Name> name) {
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorGetterCallback);
// Unlike interceptor callbacks we know that the property exists, so
// the callback is allowed to have side effects.
AcceptSideEffects();
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
AccessorNameGetterCallback f =
reinterpret_cast<AccessorNameGetterCallback>(info->getter(isolate));
PREPARE_CALLBACK_INFO_ACCESSOR(
isolate, f, v8::Value, info, direct_handle(receiver(), isolate),
ACCESSOR_GETTER, ExceptionContext::kAttributeGet);
f(v8::Utils::ToLocal(name), callback_info);
return GetReturnValue<JSAny>(isolate);
}
bool PropertyCallbackArguments::CallAccessorSetter(
DirectHandle<AccessorInfo> accessor_info, DirectHandle<Name> name,
DirectHandle<Object> value) {
Isolate* isolate = this->isolate();
RCS_SCOPE(isolate, RuntimeCallCounterId::kAccessorSetterCallback);
// Unlike interceptor callbacks we know that the property exists, so
// the callback is allowed to have side effects.
AcceptSideEffects();
slot_at(kPropertyKeyIndex).store(*name);
slot_at(kReturnValueIndex).store(ReadOnlyRoots(isolate).true_value());
// The actual type of setter callback is either
// v8::AccessorNameSetterCallback or
// i::Accessors::AccessorNameBooleanSetterCallback, depending on whether the
// AccessorInfo was created by the API or internally (see accessors.cc).
// Here we handle both cases using the AccessorNameSetterCallback signature
// and checking whether the returned result is set to default value
// (the undefined value).
// TODO(ishell, 348660658): update V8 Api to allow setter callbacks provide
// the result of [[Set]] operation according to JavaScript semantics.
AccessorNameSetterCallback f = reinterpret_cast<AccessorNameSetterCallback>(
accessor_info->setter(isolate));
PREPARE_CALLBACK_INFO_ACCESSOR(
isolate, f, void, accessor_info, direct_handle(receiver(), isolate),
ACCESSOR_SETTER, ExceptionContext::kAttributeSet);
f(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), callback_info);
// Historically, in case of v8::AccessorNameSetterCallback it wasn't allowed
// to set the result and not setting the result was treated as successful
// execution.
// During interceptors Api refactoring it was temporarily allowed to call
// v8::ReturnValue<void>::Set[NonEmpty](Local<S>) and the result was just
// converted to v8::Boolean which was then treated as a result of [[Set]].
// In case of AccessorNameBooleanSetterCallback, the result is always
// set to v8::Boolean or an exception is be thrown (in which case the
// result is ignored anyway). So, regardless of whether the signature was
// v8::AccessorNameSetterCallback or AccessorNameBooleanSetterCallback
// the result is guaranteed to be v8::Boolean value indicating success or
// failure.
DirectHandle<Boolean> result = GetReturnValue<Boolean>(isolate);
return IsTrue(*result, isolate);
}
#undef PREPARE_CALLBACK_INFO_ACCESSOR
#undef PREPARE_CALLBACK_INFO_INTERCEPTOR
} // namespace internal
} // namespace v8
#endif // V8_API_API_ARGUMENTS_INL_H_

58
deps/v8/src/api/api-arguments.cc vendored Normal file
View File

@ -0,0 +1,58 @@
// Copyright 2018 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.
#include "src/api/api-arguments.h"
#include "src/api/api-arguments-inl.h"
namespace v8 {
namespace internal {
PropertyCallbackArguments::PropertyCallbackArguments(
Isolate* isolate, Tagged<Object> data, Tagged<Object> self,
Tagged<JSObject> holder, Maybe<ShouldThrow> should_throw)
: Super(isolate)
#ifdef DEBUG
,
javascript_execution_counter_(isolate->javascript_execution_counter())
#endif // DEBUG
{
if (DEBUG_BOOL) {
// Zap these fields to ensure that they are initialized by a subsequent
// CallXXX(..).
Tagged<Object> zap_value(kZapValue);
slot_at(T::kPropertyKeyIndex).store(zap_value);
slot_at(T::kReturnValueIndex).store(zap_value);
}
slot_at(T::kThisIndex).store(self);
slot_at(T::kHolderIndex).store(holder);
slot_at(T::kDataIndex).store(data);
slot_at(T::kIsolateIndex)
.store(Tagged<Object>(reinterpret_cast<Address>(isolate)));
int value = Internals::kInferShouldThrowMode;
if (should_throw.IsJust()) {
value = should_throw.FromJust();
}
slot_at(T::kShouldThrowOnErrorIndex).store(Smi::FromInt(value));
slot_at(T::kHolderV2Index).store(Smi::zero());
DCHECK(IsHeapObject(*slot_at(T::kHolderIndex)));
DCHECK(IsSmi(*slot_at(T::kIsolateIndex)));
}
FunctionCallbackArguments::FunctionCallbackArguments(
Isolate* isolate, Tagged<FunctionTemplateInfo> target,
Tagged<HeapObject> new_target, Address* argv, int argc)
: Super(isolate), argv_(argv), argc_(argc) {
slot_at(T::kTargetIndex).store(target);
slot_at(T::kUnusedIndex).store(ReadOnlyRoots(isolate).undefined_value());
slot_at(T::kNewTargetIndex).store(new_target);
slot_at(T::kIsolateIndex)
.store(Tagged<Object>(reinterpret_cast<Address>(isolate)));
slot_at(T::kReturnValueIndex).store(ReadOnlyRoots(isolate).undefined_value());
slot_at(T::kContextIndex).store(isolate->context());
DCHECK(IsSmi(*slot_at(T::kIsolateIndex)));
}
} // namespace internal
} // namespace v8

317
deps/v8/src/api/api-arguments.h vendored Normal file
View File

@ -0,0 +1,317 @@
// Copyright 2016 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_API_API_ARGUMENTS_H_
#define V8_API_API_ARGUMENTS_H_
#include "include/v8-template.h"
#include "src/builtins/builtins-utils.h"
#include "src/execution/isolate.h"
#include "src/objects/slots.h"
#include "src/objects/visitors.h"
namespace v8 {
namespace internal {
// Custom arguments replicate a small segment of stack that can be
// accessed through an Arguments object the same way the actual stack
// can.
class CustomArgumentsBase : public Relocatable {
protected:
explicit inline CustomArgumentsBase(Isolate* isolate);
};
template <typename T>
class CustomArguments : public CustomArgumentsBase {
public:
static constexpr int kReturnValueIndex = T::kReturnValueIndex;
static_assert(T::kSize == sizeof(T));
~CustomArguments() override;
inline void IterateInstance(RootVisitor* v) override {
v->VisitRootPointers(Root::kRelocatable, nullptr, slot_at(0),
slot_at(T::kArgsLength));
}
protected:
explicit inline CustomArguments(Isolate* isolate)
: CustomArgumentsBase(isolate) {}
template <typename V>
Handle<V> GetReturnValue(Isolate* isolate) const;
inline Isolate* isolate() const {
return reinterpret_cast<Isolate*>((*slot_at(T::kIsolateIndex)).ptr());
}
inline FullObjectSlot slot_at(int index) const {
// This allows index == T::kArgsLength so "one past the end" slots
// can be retrieved for iterating purposes.
DCHECK_LE(static_cast<unsigned>(index),
static_cast<unsigned>(T::kArgsLength));
return FullObjectSlot(values_ + index);
}
Address values_[T::kArgsLength];
};
// Note: Calling args.Call() sets the return value on args. For multiple
// Call()'s, a new args should be used every time.
// This class also serves as a side effects detection scope (JavaScript code
// execution). It is used for ensuring correctness of the interceptor callback
// implementations. The idea is that the interceptor callback that does not
// intercept an operation must not produce side effects. If the callback
// signals that it has handled the operation (by either returning a respective
// result or by throwing an exception) then the AcceptSideEffects() method
// must be called to "accept" the side effects that have happened during the
// lifetime of the PropertyCallbackArguments object.
class PropertyCallbackArguments final
: public CustomArguments<PropertyCallbackInfo<Value> > {
public:
using T = PropertyCallbackInfo<Value>;
using Super = CustomArguments<T>;
static constexpr int kArgsLength = T::kArgsLength;
static constexpr int kThisIndex = T::kThisIndex;
static constexpr int kDataIndex = T::kDataIndex;
static constexpr int kHolderV2Index = T::kHolderV2Index;
static constexpr int kHolderIndex = T::kHolderIndex;
static constexpr int kIsolateIndex = T::kIsolateIndex;
static constexpr int kShouldThrowOnErrorIndex = T::kShouldThrowOnErrorIndex;
static constexpr int kPropertyKeyIndex = T::kPropertyKeyIndex;
// This constructor leaves kPropertyKeyIndex and kReturnValueIndex slots
// uninitialized in order to let them be initialized by the subsequent
// CallXXX(..) and avoid double initialization. As a consequence, there
// must be no GC call between this constructor and CallXXX(..).
// In debug mode these slots are zapped, so GC should be able to detect
// the misuse of this object.
PropertyCallbackArguments(Isolate* isolate, Tagged<Object> data,
Tagged<Object> self, Tagged<JSObject> holder,
Maybe<ShouldThrow> should_throw);
inline ~PropertyCallbackArguments();
// Don't copy PropertyCallbackArguments, because they would both have the
// same prev_ pointer.
PropertyCallbackArguments(const PropertyCallbackArguments&) = delete;
PropertyCallbackArguments& operator=(const PropertyCallbackArguments&) =
delete;
// -------------------------------------------------------------------------
// Accessor Callbacks
// Returns the result of [[Get]] operation or throws an exception.
// In case of exception empty handle is returned.
// TODO(ishell, 328490288): stop returning empty handles.
inline DirectHandle<JSAny> CallAccessorGetter(DirectHandle<AccessorInfo> info,
DirectHandle<Name> name);
// Returns the result of [[Set]] operation or throws an exception.
V8_WARN_UNUSED_RESULT
inline bool CallAccessorSetter(DirectHandle<AccessorInfo> info,
DirectHandle<Name> name,
DirectHandle<Object> value);
// -------------------------------------------------------------------------
// Named Interceptor Callbacks
// Empty handle means that the request was not intercepted.
// Pending exception handling should be done by the caller.
inline DirectHandle<Object> CallNamedQuery(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name);
inline DirectHandle<JSAny> CallNamedGetter(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name);
// Calls Setter/Definer/Deleter callback and returns whether the request
// was intercepted.
// Pending exception handling and interpretation of the result should be
// done by the caller using GetBooleanReturnValue(..).
inline v8::Intercepted CallNamedSetter(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name,
DirectHandle<Object> value);
inline v8::Intercepted CallNamedDefiner(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name,
const v8::PropertyDescriptor& desc);
inline v8::Intercepted CallNamedDeleter(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name);
// Empty handle means that the request was not intercepted.
// Pending exception handling should be done by the caller.
inline Handle<JSAny> CallNamedDescriptor(
DirectHandle<InterceptorInfo> interceptor, DirectHandle<Name> name);
// Returns JSArray-like object with property names or undefined.
inline DirectHandle<JSObjectOrUndefined> CallNamedEnumerator(
DirectHandle<InterceptorInfo> interceptor);
// -------------------------------------------------------------------------
// Indexed Interceptor Callbacks
// Empty handle means that the request was not intercepted.
// Pending exception handling should be done by the caller.
inline DirectHandle<Object> CallIndexedQuery(
DirectHandle<InterceptorInfo> interceptor, uint32_t index);
inline DirectHandle<JSAny> CallIndexedGetter(
DirectHandle<InterceptorInfo> interceptor, uint32_t index);
// Calls Setter/Definer/Deleter callback and returns whether the request
// was intercepted.
// Pending exception handling and interpretation of the result should be
// done by the caller using GetBooleanReturnValue(..).
inline v8::Intercepted CallIndexedSetter(
DirectHandle<InterceptorInfo> interceptor, uint32_t index,
DirectHandle<Object> value);
inline v8::Intercepted CallIndexedDefiner(
DirectHandle<InterceptorInfo> interceptor, uint32_t index,
const v8::PropertyDescriptor& desc);
inline v8::Intercepted CallIndexedDeleter(
DirectHandle<InterceptorInfo> interceptor, uint32_t index);
// Empty handle means that the request was not intercepted.
// Pending exception handling should be done by the caller.
inline Handle<JSAny> CallIndexedDescriptor(
DirectHandle<InterceptorInfo> interceptor, uint32_t index);
// Returns JSArray-like object with property names or undefined.
inline DirectHandle<JSObjectOrUndefined> CallIndexedEnumerator(
DirectHandle<InterceptorInfo> interceptor);
// Accept potential JavaScript side effects that might occur during life
// time of this object.
inline void AcceptSideEffects() {
#ifdef DEBUG
javascript_execution_counter_ = 0;
#endif // DEBUG
}
// Converts the result of Setter/Definer/Deleter interceptor callback to
// Maybe<InterceptorResult>.
// Currently, in certain scenarios the actual boolean result returned by
// the Setter/Definer operation is ignored and thus we don't need to process
// the actual return value.
inline Maybe<InterceptorResult> GetBooleanReturnValue(
v8::Intercepted intercepted, const char* callback_kind_for_error_message,
bool ignore_return_value = false);
// TODO(ishell): cleanup this hack by embedding the PropertyCallbackInfo
// into PropertyCallbackArguments object.
template <typename T>
const v8::PropertyCallbackInfo<T>& GetPropertyCallbackInfo() {
return *(reinterpret_cast<PropertyCallbackInfo<T>*>(&values_[0]));
}
// Forwards ShouldThrowOnError() request to the underlying
// v8::PropertyCallbackInfo<> object.
bool ShouldThrowOnError() {
return GetPropertyCallbackInfo<Value>().ShouldThrowOnError();
}
// Unofficial way of getting property key from v8::PropertyCallbackInfo<T>.
template <typename T>
static Tagged<Object> GetPropertyKey(const PropertyCallbackInfo<T>& info) {
return Tagged<Object>(info.args_[kPropertyKeyIndex]);
}
template <typename T>
static Handle<Object> GetPropertyKeyHandle(
const PropertyCallbackInfo<T>& info) {
return Handle<Object>(&info.args_[kPropertyKeyIndex]);
}
// Returns index value passed to CallIndexedXXX(). This works as long as
// all the calls to indexed interceptor callbacks are done via
// PropertyCallbackArguments.
template <typename T>
static uint32_t GetPropertyIndex(const PropertyCallbackInfo<T>& info) {
// Currently all indexed interceptor callbacks are called via
// PropertyCallbackArguments, so it's guaranteed that
// v8::PropertyCallbackInfo<T>::args_ array IS the
// PropertyCallbackArguments::values_ array. As a result we can restore
// pointer to PropertyCallbackArguments object from the former.
Address ptr = reinterpret_cast<Address>(&info.args_) -
offsetof(PropertyCallbackArguments, values_);
auto pca = reinterpret_cast<const PropertyCallbackArguments*>(ptr);
return pca->index_;
}
private:
// Returns JSArray-like object with property names or undefined.
inline DirectHandle<JSObjectOrUndefined> CallPropertyEnumerator(
DirectHandle<InterceptorInfo> interceptor);
inline Tagged<JSObject> holder() const;
inline Tagged<Object> receiver() const;
// This field is used for propagating index value from CallIndexedXXX()
// to ExceptionPropagationCallback.
uint32_t index_ = kMaxUInt32;
#ifdef DEBUG
// This stores current value of Isolate::javascript_execution_counter().
// It's used for detecting whether JavaScript code was executed between
// PropertyCallbackArguments's constructor and destructor.
uint32_t javascript_execution_counter_;
#endif // DEBUG
};
class FunctionCallbackArguments
: public CustomArguments<FunctionCallbackInfo<Value> > {
public:
using T = FunctionCallbackInfo<Value>;
using Super = CustomArguments<T>;
static constexpr int kArgsLength = T::kArgsLength;
static constexpr int kArgsLengthWithReceiver = T::kArgsLengthWithReceiver;
static constexpr int kUnusedIndex = T::kUnusedIndex;
static constexpr int kIsolateIndex = T::kIsolateIndex;
static constexpr int kContextIndex = T::kContextIndex;
static constexpr int kTargetIndex = T::kTargetIndex;
static constexpr int kNewTargetIndex = T::kNewTargetIndex;
static_assert(T::kThisValuesIndex == BuiltinArguments::kReceiverArgsIndex);
static constexpr int kSize = T::kSize;
static constexpr int kImplicitArgsOffset = T::kImplicitArgsOffset;
static constexpr int kValuesOffset = T::kValuesOffset;
static constexpr int kLengthOffset = T::kLengthOffset;
// Make sure all FunctionCallbackInfo constants are in sync.
static_assert(T::kSize == sizeof(T));
static_assert(T::kImplicitArgsOffset == offsetof(T, implicit_args_));
static_assert(T::kValuesOffset == offsetof(T, values_));
static_assert(T::kLengthOffset == offsetof(T, length_));
FunctionCallbackArguments(Isolate* isolate,
Tagged<FunctionTemplateInfo> target,
Tagged<HeapObject> new_target, Address* argv,
int argc);
/*
* The following Call function wraps the calling of all callbacks to handle
* calling either the old or the new style callbacks depending on which one
* has been registered.
* For old callbacks which return an empty handle, the ReturnValue is checked
* and used if it's been set to anything inside the callback.
* New style callbacks always use the return value.
*/
inline DirectHandle<Object> CallOrConstruct(
Tagged<FunctionTemplateInfo> function, bool is_construct);
// Unofficial way of getting target FunctionTemplateInfo from
// v8::FunctionCallbackInfo<T>.
template <typename T>
static Tagged<Object> GetTarget(const FunctionCallbackInfo<T>& info) {
return Tagged<Object>(info.implicit_args_[kTargetIndex]);
}
private:
Address* argv_;
int const argc_;
};
static_assert(BuiltinArguments::kNumExtraArgs ==
BuiltinExitFrameConstants::kNumExtraArgs);
static_assert(BuiltinArguments::kNumExtraArgsWithReceiver ==
BuiltinExitFrameConstants::kNumExtraArgsWithReceiver);
} // namespace internal
} // namespace v8
#endif // V8_API_API_ARGUMENTS_H_

357
deps/v8/src/api/api-inl.h vendored Normal file
View File

@ -0,0 +1,357 @@
// Copyright 2018 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_API_API_INL_H_
#define V8_API_API_INL_H_
#include "src/api/api.h"
// Include the non-inl header before the rest of the headers.
#include "include/v8-fast-api-calls.h"
#include "src/common/assert-scope.h"
#include "src/execution/microtask-queue.h"
#include "src/flags/flags.h"
#include "src/handles/handles-inl.h"
#include "src/heap/heap-inl.h"
#include "src/objects/foreign-inl.h"
#include "src/objects/objects-inl.h"
namespace v8 {
template <typename T, internal::ExternalPointerTag tag>
inline T ToCData(i::Isolate* isolate,
v8::internal::Tagged<v8::internal::Object> obj) {
static_assert(sizeof(T) == sizeof(v8::internal::Address));
if (obj == v8::internal::Smi::zero()) return nullptr;
return reinterpret_cast<T>(
v8::internal::Cast<v8::internal::Foreign>(obj)->foreign_address<tag>(
isolate));
}
template <internal::ExternalPointerTag tag>
inline v8::internal::Address ToCData(
i::Isolate* isolate, v8::internal::Tagged<v8::internal::Object> obj) {
if (obj == v8::internal::Smi::zero()) return v8::internal::kNullAddress;
return v8::internal::Cast<v8::internal::Foreign>(obj)->foreign_address<tag>(
isolate);
}
template <internal::ExternalPointerTag tag, typename T>
inline v8::internal::DirectHandle<i::UnionOf<i::Smi, i::Foreign>> FromCData(
v8::internal::Isolate* isolate, T obj) {
static_assert(sizeof(T) == sizeof(v8::internal::Address));
if (obj == nullptr) return direct_handle(v8::internal::Smi::zero(), isolate);
return isolate->factory()->NewForeign<tag>(
reinterpret_cast<v8::internal::Address>(obj));
}
template <internal::ExternalPointerTag tag>
inline v8::internal::DirectHandle<i::UnionOf<i::Smi, i::Foreign>> FromCData(
v8::internal::Isolate* isolate, v8::internal::Address obj) {
if (obj == v8::internal::kNullAddress) {
return direct_handle(v8::internal::Smi::zero(), isolate);
}
return isolate->factory()->NewForeign<tag>(obj);
}
template <class From, class To>
inline Local<To> Utils::Convert(v8::internal::DirectHandle<From> obj) {
DCHECK(obj.is_null() || IsSmi(*obj) || !IsTheHole(*obj));
#ifdef V8_ENABLE_DIRECT_HANDLE
if (obj.is_null()) return Local<To>();
return Local<To>::FromAddress(obj.address());
#else
// This simply uses the location of the indirect handle wrapped inside a
// "fake" direct handle.
return Local<To>::FromSlot(indirect_handle(obj).location());
#endif
}
// Implementations of ToLocal
#define MAKE_TO_LOCAL(Name) \
template <template <typename> typename HandleType, typename T, typename> \
inline auto Utils::Name(HandleType<T> obj) { \
return Utils::Name##_helper(v8::internal::DirectHandle<T>(obj)); \
}
TO_LOCAL_NAME_LIST(MAKE_TO_LOCAL)
#define MAKE_TO_LOCAL_PRIVATE(Name, From, To) \
inline Local<v8::To> Utils::Name##_helper( \
v8::internal::DirectHandle<v8::internal::From> obj) { \
return Convert<v8::internal::From, v8::To>(obj); \
}
TO_LOCAL_LIST(MAKE_TO_LOCAL_PRIVATE)
#define MAKE_TO_LOCAL_TYPED_ARRAY(Type, typeName, TYPE, ctype) \
Local<v8::Type##Array> Utils::ToLocal##Type##Array( \
v8::internal::DirectHandle<v8::internal::JSTypedArray> obj) { \
DCHECK(obj->type() == v8::internal::kExternal##Type##Array); \
return Convert<v8::internal::JSTypedArray, v8::Type##Array>(obj); \
}
TYPED_ARRAYS(MAKE_TO_LOCAL_TYPED_ARRAY)
#undef MAKE_TO_LOCAL_TYPED_ARRAY
#undef MAKE_TO_LOCAL
#undef MAKE_TO_LOCAL_PRIVATE
#undef TO_LOCAL_LIST
// Implementations of OpenHandle
#ifdef V8_ENABLE_DIRECT_HANDLE
#define MAKE_OPEN_HANDLE(From, To) \
v8::internal::Handle<v8::internal::To> Utils::OpenHandle( \
const v8::From* that, bool allow_empty_handle) { \
DCHECK(allow_empty_handle || !v8::internal::ValueHelper::IsEmpty(that)); \
DCHECK(v8::internal::ValueHelper::IsEmpty(that) || \
Is##To(v8::internal::Tagged<v8::internal::Object>( \
v8::internal::ValueHelper::ValueAsAddress(that)))); \
if (v8::internal::ValueHelper::IsEmpty(that)) { \
return v8::internal::Handle<v8::internal::To>::null(); \
} \
return v8::internal::Handle<v8::internal::To>( \
v8::HandleScope::CreateHandleForCurrentIsolate( \
v8::internal::ValueHelper::ValueAsAddress(that))); \
} \
\
v8::internal::DirectHandle<v8::internal::To> Utils::OpenDirectHandle( \
const v8::From* that, bool allow_empty_handle) { \
DCHECK(allow_empty_handle || !v8::internal::ValueHelper::IsEmpty(that)); \
DCHECK(v8::internal::ValueHelper::IsEmpty(that) || \
Is##To(v8::internal::Tagged<v8::internal::Object>( \
v8::internal::ValueHelper::ValueAsAddress(that)))); \
return v8::internal::DirectHandle<v8::internal::To>::FromAddress( \
v8::internal::ValueHelper::ValueAsAddress(that)); \
} \
\
v8::internal::IndirectHandle<v8::internal::To> Utils::OpenIndirectHandle( \
const v8::From* that, bool allow_empty_handle) { \
return Utils::OpenHandle(that, allow_empty_handle); \
}
#else // !V8_ENABLE_DIRECT_HANDLE
#define MAKE_OPEN_HANDLE(From, To) \
v8::internal::Handle<v8::internal::To> Utils::OpenHandle( \
const v8::From* that, bool allow_empty_handle) { \
DCHECK(allow_empty_handle || !v8::internal::ValueHelper::IsEmpty(that)); \
DCHECK(v8::internal::ValueHelper::IsEmpty(that) || \
Is##To(v8::internal::Tagged<v8::internal::Object>( \
v8::internal::ValueHelper::ValueAsAddress(that)))); \
return v8::internal::Handle<v8::internal::To>( \
reinterpret_cast<v8::internal::Address*>( \
const_cast<v8::From*>(that))); \
} \
\
v8::internal::DirectHandle<v8::internal::To> Utils::OpenDirectHandle( \
const v8::From* that, bool allow_empty_handle) { \
return Utils::OpenHandle(that, allow_empty_handle); \
} \
\
v8::internal::IndirectHandle<v8::internal::To> Utils::OpenIndirectHandle( \
const v8::From* that, bool allow_empty_handle) { \
return Utils::OpenHandle(that, allow_empty_handle); \
}
#endif // V8_ENABLE_DIRECT_HANDLE
OPEN_HANDLE_LIST(MAKE_OPEN_HANDLE)
#undef MAKE_OPEN_HANDLE
#undef OPEN_HANDLE_LIST
template <bool do_callback>
class V8_NODISCARD CallDepthScope {
public:
CallDepthScope(i::Isolate* isolate, Local<Context> context)
: isolate_(isolate), saved_context_(isolate->context(), isolate_) {
isolate_->thread_local_top()->IncrementCallDepth<do_callback>(this);
i::Tagged<i::NativeContext> env = *Utils::OpenDirectHandle(*context);
isolate->set_context(env);
if (do_callback) isolate_->FireBeforeCallEnteredCallback();
}
~CallDepthScope() {
i::MicrotaskQueue* microtask_queue =
i::Cast<i::NativeContext>(isolate_->context())
->microtask_queue(isolate_);
isolate_->thread_local_top()->DecrementCallDepth(this);
// Clear the exception when exiting V8 to avoid memory leaks.
// Also clear termination exceptions iff there's no TryCatch handler.
// TODO(verwaest): Drop this once we propagate exceptions to external
// TryCatch on Throw. This should be debug-only.
if (isolate_->thread_local_top()->CallDepthIsZero() &&
(isolate_->thread_local_top()->try_catch_handler_ == nullptr ||
!isolate_->is_execution_terminating())) {
isolate_->clear_internal_exception();
}
if (do_callback) isolate_->FireCallCompletedCallback(microtask_queue);
#ifdef DEBUG
if (do_callback) {
if (microtask_queue && microtask_queue->microtasks_policy() ==
v8::MicrotasksPolicy::kScoped) {
DCHECK(microtask_queue->GetMicrotasksScopeDepth() ||
!microtask_queue->DebugMicrotasksScopeDepthIsZero());
}
}
DCHECK(CheckKeptObjectsClearedAfterMicrotaskCheckpoint(microtask_queue));
#endif
isolate_->set_context(*saved_context_);
}
CallDepthScope(const CallDepthScope&) = delete;
CallDepthScope& operator=(const CallDepthScope&) = delete;
private:
#ifdef DEBUG
bool CheckKeptObjectsClearedAfterMicrotaskCheckpoint(
i::MicrotaskQueue* microtask_queue) {
bool did_perform_microtask_checkpoint =
isolate_->thread_local_top()->CallDepthIsZero() && do_callback &&
microtask_queue &&
microtask_queue->microtasks_policy() == MicrotasksPolicy::kAuto &&
!isolate_->is_execution_terminating();
return !did_perform_microtask_checkpoint ||
IsUndefined(isolate_->heap()->weak_refs_keep_during_job(), isolate_);
}
#endif
i::Isolate* const isolate_;
i::Handle<i::Context> saved_context_;
i::Address previous_stack_height_;
friend class i::ThreadLocalTop;
DISALLOW_NEW_AND_DELETE()
};
class V8_NODISCARD InternalEscapableScope : public EscapableHandleScopeBase {
public:
explicit inline InternalEscapableScope(i::Isolate* isolate)
: EscapableHandleScopeBase(reinterpret_cast<v8::Isolate*>(isolate)) {}
/**
* Pushes the value into the previous scope and returns a handle to it.
* Cannot be called twice.
*/
template <class T>
V8_INLINE Local<T> Escape(Local<T> value) {
#ifdef V8_ENABLE_DIRECT_HANDLE
return value;
#else
DCHECK(!value.IsEmpty());
return Local<T>::FromSlot(EscapeSlot(value.slot()));
#endif
}
template <class T>
V8_INLINE MaybeLocal<T> EscapeMaybe(MaybeLocal<T> maybe_value) {
Local<T> value;
if (!maybe_value.ToLocal(&value)) return maybe_value;
return Escape(value);
}
};
template <typename T>
void CopySmiElementsToTypedBuffer(T* dst, uint32_t length,
i::Tagged<i::FixedArray> elements) {
for (uint32_t i = 0; i < length; ++i) {
double value = i::Object::NumberValue(
i::Cast<i::Smi>(elements->get(static_cast<int>(i))));
// TODO(mslekova): Avoid converting back-and-forth when possible, e.g
// avoid int->double->int conversions to boost performance.
dst[i] = i::ConvertDouble<T>(value);
}
}
template <typename T>
void CopyDoubleElementsToTypedBuffer(T* dst, uint32_t length,
i::Tagged<i::FixedDoubleArray> elements) {
for (uint32_t i = 0; i < length; ++i) {
double value = elements->get_scalar(static_cast<int>(i));
// TODO(mslekova): There are certain cases, e.g. double->double, in which
// we could do a memcpy directly.
dst[i] = i::ConvertDouble<T>(value);
}
}
template <CTypeInfo::Identifier type_info_id, typename T>
bool CopyAndConvertArrayToCppBuffer(Local<Array> src, T* dst,
uint32_t max_length) {
static_assert(
std::is_same<T, typename i::CTypeInfoTraits<
CTypeInfo(type_info_id).GetType()>::ctype>::value,
"Type mismatch between the expected CTypeInfo::Type and the destination "
"array");
uint32_t length = src->Length();
if (length == 0) {
// Early return here to avoid a cast error below, as the EmptyFixedArray
// cannot be cast to a FixedDoubleArray.
return true;
}
if (length > max_length) {
return false;
}
i::DisallowGarbageCollection no_gc;
i::Tagged<i::JSArray> obj = *Utils::OpenDirectHandle(*src);
if (i::Object::IterationHasObservableEffects(obj)) {
// The array has a custom iterator.
return false;
}
i::Tagged<i::FixedArrayBase> elements = obj->elements();
switch (obj->GetElementsKind()) {
case i::PACKED_SMI_ELEMENTS:
CopySmiElementsToTypedBuffer(dst, length,
i::Cast<i::FixedArray>(elements));
return true;
case i::PACKED_DOUBLE_ELEMENTS:
CopyDoubleElementsToTypedBuffer(dst, length,
i::Cast<i::FixedDoubleArray>(elements));
return true;
default:
return false;
}
}
// Deprecated; to be removed.
template <const CTypeInfo* type_info, typename T>
inline bool V8_EXPORT TryCopyAndConvertArrayToCppBuffer(Local<Array> src,
T* dst,
uint32_t max_length) {
return CopyAndConvertArrayToCppBuffer<type_info->GetId(), T>(src, dst,
max_length);
}
template <CTypeInfo::Identifier type_info_id, typename T>
inline bool V8_EXPORT TryToCopyAndConvertArrayToCppBuffer(Local<Array> src,
T* dst,
uint32_t max_length) {
return CopyAndConvertArrayToCppBuffer<type_info_id, T>(src, dst, max_length);
}
namespace internal {
void HandleScopeImplementer::EnterContext(Tagged<NativeContext> context) {
entered_contexts_.push_back(context);
}
DirectHandle<NativeContext> HandleScopeImplementer::LastEnteredContext() {
if (entered_contexts_.empty()) return {};
return direct_handle(entered_contexts_.back(), isolate_);
}
} // namespace internal
} // namespace v8
#endif // V8_API_API_INL_H_

19
deps/v8/src/api/api-macros-undef.h vendored Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2021 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.
// PRESUBMIT_INTENTIONALLY_MISSING_INCLUDE_GUARD
#undef LOG_API
#undef ENTER_V8_BASIC
#undef ENTER_V8_HELPER_INTERNAL
#undef PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE
#undef PREPARE_FOR_EXECUTION_WITH_CONTEXT
#undef PREPARE_FOR_EXECUTION
#undef ENTER_V8
#undef ENTER_V8_NO_SCRIPT
#undef ENTER_V8_NO_SCRIPT_NO_EXCEPTION
#undef ENTER_V8_FOR_NEW_CONTEXT
#undef RETURN_ON_FAILED_EXECUTION
#undef RETURN_ON_FAILED_EXECUTION_PRIMITIVE
#undef RETURN_ESCAPED

112
deps/v8/src/api/api-macros.h vendored Normal file
View File

@ -0,0 +1,112 @@
// Copyright 2021 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.
// Note 1: Any file that includes this one should include api-macros-undef.h
// at the bottom.
// Note 2: This file is deliberately missing the include guards (the undeffing
// approach wouldn't work otherwise).
//
// PRESUBMIT_INTENTIONALLY_MISSING_INCLUDE_GUARD
/*
* Most API methods should use one of the three macros:
*
* ENTER_V8, ENTER_V8_NO_SCRIPT, ENTER_V8_NO_SCRIPT_NO_EXCEPTION.
*
* The latter two assume that no script is executed, and no exceptions are
* scheduled in addition (respectively). Creating an exception and
* removing it before returning is ok.
*
* Exceptions should be handled either by invoking one of the
* RETURN_ON_FAILED_EXECUTION* macros.
*
* API methods that are part of the debug interface should use
*
* PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE
*
* in a similar fashion to ENTER_V8.
*/
#define API_RCS_SCOPE(i_isolate, class_name, function_name) \
RCS_SCOPE(i_isolate, \
i::RuntimeCallCounterId::kAPI_##class_name##_##function_name);
#define ENTER_V8_BASIC(i_isolate) \
/* Embedders should never enter V8 after terminating it */ \
DCHECK_IMPLIES(i::v8_flags.strict_termination_checks, \
!i_isolate->is_execution_terminating()); \
i::VMState<v8::OTHER> __state__((i_isolate))
#define ENTER_V8_HELPER_INTERNAL(i_isolate, context, class_name, \
function_name, HandleScopeClass, do_callback) \
DCHECK(!i_isolate->is_execution_terminating()); \
HandleScopeClass handle_scope(i_isolate); \
CallDepthScope<do_callback> call_depth_scope(i_isolate, context); \
API_RCS_SCOPE(i_isolate, class_name, function_name); \
i::VMState<v8::OTHER> __state__((i_isolate)); \
bool has_exception = false
#define PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE(i_isolate, context, \
T) \
DCHECK(!i_isolate->is_execution_terminating()); \
InternalEscapableScope handle_scope(i_isolate); \
CallDepthScope<false> call_depth_scope(i_isolate, context); \
i::VMState<v8::OTHER> __state__((i_isolate)); \
bool has_exception = false
#define PREPARE_FOR_EXECUTION(context, class_name, function_name) \
auto i_isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate()); \
i_isolate->clear_internal_exception(); \
ENTER_V8_HELPER_INTERNAL(i_isolate, context, class_name, function_name, \
InternalEscapableScope, false);
#define ENTER_V8(i_isolate, context, class_name, function_name, \
HandleScopeClass) \
ENTER_V8_HELPER_INTERNAL(i_isolate, context, class_name, function_name, \
HandleScopeClass, true)
#ifdef DEBUG
#define ENTER_V8_NO_SCRIPT(i_isolate, context, class_name, function_name, \
HandleScopeClass) \
ENTER_V8_HELPER_INTERNAL(i_isolate, context, class_name, function_name, \
HandleScopeClass, false); \
i::DisallowJavascriptExecutionDebugOnly __no_script__((i_isolate))
// Lightweight version for APIs that don't require an active context.
#define DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate) \
i::DisallowJavascriptExecutionDebugOnly __no_script__((i_isolate)); \
i::DisallowExceptions __no_exceptions__((i_isolate))
#define ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate) \
i::VMState<v8::OTHER> __state__((i_isolate)); \
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate)
#define ENTER_V8_FOR_NEW_CONTEXT(i_isolate) \
DCHECK_IMPLIES(i::v8_flags.strict_termination_checks, \
!(i_isolate)->is_execution_terminating()); \
i::VMState<v8::OTHER> __state__((i_isolate)); \
i::DisallowExceptions __no_exceptions__((i_isolate))
#else // DEBUG
#define ENTER_V8_NO_SCRIPT(i_isolate, context, class_name, function_name, \
HandleScopeClass) \
ENTER_V8_HELPER_INTERNAL(i_isolate, context, class_name, function_name, \
HandleScopeClass, false)
#define DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate)
#define ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate) \
i::VMState<v8::OTHER> __state__((i_isolate));
#define ENTER_V8_FOR_NEW_CONTEXT(i_isolate) \
i::VMState<v8::OTHER> __state__((i_isolate));
#endif // DEBUG
#define RETURN_ON_FAILED_EXECUTION(T) \
if (has_exception) return MaybeLocal<T>();
#define RETURN_ON_FAILED_EXECUTION_PRIMITIVE(T) \
if (has_exception) return Nothing<T>();
#define RETURN_ESCAPED(value) return handle_scope.Escape(value);

695
deps/v8/src/api/api-natives.cc vendored Normal file
View File

@ -0,0 +1,695 @@
// Copyright 2015 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.
#include "src/api/api-natives.h"
#include "src/api/api-inl.h"
#include "src/common/globals.h"
#include "src/common/message-template.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/protectors-inl.h"
#include "src/heap/heap-inl.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/api-callbacks.h"
#include "src/objects/lookup.h"
#include "src/objects/templates.h"
namespace v8 {
namespace internal {
namespace {
class V8_NODISCARD InvokeScope {
public:
explicit InvokeScope(Isolate* isolate)
: isolate_(isolate), save_context_(isolate) {}
~InvokeScope() {
bool has_exception = isolate_->has_exception();
if (has_exception) {
isolate_->ReportPendingMessages();
} else {
isolate_->clear_pending_message();
}
}
private:
Isolate* isolate_;
SaveContext save_context_;
};
MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
DirectHandle<ObjectTemplateInfo> data,
DirectHandle<JSReceiver> new_target,
bool is_prototype);
MaybeHandle<JSFunction> InstantiateFunction(
Isolate* isolate, DirectHandle<NativeContext> native_context,
DirectHandle<FunctionTemplateInfo> data,
MaybeDirectHandle<Name> maybe_name = {});
MaybeHandle<JSFunction> InstantiateFunction(
Isolate* isolate, DirectHandle<FunctionTemplateInfo> data,
MaybeDirectHandle<Name> maybe_name = {}) {
return InstantiateFunction(isolate, isolate->native_context(), data,
maybe_name);
}
MaybeDirectHandle<Object> Instantiate(Isolate* isolate,
DirectHandle<Object> data,
MaybeDirectHandle<Name> maybe_name = {}) {
if (IsFunctionTemplateInfo(*data)) {
return InstantiateFunction(isolate, Cast<FunctionTemplateInfo>(data),
maybe_name);
} else if (IsObjectTemplateInfo(*data)) {
return InstantiateObject(isolate, Cast<ObjectTemplateInfo>(data), {},
false);
} else {
return data;
}
}
MaybeDirectHandle<Object> DefineAccessorProperty(
Isolate* isolate, DirectHandle<JSObject> object, DirectHandle<Name> name,
DirectHandle<Object> getter, DirectHandle<Object> setter,
PropertyAttributes attributes) {
DCHECK_IMPLIES(IsFunctionTemplateInfo(*getter),
Cast<FunctionTemplateInfo>(*getter)->is_cacheable());
DCHECK_IMPLIES(IsFunctionTemplateInfo(*setter),
Cast<FunctionTemplateInfo>(*setter)->is_cacheable());
if (IsFunctionTemplateInfo(*getter) &&
Cast<FunctionTemplateInfo>(*getter)->BreakAtEntry(isolate)) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, getter,
InstantiateFunction(isolate, Cast<FunctionTemplateInfo>(getter)));
DirectHandle<Code> trampoline = BUILTIN_CODE(isolate, DebugBreakTrampoline);
Cast<JSFunction>(getter)->UpdateCode(*trampoline);
}
if (IsFunctionTemplateInfo(*setter) &&
Cast<FunctionTemplateInfo>(*setter)->BreakAtEntry(isolate)) {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, setter,
InstantiateFunction(isolate, Cast<FunctionTemplateInfo>(setter)));
DirectHandle<Code> trampoline = BUILTIN_CODE(isolate, DebugBreakTrampoline);
Cast<JSFunction>(setter)->UpdateCode(*trampoline);
}
RETURN_ON_EXCEPTION(isolate, JSObject::DefineOwnAccessorIgnoreAttributes(
object, name, getter, setter, attributes));
return object;
}
MaybeDirectHandle<Object> DefineDataProperty(Isolate* isolate,
DirectHandle<JSObject> object,
DirectHandle<Name> name,
DirectHandle<Object> prop_data,
PropertyAttributes attributes) {
DirectHandle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
Instantiate(isolate, prop_data, name));
PropertyKey key(isolate, name);
LookupIterator it(isolate, object, key, LookupIterator::OWN_SKIP_INTERCEPTOR);
#ifdef DEBUG
Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
DCHECK(maybe.IsJust());
if (it.IsFound()) {
THROW_NEW_ERROR(
isolate,
NewTypeError(MessageTemplate::kDuplicateTemplateProperty, name));
}
#endif
MAYBE_RETURN_NULL(Object::AddDataProperty(&it, value, attributes,
Just(ShouldThrow::kThrowOnError),
StoreOrigin::kNamed));
return value;
}
void DisableAccessChecks(Isolate* isolate, DirectHandle<JSObject> object) {
DirectHandle<Map> old_map(object->map(), isolate);
// Copy map so it won't interfere constructor's initial map.
DirectHandle<Map> new_map =
Map::Copy(isolate, old_map, "DisableAccessChecks");
new_map->set_is_access_check_needed(false);
JSObject::MigrateToMap(isolate, object, new_map);
}
void EnableAccessChecks(Isolate* isolate, DirectHandle<JSObject> object) {
DirectHandle<Map> old_map(object->map(), isolate);
// Copy map so it won't interfere constructor's initial map.
DirectHandle<Map> new_map = Map::Copy(isolate, old_map, "EnableAccessChecks");
new_map->set_is_access_check_needed(true);
new_map->set_may_have_interesting_properties(true);
JSObject::MigrateToMap(isolate, object, new_map);
}
class V8_NODISCARD AccessCheckDisableScope {
public:
AccessCheckDisableScope(Isolate* isolate, DirectHandle<JSObject> obj)
: isolate_(isolate),
disabled_(obj->map()->is_access_check_needed()),
obj_(obj) {
if (disabled_) {
DisableAccessChecks(isolate_, obj_);
}
}
~AccessCheckDisableScope() {
if (disabled_) {
EnableAccessChecks(isolate_, obj_);
}
}
private:
Isolate* isolate_;
const bool disabled_;
DirectHandle<JSObject> obj_;
};
Tagged<Object> GetIntrinsic(Isolate* isolate, v8::Intrinsic intrinsic) {
DirectHandle<Context> native_context = isolate->native_context();
DCHECK(!native_context.is_null());
switch (intrinsic) {
#define GET_INTRINSIC_VALUE(name, iname) \
case v8::k##name: \
return native_context->iname();
V8_INTRINSICS_LIST(GET_INTRINSIC_VALUE)
#undef GET_INTRINSIC_VALUE
}
return Tagged<Object>();
}
template <typename TemplateInfoT>
MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj,
DirectHandle<TemplateInfoT> data) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kConfigureInstance);
HandleScope scope(isolate);
// Disable access checks while instantiating the object.
AccessCheckDisableScope access_check_scope(isolate, obj);
// Walk the inheritance chain and copy all accessors to current object.
int max_number_of_properties = 0;
Tagged<TemplateInfoT> info = *data;
while (!info.is_null()) {
Tagged<Object> props = info->property_accessors();
if (!IsUndefined(props, isolate)) {
max_number_of_properties += Cast<ArrayList>(props)->length();
}
info = info->GetParent(isolate);
}
if (max_number_of_properties > 0) {
int valid_descriptors = 0;
// Use a temporary FixedArray to accumulate unique accessors.
DirectHandle<FixedArray> array =
isolate->factory()->NewFixedArray(max_number_of_properties);
// TODO(leszeks): Avoid creating unnecessary handles for cases where we
// don't need to append anything.
for (DirectHandle<TemplateInfoT> temp(*data, isolate); !(*temp).is_null();
temp = direct_handle(temp->GetParent(isolate), isolate)) {
// Accumulate accessors.
Tagged<Object> maybe_properties = temp->property_accessors();
if (!IsUndefined(maybe_properties, isolate)) {
valid_descriptors = AccessorInfo::AppendUnique(
isolate, direct_handle(maybe_properties, isolate), array,
valid_descriptors);
}
}
// Install accumulated accessors.
for (int i = 0; i < valid_descriptors; i++) {
DirectHandle<AccessorInfo> accessor(Cast<AccessorInfo>(array->get(i)),
isolate);
DirectHandle<Name> name(Cast<Name>(accessor->name()), isolate);
JSObject::SetAccessor(obj, name, accessor,
accessor->initial_property_attributes())
.Assert();
}
}
Tagged<Object> maybe_property_list = data->property_list();
if (IsUndefined(maybe_property_list, isolate)) return obj;
DirectHandle<ArrayList> properties(Cast<ArrayList>(maybe_property_list),
isolate);
if (properties->length() == 0) return obj;
int i = 0;
for (int c = 0; c < data->number_of_properties(); c++) {
auto name = direct_handle(Cast<Name>(properties->get(i++)), isolate);
Tagged<Object> bit = properties->get(i++);
if (IsSmi(bit)) {
PropertyDetails details(Cast<Smi>(bit));
PropertyAttributes attributes = details.attributes();
PropertyKind kind = details.kind();
if (kind == PropertyKind::kData) {
auto prop_data = handle(properties->get(i++), isolate);
RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name,
prop_data, attributes));
} else {
auto getter = direct_handle(properties->get(i++), isolate);
auto setter = direct_handle(properties->get(i++), isolate);
RETURN_ON_EXCEPTION(
isolate, DefineAccessorProperty(isolate, obj, name, getter, setter,
attributes));
}
} else {
// Intrinsic data property --- Get appropriate value from the current
// context.
PropertyDetails details(Cast<Smi>(properties->get(i++)));
PropertyAttributes attributes = details.attributes();
DCHECK_EQ(PropertyKind::kData, details.kind());
v8::Intrinsic intrinsic =
static_cast<v8::Intrinsic>(Smi::ToInt(properties->get(i++)));
auto prop_data = handle(GetIntrinsic(isolate, intrinsic), isolate);
RETURN_ON_EXCEPTION(isolate, DefineDataProperty(isolate, obj, name,
prop_data, attributes));
}
}
return obj;
}
bool IsSimpleInstantiation(Isolate* isolate, Tagged<ObjectTemplateInfo> info,
Tagged<JSReceiver> new_target) {
DisallowGarbageCollection no_gc;
if (!IsJSFunction(new_target)) return false;
Tagged<JSFunction> fun = Cast<JSFunction>(new_target);
if (!fun->shared()->IsApiFunction()) return false;
if (fun->shared()->api_func_data() != info->constructor()) return false;
if (info->immutable_proto()) return false;
return fun->native_context() == isolate->raw_native_context();
}
MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
DirectHandle<ObjectTemplateInfo> info,
DirectHandle<JSReceiver> new_target,
bool is_prototype) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kInstantiateObject);
DirectHandle<JSFunction> constructor;
bool should_cache = info->is_cacheable();
if (!new_target.is_null()) {
if (IsSimpleInstantiation(isolate, *info, *new_target)) {
constructor = Cast<JSFunction>(new_target);
} else {
// Disable caching for subclass instantiation.
should_cache = false;
}
}
// Fast path.
Handle<JSObject> result;
if (should_cache) {
if (TemplateInfo::ProbeInstantiationsCache<JSObject>(
isolate, isolate->native_context(), info,
TemplateInfo::CachingMode::kLimited)
.ToHandle(&result)) {
return isolate->factory()->CopyJSObject(result);
}
}
if (constructor.is_null()) {
Tagged<Object> maybe_constructor_info = info->constructor();
if (IsUndefined(maybe_constructor_info, isolate)) {
constructor = isolate->object_function();
} else {
// Enter a new scope. Recursion could otherwise create a lot of handles.
HandleScope scope(isolate);
DirectHandle<FunctionTemplateInfo> cons_templ(
Cast<FunctionTemplateInfo>(maybe_constructor_info), isolate);
DirectHandle<JSFunction> tmp_constructor;
ASSIGN_RETURN_ON_EXCEPTION(isolate, tmp_constructor,
InstantiateFunction(isolate, cons_templ));
constructor = scope.CloseAndEscape(tmp_constructor);
}
if (new_target.is_null()) new_target = constructor;
}
const auto new_js_object_type =
constructor->has_initial_map() &&
IsJSApiWrapperObject(constructor->initial_map())
? NewJSObjectType::kAPIWrapper
: NewJSObjectType::kNoAPIWrapper;
Handle<JSObject> object;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, object,
JSObject::New(constructor, new_target, {}, new_js_object_type));
if (is_prototype) JSObject::OptimizeAsPrototype(object);
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
ConfigureInstance(isolate, object, info));
if (info->immutable_proto()) {
JSObject::SetImmutableProto(isolate, object);
}
if (!is_prototype) {
// Keep prototypes in slow-mode. Let them be lazily turned fast later on.
// TODO(dcarney): is this necessary?
JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject");
// Don't cache prototypes.
if (should_cache) {
TemplateInfo::CacheTemplateInstantiation(
isolate, isolate->native_context(), info,
TemplateInfo::CachingMode::kLimited, result);
result = isolate->factory()->CopyJSObject(result);
}
}
return result;
}
namespace {
MaybeDirectHandle<Object> GetInstancePrototype(
Isolate* isolate, DirectHandle<Object> function_template) {
// Enter a new scope. Recursion could otherwise create a lot of handles.
HandleScope scope(isolate);
DirectHandle<JSFunction> parent_instance;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, parent_instance,
InstantiateFunction(isolate,
Cast<FunctionTemplateInfo>(function_template)));
Handle<Object> instance_prototype;
// TODO(cbruni): decide what to do here.
ASSIGN_RETURN_ON_EXCEPTION(
isolate, instance_prototype,
JSObject::GetProperty(isolate, parent_instance,
isolate->factory()->prototype_string()));
return scope.CloseAndEscape(instance_prototype);
}
} // namespace
MaybeHandle<JSFunction> InstantiateFunction(
Isolate* isolate, DirectHandle<NativeContext> native_context,
DirectHandle<FunctionTemplateInfo> info,
MaybeDirectHandle<Name> maybe_name) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kInstantiateFunction);
bool should_cache = info->is_cacheable();
if (should_cache) {
Handle<JSObject> result;
if (TemplateInfo::ProbeInstantiationsCache<JSObject>(
isolate, native_context, info,
TemplateInfo::CachingMode::kUnlimited)
.ToHandle(&result)) {
return Cast<JSFunction>(result);
}
}
DirectHandle<Object> prototype;
if (!info->remove_prototype()) {
DirectHandle<Object> prototype_templ(info->GetPrototypeTemplate(), isolate);
if (IsUndefined(*prototype_templ, isolate)) {
DirectHandle<Object> protoype_provider_templ(
info->GetPrototypeProviderTemplate(), isolate);
if (IsUndefined(*protoype_provider_templ, isolate)) {
prototype = isolate->factory()->NewJSObject(
direct_handle(native_context->object_function(), isolate));
} else {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
GetInstancePrototype(isolate, protoype_provider_templ));
}
} else {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
InstantiateObject(isolate, Cast<ObjectTemplateInfo>(prototype_templ),
DirectHandle<JSReceiver>(), true));
}
DirectHandle<Object> parent(info->GetParentTemplate(), isolate);
if (!IsUndefined(*parent, isolate)) {
DirectHandle<Object> parent_prototype;
ASSIGN_RETURN_ON_EXCEPTION(isolate, parent_prototype,
GetInstancePrototype(isolate, parent));
DirectHandle<JSPrototype> checked_parent_prototype;
CHECK(TryCast(parent_prototype, &checked_parent_prototype));
JSObject::ForceSetPrototype(isolate, Cast<JSObject>(prototype),
checked_parent_prototype);
}
}
InstanceType function_type = JS_SPECIAL_API_OBJECT_TYPE;
if (!info->needs_access_check() &&
IsUndefined(info->GetNamedPropertyHandler(), isolate) &&
IsUndefined(info->GetIndexedPropertyHandler(), isolate)) {
function_type = v8_flags.experimental_embedder_instance_types
? info->GetInstanceType()
: JS_API_OBJECT_TYPE;
DCHECK(InstanceTypeChecker::IsJSApiObject(function_type));
}
Handle<JSFunction> function = ApiNatives::CreateApiFunction(
isolate, native_context, info, prototype, function_type, maybe_name);
if (should_cache) {
// Cache the function.
TemplateInfo::CacheTemplateInstantiation(
isolate, native_context, info, TemplateInfo::CachingMode::kUnlimited,
function);
}
MaybeDirectHandle<JSObject> result =
ConfigureInstance(isolate, function, info);
if (result.is_null()) {
// Uncache on error.
TemplateInfo::UncacheTemplateInstantiation(
isolate, native_context, info, TemplateInfo::CachingMode::kUnlimited);
return {};
}
info->set_published(true);
return function;
}
void AddPropertyToPropertyList(Isolate* isolate,
DirectHandle<TemplateInfoWithProperties> info,
base::Vector<DirectHandle<Object>> data) {
Tagged<Object> maybe_list = info->property_list();
DirectHandle<ArrayList> list;
if (IsUndefined(maybe_list, isolate)) {
list = ArrayList::New(isolate, static_cast<int>(data.size()),
AllocationType::kOld);
} else {
list = direct_handle(Cast<ArrayList>(maybe_list), isolate);
}
info->set_number_of_properties(info->number_of_properties() + 1);
for (DirectHandle<Object> value : data) {
if (value.is_null())
value = Cast<Object>(isolate->factory()->undefined_value());
list = ArrayList::Add(isolate, list, value);
}
info->set_property_list(*list);
}
} // namespace
// static
DirectHandle<FunctionTemplateInfo>
ApiNatives::CreateAccessorFunctionTemplateInfo(
Isolate* i_isolate, FunctionCallback callback, int length,
SideEffectType side_effect_type) {
// TODO(v8:5962): move FunctionTemplateNew() from api.cc here.
auto isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
Local<FunctionTemplate> func_template = FunctionTemplate::New(
isolate, callback, v8::Local<Value>{}, v8::Local<v8::Signature>{}, length,
v8::ConstructorBehavior::kThrow, side_effect_type);
return Utils::OpenDirectHandle(*func_template);
}
MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
Isolate* isolate, DirectHandle<NativeContext> native_context,
DirectHandle<FunctionTemplateInfo> data,
MaybeDirectHandle<Name> maybe_name) {
InvokeScope invoke_scope(isolate);
return ::v8::internal::InstantiateFunction(isolate, native_context, data,
maybe_name);
}
MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
Isolate* isolate, DirectHandle<FunctionTemplateInfo> data,
MaybeDirectHandle<Name> maybe_name) {
InvokeScope invoke_scope(isolate);
return ::v8::internal::InstantiateFunction(isolate, data, maybe_name);
}
MaybeHandle<JSObject> ApiNatives::InstantiateObject(
Isolate* isolate, DirectHandle<ObjectTemplateInfo> data,
DirectHandle<JSReceiver> new_target) {
InvokeScope invoke_scope(isolate);
return ::v8::internal::InstantiateObject(isolate, data, new_target, false);
}
MaybeHandle<JSObject> ApiNatives::InstantiateRemoteObject(
DirectHandle<ObjectTemplateInfo> data) {
Isolate* isolate = data->GetIsolate();
InvokeScope invoke_scope(isolate);
DirectHandle<FunctionTemplateInfo> constructor(
Cast<FunctionTemplateInfo>(data->constructor()), isolate);
DirectHandle<Map> object_map = isolate->factory()->NewContextlessMap(
JS_SPECIAL_API_OBJECT_TYPE,
JSSpecialObject::kHeaderSize +
data->embedder_field_count() * kEmbedderDataSlotSize,
TERMINAL_FAST_ELEMENTS_KIND);
object_map->SetConstructor(*constructor);
object_map->set_is_access_check_needed(true);
object_map->set_may_have_interesting_properties(true);
Handle<JSObject> object = isolate->factory()->NewJSObjectFromMap(
object_map, AllocationType::kYoung, DirectHandle<AllocationSite>::null(),
NewJSObjectType::kAPIWrapper);
JSObject::ForceSetPrototype(isolate, object,
isolate->factory()->null_value());
return object;
}
void ApiNatives::AddDataProperty(Isolate* isolate,
DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<Name> name,
DirectHandle<Object> value,
PropertyAttributes attributes) {
PropertyDetails details(PropertyKind::kData, attributes,
PropertyConstness::kMutable);
DirectHandle<Object> data[] = {name, direct_handle(details.AsSmi(), isolate),
value};
AddPropertyToPropertyList(isolate, info, base::VectorOf(data));
}
void ApiNatives::AddDataProperty(Isolate* isolate,
DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<Name> name,
v8::Intrinsic intrinsic,
PropertyAttributes attributes) {
auto value = direct_handle(Smi::FromInt(intrinsic), isolate);
auto intrinsic_marker = isolate->factory()->true_value();
PropertyDetails details(PropertyKind::kData, attributes,
PropertyConstness::kMutable);
DirectHandle<Object> data[] = {
name, intrinsic_marker, direct_handle(details.AsSmi(), isolate), value};
AddPropertyToPropertyList(isolate, info, base::VectorOf(data));
}
void ApiNatives::AddAccessorProperty(
Isolate* isolate, DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<Name> name, DirectHandle<FunctionTemplateInfo> getter,
DirectHandle<FunctionTemplateInfo> setter, PropertyAttributes attributes) {
if (!getter.is_null()) getter->set_published(true);
if (!setter.is_null()) setter->set_published(true);
PropertyDetails details(PropertyKind::kAccessor, attributes,
PropertyConstness::kMutable);
DirectHandle<Object> data[] = {name, direct_handle(details.AsSmi(), isolate),
getter, setter};
AddPropertyToPropertyList(isolate, info, base::VectorOf(data));
}
void ApiNatives::AddNativeDataProperty(
Isolate* isolate, DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<AccessorInfo> property) {
Tagged<Object> maybe_list = info->property_accessors();
DirectHandle<ArrayList> list;
if (IsUndefined(maybe_list, isolate)) {
list = ArrayList::New(isolate, 1, AllocationType::kOld);
} else {
list = direct_handle(Cast<ArrayList>(maybe_list), isolate);
}
list = ArrayList::Add(isolate, list, property);
info->set_property_accessors(*list);
}
Handle<JSFunction> ApiNatives::CreateApiFunction(
Isolate* isolate, DirectHandle<NativeContext> native_context,
DirectHandle<FunctionTemplateInfo> obj, DirectHandle<Object> prototype,
InstanceType type, MaybeDirectHandle<Name> maybe_name) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kCreateApiFunction);
DirectHandle<SharedFunctionInfo> shared =
FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(isolate, obj,
maybe_name);
// To simplify things, API functions always have shared name.
DCHECK(shared->HasSharedName());
Handle<JSFunction> result =
Factory::JSFunctionBuilder{isolate, shared, native_context}.Build();
if (obj->remove_prototype()) {
DCHECK(prototype.is_null());
DCHECK(result->shared()->IsApiFunction());
DCHECK(!IsConstructor(*result));
DCHECK(!result->has_prototype_slot());
return result;
}
// Down from here is only valid for API functions that can be used as a
// constructor (don't set the "remove prototype" flag).
DCHECK(result->has_prototype_slot());
if (obj->read_only_prototype()) {
result->set_map(isolate,
*isolate->sloppy_function_with_readonly_prototype_map());
}
if (IsTheHole(*prototype, isolate)) {
prototype = isolate->factory()->NewFunctionPrototype(result);
} else if (IsUndefined(obj->GetPrototypeProviderTemplate(), isolate)) {
JSObject::AddProperty(isolate, Cast<JSObject>(prototype),
isolate->factory()->constructor_string(), result,
DONT_ENUM);
}
int embedder_field_count = 0;
bool immutable_proto = false;
if (!IsUndefined(obj->GetInstanceTemplate(), isolate)) {
DirectHandle<ObjectTemplateInfo> GetInstanceTemplate(
Cast<ObjectTemplateInfo>(obj->GetInstanceTemplate()), isolate);
embedder_field_count = GetInstanceTemplate->embedder_field_count();
immutable_proto = GetInstanceTemplate->immutable_proto();
}
// JSFunction requires information about the prototype slot.
DCHECK(!InstanceTypeChecker::IsJSFunction(type));
int instance_size = JSObject::GetHeaderSize(type) +
kEmbedderDataSlotSize * embedder_field_count;
DirectHandle<Map> map = isolate->factory()->NewContextfulMap(
native_context, type, instance_size, TERMINAL_FAST_ELEMENTS_KIND);
// Mark as undetectable if needed.
if (obj->undetectable()) {
// We only allow callable undetectable receivers here, since this whole
// undetectable business is only to support document.all, which is both
// undetectable and callable. If we ever see the need to have an object
// that is undetectable but not callable, we need to update the types.h
// to allow encoding this.
CHECK(!IsUndefined(obj->GetInstanceCallHandler(), isolate));
if (Protectors::IsNoUndetectableObjectsIntact(isolate)) {
Protectors::InvalidateNoUndetectableObjects(isolate);
}
map->set_is_undetectable(true);
}
// Mark as needs_access_check if needed.
if (obj->needs_access_check()) {
map->set_is_access_check_needed(true);
map->set_may_have_interesting_properties(true);
}
// Set interceptor information in the map.
if (!IsUndefined(obj->GetNamedPropertyHandler(), isolate)) {
map->set_has_named_interceptor(true);
map->set_may_have_interesting_properties(true);
}
if (!IsUndefined(obj->GetIndexedPropertyHandler(), isolate)) {
map->set_has_indexed_interceptor(true);
}
// Mark instance as callable in the map.
if (!IsUndefined(obj->GetInstanceCallHandler(), isolate)) {
map->set_is_callable(true);
map->set_is_constructor(!obj->undetectable());
}
if (immutable_proto) map->set_is_immutable_proto(true);
JSFunction::SetInitialMap(isolate, result, map, Cast<JSObject>(prototype));
return result;
}
} // namespace internal
} // namespace v8

79
deps/v8/src/api/api-natives.h vendored Normal file
View File

@ -0,0 +1,79 @@
// Copyright 2015 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_API_API_NATIVES_H_
#define V8_API_API_NATIVES_H_
#include "include/v8-template.h"
#include "src/handles/handles.h"
#include "src/handles/maybe-handles.h"
#include "src/objects/objects.h"
#include "src/objects/property-details.h"
namespace v8 {
namespace internal {
// Forward declarations.
enum InstanceType : uint16_t;
class ObjectTemplateInfo;
class TemplateInfo;
class ApiNatives {
public:
static const int kInitialFunctionCacheSize = 256;
// A convenient internal wrapper around FunctionTemplate::New() for creating
// getter/setter callback function templates.
static DirectHandle<FunctionTemplateInfo> CreateAccessorFunctionTemplateInfo(
Isolate* isolate, FunctionCallback callback, int length,
v8::SideEffectType side_effect_type);
V8_WARN_UNUSED_RESULT static MaybeHandle<JSFunction> InstantiateFunction(
Isolate* isolate, DirectHandle<NativeContext> native_context,
DirectHandle<FunctionTemplateInfo> data,
MaybeDirectHandle<Name> maybe_name = {});
V8_WARN_UNUSED_RESULT static MaybeHandle<JSFunction> InstantiateFunction(
Isolate* isolate, DirectHandle<FunctionTemplateInfo> data,
MaybeDirectHandle<Name> maybe_name = {});
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> InstantiateObject(
Isolate* isolate, DirectHandle<ObjectTemplateInfo> data,
DirectHandle<JSReceiver> new_target = {});
V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> InstantiateRemoteObject(
DirectHandle<ObjectTemplateInfo> data);
static Handle<JSFunction> CreateApiFunction(
Isolate* isolate, DirectHandle<NativeContext> native_context,
DirectHandle<FunctionTemplateInfo> obj, DirectHandle<Object> prototype,
InstanceType type, MaybeDirectHandle<Name> name = {});
static void AddDataProperty(Isolate* isolate,
DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<Name> name,
DirectHandle<Object> value,
PropertyAttributes attributes);
static void AddDataProperty(Isolate* isolate,
DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<Name> name, v8::Intrinsic intrinsic,
PropertyAttributes attributes);
static void AddAccessorProperty(Isolate* isolate,
DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<Name> name,
DirectHandle<FunctionTemplateInfo> getter,
DirectHandle<FunctionTemplateInfo> setter,
PropertyAttributes attributes);
static void AddNativeDataProperty(
Isolate* isolate, DirectHandle<TemplateInfoWithProperties> info,
DirectHandle<AccessorInfo> property);
};
} // namespace internal
} // namespace v8
#endif // V8_API_API_NATIVES_H_

12499
deps/v8/src/api/api.cc vendored Normal file

File diff suppressed because it is too large Load Diff

554
deps/v8/src/api/api.h vendored Normal file
View File

@ -0,0 +1,554 @@
// Copyright 2012 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_API_API_H_
#define V8_API_API_H_
#include <memory>
#include "include/v8-container.h"
#include "include/v8-external.h"
#include "include/v8-function-callback.h"
#include "include/v8-proxy.h"
#include "include/v8-typed-array.h"
#include "include/v8-wasm.h"
#include "src/base/contextual.h"
#include "src/execution/isolate.h"
#include "src/objects/bigint.h"
#include "src/objects/contexts.h"
#include "src/objects/js-array-buffer.h"
#include "src/objects/js-collection.h"
#include "src/objects/js-generator.h"
#include "src/objects/js-promise.h"
#include "src/objects/js-proxy.h"
#include "src/objects/objects.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/source-text-module.h"
#include "src/objects/templates.h"
#include "src/utils/detachable-vector.h"
namespace v8 {
class DictionaryTemplate;
class Extension;
class Signature;
class Template;
namespace internal {
class JSArrayBufferView;
class JSFinalizationRegistry;
} // namespace internal
namespace debug {
class AccessorPair;
class GeneratorObject;
class ScriptSource;
class Script;
class EphemeronTable;
} // namespace debug
template <typename T, internal::ExternalPointerTag tag>
inline T ToCData(i::Isolate* isolate,
v8::internal::Tagged<v8::internal::Object> obj);
template <internal::ExternalPointerTag tag>
inline v8::internal::Address ToCData(
v8::internal::Isolate* isolate,
v8::internal::Tagged<v8::internal::Object> obj);
template <internal::ExternalPointerTag tag, typename T>
inline v8::internal::DirectHandle<
v8::internal::UnionOf<v8::internal::Smi, v8::internal::Foreign>>
FromCData(v8::internal::Isolate* isolate, T obj);
template <internal::ExternalPointerTag tag>
inline v8::internal::DirectHandle<
v8::internal::UnionOf<v8::internal::Smi, v8::internal::Foreign>>
FromCData(v8::internal::Isolate* isolate, v8::internal::Address obj);
class ApiFunction {
public:
explicit ApiFunction(v8::internal::Address addr) : addr_(addr) {}
v8::internal::Address address() { return addr_; }
private:
v8::internal::Address addr_;
};
class RegisteredExtension {
public:
static void Register(std::unique_ptr<Extension>);
static void UnregisterAll();
Extension* extension() const { return extension_.get(); }
RegisteredExtension* next() const { return next_; }
static RegisteredExtension* first_extension() { return first_extension_; }
private:
explicit RegisteredExtension(Extension*);
explicit RegisteredExtension(std::unique_ptr<Extension>);
std::unique_ptr<Extension> extension_;
RegisteredExtension* next_ = nullptr;
static RegisteredExtension* first_extension_;
};
#define TO_LOCAL_LIST(V) \
V(ToLocal, AccessorPair, debug::AccessorPair) \
V(ToLocal, NativeContext, Context) \
V(ToLocal, Object, Value) \
V(ToLocal, Module, Module) \
V(ToLocal, Name, Name) \
V(ToLocal, String, String) \
V(ToLocal, Symbol, Symbol) \
V(ToLocal, JSRegExp, RegExp) \
V(ToLocal, JSReceiver, Object) \
V(ToLocal, JSObject, Object) \
V(ToLocal, JSFunction, Function) \
V(ToLocal, JSArray, Array) \
V(ToLocal, JSMap, Map) \
V(ToLocal, JSSet, Set) \
V(ToLocal, JSProxy, Proxy) \
V(ToLocal, JSArrayBuffer, ArrayBuffer) \
V(ToLocal, JSArrayBufferView, ArrayBufferView) \
V(ToLocal, JSDataView, DataView) \
V(ToLocal, JSRabGsabDataView, DataView) \
V(ToLocal, JSTypedArray, TypedArray) \
V(ToLocalShared, JSArrayBuffer, SharedArrayBuffer) \
V(ToLocal, FunctionTemplateInfo, FunctionTemplate) \
V(ToLocal, ObjectTemplateInfo, ObjectTemplate) \
V(ToLocal, DictionaryTemplateInfo, DictionaryTemplate) \
V(SignatureToLocal, FunctionTemplateInfo, Signature) \
V(MessageToLocal, Object, Message) \
V(PromiseToLocal, JSObject, Promise) \
V(StackTraceToLocal, StackTraceInfo, StackTrace) \
V(StackFrameToLocal, StackFrameInfo, StackFrame) \
V(NumberToLocal, Object, Number) \
V(IntegerToLocal, Object, Integer) \
V(Uint32ToLocal, Object, Uint32) \
V(ToLocal, BigInt, BigInt) \
V(ExternalToLocal, JSObject, External) \
V(CallableToLocal, JSReceiver, Function) \
V(ToLocalPrimitive, Object, Primitive) \
V(FixedArrayToLocal, FixedArray, FixedArray) \
V(PrimitiveArrayToLocal, FixedArray, PrimitiveArray) \
V(ToLocal, ScriptOrModule, ScriptOrModule) \
IF_WASM(V, ToLocal, WasmMemoryMapDescriptor, WasmMemoryMapDescriptor) \
IF_WASM(V, ToLocal, WasmModuleObject, WasmModuleObject)
#define TO_LOCAL_NAME_LIST(V) \
V(ToLocal) \
V(ToLocalShared) \
V(SignatureToLocal) \
V(MessageToLocal) \
V(PromiseToLocal) \
V(StackTraceToLocal) \
V(StackFrameToLocal) \
V(NumberToLocal) \
V(IntegerToLocal) \
V(Uint32ToLocal) \
V(ExternalToLocal) \
V(CallableToLocal) \
V(ToLocalPrimitive) \
V(FixedArrayToLocal) \
V(PrimitiveArrayToLocal)
#define OPEN_HANDLE_LIST(V) \
V(Template, TemplateInfoWithProperties) \
V(FunctionTemplate, FunctionTemplateInfo) \
V(ObjectTemplate, ObjectTemplateInfo) \
V(DictionaryTemplate, DictionaryTemplateInfo) \
V(Signature, FunctionTemplateInfo) \
V(Data, Object) \
V(Number, Number) \
V(RegExp, JSRegExp) \
V(Object, JSReceiver) \
V(Array, JSArray) \
V(Map, JSMap) \
V(Set, JSSet) \
V(ArrayBuffer, JSArrayBuffer) \
V(ArrayBufferView, JSArrayBufferView) \
V(TypedArray, JSTypedArray) \
V(Uint8Array, JSTypedArray) \
V(Uint8ClampedArray, JSTypedArray) \
V(Int8Array, JSTypedArray) \
V(Uint16Array, JSTypedArray) \
V(Int16Array, JSTypedArray) \
V(Uint32Array, JSTypedArray) \
V(Int32Array, JSTypedArray) \
V(Float16Array, JSTypedArray) \
V(Float32Array, JSTypedArray) \
V(Float64Array, JSTypedArray) \
V(DataView, JSDataViewOrRabGsabDataView) \
V(SharedArrayBuffer, JSArrayBuffer) \
V(Name, Name) \
V(String, String) \
V(Symbol, Symbol) \
V(Script, JSFunction) \
V(UnboundModuleScript, SharedFunctionInfo) \
V(UnboundScript, SharedFunctionInfo) \
V(Module, Module) \
V(Function, JSReceiver) \
V(CompileHintsCollector, Script) \
V(Message, JSMessageObject) \
V(Context, NativeContext) \
V(External, Object) \
V(StackTrace, StackTraceInfo) \
V(StackFrame, StackFrameInfo) \
V(Proxy, JSProxy) \
V(debug::GeneratorObject, JSGeneratorObject) \
V(debug::ScriptSource, HeapObject) \
V(debug::Script, Script) \
V(debug::EphemeronTable, EphemeronHashTable) \
V(debug::AccessorPair, AccessorPair) \
V(Promise, JSPromise) \
V(Primitive, Object) \
V(PrimitiveArray, FixedArray) \
V(BigInt, BigInt) \
V(ScriptOrModule, ScriptOrModule) \
V(FixedArray, FixedArray) \
V(ModuleRequest, ModuleRequest) \
IF_WASM(V, WasmMemoryMapDescriptor, WasmMemoryMapDescriptor) \
IF_WASM(V, WasmMemoryObject, WasmMemoryObject)
class Utils {
public:
static V8_INLINE bool ApiCheck(bool condition, const char* location,
const char* message) {
if (V8_UNLIKELY(!condition)) {
Utils::ReportApiFailure(location, message);
}
return condition;
}
static void ReportOOMFailure(v8::internal::Isolate* isolate,
const char* location, const OOMDetails& details);
// TODO(42203211): It would be nice if we could keep only a version with
// direct handles. But the implicit conversion from handles to direct handles
// combined with the heterogeneous copy constructor for direct handles make
// this ambiguous.
// TODO(42203211): Use C++20 concepts instead of the enable_if trait, when
// they are fully supported in V8.
#define DECLARE_TO_LOCAL(Name) \
template <template <typename> typename HandleType, typename T, \
typename = std::enable_if_t<std::is_convertible_v< \
HandleType<T>, v8::internal::DirectHandle<T>>>> \
static inline auto Name(HandleType<T> obj);
TO_LOCAL_NAME_LIST(DECLARE_TO_LOCAL)
#define DECLARE_TO_LOCAL_TYPED_ARRAY(Type, typeName, TYPE, ctype) \
static inline Local<v8::Type##Array> ToLocal##Type##Array( \
v8::internal::DirectHandle<v8::internal::JSTypedArray> obj);
TYPED_ARRAYS(DECLARE_TO_LOCAL_TYPED_ARRAY)
#define DECLARE_OPEN_HANDLE(From, To) \
static inline v8::internal::Handle<v8::internal::To> OpenHandle( \
const From* that, bool allow_empty_handle = false); \
static inline v8::internal::DirectHandle<v8::internal::To> OpenDirectHandle( \
const From* that, bool allow_empty_handle = false); \
static inline v8::internal::IndirectHandle<v8::internal::To> \
OpenIndirectHandle(const From* that, bool allow_empty_handle = false);
OPEN_HANDLE_LIST(DECLARE_OPEN_HANDLE)
#undef DECLARE_OPEN_HANDLE
#undef DECLARE_TO_LOCAL_TYPED_ARRAY
#undef DECLARE_TO_LOCAL
template <class From, class To>
static inline Local<To> Convert(v8::internal::DirectHandle<From> obj);
template <class T>
static inline v8::internal::Handle<v8::internal::Object> OpenPersistent(
const v8::PersistentBase<T>& persistent) {
return v8::internal::Handle<v8::internal::Object>(persistent.slot());
}
template <class T>
static inline v8::internal::DirectHandle<v8::internal::Object> OpenPersistent(
v8::Persistent<T>* persistent) {
return OpenPersistent(*persistent);
}
template <class From, class To>
static inline v8::internal::Handle<To> OpenHandle(v8::Local<From> handle) {
return OpenHandle(*handle);
}
template <class From, class To>
static inline v8::internal::DirectHandle<To> OpenDirectHandle(
v8::Local<From> handle) {
return OpenDirectHandle(*handle);
}
private:
V8_NOINLINE V8_PRESERVE_MOST static void ReportApiFailure(
const char* location, const char* message);
#define DECLARE_TO_LOCAL_PRIVATE(Name, From, To) \
static inline Local<v8::To> Name##_helper( \
v8::internal::DirectHandle<v8::internal::From> obj);
TO_LOCAL_LIST(DECLARE_TO_LOCAL_PRIVATE)
#undef DECLARE_TO_LOCAL_PRIVATE
};
template <class T>
inline v8::Local<T> ToApiHandle(
v8::internal::DirectHandle<v8::internal::Object> obj) {
return Utils::Convert<v8::internal::Object, T>(obj);
}
template <class T>
inline bool ToLocal(v8::internal::MaybeDirectHandle<v8::internal::Object> maybe,
Local<T>* local) {
v8::internal::DirectHandle<v8::internal::Object> handle;
if (maybe.ToHandle(&handle)) {
*local = Utils::Convert<v8::internal::Object, T>(handle);
return true;
}
return false;
}
namespace internal {
class PersistentHandles;
// This class is here in order to be able to declare it a friend of
// HandleScope. Moving these methods to be members of HandleScope would be
// neat in some ways, but it would expose internal implementation details in
// our public header file, which is undesirable.
//
// An isolate has a single instance of this class to hold the current thread's
// data. In multithreaded V8 programs this data is copied in and out of storage
// so that the currently executing thread always has its own copy of this
// data.
class HandleScopeImplementer {
public:
class V8_NODISCARD EnteredContextRewindScope {
public:
explicit EnteredContextRewindScope(HandleScopeImplementer* hsi)
: hsi_(hsi), saved_entered_context_count_(hsi->EnteredContextCount()) {}
~EnteredContextRewindScope() {
DCHECK_LE(saved_entered_context_count_, hsi_->EnteredContextCount());
while (saved_entered_context_count_ < hsi_->EnteredContextCount())
hsi_->LeaveContext();
}
private:
HandleScopeImplementer* hsi_;
size_t saved_entered_context_count_;
};
explicit HandleScopeImplementer(Isolate* isolate)
: isolate_(isolate), spare_(nullptr) {}
~HandleScopeImplementer() { DeleteArray(spare_); }
HandleScopeImplementer(const HandleScopeImplementer&) = delete;
HandleScopeImplementer& operator=(const HandleScopeImplementer&) = delete;
// Threading support for handle data.
static int ArchiveSpacePerThread();
char* RestoreThread(char* from);
char* ArchiveThread(char* to);
void FreeThreadResources();
// Garbage collection support.
V8_EXPORT_PRIVATE void Iterate(v8::internal::RootVisitor* v);
V8_EXPORT_PRIVATE static char* Iterate(v8::internal::RootVisitor* v,
char* data);
inline internal::Address* GetSpareOrNewBlock();
inline void DeleteExtensions(internal::Address* prev_limit);
inline void EnterContext(Tagged<NativeContext> context);
inline void LeaveContext();
inline bool LastEnteredContextWas(Tagged<NativeContext> context);
inline size_t EnteredContextCount() const { return entered_contexts_.size(); }
// Returns the last entered context or an empty handle if no
// contexts have been entered.
inline DirectHandle<NativeContext> LastEnteredContext();
inline void SaveContext(Tagged<Context> context);
inline Tagged<Context> RestoreContext();
inline bool HasSavedContexts();
inline DetachableVector<Address*>* blocks() { return &blocks_; }
Isolate* isolate() const { return isolate_; }
void ReturnBlock(Address* block) {
DCHECK_NOT_NULL(block);
if (spare_ != nullptr) DeleteArray(spare_);
spare_ = block;
}
static const size_t kEnteredContextsOffset;
private:
void ResetAfterArchive() {
blocks_.detach();
entered_contexts_.detach();
saved_contexts_.detach();
spare_ = nullptr;
last_handle_before_persistent_block_.reset();
}
void Free() {
DCHECK(blocks_.empty());
DCHECK(entered_contexts_.empty());
DCHECK(saved_contexts_.empty());
blocks_.free();
entered_contexts_.free();
saved_contexts_.free();
if (spare_ != nullptr) {
DeleteArray(spare_);
spare_ = nullptr;
}
DCHECK(isolate_->thread_local_top()->CallDepthIsZero());
}
void BeginPersistentScope() {
DCHECK(!last_handle_before_persistent_block_.has_value());
last_handle_before_persistent_block_ = isolate()->handle_scope_data()->next;
}
bool HasPersistentScope() const {
return last_handle_before_persistent_block_.has_value();
}
std::unique_ptr<PersistentHandles> DetachPersistent(Address* first_block);
Isolate* isolate_;
DetachableVector<Address*> blocks_;
// Used as a stack to keep track of entered contexts.
DetachableVector<Tagged<NativeContext>> entered_contexts_;
// Used as a stack to keep track of saved contexts.
DetachableVector<Tagged<Context>> saved_contexts_;
Address* spare_;
std::optional<Address*> last_handle_before_persistent_block_;
// This is only used for threading support.
HandleScopeData handle_scope_data_;
void IterateThis(RootVisitor* v);
char* RestoreThreadHelper(char* from);
char* ArchiveThreadHelper(char* to);
friend class HandleScopeImplementerOffsets;
friend class PersistentHandlesScope;
};
const int kHandleBlockSize = v8::internal::KB - 2; // fit in one page
void HandleScopeImplementer::SaveContext(Tagged<Context> context) {
saved_contexts_.push_back(context);
}
Tagged<Context> HandleScopeImplementer::RestoreContext() {
Tagged<Context> last_context = saved_contexts_.back();
saved_contexts_.pop_back();
return last_context;
}
bool HandleScopeImplementer::HasSavedContexts() {
return !saved_contexts_.empty();
}
void HandleScopeImplementer::LeaveContext() {
DCHECK(!entered_contexts_.empty());
entered_contexts_.pop_back();
}
bool HandleScopeImplementer::LastEnteredContextWas(
Tagged<NativeContext> context) {
return !entered_contexts_.empty() && entered_contexts_.back() == context;
}
// If there's a spare block, use it for growing the current scope.
internal::Address* HandleScopeImplementer::GetSpareOrNewBlock() {
internal::Address* block =
(spare_ != nullptr) ? spare_
: NewArray<internal::Address>(kHandleBlockSize);
spare_ = nullptr;
return block;
}
void HandleScopeImplementer::DeleteExtensions(internal::Address* prev_limit) {
while (!blocks_.empty()) {
internal::Address* block_start = blocks_.back();
internal::Address* block_limit = block_start + kHandleBlockSize;
// SealHandleScope may make the prev_limit to point inside the block.
// Cast possibly-unrelated pointers to plain Address before comparing them
// to avoid undefined behavior.
if (reinterpret_cast<Address>(block_start) <
reinterpret_cast<Address>(prev_limit) &&
reinterpret_cast<Address>(prev_limit) <=
reinterpret_cast<Address>(block_limit)) {
#ifdef ENABLE_LOCAL_HANDLE_ZAPPING
internal::HandleScope::ZapRange(prev_limit, block_limit);
#endif
break;
}
blocks_.pop_back();
#ifdef ENABLE_LOCAL_HANDLE_ZAPPING
internal::HandleScope::ZapRange(block_start, block_limit);
#endif
if (spare_ != nullptr) {
DeleteArray(spare_);
}
spare_ = block_start;
}
DCHECK((blocks_.empty() && prev_limit == nullptr) ||
(!blocks_.empty() && prev_limit != nullptr));
}
// This is a wrapper function called from CallApiGetter builtin when profiling
// or side-effect checking is enabled. It's supposed to set up the runtime
// call stats scope and check if the getter has side-effects in case debugger
// enabled the side-effects checking mode.
// It gets additional argument, the AccessorInfo object, via
// IsolateData::api_callback_thunk_argument slot.
void InvokeAccessorGetterCallback(
v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info);
// This is a wrapper function called from CallApiCallback builtin when profiling
// or side-effect checking is enabled. It's supposed to set up the runtime
// call stats scope and check if the callback has side-effects in case debugger
// enabled the side-effects checking mode.
// It gets additional argument, the v8::FunctionCallback address, via
// IsolateData::api_callback_thunk_argument slot.
void InvokeFunctionCallbackGeneric(
const v8::FunctionCallbackInfo<v8::Value>& info);
void InvokeFunctionCallbackOptimized(
const v8::FunctionCallbackInfo<v8::Value>& info);
void InvokeFinalizationRegistryCleanupFromTask(
DirectHandle<NativeContext> native_context,
DirectHandle<JSFinalizationRegistry> finalization_registry);
template <typename T>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
T ConvertDouble(double d);
template <typename T>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
bool ValidateCallbackInfo(const FunctionCallbackInfo<T>& info);
template <typename T>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
bool ValidateCallbackInfo(const PropertyCallbackInfo<T>& info);
#ifdef ENABLE_SLOW_DCHECKS
DECLARE_CONTEXTUAL_VARIABLE_WITH_DEFAULT(StackAllocatedCheck, const bool, true);
#endif
} // namespace internal
} // namespace v8
#endif // V8_API_API_H_

14
deps/v8/src/asmjs/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>WebAssembly"
}
buganizer_public: {
component_id: 1456332
}

2
deps/v8/src/asmjs/OWNERS vendored Normal file
View File

@ -0,0 +1,2 @@
ahaas@chromium.org
clemensb@chromium.org

459
deps/v8/src/asmjs/asm-js.cc vendored Normal file
View File

@ -0,0 +1,459 @@
// Copyright 2015 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.
#include "src/asmjs/asm-js.h"
#include <optional>
#include "src/asmjs/asm-names.h"
#include "src/asmjs/asm-parser.h"
#include "src/ast/ast.h"
#include "src/base/platform/elapsed-timer.h"
#include "src/base/vector.h"
#include "src/codegen/compiler.h"
#include "src/codegen/unoptimized-compilation-info.h"
#include "src/common/assert-scope.h"
#include "src/common/message-template.h"
#include "src/execution/execution.h"
#include "src/execution/isolate.h"
#include "src/handles/handles.h"
#include "src/heap/factory.h"
#include "src/logging/counters.h"
#include "src/objects/heap-number-inl.h"
#include "src/objects/objects-inl.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/scanner-character-streams.h"
#include "src/parsing/scanner.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-js.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-result.h"
namespace v8 {
namespace internal {
const char* const AsmJs::kSingleFunctionName = "__single_function__";
namespace {
DirectHandle<Object> StdlibMathMember(Isolate* isolate,
DirectHandle<JSReceiver> stdlib,
DirectHandle<Name> name) {
DirectHandle<Name> math_name(
isolate->factory()->InternalizeString(base::StaticCharVector("Math")));
DirectHandle<Object> math =
JSReceiver::GetDataProperty(isolate, stdlib, math_name);
if (!IsJSReceiver(*math)) return isolate->factory()->undefined_value();
DirectHandle<JSReceiver> math_receiver = Cast<JSReceiver>(math);
return JSReceiver::GetDataProperty(isolate, math_receiver, name);
}
bool AreStdlibMembersValid(Isolate* isolate, DirectHandle<JSReceiver> stdlib,
wasm::AsmJsParser::StdlibSet members,
bool* is_typed_array) {
if (members.contains(wasm::AsmJsParser::StandardMember::kInfinity)) {
members.Remove(wasm::AsmJsParser::StandardMember::kInfinity);
DirectHandle<Name> name = isolate->factory()->Infinity_string();
DirectHandle<Object> value =
JSReceiver::GetDataProperty(isolate, stdlib, name);
if (!IsNumber(*value) || !std::isinf(Object::NumberValue(*value)))
return false;
}
if (members.contains(wasm::AsmJsParser::StandardMember::kNaN)) {
members.Remove(wasm::AsmJsParser::StandardMember::kNaN);
DirectHandle<Name> name = isolate->factory()->NaN_string();
DirectHandle<Object> value =
JSReceiver::GetDataProperty(isolate, stdlib, name);
if (!IsNaN(*value)) return false;
}
#define STDLIB_MATH_FUNC(fname, FName, ignore1, ignore2) \
if (members.contains(wasm::AsmJsParser::StandardMember::kMath##FName)) { \
members.Remove(wasm::AsmJsParser::StandardMember::kMath##FName); \
DirectHandle<Name> name(isolate->factory()->InternalizeString( \
base::StaticCharVector(#fname))); \
DirectHandle<Object> value = StdlibMathMember(isolate, stdlib, name); \
if (!IsJSFunction(*value)) return false; \
Tagged<SharedFunctionInfo> shared = Cast<JSFunction>(value)->shared(); \
if (!shared->HasBuiltinId() || \
shared->builtin_id() != Builtin::kMath##FName) { \
return false; \
} \
DCHECK_EQ(shared->GetCode(isolate), \
isolate->builtins()->code(Builtin::kMath##FName)); \
}
STDLIB_MATH_FUNCTION_LIST(STDLIB_MATH_FUNC)
#undef STDLIB_MATH_FUNC
#define STDLIB_MATH_CONST(cname, const_value) \
if (members.contains(wasm::AsmJsParser::StandardMember::kMath##cname)) { \
members.Remove(wasm::AsmJsParser::StandardMember::kMath##cname); \
DirectHandle<Name> name(isolate->factory()->InternalizeString( \
base::StaticCharVector(#cname))); \
DirectHandle<Object> value = StdlibMathMember(isolate, stdlib, name); \
if (!IsNumber(*value) || Object::NumberValue(*value) != const_value) \
return false; \
}
STDLIB_MATH_VALUE_LIST(STDLIB_MATH_CONST)
#undef STDLIB_MATH_CONST
#define STDLIB_ARRAY_TYPE(fname, FName) \
if (members.contains(wasm::AsmJsParser::StandardMember::k##FName)) { \
members.Remove(wasm::AsmJsParser::StandardMember::k##FName); \
*is_typed_array = true; \
DirectHandle<Name> name(isolate->factory()->InternalizeString( \
base::StaticCharVector(#FName))); \
DirectHandle<Object> value = \
JSReceiver::GetDataProperty(isolate, stdlib, name); \
if (!IsJSFunction(*value)) return false; \
DirectHandle<JSFunction> func = Cast<JSFunction>(value); \
if (!func.is_identical_to(isolate->fname())) return false; \
}
STDLIB_ARRAY_TYPE(int8_array_fun, Int8Array)
STDLIB_ARRAY_TYPE(uint8_array_fun, Uint8Array)
STDLIB_ARRAY_TYPE(int16_array_fun, Int16Array)
STDLIB_ARRAY_TYPE(uint16_array_fun, Uint16Array)
STDLIB_ARRAY_TYPE(int32_array_fun, Int32Array)
STDLIB_ARRAY_TYPE(uint32_array_fun, Uint32Array)
STDLIB_ARRAY_TYPE(float32_array_fun, Float32Array)
STDLIB_ARRAY_TYPE(float64_array_fun, Float64Array)
#undef STDLIB_ARRAY_TYPE
// All members accounted for.
DCHECK(members.empty());
return true;
}
void Report(Handle<Script> script, int position, base::Vector<const char> text,
MessageTemplate message_template,
v8::Isolate::MessageErrorLevel level) {
Isolate* isolate = script->GetIsolate();
MessageLocation location(script, position, position);
DirectHandle<String> text_object =
isolate->factory()->InternalizeUtf8String(text);
DirectHandle<JSMessageObject> message = MessageHandler::MakeMessageObject(
isolate, message_template, &location, text_object);
message->set_error_level(level);
MessageHandler::ReportMessage(isolate, &location, message);
}
// Hook to report successful execution of {AsmJs::CompileAsmViaWasm} phase.
void ReportCompilationSuccess(Handle<Script> script, int position,
double compile_time, size_t module_size) {
if (v8_flags.suppress_asm_messages || !v8_flags.trace_asm_time) return;
base::EmbeddedVector<char, 100> text;
int length = SNPrintF(text, "success, compile time %0.3f ms, %zu bytes",
compile_time, module_size);
CHECK_NE(-1, length);
text.Truncate(length);
Report(script, position, text, MessageTemplate::kAsmJsCompiled,
v8::Isolate::kMessageInfo);
}
// Hook to report failed execution of {AsmJs::CompileAsmViaWasm} phase.
void ReportCompilationFailure(ParseInfo* parse_info, int position,
const char* reason) {
if (v8_flags.suppress_asm_messages) return;
parse_info->pending_error_handler()->ReportWarningAt(
position, position, MessageTemplate::kAsmJsInvalid, reason);
}
// Hook to report successful execution of {AsmJs::InstantiateAsmWasm} phase.
void ReportInstantiationSuccess(Handle<Script> script, int position,
double instantiate_time) {
if (v8_flags.suppress_asm_messages || !v8_flags.trace_asm_time) return;
base::EmbeddedVector<char, 50> text;
int length = SNPrintF(text, "success, %0.3f ms", instantiate_time);
CHECK_NE(-1, length);
text.Truncate(length);
Report(script, position, text, MessageTemplate::kAsmJsInstantiated,
v8::Isolate::kMessageInfo);
}
// Hook to report failed execution of {AsmJs::InstantiateAsmWasm} phase.
void ReportInstantiationFailure(Handle<Script> script, int position,
const char* reason) {
if (v8_flags.suppress_asm_messages) return;
base::Vector<const char> text = base::CStrVector(reason);
Report(script, position, text, MessageTemplate::kAsmJsLinkingFailed,
v8::Isolate::kMessageWarning);
}
} // namespace
// The compilation of asm.js modules is split into two distinct steps:
// [1] ExecuteJobImpl: The asm.js module source is parsed, validated, and
// translated to a valid WebAssembly module. The result are two vectors
// representing the encoded module as well as encoded source position
// information and a StdlibSet bit set.
// [2] FinalizeJobImpl: The module is handed to WebAssembly which decodes it
// into an internal representation and eventually compiles it to machine
// code.
class AsmJsCompilationJob final : public UnoptimizedCompilationJob {
public:
explicit AsmJsCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal,
AccountingAllocator* allocator)
: UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
&compilation_info_),
allocator_(allocator),
zone_(allocator, ZONE_NAME),
compilation_info_(&zone_, parse_info, literal),
module_(nullptr),
asm_offsets_(nullptr),
compile_time_(0),
module_source_size_(0) {}
AsmJsCompilationJob(const AsmJsCompilationJob&) = delete;
AsmJsCompilationJob& operator=(const AsmJsCompilationJob&) = delete;
protected:
Status ExecuteJobImpl() final;
Status FinalizeJobImpl(DirectHandle<SharedFunctionInfo> shared_info,
Isolate* isolate) final;
Status FinalizeJobImpl(DirectHandle<SharedFunctionInfo> shared_info,
LocalIsolate* isolate) final {
return CompilationJob::RETRY_ON_MAIN_THREAD;
}
private:
void RecordHistograms(Isolate* isolate);
AccountingAllocator* allocator_;
Zone zone_;
UnoptimizedCompilationInfo compilation_info_;
wasm::ZoneBuffer* module_;
wasm::ZoneBuffer* asm_offsets_;
wasm::AsmJsParser::StdlibSet stdlib_uses_;
double compile_time_; // Time (milliseconds) taken to execute step [2].
int module_source_size_; // Module source size in bytes.
};
UnoptimizedCompilationJob::Status AsmJsCompilationJob::ExecuteJobImpl() {
DisallowHeapAccess no_heap_access;
// Step 1: Translate asm.js module to WebAssembly module.
Zone* compile_zone = &zone_;
Zone translate_zone(allocator_, ZONE_NAME);
Utf16CharacterStream* stream = parse_info()->character_stream();
std::optional<AllowHandleDereference> allow_deref;
if (stream->can_access_heap()) {
allow_deref.emplace();
}
stream->Seek(compilation_info()->literal()->start_position());
wasm::AsmJsParser parser(&translate_zone, stack_limit(), stream);
if (!parser.Run()) {
if (!v8_flags.suppress_asm_messages) {
ReportCompilationFailure(parse_info(), parser.failure_location(),
parser.failure_message());
}
return FAILED;
}
module_ = compile_zone->New<wasm::ZoneBuffer>(compile_zone);
parser.module_builder()->WriteTo(module_);
if (module_->size() > v8_flags.wasm_max_module_size) {
if (!v8_flags.suppress_asm_messages) {
ReportCompilationFailure(
parse_info(), parser.failure_location(),
"Module size exceeds engine's supported maximum");
}
return FAILED;
}
asm_offsets_ = compile_zone->New<wasm::ZoneBuffer>(compile_zone);
parser.module_builder()->WriteAsmJsOffsetTable(asm_offsets_);
stdlib_uses_ = *parser.stdlib_uses();
module_source_size_ = compilation_info()->literal()->end_position() -
compilation_info()->literal()->start_position();
return SUCCEEDED;
}
UnoptimizedCompilationJob::Status AsmJsCompilationJob::FinalizeJobImpl(
DirectHandle<SharedFunctionInfo> shared_info, Isolate* isolate) {
// Step 2: Compile and decode the WebAssembly module.
base::ElapsedTimer compile_timer;
compile_timer.Start();
DirectHandle<HeapNumber> uses_bitset =
isolate->factory()->NewHeapNumberFromBits(stdlib_uses_.ToIntegral());
// The result is a compiled module and serialized standard library uses.
wasm::ErrorThrower thrower(isolate, "AsmJs::Compile");
Handle<Script> script(Cast<Script>(shared_info->script()), isolate);
Handle<AsmWasmData> result =
wasm::GetWasmEngine()
->SyncCompileTranslatedAsmJs(
isolate, &thrower, base::OwnedCopyOf(*module_), script,
base::VectorOf(*asm_offsets_), uses_bitset,
shared_info->language_mode())
.ToHandleChecked();
DCHECK(!thrower.error());
compile_time_ = compile_timer.Elapsed().InMillisecondsF();
compilation_info()->SetAsmWasmData(result);
RecordHistograms(isolate);
ReportCompilationSuccess(script, shared_info->StartPosition(), compile_time_,
module_->size());
return SUCCEEDED;
}
void AsmJsCompilationJob::RecordHistograms(Isolate* isolate) {
isolate->counters()->asm_module_size_bytes()->AddSample(module_source_size_);
}
std::unique_ptr<UnoptimizedCompilationJob> AsmJs::NewCompilationJob(
ParseInfo* parse_info, FunctionLiteral* literal,
AccountingAllocator* allocator) {
return std::make_unique<AsmJsCompilationJob>(parse_info, literal, allocator);
}
namespace {
inline bool IsValidAsmjsMemorySize(size_t size) {
// Enforce asm.js spec minimum size.
if (size < (1u << 12u)) return false;
// Enforce engine-limited and flag-limited maximum allocation size.
if (size > wasm::max_mem32_bytes()) return false;
// Enforce power-of-2 sizes for 2^12 - 2^24.
if (size < (1u << 24u)) {
uint32_t size32 = static_cast<uint32_t>(size);
return base::bits::IsPowerOfTwo(size32);
}
// Enforce multiple of 2^24 for sizes >= 2^24
if ((size % (1u << 24u)) != 0) return false;
// Limitation of our implementation: for performance reasons, we use unsigned
// uint32-to-uintptr extensions for memory addresses, which would give
// incorrect behavior for memories larger than 2 GiB.
// Note that this does not affect Chrome, which does not allow allocating
// larger ArrayBuffers anyway.
if (size > 0x8000'0000u) return false;
// All checks passed!
return true;
}
} // namespace
MaybeDirectHandle<Object> AsmJs::InstantiateAsmWasm(
Isolate* isolate, DirectHandle<SharedFunctionInfo> shared,
DirectHandle<AsmWasmData> wasm_data, DirectHandle<JSReceiver> stdlib,
DirectHandle<JSReceiver> foreign, DirectHandle<JSArrayBuffer> memory) {
base::ElapsedTimer instantiate_timer;
instantiate_timer.Start();
DirectHandle<HeapNumber> uses_bitset(wasm_data->uses_bitset(), isolate);
Handle<Script> script(Cast<Script>(shared->script()), isolate);
auto* wasm_engine = wasm::GetWasmEngine();
// Allocate the WasmModuleObject.
DirectHandle<WasmModuleObject> module =
wasm_engine->FinalizeTranslatedAsmJs(isolate, wasm_data, script);
// TODO(asmjs): The position currently points to the module definition
// but should instead point to the instantiation site (more intuitive).
int position = shared->StartPosition();
// Check that the module is not instantiated as a generator or async function.
if (IsResumableFunction(shared->scope_info()->function_kind())) {
ReportInstantiationFailure(script, position,
"Cannot be instantiated as resumable function");
return {};
}
// Check that all used stdlib members are valid.
bool stdlib_use_of_typed_array_present = false;
wasm::AsmJsParser::StdlibSet stdlib_uses =
wasm::AsmJsParser::StdlibSet::FromIntegral(uses_bitset->value_as_bits());
if (!stdlib_uses.empty()) { // No checking needed if no uses.
if (stdlib.is_null()) {
ReportInstantiationFailure(script, position, "Requires standard library");
return {};
}
if (!AreStdlibMembersValid(isolate, stdlib, stdlib_uses,
&stdlib_use_of_typed_array_present)) {
ReportInstantiationFailure(script, position, "Unexpected stdlib member");
return {};
}
}
// Check that a valid heap buffer is provided if required.
if (stdlib_use_of_typed_array_present) {
if (memory.is_null()) {
ReportInstantiationFailure(script, position, "Requires heap buffer");
return {};
}
// AsmJs memory must be an ArrayBuffer.
if (memory->is_shared()) {
ReportInstantiationFailure(script, position,
"Invalid heap type: SharedArrayBuffer");
return {};
}
// We don't allow resizable ArrayBuffers because resizable ArrayBuffers may
// shrink, and then asm.js does out of bounds memory accesses.
if (memory->is_resizable_by_js()) {
ReportInstantiationFailure(script, position,
"Invalid heap type: resizable ArrayBuffer");
return {};
}
// We don't allow WebAssembly.Memory, because WebAssembly.Memory.grow()
// detaches the ArrayBuffer, and that would invalidate the asm.js module.
if (memory->GetBackingStore() &&
memory->GetBackingStore()->is_wasm_memory()) {
ReportInstantiationFailure(script, position,
"Invalid heap type: WebAssembly.Memory");
return {};
}
size_t size = memory->byte_length();
// Check the asm.js heap size against the valid limits.
if (!IsValidAsmjsMemorySize(size)) {
ReportInstantiationFailure(script, position, "Invalid heap size");
return {};
}
// Mark the buffer as undetachable. This implies that the buffer cannot be
// postMessage()'d, as that detaches the buffer.
memory->set_is_detachable(false);
} else {
memory = Handle<JSArrayBuffer>::null();
}
wasm::ErrorThrower thrower(isolate, "AsmJs::Instantiate");
MaybeDirectHandle<WasmInstanceObject> maybe_instance =
wasm_engine->SyncInstantiate(isolate, &thrower, module, foreign, memory);
if (maybe_instance.is_null()) {
// Clear a possible stack overflow from function entry that would have
// bypassed the {ErrorThrower}. Be careful not to clear a termination
// exception.
if (isolate->is_execution_terminating()) return {};
if (isolate->has_exception()) isolate->clear_exception();
if (thrower.error()) {
base::ScopedVector<char> error_reason(100);
SNPrintF(error_reason, "Internal wasm failure: %s", thrower.error_msg());
ReportInstantiationFailure(script, position, error_reason.begin());
} else {
ReportInstantiationFailure(script, position, "Internal wasm failure");
}
thrower.Reset(); // Ensure exceptions do not propagate.
return {};
}
DCHECK(!thrower.error());
DirectHandle<WasmInstanceObject> instance = maybe_instance.ToHandleChecked();
ReportInstantiationSuccess(script, position,
instantiate_timer.Elapsed().InMillisecondsF());
DirectHandle<Name> single_function_name(
isolate->factory()->InternalizeUtf8String(AsmJs::kSingleFunctionName));
MaybeDirectHandle<Object> single_function =
Object::GetProperty(isolate, instance, single_function_name);
if (!single_function.is_null() &&
!IsUndefined(*single_function.ToHandleChecked(), isolate)) {
return single_function;
}
// Here we rely on the fact that the exports object is eagerly created.
// The following check is a weak indicator for that. If this ever changes,
// then we'll have to call the "exports" getter, and be careful about
// handling possible stack overflow exceptions.
DCHECK(IsJSObject(instance->exports_object()));
return direct_handle(instance->exports_object(), isolate);
}
} // namespace internal
} // namespace v8

44
deps/v8/src/asmjs/asm-js.h vendored Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2016 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_ASMJS_ASM_JS_H_
#define V8_ASMJS_ASM_JS_H_
// Clients of this interface shouldn't depend on lots of asmjs internals.
// Do not include anything from src/asmjs here!
#include <memory>
#include "src/common/globals.h"
namespace v8 {
namespace internal {
class AccountingAllocator;
class AsmWasmData;
class FunctionLiteral;
class JSArrayBuffer;
class ParseInfo;
class SharedFunctionInfo;
class UnoptimizedCompilationJob;
// Interface to compile and instantiate for asm.js modules.
class AsmJs {
public:
static std::unique_ptr<UnoptimizedCompilationJob> NewCompilationJob(
ParseInfo* parse_info, FunctionLiteral* literal,
AccountingAllocator* allocator);
static MaybeDirectHandle<Object> InstantiateAsmWasm(
Isolate* isolate, DirectHandle<SharedFunctionInfo>,
DirectHandle<AsmWasmData> wasm_data, DirectHandle<JSReceiver> stdlib,
DirectHandle<JSReceiver> foreign, DirectHandle<JSArrayBuffer> memory);
// Special export name used to indicate that the module exports a single
// function instead of a JavaScript object holding multiple functions.
static const char* const kSingleFunctionName;
};
} // namespace internal
} // namespace v8
#endif // V8_ASMJS_ASM_JS_H_

111
deps/v8/src/asmjs/asm-names.h vendored Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2017 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_ASMJS_ASM_NAMES_H_
#define V8_ASMJS_ASM_NAMES_H_
// V(stdlib.Math.<name>, constant-value)
#define STDLIB_MATH_VALUE_LIST(V) \
V(E, 2.718281828459045) \
V(LN10, 2.302585092994046) \
V(LN2, 0.6931471805599453) \
V(LOG2E, 1.4426950408889634) \
V(LOG10E, 0.4342944819032518) \
V(PI, 3.141592653589793) \
V(SQRT1_2, 0.7071067811865476) \
V(SQRT2, 1.4142135623730951)
// V(stdlib.Math.<name>, Name, wasm-opcode, asm-js-type)
#define STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V) \
V(acos, Acos, kExprF64Acos, dq2d) \
V(asin, Asin, kExprF64Asin, dq2d) \
V(atan, Atan, kExprF64Atan, dq2d) \
V(cos, Cos, kExprF64Cos, dq2d) \
V(sin, Sin, kExprF64Sin, dq2d) \
V(tan, Tan, kExprF64Tan, dq2d) \
V(exp, Exp, kExprF64Exp, dq2d) \
V(log, Log, kExprF64Log, dq2d) \
V(atan2, Atan2, kExprF64Atan2, dqdq2d) \
V(pow, Pow, kExprF64Pow, dqdq2d) \
V(imul, Imul, kExprI32Mul, ii2s) \
V(clz32, Clz32, kExprI32Clz, i2s)
// V(stdlib.Math.<name>, Name, unused, asm-js-type)
#define STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V) \
V(ceil, Ceil, x, ceil_like) \
V(floor, Floor, x, ceil_like) \
V(sqrt, Sqrt, x, ceil_like)
// V(stdlib.Math.<name>, Name, unused, asm-js-type)
#define STDLIB_MATH_FUNCTION_LIST(V) \
V(min, Min, x, minmax) \
V(max, Max, x, minmax) \
V(abs, Abs, x, abs) \
V(fround, Fround, x, fround) \
STDLIB_MATH_FUNCTION_MONOMORPHIC_LIST(V) \
STDLIB_MATH_FUNCTION_CEIL_LIKE_LIST(V)
// V(stdlib.<name>, wasm-load-type, wasm-store-type, wasm-type)
#define STDLIB_ARRAY_TYPE_LIST(V) \
V(Int8Array, Mem8S, Mem8, I32) \
V(Uint8Array, Mem8U, Mem8, I32) \
V(Int16Array, Mem16S, Mem16, I32) \
V(Uint16Array, Mem16U, Mem16, I32) \
V(Int32Array, Mem, Mem, I32) \
V(Uint32Array, Mem, Mem, I32) \
V(Float32Array, Mem, Mem, F32) \
V(Float64Array, Mem, Mem, F64)
#define STDLIB_OTHER_LIST(V) \
V(Infinity) \
V(NaN) \
V(Math)
// clang-format off (for return)
#define KEYWORD_NAME_LIST(V) \
V(arguments) \
V(break) \
V(case) \
V(const) \
V(continue) \
V(default) \
V(do) \
V(else) \
V(eval) \
V(for) \
V(function) \
V(if) \
V(new) \
V(return ) \
V(switch) \
V(var) \
V(while)
// clang-format on
// V(token-string, token-name)
#define LONG_SYMBOL_NAME_LIST(V) \
V("<=", LE) \
V(">=", GE) \
V("==", EQ) \
V("!=", NE) \
V("<<", SHL) \
V(">>", SAR) \
V(">>>", SHR) \
V("'use asm'", UseAsm)
// clang-format off
#define SIMPLE_SINGLE_TOKEN_LIST(V) \
V('+') V('-') V('*') V('%') V('~') V('^') V('&') V('|') V('(') V(')') \
V('[') V(']') V('{') V('}') V(':') V(';') V(',') V('?')
// clang-format on
// V(name, value, string-name)
#define SPECIAL_TOKEN_LIST(V) \
V(kUninitialized, 0, "{uninitialized}") \
V(kEndOfInput, -1, "{end of input}") \
V(kParseError, -2, "{parse error}") \
V(kUnsigned, -3, "{unsigned value}") \
V(kDouble, -4, "{double value}")
#endif // V8_ASMJS_ASM_NAMES_H_

2595
deps/v8/src/asmjs/asm-parser.cc vendored Normal file

File diff suppressed because it is too large Load Diff

414
deps/v8/src/asmjs/asm-parser.h vendored Normal file
View File

@ -0,0 +1,414 @@
// Copyright 2017 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_ASMJS_ASM_PARSER_H_
#define V8_ASMJS_ASM_PARSER_H_
#include <memory>
#include "src/asmjs/asm-scanner.h"
#include "src/asmjs/asm-types.h"
#include "src/base/enum-set.h"
#include "src/base/vector.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class Utf16CharacterStream;
namespace wasm {
// A custom parser + validator + wasm converter for asm.js:
// http://asmjs.org/spec/latest/
// This parser intentionally avoids the portion of JavaScript parsing
// that are not required to determine if code is valid asm.js code.
// * It is mostly one pass.
// * It bails out on unexpected input.
// * It assumes strict ordering insofar as permitted by asm.js validation rules.
// * It relies on a custom scanner that provides de-duped identifiers in two
// scopes (local + module wide).
class AsmJsParser {
public:
// clang-format off
enum StandardMember {
kInfinity,
kNaN,
#define V(_unused1, name, _unused2, _unused3) kMath##name,
STDLIB_MATH_FUNCTION_LIST(V)
#undef V
#define V(name, _unused1) kMath##name,
STDLIB_MATH_VALUE_LIST(V)
#undef V
#define V(name, _unused1, _unused2, _unused3) k##name,
STDLIB_ARRAY_TYPE_LIST(V)
#undef V
};
// clang-format on
using StdlibSet = base::EnumSet<StandardMember, uint64_t>;
explicit AsmJsParser(Zone* zone, uintptr_t stack_limit,
Utf16CharacterStream* stream);
bool Run();
const char* failure_message() const { return failure_message_; }
int failure_location() const { return failure_location_; }
WasmModuleBuilder* module_builder() { return module_builder_; }
const StdlibSet* stdlib_uses() const { return &stdlib_uses_; }
private:
// clang-format off
enum class VarKind {
kUnused,
kLocal,
kGlobal,
kSpecial,
kFunction,
kTable,
kImportedFunction,
#define V(_unused0, Name, _unused1, _unused2) kMath##Name,
STDLIB_MATH_FUNCTION_LIST(V)
#undef V
#define V(Name, _unused1) kMath##Name,
STDLIB_MATH_VALUE_LIST(V)
#undef V
};
// clang-format on
// A single import in asm.js can require multiple imports in wasm, if the
// function is used with different signatures. {cache} keeps the wasm
// imports for the single asm.js import of name {function_name}.
struct FunctionImportInfo {
base::Vector<const char> function_name;
ZoneUnorderedMap<FunctionSig, uint32_t> cache;
// Constructor.
FunctionImportInfo(base::Vector<const char> name, Zone* zone)
: function_name(name), cache(zone) {}
};
struct VarInfo {
AsmType* type = AsmType::None();
WasmFunctionBuilder* function_builder = nullptr;
FunctionImportInfo* import = nullptr;
uint32_t mask = 0;
uint32_t index = 0;
VarKind kind = VarKind::kUnused;
bool mutable_variable = true;
bool function_defined = false;
};
struct GlobalImport {
base::Vector<const char> import_name;
ValueType value_type;
VarInfo* var_info;
};
// Distinguish different kinds of blocks participating in {block_stack}. Each
// entry on that stack represents one block in the wasm code, and determines
// which block 'break' and 'continue' target in the current context:
// - kRegular: The target of a 'break' (with & without identifier).
// Pushed by an IterationStatement and a SwitchStatement.
// - kLoop : The target of a 'continue' (with & without identifier).
// Pushed by an IterationStatement.
// - kNamed : The target of a 'break' with a specific identifier.
// Pushed by a BlockStatement.
// - kOther : Only used for internal blocks, can never be targeted.
enum class BlockKind { kRegular, kLoop, kNamed, kOther };
// One entry in the {block_stack}, see {BlockKind} above for details. Blocks
// without a label have {kTokenNone} set as their label.
struct BlockInfo {
BlockKind kind;
AsmJsScanner::token_t label;
};
// Helper class to make {TempVariable} safe for nesting.
class TemporaryVariableScope;
template <typename T>
class CachedVectors {
public:
explicit CachedVectors(Zone* zone) : reusable_vectors_(zone) {}
Zone* zone() const { return reusable_vectors_.zone(); }
inline void fill(ZoneVector<T>* vec) {
if (reusable_vectors_.empty()) return;
reusable_vectors_.back().swap(*vec);
reusable_vectors_.pop_back();
vec->clear();
}
inline void reuse(ZoneVector<T>* vec) {
reusable_vectors_.emplace_back(std::move(*vec));
}
private:
ZoneVector<ZoneVector<T>> reusable_vectors_;
};
template <typename T>
class CachedVector final : public ZoneVector<T> {
public:
explicit CachedVector(CachedVectors<T>* cache)
: ZoneVector<T>(cache->zone()), cache_(cache) {
cache->fill(this);
}
~CachedVector() { cache_->reuse(this); }
private:
CachedVectors<T>* cache_;
};
Zone* zone_;
AsmJsScanner scanner_;
WasmModuleBuilder* module_builder_;
WasmFunctionBuilder* current_function_builder_;
AsmType* return_type_ = nullptr;
uintptr_t stack_limit_;
StdlibSet stdlib_uses_;
base::Vector<VarInfo> global_var_info_;
base::Vector<VarInfo> local_var_info_;
size_t num_globals_ = 0;
CachedVectors<ValueType> cached_valuetype_vectors_{zone_};
CachedVectors<AsmType*> cached_asm_type_p_vectors_{zone_};
CachedVectors<AsmJsScanner::token_t> cached_token_t_vectors_{zone_};
CachedVectors<int32_t> cached_int_vectors_{zone_};
int function_temp_locals_offset_;
int function_temp_locals_used_;
int function_temp_locals_depth_;
// Error Handling related
bool failed_ = false;
const char* failure_message_;
int failure_location_ = kNoSourcePosition;
// Module Related.
AsmJsScanner::token_t stdlib_name_ = kTokenNone;
AsmJsScanner::token_t foreign_name_ = kTokenNone;
AsmJsScanner::token_t heap_name_ = kTokenNone;
static const AsmJsScanner::token_t kTokenNone = 0;
// Track if parsing a heap assignment.
bool inside_heap_assignment_ = false;
AsmType* heap_access_type_ = nullptr;
ZoneVector<BlockInfo> block_stack_;
// Types used for stdlib function and their set up.
AsmType* stdlib_dq2d_;
AsmType* stdlib_dqdq2d_;
AsmType* stdlib_i2s_;
AsmType* stdlib_ii2s_;
AsmType* stdlib_minmax_;
AsmType* stdlib_abs_;
AsmType* stdlib_ceil_like_;
AsmType* stdlib_fround_;
// When making calls, the return type is needed to lookup signatures.
// For `+callsite(..)` or `fround(callsite(..))` use this value to pass
// along the coercion.
AsmType* call_coercion_ = nullptr;
// The source position associated with the above {call_coercion}.
size_t call_coercion_position_;
// When making calls, the coercion can also appear in the source stream
// syntactically "behind" the call site. For `callsite(..)|0` use this
// value to flag that such a coercion must happen.
AsmType* call_coercion_deferred_ = nullptr;
// The source position at which requesting a deferred coercion via the
// aforementioned {call_coercion_deferred} is allowed.
size_t call_coercion_deferred_position_;
// The code position of the last heap access shift by an immediate value.
// For `heap[expr >> value:NumericLiteral]` this indicates from where to
// delete code when the expression is used as part of a valid heap access.
// Will be set to {kNoHeapAccessShift} if heap access shift wasn't matched.
size_t heap_access_shift_position_;
uint32_t heap_access_shift_value_;
static const size_t kNoHeapAccessShift = -1;
// Used to track the last label we've seen so it can be matched to later
// statements it's attached to.
AsmJsScanner::token_t pending_label_ = kTokenNone;
// Global imports. The list of imported variables that are copied during
// module instantiation into a corresponding global variable.
ZoneLinkedList<GlobalImport> global_imports_;
Zone* zone() { return zone_; }
inline bool Peek(AsmJsScanner::token_t token) {
return scanner_.Token() == token;
}
inline bool PeekForZero() {
return (scanner_.IsUnsigned() && scanner_.AsUnsigned() == 0);
}
inline bool Check(AsmJsScanner::token_t token) {
if (scanner_.Token() == token) {
scanner_.Next();
return true;
} else {
return false;
}
}
inline bool CheckForZero() {
if (scanner_.IsUnsigned() && scanner_.AsUnsigned() == 0) {
scanner_.Next();
return true;
} else {
return false;
}
}
inline bool CheckForDouble(double* value) {
if (scanner_.IsDouble()) {
*value = scanner_.AsDouble();
scanner_.Next();
return true;
} else {
return false;
}
}
inline bool CheckForUnsigned(uint32_t* value) {
if (scanner_.IsUnsigned()) {
*value = scanner_.AsUnsigned();
scanner_.Next();
return true;
} else {
return false;
}
}
inline bool CheckForUnsignedBelow(uint32_t limit, uint32_t* value) {
if (scanner_.IsUnsigned() && scanner_.AsUnsigned() < limit) {
*value = scanner_.AsUnsigned();
scanner_.Next();
return true;
} else {
return false;
}
}
inline AsmJsScanner::token_t Consume() {
AsmJsScanner::token_t ret = scanner_.Token();
scanner_.Next();
return ret;
}
void SkipSemicolon();
VarInfo* GetVarInfo(AsmJsScanner::token_t token);
uint32_t VarIndex(VarInfo* info);
void DeclareGlobal(VarInfo* info, bool mutable_variable, AsmType* type,
ValueType vtype, WasmInitExpr init);
void DeclareStdlibFunc(VarInfo* info, VarKind kind, AsmType* type);
void AddGlobalImport(base::Vector<const char> name, AsmType* type,
ValueType vtype, bool mutable_variable, VarInfo* info);
// Allocates a temporary local variable. The given {index} is absolute within
// the function body, consider using {TemporaryVariableScope} when nesting.
uint32_t TempVariable(int index);
// Preserves a copy of the scanner's current identifier string in the zone.
base::Vector<const char> CopyCurrentIdentifierString();
// Use to set up block stack layers (including synthetic ones for if-else).
// Begin/Loop/End below are implemented with these plus code generation.
void BareBegin(BlockKind kind, AsmJsScanner::token_t label = 0);
void BareEnd();
int FindContinueLabelDepth(AsmJsScanner::token_t label);
int FindBreakLabelDepth(AsmJsScanner::token_t label);
// Use to set up actual wasm blocks/loops.
void Begin(AsmJsScanner::token_t label = 0);
void Loop(AsmJsScanner::token_t label = 0);
void End();
void InitializeStdlibTypes();
FunctionSig* ConvertSignature(AsmType* return_type,
const ZoneVector<AsmType*>& params);
void ValidateModule(); // 6.1 ValidateModule
void ValidateModuleParameters(); // 6.1 ValidateModule - parameters
void ValidateModuleVars(); // 6.1 ValidateModule - variables
void ValidateModuleVar(bool mutable_variable);
void ValidateModuleVarImport(VarInfo* info, bool mutable_variable);
void ValidateModuleVarStdlib(VarInfo* info);
void ValidateModuleVarNewStdlib(VarInfo* info);
void ValidateModuleVarFromGlobal(VarInfo* info, bool mutable_variable);
void ValidateExport(); // 6.2 ValidateExport
void ValidateFunctionTable(); // 6.3 ValidateFunctionTable
void ValidateFunction(); // 6.4 ValidateFunction
void ValidateFunctionParams(ZoneVector<AsmType*>* params);
void ValidateFunctionLocals(size_t param_count,
ZoneVector<ValueType>* locals);
void ValidateStatement(); // 6.5 ValidateStatement
void Block(); // 6.5.1 Block
void ExpressionStatement(); // 6.5.2 ExpressionStatement
void EmptyStatement(); // 6.5.3 EmptyStatement
void IfStatement(); // 6.5.4 IfStatement
void ReturnStatement(); // 6.5.5 ReturnStatement
bool IterationStatement(); // 6.5.6 IterationStatement
void WhileStatement(); // 6.5.6 IterationStatement - while
void DoStatement(); // 6.5.6 IterationStatement - do
void ForStatement(); // 6.5.6 IterationStatement - for
void BreakStatement(); // 6.5.7 BreakStatement
void ContinueStatement(); // 6.5.8 ContinueStatement
void LabelledStatement(); // 6.5.9 LabelledStatement
void SwitchStatement(); // 6.5.10 SwitchStatement
void ValidateCase(); // 6.6. ValidateCase
void ValidateDefault(); // 6.7 ValidateDefault
AsmType* ValidateExpression(); // 6.8 ValidateExpression
AsmType* Expression(AsmType* expect); // 6.8.1 Expression
AsmType* NumericLiteral(); // 6.8.2 NumericLiteral
AsmType* Identifier(); // 6.8.3 Identifier
AsmType* CallExpression(); // 6.8.4 CallExpression
AsmType* MemberExpression(); // 6.8.5 MemberExpression
AsmType* AssignmentExpression(); // 6.8.6 AssignmentExpression
AsmType* UnaryExpression(); // 6.8.7 UnaryExpression
AsmType* MultiplicativeExpression(); // 6.8.8 MultiplicativeExpression
AsmType* AdditiveExpression(); // 6.8.9 AdditiveExpression
AsmType* ShiftExpression(); // 6.8.10 ShiftExpression
AsmType* RelationalExpression(); // 6.8.11 RelationalExpression
AsmType* EqualityExpression(); // 6.8.12 EqualityExpression
AsmType* BitwiseANDExpression(); // 6.8.13 BitwiseANDExpression
AsmType* BitwiseXORExpression(); // 6.8.14 BitwiseXORExpression
AsmType* BitwiseORExpression(); // 6.8.15 BitwiseORExpression
AsmType* ConditionalExpression(); // 6.8.16 ConditionalExpression
AsmType* ParenthesizedExpression(); // 6.8.17 ParenthesiedExpression
AsmType* ValidateCall(); // 6.9 ValidateCall
bool PeekCall(); // 6.9 ValidateCall - helper
void ValidateHeapAccess(); // 6.10 ValidateHeapAccess
void ValidateFloatCoercion(); // 6.11 ValidateFloatCoercion
// Used as part of {ForStatement}. Scans forward to the next `)` in order to
// skip over the third expression in a for-statement. This is one piece that
// makes this parser not be a pure single-pass.
void ScanToClosingParenthesis();
// Used as part of {SwitchStatement}. Collects all case labels in the current
// switch-statement, then resets the scanner position. This is one piece that
// makes this parser not be a pure single-pass.
void GatherCases(ZoneVector<int32_t>* cases);
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_ASMJS_ASM_PARSER_H_

478
deps/v8/src/asmjs/asm-scanner.cc vendored Normal file
View File

@ -0,0 +1,478 @@
// Copyright 2017 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.
#include "src/asmjs/asm-scanner.h"
#include <cinttypes>
#include "src/base/iterator.h"
#include "src/flags/flags.h"
#include "src/numbers/conversions.h"
#include "src/parsing/scanner.h"
#include "src/strings/char-predicates-inl.h"
namespace v8 {
namespace internal {
namespace {
// Cap number of identifiers to ensure we can assign both global and
// local ones a token id in the range of an int32_t.
static const int kMaxIdentifierCount = 0xF000000;
} // namespace
AsmJsScanner::AsmJsScanner(Utf16CharacterStream* stream)
: stream_(stream),
token_(kUninitialized),
preceding_token_(kUninitialized),
next_token_(kUninitialized),
position_(0),
preceding_position_(0),
next_position_(0),
rewind_(false),
in_local_scope_(false),
global_count_(0),
double_value_(0.0),
unsigned_value_(0),
preceded_by_newline_(false) {
#define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
STDLIB_MATH_FUNCTION_LIST(V)
STDLIB_ARRAY_TYPE_LIST(V)
#undef V
#define V(name, _junk1) property_names_[#name] = kToken_##name;
STDLIB_MATH_VALUE_LIST(V)
#undef V
#define V(name) property_names_[#name] = kToken_##name;
STDLIB_OTHER_LIST(V)
#undef V
#define V(name) global_names_[#name] = kToken_##name;
KEYWORD_NAME_LIST(V)
#undef V
Next();
}
void AsmJsScanner::Next() {
if (rewind_) {
preceding_token_ = token_;
preceding_position_ = position_;
token_ = next_token_;
position_ = next_position_;
next_token_ = kUninitialized;
next_position_ = 0;
rewind_ = false;
return;
}
if (token_ == kEndOfInput || token_ == kParseError) {
return;
}
#if DEBUG
if (v8_flags.trace_asm_scanner) {
if (Token() == kDouble) {
PrintF("%lf ", AsDouble());
} else if (Token() == kUnsigned) {
PrintF("%" PRIu32 " ", AsUnsigned());
} else {
std::string name = Name(Token());
PrintF("%s ", name.c_str());
}
}
#endif
preceded_by_newline_ = false;
preceding_token_ = token_;
preceding_position_ = position_;
for (;;) {
position_ = stream_->pos();
base::uc32 ch = stream_->Advance();
switch (ch) {
case ' ':
case '\t':
case '\r':
// Ignore whitespace.
break;
case '\n':
// Track when we've passed a newline for optional semicolon support,
// but keep scanning.
preceded_by_newline_ = true;
break;
case kEndOfInputU:
token_ = kEndOfInput;
return;
case '\'':
case '"':
ConsumeString(ch);
return;
case '/':
ch = stream_->Advance();
if (ch == '/') {
ConsumeCPPComment();
} else if (ch == '*') {
if (!ConsumeCComment()) {
token_ = kParseError;
return;
}
} else {
stream_->Back();
token_ = '/';
return;
}
// Breaks out of switch, but loops again (i.e. the case when we parsed
// a comment, but need to continue to look for the next token).
break;
case '<':
case '>':
case '=':
case '!':
ConsumeCompareOrShift(ch);
return;
#define V(single_char_token) case single_char_token:
SIMPLE_SINGLE_TOKEN_LIST(V)
#undef V
// Use fixed token IDs for ASCII.
token_ = ch;
return;
default:
if (IsIdentifierStart(ch)) {
ConsumeIdentifier(ch);
} else if (IsNumberStart(ch)) {
ConsumeNumber(ch);
} else {
// TODO(bradnelson): Support unicode (probably via UnicodeCache).
token_ = kParseError;
}
return;
}
}
}
void AsmJsScanner::Rewind() {
DCHECK_NE(kUninitialized, preceding_token_);
// TODO(bradnelson): Currently rewinding needs to leave in place the
// preceding newline state (in case a |0 ends a line).
// This is weird and stateful, fix me.
DCHECK(!rewind_);
next_token_ = token_;
next_position_ = position_;
token_ = preceding_token_;
position_ = preceding_position_;
preceding_token_ = kUninitialized;
preceding_position_ = 0;
rewind_ = true;
identifier_string_.clear();
}
void AsmJsScanner::ResetLocals() { local_names_.clear(); }
#if DEBUG
// Only used for debugging.
std::string AsmJsScanner::Name(token_t token) const {
if (token >= 32 && token < 127) {
return std::string(1, static_cast<char>(token));
}
for (auto& i : local_names_) {
if (i.second == token) {
return i.first;
}
}
for (auto& i : global_names_) {
if (i.second == token) {
return i.first;
}
}
for (auto& i : property_names_) {
if (i.second == token) {
return i.first;
}
}
switch (token) {
#define V(rawname, name) \
case kToken_##name: \
return rawname;
LONG_SYMBOL_NAME_LIST(V)
#undef V
#define V(name, value, string_name) \
case name: \
return string_name;
SPECIAL_TOKEN_LIST(V)
default:
break;
#undef V
}
UNREACHABLE();
}
#endif
void AsmJsScanner::Seek(size_t pos) {
stream_->Seek(pos);
preceding_token_ = kUninitialized;
token_ = kUninitialized;
next_token_ = kUninitialized;
preceding_position_ = 0;
position_ = 0;
next_position_ = 0;
rewind_ = false;
Next();
}
void AsmJsScanner::ConsumeIdentifier(base::uc32 ch) {
// Consume characters while still part of the identifier.
identifier_string_.clear();
while (IsIdentifierPart(ch)) {
identifier_string_ += ch;
ch = stream_->Advance();
}
// Go back one for next time.
stream_->Back();
// Decode what the identifier means.
if (preceding_token_ == '.') {
auto i = property_names_.find(identifier_string_);
if (i != property_names_.end()) {
token_ = i->second;
return;
}
} else {
{
auto i = local_names_.find(identifier_string_);
if (i != local_names_.end()) {
token_ = i->second;
return;
}
}
if (!in_local_scope_) {
auto i = global_names_.find(identifier_string_);
if (i != global_names_.end()) {
token_ = i->second;
return;
}
}
}
if (preceding_token_ == '.') {
CHECK_LT(global_count_, kMaxIdentifierCount);
token_ = kGlobalsStart + global_count_++;
property_names_[identifier_string_] = token_;
} else if (in_local_scope_) {
CHECK_LT(local_names_.size(), kMaxIdentifierCount);
token_ = kLocalsStart - static_cast<token_t>(local_names_.size());
local_names_[identifier_string_] = token_;
} else {
CHECK_LT(global_count_, kMaxIdentifierCount);
token_ = kGlobalsStart + global_count_++;
global_names_[identifier_string_] = token_;
}
}
namespace {
bool IsValidImplicitOctal(std::string_view number) {
DCHECK_EQ(number[0], '0');
return std::all_of(number.begin() + 1, number.end(), IsOctalDigit);
}
} // namespace
void AsmJsScanner::ConsumeNumber(base::uc32 ch) {
std::string number;
number.assign(1, ch);
bool has_dot = ch == '.';
bool has_prefix = false;
for (;;) {
ch = stream_->Advance();
if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
ch == 'x' ||
((ch == '-' || ch == '+') && !has_prefix &&
(number[number.size() - 1] == 'e' ||
number[number.size() - 1] == 'E'))) {
// TODO(bradnelson): Test weird cases ending in -.
if (ch == '.') {
has_dot = true;
}
if (ch == 'b' || ch == 'o' || ch == 'x') {
has_prefix = true;
}
number.push_back(ch);
} else {
break;
}
}
stream_->Back();
// Special case the most common number.
if (number.size() == 1 && number[0] == '0') {
unsigned_value_ = 0;
token_ = kUnsigned;
return;
}
// Pick out dot.
if (number.size() == 1 && number[0] == '.') {
token_ = '.';
return;
}
// Decode numbers, with seperate paths for prefixes and implicit octals.
if (has_prefix && number[0] == '0') {
// "0[xob]" by itself is a parse error.
if (number.size() <= 2) {
token_ = kParseError;
return;
}
switch (number[1]) {
case 'b':
double_value_ = BinaryStringToDouble(
base::Vector<const uint8_t>::cast(base::VectorOf(number)));
break;
case 'o':
double_value_ = OctalStringToDouble(
base::Vector<const uint8_t>::cast(base::VectorOf(number)));
break;
case 'x':
double_value_ = HexStringToDouble(
base::Vector<const uint8_t>::cast(base::VectorOf(number)));
break;
default:
// If there is a prefix character, but it's not the second character,
// then there's a parse error somewhere.
token_ = kParseError;
break;
}
} else if (number[0] == '0' && !has_prefix && IsValidImplicitOctal(number)) {
double_value_ = ImplicitOctalStringToDouble(
base::Vector<const uint8_t>::cast(base::VectorOf(number)));
} else {
double_value_ = StringToDouble(
base::Vector<const uint8_t>::cast(base::VectorOf(number)),
NO_CONVERSION_FLAG);
}
if (std::isnan(double_value_)) {
// Check if string to number conversion didn't consume all the characters.
// This happens if the character filter let through something invalid
// like: 0123ef for example.
// TODO(bradnelson): Check if this happens often enough to be a perf
// problem.
if (number[0] == '.') {
for (size_t k = 1; k < number.size(); ++k) {
stream_->Back();
}
token_ = '.';
return;
}
// Anything else that doesn't parse is an error.
token_ = kParseError;
return;
}
if (has_dot || trunc(double_value_) != double_value_) {
token_ = kDouble;
} else {
// Exceeding safe integer range is an error.
if (double_value_ > static_cast<double>(kMaxUInt32)) {
token_ = kParseError;
return;
}
unsigned_value_ = static_cast<uint32_t>(double_value_);
token_ = kUnsigned;
}
}
bool AsmJsScanner::ConsumeCComment() {
for (;;) {
base::uc32 ch = stream_->Advance();
while (ch == '*') {
ch = stream_->Advance();
if (ch == '/') {
return true;
}
}
if (ch == '\n') {
preceded_by_newline_ = true;
}
if (ch == kEndOfInputU) {
return false;
}
}
}
void AsmJsScanner::ConsumeCPPComment() {
for (;;) {
base::uc32 ch = stream_->Advance();
if (ch == '\n') {
preceded_by_newline_ = true;
return;
}
if (ch == kEndOfInputU) {
return;
}
}
}
void AsmJsScanner::ConsumeString(base::uc32 quote) {
// Only string allowed is 'use asm' / "use asm".
const char* expected = "use asm";
for (; *expected != '\0'; ++expected) {
if (stream_->Advance() != static_cast<base::uc32>(*expected)) {
token_ = kParseError;
return;
}
}
if (stream_->Advance() != quote) {
token_ = kParseError;
return;
}
token_ = kToken_UseAsm;
}
void AsmJsScanner::ConsumeCompareOrShift(base::uc32 ch) {
base::uc32 next_ch = stream_->Advance();
if (next_ch == '=') {
switch (ch) {
case '<':
token_ = kToken_LE;
break;
case '>':
token_ = kToken_GE;
break;
case '=':
token_ = kToken_EQ;
break;
case '!':
token_ = kToken_NE;
break;
default:
UNREACHABLE();
}
} else if (ch == '<' && next_ch == '<') {
token_ = kToken_SHL;
} else if (ch == '>' && next_ch == '>') {
if (stream_->Advance() == '>') {
token_ = kToken_SHR;
} else {
token_ = kToken_SAR;
stream_->Back();
}
} else {
stream_->Back();
token_ = ch;
}
}
bool AsmJsScanner::IsIdentifierStart(base::uc32 ch) {
return base::IsInRange(AsciiAlphaToLower(ch), 'a', 'z') || ch == '_' ||
ch == '$';
}
bool AsmJsScanner::IsIdentifierPart(base::uc32 ch) {
return IsAsciiIdentifier(ch);
}
bool AsmJsScanner::IsNumberStart(base::uc32 ch) {
return ch == '.' || IsDecimalDigit(ch);
}
} // namespace internal
} // namespace v8

177
deps/v8/src/asmjs/asm-scanner.h vendored Normal file
View File

@ -0,0 +1,177 @@
// Copyright 2017 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_ASMJS_ASM_SCANNER_H_
#define V8_ASMJS_ASM_SCANNER_H_
#include <memory>
#include <string>
#include <unordered_map>
#include "src/asmjs/asm-names.h"
#include "src/base/logging.h"
#include "src/base/strings.h"
namespace v8 {
namespace internal {
class Utf16CharacterStream;
// A custom scanner to extract the token stream needed to parse valid
// asm.js: http://asmjs.org/spec/latest/
// This scanner intentionally avoids the portion of JavaScript lexing
// that are not required to determine if code is valid asm.js code.
// * Strings are disallowed except for 'use asm'.
// * Only the subset of keywords needed to check asm.js invariants are
// included.
// * Identifiers are accumulated into local + global string tables
// (for performance).
class V8_EXPORT_PRIVATE AsmJsScanner {
public:
using token_t = int32_t;
explicit AsmJsScanner(Utf16CharacterStream* stream);
// Get current token.
token_t Token() const { return token_; }
// Get position of current token.
size_t Position() const { return position_; }
// Advance to the next token.
void Next();
// Back up by one token.
void Rewind();
// Get raw string for current identifier. Note that the returned string will
// become invalid when the scanner advances, create a copy to preserve it.
const std::string& GetIdentifierString() const {
// Identifier strings don't work after a rewind.
DCHECK(!rewind_);
return identifier_string_;
}
// Check if we just passed a newline.
bool IsPrecededByNewline() const {
// Newline tracking doesn't work if you back up.
DCHECK(!rewind_);
return preceded_by_newline_;
}
#if DEBUG
// Debug only method to go from a token back to its name.
// Slow, only use for debugging.
std::string Name(token_t token) const;
#endif
// Restores old position (token after that position). Note that it is not
// allowed to rewind right after a seek, because previous tokens are unknown.
void Seek(size_t pos);
// Select whether identifiers are resolved in global or local scope,
// and which scope new identifiers are added to.
void EnterLocalScope() { in_local_scope_ = true; }
void EnterGlobalScope() { in_local_scope_ = false; }
// Drop all current local identifiers.
void ResetLocals();
// Methods to check if a token is an identifier and which scope.
bool IsLocal() const { return IsLocal(Token()); }
bool IsGlobal() const { return IsGlobal(Token()); }
static bool IsLocal(token_t token) { return token <= kLocalsStart; }
static bool IsGlobal(token_t token) { return token >= kGlobalsStart; }
// Methods to find the index position of an identifier (count starting from
// 0 for each scope separately).
static size_t LocalIndex(token_t token) {
DCHECK(IsLocal(token));
return -(token - kLocalsStart);
}
static size_t GlobalIndex(token_t token) {
DCHECK(IsGlobal(token));
return token - kGlobalsStart;
}
// Methods to check if the current token is a numeric literal considered an
// asm.js "double" (contains a dot) or an "unsigned" (without a dot). Note
// that numbers without a dot outside the [0 .. 2^32) range are errors.
bool IsUnsigned() const { return Token() == kUnsigned; }
uint32_t AsUnsigned() const {
DCHECK(IsUnsigned());
return unsigned_value_;
}
bool IsDouble() const { return Token() == kDouble; }
double AsDouble() const {
DCHECK(IsDouble());
return double_value_;
}
// clang-format off
enum {
// [-10000-kMaxIdentifierCount, -10000) :: Local identifiers (counting
// backwards)
// [-10000 .. -1) :: Builtin tokens like keywords
// (also includes some special
// ones like end of input)
// 0 .. 255 :: Single char tokens
// 256 .. 256+kMaxIdentifierCount :: Global identifiers
kLocalsStart = -10000,
#define V(name, _junk1, _junk2, _junk3) kToken_##name,
STDLIB_MATH_FUNCTION_LIST(V)
STDLIB_ARRAY_TYPE_LIST(V)
#undef V
#define V(name, _junk1) kToken_##name,
STDLIB_MATH_VALUE_LIST(V)
#undef V
#define V(name) kToken_##name,
STDLIB_OTHER_LIST(V)
KEYWORD_NAME_LIST(V)
#undef V
#define V(rawname, name) kToken_##name,
LONG_SYMBOL_NAME_LIST(V)
#undef V
#define V(name, value, string_name) name = value,
SPECIAL_TOKEN_LIST(V)
#undef V
kGlobalsStart = 256,
};
// clang-format on
static constexpr base::uc32 kEndOfInputU =
static_cast<base::uc32>(kEndOfInput);
private:
Utf16CharacterStream* stream_;
token_t token_;
token_t preceding_token_;
token_t next_token_; // Only set when in {rewind} state.
size_t position_; // Corresponds to {token} position.
size_t preceding_position_; // Corresponds to {preceding_token} position.
size_t next_position_; // Only set when in {rewind} state.
bool rewind_;
std::string identifier_string_;
bool in_local_scope_;
std::unordered_map<std::string, token_t> local_names_;
std::unordered_map<std::string, token_t> global_names_;
std::unordered_map<std::string, token_t> property_names_;
int global_count_;
double double_value_;
uint32_t unsigned_value_;
bool preceded_by_newline_;
// Consume multiple characters.
void ConsumeIdentifier(base::uc32 ch);
void ConsumeNumber(base::uc32 ch);
bool ConsumeCComment();
void ConsumeCPPComment();
void ConsumeString(base::uc32 quote);
void ConsumeCompareOrShift(base::uc32 ch);
// Classify character categories.
bool IsIdentifierStart(base::uc32 ch);
bool IsIdentifierPart(base::uc32 ch);
bool IsNumberStart(base::uc32 ch);
};
} // namespace internal
} // namespace v8
#endif // V8_ASMJS_ASM_SCANNER_H_

308
deps/v8/src/asmjs/asm-types.cc vendored Normal file
View File

@ -0,0 +1,308 @@
// Copyright 2016 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.
#include "src/asmjs/asm-types.h"
#include <cinttypes>
namespace v8 {
namespace internal {
namespace wasm {
AsmCallableType* AsmType::AsCallableType() {
if (AsValueType() != nullptr) {
return nullptr;
}
return reinterpret_cast<AsmCallableType*>(this);
}
std::string AsmType::Name() {
AsmValueType* avt = this->AsValueType();
if (avt != nullptr) {
switch (avt->Bitset()) {
#define RETURN_TYPE_NAME(CamelName, string_name, number, parent_types) \
case AsmValueType::kAsm##CamelName: \
return string_name;
FOR_EACH_ASM_VALUE_TYPE_LIST(RETURN_TYPE_NAME)
#undef RETURN_TYPE_NAME
default:
UNREACHABLE();
}
}
return this->AsCallableType()->Name();
}
bool AsmType::IsExactly(AsmType* x, AsmType* y) {
// TODO(jpp): maybe this can become x == y.
if (x == nullptr) return y == nullptr;
AsmValueType* avt = x->AsValueType();
if (avt != nullptr) {
AsmValueType* tavt = y->AsValueType();
if (tavt == nullptr) {
return false;
}
return avt->Bitset() == tavt->Bitset();
}
// TODO(jpp): is it useful to allow non-value types to be tested with
// IsExactly?
return x == y;
}
bool AsmType::IsA(AsmType* that) {
// IsA is used for querying inheritance relationships. Therefore it is only
// meaningful for basic types.
if (auto* avt = this->AsValueType()) {
if (auto* tavt = that->AsValueType()) {
return (avt->Bitset() & tavt->Bitset()) == tavt->Bitset();
}
return false;
}
if (auto* as_callable = this->AsCallableType()) {
return as_callable->IsA(that);
}
UNREACHABLE();
}
int32_t AsmType::ElementSizeInBytes() {
auto* value = AsValueType();
if (value == nullptr) {
return AsmType::kNotHeapType;
}
switch (value->Bitset()) {
case AsmValueType::kAsmInt8Array:
case AsmValueType::kAsmUint8Array:
return 1;
case AsmValueType::kAsmInt16Array:
case AsmValueType::kAsmUint16Array:
return 2;
case AsmValueType::kAsmInt32Array:
case AsmValueType::kAsmUint32Array:
case AsmValueType::kAsmFloat32Array:
return 4;
case AsmValueType::kAsmFloat64Array:
return 8;
default:
return AsmType::kNotHeapType;
}
}
AsmType* AsmType::LoadType() {
auto* value = AsValueType();
if (value == nullptr) {
return AsmType::None();
}
switch (value->Bitset()) {
case AsmValueType::kAsmInt8Array:
case AsmValueType::kAsmUint8Array:
case AsmValueType::kAsmInt16Array:
case AsmValueType::kAsmUint16Array:
case AsmValueType::kAsmInt32Array:
case AsmValueType::kAsmUint32Array:
return AsmType::Intish();
case AsmValueType::kAsmFloat32Array:
return AsmType::FloatQ();
case AsmValueType::kAsmFloat64Array:
return AsmType::DoubleQ();
default:
return AsmType::None();
}
}
AsmType* AsmType::StoreType() {
auto* value = AsValueType();
if (value == nullptr) {
return AsmType::None();
}
switch (value->Bitset()) {
case AsmValueType::kAsmInt8Array:
case AsmValueType::kAsmUint8Array:
case AsmValueType::kAsmInt16Array:
case AsmValueType::kAsmUint16Array:
case AsmValueType::kAsmInt32Array:
case AsmValueType::kAsmUint32Array:
return AsmType::Intish();
case AsmValueType::kAsmFloat32Array:
return AsmType::FloatishDoubleQ();
case AsmValueType::kAsmFloat64Array:
return AsmType::FloatQDoubleQ();
default:
return AsmType::None();
}
}
bool AsmCallableType::IsA(AsmType* other) {
return other->AsCallableType() == this;
}
std::string AsmFunctionType::Name() {
std::string ret;
ret += "(";
for (size_t ii = 0; ii < args_.size(); ++ii) {
ret += args_[ii]->Name();
if (ii != args_.size() - 1) {
ret += ", ";
}
}
ret += ") -> ";
ret += return_type_->Name();
return ret;
}
namespace {
class AsmFroundType final : public AsmCallableType {
public:
friend AsmType;
AsmFroundType() : AsmCallableType() {}
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
std::string Name() override { return "fround"; }
};
} // namespace
AsmType* AsmType::FroundType(Zone* zone) {
auto* Fround = zone->New<AsmFroundType>();
return reinterpret_cast<AsmType*>(Fround);
}
bool AsmFroundType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (args.size() != 1) {
return false;
}
auto* arg = args[0];
if (!arg->IsA(AsmType::Floatish()) && !arg->IsA(AsmType::DoubleQ()) &&
!arg->IsA(AsmType::Signed()) && !arg->IsA(AsmType::Unsigned())) {
return false;
}
return true;
}
namespace {
class AsmMinMaxType final : public AsmCallableType {
private:
friend AsmType;
friend Zone;
AsmMinMaxType(AsmType* dest, AsmType* src)
: AsmCallableType(), return_type_(dest), arg_(src) {}
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override {
if (!AsmType::IsExactly(return_type_, return_type)) {
return false;
}
if (args.size() < 2) {
return false;
}
for (size_t ii = 0; ii < args.size(); ++ii) {
if (!args[ii]->IsA(arg_)) {
return false;
}
}
return true;
}
std::string Name() override {
return "(" + arg_->Name() + ", " + arg_->Name() + "...) -> " +
return_type_->Name();
}
AsmType* return_type_;
AsmType* arg_;
};
} // namespace
AsmType* AsmType::MinMaxType(Zone* zone, AsmType* dest, AsmType* src) {
DCHECK_NOT_NULL(dest->AsValueType());
DCHECK_NOT_NULL(src->AsValueType());
auto* MinMax = zone->New<AsmMinMaxType>(dest, src);
return reinterpret_cast<AsmType*>(MinMax);
}
bool AsmFunctionType::IsA(AsmType* other) {
auto* that = other->AsFunctionType();
if (that == nullptr) {
return false;
}
if (!AsmType::IsExactly(return_type_, that->return_type_)) {
return false;
}
if (args_.size() != that->args_.size()) {
return false;
}
for (size_t ii = 0; ii < args_.size(); ++ii) {
if (!AsmType::IsExactly(args_[ii], that->args_[ii])) {
return false;
}
}
return true;
}
bool AsmFunctionType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (!AsmType::IsExactly(return_type_, return_type)) {
return false;
}
if (args_.size() != args.size()) {
return false;
}
for (size_t ii = 0; ii < args_.size(); ++ii) {
if (!args[ii]->IsA(args_[ii])) {
return false;
}
}
return true;
}
std::string AsmOverloadedFunctionType::Name() {
std::string ret;
for (size_t ii = 0; ii < overloads_.size(); ++ii) {
if (ii != 0) {
ret += " /\\ ";
}
ret += overloads_[ii]->Name();
}
return ret;
}
bool AsmOverloadedFunctionType::CanBeInvokedWith(
AsmType* return_type, const ZoneVector<AsmType*>& args) {
for (size_t ii = 0; ii < overloads_.size(); ++ii) {
if (overloads_[ii]->AsCallableType()->CanBeInvokedWith(return_type, args)) {
return true;
}
}
return false;
}
void AsmOverloadedFunctionType::AddOverload(AsmType* overload) {
DCHECK_NOT_NULL(overload->AsCallableType());
overloads_.push_back(overload);
}
} // namespace wasm
} // namespace internal
} // namespace v8

247
deps/v8/src/asmjs/asm-types.h vendored Normal file
View File

@ -0,0 +1,247 @@
// Copyright 2016 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_ASMJS_ASM_TYPES_H_
#define V8_ASMJS_ASM_TYPES_H_
#include <string>
#include "src/base/compiler-specific.h"
#include "src/base/macros.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace wasm {
class AsmType;
class AsmFunctionType;
class AsmOverloadedFunctionType;
// List of V(CamelName, string_name, number, parent_types)
#define FOR_EACH_ASM_VALUE_TYPE_LIST(V) \
/* These tags are not types that are expressable in the asm source. They */ \
/* are used to express semantic information about the types they tag. */ \
V(Heap, "[]", 1, 0) \
V(FloatishDoubleQ, "floatish|double?", 2, 0) \
V(FloatQDoubleQ, "float?|double?", 3, 0) \
/* The following are actual types that appear in the asm source. */ \
V(Void, "void", 4, 0) \
V(Extern, "extern", 5, 0) \
V(DoubleQ, "double?", 6, kAsmFloatishDoubleQ | kAsmFloatQDoubleQ) \
V(Double, "double", 7, kAsmDoubleQ | kAsmExtern) \
V(Intish, "intish", 8, 0) \
V(Int, "int", 9, kAsmIntish) \
V(Signed, "signed", 10, kAsmInt | kAsmExtern) \
V(Unsigned, "unsigned", 11, kAsmInt) \
V(FixNum, "fixnum", 12, kAsmSigned | kAsmUnsigned) \
V(Floatish, "floatish", 13, kAsmFloatishDoubleQ) \
V(FloatQ, "float?", 14, kAsmFloatQDoubleQ | kAsmFloatish) \
V(Float, "float", 15, kAsmFloatQ) \
/* Types used for expressing the Heap accesses. */ \
V(Uint8Array, "Uint8Array", 16, kAsmHeap) \
V(Int8Array, "Int8Array", 17, kAsmHeap) \
V(Uint16Array, "Uint16Array", 18, kAsmHeap) \
V(Int16Array, "Int16Array", 19, kAsmHeap) \
V(Uint32Array, "Uint32Array", 20, kAsmHeap) \
V(Int32Array, "Int32Array", 21, kAsmHeap) \
V(Float32Array, "Float32Array", 22, kAsmHeap) \
V(Float64Array, "Float64Array", 23, kAsmHeap) \
/* None is used to represent errors in the type checker. */ \
V(None, "<none>", 31, 0)
// List of V(CamelName)
#define FOR_EACH_ASM_CALLABLE_TYPE_LIST(V) \
V(FunctionType) \
V(OverloadedFunctionType)
class AsmValueType {
public:
using bitset_t = uint32_t;
enum : uint32_t {
#define DEFINE_TAG(CamelName, string_name, number, parent_types) \
kAsm##CamelName = ((1u << (number)) | (parent_types)),
FOR_EACH_ASM_VALUE_TYPE_LIST(DEFINE_TAG)
#undef DEFINE_TAG
kAsmUnknown = 0,
kAsmValueTypeTag = 1u
};
private:
friend class AsmType;
static AsmValueType* AsValueType(AsmType* type) {
if ((reinterpret_cast<uintptr_t>(type) & kAsmValueTypeTag) ==
kAsmValueTypeTag) {
return reinterpret_cast<AsmValueType*>(type);
}
return nullptr;
}
bitset_t Bitset() const {
DCHECK_EQ(reinterpret_cast<uintptr_t>(this) & kAsmValueTypeTag,
kAsmValueTypeTag);
return static_cast<bitset_t>(reinterpret_cast<uintptr_t>(this) &
~kAsmValueTypeTag);
}
static AsmType* New(bitset_t bits) {
DCHECK_EQ((bits & kAsmValueTypeTag), 0u);
return reinterpret_cast<AsmType*>(
static_cast<uintptr_t>(bits | kAsmValueTypeTag));
}
// AsmValueTypes can't be created except through AsmValueType::New.
DISALLOW_IMPLICIT_CONSTRUCTORS(AsmValueType);
};
class V8_EXPORT_PRIVATE AsmCallableType : public NON_EXPORTED_BASE(ZoneObject) {
public:
AsmCallableType(const AsmCallableType&) = delete;
AsmCallableType& operator=(const AsmCallableType&) = delete;
virtual std::string Name() = 0;
virtual bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) = 0;
#define DECLARE_CAST(CamelName) \
virtual Asm##CamelName* As##CamelName() { return nullptr; }
FOR_EACH_ASM_CALLABLE_TYPE_LIST(DECLARE_CAST)
#undef DECLARE_CAST
protected:
AsmCallableType() = default;
virtual ~AsmCallableType() = default;
virtual bool IsA(AsmType* other);
private:
friend class AsmType;
};
class V8_EXPORT_PRIVATE AsmFunctionType final : public AsmCallableType {
public:
AsmFunctionType(const AsmFunctionType&) = delete;
AsmFunctionType& operator=(const AsmFunctionType&) = delete;
AsmFunctionType* AsFunctionType() final { return this; }
void AddArgument(AsmType* type) { args_.push_back(type); }
const ZoneVector<AsmType*>& Arguments() const { return args_; }
AsmType* ReturnType() const { return return_type_; }
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
protected:
AsmFunctionType(Zone* zone, AsmType* return_type)
: return_type_(return_type), args_(zone) {}
private:
friend AsmType;
friend Zone;
std::string Name() override;
bool IsA(AsmType* other) override;
AsmType* return_type_;
ZoneVector<AsmType*> args_;
};
class V8_EXPORT_PRIVATE AsmOverloadedFunctionType final
: public AsmCallableType {
public:
AsmOverloadedFunctionType* AsOverloadedFunctionType() override {
return this;
}
void AddOverload(AsmType* overload);
private:
friend AsmType;
friend Zone;
explicit AsmOverloadedFunctionType(Zone* zone) : overloads_(zone) {}
std::string Name() override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
ZoneVector<AsmType*> overloads_;
DISALLOW_IMPLICIT_CONSTRUCTORS(AsmOverloadedFunctionType);
};
class V8_EXPORT_PRIVATE AsmType {
public:
#define DEFINE_CONSTRUCTOR(CamelName, string_name, number, parent_types) \
static AsmType* CamelName() { \
return AsmValueType::New(AsmValueType::kAsm##CamelName); \
}
FOR_EACH_ASM_VALUE_TYPE_LIST(DEFINE_CONSTRUCTOR)
#undef DEFINE_CONSTRUCTOR
#define DEFINE_CAST(CamelCase) \
Asm##CamelCase* As##CamelCase() { \
if (AsValueType() != nullptr) { \
return nullptr; \
} \
return reinterpret_cast<AsmCallableType*>(this)->As##CamelCase(); \
}
FOR_EACH_ASM_CALLABLE_TYPE_LIST(DEFINE_CAST)
#undef DEFINE_CAST
AsmValueType* AsValueType() { return AsmValueType::AsValueType(this); }
AsmCallableType* AsCallableType();
// A function returning ret. Callers still need to invoke AddArgument with the
// returned type to fully create this type.
static AsmType* Function(Zone* zone, AsmType* ret) {
AsmFunctionType* f = zone->New<AsmFunctionType>(zone, ret);
return reinterpret_cast<AsmType*>(f);
}
// Overloaded function types. Not creatable by asm source, but useful to
// represent the overloaded stdlib functions.
static AsmType* OverloadedFunction(Zone* zone) {
auto* f = zone->New<AsmOverloadedFunctionType>(zone);
return reinterpret_cast<AsmType*>(f);
}
// The type for fround(src).
static AsmType* FroundType(Zone* zone);
// The (variadic) type for min and max.
static AsmType* MinMaxType(Zone* zone, AsmType* dest, AsmType* src);
std::string Name();
// IsExactly returns true if x is the exact same type as y. For
// non-value types (e.g., callables), this returns x == y.
static bool IsExactly(AsmType* x, AsmType* y);
// IsA is used to query whether this is an instance of that (i.e., if this is
// a type derived from that.) For non-value types (e.g., callables), this
// returns this == that.
bool IsA(AsmType* that);
// The following methods are meant to be used for inspecting the traits of
// element types for the heap view types.
enum : int32_t { kNotHeapType = -1 };
// Returns the element size if this is a heap type. Otherwise returns
// kNotHeapType.
int32_t ElementSizeInBytes();
// Returns the load type if this is a heap type. AsmType::None is returned if
// this is not a heap type.
AsmType* LoadType();
// Returns the store type if this is a heap type. AsmType::None is returned if
// this is not a heap type.
AsmType* StoreType();
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_ASMJS_ASM_TYPES_H_

14
deps/v8/src/ast/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>Parser"
}
buganizer_public: {
component_id: 1456517
}

4
deps/v8/src/ast/OWNERS vendored Normal file
View File

@ -0,0 +1,4 @@
leszeks@chromium.org
marja@chromium.org
syg@chromium.org
verwaest@chromium.org

View File

@ -0,0 +1,119 @@
// Copyright 2016 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.
#include "src/ast/ast-function-literal-id-reindexer.h"
#include "src/ast/ast.h"
namespace v8 {
namespace internal {
AstFunctionLiteralIdReindexer::AstFunctionLiteralIdReindexer(size_t stack_limit,
int delta)
: AstTraversalVisitor(stack_limit), delta_(delta) {}
AstFunctionLiteralIdReindexer::~AstFunctionLiteralIdReindexer() = default;
void AstFunctionLiteralIdReindexer::Reindex(Expression* pattern) {
#ifdef DEBUG
visited_.clear();
#endif
Visit(pattern);
CheckVisited(pattern);
}
void AstFunctionLiteralIdReindexer::VisitFunctionLiteral(FunctionLiteral* lit) {
// Make sure we're not already in the visited set.
DCHECK(visited_.insert(lit).second);
AstTraversalVisitor::VisitFunctionLiteral(lit);
lit->set_function_literal_id(lit->function_literal_id() + delta_);
}
void AstFunctionLiteralIdReindexer::VisitCall(Call* expr) {
AstTraversalVisitor::VisitCall(expr);
if (expr->is_possibly_eval()) {
expr->adjust_eval_scope_info_index(delta_);
}
}
void AstFunctionLiteralIdReindexer::VisitClassLiteral(ClassLiteral* expr) {
// Manually visit the class literal so that we can change the property walk.
// This should be kept in-sync with AstTraversalVisitor::VisitClassLiteral.
if (expr->extends() != nullptr) {
Visit(expr->extends());
}
Visit(expr->constructor());
if (expr->static_initializer() != nullptr) {
Visit(expr->static_initializer());
}
if (expr->instance_members_initializer_function() != nullptr) {
Visit(expr->instance_members_initializer_function());
}
ZonePtrList<ClassLiteral::Property>* private_members =
expr->private_members();
for (int i = 0; i < private_members->length(); ++i) {
ClassLiteralProperty* prop = private_members->at(i);
// Private fields have their key and value present in
// instance_members_initializer_function, so they will
// already have been visited.
if (prop->kind() == ClassLiteralProperty::Kind::FIELD) {
CheckVisited(prop->value());
} else {
Visit(prop->value());
}
}
ZonePtrList<ClassLiteral::Property>* props = expr->public_members();
for (int i = 0; i < props->length(); ++i) {
ClassLiteralProperty* prop = props->at(i);
// Public fields with computed names have their key
// and value present in instance_members_initializer_function, so they will
// already have been visited.
if (prop->is_computed_name() &&
prop->kind() == ClassLiteralProperty::Kind::FIELD) {
if (!prop->key()->IsLiteral()) {
CheckVisited(prop->key());
}
CheckVisited(prop->value());
} else {
if (!prop->key()->IsLiteral()) {
Visit(prop->key());
}
Visit(prop->value());
}
}
}
#ifdef DEBUG
namespace {
class AstFunctionLiteralIdReindexChecker final
: public AstTraversalVisitor<AstFunctionLiteralIdReindexChecker> {
public:
AstFunctionLiteralIdReindexChecker(size_t stack_limit,
const std::set<FunctionLiteral*>* visited)
: AstTraversalVisitor(stack_limit), visited_(visited) {}
void VisitFunctionLiteral(FunctionLiteral* lit) {
// TODO(leszeks): It would be nice to print the unvisited function literal
// here, but that requires more advanced DCHECK support with formatting.
DCHECK(visited_->find(lit) != visited_->end());
}
private:
const std::set<FunctionLiteral*>* visited_;
};
} // namespace
void AstFunctionLiteralIdReindexer::CheckVisited(Expression* expr) {
AstFunctionLiteralIdReindexChecker(stack_limit(), &visited_).Visit(expr);
}
#endif
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,53 @@
// Copyright 2016 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_AST_AST_FUNCTION_LITERAL_ID_REINDEXER_H_
#define V8_AST_AST_FUNCTION_LITERAL_ID_REINDEXER_H_
#include "src/ast/ast-traversal-visitor.h"
#ifdef DEBUG
#include <set>
#endif
namespace v8 {
namespace internal {
// Changes the ID of all FunctionLiterals in the given Expression by adding the
// given delta.
class AstFunctionLiteralIdReindexer final
: public AstTraversalVisitor<AstFunctionLiteralIdReindexer> {
public:
AstFunctionLiteralIdReindexer(size_t stack_limit, int delta);
AstFunctionLiteralIdReindexer(const AstFunctionLiteralIdReindexer&) = delete;
AstFunctionLiteralIdReindexer& operator=(
const AstFunctionLiteralIdReindexer&) = delete;
~AstFunctionLiteralIdReindexer();
void Reindex(Expression* pattern);
// AstTraversalVisitor implementation.
void VisitFunctionLiteral(FunctionLiteral* lit);
void VisitClassLiteral(ClassLiteral* lit);
void VisitCall(Call* lit);
private:
int delta_;
#ifdef DEBUG
// Visited set, only used in DCHECKs for verification.
std::set<FunctionLiteral*> visited_;
// Visit all function literals, checking if they have already been visited
// (are in the visited set).
void CheckVisited(Expression* expr);
#else
void CheckVisited(Expression* expr) {}
#endif
};
} // namespace internal
} // namespace v8
#endif // V8_AST_AST_FUNCTION_LITERAL_ID_REINDEXER_H_

449
deps/v8/src/ast/ast-source-ranges.h vendored Normal file
View File

@ -0,0 +1,449 @@
// Copyright 2017 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_AST_AST_SOURCE_RANGES_H_
#define V8_AST_AST_SOURCE_RANGES_H_
#include "src/ast/ast.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
// Specifies a range within the source code. {start} is 0-based and inclusive,
// {end} is 0-based and exclusive.
struct SourceRange {
SourceRange() : SourceRange(kNoSourcePosition, kNoSourcePosition) {}
SourceRange(int start, int end) : start(start), end(end) {}
bool IsEmpty() const { return start == kNoSourcePosition; }
static SourceRange Empty() { return SourceRange(); }
static SourceRange OpenEnded(int32_t start) {
return SourceRange(start, kNoSourcePosition);
}
static SourceRange ContinuationOf(const SourceRange& that,
int end = kNoSourcePosition) {
return that.IsEmpty() ? Empty() : SourceRange(that.end, end);
}
static constexpr int kFunctionLiteralSourcePosition = -2;
static_assert(kFunctionLiteralSourcePosition == kNoSourcePosition - 1);
// Source ranges associated with a function literal do not contain real
// source positions; instead, they are created with special marker values.
// These are later recognized and rewritten during processing in
// Coverage::Collect().
static SourceRange FunctionLiteralMarkerRange() {
return {kFunctionLiteralSourcePosition, kFunctionLiteralSourcePosition};
}
int32_t start, end;
};
// The list of ast node kinds that have associated source ranges. Note that this
// macro is not undefined at the end of this file.
#define AST_SOURCE_RANGE_LIST(V) \
V(BinaryOperation) \
V(Block) \
V(CaseClause) \
V(ConditionalChain) \
V(Conditional) \
V(Expression) \
V(FunctionLiteral) \
V(IfStatement) \
V(IterationStatement) \
V(JumpStatement) \
V(NaryOperation) \
V(Suspend) \
V(SwitchStatement) \
V(Throw) \
V(TryCatchStatement) \
V(TryFinallyStatement)
enum class SourceRangeKind {
kBody,
kCatch,
kContinuation,
kElse,
kFinally,
kRight,
kThen,
};
class AstNodeSourceRanges : public ZoneObject {
public:
virtual ~AstNodeSourceRanges() = default;
virtual SourceRange GetRange(SourceRangeKind kind) = 0;
virtual bool HasRange(SourceRangeKind kind) = 0;
virtual void RemoveContinuationRange() { UNREACHABLE(); }
};
class BinaryOperationSourceRanges final : public AstNodeSourceRanges {
public:
explicit BinaryOperationSourceRanges(const SourceRange& right_range)
: right_range_(right_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
return right_range_;
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kRight;
}
private:
SourceRange right_range_;
};
class ContinuationSourceRanges : public AstNodeSourceRanges {
public:
explicit ContinuationSourceRanges(int32_t continuation_position)
: continuation_position_(continuation_position) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
return SourceRange::OpenEnded(continuation_position_);
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kContinuation;
}
void RemoveContinuationRange() override {
DCHECK(HasRange(SourceRangeKind::kContinuation));
continuation_position_ = kNoSourcePosition;
}
private:
int32_t continuation_position_;
};
class BlockSourceRanges final : public ContinuationSourceRanges {
public:
explicit BlockSourceRanges(int32_t continuation_position)
: ContinuationSourceRanges(continuation_position) {}
};
class CaseClauseSourceRanges final : public AstNodeSourceRanges {
public:
explicit CaseClauseSourceRanges(const SourceRange& body_range)
: body_range_(body_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
return body_range_;
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kBody;
}
private:
SourceRange body_range_;
};
class ConditionalChainSourceRanges final : public AstNodeSourceRanges {
public:
explicit ConditionalChainSourceRanges(Zone* zone)
: then_ranges_(zone), else_ranges_(zone) {}
SourceRange GetRangeAtIndex(SourceRangeKind kind, size_t index) {
if (kind == SourceRangeKind::kThen) {
DCHECK_LT(index, then_ranges_.size());
return then_ranges_[index];
}
DCHECK_EQ(kind, SourceRangeKind::kElse);
DCHECK_LT(index, else_ranges_.size());
return else_ranges_[index];
}
void AddThenRanges(const SourceRange& range) {
then_ranges_.push_back(range);
}
void AddElseRange(const SourceRange& else_range) {
else_ranges_.push_back(else_range);
}
size_t RangeCount() const { return then_ranges_.size(); }
SourceRange GetRange(SourceRangeKind kind) override { UNREACHABLE(); }
bool HasRange(SourceRangeKind kind) override { return false; }
private:
ZoneVector<SourceRange> then_ranges_;
ZoneVector<SourceRange> else_ranges_;
};
class ConditionalSourceRanges final : public AstNodeSourceRanges {
public:
explicit ConditionalSourceRanges(const SourceRange& then_range,
const SourceRange& else_range)
: then_range_(then_range), else_range_(else_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
switch (kind) {
case SourceRangeKind::kThen:
return then_range_;
case SourceRangeKind::kElse:
return else_range_;
default:
UNREACHABLE();
}
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kThen || kind == SourceRangeKind::kElse;
}
private:
SourceRange then_range_;
SourceRange else_range_;
};
class FunctionLiteralSourceRanges final : public AstNodeSourceRanges {
public:
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
return SourceRange::FunctionLiteralMarkerRange();
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kBody;
}
};
class IfStatementSourceRanges final : public AstNodeSourceRanges {
public:
explicit IfStatementSourceRanges(const SourceRange& then_range,
const SourceRange& else_range)
: then_range_(then_range), else_range_(else_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
switch (kind) {
case SourceRangeKind::kElse:
return else_range_;
case SourceRangeKind::kThen:
return then_range_;
case SourceRangeKind::kContinuation: {
if (!has_continuation_) return SourceRange::Empty();
const SourceRange& trailing_range =
else_range_.IsEmpty() ? then_range_ : else_range_;
return SourceRange::ContinuationOf(trailing_range);
}
default:
UNREACHABLE();
}
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kThen || kind == SourceRangeKind::kElse ||
kind == SourceRangeKind::kContinuation;
}
void RemoveContinuationRange() override {
DCHECK(HasRange(SourceRangeKind::kContinuation));
has_continuation_ = false;
}
private:
SourceRange then_range_;
SourceRange else_range_;
bool has_continuation_ = true;
};
class IterationStatementSourceRanges final : public AstNodeSourceRanges {
public:
explicit IterationStatementSourceRanges(const SourceRange& body_range)
: body_range_(body_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
switch (kind) {
case SourceRangeKind::kBody:
return body_range_;
case SourceRangeKind::kContinuation:
if (!has_continuation_) return SourceRange::Empty();
return SourceRange::ContinuationOf(body_range_);
default:
UNREACHABLE();
}
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kBody ||
kind == SourceRangeKind::kContinuation;
}
void RemoveContinuationRange() override {
DCHECK(HasRange(SourceRangeKind::kContinuation));
has_continuation_ = false;
}
private:
SourceRange body_range_;
bool has_continuation_ = true;
};
class JumpStatementSourceRanges final : public ContinuationSourceRanges {
public:
explicit JumpStatementSourceRanges(int32_t continuation_position)
: ContinuationSourceRanges(continuation_position) {}
};
class NaryOperationSourceRanges final : public AstNodeSourceRanges {
public:
NaryOperationSourceRanges(Zone* zone, const SourceRange& range)
: ranges_(zone) {
AddRange(range);
}
SourceRange GetRangeAtIndex(size_t index) {
DCHECK(index < ranges_.size());
return ranges_[index];
}
void AddRange(const SourceRange& range) { ranges_.push_back(range); }
size_t RangeCount() const { return ranges_.size(); }
SourceRange GetRange(SourceRangeKind kind) override { UNREACHABLE(); }
bool HasRange(SourceRangeKind kind) override { return false; }
private:
ZoneVector<SourceRange> ranges_;
};
class ExpressionSourceRanges final : public AstNodeSourceRanges {
public:
explicit ExpressionSourceRanges(const SourceRange& right_range)
: right_range_(right_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
return right_range_;
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kRight;
}
private:
SourceRange right_range_;
};
class SuspendSourceRanges final : public ContinuationSourceRanges {
public:
explicit SuspendSourceRanges(int32_t continuation_position)
: ContinuationSourceRanges(continuation_position) {}
};
class SwitchStatementSourceRanges final : public ContinuationSourceRanges {
public:
explicit SwitchStatementSourceRanges(int32_t continuation_position)
: ContinuationSourceRanges(continuation_position) {}
};
class ThrowSourceRanges final : public ContinuationSourceRanges {
public:
explicit ThrowSourceRanges(int32_t continuation_position)
: ContinuationSourceRanges(continuation_position) {}
};
class TryCatchStatementSourceRanges final : public AstNodeSourceRanges {
public:
explicit TryCatchStatementSourceRanges(const SourceRange& catch_range)
: catch_range_(catch_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
switch (kind) {
case SourceRangeKind::kCatch:
return catch_range_;
case SourceRangeKind::kContinuation:
if (!has_continuation_) return SourceRange::Empty();
return SourceRange::ContinuationOf(catch_range_);
default:
UNREACHABLE();
}
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kCatch ||
kind == SourceRangeKind::kContinuation;
}
void RemoveContinuationRange() override {
DCHECK(HasRange(SourceRangeKind::kContinuation));
has_continuation_ = false;
}
private:
SourceRange catch_range_;
bool has_continuation_ = true;
};
class TryFinallyStatementSourceRanges final : public AstNodeSourceRanges {
public:
explicit TryFinallyStatementSourceRanges(const SourceRange& finally_range)
: finally_range_(finally_range) {}
SourceRange GetRange(SourceRangeKind kind) override {
DCHECK(HasRange(kind));
switch (kind) {
case SourceRangeKind::kFinally:
return finally_range_;
case SourceRangeKind::kContinuation:
if (!has_continuation_) return SourceRange::Empty();
return SourceRange::ContinuationOf(finally_range_);
default:
UNREACHABLE();
}
}
bool HasRange(SourceRangeKind kind) override {
return kind == SourceRangeKind::kFinally ||
kind == SourceRangeKind::kContinuation;
}
void RemoveContinuationRange() override {
DCHECK(HasRange(SourceRangeKind::kContinuation));
has_continuation_ = false;
}
private:
SourceRange finally_range_;
bool has_continuation_ = true;
};
// Maps ast node pointers to associated source ranges. The parser creates these
// mappings and the bytecode generator consumes them.
class SourceRangeMap final : public ZoneObject {
public:
explicit SourceRangeMap(Zone* zone) : map_(zone) {}
AstNodeSourceRanges* Find(ZoneObject* node) {
auto it = map_.find(node);
if (it == map_.end()) return nullptr;
return it->second;
}
// Type-checked insertion.
#define DEFINE_MAP_INSERT(type) \
void Insert(type* node, type##SourceRanges* ranges) { \
DCHECK_NOT_NULL(node); \
map_.emplace(node, ranges); \
}
AST_SOURCE_RANGE_LIST(DEFINE_MAP_INSERT)
#undef DEFINE_MAP_INSERT
private:
ZoneMap<ZoneObject*, AstNodeSourceRanges*> map_;
};
} // namespace internal
} // namespace v8
#endif // V8_AST_AST_SOURCE_RANGES_H_

621
deps/v8/src/ast/ast-traversal-visitor.h vendored Normal file
View File

@ -0,0 +1,621 @@
// Copyright 2016 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_AST_AST_TRAVERSAL_VISITOR_H_
#define V8_AST_AST_TRAVERSAL_VISITOR_H_
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/execution/isolate.h"
namespace v8 {
namespace internal {
// ----------------------------------------------------------------------------
// Traversal visitor
// - fully traverses the entire AST.
//
// Sub-class should parametrize AstTraversalVisitor with itself, e.g.:
// class SpecificVisitor : public AstTraversalVisitor<SpecificVisitor> { ... }
//
// It invokes VisitNode on each AST node, before proceeding with its subtrees.
// It invokes VisitExpression (after VisitNode) on each AST node that is an
// expression, before proceeding with its subtrees.
// It proceeds with the subtrees only if these two methods return true.
// Sub-classes may override VisitNode and VisitExpressions, whose implementation
// is dummy here. Or they may override the specific Visit* methods.
template <class Subclass>
class AstTraversalVisitor : public AstVisitor<Subclass> {
public:
explicit AstTraversalVisitor(Isolate* isolate, AstNode* root = nullptr);
explicit AstTraversalVisitor(uintptr_t stack_limit, AstNode* root = nullptr);
AstTraversalVisitor(const AstTraversalVisitor&) = delete;
AstTraversalVisitor& operator=(const AstTraversalVisitor&) = delete;
void Run() {
DCHECK_NOT_NULL(root_);
Visit(root_);
}
bool VisitNode(AstNode* node) { return true; }
bool VisitExpression(Expression* node) { return true; }
// Iteration left-to-right.
void VisitDeclarations(Declaration::List* declarations);
void VisitStatements(const ZonePtrList<Statement>* statements);
// Individual nodes
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
protected:
int depth() const { return depth_; }
private:
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
AstNode* root_;
int depth_;
};
// ----------------------------------------------------------------------------
// Implementation of AstTraversalVisitor
#define PROCESS_NODE(node) do { \
if (!(this->impl()->VisitNode(node))) return; \
} while (false)
#define PROCESS_EXPRESSION(node) do { \
PROCESS_NODE(node); \
if (!(this->impl()->VisitExpression(node))) return; \
} while (false)
#define RECURSE(call) \
do { \
DCHECK(!HasStackOverflow()); \
this->impl()->call; \
if (HasStackOverflow()) return; \
} while (false)
#define RECURSE_EXPRESSION(call) \
do { \
DCHECK(!HasStackOverflow()); \
++depth_; \
this->impl()->call; \
--depth_; \
if (HasStackOverflow()) return; \
} while (false)
template <class Subclass>
AstTraversalVisitor<Subclass>::AstTraversalVisitor(Isolate* isolate,
AstNode* root)
: root_(root), depth_(0) {
InitializeAstVisitor(isolate);
}
template <class Subclass>
AstTraversalVisitor<Subclass>::AstTraversalVisitor(uintptr_t stack_limit,
AstNode* root)
: root_(root), depth_(0) {
InitializeAstVisitor(stack_limit);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitDeclarations(
Declaration::List* decls) {
for (Declaration* decl : *decls) {
RECURSE(Visit(decl));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitStatements(
const ZonePtrList<Statement>* stmts) {
for (int i = 0; i < stmts->length(); ++i) {
Statement* stmt = stmts->at(i);
RECURSE(Visit(stmt));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitVariableDeclaration(
VariableDeclaration* decl) {
PROCESS_NODE(decl);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitFunctionDeclaration(
FunctionDeclaration* decl) {
PROCESS_NODE(decl);
RECURSE(Visit(decl->fun()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitBlock(Block* stmt) {
PROCESS_NODE(stmt);
if (stmt->scope() != nullptr) {
RECURSE_EXPRESSION(VisitDeclarations(stmt->scope()->declarations()));
}
RECURSE(VisitStatements(stmt->statements()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitExpressionStatement(
ExpressionStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitEmptyStatement(EmptyStatement* stmt) {}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSloppyBlockFunctionStatement(
SloppyBlockFunctionStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->statement()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitIfStatement(IfStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->condition()));
RECURSE(Visit(stmt->then_statement()));
RECURSE(Visit(stmt->else_statement()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitContinueStatement(
ContinueStatement* stmt) {
PROCESS_NODE(stmt);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitBreakStatement(BreakStatement* stmt) {
PROCESS_NODE(stmt);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitReturnStatement(
ReturnStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitWithStatement(WithStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->expression()));
RECURSE(Visit(stmt->statement()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSwitchStatement(
SwitchStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->tag()));
ZonePtrList<CaseClause>* clauses = stmt->cases();
for (int i = 0; i < clauses->length(); ++i) {
CaseClause* clause = clauses->at(i);
if (!clause->is_default()) {
Expression* label = clause->label();
RECURSE(Visit(label));
}
const ZonePtrList<Statement>* stmts = clause->statements();
RECURSE(VisitStatements(stmts));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitDoWhileStatement(
DoWhileStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->body()));
RECURSE(Visit(stmt->cond()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitWhileStatement(WhileStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->cond()));
RECURSE(Visit(stmt->body()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitForStatement(ForStatement* stmt) {
PROCESS_NODE(stmt);
if (stmt->init() != nullptr) {
RECURSE(Visit(stmt->init()));
}
if (stmt->cond() != nullptr) {
RECURSE(Visit(stmt->cond()));
}
if (stmt->next() != nullptr) {
RECURSE(Visit(stmt->next()));
}
RECURSE(Visit(stmt->body()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitForInStatement(ForInStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->each()));
RECURSE(Visit(stmt->subject()));
RECURSE(Visit(stmt->body()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitForOfStatement(ForOfStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->each()));
RECURSE(Visit(stmt->subject()));
RECURSE(Visit(stmt->body()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitTryCatchStatement(
TryCatchStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->try_block()));
RECURSE(Visit(stmt->catch_block()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitTryFinallyStatement(
TryFinallyStatement* stmt) {
PROCESS_NODE(stmt);
RECURSE(Visit(stmt->try_block()));
RECURSE(Visit(stmt->finally_block()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitDebuggerStatement(
DebuggerStatement* stmt) {
PROCESS_NODE(stmt);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitFunctionLiteral(
FunctionLiteral* expr) {
PROCESS_EXPRESSION(expr);
DeclarationScope* scope = expr->scope();
RECURSE_EXPRESSION(VisitDeclarations(scope->declarations()));
// A lazily parsed function literal won't have a body.
if (expr->scope()->was_lazily_parsed()) return;
RECURSE_EXPRESSION(VisitStatements(expr->body()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitNativeFunctionLiteral(
NativeFunctionLiteral* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitConditionalChain(
ConditionalChain* expr) {
PROCESS_EXPRESSION(expr);
for (size_t i = 0; i < expr->conditional_chain_length(); ++i) {
RECURSE_EXPRESSION(Visit(expr->condition_at(i)));
RECURSE_EXPRESSION(Visit(expr->then_expression_at(i)));
}
RECURSE(Visit(expr->else_expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitConditional(Conditional* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->condition()));
RECURSE_EXPRESSION(Visit(expr->then_expression()));
RECURSE_EXPRESSION(Visit(expr->else_expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitVariableProxy(VariableProxy* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitLiteral(Literal* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitRegExpLiteral(RegExpLiteral* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitObjectLiteral(ObjectLiteral* expr) {
PROCESS_EXPRESSION(expr);
const ZonePtrList<ObjectLiteralProperty>* props = expr->properties();
for (int i = 0; i < props->length(); ++i) {
ObjectLiteralProperty* prop = props->at(i);
RECURSE_EXPRESSION(Visit(prop->key()));
RECURSE_EXPRESSION(Visit(prop->value()));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitArrayLiteral(ArrayLiteral* expr) {
PROCESS_EXPRESSION(expr);
const ZonePtrList<Expression>* values = expr->values();
for (int i = 0; i < values->length(); ++i) {
Expression* value = values->at(i);
RECURSE_EXPRESSION(Visit(value));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitAssignment(Assignment* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->target()));
RECURSE_EXPRESSION(Visit(expr->value()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCompoundAssignment(
CompoundAssignment* expr) {
VisitAssignment(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitYield(Yield* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitYieldStar(YieldStar* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitAwait(Await* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitThrow(Throw* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->exception()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitOptionalChain(OptionalChain* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitProperty(Property* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->obj()));
RECURSE_EXPRESSION(Visit(expr->key()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCall(Call* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
const ZonePtrList<Expression>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
RECURSE_EXPRESSION(Visit(arg));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCallNew(CallNew* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
const ZonePtrList<Expression>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
RECURSE_EXPRESSION(Visit(arg));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCallRuntime(CallRuntime* expr) {
PROCESS_EXPRESSION(expr);
const ZonePtrList<Expression>* args = expr->arguments();
for (int i = 0; i < args->length(); ++i) {
Expression* arg = args->at(i);
RECURSE_EXPRESSION(Visit(arg));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitUnaryOperation(UnaryOperation* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCountOperation(CountOperation* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitBinaryOperation(
BinaryOperation* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->left()));
RECURSE_EXPRESSION(Visit(expr->right()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitNaryOperation(NaryOperation* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->first()));
for (size_t i = 0; i < expr->subsequent_length(); ++i) {
RECURSE_EXPRESSION(Visit(expr->subsequent(i)));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitCompareOperation(
CompareOperation* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->left()));
RECURSE_EXPRESSION(Visit(expr->right()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitThisExpression(ThisExpression* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitClassLiteral(ClassLiteral* expr) {
PROCESS_EXPRESSION(expr);
if (expr->extends() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->extends()));
}
RECURSE_EXPRESSION(Visit(expr->constructor()));
if (expr->static_initializer() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->static_initializer()));
}
if (expr->instance_members_initializer_function() != nullptr) {
RECURSE_EXPRESSION(Visit(expr->instance_members_initializer_function()));
}
ZonePtrList<ClassLiteral::Property>* private_members =
expr->private_members();
for (int i = 0; i < private_members->length(); ++i) {
ClassLiteralProperty* prop = private_members->at(i);
RECURSE_EXPRESSION(Visit(prop->value()));
}
ZonePtrList<ClassLiteral::Property>* props = expr->public_members();
for (int i = 0; i < props->length(); ++i) {
ClassLiteralProperty* prop = props->at(i);
if (!prop->key()->IsLiteral()) {
RECURSE_EXPRESSION(Visit(prop->key()));
}
RECURSE_EXPRESSION(Visit(prop->value()));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitInitializeClassMembersStatement(
InitializeClassMembersStatement* stmt) {
PROCESS_NODE(stmt);
ZonePtrList<ClassLiteral::Property>* props = stmt->fields();
for (int i = 0; i < props->length(); ++i) {
ClassLiteralProperty* prop = props->at(i);
if (!prop->key()->IsLiteral()) {
RECURSE(Visit(prop->key()));
}
RECURSE(Visit(prop->value()));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitInitializeClassStaticElementsStatement(
InitializeClassStaticElementsStatement* stmt) {
PROCESS_NODE(stmt);
ZonePtrList<ClassLiteral::StaticElement>* elements = stmt->elements();
for (int i = 0; i < elements->length(); ++i) {
ClassLiteral::StaticElement* element = elements->at(i);
switch (element->kind()) {
case ClassLiteral::StaticElement::PROPERTY: {
ClassLiteral::Property* prop = element->property();
if (!prop->key()->IsLiteral()) {
RECURSE(Visit(prop->key()));
}
RECURSE(Visit(prop->value()));
break;
}
case ClassLiteral::StaticElement::STATIC_BLOCK:
RECURSE(Visit(element->static_block()));
break;
}
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitAutoAccessorGetterBody(
AutoAccessorGetterBody* stmt) {
PROCESS_NODE(stmt);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitAutoAccessorSetterBody(
AutoAccessorSetterBody* stmt) {
PROCESS_NODE(stmt);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSpread(Spread* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitEmptyParentheses(
EmptyParentheses* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitGetTemplateObject(
GetTemplateObject* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitTemplateLiteral(
TemplateLiteral* expr) {
PROCESS_EXPRESSION(expr);
for (Expression* sub : *expr->substitutions()) {
RECURSE_EXPRESSION(Visit(sub));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitImportCallExpression(
ImportCallExpression* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->specifier()));
if (expr->import_options()) {
RECURSE_EXPRESSION(Visit(expr->import_options()));
}
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSuperPropertyReference(
SuperPropertyReference* expr) {
PROCESS_EXPRESSION(expr);
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSuperCallReference(
SuperCallReference* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(VisitVariableProxy(expr->new_target_var()));
RECURSE_EXPRESSION(VisitVariableProxy(expr->this_function_var()));
}
template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitSuperCallForwardArgs(
SuperCallForwardArgs* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->expression()));
}
#undef PROCESS_NODE
#undef PROCESS_EXPRESSION
#undef RECURSE_EXPRESSION
#undef RECURSE
} // namespace internal
} // namespace v8
#endif // V8_AST_AST_TRAVERSAL_VISITOR_H_

418
deps/v8/src/ast/ast-value-factory.cc vendored Normal file
View File

@ -0,0 +1,418 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/ast/ast-value-factory.h"
#include "src/base/hashmap-entry.h"
#include "src/base/logging.h"
#include "src/common/globals.h"
#include "src/heap/factory-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/objects/string-inl.h"
#include "src/roots/roots.h"
#include "src/strings/string-hasher.h"
#include "src/utils/utils-inl.h"
namespace v8 {
namespace internal {
namespace {
// For using StringToIndex.
class OneByteStringStream {
public:
explicit OneByteStringStream(base::Vector<const uint8_t> lb)
: literal_bytes_(lb), pos_(0) {}
bool HasMore() { return pos_ < literal_bytes_.length(); }
uint16_t GetNext() { return literal_bytes_[pos_++]; }
private:
base::Vector<const uint8_t> literal_bytes_;
int pos_;
};
} // namespace
template <typename IsolateT>
void AstRawString::Internalize(IsolateT* isolate) {
DCHECK(!has_string_);
if (literal_bytes_.empty()) {
set_string(isolate->factory()->empty_string());
} else if (is_one_byte()) {
OneByteStringKey key(raw_hash_field_, literal_bytes_);
set_string(isolate->factory()->InternalizeStringWithKey(&key));
} else {
TwoByteStringKey key(raw_hash_field_,
base::Vector<const uint16_t>::cast(literal_bytes_));
set_string(isolate->factory()->InternalizeStringWithKey(&key));
}
}
template EXPORT_TEMPLATE_DEFINE(
V8_EXPORT_PRIVATE) void AstRawString::Internalize(Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(
V8_EXPORT_PRIVATE) void AstRawString::Internalize(LocalIsolate* isolate);
bool AstRawString::AsArrayIndex(uint32_t* index) const {
// The StringHasher will set up the hash. Bail out early if we know it
// can't be convertible to an array index.
if (!IsIntegerIndex()) return false;
if (length() <= Name::kMaxCachedArrayIndexLength) {
*index = Name::ArrayIndexValueBits::decode(raw_hash_field_);
return true;
}
// Might be an index, but too big to cache it. Do the slow conversion. This
// might fail if the string is outside uint32_t (but within "safe integer")
// range.
OneByteStringStream stream(literal_bytes_);
return StringToIndex(&stream, index);
}
bool AstRawString::IsIntegerIndex() const {
return Name::IsIntegerIndex(raw_hash_field_);
}
bool AstRawString::IsOneByteEqualTo(const char* data) const {
if (!is_one_byte()) return false;
size_t length = literal_bytes_.size();
if (length != strlen(data)) return false;
return 0 == strncmp(reinterpret_cast<const char*>(literal_bytes_.begin()),
data, length);
}
uint16_t AstRawString::FirstCharacter() const {
if (is_one_byte()) return literal_bytes_[0];
const uint16_t* c = reinterpret_cast<const uint16_t*>(literal_bytes_.begin());
return *c;
}
bool AstRawString::Equal(const AstRawString* lhs, const AstRawString* rhs) {
DCHECK_EQ(lhs->Hash(), rhs->Hash());
if (lhs->length() != rhs->length()) return false;
if (lhs->length() == 0) return true;
const unsigned char* l = lhs->raw_data();
const unsigned char* r = rhs->raw_data();
size_t length = rhs->length();
if (lhs->is_one_byte()) {
if (rhs->is_one_byte()) {
return CompareCharsEqualUnsigned(reinterpret_cast<const uint8_t*>(l),
reinterpret_cast<const uint8_t*>(r),
length);
} else {
return CompareCharsEqualUnsigned(reinterpret_cast<const uint8_t*>(l),
reinterpret_cast<const uint16_t*>(r),
length);
}
} else {
if (rhs->is_one_byte()) {
return CompareCharsEqualUnsigned(reinterpret_cast<const uint16_t*>(l),
reinterpret_cast<const uint8_t*>(r),
length);
} else {
return CompareCharsEqualUnsigned(reinterpret_cast<const uint16_t*>(l),
reinterpret_cast<const uint16_t*>(r),
length);
}
}
}
int AstRawString::Compare(const AstRawString* lhs, const AstRawString* rhs) {
// Fast path for equal pointers.
if (lhs == rhs) return 0;
const unsigned char* lhs_data = lhs->raw_data();
const unsigned char* rhs_data = rhs->raw_data();
size_t length = std::min(lhs->length(), rhs->length());
// Code point order by contents.
if (lhs->is_one_byte()) {
if (rhs->is_one_byte()) {
if (int result = CompareCharsUnsigned(
reinterpret_cast<const uint8_t*>(lhs_data),
reinterpret_cast<const uint8_t*>(rhs_data), length))
return result;
} else {
if (int result = CompareCharsUnsigned(
reinterpret_cast<const uint8_t*>(lhs_data),
reinterpret_cast<const uint16_t*>(rhs_data), length))
return result;
}
} else {
if (rhs->is_one_byte()) {
if (int result = CompareCharsUnsigned(
reinterpret_cast<const uint16_t*>(lhs_data),
reinterpret_cast<const uint8_t*>(rhs_data), length))
return result;
} else {
if (int result = CompareCharsUnsigned(
reinterpret_cast<const uint16_t*>(lhs_data),
reinterpret_cast<const uint16_t*>(rhs_data), length))
return result;
}
}
return lhs->byte_length() - rhs->byte_length();
}
#ifdef OBJECT_PRINT
void AstRawString::Print() const { printf("%.*s", byte_length(), raw_data()); }
#endif // OBJECT_PRINT
template <typename IsolateT>
Handle<String> AstConsString::Allocate(IsolateT* isolate) const {
DCHECK(string_.is_null());
if (IsEmpty()) {
return isolate->factory()->empty_string();
}
// AstRawStrings are internalized before AstConsStrings are allocated, so
// AstRawString::string() will just work.
Handle<String> tmp = segment_.string->string();
for (AstConsString::Segment* current = segment_.next; current != nullptr;
current = current->next) {
tmp = isolate->factory()
->NewConsString(current->string->string(), tmp,
AllocationType::kOld)
.ToHandleChecked();
}
return tmp;
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<String> AstConsString::Allocate<Isolate>(Isolate* isolate) const;
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<String> AstConsString::Allocate<LocalIsolate>(
LocalIsolate* isolate) const;
template <typename IsolateT>
Handle<String> AstConsString::AllocateFlat(IsolateT* isolate) const {
if (IsEmpty()) {
return isolate->factory()->empty_string();
}
if (!segment_.next) {
return segment_.string->string();
}
int result_length = 0;
bool is_one_byte = true;
for (const AstConsString::Segment* current = &segment_; current != nullptr;
current = current->next) {
result_length += current->string->length();
is_one_byte = is_one_byte && current->string->is_one_byte();
}
if (is_one_byte) {
Handle<SeqOneByteString> result =
isolate->factory()
->NewRawOneByteString(result_length, AllocationType::kOld)
.ToHandleChecked();
DisallowGarbageCollection no_gc;
uint8_t* dest =
result->GetChars(no_gc, SharedStringAccessGuardIfNeeded::NotNeeded()) +
result_length;
for (const AstConsString::Segment* current = &segment_; current != nullptr;
current = current->next) {
int length = current->string->length();
dest -= length;
CopyChars(dest, current->string->raw_data(), length);
}
DCHECK_EQ(dest, result->GetChars(
no_gc, SharedStringAccessGuardIfNeeded::NotNeeded()));
return result;
}
Handle<SeqTwoByteString> result =
isolate->factory()
->NewRawTwoByteString(result_length, AllocationType::kOld)
.ToHandleChecked();
DisallowGarbageCollection no_gc;
uint16_t* dest =
result->GetChars(no_gc, SharedStringAccessGuardIfNeeded::NotNeeded()) +
result_length;
for (const AstConsString::Segment* current = &segment_; current != nullptr;
current = current->next) {
int length = current->string->length();
dest -= length;
if (current->string->is_one_byte()) {
CopyChars(dest, current->string->raw_data(), length);
} else {
CopyChars(dest,
reinterpret_cast<const uint16_t*>(current->string->raw_data()),
length);
}
}
DCHECK_EQ(dest, result->GetChars(
no_gc, SharedStringAccessGuardIfNeeded::NotNeeded()));
return result;
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<String> AstConsString::AllocateFlat<Isolate>(Isolate* isolate) const;
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<String> AstConsString::AllocateFlat<LocalIsolate>(
LocalIsolate* isolate) const;
std::forward_list<const AstRawString*> AstConsString::ToRawStrings() const {
std::forward_list<const AstRawString*> result;
if (IsEmpty()) {
return result;
}
result.emplace_front(segment_.string);
for (AstConsString::Segment* current = segment_.next; current != nullptr;
current = current->next) {
result.emplace_front(current->string);
}
return result;
}
AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed)
: zone_(isolate->allocator(), ZONE_NAME),
string_table_(),
hash_seed_(hash_seed) {
DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
#define F(name, str) \
{ \
static const char data[] = str; \
base::Vector<const uint8_t> literal( \
reinterpret_cast<const uint8_t*>(data), \
static_cast<int>(arraysize(data) - 1)); \
IndirectHandle<String> handle = isolate->factory()->name(); \
uint32_t raw_hash_field = handle->raw_hash_field(); \
DCHECK_EQ(raw_hash_field, \
StringHasher::HashSequentialString<uint8_t>( \
literal.begin(), literal.length(), hash_seed_)); \
DCHECK_EQ(literal.length(), handle->length()); \
name##_ = zone_.New<AstRawString>(true, literal, raw_hash_field); \
/* The Handle returned by the factory is located on the roots */ \
/* array, not on the temporary HandleScope, so this is safe. */ \
name##_->set_string(handle); \
string_table_.InsertNew(name##_, name##_->Hash()); \
}
AST_STRING_CONSTANTS(F)
#undef F
}
const AstRawString* AstValueFactory::GetOneByteStringInternal(
base::Vector<const uint8_t> literal) {
if (literal.length() == 1) {
uint8_t key = literal[0];
if (key < AstStringConstants::kMaxOneCharStringValue) {
return string_constants_->one_character_string(key);
}
}
uint32_t raw_hash_field = StringHasher::HashSequentialString<uint8_t>(
literal.begin(), literal.length(), hash_seed_);
return GetString(raw_hash_field, true, literal);
}
const AstRawString* AstValueFactory::GetTwoByteStringInternal(
base::Vector<const uint16_t> literal) {
uint32_t raw_hash_field = StringHasher::HashSequentialString<uint16_t>(
literal.begin(), literal.length(), hash_seed_);
return GetString(raw_hash_field, false,
base::Vector<const uint8_t>::cast(literal));
}
const AstRawString* AstValueFactory::GetString(
Tagged<String> literal,
const SharedStringAccessGuardIfNeeded& access_guard) {
const AstRawString* result = nullptr;
DisallowGarbageCollection no_gc;
String::FlatContent content = literal->GetFlatContent(no_gc, access_guard);
if (content.IsOneByte()) {
result = GetOneByteStringInternal(content.ToOneByteVector());
} else {
DCHECK(content.IsTwoByte());
result = GetTwoByteStringInternal(content.ToUC16Vector());
}
return result;
}
AstConsString* AstValueFactory::NewConsString() {
return single_parse_zone()->New<AstConsString>();
}
AstConsString* AstValueFactory::NewConsString(const AstRawString* str) {
return NewConsString()->AddString(single_parse_zone(), str);
}
AstConsString* AstValueFactory::NewConsString(const AstRawString* str1,
const AstRawString* str2) {
return NewConsString()
->AddString(single_parse_zone(), str1)
->AddString(single_parse_zone(), str2);
}
template <typename IsolateT>
void AstValueFactory::Internalize(IsolateT* isolate) {
// Strings need to be internalized before values, because values refer to
// strings.
for (AstRawString* current = strings_; current != nullptr;) {
AstRawString* next = current->next();
current->Internalize(isolate);
current = next;
}
ResetStrings();
}
template EXPORT_TEMPLATE_DEFINE(
V8_EXPORT_PRIVATE) void AstValueFactory::Internalize(Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(
V8_EXPORT_PRIVATE) void AstValueFactory::Internalize(LocalIsolate* isolate);
const AstRawString* AstValueFactory::GetString(
uint32_t raw_hash_field, bool is_one_byte,
base::Vector<const uint8_t> literal_bytes) {
// literal_bytes here points to whatever the user passed, and this is OK
// because we use vector_compare (which checks the contents) to compare
// against the AstRawStrings which are in the string_table_. We should not
// return this AstRawString.
AstRawString key(is_one_byte, literal_bytes, raw_hash_field);
AstRawStringMap::Entry* entry = string_table_.LookupOrInsert(
&key, key.Hash(),
[&]() {
// Copy literal contents for later comparison.
int length = literal_bytes.length();
uint8_t* new_literal_bytes =
ast_raw_string_zone()->AllocateArray<uint8_t>(length);
memcpy(new_literal_bytes, literal_bytes.begin(), length);
AstRawString* new_string = ast_raw_string_zone()->New<AstRawString>(
is_one_byte, base::Vector<const uint8_t>(new_literal_bytes, length),
raw_hash_field);
CHECK_NOT_NULL(new_string);
AddString(new_string);
return new_string;
},
[&]() { return base::NoHashMapValue(); });
return entry->key;
}
} // namespace internal
} // namespace v8

450
deps/v8/src/ast/ast-value-factory.h vendored Normal file
View File

@ -0,0 +1,450 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_AST_AST_VALUE_FACTORY_H_
#define V8_AST_AST_VALUE_FACTORY_H_
#include <forward_list>
#include "src/base/hashmap.h"
#include "src/base/logging.h"
#include "src/common/globals.h"
#include "src/handles/handles.h"
#include "src/numbers/conversions.h"
#include "src/objects/name.h"
#include "src/zone/zone.h"
// Ast(Raw|Cons)String and AstValueFactory are for storing strings and
// values independent of the V8 heap and internalizing them later. During
// parsing, they are created and stored outside the heap, in AstValueFactory.
// After parsing, the strings and values are internalized (moved into the V8
// heap).
namespace v8 {
namespace internal {
class Isolate;
class AstRawString final : public ZoneObject {
public:
static bool Equal(const AstRawString* lhs, const AstRawString* rhs);
// Returns 0 if lhs is equal to rhs.
// Returns <0 if lhs is less than rhs in code point order.
// Returns >0 if lhs is greater than than rhs in code point order.
static int Compare(const AstRawString* lhs, const AstRawString* rhs);
bool IsEmpty() const { return literal_bytes_.length() == 0; }
int length() const {
return is_one_byte() ? literal_bytes_.length()
: literal_bytes_.length() / 2;
}
bool AsArrayIndex(uint32_t* index) const;
bool IsIntegerIndex() const;
V8_EXPORT_PRIVATE bool IsOneByteEqualTo(const char* data) const;
uint16_t FirstCharacter() const;
template <typename IsolateT>
void Internalize(IsolateT* isolate);
// Access the physical representation:
bool is_one_byte() const { return is_one_byte_; }
int byte_length() const { return literal_bytes_.length(); }
const unsigned char* raw_data() const { return literal_bytes_.begin(); }
bool IsPrivateName() const { return length() > 0 && FirstCharacter() == '#'; }
// For storing AstRawStrings in a hash map.
uint32_t raw_hash_field() const { return raw_hash_field_; }
uint32_t Hash() const {
// Hash field must be computed.
DCHECK_EQ(raw_hash_field_ & Name::kHashNotComputedMask, 0);
return Name::HashBits::decode(raw_hash_field_);
}
// This function can be called after internalizing.
V8_INLINE IndirectHandle<String> string() const {
DCHECK(has_string_);
return string_;
}
#ifdef OBJECT_PRINT
void Print() const;
#endif // OBJECT_PRINT
private:
friend class AstRawStringInternalizationKey;
friend class AstStringConstants;
friend class AstValueFactory;
friend Zone;
// Members accessed only by the AstValueFactory & related classes:
AstRawString(bool is_one_byte, base::Vector<const uint8_t> literal_bytes,
uint32_t raw_hash_field)
: next_(nullptr),
literal_bytes_(literal_bytes),
raw_hash_field_(raw_hash_field),
is_one_byte_(is_one_byte) {}
AstRawString* next() {
DCHECK(!has_string_);
return next_;
}
AstRawString** next_location() {
DCHECK(!has_string_);
return &next_;
}
void set_string(IndirectHandle<String> string) {
DCHECK(!string.is_null());
DCHECK(!has_string_);
string_ = string;
#ifdef DEBUG
has_string_ = true;
#endif
}
union {
AstRawString* next_;
IndirectHandle<String> string_;
};
base::Vector<const uint8_t> literal_bytes_; // Memory owned by Zone.
uint32_t raw_hash_field_;
bool is_one_byte_;
#ifdef DEBUG
// (Debug-only:) Verify the object life-cylce: Some functions may only be
// called after internalization (that is, after a v8::internal::String has
// been set); some only before.
bool has_string_ = false;
#endif
};
extern template EXPORT_TEMPLATE_DECLARE(
V8_EXPORT_PRIVATE) void AstRawString::Internalize(Isolate* isolate);
extern template EXPORT_TEMPLATE_DECLARE(
V8_EXPORT_PRIVATE) void AstRawString::Internalize(LocalIsolate* isolate);
class AstConsString final : public ZoneObject {
public:
AstConsString* AddString(Zone* zone, const AstRawString* s) {
if (s->IsEmpty()) return this;
if (!IsEmpty()) {
// We're putting the new string to the head of the list, meaning
// the string segments will be in reverse order.
Segment* tmp = zone->New<Segment>(segment_);
segment_.next = tmp;
}
segment_.string = s;
return this;
}
bool IsEmpty() const {
DCHECK_IMPLIES(segment_.string == nullptr, segment_.next == nullptr);
DCHECK_IMPLIES(segment_.string != nullptr, !segment_.string->IsEmpty());
return segment_.string == nullptr;
}
template <typename IsolateT>
IndirectHandle<String> GetString(IsolateT* isolate) {
if (string_.is_null()) {
string_ = Allocate(isolate);
}
return string_;
}
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Handle<String> AllocateFlat(IsolateT* isolate) const;
std::forward_list<const AstRawString*> ToRawStrings() const;
const AstRawString* last() const { return segment_.string; }
private:
friend class AstValueFactory;
friend Zone;
AstConsString() : string_(), segment_({nullptr, nullptr}) {}
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Handle<String> Allocate(IsolateT* isolate) const;
IndirectHandle<String> string_;
// A linked list of AstRawStrings of the contents of this AstConsString.
// This list has several properties:
//
// * For empty strings the string pointer is null,
// * Appended raw strings are added to the head of the list, so they are in
// reverse order
struct Segment {
const AstRawString* string;
AstConsString::Segment* next;
};
Segment segment_;
};
class AstBigInt {
public:
// |bigint| must be a NUL-terminated string of ASCII characters
// representing a BigInt (suitable for passing to BigIntLiteral()
// from conversions.h).
explicit AstBigInt(const char* bigint) : bigint_(bigint) {}
const char* c_str() const { return bigint_; }
private:
const char* bigint_;
};
struct AstRawStringMapMatcher {
bool operator()(uint32_t hash1, uint32_t hash2,
const AstRawString* lookup_key,
const AstRawString* entry_key) const {
return hash1 == hash2 && AstRawString::Equal(lookup_key, entry_key);
}
};
using AstRawStringMap =
base::TemplateHashMapImpl<const AstRawString*, base::NoHashMapValue,
AstRawStringMapMatcher,
base::DefaultAllocationPolicy>;
// For generating constants.
#define AST_STRING_CONSTANTS_INTERNALIZED_STRING_LIST_ADAPTER(F, name, \
contents) \
F(name, contents)
#define SINGLE_CHARACTER_ASCII_AST_STRING_CONSTANTS(F) \
SINGLE_CHARACTER_ASCII_INTERNALIZED_STRING_LIST_GENERATOR( \
AST_STRING_CONSTANTS_INTERNALIZED_STRING_LIST_ADAPTER, F)
#define AST_STRING_CONSTANTS(F) \
SINGLE_CHARACTER_ASCII_AST_STRING_CONSTANTS(F) \
F(anonymous_string, "anonymous") \
F(arguments_string, "arguments") \
F(as_string, "as") \
F(assert_string, "assert") \
F(async_string, "async") \
F(bigint_string, "bigint") \
F(boolean_string, "boolean") \
F(computed_string, "<computed>") \
F(dot_brand_string, ".brand") \
F(constructor_string, "constructor") \
F(default_string, "default") \
F(done_string, "done") \
F(dot_default_string, ".default") \
F(dot_for_string, ".for") \
F(dot_generator_object_string, ".generator_object") \
F(dot_home_object_string, ".home_object") \
F(dot_result_string, ".result") \
F(dot_repl_result_string, ".repl_result") \
F(dot_static_home_object_string, ".static_home_object") \
F(dot_switch_tag_string, ".switch_tag") \
F(dot_catch_string, ".catch") \
F(empty_string, "") \
F(eval_string, "eval") \
F(from_string, "from") \
F(function_string, "function") \
F(get_space_string, "get ") \
F(length_string, "length") \
F(let_string, "let") \
F(meta_string, "meta") \
F(native_string, "native") \
F(new_target_string, ".new.target") \
F(next_string, "next") \
F(number_string, "number") \
F(object_string, "object") \
F(private_constructor_string, "#constructor") \
F(proto_string, "__proto__") \
F(prototype_string, "prototype") \
F(return_string, "return") \
F(set_space_string, "set ") \
F(source_string, "source") \
F(string_string, "string") \
F(symbol_string, "symbol") \
F(target_string, "target") \
F(this_string, "this") \
F(this_function_string, ".this_function") \
F(throw_string, "throw") \
F(undefined_string, "undefined") \
F(value_string, "value")
class AstStringConstants final {
public:
#define F(name, str) +1
static constexpr int kMaxOneCharStringValue =
0 SINGLE_CHARACTER_ASCII_AST_STRING_CONSTANTS(F);
#undef F
AstStringConstants(Isolate* isolate, uint64_t hash_seed);
AstStringConstants(const AstStringConstants&) = delete;
AstStringConstants& operator=(const AstStringConstants&) = delete;
#define F(name, str) \
const AstRawString* name() const { return name##_; }
AST_STRING_CONSTANTS(F)
#undef F
uint64_t hash_seed() const { return hash_seed_; }
const AstRawStringMap* string_table() const { return &string_table_; }
const AstRawString* one_character_string(int c) const {
DCHECK_GE(c, 0);
DCHECK_LT(c, kMaxOneCharStringValue);
// Make sure we can access the one character strings via an offset
// from ascii_nul_string_.
#define F(name, str) \
static_assert(offsetof(AstStringConstants, name##_) == \
offsetof(AstStringConstants, ascii_nul_string_) + \
str[0] * sizeof(AstRawString*));
SINGLE_CHARACTER_ASCII_AST_STRING_CONSTANTS(F)
#undef F
return (&ascii_nul_string_)[c];
}
private:
Zone zone_;
AstRawStringMap string_table_;
uint64_t hash_seed_;
#define F(name, str) AstRawString* name##_;
AST_STRING_CONSTANTS(F)
#undef F
};
class AstValueFactory {
public:
AstValueFactory(Zone* zone, const AstStringConstants* string_constants,
uint64_t hash_seed)
: AstValueFactory(zone, zone, string_constants, hash_seed) {}
AstValueFactory(Zone* ast_raw_string_zone, Zone* single_parse_zone,
const AstStringConstants* string_constants,
uint64_t hash_seed)
: string_table_(string_constants->string_table()),
strings_(nullptr),
strings_end_(&strings_),
string_constants_(string_constants),
empty_cons_string_(nullptr),
ast_raw_string_zone_(ast_raw_string_zone),
single_parse_zone_(single_parse_zone),
hash_seed_(hash_seed) {
DCHECK_NOT_NULL(ast_raw_string_zone_);
DCHECK_NOT_NULL(single_parse_zone_);
DCHECK_EQ(hash_seed, string_constants->hash_seed());
// Allocate the empty ConsString in the AstRawString Zone instead of the
// single parse Zone like other ConsStrings, because unlike those it can be
// reused across parses.
empty_cons_string_ = ast_raw_string_zone_->New<AstConsString>();
}
Zone* ast_raw_string_zone() const {
DCHECK_NOT_NULL(ast_raw_string_zone_);
return ast_raw_string_zone_;
}
Zone* single_parse_zone() const {
DCHECK_NOT_NULL(single_parse_zone_);
return single_parse_zone_;
}
const AstRawString* GetOneByteString(base::Vector<const uint8_t> literal) {
return GetOneByteStringInternal(literal);
}
const AstRawString* GetOneByteString(const char* string) {
return GetOneByteString(base::OneByteVector(string));
}
const AstRawString* GetTwoByteString(base::Vector<const uint16_t> literal) {
return GetTwoByteStringInternal(literal);
}
const AstRawString* GetString(Tagged<String> literal,
const SharedStringAccessGuardIfNeeded&);
V8_EXPORT_PRIVATE AstConsString* NewConsString();
V8_EXPORT_PRIVATE AstConsString* NewConsString(const AstRawString* str);
V8_EXPORT_PRIVATE AstConsString* NewConsString(const AstRawString* str1,
const AstRawString* str2);
// Internalize all the strings in the factory, and prevent any more from being
// allocated. Multiple calls to Internalize are allowed, for simplicity, where
// subsequent calls are a no-op.
template <typename IsolateT>
void Internalize(IsolateT* isolate);
#define F(name, str) \
const AstRawString* name() const { return string_constants_->name(); }
AST_STRING_CONSTANTS(F)
#undef F
AstConsString* empty_cons_string() const { return empty_cons_string_; }
private:
AstRawString* AddString(AstRawString* string) {
*strings_end_ = string;
strings_end_ = string->next_location();
return string;
}
void ResetStrings() {
strings_ = nullptr;
strings_end_ = &strings_;
}
V8_EXPORT_PRIVATE const AstRawString* GetOneByteStringInternal(
base::Vector<const uint8_t> literal);
const AstRawString* GetTwoByteStringInternal(
base::Vector<const uint16_t> literal);
const AstRawString* GetString(uint32_t raw_hash_field, bool is_one_byte,
base::Vector<const uint8_t> literal_bytes);
// All strings are copied here.
AstRawStringMap string_table_;
AstRawString* strings_;
AstRawString** strings_end_;
// Holds constant string values which are shared across the isolate.
const AstStringConstants* string_constants_;
AstConsString* empty_cons_string_;
Zone* ast_raw_string_zone_;
Zone* single_parse_zone_;
uint64_t hash_seed_;
};
extern template EXPORT_TEMPLATE_DECLARE(
V8_EXPORT_PRIVATE) void AstValueFactory::Internalize<Isolate>(Isolate*
isolate);
extern template EXPORT_TEMPLATE_DECLARE(
V8_EXPORT_PRIVATE) void AstValueFactory::
Internalize<LocalIsolate>(LocalIsolate* isolate);
} // namespace internal
} // namespace v8
#endif // V8_AST_AST_VALUE_FACTORY_H_

1150
deps/v8/src/ast/ast.cc vendored Normal file

File diff suppressed because it is too large Load Diff

3641
deps/v8/src/ast/ast.h vendored Normal file

File diff suppressed because it is too large Load Diff

387
deps/v8/src/ast/modules.cc vendored Normal file
View File

@ -0,0 +1,387 @@
// Copyright 2012 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.
#include "src/ast/modules.h"
#include "src/ast/ast-value-factory.h"
#include "src/ast/scopes.h"
#include "src/common/globals.h"
#include "src/heap/local-factory-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-inl.h"
#include "src/parsing/pending-compilation-error-handler.h"
namespace v8 {
namespace internal {
bool SourceTextModuleDescriptor::AstRawStringComparer::operator()(
const AstRawString* lhs, const AstRawString* rhs) const {
return AstRawString::Compare(lhs, rhs) < 0;
}
bool SourceTextModuleDescriptor::ModuleRequestComparer::operator()(
const AstModuleRequest* lhs, const AstModuleRequest* rhs) const {
if (int specifier_comparison =
AstRawString::Compare(lhs->specifier(), rhs->specifier())) {
return specifier_comparison < 0;
}
auto lhsIt = lhs->import_attributes()->cbegin();
auto rhsIt = rhs->import_attributes()->cbegin();
for (; lhsIt != lhs->import_attributes()->cend() &&
rhsIt != rhs->import_attributes()->cend();
++lhsIt, ++rhsIt) {
if (int assertion_key_comparison =
AstRawString::Compare(lhsIt->first, rhsIt->first)) {
return assertion_key_comparison < 0;
}
if (int assertion_value_comparison =
AstRawString::Compare(lhsIt->second.first, rhsIt->second.first)) {
return assertion_value_comparison < 0;
}
}
if (lhs->import_attributes()->size() != rhs->import_attributes()->size()) {
return (lhs->import_attributes()->size() <
rhs->import_attributes()->size());
}
if (lhs->phase() != rhs->phase()) {
return lhs->phase() < rhs->phase();
}
return false;
}
void SourceTextModuleDescriptor::AddImport(
const AstRawString* import_name, const AstRawString* local_name,
const AstRawString* specifier, const ModuleImportPhase import_phase,
const ImportAttributes* import_attributes, const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->local_name = local_name;
entry->import_name = import_name;
entry->module_request = AddModuleRequest(
specifier, import_phase, import_attributes, specifier_loc, zone);
AddRegularImport(entry);
}
void SourceTextModuleDescriptor::AddStarImport(
const AstRawString* local_name, const AstRawString* specifier,
const ImportAttributes* import_attributes, const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->local_name = local_name;
entry->module_request =
AddModuleRequest(specifier, ModuleImportPhase::kEvaluation,
import_attributes, specifier_loc, zone);
AddNamespaceImport(entry, zone);
}
void SourceTextModuleDescriptor::AddEmptyImport(
const AstRawString* specifier, const ImportAttributes* import_attributes,
const Scanner::Location specifier_loc, Zone* zone) {
AddModuleRequest(specifier, ModuleImportPhase::kEvaluation, import_attributes,
specifier_loc, zone);
}
void SourceTextModuleDescriptor::AddExport(const AstRawString* local_name,
const AstRawString* export_name,
Scanner::Location loc, Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->export_name = export_name;
entry->local_name = local_name;
AddRegularExport(entry);
}
void SourceTextModuleDescriptor::AddExport(
const AstRawString* import_name, const AstRawString* export_name,
const AstRawString* specifier, const ImportAttributes* import_attributes,
const Scanner::Location loc, const Scanner::Location specifier_loc,
Zone* zone) {
DCHECK_NOT_NULL(import_name);
DCHECK_NOT_NULL(export_name);
Entry* entry = zone->New<Entry>(loc);
entry->export_name = export_name;
entry->import_name = import_name;
entry->module_request =
AddModuleRequest(specifier, ModuleImportPhase::kEvaluation,
import_attributes, specifier_loc, zone);
AddSpecialExport(entry, zone);
}
void SourceTextModuleDescriptor::AddStarExport(
const AstRawString* specifier, const ImportAttributes* import_attributes,
const Scanner::Location loc, const Scanner::Location specifier_loc,
Zone* zone) {
Entry* entry = zone->New<Entry>(loc);
entry->module_request =
AddModuleRequest(specifier, ModuleImportPhase::kEvaluation,
import_attributes, specifier_loc, zone);
AddSpecialExport(entry, zone);
}
namespace {
template <typename IsolateT>
Handle<UnionOf<String, Undefined>> ToStringOrUndefined(IsolateT* isolate,
const AstRawString* s) {
if (s == nullptr) return isolate->factory()->undefined_value();
return s->string();
}
} // namespace
template <typename IsolateT>
DirectHandle<ModuleRequest>
SourceTextModuleDescriptor::AstModuleRequest::Serialize(
IsolateT* isolate) const {
// The import attributes will be stored in this array in the form:
// [key1, value1, location1, key2, value2, location2, ...]
DirectHandle<FixedArray> import_attributes_array =
isolate->factory()->NewFixedArray(
static_cast<int>(import_attributes()->size() *
ModuleRequest::kAttributeEntrySize),
AllocationType::kOld);
{
DisallowGarbageCollection no_gc;
Tagged<FixedArray> raw_import_attributes = *import_attributes_array;
int i = 0;
for (auto iter = import_attributes()->cbegin();
iter != import_attributes()->cend();
++iter, i += ModuleRequest::kAttributeEntrySize) {
raw_import_attributes->set(i, *iter->first->string());
raw_import_attributes->set(i + 1, *iter->second.first->string());
raw_import_attributes->set(i + 2,
Smi::FromInt(iter->second.second.beg_pos));
}
}
return v8::internal::ModuleRequest::New(isolate, specifier()->string(),
phase_, import_attributes_array,
position());
}
template DirectHandle<ModuleRequest>
SourceTextModuleDescriptor::AstModuleRequest::Serialize(Isolate* isolate) const;
template DirectHandle<ModuleRequest>
SourceTextModuleDescriptor::AstModuleRequest::Serialize(
LocalIsolate* isolate) const;
template <typename IsolateT>
DirectHandle<SourceTextModuleInfoEntry>
SourceTextModuleDescriptor::Entry::Serialize(IsolateT* isolate) const {
CHECK(Smi::IsValid(module_request)); // TODO(neis): Check earlier?
return SourceTextModuleInfoEntry::New(
isolate, ToStringOrUndefined(isolate, export_name),
ToStringOrUndefined(isolate, local_name),
ToStringOrUndefined(isolate, import_name), module_request, cell_index,
location.beg_pos, location.end_pos);
}
template DirectHandle<SourceTextModuleInfoEntry>
SourceTextModuleDescriptor::Entry::Serialize(Isolate* isolate) const;
template DirectHandle<SourceTextModuleInfoEntry>
SourceTextModuleDescriptor::Entry::Serialize(LocalIsolate* isolate) const;
template <typename IsolateT>
DirectHandle<FixedArray> SourceTextModuleDescriptor::SerializeRegularExports(
IsolateT* isolate, Zone* zone) const {
// We serialize regular exports in a way that lets us later iterate over their
// local names and for each local name immediately access all its export
// names. (Regular exports have neither import name nor module request.)
ZoneVector<IndirectHandle<Object>> data(
SourceTextModuleInfo::kRegularExportLength * regular_exports_.size(),
zone);
int index = 0;
for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
// Find out how many export names this local name has.
auto next = it;
int count = 0;
do {
DCHECK_EQ(it->second->local_name, next->second->local_name);
DCHECK_EQ(it->second->cell_index, next->second->cell_index);
++next;
++count;
} while (next != regular_exports_.end() && next->first == it->first);
Handle<FixedArray> export_names =
isolate->factory()->NewFixedArray(count, AllocationType::kOld);
data[index + SourceTextModuleInfo::kRegularExportLocalNameOffset] =
it->second->local_name->string();
data[index + SourceTextModuleInfo::kRegularExportCellIndexOffset] =
handle(Smi::FromInt(it->second->cell_index), isolate);
data[index + SourceTextModuleInfo::kRegularExportExportNamesOffset] =
export_names;
index += SourceTextModuleInfo::kRegularExportLength;
// Collect the export names.
int i = 0;
for (; it != next; ++it) {
export_names->set(i++, *it->second->export_name->string());
}
DCHECK_EQ(i, count);
// Continue with the next distinct key.
DCHECK(it == next);
}
DCHECK_LE(index, static_cast<int>(data.size()));
data.resize(index);
// We cannot create the FixedArray earlier because we only now know the
// precise size.
DirectHandle<FixedArray> result =
isolate->factory()->NewFixedArray(index, AllocationType::kOld);
for (int i = 0; i < index; ++i) {
result->set(i, *data[i]);
}
return result;
}
template DirectHandle<FixedArray>
SourceTextModuleDescriptor::SerializeRegularExports(Isolate* isolate,
Zone* zone) const;
template DirectHandle<FixedArray>
SourceTextModuleDescriptor::SerializeRegularExports(LocalIsolate* isolate,
Zone* zone) const;
void SourceTextModuleDescriptor::MakeIndirectExportsExplicit(Zone* zone) {
for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
Entry* entry = it->second;
DCHECK_NOT_NULL(entry->local_name);
auto import = regular_imports_.find(entry->local_name);
if (import != regular_imports_.end()) {
// Found an indirect export. Patch export entry and move it from regular
// to special.
DCHECK_NULL(entry->import_name);
DCHECK_LT(entry->module_request, 0);
DCHECK_NOT_NULL(import->second->import_name);
DCHECK_LE(0, import->second->module_request);
DCHECK_LT(import->second->module_request,
static_cast<int>(module_requests_.size()));
entry->import_name = import->second->import_name;
entry->module_request = import->second->module_request;
// Hack: When the indirect export cannot be resolved, we want the error
// message to point at the import statement, not at the export statement.
// Therefore we overwrite [entry]'s location here. Note that Validate()
// has already checked for duplicate exports, so it's guaranteed that we
// won't need to report any error pointing at the (now lost) export
// location.
entry->location = import->second->location;
entry->local_name = nullptr;
AddSpecialExport(entry, zone);
it = regular_exports_.erase(it);
} else {
it++;
}
}
}
SourceTextModuleDescriptor::CellIndexKind
SourceTextModuleDescriptor::GetCellIndexKind(int cell_index) {
if (cell_index > 0) return kExport;
if (cell_index < 0) return kImport;
return kInvalid;
}
void SourceTextModuleDescriptor::AssignCellIndices() {
int export_index = 1;
for (auto it = regular_exports_.begin(); it != regular_exports_.end();) {
auto current_key = it->first;
// This local name may be exported under multiple export names. Assign the
// same index to each such entry.
do {
Entry* entry = it->second;
DCHECK_NOT_NULL(entry->local_name);
DCHECK_NULL(entry->import_name);
DCHECK_LT(entry->module_request, 0);
DCHECK_EQ(entry->cell_index, 0);
entry->cell_index = export_index;
it++;
} while (it != regular_exports_.end() && it->first == current_key);
export_index++;
}
int import_index = -1;
for (const auto& elem : regular_imports_) {
Entry* entry = elem.second;
DCHECK_NOT_NULL(entry->local_name);
DCHECK_NOT_NULL(entry->import_name);
DCHECK_LE(0, entry->module_request);
DCHECK_EQ(entry->cell_index, 0);
entry->cell_index = import_index;
import_index--;
}
}
namespace {
const SourceTextModuleDescriptor::Entry* BetterDuplicate(
const SourceTextModuleDescriptor::Entry* candidate,
ZoneMap<const AstRawString*, const SourceTextModuleDescriptor::Entry*>&
export_names,
const SourceTextModuleDescriptor::Entry* current_duplicate) {
DCHECK_NOT_NULL(candidate->export_name);
DCHECK(candidate->location.IsValid());
auto insert_result =
export_names.insert(std::make_pair(candidate->export_name, candidate));
if (insert_result.second) return current_duplicate;
if (current_duplicate == nullptr) {
current_duplicate = insert_result.first->second;
}
return (candidate->location.beg_pos > current_duplicate->location.beg_pos)
? candidate
: current_duplicate;
}
} // namespace
const SourceTextModuleDescriptor::Entry*
SourceTextModuleDescriptor::FindDuplicateExport(Zone* zone) const {
const SourceTextModuleDescriptor::Entry* duplicate = nullptr;
ZoneMap<const AstRawString*, const SourceTextModuleDescriptor::Entry*>
export_names(zone);
for (const auto& elem : regular_exports_) {
duplicate = BetterDuplicate(elem.second, export_names, duplicate);
}
for (auto entry : special_exports_) {
if (entry->export_name == nullptr) continue; // Star export.
duplicate = BetterDuplicate(entry, export_names, duplicate);
}
return duplicate;
}
bool SourceTextModuleDescriptor::Validate(
ModuleScope* module_scope, PendingCompilationErrorHandler* error_handler,
Zone* zone) {
DCHECK_EQ(this, module_scope->module());
DCHECK_NOT_NULL(error_handler);
// Report error iff there are duplicate exports.
{
const Entry* entry = FindDuplicateExport(zone);
if (entry != nullptr) {
error_handler->ReportMessageAt(
entry->location.beg_pos, entry->location.end_pos,
MessageTemplate::kDuplicateExport, entry->export_name);
return false;
}
}
// Report error iff there are exports of non-existent local names.
for (const auto& elem : regular_exports_) {
const Entry* entry = elem.second;
DCHECK_NOT_NULL(entry->local_name);
if (module_scope->LookupLocal(entry->local_name) == nullptr) {
error_handler->ReportMessageAt(
entry->location.beg_pos, entry->location.end_pos,
MessageTemplate::kModuleExportUndefined, entry->local_name);
return false;
}
}
MakeIndirectExportsExplicit(zone);
AssignCellIndices();
return true;
}
} // namespace internal
} // namespace v8

288
deps/v8/src/ast/modules.h vendored Normal file
View File

@ -0,0 +1,288 @@
// Copyright 2012 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_AST_MODULES_H_
#define V8_AST_MODULES_H_
#include "src/parsing/import-attributes.h"
#include "src/parsing/scanner.h" // Only for Scanner::Location.
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class AstRawString;
class AstRawStringComparer;
class ModuleRequest;
class SourceTextModuleInfo;
class SourceTextModuleInfoEntry;
class PendingCompilationErrorHandler;
class SourceTextModuleDescriptor : public ZoneObject {
public:
explicit SourceTextModuleDescriptor(Zone* zone)
: module_requests_(zone),
special_exports_(zone),
namespace_imports_(zone),
regular_exports_(zone),
regular_imports_(zone) {}
// The following Add* methods are high-level convenience functions for use by
// the parser.
// import x from "foo.js";
// import {x} from "foo.js";
// import {x as y} from "foo.js";
void AddImport(const AstRawString* import_name,
const AstRawString* local_name, const AstRawString* specifier,
const ModuleImportPhase import_phase,
const ImportAttributes* import_attributes,
const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone);
// import * as x from "foo.js";
void AddStarImport(const AstRawString* local_name,
const AstRawString* specifier,
const ImportAttributes* import_attributes,
const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone);
// import "foo.js";
// import {} from "foo.js";
// export {} from "foo.js"; (sic!)
void AddEmptyImport(const AstRawString* specifier,
const ImportAttributes* import_attributes,
const Scanner::Location specifier_loc, Zone* zone);
// export {x};
// export {x as y};
// export VariableStatement
// export Declaration
// export default ...
void AddExport(
const AstRawString* local_name, const AstRawString* export_name,
const Scanner::Location loc, Zone* zone);
// export {x} from "foo.js";
// export {x as y} from "foo.js";
void AddExport(const AstRawString* export_name,
const AstRawString* import_name, const AstRawString* specifier,
const ImportAttributes* import_attributes,
const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone);
// export * from "foo.js";
void AddStarExport(const AstRawString* specifier,
const ImportAttributes* import_attributes,
const Scanner::Location loc,
const Scanner::Location specifier_loc, Zone* zone);
// Check if module is well-formed and report error if not.
// Also canonicalize indirect exports.
bool Validate(ModuleScope* module_scope,
PendingCompilationErrorHandler* error_handler, Zone* zone);
struct Entry : public ZoneObject {
Scanner::Location location;
const AstRawString* export_name;
const AstRawString* local_name;
const AstRawString* import_name;
// The module_request value records the order in which modules are
// requested. It also functions as an index into the SourceTextModuleInfo's
// array of module specifiers and into the Module's array of requested
// modules. A negative value means no module request.
int module_request;
// Import/export entries that are associated with a MODULE-allocated
// variable (i.e. regular_imports and regular_exports after Validate) use
// the cell_index value to encode the location of their cell. During
// variable allocation, this will be be copied into the variable's index
// field.
// Entries that are not associated with a MODULE-allocated variable have
// GetCellIndexKind(cell_index) == kInvalid.
int cell_index;
// TODO(neis): Remove local_name component?
explicit Entry(Scanner::Location loc)
: location(loc),
export_name(nullptr),
local_name(nullptr),
import_name(nullptr),
module_request(-1),
cell_index(0) {}
template <typename IsolateT>
DirectHandle<SourceTextModuleInfoEntry> Serialize(IsolateT* isolate) const;
};
enum CellIndexKind { kInvalid, kExport, kImport };
static CellIndexKind GetCellIndexKind(int cell_index);
class AstModuleRequest : public ZoneObject {
public:
AstModuleRequest(const AstRawString* specifier,
const ModuleImportPhase phase,
const ImportAttributes* import_attributes, int position,
int index)
: specifier_(specifier),
phase_(phase),
import_attributes_(import_attributes),
position_(position),
index_(index) {}
template <typename IsolateT>
DirectHandle<v8::internal::ModuleRequest> Serialize(
IsolateT* isolate) const;
const AstRawString* specifier() const { return specifier_; }
const ImportAttributes* import_attributes() const {
return import_attributes_;
}
ModuleImportPhase phase() const { return phase_; }
int position() const { return position_; }
int index() const { return index_; }
private:
const AstRawString* specifier_;
const ModuleImportPhase phase_;
const ImportAttributes* import_attributes_;
// The JS source code position of the request, used for reporting errors.
int position_;
// The index at which we will place the request in SourceTextModuleInfo's
// module_requests FixedArray.
int index_;
};
// Custom content-based comparer for the below maps, to keep them stable
// across parses.
struct V8_EXPORT_PRIVATE AstRawStringComparer {
bool operator()(const AstRawString* lhs, const AstRawString* rhs) const;
};
struct V8_EXPORT_PRIVATE ModuleRequestComparer {
bool operator()(const AstModuleRequest* lhs,
const AstModuleRequest* rhs) const;
};
using ModuleRequestMap =
ZoneSet<const AstModuleRequest*, ModuleRequestComparer>;
using RegularExportMap =
ZoneMultimap<const AstRawString*, Entry*, AstRawStringComparer>;
using RegularImportMap =
ZoneMap<const AstRawString*, Entry*, AstRawStringComparer>;
// Module requests.
const ModuleRequestMap& module_requests() const { return module_requests_; }
// Namespace imports.
const ZoneVector<const Entry*>& namespace_imports() const {
return namespace_imports_;
}
// All the remaining imports, indexed by local name.
const RegularImportMap& regular_imports() const { return regular_imports_; }
// Star exports and explicitly indirect exports.
const ZoneVector<const Entry*>& special_exports() const {
return special_exports_;
}
// All the remaining exports, indexed by local name.
// After canonicalization (see Validate), these are exactly the local exports.
const RegularExportMap& regular_exports() const { return regular_exports_; }
void AddRegularExport(Entry* entry) {
DCHECK_NOT_NULL(entry->export_name);
DCHECK_NOT_NULL(entry->local_name);
DCHECK_NULL(entry->import_name);
DCHECK_LT(entry->module_request, 0);
regular_exports_.insert(std::make_pair(entry->local_name, entry));
}
void AddSpecialExport(const Entry* entry, Zone* zone) {
DCHECK_NULL(entry->local_name);
DCHECK_LE(0, entry->module_request);
special_exports_.push_back(entry);
}
void AddRegularImport(Entry* entry) {
DCHECK_NOT_NULL(entry->import_name);
DCHECK_NOT_NULL(entry->local_name);
DCHECK_NULL(entry->export_name);
DCHECK_LE(0, entry->module_request);
regular_imports_.insert(std::make_pair(entry->local_name, entry));
// We don't care if there's already an entry for this local name, as in that
// case we will report an error when declaring the variable.
}
void AddNamespaceImport(const Entry* entry, Zone* zone) {
DCHECK_NULL(entry->import_name);
DCHECK_NULL(entry->export_name);
DCHECK_NOT_NULL(entry->local_name);
DCHECK_LE(0, entry->module_request);
namespace_imports_.push_back(entry);
}
template <typename IsolateT>
DirectHandle<FixedArray> SerializeRegularExports(IsolateT* isolate,
Zone* zone) const;
private:
ModuleRequestMap module_requests_;
ZoneVector<const Entry*> special_exports_;
ZoneVector<const Entry*> namespace_imports_;
RegularExportMap regular_exports_;
RegularImportMap regular_imports_;
// If there are multiple export entries with the same export name, return the
// last of them (in source order). Otherwise return nullptr.
const Entry* FindDuplicateExport(Zone* zone) const;
// Find any implicitly indirect exports and make them explicit.
//
// An explicitly indirect export is an export entry arising from an export
// statement of the following form:
// export {a as c} from "X";
// An implicitly indirect export corresponds to
// export {b as c};
// in the presence of an import statement of the form
// import {a as b} from "X";
// This function finds such implicitly indirect export entries and rewrites
// them by filling in the import name and module request, as well as nulling
// out the local name. Effectively, it turns
// import {a as b} from "X"; export {b as c};
// into:
// import {a as b} from "X"; export {a as c} from "X";
// (The import entry is never deleted.)
void MakeIndirectExportsExplicit(Zone* zone);
// Assign a cell_index of -1,-2,... to regular imports.
// Assign a cell_index of +1,+2,... to regular (local) exports.
// Assign a cell_index of 0 to anything else.
void AssignCellIndices();
int AddModuleRequest(const AstRawString* specifier,
const ModuleImportPhase import_phase,
const ImportAttributes* import_attributes,
Scanner::Location specifier_loc, Zone* zone) {
DCHECK_NOT_NULL(specifier);
int module_requests_count = static_cast<int>(module_requests_.size());
auto it = module_requests_
.insert(zone->New<AstModuleRequest>(
specifier, import_phase, import_attributes,
specifier_loc.beg_pos, module_requests_count))
.first;
return (*it)->index();
}
};
} // namespace internal
} // namespace v8
#endif // V8_AST_MODULES_H_

1548
deps/v8/src/ast/prettyprinter.cc vendored Normal file

File diff suppressed because it is too large Load Diff

155
deps/v8/src/ast/prettyprinter.h vendored Normal file
View File

@ -0,0 +1,155 @@
// Copyright 2012 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_AST_PRETTYPRINTER_H_
#define V8_AST_PRETTYPRINTER_H_
#include "src/ast/ast.h"
#include "src/base/compiler-specific.h"
#include "src/execution/isolate.h"
#include "src/objects/function-kind.h"
#include "src/strings/string-builder.h"
namespace v8 {
namespace internal {
class CallPrinter final : public AstVisitor<CallPrinter> {
public:
enum class SpreadErrorInArgsHint { kErrorInArgs, kNoErrorInArgs };
explicit CallPrinter(Isolate* isolate, bool is_user_js,
SpreadErrorInArgsHint error_in_spread_args =
SpreadErrorInArgsHint::kNoErrorInArgs);
~CallPrinter();
// The following routine prints the node with position |position| into a
// string.
DirectHandle<String> Print(FunctionLiteral* program, int position);
enum class ErrorHint {
kNone,
kNormalIterator,
kAsyncIterator,
kCallAndNormalIterator,
kCallAndAsyncIterator
};
ErrorHint GetErrorHint() const;
Expression* spread_arg() const { return spread_arg_; }
ObjectLiteralProperty* destructuring_prop() const {
return destructuring_prop_;
}
Assignment* destructuring_assignment() const {
return destructuring_assignment_;
}
// Individual nodes
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
private:
void Print(char c);
void Print(const char* str);
void Print(DirectHandle<String> str);
void Find(AstNode* node, bool print = false);
Isolate* isolate_;
int num_prints_;
IncrementalStringBuilder builder_;
int position_; // position of ast node to print
bool found_;
bool done_;
bool is_user_js_;
bool is_iterator_error_;
bool is_async_iterator_error_;
bool is_call_error_;
SpreadErrorInArgsHint error_in_spread_args_;
ObjectLiteralProperty* destructuring_prop_;
Assignment* destructuring_assignment_;
Expression* spread_arg_;
FunctionKind function_kind_;
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
protected:
void PrintLiteral(DirectHandle<Object> value, bool quote);
void PrintLiteral(const AstRawString* value, bool quote);
void FindStatements(const ZonePtrList<Statement>* statements);
void FindArguments(const ZonePtrList<Expression>* arguments);
};
#ifdef DEBUG
class AstPrinter final : public AstVisitor<AstPrinter> {
public:
explicit AstPrinter(uintptr_t stack_limit);
~AstPrinter();
// The following routines print a node into a string.
// The result string is alive as long as the AstPrinter is alive.
const char* Print(AstNode* node);
const char* PrintProgram(FunctionLiteral* program);
void PRINTF_FORMAT(2, 3) Print(const char* format, ...);
// Print a node to stdout.
static void PrintOut(Isolate* isolate, AstNode* node);
// Individual nodes
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
private:
friend class IndentedScope;
void Init();
void PrintLabels(ZonePtrList<const AstRawString>* labels);
void PrintLiteral(const AstRawString* value, bool quote);
void PrintLiteral(const AstConsString* value, bool quote);
void PrintLiteral(Literal* literal, bool quote);
void PrintIndented(const char* txt);
void PrintIndentedVisit(const char* s, AstNode* node);
void PrintStatements(const ZonePtrList<Statement>* statements);
void PrintDeclarations(Declaration::List* declarations);
void PrintParameters(DeclarationScope* scope);
void PrintArguments(const ZonePtrList<Expression>* arguments);
void PrintCaseClause(CaseClause* clause);
void PrintLiteralIndented(const char* info, Literal* literal, bool quote);
void PrintLiteralIndented(const char* info, const AstRawString* value,
bool quote);
void PrintLiteralIndented(const char* info, const AstConsString* value,
bool quote);
void PrintLiteralWithModeIndented(const char* info, Variable* var,
const AstRawString* value);
void PrintLabelsIndented(ZonePtrList<const AstRawString>* labels,
const char* prefix = "");
void PrintObjectProperties(
const ZonePtrList<ObjectLiteral::Property>* properties);
void PrintClassProperty(ClassLiteral::Property* property);
void PrintClassProperties(
const ZonePtrList<ClassLiteral::Property>* properties);
void PrintClassStaticElements(
const ZonePtrList<ClassLiteral::StaticElement>* static_elements);
void inc_indent() { indent_++; }
void dec_indent() { indent_--; }
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
char* output_; // output string buffer
int size_; // output_ size
int pos_; // current printing position
int indent_;
};
#endif // DEBUG
} // namespace internal
} // namespace v8
#endif // V8_AST_PRETTYPRINTER_H_

3258
deps/v8/src/ast/scopes.cc vendored Normal file

File diff suppressed because it is too large Load Diff

1560
deps/v8/src/ast/scopes.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,131 @@
// Copyright 2018 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.
#include "src/ast/source-range-ast-visitor.h"
#include "src/ast/ast-source-ranges.h"
namespace v8 {
namespace internal {
SourceRangeAstVisitor::SourceRangeAstVisitor(uintptr_t stack_limit,
Expression* root,
SourceRangeMap* source_range_map)
: AstTraversalVisitor(stack_limit, root),
source_range_map_(source_range_map) {}
void SourceRangeAstVisitor::VisitBlock(Block* stmt) {
AstTraversalVisitor::VisitBlock(stmt);
ZonePtrList<Statement>* stmts = stmt->statements();
AstNodeSourceRanges* enclosingSourceRanges = source_range_map_->Find(stmt);
if (enclosingSourceRanges != nullptr) {
CHECK(enclosingSourceRanges->HasRange(SourceRangeKind::kContinuation));
MaybeRemoveLastContinuationRange(stmts);
}
}
void SourceRangeAstVisitor::VisitSwitchStatement(SwitchStatement* stmt) {
AstTraversalVisitor::VisitSwitchStatement(stmt);
ZonePtrList<CaseClause>* clauses = stmt->cases();
for (CaseClause* clause : *clauses) {
MaybeRemoveLastContinuationRange(clause->statements());
}
}
void SourceRangeAstVisitor::VisitFunctionLiteral(FunctionLiteral* expr) {
AstTraversalVisitor::VisitFunctionLiteral(expr);
ZonePtrList<Statement>* stmts = expr->body();
MaybeRemoveLastContinuationRange(stmts);
}
void SourceRangeAstVisitor::VisitTryCatchStatement(TryCatchStatement* stmt) {
AstTraversalVisitor::VisitTryCatchStatement(stmt);
MaybeRemoveContinuationRange(stmt->try_block());
MaybeRemoveContinuationRangeOfAsyncReturn(stmt);
}
void SourceRangeAstVisitor::VisitTryFinallyStatement(
TryFinallyStatement* stmt) {
AstTraversalVisitor::VisitTryFinallyStatement(stmt);
MaybeRemoveContinuationRange(stmt->try_block());
}
bool SourceRangeAstVisitor::VisitNode(AstNode* node) {
AstNodeSourceRanges* range = source_range_map_->Find(node);
if (range == nullptr) return true;
if (!range->HasRange(SourceRangeKind::kContinuation)) return true;
// Called in pre-order. In case of conflicting continuation ranges, only the
// outermost range may survive.
SourceRange continuation = range->GetRange(SourceRangeKind::kContinuation);
if (continuation_positions_.find(continuation.start) !=
continuation_positions_.end()) {
range->RemoveContinuationRange();
} else {
continuation_positions_.emplace(continuation.start);
}
return true;
}
void SourceRangeAstVisitor::MaybeRemoveContinuationRange(
Statement* last_statement) {
AstNodeSourceRanges* last_range = nullptr;
if (last_statement->IsExpressionStatement() &&
last_statement->AsExpressionStatement()->expression()->IsThrow()) {
// For ThrowStatement, source range is tied to Throw expression not
// ExpressionStatement.
last_range = source_range_map_->Find(
last_statement->AsExpressionStatement()->expression());
} else {
last_range = source_range_map_->Find(last_statement);
}
if (last_range == nullptr) return;
if (last_range->HasRange(SourceRangeKind::kContinuation)) {
last_range->RemoveContinuationRange();
}
}
void SourceRangeAstVisitor::MaybeRemoveLastContinuationRange(
ZonePtrList<Statement>* statements) {
if (statements->is_empty()) return;
MaybeRemoveContinuationRange(statements->last());
}
namespace {
Statement* FindLastNonSyntheticStatement(ZonePtrList<Statement>* statements) {
for (int i = statements->length() - 1; i >= 0; --i) {
Statement* stmt = statements->at(i);
if (stmt->IsReturnStatement() &&
stmt->AsReturnStatement()->is_synthetic_async_return()) {
continue;
}
return stmt;
}
return nullptr;
}
} // namespace
void SourceRangeAstVisitor::MaybeRemoveContinuationRangeOfAsyncReturn(
TryCatchStatement* try_catch_stmt) {
// Detect try-catch inserted by NewTryCatchStatementForAsyncAwait in the
// parser (issued for async functions, including async generators), and
// remove the continuation range of the last statement, such that the
// range of the enclosing function body is used.
if (try_catch_stmt->is_try_catch_for_async()) {
Statement* last_non_synthetic =
FindLastNonSyntheticStatement(try_catch_stmt->try_block()->statements());
if (last_non_synthetic) {
MaybeRemoveContinuationRange(last_non_synthetic);
}
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,54 @@
// Copyright 2018 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_AST_SOURCE_RANGE_AST_VISITOR_H_
#define V8_AST_SOURCE_RANGE_AST_VISITOR_H_
#include <unordered_set>
#include "src/ast/ast-traversal-visitor.h"
namespace v8 {
namespace internal {
class SourceRangeMap;
// Post-processes generated source ranges while the AST structure still exists.
//
// In particular, SourceRangeAstVisitor
//
// 1. deduplicates continuation source ranges, only keeping the outermost one.
// See also: https://crbug.com/v8/8539.
//
// 2. removes the source range associated with the final statement in a block
// or function body if the parent itself has a source range associated with it.
// See also: https://crbug.com/v8/8381.
class SourceRangeAstVisitor final
: public AstTraversalVisitor<SourceRangeAstVisitor> {
public:
SourceRangeAstVisitor(uintptr_t stack_limit, Expression* root,
SourceRangeMap* source_range_map);
private:
friend class AstTraversalVisitor<SourceRangeAstVisitor>;
void VisitBlock(Block* stmt);
void VisitSwitchStatement(SwitchStatement* stmt);
void VisitFunctionLiteral(FunctionLiteral* expr);
bool VisitNode(AstNode* node);
void VisitTryCatchStatement(TryCatchStatement* stmt);
void VisitTryFinallyStatement(TryFinallyStatement* stmt);
void MaybeRemoveContinuationRange(Statement* last_statement);
void MaybeRemoveLastContinuationRange(ZonePtrList<Statement>* stmts);
void MaybeRemoveContinuationRangeOfAsyncReturn(TryCatchStatement* stmt);
SourceRangeMap* source_range_map_ = nullptr;
std::unordered_set<int> continuation_positions_;
};
} // namespace internal
} // namespace v8
#endif // V8_AST_SOURCE_RANGE_AST_VISITOR_H_

61
deps/v8/src/ast/variables.cc vendored Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2011 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.
#include "src/ast/variables.h"
#include "src/ast/scopes.h"
#include "src/common/globals.h"
namespace v8 {
namespace internal {
// ----------------------------------------------------------------------------
// Implementation Variable.
Variable::Variable(Variable* other)
: scope_(other->scope_),
name_(other->name_),
local_if_not_shadowed_(nullptr),
next_(nullptr),
index_(other->index_),
initializer_position_(other->initializer_position_),
bit_field_(other->bit_field_) {}
bool Variable::IsGlobalObjectProperty() const {
// Temporaries are never global, they must always be allocated in the
// activation frame.
return (IsDynamicVariableMode(mode()) || mode() == VariableMode::kVar) &&
scope_ != nullptr && scope_->is_script_scope();
}
bool Variable::IsReplGlobal() const {
return scope()->is_repl_mode_scope() &&
(mode() == VariableMode::kLet || mode() == VariableMode::kConst ||
mode() == VariableMode::kUsing ||
mode() == VariableMode::kAwaitUsing);
}
void Variable::RewriteLocationForRepl() {
DCHECK(scope_->is_repl_mode_scope());
if (mode() == VariableMode::kLet || mode() == VariableMode::kConst ||
mode() == VariableMode::kUsing || mode() == VariableMode::kAwaitUsing) {
DCHECK_EQ(location(), VariableLocation::CONTEXT);
bit_field_ =
LocationField::update(bit_field_, VariableLocation::REPL_GLOBAL);
}
}
void Variable::AssignHoleCheckBitmapIndex(ZoneVector<Variable*>& list,
uint8_t next_index) {
DCHECK_EQ(next_index, list.size() + 1);
DCHECK_NE(kUncacheableHoleCheckBitmapIndex, next_index);
DCHECK_LT(next_index, kHoleCheckBitmapBits);
hole_check_analysis_bit_field_ = HoleCheckBitmapIndexField::update(
hole_check_analysis_bit_field_, next_index);
list.push_back(this);
}
} // namespace internal
} // namespace v8

369
deps/v8/src/ast/variables.h vendored Normal file
View File

@ -0,0 +1,369 @@
// Copyright 2011 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_AST_VARIABLES_H_
#define V8_AST_VARIABLES_H_
#include "src/ast/ast-value-factory.h"
#include "src/base/threaded-list.h"
#include "src/common/globals.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
// The AST refers to variables via VariableProxies - placeholders for the actual
// variables. Variables themselves are never directly referred to from the AST,
// they are maintained by scopes, and referred to from VariableProxies and Slots
// after binding and variable allocation.
class Variable final : public ZoneObject {
public:
Variable(Scope* scope, const AstRawString* name, VariableMode mode,
VariableKind kind, InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
IsStaticFlag is_static_flag = IsStaticFlag::kNotStatic)
: scope_(scope),
name_(name),
local_if_not_shadowed_(nullptr),
next_(nullptr),
index_(-1),
initializer_position_(kNoSourcePosition),
bit_field_(MaybeAssignedFlagField::encode(maybe_assigned_flag) |
InitializationFlagField::encode(initialization_flag) |
VariableModeField::encode(mode) |
IsUsedField::encode(false) |
ForceContextAllocationBit::encode(false) |
LocationField::encode(VariableLocation::UNALLOCATED) |
VariableKindField::encode(kind) |
IsStaticFlagField::encode(is_static_flag)),
hole_check_analysis_bit_field_(HoleCheckBitmapIndexField::encode(
kUncacheableHoleCheckBitmapIndex) |
ForceHoleInitializationFlagField::encode(
kHoleInitializationNotForced)) {
// Var declared variables never need initialization.
DCHECK(!(mode == VariableMode::kVar &&
initialization_flag == kNeedsInitialization));
DCHECK_IMPLIES(is_static_flag == IsStaticFlag::kStatic,
IsImmutableLexicalOrPrivateVariableMode(mode));
}
explicit Variable(Variable* other);
// The source code for an eval() call may refer to a variable that is
// in an outer scope about which we don't know anything (it may not
// be the script scope). scope() is nullptr in that case. Currently the
// scope is only used to follow the context chain length.
Scope* scope() const { return scope_; }
// This is for adjusting the scope of temporaries used when desugaring
// parameter initializers.
void set_scope(Scope* scope) { scope_ = scope; }
Handle<String> name() const { return name_->string(); }
const AstRawString* raw_name() const { return name_; }
VariableMode mode() const { return VariableModeField::decode(bit_field_); }
void set_mode(VariableMode mode) {
bit_field_ = VariableModeField::update(bit_field_, mode);
}
void set_is_static_flag(IsStaticFlag is_static_flag) {
bit_field_ = IsStaticFlagField::update(bit_field_, is_static_flag);
}
IsStaticFlag is_static_flag() const {
return IsStaticFlagField::decode(bit_field_);
}
bool is_static() const { return is_static_flag() == IsStaticFlag::kStatic; }
bool has_forced_context_allocation() const {
return ForceContextAllocationBit::decode(bit_field_);
}
void ForceContextAllocation() {
DCHECK(IsUnallocated() || IsContextSlot() || IsLookupSlot() ||
location() == VariableLocation::MODULE);
bit_field_ = ForceContextAllocationBit::update(bit_field_, true);
}
bool is_used() { return IsUsedField::decode(bit_field_); }
void set_is_used() { bit_field_ = IsUsedField::update(bit_field_, true); }
MaybeAssignedFlag maybe_assigned() const {
return MaybeAssignedFlagField::decode(bit_field_);
}
void clear_maybe_assigned() {
bit_field_ = MaybeAssignedFlagField::update(bit_field_, kNotAssigned);
}
void SetMaybeAssigned() {
if (IsImmutableLexicalVariableMode(mode())) {
return;
}
// Private names are only initialized once by us.
if (name_->IsPrivateName()) {
return;
}
// If this variable is dynamically shadowing another variable, then that
// variable could also be assigned (in the non-shadowing case).
if (has_local_if_not_shadowed()) {
// Avoid repeatedly marking the same tree of variables by only recursing
// when this variable's maybe_assigned status actually changes.
if (!maybe_assigned()) {
local_if_not_shadowed()->SetMaybeAssigned();
}
DCHECK_IMPLIES(
(!IsImmutableLexicalVariableMode(local_if_not_shadowed()->mode())),
local_if_not_shadowed()->maybe_assigned());
}
set_maybe_assigned();
}
bool requires_brand_check() const {
return IsPrivateMethodOrAccessorVariableMode(mode());
}
int initializer_position() { return initializer_position_; }
void set_initializer_position(int pos) { initializer_position_ = pos; }
bool IsUnallocated() const {
return location() == VariableLocation::UNALLOCATED;
}
bool IsParameter() const { return location() == VariableLocation::PARAMETER; }
bool IsStackLocal() const { return location() == VariableLocation::LOCAL; }
bool IsStackAllocated() const { return IsParameter() || IsStackLocal(); }
bool IsContextSlot() const { return location() == VariableLocation::CONTEXT; }
bool IsLookupSlot() const { return location() == VariableLocation::LOOKUP; }
bool IsGlobalObjectProperty() const;
// True for 'let' and 'const' variables declared in the script scope of a REPL
// script.
bool IsReplGlobal() const;
bool is_dynamic() const { return IsDynamicVariableMode(mode()); }
// Returns the InitializationFlag this Variable was created with.
// Scope analysis may allow us to relax this initialization
// requirement, which will be reflected in the return value of
// binding_needs_init().
InitializationFlag initialization_flag() const {
return InitializationFlagField::decode(bit_field_);
}
// Whether this variable needs to be initialized with the hole at
// declaration time. Only returns valid results after scope analysis.
bool binding_needs_init() const {
DCHECK_IMPLIES(initialization_flag() == kNeedsInitialization,
IsLexicalVariableMode(mode()) ||
IsPrivateMethodOrAccessorVariableMode(mode()));
DCHECK_IMPLIES(IsHoleInitializationForced(),
initialization_flag() == kNeedsInitialization);
// Always initialize if hole initialization was forced during
// scope analysis.
if (IsHoleInitializationForced()) return true;
// If initialization was not forced, no need for initialization
// for stack allocated variables, since UpdateNeedsHoleCheck()
// in scopes.cc has proven that no VariableProxy refers to
// this variable in such a way that a runtime hole check
// would be generated.
if (IsStackAllocated()) return false;
// Otherwise, defer to the flag set when this Variable was constructed.
return initialization_flag() == kNeedsInitialization;
}
enum ForceHoleInitializationFlag {
kHoleInitializationNotForced = 0,
kHasHoleCheckUseInDifferentClosureScope = 1 << 0,
kHasHoleCheckUseInSameClosureScope = 1 << 1,
kHasHoleCheckUseInUnknownScope = kHasHoleCheckUseInDifferentClosureScope |
kHasHoleCheckUseInSameClosureScope
};
ForceHoleInitializationFlag force_hole_initialization_flag_field() const {
return ForceHoleInitializationFlagField::decode(
hole_check_analysis_bit_field_);
}
bool IsHoleInitializationForced() const {
return force_hole_initialization_flag_field() !=
kHoleInitializationNotForced;
}
bool HasHoleCheckUseInSameClosureScope() const {
return force_hole_initialization_flag_field() &
kHasHoleCheckUseInSameClosureScope;
}
// Called during scope analysis when a VariableProxy is found to
// reference this Variable in such a way that a hole check will
// be required at runtime.
void ForceHoleInitialization(ForceHoleInitializationFlag flag) {
DCHECK_EQ(kNeedsInitialization, initialization_flag());
DCHECK_NE(kHoleInitializationNotForced, flag);
DCHECK(IsLexicalVariableMode(mode()) ||
IsPrivateMethodOrAccessorVariableMode(mode()));
hole_check_analysis_bit_field_ |=
ForceHoleInitializationFlagField::encode(flag);
}
// The first N-1 lexical bindings that need hole checks in a compilation are
// numbered, where N is the number of bits in HoleCheckBitmap. This number is
// an index into a bitmap that the BytecodeGenerator uses to elide redundant
// hole checks.
using HoleCheckBitmap = uint64_t;
// The 0th index is reserved for bindings for which the BytecodeGenerator
// should not elide hole checks, such as for bindings beyond the first N-1.
//
// This index in the bitmap must always be 0.
static constexpr uint8_t kUncacheableHoleCheckBitmapIndex = 0;
static constexpr uint8_t kHoleCheckBitmapBits =
std::numeric_limits<HoleCheckBitmap>::digits;
void ResetHoleCheckBitmapIndex() {
hole_check_analysis_bit_field_ = HoleCheckBitmapIndexField::update(
hole_check_analysis_bit_field_, kUncacheableHoleCheckBitmapIndex);
}
void RememberHoleCheckInBitmap(HoleCheckBitmap& bitmap,
ZoneVector<Variable*>& list) {
DCHECK(v8_flags.ignition_elide_redundant_tdz_checks);
uint8_t index = HoleCheckBitmapIndex();
if (V8_UNLIKELY(index == kUncacheableHoleCheckBitmapIndex)) {
index = list.size() + 1;
// The bitmap is full.
if (index == kHoleCheckBitmapBits) return;
AssignHoleCheckBitmapIndex(list, index);
}
bitmap |= HoleCheckBitmap{1} << index;
DCHECK_EQ(
0, bitmap & (HoleCheckBitmap{1} << kUncacheableHoleCheckBitmapIndex));
}
bool HasRememberedHoleCheck(HoleCheckBitmap bitmap) const {
uint8_t index = HoleCheckBitmapIndex();
bool result = bitmap & (HoleCheckBitmap{1} << index);
DCHECK_IMPLIES(index == kUncacheableHoleCheckBitmapIndex, !result);
return result;
}
bool throw_on_const_assignment(LanguageMode language_mode) const {
return kind() != SLOPPY_FUNCTION_NAME_VARIABLE || is_strict(language_mode);
}
bool is_this() const { return kind() == THIS_VARIABLE; }
bool is_sloppy_function_name() const {
return kind() == SLOPPY_FUNCTION_NAME_VARIABLE;
}
bool is_parameter() const { return kind() == PARAMETER_VARIABLE; }
bool is_sloppy_block_function() {
return kind() == SLOPPY_BLOCK_FUNCTION_VARIABLE;
}
Variable* local_if_not_shadowed() const {
DCHECK((mode() == VariableMode::kDynamicLocal ||
mode() == VariableMode::kDynamic) &&
has_local_if_not_shadowed());
return local_if_not_shadowed_;
}
bool has_local_if_not_shadowed() const {
return local_if_not_shadowed_ != nullptr;
}
void set_local_if_not_shadowed(Variable* local) {
local_if_not_shadowed_ = local;
}
VariableLocation location() const {
return LocationField::decode(bit_field_);
}
VariableKind kind() const { return VariableKindField::decode(bit_field_); }
int index() const { return index_; }
bool IsReceiver() const {
DCHECK(IsParameter());
return index_ == -1;
}
bool IsExport() const {
DCHECK_EQ(location(), VariableLocation::MODULE);
DCHECK_NE(index(), 0);
return index() > 0;
}
void AllocateTo(VariableLocation location, int index) {
DCHECK(IsUnallocated() ||
(this->location() == location && this->index() == index));
DCHECK_IMPLIES(location == VariableLocation::MODULE, index != 0);
bit_field_ = LocationField::update(bit_field_, location);
DCHECK_EQ(location, this->location());
index_ = index;
}
void MakeParameterNonSimple() {
DCHECK(is_parameter());
bit_field_ = VariableModeField::update(bit_field_, VariableMode::kLet);
bit_field_ =
InitializationFlagField::update(bit_field_, kNeedsInitialization);
}
static InitializationFlag DefaultInitializationFlag(VariableMode mode) {
DCHECK(IsDeclaredVariableMode(mode));
return mode == VariableMode::kVar ? kCreatedInitialized
: kNeedsInitialization;
}
// Rewrites the VariableLocation of repl script scope 'lets' to REPL_GLOBAL.
void RewriteLocationForRepl();
using List = base::ThreadedList<Variable>;
private:
Scope* scope_;
const AstRawString* name_;
// If this field is set, this variable references the stored locally bound
// variable, but it might be shadowed by variable bindings introduced by with
// blocks or sloppy 'eval' calls between the reference scope (inclusive) and
// the binding scope (exclusive).
Variable* local_if_not_shadowed_;
Variable* next_;
int index_;
int initializer_position_;
uint16_t bit_field_;
uint16_t hole_check_analysis_bit_field_;
void set_maybe_assigned() {
bit_field_ = MaybeAssignedFlagField::update(bit_field_, kMaybeAssigned);
}
uint8_t HoleCheckBitmapIndex() const {
return HoleCheckBitmapIndexField::decode(hole_check_analysis_bit_field_);
}
void AssignHoleCheckBitmapIndex(ZoneVector<Variable*>& list,
uint8_t next_index);
using VariableModeField = base::BitField16<VariableMode, 0, 4>;
using VariableKindField = VariableModeField::Next<VariableKind, 3>;
using LocationField = VariableKindField::Next<VariableLocation, 3>;
using ForceContextAllocationBit = LocationField::Next<bool, 1>;
using IsUsedField = ForceContextAllocationBit::Next<bool, 1>;
using InitializationFlagField = IsUsedField::Next<InitializationFlag, 1>;
using MaybeAssignedFlagField =
InitializationFlagField::Next<MaybeAssignedFlag, 1>;
using IsStaticFlagField = MaybeAssignedFlagField::Next<IsStaticFlag, 1>;
using HoleCheckBitmapIndexField = base::BitField16<uint8_t, 0, 8>;
using ForceHoleInitializationFlagField =
HoleCheckBitmapIndexField::Next<ForceHoleInitializationFlag, 2>;
Variable** next() { return &next_; }
friend List;
friend base::ThreadedListTraits<Variable>;
};
} // namespace internal
} // namespace v8
#endif // V8_AST_VARIABLES_H_

13
deps/v8/src/base/DEPS vendored Normal file
View File

@ -0,0 +1,13 @@
include_rules = [
"-include",
"+include/v8config.h",
"+include/v8-platform.h",
"-src",
"+src/base",
]
specific_include_rules = {
"ieee754.h": [
"+third_party/glibc/src/sysdeps/ieee754/dbl-64/trig.h"
],
}

11
deps/v8/src/base/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,11 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript"
}

5
deps/v8/src/base/OWNERS vendored Normal file
View File

@ -0,0 +1,5 @@
bikineev@chromium.org
clemensb@chromium.org
ishell@chromium.org
mlippautz@chromium.org
nicohartmann@chromium.org

13
deps/v8/src/base/abort-mode.cc vendored Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2023 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.
#include "src/base/abort-mode.h"
namespace v8 {
namespace base {
AbortMode g_abort_mode = AbortMode::kDefault;
} // namespace base
} // namespace v8

54
deps/v8/src/base/abort-mode.h vendored Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2023 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_ABORT_MODE_H_
#define V8_BASE_ABORT_MODE_H_
// This file describes the way aborts are handled in OS::Abort and the way
// DCHECKs are working.
#include "src/base/base-export.h"
namespace v8 {
namespace base {
enum class AbortMode {
// Used for example for fuzzing when controlled crashes are harmless, such
// as for example for the sandbox. With this:
// - DCHECKs are turned into No-ops and as such V8 is allowed to continue
// execution. This way, the fuzzer can progress past them.
// - CHECKs, FATAL, etc. are turned into regular exits, which allows fuzzers
// to ignore them, as they are harmless in this context.
// - The exit code will either be zero (signaling success) or non-zero
// (signaling failure). The former is for example used in tests in which a
// controlled crash counts as success (for example in sandbox regression
// tests), the latter is typically used for fuzzing where samples that exit
// in this way should be discarded and not mutated further.
kExitWithSuccessAndIgnoreDcheckFailures,
kExitWithFailureAndIgnoreDcheckFailures,
// DCHECKs, CHECKs, etc. use IMMEDIATE_CRASH() to signal abnormal program
// termination. See the --hard-abort flag for more details.
kImmediateCrash,
// CHECKs, DCHECKs, etc. use abort() to signal abnormal program termination.
kDefault
};
V8_BASE_EXPORT extern AbortMode g_abort_mode;
V8_INLINE bool ControlledCrashesAreHarmless() {
return g_abort_mode == AbortMode::kExitWithSuccessAndIgnoreDcheckFailures ||
g_abort_mode == AbortMode::kExitWithFailureAndIgnoreDcheckFailures;
}
V8_INLINE bool DcheckFailuresAreIgnored() {
return g_abort_mode == AbortMode::kExitWithSuccessAndIgnoreDcheckFailures ||
g_abort_mode == AbortMode::kExitWithFailureAndIgnoreDcheckFailures;
}
} // namespace base
} // namespace v8
#endif // V8_BASE_ABORT_MODE_H_

100
deps/v8/src/base/address-region.h vendored Normal file
View File

@ -0,0 +1,100 @@
// Copyright 2018 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_ADDRESS_REGION_H_
#define V8_BASE_ADDRESS_REGION_H_
#include <iostream>
#include "src/base/macros.h"
namespace v8 {
namespace base {
// Helper class representing an address region of certain size.
class AddressRegion {
public:
// Function object that compares the start address of two regions. Usable as
// compare function on std data structures and algorithms.
struct StartAddressLess {
bool operator()(base::AddressRegion a, base::AddressRegion b) const {
return a.begin() < b.begin();
}
};
using Address = uintptr_t;
constexpr AddressRegion() = default;
constexpr AddressRegion(Address address, size_t size)
: address_(address), size_(size) {}
Address begin() const { return address_; }
Address end() const { return address_ + size_; }
size_t size() const { return size_; }
void set_size(size_t size) { size_ = size; }
bool is_empty() const { return size_ == 0; }
bool contains(Address address) const {
static_assert(std::is_unsigned<Address>::value);
return (address - begin()) < size();
}
bool contains(Address address, size_t size) const {
static_assert(std::is_unsigned<Address>::value);
Address offset = address - begin();
return (offset < size_) && (offset + size <= size_);
}
bool contains(AddressRegion region) const {
return contains(region.address_, region.size_);
}
base::AddressRegion GetOverlap(AddressRegion region) const {
Address overlap_start = std::max(begin(), region.begin());
Address overlap_end =
std::max(overlap_start, std::min(end(), region.end()));
return {overlap_start, overlap_end - overlap_start};
}
bool operator==(AddressRegion other) const {
return address_ == other.address_ && size_ == other.size_;
}
bool operator!=(AddressRegion other) const {
return address_ != other.address_ || size_ != other.size_;
}
private:
Address address_ = 0;
size_t size_ = 0;
};
ASSERT_TRIVIALLY_COPYABLE(AddressRegion);
// Construct an AddressRegion from a start pointer and a size.
template <typename T>
inline AddressRegion AddressRegionOf(T* ptr, size_t size) {
return AddressRegion{reinterpret_cast<AddressRegion::Address>(ptr),
sizeof(T) * size};
}
// Construct an AddressRegion from anything providing a {data()} and {size()}
// accessor.
template <typename Container>
inline auto AddressRegionOf(Container&& c)
-> decltype(AddressRegionOf(c.data(), c.size())) {
return AddressRegionOf(c.data(), c.size());
}
inline std::ostream& operator<<(std::ostream& out, AddressRegion region) {
return out << "[" << reinterpret_cast<void*>(region.begin()) << "+"
<< region.size() << "]";
}
} // namespace base
} // namespace v8
#endif // V8_BASE_ADDRESS_REGION_H_

313
deps/v8/src/base/atomic-utils.h vendored Normal file
View File

@ -0,0 +1,313 @@
// Copyright 2015 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_ATOMIC_UTILS_H_
#define V8_BASE_ATOMIC_UTILS_H_
#include <limits.h>
#include <atomic>
#include <type_traits>
#include "src/base/atomicops.h"
#include "src/base/macros.h"
#include "src/base/strong-alias.h"
namespace v8 {
namespace base {
// Deprecated. Use std::atomic<T> for new code.
// Flag using T atomically. Also accepts void* as T.
template <typename T>
class AtomicValue {
public:
AtomicValue() : value_(0) {}
explicit AtomicValue(T initial)
: value_(cast_helper<T>::to_storage_type(initial)) {}
V8_INLINE T Value() const {
return cast_helper<T>::to_return_type(base::Acquire_Load(&value_));
}
V8_INLINE void SetValue(T new_value) {
base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value));
}
private:
static_assert(sizeof(T) <= sizeof(base::AtomicWord));
template <typename S>
struct cast_helper {
static base::AtomicWord to_storage_type(S value) {
return static_cast<base::AtomicWord>(value);
}
static S to_return_type(base::AtomicWord value) {
return static_cast<S>(value);
}
};
template <typename S>
struct cast_helper<S*> {
static base::AtomicWord to_storage_type(S* value) {
return reinterpret_cast<base::AtomicWord>(value);
}
static S* to_return_type(base::AtomicWord value) {
return reinterpret_cast<S*>(value);
}
};
base::AtomicWord value_;
};
// Provides atomic operations for a values stored at some address.
template <typename TAtomicStorageType>
class AsAtomicImpl {
public:
using AtomicStorageType = TAtomicStorageType;
template <typename T>
static T SeqCst_Load(T* addr) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(
base::SeqCst_Load(to_storage_addr(addr)));
}
template <typename T>
static T Acquire_Load(T* addr) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(
base::Acquire_Load(to_storage_addr(addr)));
}
template <typename T>
static T Relaxed_Load(T* addr) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(
base::Relaxed_Load(to_storage_addr(addr)));
}
template <typename T>
static void SeqCst_Store(T* addr,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
base::SeqCst_Store(to_storage_addr(addr),
cast_helper<T>::to_storage_type(new_value));
}
template <typename T>
static void Release_Store(T* addr,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
base::Release_Store(to_storage_addr(addr),
cast_helper<T>::to_storage_type(new_value));
}
template <typename T>
static void Relaxed_Store(T* addr,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
base::Relaxed_Store(to_storage_addr(addr),
cast_helper<T>::to_storage_type(new_value));
}
template <typename T>
static T SeqCst_Swap(T* addr,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return base::SeqCst_AtomicExchange(
to_storage_addr(addr), cast_helper<T>::to_storage_type(new_value));
}
template <typename T>
static T Release_CompareAndSwap(
T* addr, typename std::remove_reference<T>::type old_value,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(base::Release_CompareAndSwap(
to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
cast_helper<T>::to_storage_type(new_value)));
}
template <typename T>
static T Relaxed_CompareAndSwap(
T* addr, typename std::remove_reference<T>::type old_value,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(base::Relaxed_CompareAndSwap(
to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
cast_helper<T>::to_storage_type(new_value)));
}
template <typename T>
static T AcquireRelease_CompareAndSwap(
T* addr, typename std::remove_reference<T>::type old_value,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(base::AcquireRelease_CompareAndSwap(
to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
cast_helper<T>::to_storage_type(new_value)));
}
template <typename T>
static T SeqCst_CompareAndSwap(
T* addr, typename std::remove_reference<T>::type old_value,
typename std::remove_reference<T>::type new_value) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
return cast_helper<T>::to_return_type(base::SeqCst_CompareAndSwap(
to_storage_addr(addr), cast_helper<T>::to_storage_type(old_value),
cast_helper<T>::to_storage_type(new_value)));
}
// Atomically sets bits selected by the mask to the given value.
// Returns false if the bits are already set as needed.
template <typename T>
static bool Release_SetBits(T* addr, T bits, T mask) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
DCHECK_EQ(bits & ~mask, static_cast<T>(0));
T old_value = Relaxed_Load(addr);
T new_value, old_value_before_cas;
do {
if ((old_value & mask) == bits) return false;
new_value = (old_value & ~mask) | bits;
old_value_before_cas = old_value;
old_value = Release_CompareAndSwap(addr, old_value, new_value);
} while (old_value != old_value_before_cas);
return true;
}
// Atomically sets bits selected by the mask to the given value.
// Returns false if the bits are already set as needed.
template <typename T>
static bool Relaxed_SetBits(T* addr, T bits, T mask) {
static_assert(sizeof(T) <= sizeof(AtomicStorageType));
DCHECK_EQ(bits & ~mask, static_cast<T>(0));
T old_value = Relaxed_Load(addr);
T new_value, old_value_before_cas;
do {
if ((old_value & mask) == bits) return false;
new_value = (old_value & ~mask) | bits;
old_value_before_cas = old_value;
old_value = Relaxed_CompareAndSwap(addr, old_value, new_value);
} while (old_value != old_value_before_cas);
return true;
}
private:
template <typename U>
struct cast_helper {
static AtomicStorageType to_storage_type(U value) {
return static_cast<AtomicStorageType>(value);
}
static U to_return_type(AtomicStorageType value) {
return static_cast<U>(value);
}
};
template <typename U>
struct cast_helper<U*> {
static AtomicStorageType to_storage_type(U* value) {
return reinterpret_cast<AtomicStorageType>(value);
}
static U* to_return_type(AtomicStorageType value) {
return reinterpret_cast<U*>(value);
}
};
template <typename T, typename U>
struct cast_helper<base::StrongAlias<T, U>> {
static AtomicStorageType to_storage_type(base::StrongAlias<T, U> value) {
return static_cast<AtomicStorageType>(value.value());
}
static base::StrongAlias<T, U> to_return_type(AtomicStorageType value) {
return base::StrongAlias<T, U>(static_cast<U>(value));
}
};
template <typename T>
static AtomicStorageType* to_storage_addr(T* value) {
return reinterpret_cast<AtomicStorageType*>(value);
}
template <typename T>
static const AtomicStorageType* to_storage_addr(const T* value) {
return reinterpret_cast<const AtomicStorageType*>(value);
}
};
using AsAtomic8 = AsAtomicImpl<base::Atomic8>;
using AsAtomic16 = AsAtomicImpl<base::Atomic16>;
using AsAtomic32 = AsAtomicImpl<base::Atomic32>;
using AsAtomicWord = AsAtomicImpl<base::AtomicWord>;
template <int Width>
struct AtomicTypeFromByteWidth {};
template <>
struct AtomicTypeFromByteWidth<1> {
using type = base::Atomic8;
};
template <>
struct AtomicTypeFromByteWidth<2> {
using type = base::Atomic16;
};
template <>
struct AtomicTypeFromByteWidth<4> {
using type = base::Atomic32;
};
#if V8_HOST_ARCH_64_BIT
template <>
struct AtomicTypeFromByteWidth<8> {
using type = base::Atomic64;
};
#endif
// This is similar to AsAtomicWord but it explicitly deletes functionality
// provided atomic access to bit representation of stored values.
template <typename TAtomicStorageType>
class AsAtomicPointerImpl : public AsAtomicImpl<TAtomicStorageType> {
public:
template <typename T>
static bool SetBits(T* addr, T bits, T mask) = delete;
};
using AsAtomicPointer = AsAtomicPointerImpl<base::AtomicWord>;
template <typename T>
inline void CheckedIncrement(
std::atomic<T>* number, T amount,
std::memory_order order = std::memory_order_seq_cst)
requires std::is_unsigned<T>::value
{
const T old = number->fetch_add(amount, order);
DCHECK_GE(old + amount, old);
USE(old);
}
template <typename T>
inline void CheckedDecrement(
std::atomic<T>* number, T amount,
std::memory_order order = std::memory_order_seq_cst)
requires std::is_unsigned<T>::value
{
const T old = number->fetch_sub(amount, order);
DCHECK_GE(old, amount);
USE(old);
}
template <typename T>
V8_INLINE std::atomic<T>* AsAtomicPtr(T* t) {
static_assert(sizeof(T) == sizeof(std::atomic<T>));
static_assert(alignof(T) >= alignof(std::atomic<T>));
return reinterpret_cast<std::atomic<T>*>(t);
}
template <typename T>
V8_INLINE const std::atomic<T>* AsAtomicPtr(const T* t) {
static_assert(sizeof(T) == sizeof(std::atomic<T>));
static_assert(alignof(T) >= alignof(std::atomic<T>));
return reinterpret_cast<const std::atomic<T>*>(t);
}
} // namespace base
} // namespace v8
#endif // V8_BASE_ATOMIC_UTILS_H_

486
deps/v8/src/base/atomicops.h vendored Normal file
View File

@ -0,0 +1,486 @@
// Copyright 2010 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_ATOMICOPS_H_
#define V8_BASE_ATOMICOPS_H_
// The routines exported by this module are subtle. If you use them, even if
// you get the code right, it will depend on careful reasoning about atomicity
// and memory ordering; it will be less readable, and harder to maintain. If
// you plan to use these routines, you should have a good reason, such as solid
// evidence that performance would otherwise suffer, or there being no
// alternative. You should assume only properties explicitly guaranteed by the
// specifications in this file. You are almost certainly _not_ writing code
// just for the x86; if you assume x86 semantics, x86 hardware bugs and
// implementations on other archtectures will cause your code to break. If you
// do not know what you are doing, avoid these routines, and use a Mutex.
//
// It is incorrect to make direct assignments to/from an atomic variable.
// You should use one of the Load or Store routines. The Relaxed versions
// are provided when no fences are needed:
// Relaxed_Store()
// Relaxed_Load()
// Although there are currently no compiler enforcement, you are encouraged
// to use these.
//
#include <stdint.h>
#include <atomic>
// Small C++ header which defines implementation specific macros used to
// identify the STL implementation.
// - libc++: captures __config for _LIBCPP_VERSION
// - libstdc++: captures bits/c++config.h for __GLIBCXX__
#include <cstddef>
#include "src/base/base-export.h"
#include "src/base/build_config.h"
#include "src/base/macros.h"
#if defined(V8_OS_STARBOARD)
#include "starboard/atomic.h"
#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;
#if defined(V8_HOST_ARCH_64_BIT)
// We need to be able to go between Atomic64 and AtomicWord implicitly. This
// means Atomic64 and AtomicWord should be the same type on 64-bit.
#if defined(__ILP32__)
using Atomic64 = int64_t;
#else
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_HOST_ARCH_64_BIT)
using AtomicWord = Atomic64;
#else
using AtomicWord = Atomic32;
#endif
static_assert(sizeof(void*) == sizeof(AtomicWord));
namespace helper {
template <typename T>
volatile std::atomic<T>* to_std_atomic(volatile T* ptr) {
return reinterpret_cast<volatile std::atomic<T>*>(ptr);
}
template <typename T>
volatile const std::atomic<T>* to_std_atomic_const(volatile const T* ptr) {
return reinterpret_cast<volatile const std::atomic<T>*>(ptr);
}
} // namespace helper
inline void SeqCst_MemoryFence() {
std::atomic_thread_fence(std::memory_order_seq_cst);
}
// Atomically execute:
// result = *ptr;
// if (result == old_value)
// *ptr = new_value;
// return result;
//
// 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.
inline Atomic8 Relaxed_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value,
Atomic8 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_relaxed, std::memory_order_relaxed);
return old_value;
}
inline Atomic16 Relaxed_CompareAndSwap(volatile Atomic16* ptr,
Atomic16 old_value, Atomic16 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_relaxed, std::memory_order_relaxed);
return old_value;
}
inline Atomic32 Relaxed_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_relaxed, std::memory_order_relaxed);
return old_value;
}
inline Atomic32 Relaxed_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
std::memory_order_relaxed);
}
inline Atomic32 SeqCst_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
std::memory_order_seq_cst);
}
inline Atomic32 Relaxed_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return increment + std::atomic_fetch_add_explicit(helper::to_std_atomic(ptr),
increment,
std::memory_order_relaxed);
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_acquire, std::memory_order_acquire);
return old_value;
}
inline Atomic8 Release_CompareAndSwap(volatile Atomic8* ptr, Atomic8 old_value,
Atomic8 new_value) {
bool result = atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_release, std::memory_order_relaxed);
USE(result); // Make gcc compiler happy.
return old_value;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_release, std::memory_order_relaxed);
return old_value;
}
inline Atomic32 AcquireRelease_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_acq_rel, std::memory_order_acquire);
return old_value;
}
inline Atomic32 SeqCst_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_seq_cst, std::memory_order_seq_cst);
return old_value;
}
inline void Relaxed_Store(volatile Atomic8* ptr, Atomic8 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_relaxed);
}
inline void Relaxed_Store(volatile Atomic16* ptr, Atomic16 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_relaxed);
}
inline void Relaxed_Store(volatile Atomic32* ptr, Atomic32 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_relaxed);
}
inline void Release_Store(volatile Atomic8* ptr, Atomic8 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_release);
}
inline void Release_Store(volatile Atomic16* ptr, Atomic16 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_release);
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_release);
}
inline void SeqCst_Store(volatile Atomic8* ptr, Atomic8 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_seq_cst);
}
inline void SeqCst_Store(volatile Atomic16* ptr, Atomic16 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_seq_cst);
}
inline void SeqCst_Store(volatile Atomic32* ptr, Atomic32 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_seq_cst);
}
inline Atomic8 Relaxed_Load(volatile const Atomic8* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_relaxed);
}
inline Atomic16 Relaxed_Load(volatile const Atomic16* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_relaxed);
}
inline Atomic32 Relaxed_Load(volatile const Atomic32* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_relaxed);
}
inline Atomic8 Acquire_Load(volatile const Atomic8* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_acquire);
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_acquire);
}
inline Atomic8 SeqCst_Load(volatile const Atomic8* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_seq_cst);
}
inline Atomic32 SeqCst_Load(volatile const Atomic32* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_seq_cst);
}
#if defined(V8_HOST_ARCH_64_BIT)
inline Atomic64 Relaxed_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_relaxed, std::memory_order_relaxed);
return old_value;
}
inline Atomic64 Relaxed_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
std::memory_order_relaxed);
}
inline Atomic64 SeqCst_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
return std::atomic_exchange_explicit(helper::to_std_atomic(ptr), new_value,
std::memory_order_seq_cst);
}
inline Atomic64 Relaxed_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return increment + std::atomic_fetch_add_explicit(helper::to_std_atomic(ptr),
increment,
std::memory_order_relaxed);
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_acquire, std::memory_order_acquire);
return old_value;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_release, std::memory_order_relaxed);
return old_value;
}
inline Atomic64 AcquireRelease_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_acq_rel, std::memory_order_acquire);
return old_value;
}
inline Atomic64 SeqCst_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
std::atomic_compare_exchange_strong_explicit(
helper::to_std_atomic(ptr), &old_value, new_value,
std::memory_order_seq_cst, std::memory_order_seq_cst);
return old_value;
}
inline void Relaxed_Store(volatile Atomic64* ptr, Atomic64 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_relaxed);
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_release);
}
inline void SeqCst_Store(volatile Atomic64* ptr, Atomic64 value) {
std::atomic_store_explicit(helper::to_std_atomic(ptr), value,
std::memory_order_seq_cst);
}
inline Atomic64 Relaxed_Load(volatile const Atomic64* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_relaxed);
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_acquire);
}
inline Atomic64 SeqCst_Load(volatile const Atomic64* ptr) {
return std::atomic_load_explicit(helper::to_std_atomic_const(ptr),
std::memory_order_seq_cst);
}
#endif // defined(V8_HOST_ARCH_64_BIT)
inline void Relaxed_Memcpy(volatile Atomic8* dst, volatile const Atomic8* src,
size_t bytes) {
constexpr size_t kAtomicWordSize = sizeof(AtomicWord);
while (bytes > 0 &&
!IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
Relaxed_Store(dst++, Relaxed_Load(src++));
--bytes;
}
if (IsAligned(reinterpret_cast<uintptr_t>(src), kAtomicWordSize) &&
IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
while (bytes >= kAtomicWordSize) {
Relaxed_Store(
reinterpret_cast<volatile AtomicWord*>(dst),
Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(src)));
dst += kAtomicWordSize;
src += kAtomicWordSize;
bytes -= kAtomicWordSize;
}
}
while (bytes > 0) {
Relaxed_Store(dst++, Relaxed_Load(src++));
--bytes;
}
}
inline void Relaxed_Memmove(volatile Atomic8* dst, volatile const Atomic8* src,
size_t bytes) {
// Use Relaxed_Memcpy if copying forwards is safe. This is the case if there
// is no overlap, or {dst} lies before {src}.
// This single check checks for both:
if (reinterpret_cast<uintptr_t>(dst) - reinterpret_cast<uintptr_t>(src) >=
bytes) {
Relaxed_Memcpy(dst, src, bytes);
return;
}
// Otherwise copy backwards.
dst += bytes;
src += bytes;
constexpr size_t kAtomicWordSize = sizeof(AtomicWord);
while (bytes > 0 &&
!IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
Relaxed_Store(--dst, Relaxed_Load(--src));
--bytes;
}
if (IsAligned(reinterpret_cast<uintptr_t>(src), kAtomicWordSize) &&
IsAligned(reinterpret_cast<uintptr_t>(dst), kAtomicWordSize)) {
while (bytes >= kAtomicWordSize) {
dst -= kAtomicWordSize;
src -= kAtomicWordSize;
bytes -= kAtomicWordSize;
Relaxed_Store(
reinterpret_cast<volatile AtomicWord*>(dst),
Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(src)));
}
}
while (bytes > 0) {
Relaxed_Store(--dst, Relaxed_Load(--src));
--bytes;
}
}
namespace helper {
inline int MemcmpNotEqualFundamental(Atomic8 u1, Atomic8 u2) {
DCHECK_NE(u1, u2);
return u1 < u2 ? -1 : 1;
}
inline int MemcmpNotEqualFundamental(AtomicWord u1, AtomicWord u2) {
DCHECK_NE(u1, u2);
#if defined(V8_TARGET_BIG_ENDIAN)
return u1 < u2 ? -1 : 1;
#else
for (size_t i = 0; i < sizeof(AtomicWord); ++i) {
uint8_t byte1 = u1 & 0xFF;
uint8_t byte2 = u2 & 0xFF;
if (byte1 != byte2) return byte1 < byte2 ? -1 : 1;
u1 >>= 8;
u2 >>= 8;
}
UNREACHABLE();
#endif
}
} // namespace helper
inline int Relaxed_Memcmp(volatile const Atomic8* s1,
volatile const Atomic8* s2, size_t len) {
constexpr size_t kAtomicWordSize = sizeof(AtomicWord);
while (len > 0 &&
!(IsAligned(reinterpret_cast<uintptr_t>(s1), kAtomicWordSize) &&
IsAligned(reinterpret_cast<uintptr_t>(s2), kAtomicWordSize))) {
Atomic8 u1 = Relaxed_Load(s1++);
Atomic8 u2 = Relaxed_Load(s2++);
if (u1 != u2) return helper::MemcmpNotEqualFundamental(u1, u2);
--len;
}
if (IsAligned(reinterpret_cast<uintptr_t>(s1), kAtomicWordSize) &&
IsAligned(reinterpret_cast<uintptr_t>(s2), kAtomicWordSize)) {
while (len >= kAtomicWordSize) {
AtomicWord u1 =
Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(s1));
AtomicWord u2 =
Relaxed_Load(reinterpret_cast<const volatile AtomicWord*>(s2));
if (u1 != u2) return helper::MemcmpNotEqualFundamental(u1, u2);
s1 += kAtomicWordSize;
s2 += kAtomicWordSize;
len -= kAtomicWordSize;
}
}
while (len > 0) {
Atomic8 u1 = Relaxed_Load(s1++);
Atomic8 u2 = Relaxed_Load(s2++);
if (u1 != u2) return helper::MemcmpNotEqualFundamental(u1, u2);
--len;
}
return 0;
}
} // namespace base
} // namespace v8
#endif // V8_BASE_ATOMICOPS_H_

31
deps/v8/src/base/base-export.h vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2016 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_BASE_EXPORT_H_
#define V8_BASE_BASE_EXPORT_H_
#include "include/v8config.h"
#if V8_OS_WIN
#ifdef BUILDING_V8_BASE_SHARED
#define V8_BASE_EXPORT __declspec(dllexport)
#elif USING_V8_BASE_SHARED
#define V8_BASE_EXPORT __declspec(dllimport)
#else
#define V8_BASE_EXPORT
#endif // BUILDING_V8_BASE_SHARED
#else // !V8_OS_WIN
// Setup for Linux shared library export.
#if defined(BUILDING_V8_BASE_SHARED) || USING_V8_BASE_SHARED
#define V8_BASE_EXPORT __attribute__((visibility("default")))
#else
#define V8_BASE_EXPORT
#endif // defined(BUILDING_V8_BASE_SHARED) || ...
#endif // V8_OS_WIN
#endif // V8_BASE_BASE_EXPORT_H_

176
deps/v8/src/base/bit-field.h vendored Normal file
View File

@ -0,0 +1,176 @@
// Copyright 2019 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_BIT_FIELD_H_
#define V8_BASE_BIT_FIELD_H_
#include <stdint.h>
#include <algorithm>
#include "src/base/macros.h"
namespace v8 {
namespace base {
// ----------------------------------------------------------------------------
// BitField is a help template for encoding and decode bitfield with
// unsigned content.
// Instantiate them via 'using', which is cheaper than deriving a new class:
// using MyBitField = base::BitField<MyEnum, 4, 2>;
// The BitField class is final to enforce this style over derivation.
template <class T, int shift, int size, class U = uint32_t>
class BitField final {
public:
static_assert(std::is_unsigned<U>::value);
static_assert(shift < 8 * sizeof(U)); // Otherwise shifts by {shift} are UB.
static_assert(size < 8 * sizeof(U)); // Otherwise shifts by {size} are UB.
static_assert(shift + size <= 8 * sizeof(U));
static_assert(size > 0);
using FieldType = T;
using BaseType = U;
// A type U mask of bit field. To use all bits of a type U of x bits
// in a bitfield without compiler warnings we have to compute 2^x
// without using a shift count of x in the computation.
static constexpr int kShift = shift;
static constexpr int kSize = size;
static constexpr U kMask = ((U{1} << kShift) << kSize) - (U{1} << kShift);
static constexpr int kLastUsedBit = kShift + kSize - 1;
static constexpr U kNumValues = U{1} << kSize;
static constexpr U kMax = kNumValues - 1;
template <class T2, int size2>
using Next = BitField<T2, kShift + kSize, size2, U>;
// Tells whether the provided value fits into the bit field.
static constexpr bool is_valid(T value) {
return (static_cast<U>(value) & ~kMax) == 0;
}
// Returns a type U with the bit field value encoded.
static constexpr U encode(T value) {
DCHECK(is_valid(value));
return static_cast<U>(value) << kShift;
}
// Returns a type U with the bit field value updated.
V8_NODISCARD static constexpr U update(U previous, T value) {
return (previous & ~kMask) | encode(value);
}
// Extracts the bit field from the value.
static constexpr T decode(U value) {
return static_cast<T>((value & kMask) >> kShift);
}
};
// ----------------------------------------------------------------------------
// BitFieldUnion can be used to combine two linear BitFields.
// So far only the static mask is computed. Encoding and decoding tbd.
// Can be used for example as a quick combined check:
// `if (BitFieldUnion<BFA, BFB>::kMask & bitfield) ...`
template <typename A, typename B>
class BitFieldUnion final {
public:
static_assert(
std::is_same<typename A::BaseType, typename B::BaseType>::value);
static_assert((A::kMask & B::kMask) == 0);
static constexpr int kShift = std::min(A::kShift, B::kShift);
static constexpr int kMask = A::kMask | B::kMask;
static constexpr int kSize =
A::kSize + B::kSize + (std::max(A::kShift, B::kShift) - kShift);
};
template <class T, int shift, int size>
using BitField8 = BitField<T, shift, size, uint8_t>;
template <class T, int shift, int size>
using BitField16 = BitField<T, shift, size, uint16_t>;
template <class T, int shift, int size>
using BitField64 = BitField<T, shift, size, uint64_t>;
// Helper macros for defining a contiguous sequence of bit fields. Example:
// (backslashes at the ends of respective lines of this multi-line macro
// definition are omitted here to please the compiler)
//
// #define MAP_BIT_FIELD1(V, _)
// V(IsAbcBit, bool, 1, _)
// V(IsBcdBit, bool, 1, _)
// V(CdeBits, int, 5, _)
// V(DefBits, MutableMode, 1, _)
//
// DEFINE_BIT_FIELDS(MAP_BIT_FIELD1)
// or
// DEFINE_BIT_FIELDS_64(MAP_BIT_FIELD1)
//
#define DEFINE_BIT_FIELD_RANGE_TYPE(Name, Type, Size, _) \
k##Name##Start, k##Name##End = k##Name##Start + Size - 1,
#define DEFINE_BIT_RANGES(LIST_MACRO) \
struct LIST_MACRO##_Ranges { \
enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE, _) kBitsCount }; \
};
#define DEFINE_BIT_FIELD_TYPE(Name, Type, Size, RangesName) \
using Name = base::BitField<Type, RangesName::k##Name##Start, Size>;
#define DEFINE_BIT_FIELD_64_TYPE(Name, Type, Size, RangesName) \
using Name = base::BitField64<Type, RangesName::k##Name##Start, Size>;
#define DEFINE_BIT_FIELDS(LIST_MACRO) \
DEFINE_BIT_RANGES(LIST_MACRO) \
LIST_MACRO(DEFINE_BIT_FIELD_TYPE, LIST_MACRO##_Ranges)
#define DEFINE_BIT_FIELDS_64(LIST_MACRO) \
DEFINE_BIT_RANGES(LIST_MACRO) \
LIST_MACRO(DEFINE_BIT_FIELD_64_TYPE, LIST_MACRO##_Ranges)
// ----------------------------------------------------------------------------
// BitSetComputer is a help template for encoding and decoding information for
// a variable number of items in an array.
//
// To encode boolean data in a smi array you would use:
// using BoolComputer = BitSetComputer<bool, 1, kSmiValueSize, uint32_t>;
//
template <class T, int kBitsPerItem, int kBitsPerWord, class U>
class BitSetComputer {
public:
static const int kItemsPerWord = kBitsPerWord / kBitsPerItem;
static const int kMask = (1 << kBitsPerItem) - 1;
// The number of array elements required to embed T information for each item.
static int word_count(int items) {
if (items == 0) return 0;
return (items - 1) / kItemsPerWord + 1;
}
// The array index to look at for item.
static int index(int base_index, int item) {
return base_index + item / kItemsPerWord;
}
// Extract T data for a given item from data.
static T decode(U data, int item) {
return static_cast<T>((data >> shift(item)) & kMask);
}
// Return the encoding for a store of value for item in previous.
static U encode(U previous, int item, T value) {
int shift_value = shift(item);
int set_bits = (static_cast<int>(value) << shift_value);
return (previous & ~(kMask << shift_value)) | set_bits;
}
static int shift(int item) { return (item % kItemsPerWord) * kBitsPerItem; }
};
} // namespace base
} // namespace v8
#endif // V8_BASE_BIT_FIELD_H_

58
deps/v8/src/base/bits-iterator.h vendored Normal file
View File

@ -0,0 +1,58 @@
// 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_BITS_ITERATOR_H_
#define V8_BASE_BITS_ITERATOR_H_
#include <type_traits>
#include "src/base/bits.h"
#include "src/base/iterator.h"
namespace v8 {
namespace base {
namespace bits {
template <typename T, bool kMSBFirst = false>
class BitsIterator : public iterator<std::forward_iterator_tag, int> {
static_assert(std::is_integral<T>::value);
public:
explicit BitsIterator(T bits) : bits_(bits) {}
int operator*() const {
return kMSBFirst ? 8 * sizeof(T) - 1 - CountLeadingZeros(bits_)
: CountTrailingZeros(bits_);
}
BitsIterator& operator++() {
bits_ &= ~(T{1} << **this);
return *this;
}
bool operator==(BitsIterator other) { return bits_ == other.bits_; }
bool operator!=(BitsIterator other) { return bits_ != other.bits_; }
private:
T bits_;
};
// Returns an iterable over the bits in {bits}, from LSB to MSB.
template <typename T>
auto IterateBits(T bits) {
return make_iterator_range(BitsIterator<T>{bits}, BitsIterator<T>{0});
}
// Returns an iterable over the bits in {bits}, from MSB to LSB.
template <typename T>
auto IterateBitsBackwards(T bits) {
return make_iterator_range(BitsIterator<T, true>{bits},
BitsIterator<T, true>{0});
}
} // namespace bits
} // namespace base
} // namespace v8
#endif // V8_BASE_BITS_ITERATOR_H_

111
deps/v8/src/base/bits.cc vendored Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2014 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.
#include "src/base/bits.h"
#include <limits>
#include "src/base/logging.h"
namespace v8 {
namespace base {
namespace bits {
int32_t SignedMulHigh32(int32_t lhs, int32_t rhs) {
int64_t const value = static_cast<int64_t>(lhs) * static_cast<int64_t>(rhs);
return base::bit_cast<int32_t, uint32_t>(base::bit_cast<uint64_t>(value) >>
32u);
}
// The algorithm used is described in section 8.2 of
// Hacker's Delight, by Henry S. Warren, Jr.
// It assumes that a right shift on a signed integer is an arithmetic shift.
int64_t SignedMulHigh64(int64_t u, int64_t v) {
uint64_t u0 = u & 0xFFFFFFFF;
int64_t u1 = u >> 32;
uint64_t v0 = v & 0xFFFFFFFF;
int64_t v1 = v >> 32;
uint64_t w0 = u0 * v0;
int64_t t = u1 * v0 + (w0 >> 32);
int64_t w1 = t & 0xFFFFFFFF;
int64_t w2 = t >> 32;
w1 = u0 * v1 + w1;
return u1 * v1 + w2 + (w1 >> 32);
}
// The algorithm used is described in section 8.2 of
// Hacker's Delight, by Henry S. Warren, Jr.
uint64_t UnsignedMulHigh64(uint64_t u, uint64_t v) {
uint64_t u0 = u & 0xFFFFFFFF;
uint64_t u1 = u >> 32;
uint64_t v0 = v & 0xFFFFFFFF;
uint64_t v1 = v >> 32;
uint64_t w0 = u0 * v0;
uint64_t t = u1 * v0 + (w0 >> 32);
uint64_t w1 = t & 0xFFFFFFFFLL;
uint64_t w2 = t >> 32;
w1 = u0 * v1 + w1;
return u1 * v1 + w2 + (w1 >> 32);
}
uint32_t UnsignedMulHigh32(uint32_t lhs, uint32_t rhs) {
uint64_t const value =
static_cast<uint64_t>(lhs) * static_cast<uint64_t>(rhs);
return static_cast<uint32_t>(value >> 32u);
}
int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs, int32_t acc) {
return base::bit_cast<int32_t>(
base::bit_cast<uint32_t>(acc) +
base::bit_cast<uint32_t>(SignedMulHigh32(lhs, rhs)));
}
int32_t SignedDiv32(int32_t lhs, int32_t rhs) {
if (rhs == 0) return 0;
if (rhs == -1) return lhs == std::numeric_limits<int32_t>::min() ? lhs : -lhs;
return lhs / rhs;
}
int64_t SignedDiv64(int64_t lhs, int64_t rhs) {
if (rhs == 0) return 0;
if (rhs == -1) return lhs == std::numeric_limits<int64_t>::min() ? lhs : -lhs;
return lhs / rhs;
}
int32_t SignedMod32(int32_t lhs, int32_t rhs) {
if (rhs == 0 || rhs == -1) return 0;
return lhs % rhs;
}
int64_t SignedMod64(int64_t lhs, int64_t rhs) {
if (rhs == 0 || rhs == -1) return 0;
return lhs % rhs;
}
int64_t SignedSaturatedAdd64(int64_t lhs, int64_t rhs) {
using limits = std::numeric_limits<int64_t>;
// Underflow if {lhs + rhs < min}. In that case, return {min}.
if (rhs < 0 && lhs < limits::min() - rhs) return limits::min();
// Overflow if {lhs + rhs > max}. In that case, return {max}.
if (rhs >= 0 && lhs > limits::max() - rhs) return limits::max();
return lhs + rhs;
}
int64_t SignedSaturatedSub64(int64_t lhs, int64_t rhs) {
using limits = std::numeric_limits<int64_t>;
// Underflow if {lhs - rhs < min}. In that case, return {min}.
if (rhs > 0 && lhs < limits::min() + rhs) return limits::min();
// Overflow if {lhs - rhs > max}. In that case, return {max}.
if (rhs <= 0 && lhs > limits::max() + rhs) return limits::max();
return lhs - rhs;
}
} // namespace bits
} // namespace base
} // namespace v8

494
deps/v8/src/base/bits.h vendored Normal file
View File

@ -0,0 +1,494 @@
// Copyright 2014 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_BITS_H_
#define V8_BASE_BITS_H_
#include <stdint.h>
#include <type_traits>
#include "src/base/base-export.h"
#include "src/base/macros.h"
#if V8_CC_MSVC
#include <intrin.h>
#endif
#if V8_OS_WIN32
#include "src/base/win32-headers.h"
#endif
namespace v8 {
namespace base {
namespace bits {
// CountPopulation(value) returns the number of bits set in |value|.
template <typename T>
constexpr inline unsigned CountPopulation(T value)
requires(std::is_unsigned<T>::value && sizeof(T) <= 8)
{
static_assert(sizeof(T) <= 8);
#if V8_HAS_BUILTIN_POPCOUNT
return sizeof(T) == 8 ? __builtin_popcountll(static_cast<uint64_t>(value))
: __builtin_popcount(static_cast<uint32_t>(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 64 buckets of 1 bits, holding values from [0,1].
value = ((value >> 1) & mask[0]) + (value & mask[0]);
// Having 32 buckets of 2 bits, holding values from [0,2] now.
value = ((value >> 2) & mask[1]) + (value & mask[1]);
// 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 2 buckets of 32 bits, holding values from [0,32] now.
if (sizeof(T) > 4) value = (value >> (sizeof(T) > 4 ? 32 : 0)) + value;
// Having 1 buckets of 64 bits, holding values from [0,64] now.
return static_cast<unsigned>(value & 0xff);
#endif
}
// ReverseBits(value) returns |value| in reverse bit order.
template <typename T>
T ReverseBits(T value) {
static_assert((sizeof(value) == 1) || (sizeof(value) == 2) ||
(sizeof(value) == 4) || (sizeof(value) == 8));
T result = 0;
for (unsigned i = 0; i < (sizeof(value) * 8); i++) {
result = (result << 1) | (value & 1);
value >>= 1;
}
return result;
}
// ReverseBytes(value) returns |value| in reverse byte order.
template <typename T>
T ReverseBytes(T value) {
static_assert((sizeof(value) == 1) || (sizeof(value) == 2) ||
(sizeof(value) == 4) || (sizeof(value) == 8));
T result = 0;
for (unsigned i = 0; i < sizeof(value); i++) {
result = (result << 8) | (value & 0xff);
value >>= 8;
}
return result;
}
template <class T>
inline constexpr std::make_unsigned_t<T> Unsigned(T value) {
static_assert(std::is_signed_v<T>);
return static_cast<std::make_unsigned_t<T>>(value);
}
template <class T>
inline constexpr std::make_signed_t<T> Signed(T value) {
static_assert(std::is_unsigned_v<T>);
return static_cast<std::make_signed_t<T>>(value);
}
// CountLeadingZeros(value) returns the number of zero bits following the most
// significant 1 bit in |value| if |value| is non-zero, otherwise it returns
// {sizeof(T) * 8}.
template <typename T, unsigned bits = sizeof(T) * 8>
inline constexpr unsigned CountLeadingZeros(T value)
requires(std::is_unsigned<T>::value && sizeof(T) <= 8)
{
static_assert(bits > 0, "invalid instantiation");
#if V8_HAS_BUILTIN_CLZ
return value == 0
? bits
: bits == 64
? __builtin_clzll(static_cast<uint64_t>(value))
: __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits);
#else
// Binary search algorithm taken from "Hacker's Delight" (by Henry S. Warren,
// Jr.), figures 5-11 and 5-12.
if (bits == 1) return static_cast<unsigned>(value) ^ 1;
T upper_half = value >> (bits / 2);
T next_value = upper_half != 0 ? upper_half : value;
unsigned add = upper_half != 0 ? 0 : bits / 2;
constexpr unsigned next_bits = bits == 1 ? 1 : bits / 2;
return CountLeadingZeros<T, next_bits>(next_value) + add;
#endif
}
inline constexpr unsigned CountLeadingZeros32(uint32_t value) {
return CountLeadingZeros(value);
}
inline constexpr unsigned CountLeadingZeros64(uint64_t value) {
return CountLeadingZeros(value);
}
// The number of leading zeros for a positive number,
// the number of leading ones for a negative number.
template <class T>
constexpr unsigned CountLeadingSignBits(T value) {
static_assert(std::is_signed_v<T>);
return value < 0 ? CountLeadingZeros(~Unsigned(value))
: CountLeadingZeros(Unsigned(value));
}
// CountTrailingZeros(value) returns the number of zero bits preceding the
// least significant 1 bit in |value| if |value| is non-zero, otherwise it
// returns {sizeof(T) * 8}.
// See CountTrailingZerosNonZero for an optimized version for the case that
// |value| is guaranteed to be non-zero.
template <typename T, unsigned bits = sizeof(T) * 8>
inline constexpr unsigned CountTrailingZeros(T value)
requires(std::is_integral<T>::value && sizeof(T) <= 8)
{
#if V8_HAS_BUILTIN_CTZ
return value == 0 ? bits
: bits == 64 ? __builtin_ctzll(static_cast<uint64_t>(value))
: __builtin_ctz(static_cast<uint32_t>(value));
#else
// Fall back to popcount (see "Hacker's Delight" by Henry S. Warren, Jr.),
// chapter 5-4. On x64, since is faster than counting in a loop and faster
// than doing binary search.
using U = typename std::make_unsigned<T>::type;
U u = value;
return CountPopulation(static_cast<U>(~u & (u - 1u)));
#endif
}
inline constexpr unsigned CountTrailingZeros32(uint32_t value) {
return CountTrailingZeros(value);
}
inline constexpr unsigned CountTrailingZeros64(uint64_t value) {
return CountTrailingZeros(value);
}
// CountTrailingZerosNonZero(value) returns the number of zero bits preceding
// the least significant 1 bit in |value| if |value| is non-zero, otherwise the
// behavior is undefined.
// See CountTrailingZeros for an alternative version that allows |value| == 0.
template <typename T, unsigned bits = sizeof(T) * 8>
inline constexpr unsigned CountTrailingZerosNonZero(T value)
requires(std::is_integral<T>::value && sizeof(T) <= 8)
{
DCHECK_NE(0, value);
#if V8_HAS_BUILTIN_CTZ
return bits == 64 ? __builtin_ctzll(static_cast<uint64_t>(value))
: __builtin_ctz(static_cast<uint32_t>(value));
#else
return CountTrailingZeros<T, bits>(value);
#endif
}
// Returns true iff |value| is a power of 2.
template <typename T>
constexpr inline bool IsPowerOfTwo(T value)
requires(std::is_integral<T>::value || std::is_enum<T>::value)
{
return value > 0 && (value & (value - 1)) == 0;
}
// Identical to {CountTrailingZeros}, but only works for powers of 2.
template <typename T>
inline constexpr int WhichPowerOfTwo(T value)
requires std::is_integral<T>::value
{
DCHECK(IsPowerOfTwo(value));
#if V8_HAS_BUILTIN_CTZ
static_assert(sizeof(T) <= 8);
return sizeof(T) == 8 ? __builtin_ctzll(static_cast<uint64_t>(value))
: __builtin_ctz(static_cast<uint32_t>(value));
#else
// Fall back to popcount (see "Hacker's Delight" by Henry S. Warren, Jr.),
// chapter 5-4. On x64, since is faster than counting in a loop and faster
// than doing binary search.
using U = typename std::make_unsigned<T>::type;
U u = value;
return CountPopulation(static_cast<U>(u - 1));
#endif
}
// RoundUpToPowerOfTwo32(value) returns the smallest power of two which is
// greater than or equal to |value|. If you pass in a |value| that is already a
// power of two, it is returned as is. |value| must be less than or equal to
// 0x80000000u. Uses computation based on leading zeros if we have compiler
// support for that. Falls back to the implementation from "Hacker's Delight" by
// Henry S. Warren, Jr., figure 3-3, page 48, where the function is called clp2.
V8_BASE_EXPORT constexpr uint32_t RoundUpToPowerOfTwo32(uint32_t value) {
DCHECK_LE(value, uint32_t{1} << 31);
if (value) --value;
// Use computation based on leading zeros if we have compiler support for that.
#if V8_HAS_BUILTIN_CLZ || V8_CC_MSVC
return 1u << (32 - CountLeadingZeros(value));
#else
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
return value + 1;
#endif
}
// Same for 64 bit integers. |value| must be <= 2^63
V8_BASE_EXPORT constexpr uint64_t RoundUpToPowerOfTwo64(uint64_t value) {
DCHECK_LE(value, uint64_t{1} << 63);
if (value) --value;
// Use computation based on leading zeros if we have compiler support for that.
#if V8_HAS_BUILTIN_CLZ
return uint64_t{1} << (64 - CountLeadingZeros(value));
#else
value |= value >> 1;
value |= value >> 2;
value |= value >> 4;
value |= value >> 8;
value |= value >> 16;
value |= value >> 32;
return value + 1;
#endif
}
// Same for size_t integers.
inline constexpr size_t RoundUpToPowerOfTwo(size_t value) {
if (sizeof(size_t) == sizeof(uint64_t)) {
return RoundUpToPowerOfTwo64(value);
} else {
// Without windows.h included this line triggers a truncation warning on
// 64-bit builds. Presumably windows.h disables the relevant warning.
return RoundUpToPowerOfTwo32(static_cast<uint32_t>(value));
}
}
// RoundDownToPowerOfTwo32(value) returns the greatest power of two which is
// less than or equal to |value|. If you pass in a |value| that is already a
// power of two, it is returned as is.
inline uint32_t RoundDownToPowerOfTwo32(uint32_t value) {
if (value > 0x80000000u) return 0x80000000u;
uint32_t result = RoundUpToPowerOfTwo32(value);
if (result > value) result >>= 1;
return result;
}
// Precondition: 0 <= shift < 32
inline constexpr uint32_t RotateRight32(uint32_t value, uint32_t shift) {
return (value >> shift) | (value << ((32 - shift) & 31));
}
// Precondition: 0 <= shift < 32
inline constexpr uint32_t RotateLeft32(uint32_t value, uint32_t shift) {
return (value << shift) | (value >> ((32 - shift) & 31));
}
// Precondition: 0 <= shift < 64
inline constexpr uint64_t RotateRight64(uint64_t value, uint64_t shift) {
return (value >> shift) | (value << ((64 - shift) & 63));
}
// Precondition: 0 <= shift < 64
inline constexpr uint64_t RotateLeft64(uint64_t value, uint64_t shift) {
return (value << shift) | (value >> ((64 - shift) & 63));
}
// SignedAddOverflow32(lhs,rhs,val) performs a signed summation of |lhs| and
// |rhs| and stores the result into the variable pointed to by |val| and
// returns true if the signed summation resulted in an overflow.
inline bool SignedAddOverflow32(int32_t lhs, int32_t rhs, int32_t* val) {
#if V8_HAS_BUILTIN_SADD_OVERFLOW
return __builtin_sadd_overflow(lhs, rhs, val);
#else
uint32_t res = static_cast<uint32_t>(lhs) + static_cast<uint32_t>(rhs);
*val = base::bit_cast<int32_t>(res);
return ((res ^ lhs) & (res ^ rhs) & (1U << 31)) != 0;
#endif
}
// SignedSubOverflow32(lhs,rhs,val) performs a signed subtraction of |lhs| and
// |rhs| and stores the result into the variable pointed to by |val| and
// returns true if the signed subtraction resulted in an overflow.
inline bool SignedSubOverflow32(int32_t lhs, int32_t rhs, int32_t* val) {
#if V8_HAS_BUILTIN_SSUB_OVERFLOW
return __builtin_ssub_overflow(lhs, rhs, val);
#else
uint32_t res = static_cast<uint32_t>(lhs) - static_cast<uint32_t>(rhs);
*val = base::bit_cast<int32_t>(res);
return ((res ^ lhs) & (res ^ ~rhs) & (1U << 31)) != 0;
#endif
}
// SignedMulOverflow32(lhs,rhs,val) performs a signed multiplication of |lhs|
// and |rhs| and stores the result into the variable pointed to by |val| and
// returns true if the signed multiplication resulted in an overflow.
inline bool SignedMulOverflow32(int32_t lhs, int32_t rhs, int32_t* val) {
#if V8_HAS_BUILTIN_SMUL_OVERFLOW
return __builtin_smul_overflow(lhs, rhs, val);
#else
// Compute the result as {int64_t}, then check for overflow.
int64_t result = int64_t{lhs} * int64_t{rhs};
*val = static_cast<int32_t>(result);
using limits = std::numeric_limits<int32_t>;
return result < limits::min() || result > limits::max();
#endif
}
// SignedAddOverflow64(lhs,rhs,val) performs a signed summation of |lhs| and
// |rhs| and stores the result into the variable pointed to by |val| and
// returns true if the signed summation resulted in an overflow.
inline bool SignedAddOverflow64(int64_t lhs, int64_t rhs, int64_t* val) {
#if V8_HAS_BUILTIN_ADD_OVERFLOW
return __builtin_add_overflow(lhs, rhs, val);
#else
uint64_t res = static_cast<uint64_t>(lhs) + static_cast<uint64_t>(rhs);
*val = base::bit_cast<int64_t>(res);
return ((res ^ lhs) & (res ^ rhs) & (1ULL << 63)) != 0;
#endif
}
// SignedSubOverflow64(lhs,rhs,val) performs a signed subtraction of |lhs| and
// |rhs| and stores the result into the variable pointed to by |val| and
// returns true if the signed subtraction resulted in an overflow.
inline bool SignedSubOverflow64(int64_t lhs, int64_t rhs, int64_t* val) {
#if V8_HAS_BUILTIN_SUB_OVERFLOW
return __builtin_sub_overflow(lhs, rhs, val);
#else
uint64_t res = static_cast<uint64_t>(lhs) - static_cast<uint64_t>(rhs);
*val = base::bit_cast<int64_t>(res);
return ((res ^ lhs) & (res ^ ~rhs) & (1ULL << 63)) != 0;
#endif
}
// SignedMulOverflow64(lhs,rhs,val) performs a signed multiplication of |lhs|
// and |rhs| and stores the result into the variable pointed to by |val| and
// returns true if the signed multiplication resulted in an overflow.
inline bool SignedMulOverflow64(int64_t lhs, int64_t rhs, int64_t* val) {
#if V8_HAS_BUILTIN_MUL_OVERFLOW
return __builtin_mul_overflow(lhs, rhs, val);
#else
int64_t res = base::bit_cast<int64_t>(static_cast<uint64_t>(lhs) *
static_cast<uint64_t>(rhs));
*val = res;
// Check for INT64_MIN / -1 as it's undefined behaviour and could cause
// hardware exceptions.
if ((res == INT64_MIN && lhs == -1)) {
return true;
}
return lhs != 0 && (res / lhs) != rhs;
#endif
}
// SignedMulHigh32(lhs, rhs) multiplies two signed 32-bit values |lhs| and
// |rhs|, extracts the most significant 32 bits of the result, and returns
// those.
V8_BASE_EXPORT int32_t SignedMulHigh32(int32_t lhs, int32_t rhs);
// UnsignedMulHigh32(lhs, rhs) multiplies two unsigned 32-bit values |lhs| and
// |rhs|, extracts the most significant 32 bits of the result, and returns
// those.
V8_BASE_EXPORT uint32_t UnsignedMulHigh32(uint32_t lhs, uint32_t rhs);
// SignedMulHigh64(lhs, rhs) multiplies two signed 64-bit values |lhs| and
// |rhs|, extracts the most significant 64 bits of the result, and returns
// those.
V8_BASE_EXPORT int64_t SignedMulHigh64(int64_t lhs, int64_t rhs);
// UnsignedMulHigh64(lhs, rhs) multiplies two unsigned 64-bit values |lhs| and
// |rhs|, extracts the most significant 64 bits of the result, and returns
// those.
V8_BASE_EXPORT uint64_t UnsignedMulHigh64(uint64_t lhs, uint64_t rhs);
// SignedMulHighAndAdd32(lhs, rhs, acc) multiplies two signed 32-bit values
// |lhs| and |rhs|, extracts the most significant 32 bits of the result, and
// adds the accumulate value |acc|.
V8_BASE_EXPORT int32_t SignedMulHighAndAdd32(int32_t lhs, int32_t rhs,
int32_t acc);
// SignedDiv32(lhs, rhs) divides |lhs| by |rhs| and returns the quotient
// truncated to int32. If |rhs| is zero, then zero is returned. If |lhs|
// is minint and |rhs| is -1, it returns minint.
V8_BASE_EXPORT int32_t SignedDiv32(int32_t lhs, int32_t rhs);
// SignedDiv64(lhs, rhs) divides |lhs| by |rhs| and returns the quotient
// truncated to int64. If |rhs| is zero, then zero is returned. If |lhs|
// is minint and |rhs| is -1, it returns minint.
V8_BASE_EXPORT int64_t SignedDiv64(int64_t lhs, int64_t rhs);
// SignedMod32(lhs, rhs) divides |lhs| by |rhs| and returns the remainder
// truncated to int32. If either |rhs| is zero or |lhs| is minint and |rhs|
// is -1, it returns zero.
V8_BASE_EXPORT int32_t SignedMod32(int32_t lhs, int32_t rhs);
// SignedMod64(lhs, rhs) divides |lhs| by |rhs| and returns the remainder
// truncated to int64. If either |rhs| is zero or |lhs| is minint and |rhs|
// is -1, it returns zero.
V8_BASE_EXPORT int64_t SignedMod64(int64_t lhs, int64_t rhs);
// UnsignedAddOverflow32(lhs,rhs,val) performs an unsigned summation of |lhs|
// and |rhs| and stores the result into the variable pointed to by |val| and
// returns true if the unsigned summation resulted in an overflow.
inline bool UnsignedAddOverflow32(uint32_t lhs, uint32_t rhs, uint32_t* val) {
#if V8_HAS_BUILTIN_SADD_OVERFLOW
return __builtin_uadd_overflow(lhs, rhs, val);
#else
*val = lhs + rhs;
return *val < (lhs | rhs);
#endif
}
// UnsignedDiv32(lhs, rhs) divides |lhs| by |rhs| and returns the quotient
// truncated to uint32. If |rhs| is zero, then zero is returned.
inline uint32_t UnsignedDiv32(uint32_t lhs, uint32_t rhs) {
return rhs ? lhs / rhs : 0u;
}
// UnsignedDiv64(lhs, rhs) divides |lhs| by |rhs| and returns the quotient
// truncated to uint64. If |rhs| is zero, then zero is returned.
inline uint64_t UnsignedDiv64(uint64_t lhs, uint64_t rhs) {
return rhs ? lhs / rhs : 0u;
}
// UnsignedMod32(lhs, rhs) divides |lhs| by |rhs| and returns the remainder
// truncated to uint32. If |rhs| is zero, then zero is returned.
inline uint32_t UnsignedMod32(uint32_t lhs, uint32_t rhs) {
return rhs ? lhs % rhs : 0u;
}
// UnsignedMod64(lhs, rhs) divides |lhs| by |rhs| and returns the remainder
// truncated to uint64. If |rhs| is zero, then zero is returned.
inline uint64_t UnsignedMod64(uint64_t lhs, uint64_t rhs) {
return rhs ? lhs % rhs : 0u;
}
// Wraparound integer arithmetic without undefined behavior.
inline int32_t WraparoundAdd32(int32_t lhs, int32_t rhs) {
return static_cast<int32_t>(static_cast<uint32_t>(lhs) +
static_cast<uint32_t>(rhs));
}
inline int32_t WraparoundNeg32(int32_t x) {
return static_cast<int32_t>(-static_cast<uint32_t>(x));
}
// SignedSaturatedAdd64(lhs, rhs) adds |lhs| and |rhs|,
// checks and returns the result.
V8_BASE_EXPORT int64_t SignedSaturatedAdd64(int64_t lhs, int64_t rhs);
// SignedSaturatedSub64(lhs, rhs) subtracts |lhs| by |rhs|,
// checks and returns the result.
V8_BASE_EXPORT int64_t SignedSaturatedSub64(int64_t lhs, int64_t rhs);
template <class T>
V8_BASE_EXPORT constexpr int BitWidth(T x) {
return std::numeric_limits<T>::digits - CountLeadingZeros(x);
}
} // namespace bits
} // namespace base
} // namespace v8
#endif // V8_BASE_BITS_H_

View File

@ -0,0 +1,277 @@
// Copyright 2018 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.
#include "src/base/bounded-page-allocator.h"
namespace v8 {
namespace base {
BoundedPageAllocator::BoundedPageAllocator(
v8::PageAllocator* page_allocator, Address start, size_t size,
size_t allocate_page_size, PageInitializationMode page_initialization_mode,
PageFreeingMode page_freeing_mode)
: allocate_page_size_(allocate_page_size),
commit_page_size_(page_allocator->CommitPageSize()),
page_allocator_(page_allocator),
region_allocator_(start, size, allocate_page_size_),
page_initialization_mode_(page_initialization_mode),
page_freeing_mode_(page_freeing_mode) {
DCHECK_NOT_NULL(page_allocator);
DCHECK(IsAligned(allocate_page_size, page_allocator->AllocatePageSize()));
DCHECK(IsAligned(allocate_page_size_, commit_page_size_));
}
BoundedPageAllocator::Address BoundedPageAllocator::begin() const {
return region_allocator_.begin();
}
size_t BoundedPageAllocator::size() const { return region_allocator_.size(); }
void* BoundedPageAllocator::AllocatePages(void* hint, size_t size,
size_t alignment,
PageAllocator::Permission access) {
MutexGuard guard(&mutex_);
DCHECK(IsAligned(alignment, region_allocator_.page_size()));
DCHECK(IsAligned(alignment, allocate_page_size_));
Address address = RegionAllocator::kAllocationFailure;
Address hint_address = reinterpret_cast<Address>(hint);
if (hint_address && IsAligned(hint_address, alignment) &&
region_allocator_.contains(hint_address, size)) {
if (region_allocator_.AllocateRegionAt(hint_address, size)) {
address = hint_address;
}
}
if (address == RegionAllocator::kAllocationFailure) {
if (alignment <= allocate_page_size_) {
// TODO(ishell): Consider using randomized version here.
address = region_allocator_.AllocateRegion(size);
} else {
address = region_allocator_.AllocateAlignedRegion(size, alignment);
}
}
if (address == RegionAllocator::kAllocationFailure) {
allocation_status_ = AllocationStatus::kRanOutOfReservation;
return nullptr;
}
void* ptr = reinterpret_cast<void*>(address);
// It's assumed that free regions are in kNoAccess/kNoAccessWillJitLater
// state.
if (access == PageAllocator::kNoAccess ||
access == PageAllocator::kNoAccessWillJitLater) {
allocation_status_ = AllocationStatus::kSuccess;
return ptr;
}
if (page_initialization_mode_ == PageInitializationMode::kRecommitOnly) {
if (page_allocator_->RecommitPages(ptr, size, access)) {
allocation_status_ = AllocationStatus::kSuccess;
return ptr;
}
} else {
if (page_allocator_->SetPermissions(ptr, size, access)) {
allocation_status_ = AllocationStatus::kSuccess;
return ptr;
}
}
// This most likely means that we ran out of memory.
CHECK_EQ(region_allocator_.FreeRegion(address), size);
allocation_status_ = AllocationStatus::kFailedToCommit;
return nullptr;
}
bool BoundedPageAllocator::AllocatePagesAt(Address address, size_t size,
PageAllocator::Permission access) {
MutexGuard guard(&mutex_);
DCHECK(IsAligned(address, allocate_page_size_));
DCHECK(IsAligned(size, allocate_page_size_));
DCHECK(region_allocator_.contains(address, size));
if (!region_allocator_.AllocateRegionAt(address, size)) {
allocation_status_ = AllocationStatus::kHintedAddressTakenOrNotFound;
return false;
}
void* ptr = reinterpret_cast<void*>(address);
if (!page_allocator_->SetPermissions(ptr, size, access)) {
// This most likely means that we ran out of memory.
CHECK_EQ(region_allocator_.FreeRegion(address), size);
allocation_status_ = AllocationStatus::kFailedToCommit;
return false;
}
allocation_status_ = AllocationStatus::kSuccess;
return true;
}
bool BoundedPageAllocator::ReserveForSharedMemoryMapping(void* ptr,
size_t size) {
MutexGuard guard(&mutex_);
Address address = reinterpret_cast<Address>(ptr);
DCHECK(IsAligned(address, allocate_page_size_));
DCHECK(IsAligned(size, commit_page_size_));
DCHECK(region_allocator_.contains(address, size));
// Region allocator requires page size rather than commit size so just over-
// allocate there since any extra space couldn't be used anyway.
size_t region_size = RoundUp(size, allocate_page_size_);
if (!region_allocator_.AllocateRegionAt(
address, region_size, RegionAllocator::RegionState::kExcluded)) {
allocation_status_ = AllocationStatus::kHintedAddressTakenOrNotFound;
return false;
}
const bool success = page_allocator_->SetPermissions(
ptr, size, PageAllocator::Permission::kNoAccess);
if (success) {
allocation_status_ = AllocationStatus::kSuccess;
} else {
allocation_status_ = AllocationStatus::kFailedToCommit;
}
return success;
}
bool BoundedPageAllocator::FreePages(void* raw_address, size_t size) {
// Careful: we are not locked here, do not touch BoundedPageAllocator
// metadata.
bool success;
Address address = reinterpret_cast<Address>(raw_address);
// The operations below can be expensive, don't hold the lock while they
// happen. There is still potentially contention in the kernel, but at least
// we don't need to hold the V8-side lock.
if (page_initialization_mode_ ==
PageInitializationMode::kAllocatedPagesMustBeZeroInitialized) {
DCHECK_NE(page_freeing_mode_, PageFreeingMode::kDiscard);
// When we are required to return zero-initialized pages, we decommit the
// pages here, which will cause any wired pages to be removed by the OS.
success = page_allocator_->DecommitPages(raw_address, size);
} else {
switch (page_freeing_mode_) {
case PageFreeingMode::kMakeInaccessible:
DCHECK_EQ(page_initialization_mode_,
PageInitializationMode::kAllocatedPagesCanBeUninitialized);
success = page_allocator_->SetPermissions(raw_address, size,
PageAllocator::kNoAccess);
break;
case PageFreeingMode::kDiscard:
success = page_allocator_->DiscardSystemPages(raw_address, size);
break;
}
}
MutexGuard guard(&mutex_);
CHECK_EQ(size, region_allocator_.FreeRegion(address));
return success;
}
bool BoundedPageAllocator::ReleasePages(void* raw_address, size_t size,
size_t new_size) {
Address address = reinterpret_cast<Address>(raw_address);
DCHECK(IsAligned(address, allocate_page_size_));
DCHECK_LT(new_size, size);
DCHECK(IsAligned(size - new_size, commit_page_size_));
// This must be held until the page permissions are updated.
MutexGuard guard(&mutex_);
// Check if we freed any allocatable pages by this release.
size_t allocated_size = RoundUp(size, allocate_page_size_);
size_t new_allocated_size = RoundUp(new_size, allocate_page_size_);
#ifdef DEBUG
{
// There must be an allocated region at given |address| of a size not
// smaller than |size|.
DCHECK_EQ(allocated_size, region_allocator_.CheckRegion(address));
}
#endif
if (new_allocated_size < allocated_size) {
region_allocator_.TrimRegion(address, new_allocated_size);
}
// Keep the region in "used" state just uncommit some pages.
void* free_address = reinterpret_cast<void*>(address + new_size);
size_t free_size = size - new_size;
if (page_initialization_mode_ ==
PageInitializationMode::kAllocatedPagesMustBeZeroInitialized) {
DCHECK_NE(page_freeing_mode_, PageFreeingMode::kDiscard);
// See comment in FreePages().
return (page_allocator_->DecommitPages(free_address, free_size));
}
if (page_freeing_mode_ == PageFreeingMode::kMakeInaccessible) {
DCHECK_EQ(page_initialization_mode_,
PageInitializationMode::kAllocatedPagesCanBeUninitialized);
return page_allocator_->SetPermissions(free_address, free_size,
PageAllocator::kNoAccess);
}
CHECK_EQ(page_freeing_mode_, PageFreeingMode::kDiscard);
return page_allocator_->DiscardSystemPages(free_address, free_size);
}
bool BoundedPageAllocator::SetPermissions(void* address, size_t size,
PageAllocator::Permission access) {
DCHECK(IsAligned(reinterpret_cast<Address>(address), commit_page_size_));
DCHECK(IsAligned(size, commit_page_size_));
DCHECK(region_allocator_.contains(reinterpret_cast<Address>(address), size));
const bool success = page_allocator_->SetPermissions(address, size, access);
if (!success) {
allocation_status_ = AllocationStatus::kFailedToCommit;
}
return success;
}
bool BoundedPageAllocator::RecommitPages(void* address, size_t size,
PageAllocator::Permission access) {
DCHECK(IsAligned(reinterpret_cast<Address>(address), commit_page_size_));
DCHECK(IsAligned(size, commit_page_size_));
DCHECK(region_allocator_.contains(reinterpret_cast<Address>(address), size));
const bool success = page_allocator_->RecommitPages(address, size, access);
if (!success) {
allocation_status_ = AllocationStatus::kFailedToCommit;
}
return success;
}
bool BoundedPageAllocator::DiscardSystemPages(void* address, size_t size) {
return page_allocator_->DiscardSystemPages(address, size);
}
bool BoundedPageAllocator::DecommitPages(void* address, size_t size) {
return page_allocator_->DecommitPages(address, size);
}
bool BoundedPageAllocator::SealPages(void* address, size_t size) {
return page_allocator_->SealPages(address, size);
}
const char* BoundedPageAllocator::AllocationStatusToString(
AllocationStatus allocation_status) {
switch (allocation_status) {
case AllocationStatus::kSuccess:
return "Success";
case AllocationStatus::kFailedToCommit:
return "Failed to commit";
case AllocationStatus::kRanOutOfReservation:
return "Ran out of reservation";
case AllocationStatus::kHintedAddressTakenOrNotFound:
return "Hinted address was taken or not found";
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,142 @@
// Copyright 2018 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_BOUNDED_PAGE_ALLOCATOR_H_
#define V8_BASE_BOUNDED_PAGE_ALLOCATOR_H_
#include "include/v8-platform.h"
#include "src/base/platform/mutex.h"
#include "src/base/region-allocator.h"
namespace v8 {
namespace base {
// Defines the page initialization mode of a BoundedPageAllocator.
enum class PageInitializationMode {
// The contents of allocated pages must be zero initialized. This causes any
// committed pages to be decommitted during FreePages and ReleasePages.
kAllocatedPagesMustBeZeroInitialized,
// Allocated pages do not have to be be zero initialized and can contain old
// data. This is slightly faster as comitted pages are not decommitted
// during FreePages and ReleasePages, but only made inaccessible.
kAllocatedPagesCanBeUninitialized,
// Assume pages are in discarded state and already have the right page
// permissions. Using this mode requires PageFreeingMode::kDiscard.
kRecommitOnly,
};
// Defines how BoundedPageAllocator frees pages when FreePages or ReleasePages
// is requested.
enum class PageFreeingMode {
// Pages are freed/released by setting permissions to kNoAccess. This is the
// preferred mode when current platform/configuration allows any page
// permissions reconfiguration.
kMakeInaccessible,
// Pages are freed/released by using DiscardSystemPages of the underlying
// page allocator. This mode should be used for the cases when page permission
// reconfiguration is not allowed. In particular, on MacOS on ARM64 ("Apple
// M1"/Apple Silicon) it's not allowed to reconfigure RWX pages to anything
// else.
// This mode is not compatible with kAllocatedPagesMustBeZeroInitialized
// page initialization mode.
kDiscard,
};
// This is a v8::PageAllocator implementation that allocates pages within the
// pre-reserved region of virtual space. This class requires the virtual space
// to be kept reserved during the lifetime of this object.
// The main application of bounded page allocator are
// - V8 heap pointer compression which requires the whole V8 heap to be
// allocated within a contiguous range of virtual address space,
// - executable page allocation, which allows to use PC-relative 32-bit code
// displacement on certain 64-bit platforms.
// Bounded page allocator uses other page allocator instance for doing actual
// page allocations.
// The implementation is thread-safe.
class V8_BASE_EXPORT BoundedPageAllocator : public v8::PageAllocator {
public:
enum class AllocationStatus {
kSuccess,
kFailedToCommit,
kRanOutOfReservation,
kHintedAddressTakenOrNotFound,
};
using Address = uintptr_t;
static const char* AllocationStatusToString(AllocationStatus);
BoundedPageAllocator(v8::PageAllocator* page_allocator, Address start,
size_t size, size_t allocate_page_size,
PageInitializationMode page_initialization_mode,
PageFreeingMode page_freeing_mode);
BoundedPageAllocator(const BoundedPageAllocator&) = delete;
BoundedPageAllocator& operator=(const BoundedPageAllocator&) = delete;
~BoundedPageAllocator() override = default;
// These functions are not inlined to avoid https://crbug.com/v8/8275.
Address begin() const;
size_t size() const;
// Returns true if given address is in the range controlled by the bounded
// page allocator instance.
bool contains(Address address) const {
return region_allocator_.contains(address);
}
size_t AllocatePageSize() override { return allocate_page_size_; }
size_t CommitPageSize() override { return commit_page_size_; }
void SetRandomMmapSeed(int64_t seed) override {
page_allocator_->SetRandomMmapSeed(seed);
}
void* GetRandomMmapAddr() override {
return page_allocator_->GetRandomMmapAddr();
}
void* AllocatePages(void* hint, size_t size, size_t alignment,
Permission access) override;
bool ReserveForSharedMemoryMapping(void* address, size_t size) override;
// Allocates pages at given address, returns true on success.
bool AllocatePagesAt(Address address, size_t size, Permission access);
bool FreePages(void* address, size_t size) override;
bool ReleasePages(void* address, size_t size, size_t new_size) override;
bool SetPermissions(void* address, size_t size, Permission access) override;
bool RecommitPages(void* address, size_t size,
PageAllocator::Permission access) override;
bool DiscardSystemPages(void* address, size_t size) override;
bool DecommitPages(void* address, size_t size) override;
bool SealPages(void* address, size_t size) override;
AllocationStatus get_last_allocation_status() const {
return allocation_status_;
}
private:
v8::base::Mutex mutex_;
const size_t allocate_page_size_;
const size_t commit_page_size_;
v8::PageAllocator* const page_allocator_;
v8::base::RegionAllocator region_allocator_;
const PageInitializationMode page_initialization_mode_;
const PageFreeingMode page_freeing_mode_;
AllocationStatus allocation_status_ = AllocationStatus::kSuccess;
};
} // namespace base
} // namespace v8
#endif // V8_BASE_BOUNDED_PAGE_ALLOCATOR_H_

73
deps/v8/src/base/bounds.h vendored Normal file
View File

@ -0,0 +1,73 @@
// Copyright 2019 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_BOUNDS_H_
#define V8_BASE_BOUNDS_H_
#include "include/v8config.h"
#include "src/base/macros.h"
namespace v8 {
namespace base {
// Checks if value is in range [lower_limit, higher_limit] using a single
// branch.
template <typename T, typename U>
requires((std::is_integral_v<T> || std::is_enum_v<T>) &&
(std::is_integral_v<U> || std::is_enum_v<U>)) &&
(sizeof(U) <= sizeof(T))
inline constexpr bool IsInRange(T value, U lower_limit, U higher_limit) {
DCHECK_LE(lower_limit, higher_limit);
using unsigned_T = typename std::make_unsigned<T>::type;
// Use static_cast to support enum classes.
return static_cast<unsigned_T>(static_cast<unsigned_T>(value) -
static_cast<unsigned_T>(lower_limit)) <=
static_cast<unsigned_T>(static_cast<unsigned_T>(higher_limit) -
static_cast<unsigned_T>(lower_limit));
}
// Like IsInRange but for the half-open range [lower_limit, higher_limit).
template <typename T, typename U>
requires((std::is_integral_v<T> || std::is_enum_v<T>) &&
(std::is_integral_v<U> || std::is_enum_v<U>)) &&
(sizeof(U) <= sizeof(T))
inline constexpr bool IsInHalfOpenRange(T value, U lower_limit,
U higher_limit) {
DCHECK_LE(lower_limit, higher_limit);
using unsigned_T = typename std::make_unsigned<T>::type;
// Use static_cast to support enum classes.
return static_cast<unsigned_T>(static_cast<unsigned_T>(value) -
static_cast<unsigned_T>(lower_limit)) <
static_cast<unsigned_T>(static_cast<unsigned_T>(higher_limit) -
static_cast<unsigned_T>(lower_limit));
}
// Checks if [index, index+length) is in range [0, max). Note that this check
// works even if {index+length} would wrap around.
template <typename T>
inline constexpr bool IsInBounds(T index, T length, T max)
requires std::is_unsigned<T>::value
{
return length <= max && index <= (max - length);
}
// Checks if [index, index+length) is in range [0, max). If not, {length} is
// clamped to its valid range. Note that this check works even if
// {index+length} would wrap around.
template <typename T>
inline bool ClampToBounds(T index, T* length, T max) {
if (index > max) {
*length = 0;
return false;
}
T avail = max - index;
bool oob = *length > avail;
if (oob) *length = avail;
return !oob;
}
} // namespace base
} // namespace v8
#endif // V8_BASE_BOUNDS_H_

103
deps/v8/src/base/build_config.h vendored Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2014 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_BUILD_CONFIG_H_
#define V8_BASE_BUILD_CONFIG_H_
#include "include/v8config.h"
#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \
defined(__ARM_ARCH_7__)
#define CAN_USE_ARMV7_INSTRUCTIONS 1
#ifdef __ARM_ARCH_EXT_IDIV__
#define CAN_USE_SUDIV 1
#endif
#ifndef CAN_USE_VFP3_INSTRUCTIONS
#define CAN_USE_VFP3_INSTRUCTIONS 1
#endif
#endif
#if defined(__ARM_ARCH_8A__)
#define CAN_USE_ARMV7_INSTRUCTIONS 1
#define CAN_USE_SUDIV 1
#define CAN_USE_ARMV8_INSTRUCTIONS 1
#ifndef CAN_USE_VFP3_INSTRUCTIONS
#define CAN_USE_VFP3_INSTRUCTIONS 1
#endif
#endif
// pthread_jit_write_protect is only available on arm64 Mac.
#if defined(V8_HOST_ARCH_ARM64) && defined(V8_OS_MACOS)
#define V8_HAS_PTHREAD_JIT_WRITE_PROTECT 1
#else
#define V8_HAS_PTHREAD_JIT_WRITE_PROTECT 0
#endif
// BrowserEngineCore JIT write protect is only available on iOS 17.4 and later.
#if defined(V8_HOST_ARCH_ARM64) && defined(V8_OS_IOS) && \
defined(__IPHONE_17_4) && \
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_17_4
#define V8_HAS_BECORE_JIT_WRITE_PROTECT 1
#else
#define V8_HAS_BECORE_JIT_WRITE_PROTECT 0
#endif
#if defined(V8_OS_LINUX) && defined(V8_HOST_ARCH_X64)
#define V8_HAS_PKU_JIT_WRITE_PROTECT 1
#else
#define V8_HAS_PKU_JIT_WRITE_PROTECT 0
#endif
#if defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_X64)
#define V8_TARGET_ARCH_STORES_RETURN_ADDRESS_ON_STACK true
#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_HOST_ARCH_PPC64) && !defined(V8_OS_AIX)
// Native PPC linux has large (64KB) physical pages.
// Simulator (and Aix) need to use the same value as x64.
constexpr int kPageSizeBits = 19;
#elif defined(ENABLE_HUGEPAGE)
// When enabling huge pages, adjust V8 page size to take up exactly one huge
// page. This avoids huge-page-internal fragmentation for unused address ranges.
constexpr int kHugePageBits = 21;
constexpr int kHugePageSize = 1 << kHugePageBits;
constexpr int kPageSizeBits = kHugePageBits;
#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.
constexpr int kPageSizeBits = 18;
#endif
constexpr int kRegularPageSize = 1 << kPageSizeBits;
// The minimal supported page size by the operation system. Any region aligned
// to that size needs to be individually protectable via
// {base::OS::SetPermission} and friends.
#if (defined(V8_OS_MACOS) && defined(V8_HOST_ARCH_ARM64)) || \
(defined(V8_OS_ANDROID) && \
(defined(V8_HOST_ARCH_ARM64) || defined(V8_HOST_ARCH_X64))) || \
defined(V8_HOST_ARCH_LOONG64) || defined(V8_HOST_ARCH_MIPS64) || \
defined(V8_OS_IOS)
// Android 64 bit has experimental support for 16kB pages.
// MacOS & iOS on arm64 uses 16kB pages.
// LOONG64 and MIPS64 also use 16kB pages.
constexpr int kMinimumOSPageSize = 16 * 1024;
#elif defined(V8_OS_LINUX) && !defined(V8_OS_ANDROID) && \
(defined(V8_HOST_ARCH_ARM64) || defined(V8_HOST_ARCH_PPC64))
// Linux on arm64 (excluding android) and PPC64 can be configured for up to 64kB
// pages.
constexpr int kMinimumOSPageSize = 64 * 1024;
#else
// Everything else uses 4kB pages.
constexpr int kMinimumOSPageSize = 4 * 1024;
#endif
#endif // V8_BASE_BUILD_CONFIG_H_

151
deps/v8/src/base/compiler-specific.h vendored Normal file
View File

@ -0,0 +1,151 @@
// Copyright 2014 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_COMPILER_SPECIFIC_H_
#define V8_BASE_COMPILER_SPECIFIC_H_
#include "include/v8config.h"
// Annotation to silence compiler warnings about unused
// types/functions/variables. Use like:
//
// using V8_ALLOW_UNUSED Bar = Foo;
// V8_ALLOW_UNUSED void foo() {}
#if V8_HAS_ATTRIBUTE_UNUSED
#define V8_ALLOW_UNUSED __attribute__((unused))
#else
#define V8_ALLOW_UNUSED
#endif
// Tell the compiler a function is using a printf-style format string.
// |format_param| is the one-based index of the format string parameter;
// |dots_param| is the one-based index of the "..." parameter.
// For v*printf functions (which take a va_list), pass 0 for dots_param.
// (This is undocumented but matches what the system C headers do.)
#if defined(__GNUC__)
#define PRINTF_FORMAT(format_param, dots_param) \
__attribute__((format(printf, format_param, dots_param)))
#else
#define PRINTF_FORMAT(format_param, dots_param)
#endif
// The C++ standard requires that static const members have an out-of-class
// definition (in a single compilation unit), but MSVC chokes on this (when
// language extensions, which are required, are enabled). (You're only likely to
// notice the need for a definition if you take the address of the member or,
// more commonly, pass it to a function that takes it as a reference argument --
// probably an STL function.) This macro makes MSVC do the right thing. See
// http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx for more
// information. Use like:
//
// In .h file:
// struct Foo {
// static const int kBar = 5;
// };
//
// In .cc file:
// STATIC_CONST_MEMBER_DEFINITION const int Foo::kBar;
#if V8_HAS_DECLSPEC_SELECTANY
#define STATIC_CONST_MEMBER_DEFINITION __declspec(selectany)
#else
#define STATIC_CONST_MEMBER_DEFINITION
#endif
#if V8_CC_MSVC
#include <sal.h>
// Macros for suppressing and disabling warnings on MSVC.
//
// Warning numbers are enumerated at:
// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
//
// The warning pragma:
// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
//
// Using __pragma instead of #pragma inside macros:
// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and
// for the next line of the source file.
#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress : n))
// Allows exporting a class that inherits from a non-exported base class.
// This uses suppress instead of push/pop because the delimiter after the
// declaration (either "," or "{") has to be placed before the pop macro.
//
// Example usage:
// class EXPORT_API Foo : NON_EXPORTED_BASE(public Bar) {
//
// MSVC Compiler warning C4275:
// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
// Note that this is intended to be used only when no access to the base class'
// static data is done through derived classes or inline methods. For more info,
// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
#define NON_EXPORTED_BASE(code) \
MSVC_SUPPRESS_WARNING(4275) \
code
#else // Not MSVC
#define MSVC_SUPPRESS_WARNING(n)
#define NON_EXPORTED_BASE(code) code
#endif // V8_CC_MSVC
// Allowing the use of noexcept by removing the keyword on older compilers that
// do not support adding noexcept to default members.
// Disabled on MSVC because constructors of standard containers are not noexcept
// there.
#if ((!defined(V8_CC_GNU) && !defined(V8_CC_MSVC) && \
!defined(V8_TARGET_ARCH_MIPS64) && !defined(V8_TARGET_ARCH_PPC64) && \
!defined(V8_TARGET_ARCH_RISCV64) && !defined(V8_TARGET_ARCH_RISCV32)) || \
defined(__clang__))
#define V8_NOEXCEPT noexcept
#else
#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
// Functions called from GDB.
// Forces the linker to not optimize out the function.
#if V8_HAS_ATTRIBUTE_USED && V8_HAS_ATTRIBUTE_RETAIN && \
V8_HAS_ATTRIBUTE_OPTNONE && V8_HAS_ATTRIBUTE_VISIBILITY
#define V8_DEBUGGING_EXPORT \
__attribute__((used, retain, optnone, visibility("default")))
#else
#define V8_DEBUGGING_EXPORT
#endif
#if __cplusplus >= 202002L
#define HAS_CPP_CLASS_TYPES_AS_TEMPLATE_ARGS 1
#endif
#endif // V8_BASE_COMPILER_SPECIFIC_H_

146
deps/v8/src/base/container-utils.h vendored Normal file
View File

@ -0,0 +1,146 @@
// Copyright 2022 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_CONTAINER_UTILS_H_
#define V8_BASE_CONTAINER_UTILS_H_
#include <stddef.h>
#include <algorithm>
#include <iterator>
#include <optional>
#include <vector>
namespace v8::base {
// Returns true iff the {element} is found in the {container}.
template <typename C, typename T>
bool contains(const C& container, const T& element) {
const auto e = std::end(container);
return std::find(std::begin(container), e, element) != e;
}
// Returns the first index of {element} in {container}. Returns std::nullopt if
// {container} does not contain {element}.
template <typename C, typename T>
std::optional<size_t> index_of(const C& container, const T& element) {
const auto b = std::begin(container);
const auto e = std::end(container);
if (auto it = std::find(b, e, element); it != e) {
return {std::distance(b, it)};
}
return std::nullopt;
}
// Returns the index of the first element in {container} that satisfies
// {predicate}. Returns std::nullopt if no element satisfies {predicate}.
template <typename C, typename P>
std::optional<size_t> index_of_if(const C& container, const P& predicate) {
const auto b = std::begin(container);
const auto e = std::end(container);
if (auto it = std::find_if(b, e, predicate); it != e) {
return {std::distance(b, it)};
}
return std::nullopt;
}
// Removes {count} elements from {container} starting at {index}. If {count} is
// larger than the number of elements after {index}, all elements after {index}
// are removed. Returns the number of removed elements.
template <typename C>
inline size_t erase_at(C& container, size_t index, size_t count = 1) {
// TODO(C++20): Replace with std::erase.
if (std::size(container) <= index) return 0;
auto start = std::begin(container) + index;
count = std::min<size_t>(count, std::distance(start, std::end(container)));
container.erase(start, start + count);
return count;
}
// Removes all elements from {container} that satisfy {predicate}. Returns the
// number of removed elements.
// TODO(C++20): Replace with std::erase_if.
template <typename C, typename P>
inline size_t erase_if(C& container, const P& predicate) {
auto it =
std::remove_if(std::begin(container), std::end(container), predicate);
auto count = std::distance(it, std::end(container));
container.erase(it, std::end(container));
return count;
}
// Helper for std::count_if.
template <typename C, typename P>
inline size_t count_if(const C& container, const P& predicate) {
return std::count_if(std::begin(container), std::end(container), predicate);
}
// Helper for std::all_of.
template <typename C, typename P>
inline bool all_of(const C& container, const P& predicate) {
return std::all_of(std::begin(container), std::end(container), predicate);
}
template <typename C>
inline bool all_of(const C& container) {
return std::all_of(
std::begin(container), std::end(container),
[](const auto& value) { return static_cast<bool>(value); });
}
// Helper for std::any_of.
template <typename C, typename P>
inline bool any_of(const C& container, const P& predicate) {
return std::any_of(std::begin(container), std::end(container), predicate);
}
template <typename C>
inline bool any_of(const C& container) {
return std::any_of(
std::begin(container), std::end(container),
[](const auto& value) { return static_cast<bool>(value); });
}
// Helper for std::none_of.
template <typename C, typename P>
inline bool none_of(const C& container, const P& predicate) {
return std::none_of(std::begin(container), std::end(container), predicate);
}
// Helper for std::sort.
template <typename C>
inline void sort(C& container) {
std::sort(std::begin(container), std::end(container));
}
template <typename C, typename Comp>
inline void sort(C& container, Comp comp) {
std::sort(std::begin(container), std::end(container), comp);
}
// Returns true iff all elements of {container} compare equal using operator==.
template <typename C>
inline bool all_equal(const C& container) {
if (std::size(container) <= 1) return true;
auto b = std::begin(container);
const auto& value = *b;
return std::all_of(++b, std::end(container),
[&](const auto& v) { return v == value; });
}
// Returns true iff all elements of {container} compare equal to {value} using
// operator==.
template <typename C, typename T>
inline bool all_equal(const C& container, const T& value) {
return std::all_of(std::begin(container), std::end(container),
[&](const auto& v) { return v == value; });
}
// Appends to vector {v} all the elements in the range {std::begin(container)}
// and {std::end(container)}.
template <typename V, typename C>
inline void vector_append(V& v, const C& container) {
v.insert(std::end(v), std::begin(container), std::end(container));
}
} // namespace v8::base
#endif // V8_BASE_CONTAINER_UTILS_H_

131
deps/v8/src/base/contextual.h vendored Normal file
View File

@ -0,0 +1,131 @@
// Copyright 2018 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_CONTEXTUAL_H_
#define V8_BASE_CONTEXTUAL_H_
#include <type_traits>
#include "src/base/export-template.h"
#include "src/base/macros.h"
#include "src/base/platform/platform.h"
namespace v8::base {
// {ContextualVariable} provides a clean alternative to a global variable.
// The contextual variable is mutable, and supports managing the value of
// a variable in a well-nested fashion via the {Scope} class.
// {ContextualVariable} only stores a pointer to the current value, which
// is stored in a {Scope} object. The most recent value can be retrieved
// via Get(). Because only {Scope} has actual storage, there must be at
// least one active {Scope} (i.e. in a surrounding C++ scope), whenever Get()
// is called.
// Note that contextual variables must only be used from the same thread,
// i.e. {Scope} and Get() have to be in the same thread.
template <class Derived, class VarType>
class V8_EXPORT_PRIVATE ContextualVariable {
public:
using VarT = VarType;
// A {Scope} contains a new object of type {VarType} and gives
// ContextualVariable::Get() access to it. Upon destruction, the contextual
// variable is restored to the state before the {Scope} was created. Scopes
// have to follow a stack discipline: A {Scope} has to be destructed before
// any older scope is destructed.
class V8_NODISCARD Scope {
public:
template <class... Args>
explicit Scope(Args&&... args)
: value_(std::forward<Args>(args)...), previous_(Top()) {
Top() = this;
}
~Scope() {
// Ensure stack discipline.
DCHECK_EQ(this, Top());
Top() = previous_;
}
Scope(const Scope&) = delete;
Scope& operator=(const Scope&) = delete;
VarType& Value() { return value_; }
private:
VarType value_;
Scope* previous_;
static_assert(std::is_base_of<ContextualVariable, Derived>::value,
"Curiously Recurring Template Pattern");
DISALLOW_NEW_AND_DELETE()
};
static VarType& Get() {
DCHECK(HasScope());
return Top()->Value();
}
static bool HasScope() { return Top() != nullptr; }
private:
inline static thread_local Scope* top_ = nullptr;
#if defined(USING_V8_SHARED)
// Hide the access to `top_` from other DLLs/libraries, since access to
// thread_local variables from other DLLs/libraries does not work correctly.
static Scope*& Top() { return ExportedTop(); }
#else
static Scope*& Top() { return top_; }
#endif
// Same as `Top()`, but non-inline and exported to DLLs/libraries.
// If there is a linking error for `ExportedTop()`, then the contextual
// variable probably needs to be exported using EXPORT_CONTEXTUAL_VARIABLE.
static Scope*& ExportedTop();
};
// Usage: DECLARE_CONTEXTUAL_VARIABLE(VarName, VarType)
#define DECLARE_CONTEXTUAL_VARIABLE(VarName, ...) \
struct VarName : ::v8::base::ContextualVariable<VarName, __VA_ARGS__> {}
// Contextual variables that are accessed in tests need to be
// exported. For this, place the following macro in the global namespace inside
// of a .cc file.
#define EXPORT_CONTEXTUAL_VARIABLE(VarName) \
namespace v8::base { \
template <> \
V8_EXPORT_PRIVATE typename VarName::Scope*& \
ContextualVariable<VarName, typename VarName::VarT>::ExportedTop() { \
return top_; \
} \
}
// By inheriting from {ContextualClass} a class can become a contextual variable
// of itself, which is very similar to a singleton.
template <class T>
using ContextualClass = ContextualVariable<T, T>;
// {ContextualVariableWithDefault} is similar to a {ContextualVariable},
// with the difference that a default value is used if there is no active
// {Scope} object.
template <class Derived, class VarType, auto... default_args>
class V8_EXPORT_PRIVATE ContextualVariableWithDefault
: public ContextualVariable<Derived, VarType> {
public:
static VarType& Get() {
return Base::HasScope() ? Base::Get() : default_value_;
}
private:
using Base = ContextualVariable<Derived, VarType>;
inline static thread_local VarType default_value_{default_args...};
};
// Usage: DECLARE_CONTEXTUAL_VARIABLE_WITH_DEFAULT(VarName, VarType, Args...)
#define DECLARE_CONTEXTUAL_VARIABLE_WITH_DEFAULT(VarName, ...) \
struct VarName \
: ::v8::base::ContextualVariableWithDefault<VarName, __VA_ARGS__> {}
} // namespace v8::base
#endif // V8_BASE_CONTEXTUAL_H_

1013
deps/v8/src/base/cpu.cc vendored Normal file

File diff suppressed because it is too large Load Diff

210
deps/v8/src/base/cpu.h vendored Normal file
View File

@ -0,0 +1,210 @@
// Copyright 2006-2013 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_CPU_H_
#define V8_BASE_CPU_H_
// This module contains the architecture-specific code. This make the rest of
// the code less dependent on differences between different processor
// architecture.
// The classes have the same definition for all architectures. The
// implementation for a particular architecture is put in cpu_<arch>.cc.
// The build system then uses the implementation for the target architecture.
//
#include "src/base/base-export.h"
#include "src/base/macros.h"
namespace v8 {
namespace base {
// ----------------------------------------------------------------------------
// CPU
//
// Query information about the processor.
//
// This class also has static methods for the architecture specific functions.
// Add methods here to cope with differences between the supported
// architectures. For each architecture the file cpu_<arch>.cc contains the
// implementation of these static functions.
class V8_BASE_EXPORT CPU final {
public:
CPU();
// x86 CPUID information
const char* vendor() const { return vendor_; }
int stepping() const { return stepping_; }
int model() const { return model_; }
int ext_model() const { return ext_model_; }
int family() const { return family_; }
int ext_family() const { return ext_family_; }
int type() const { return type_; }
// arm implementer/part information
int implementer() const { return implementer_; }
static const int kArm = 0x41;
static const int kNvidia = 0x4e;
static const int kQualcomm = 0x51;
int architecture() const { return architecture_; }
int variant() const { return variant_; }
static const int kNvidiaDenver = 0x0;
int part() const { return part_; }
// ARM-specific part codes
static const int kArmCortexA5 = 0xc05;
static const int kArmCortexA7 = 0xc07;
static const int kArmCortexA8 = 0xc08;
static const int kArmCortexA9 = 0xc09;
static const int kArmCortexA12 = 0xc0c;
static const int kArmCortexA15 = 0xc0f;
// Denver-specific part code
static const int kNvidiaDenverV10 = 0x002;
// PPC-specific part codes
enum { kPPCPower8, kPPCPower9, kPPCPower10 };
// General features
bool has_fpu() const { return has_fpu_; }
int icache_line_size() const { return icache_line_size_; }
int dcache_line_size() const { return dcache_line_size_; }
static const int kUnknownCacheLineSize = 0;
// x86 features
bool has_cmov() const { return has_cmov_; }
bool has_sahf() const { return has_sahf_; }
bool has_mmx() const { return has_mmx_; }
bool has_sse() const { return has_sse_; }
bool has_sse2() const { return has_sse2_; }
bool has_sse3() const { return has_sse3_; }
bool has_ssse3() const { return has_ssse3_; }
bool has_sse41() const { return has_sse41_; }
bool has_sse42() const { return has_sse42_; }
bool has_osxsave() const { return has_osxsave_; }
bool has_avx() const { return has_avx_; }
bool has_avx2() const { return has_avx2_; }
bool has_avx_vnni() const { return has_avx_vnni_; }
bool has_avx_vnni_int8() const { return has_avx_vnni_int8_; }
bool has_fma3() const { return has_fma3_; }
bool has_f16c() const { return has_f16c_; }
bool has_bmi1() const { return has_bmi1_; }
bool has_bmi2() const { return has_bmi2_; }
bool has_lzcnt() const { return has_lzcnt_; }
bool has_popcnt() const { return has_popcnt_; }
bool is_atom() const { return is_atom_; }
bool has_intel_jcc_erratum() const { return has_intel_jcc_erratum_; }
bool has_cetss() const { return has_cetss_; }
bool has_non_stop_time_stamp_counter() const {
return has_non_stop_time_stamp_counter_;
}
bool is_running_in_vm() const { return is_running_in_vm_; }
bool exposes_num_virtual_address_bits() const {
return num_virtual_address_bits_ != kUnknownNumVirtualAddressBits;
}
int num_virtual_address_bits() const {
DCHECK(exposes_num_virtual_address_bits());
return num_virtual_address_bits_;
}
static const int kUnknownNumVirtualAddressBits = 0;
// arm features
bool has_idiva() const { return has_idiva_; }
bool has_neon() const { return has_neon_; }
bool has_thumb2() const { return has_thumb2_; }
bool has_vfp() const { return has_vfp_; }
bool has_vfp3() const { return has_vfp3_; }
bool has_vfp3_d32() const { return has_vfp3_d32_; }
bool has_jscvt() const { return has_jscvt_; }
bool has_dot_prod() const { return has_dot_prod_; }
bool has_lse() const { return has_lse_; }
bool has_mte() const { return has_mte_; }
bool has_pmull1q() const { return has_pmull1q_; }
bool has_fp16() const { return has_fp16_; }
// mips features
bool is_fp64_mode() const { return is_fp64_mode_; }
bool has_msa() const { return has_msa_; }
// riscv-specific part codes
bool has_rvv() const { return has_rvv_; }
bool has_zba() const { return has_zba_; }
bool has_zbb() const { return has_zbb_; }
bool has_zbs() const { return has_zbs_; }
enum class RV_MMU_MODE {
kRiscvSV39,
kRiscvSV48,
kRiscvSV57,
};
RV_MMU_MODE riscv_mmu() const { return riscv_mmu_; }
private:
#if defined(V8_OS_STARBOARD)
bool StarboardDetectCPU();
#endif
char vendor_[13];
int stepping_;
int model_;
int ext_model_;
int family_;
int ext_family_;
int type_;
int implementer_;
int architecture_;
int variant_;
int part_;
int icache_line_size_;
int dcache_line_size_;
int num_virtual_address_bits_;
bool has_fpu_;
bool has_cmov_;
bool has_sahf_;
bool has_mmx_;
bool has_sse_;
bool has_sse2_;
bool has_sse3_;
bool has_ssse3_;
bool has_sse41_;
bool has_sse42_;
bool is_atom_;
bool has_intel_jcc_erratum_;
bool has_cetss_;
bool has_osxsave_;
bool has_avx_;
bool has_avx2_;
bool has_avx_vnni_;
bool has_avx_vnni_int8_;
bool has_fma3_;
bool has_f16c_;
bool has_bmi1_;
bool has_bmi2_;
bool has_lzcnt_;
bool has_popcnt_;
bool has_idiva_;
bool has_neon_;
bool has_thumb2_;
bool has_vfp_;
bool has_vfp3_;
bool has_vfp3_d32_;
bool has_jscvt_;
bool has_dot_prod_;
bool has_lse_;
bool has_mte_;
bool has_pmull1q_;
bool has_fp16_;
bool is_fp64_mode_;
bool has_non_stop_time_stamp_counter_;
bool is_running_in_vm_;
bool has_msa_;
RV_MMU_MODE riscv_mmu_;
bool has_rvv_;
bool has_zba_;
bool has_zbb_;
bool has_zbs_;
};
} // namespace base
} // namespace v8
#endif // V8_BASE_CPU_H_

40
deps/v8/src/base/debug/stack_trace.cc vendored Normal file
View File

@ -0,0 +1,40 @@
// Copyright 2016 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.
#include "src/base/debug/stack_trace.h"
#include <string.h>
#include <algorithm>
#include <sstream>
#include "src/base/macros.h"
namespace v8 {
namespace base {
namespace debug {
StackTrace::StackTrace(const void* const* trace, size_t count) {
count = std::min(count, arraysize(trace_));
if (count) memcpy(trace_, trace, count * sizeof(trace_[0]));
count_ = count;
}
StackTrace::~StackTrace() = default;
const void* const* StackTrace::Addresses(size_t* count) const {
*count = count_;
if (count_) return trace_;
return nullptr;
}
std::string StackTrace::ToString() const {
std::stringstream stream;
OutputToStream(&stream);
return stream.str();
}
} // namespace debug
} // namespace base
} // namespace v8

97
deps/v8/src/base/debug/stack_trace.h vendored Normal file
View File

@ -0,0 +1,97 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Slightly adapted for inclusion in V8.
// Copyright 2016 the V8 project authors. All rights reserved.
#ifndef V8_BASE_DEBUG_STACK_TRACE_H_
#define V8_BASE_DEBUG_STACK_TRACE_H_
#include <stddef.h>
#include <iosfwd>
#include <string>
#include "src/base/base-export.h"
#include "src/base/build_config.h"
#if V8_OS_POSIX
#include <unistd.h>
#endif
#if V8_OS_WIN
struct _EXCEPTION_POINTERS;
struct _CONTEXT;
#endif
namespace v8 {
namespace base {
namespace debug {
// Enables stack dump to console output on exception and signals.
// When enabled, the process will quit immediately. This is meant to be used in
// tests only!
V8_BASE_EXPORT bool EnableInProcessStackDumping();
V8_BASE_EXPORT void DisableSignalStackDump();
// A stacktrace can be helpful in debugging. For example, you can include a
// stacktrace member in an object (probably around #ifndef NDEBUG) so that you
// can later see where the given object was created from.
class V8_BASE_EXPORT StackTrace {
public:
// Creates a stacktrace from the current location.
StackTrace();
// Creates a stacktrace from an existing array of instruction
// pointers (such as returned by Addresses()). |count| will be
// trimmed to |kMaxTraces|.
StackTrace(const void* const* trace, size_t count);
#if V8_OS_WIN
// Creates a stacktrace for an exception.
// Note: this function will throw an import not found (StackWalk64) exception
// on system without dbghelp 5.1.
explicit StackTrace(_EXCEPTION_POINTERS* exception_pointers);
explicit StackTrace(const _CONTEXT* context);
#endif
// Copying and assignment are allowed with the default functions.
~StackTrace();
// Gets an array of instruction pointer values. |*count| will be set to the
// number of elements in the returned array.
const void* const* Addresses(size_t* count) const;
// Prints the stack trace to stderr.
void Print() const;
// Resolves backtrace to symbols and write to stream.
void OutputToStream(std::ostream* os) const;
// Resolves backtrace to symbols and returns as string.
std::string ToString() const;
private:
#if V8_OS_WIN
void InitTrace(const _CONTEXT* context_record);
#endif
// From http://msdn.microsoft.com/en-us/library/bb204633.aspx,
// the sum of FramesToSkip and FramesToCapture must be less than 63,
// so set it to 62. Even if on POSIX it could be a larger value, it usually
// doesn't give much more information.
static const int kMaxTraces = 62;
void* trace_[kMaxTraces];
// The number of valid frames in |trace_|.
size_t count_;
};
} // namespace debug
} // namespace base
} // namespace v8
#endif // V8_BASE_DEBUG_STACK_TRACE_H_

View File

@ -0,0 +1,91 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Slightly adapted for inclusion in V8.
// Copyright 2016 the V8 project authors. All rights reserved.
#include "src/base/debug/stack_trace.h"
#include <signal.h>
#include <stddef.h>
#include <string.h>
#include <unwind.h>
#include <src/base/platform/platform.h>
#include <iomanip>
#include <ostream>
namespace {
struct StackCrawlState {
StackCrawlState(uintptr_t* frames, size_t max_depth)
: frames(frames),
frame_count(0),
max_depth(max_depth),
have_skipped_self(false) {}
uintptr_t* frames;
size_t frame_count;
size_t max_depth;
bool have_skipped_self;
};
_Unwind_Reason_Code TraceStackFrame(_Unwind_Context* context, void* arg) {
StackCrawlState* state = static_cast<StackCrawlState*>(arg);
uintptr_t ip = _Unwind_GetIP(context);
// The first stack frame is this function itself. Skip it.
if (ip != 0 && !state->have_skipped_self) {
state->have_skipped_self = true;
return _URC_NO_REASON;
}
state->frames[state->frame_count++] = ip;
if (state->frame_count >= state->max_depth)
return _URC_END_OF_STACK;
return _URC_NO_REASON;
}
} // namespace
namespace v8 {
namespace base {
namespace debug {
bool EnableInProcessStackDumping() {
// When running in an application, our code typically expects SIGPIPE
// to be ignored. Therefore, when testing that same code, it should run
// with SIGPIPE ignored as well.
// TODO(phajdan.jr): De-duplicate this SIGPIPE code.
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = SIG_IGN;
sigemptyset(&action.sa_mask);
return (sigaction(SIGPIPE, &action, nullptr) == 0);
}
void DisableSignalStackDump() {
}
StackTrace::StackTrace() {
StackCrawlState state(reinterpret_cast<uintptr_t*>(trace_), kMaxTraces);
_Unwind_Backtrace(&TraceStackFrame, &state);
count_ = state.frame_count;
}
void StackTrace::Print() const {
std::string backtrace = ToString();
OS::Print("%s\n", backtrace.c_str());
}
void StackTrace::OutputToStream(std::ostream* os) const {
for (size_t i = 0; i < count_; ++i) {
*os << "#" << std::setw(2) << i << trace_[i] << "\n";
}
}
} // namespace debug
} // namespace base
} // namespace v8

View File

@ -0,0 +1,41 @@
// Copyright 2017 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.
#include "src/base/debug/stack_trace.h"
#include <iomanip>
#include <ostream>
#include "src/base/platform/platform.h"
namespace v8 {
namespace base {
namespace debug {
bool EnableInProcessStackDumping() {
// The system crashlogger captures and prints backtraces which are then
// symbolized by a host-side script that runs addr2line. Because symbols are
// not available on device, there's not much use in implementing in-process
// capture.
return false;
}
void DisableSignalStackDump() {}
StackTrace::StackTrace() {}
void StackTrace::Print() const {
std::string backtrace = ToString();
OS::Print("%s\n", backtrace.c_str());
}
void StackTrace::OutputToStream(std::ostream* os) const {
for (size_t i = 0; i < count_; ++i) {
*os << "#" << std::setw(2) << i << trace_[i] << "\n";
}
}
} // namespace debug
} // namespace base
} // namespace v8

View File

@ -0,0 +1,479 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Slightly adapted for inclusion in V8.
// Copyright 2016 the V8 project authors. All rights reserved.
#include "src/base/debug/stack_trace.h"
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <map>
#include <memory>
#include <ostream>
#include <string>
#include <vector>
#if V8_LIBC_GLIBC || V8_LIBC_BSD || V8_LIBC_UCLIBC || V8_OS_SOLARIS
#define HAVE_EXECINFO_H 1
#endif
#if HAVE_EXECINFO_H
#include <cxxabi.h>
#include <execinfo.h>
#endif
#if V8_OS_DARWIN
#include <AvailabilityMacros.h>
#endif
#include "src/base/build_config.h"
#include "src/base/free_deleter.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
namespace v8 {
namespace base {
namespace debug {
namespace internal {
// POSIX doesn't define any async-signal safe function for converting
// an integer to ASCII. We'll have to define our own version.
// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the
// conversion was successful or nullptr otherwise. It never writes more than
// "sz" bytes. Output will be truncated as needed, and a NUL character is always
// appended.
char* itoa_r(intptr_t i, char* buf, size_t sz, int base, size_t padding);
} // namespace internal
namespace {
volatile sig_atomic_t in_signal_handler = 0;
bool dump_stack_in_signal_handler = true;
// The prefix used for mangled symbols, per the Itanium C++ ABI:
// http://www.codesourcery.com/cxx-abi/abi.html#mangling
const char kMangledSymbolPrefix[] = "_Z";
// Characters that can be used for symbols, generated by Ruby:
// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
const char kSymbolCharacters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
#if HAVE_EXECINFO_H
// Demangles C++ symbols in the given text. Example:
//
// "out/Debug/base_unittests(_ZN10StackTraceC1Ev+0x20) [0x817778c]"
// =>
// "out/Debug/base_unittests(StackTrace::StackTrace()+0x20) [0x817778c]"
void DemangleSymbols(std::string* text) {
// Note: code in this function is NOT async-signal safe (std::string uses
// malloc internally).
std::string::size_type search_from = 0;
while (search_from < text->size()) {
// Look for the start of a mangled symbol, from search_from.
std::string::size_type mangled_start =
text->find(kMangledSymbolPrefix, search_from);
if (mangled_start == std::string::npos) {
break; // Mangled symbol not found.
}
// Look for the end of the mangled symbol.
std::string::size_type mangled_end =
text->find_first_not_of(kSymbolCharacters, mangled_start);
if (mangled_end == std::string::npos) {
mangled_end = text->size();
}
std::string mangled_symbol =
text->substr(mangled_start, mangled_end - mangled_start);
// Try to demangle the mangled symbol candidate.
int status = 0;
std::unique_ptr<char, FreeDeleter> demangled_symbol(
abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, nullptr, &status));
if (status == 0) { // Demangling is successful.
// Remove the mangled symbol.
text->erase(mangled_start, mangled_end - mangled_start);
// Insert the demangled symbol.
text->insert(mangled_start, demangled_symbol.get());
// Next time, we'll start right after the demangled symbol we inserted.
search_from = mangled_start + strlen(demangled_symbol.get());
} else {
// Failed to demangle. Retry after the "_Z" we just found.
search_from = mangled_start + 2;
}
}
}
#endif // HAVE_EXECINFO_H
class BacktraceOutputHandler {
public:
virtual void HandleOutput(const char* output) = 0;
// If this output handler writes directly to a file descriptor, this file
// descriptor can be exposed by overwriting this method. That is in turn
// useful for ProcessBacktrace which can then use backtrace_symbols_fd.
virtual int OutputFileDescriptor() const { return 0; }
protected:
virtual ~BacktraceOutputHandler() = default;
};
#if HAVE_EXECINFO_H
void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
// This should be more than enough to store a 64-bit number in hex:
// 16 hex digits + 1 for null-terminator.
char buf[17] = {'\0'};
handler->HandleOutput("0x");
internal::itoa_r(reinterpret_cast<intptr_t>(pointer), buf, sizeof(buf), 16,
12);
handler->HandleOutput(buf);
}
void ProcessBacktrace(void* const* trace, size_t size,
BacktraceOutputHandler* handler) {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
handler->HandleOutput("\n");
handler->HandleOutput("==== C stack trace ===============================\n");
handler->HandleOutput("\n");
bool printed = false;
// Below part is async-signal unsafe (uses malloc), so execute it only
// when we are not executing the signal handler.
if (in_signal_handler == 0) {
std::unique_ptr<char*, FreeDeleter> trace_symbols(
backtrace_symbols(trace, static_cast<int>(size)));
if (trace_symbols) {
for (size_t i = 0; i < size; ++i) {
std::string trace_symbol = trace_symbols.get()[i];
DemangleSymbols(&trace_symbol);
handler->HandleOutput(" ");
handler->HandleOutput(trace_symbol.c_str());
handler->HandleOutput("\n");
}
printed = true;
}
} else if (handler->OutputFileDescriptor() != 0) {
// In this case, we can use backtrace_symbols_fd to write directly to the
// output file descriptor. This isn't quite as nice as we don't control the
// formatting and because mangled function names will be used, but still
// better than just raw addresses (which are also included in this output).
backtrace_symbols_fd(trace, static_cast<int>(size),
handler->OutputFileDescriptor());
printed = true;
}
if (!printed) {
for (size_t i = 0; i < size; ++i) {
handler->HandleOutput(" [");
OutputPointer(trace[i], handler);
handler->HandleOutput("]\n");
}
}
}
#endif // HAVE_EXECINFO_H
void PrintToStderr(const char* output) {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
ssize_t return_val = write(STDERR_FILENO, output, strlen(output));
USE(return_val);
}
void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
// NOTE: This code MUST be async-signal safe.
// NO malloc or stdio is allowed here.
// Record the fact that we are in the signal handler now, so that the rest
// of StackTrace can behave in an async-signal-safe manner.
in_signal_handler = 1;
PrintToStderr("Received signal ");
char buf[1024] = {0};
internal::itoa_r(signal, buf, sizeof(buf), 10, 0);
PrintToStderr(buf);
if (signal == SIGBUS) {
if (info->si_code == BUS_ADRALN)
PrintToStderr(" BUS_ADRALN ");
else if (info->si_code == BUS_ADRERR)
PrintToStderr(" BUS_ADRERR ");
else if (info->si_code == BUS_OBJERR)
PrintToStderr(" BUS_OBJERR ");
else
PrintToStderr(" <unknown> ");
} else if (signal == SIGFPE) {
if (info->si_code == FPE_FLTDIV)
PrintToStderr(" FPE_FLTDIV ");
else if (info->si_code == FPE_FLTINV)
PrintToStderr(" FPE_FLTINV ");
else if (info->si_code == FPE_FLTOVF)
PrintToStderr(" FPE_FLTOVF ");
else if (info->si_code == FPE_FLTRES)
PrintToStderr(" FPE_FLTRES ");
else if (info->si_code == FPE_FLTSUB)
PrintToStderr(" FPE_FLTSUB ");
else if (info->si_code == FPE_FLTUND)
PrintToStderr(" FPE_FLTUND ");
else if (info->si_code == FPE_INTDIV)
PrintToStderr(" FPE_INTDIV ");
else if (info->si_code == FPE_INTOVF)
PrintToStderr(" FPE_INTOVF ");
else
PrintToStderr(" <unknown> ");
} else if (signal == SIGILL) {
if (info->si_code == ILL_BADSTK)
PrintToStderr(" ILL_BADSTK ");
else if (info->si_code == ILL_COPROC)
PrintToStderr(" ILL_COPROC ");
else if (info->si_code == ILL_ILLOPN)
PrintToStderr(" ILL_ILLOPN ");
else if (info->si_code == ILL_ILLADR)
PrintToStderr(" ILL_ILLADR ");
else if (info->si_code == ILL_ILLTRP)
PrintToStderr(" ILL_ILLTRP ");
else if (info->si_code == ILL_PRVOPC)
PrintToStderr(" ILL_PRVOPC ");
else if (info->si_code == ILL_PRVREG)
PrintToStderr(" ILL_PRVREG ");
else
PrintToStderr(" <unknown> ");
} else if (signal == SIGSEGV) {
if (info->si_code == SEGV_MAPERR)
PrintToStderr(" SEGV_MAPERR ");
else if (info->si_code == SEGV_ACCERR)
PrintToStderr(" SEGV_ACCERR ");
else
PrintToStderr(" <unknown> ");
}
if (signal == SIGBUS || signal == SIGFPE || signal == SIGILL ||
signal == SIGSEGV) {
internal::itoa_r(reinterpret_cast<intptr_t>(info->si_addr), buf,
sizeof(buf), 16, 12);
PrintToStderr(buf);
}
PrintToStderr("\n");
if (dump_stack_in_signal_handler) {
debug::StackTrace().Print();
PrintToStderr("[end of stack trace]\n");
}
if (::signal(signal, SIG_DFL) == SIG_ERR) _exit(1);
}
class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
public:
PrintBacktraceOutputHandler() = default;
PrintBacktraceOutputHandler(const PrintBacktraceOutputHandler&) = delete;
PrintBacktraceOutputHandler& operator=(const PrintBacktraceOutputHandler&) =
delete;
void HandleOutput(const char* output) override {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
PrintToStderr(output);
}
int OutputFileDescriptor() const override { return STDERR_FILENO; }
};
class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
public:
explicit StreamBacktraceOutputHandler(std::ostream* os) : os_(os) {}
StreamBacktraceOutputHandler(const StreamBacktraceOutputHandler&) = delete;
StreamBacktraceOutputHandler& operator=(const StreamBacktraceOutputHandler&) =
delete;
void HandleOutput(const char* output) override { (*os_) << output; }
private:
std::ostream* os_;
};
void WarmUpBacktrace() {
// Warm up stack trace infrastructure. It turns out that on the first
// call glibc initializes some internal data structures using pthread_once,
// and even backtrace() can call malloc(), leading to hangs.
//
// Example stack trace snippet (with tcmalloc):
//
// #8 0x0000000000a173b5 in tc_malloc
// at ./third_party/tcmalloc/chromium/src/debugallocation.cc:1161
// #9 0x00007ffff7de7900 in _dl_map_object_deps at dl-deps.c:517
// #10 0x00007ffff7ded8a9 in dl_open_worker at dl-open.c:262
// #11 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
// #12 0x00007ffff7ded31a in _dl_open (file=0x7ffff625e298 "libgcc_s.so.1")
// at dl-open.c:639
// #13 0x00007ffff6215602 in do_dlopen at dl-libc.c:89
// #14 0x00007ffff7de9176 in _dl_catch_error at dl-error.c:178
// #15 0x00007ffff62156c4 in dlerror_run at dl-libc.c:48
// #16 __GI___libc_dlopen_mode at dl-libc.c:165
// #17 0x00007ffff61ef8f5 in init
// at ../sysdeps/x86_64/../ia64/backtrace.c:53
// #18 0x00007ffff6aad400 in pthread_once
// at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:104
// #19 0x00007ffff61efa14 in __GI___backtrace
// at ../sysdeps/x86_64/../ia64/backtrace.c:104
// #20 0x0000000000752a54 in base::debug::StackTrace::StackTrace
// at base/debug/stack_trace_posix.cc:175
// #21 0x00000000007a4ae5 in
// base::(anonymous namespace)::StackDumpSignalHandler
// at base/process_util_posix.cc:172
// #22 <signal handler called>
StackTrace stack_trace;
}
} // namespace
bool EnableInProcessStackDumping() {
// When running in an application, our code typically expects SIGPIPE
// to be ignored. Therefore, when testing that same code, it should run
// with SIGPIPE ignored as well.
struct sigaction sigpipe_action;
memset(&sigpipe_action, 0, sizeof(sigpipe_action));
sigpipe_action.sa_handler = SIG_IGN;
sigemptyset(&sigpipe_action.sa_mask);
bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0);
// Avoid hangs during backtrace initialization, see above.
WarmUpBacktrace();
struct sigaction action;
memset(&action, 0, sizeof(action));
// Use SA_ONSTACK so that iff an alternate stack has been registered, the
// handler will run on that stack instead of the default stack. This can be
// useful for example if the stack pointer gets corrupted or in case of stack
// overflows, since that might prevent the handler from running properly.
action.sa_flags = SA_RESETHAND | SA_SIGINFO | SA_ONSTACK;
action.sa_sigaction = &StackDumpSignalHandler;
sigemptyset(&action.sa_mask);
success &= (sigaction(SIGILL, &action, nullptr) == 0);
success &= (sigaction(SIGABRT, &action, nullptr) == 0);
success &= (sigaction(SIGFPE, &action, nullptr) == 0);
success &= (sigaction(SIGBUS, &action, nullptr) == 0);
success &= (sigaction(SIGSEGV, &action, nullptr) == 0);
success &= (sigaction(SIGSYS, &action, nullptr) == 0);
dump_stack_in_signal_handler = true;
return success;
}
void DisableSignalStackDump() {
dump_stack_in_signal_handler = false;
}
StackTrace::StackTrace() {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
#if HAVE_EXECINFO_H
// Though the backtrace API man page does not list any possible negative
// return values, we take no chance.
count_ = static_cast<size_t>(backtrace(trace_, arraysize(trace_)));
#else
count_ = 0;
#endif
}
void StackTrace::Print() const {
// NOTE: This code MUST be async-signal safe (it's used by in-process
// stack dumping signal handler). NO malloc or stdio is allowed here.
#if HAVE_EXECINFO_H
PrintBacktraceOutputHandler handler;
ProcessBacktrace(trace_, count_, &handler);
#endif
}
void StackTrace::OutputToStream(std::ostream* os) const {
#if HAVE_EXECINFO_H
StreamBacktraceOutputHandler handler(os);
ProcessBacktrace(trace_, count_, &handler);
#endif
}
namespace internal {
// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
char* itoa_r(intptr_t i, char* buf, size_t sz, int base, size_t padding) {
// Make sure we can write at least one NUL byte.
size_t n = 1;
if (n > sz) return nullptr;
if (base < 2 || base > 16) {
buf[0] = '\0';
return nullptr;
}
char* start = buf;
uintptr_t j = i;
// Handle negative numbers (only for base 10).
if (i < 0 && base == 10) {
// This does "j = -i" while avoiding integer overflow.
j = static_cast<uintptr_t>(-(i + 1)) + 1;
// Make sure we can write the '-' character.
if (++n > sz) {
buf[0] = '\0';
return nullptr;
}
*start++ = '-';
}
// Loop until we have converted the entire number. Output at least one
// character (i.e. '0').
char* ptr = start;
do {
// Make sure there is still enough space left in our output buffer.
if (++n > sz) {
buf[0] = '\0';
return nullptr;
}
// Output the next digit.
*ptr++ = "0123456789abcdef"[j % base];
j /= base;
if (padding > 0) padding--;
} while (j > 0 || padding > 0);
// Terminate the output with a NUL character.
*ptr = '\0';
// Conversion to ASCII actually resulted in the digits being in reverse
// order. We can't easily generate them in forward order, as we can't tell
// the number of characters needed until we are done converting.
// So, now, we reverse the string (except for the possible "-" sign).
while (--ptr > start) {
char ch = *ptr;
*ptr = *start;
*start++ = ch;
}
return buf;
}
} // namespace internal
} // namespace debug
} // namespace base
} // namespace v8

View File

@ -0,0 +1,249 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Slightly adapted for inclusion in V8.
// Copyright 2016 the V8 project authors. All rights reserved.
#include "src/base/debug/stack_trace.h"
// This file can't use "src/base/win32-headers.h" because it defines symbols
// that lead to compilation errors. But `NOMINMAX` should be defined to disable
// defining of the `min` and `max` MACROS.
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <dbghelp.h>
#include <stddef.h>
#include <iostream>
#include <memory>
#include <string>
#include "src/base/logging.h"
#include "src/base/macros.h"
namespace v8 {
namespace base {
namespace debug {
namespace {
// Previous unhandled filter. Will be called if not nullptr when we intercept an
// exception. Only used in unit tests.
LPTOP_LEVEL_EXCEPTION_FILTER g_previous_filter = nullptr;
bool g_dump_stack_in_signal_handler = true;
bool g_initialized_symbols = false;
DWORD g_init_error = ERROR_SUCCESS;
// Prints the exception call stack.
// This is the unit tests exception filter.
long WINAPI StackDumpExceptionFilter(EXCEPTION_POINTERS* info) { // NOLINT
if (g_dump_stack_in_signal_handler) {
debug::StackTrace(info).Print();
}
if (g_previous_filter) return g_previous_filter(info);
return EXCEPTION_CONTINUE_SEARCH;
}
bool InitializeSymbols() {
if (g_initialized_symbols) return g_init_error == ERROR_SUCCESS;
g_initialized_symbols = true;
// Defer symbol load until they're needed, use undecorated names, and get line
// numbers.
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
if (!SymInitialize(GetCurrentProcess(), nullptr, TRUE)) {
g_init_error = GetLastError();
// TODO(awong): Handle error: SymInitialize can fail with
// ERROR_INVALID_PARAMETER.
// When it fails, we should not call debugbreak since it kills the current
// process (prevents future tests from running or kills the browser
// process).
return false;
}
// When transferring the binaries e.g. between bots, path put
// into the executable will get off. To still retrieve symbols correctly,
// add the directory of the executable to symbol search path.
// All following errors are non-fatal.
const size_t kSymbolsArraySize = 1024;
std::unique_ptr<wchar_t[]> symbols_path(new wchar_t[kSymbolsArraySize]);
// Note: The below function takes buffer size as number of characters,
// not number of bytes!
if (!SymGetSearchPathW(GetCurrentProcess(), symbols_path.get(),
kSymbolsArraySize)) {
g_init_error = GetLastError();
return false;
}
wchar_t exe_path[MAX_PATH];
GetModuleFileName(nullptr, exe_path, MAX_PATH);
std::wstring exe_path_wstring(exe_path);
// To get the path without the filename, we just need to remove the final
// slash and everything after it.
std::wstring new_path(
std::wstring(symbols_path.get()) + L";" +
exe_path_wstring.substr(0, exe_path_wstring.find_last_of(L"\\/")));
if (!SymSetSearchPathW(GetCurrentProcess(), new_path.c_str())) {
g_init_error = GetLastError();
return false;
}
g_init_error = ERROR_SUCCESS;
return true;
}
// For the given trace, attempts to resolve the symbols, and output a trace
// to the ostream os. The format for each line of the backtrace is:
//
// <tab>SymbolName[0xAddress+Offset] (FileName:LineNo)
//
// This function should only be called if Init() has been called. We do not
// LOG(FATAL) here because this code is called might be triggered by a
// LOG(FATAL) itself. Also, it should not be calling complex code that is
// extensible like PathService since that can in turn fire CHECKs.
void OutputTraceToStream(const void* const* trace, size_t count,
std::ostream* os) {
for (size_t i = 0; (i < count) && os->good(); ++i) {
const int kMaxNameLength = 256;
DWORD_PTR frame = reinterpret_cast<DWORD_PTR>(trace[i]);
// Code adapted from MSDN example:
// http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
ULONG64 buffer[(sizeof(SYMBOL_INFO) + kMaxNameLength * sizeof(wchar_t) +
sizeof(ULONG64) - 1) /
sizeof(ULONG64)];
memset(buffer, 0, sizeof(buffer));
// Initialize symbol information retrieval structures.
DWORD64 sym_displacement = 0;
PSYMBOL_INFO symbol = reinterpret_cast<PSYMBOL_INFO>(&buffer[0]);
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = kMaxNameLength - 1;
BOOL has_symbol =
SymFromAddr(GetCurrentProcess(), frame, &sym_displacement, symbol);
// Attempt to retrieve line number information.
DWORD line_displacement = 0;
IMAGEHLP_LINE64 line = {};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
BOOL has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame,
&line_displacement, &line);
// Output the backtrace line.
(*os) << "\t";
if (has_symbol) {
(*os) << symbol->Name << " [0x" << trace[i] << "+" << sym_displacement
<< "]";
} else {
// If there is no symbol information, add a spacer.
(*os) << "(No symbol) [0x" << trace[i] << "]";
}
if (has_line) {
(*os) << " (" << line.FileName << ":" << line.LineNumber << ")";
}
(*os) << "\n";
}
}
} // namespace
bool EnableInProcessStackDumping() {
// Add stack dumping support on exception on windows. Similar to OS_POSIX
// signal() handling in process_util_posix.cc.
g_previous_filter = SetUnhandledExceptionFilter(&StackDumpExceptionFilter);
g_dump_stack_in_signal_handler = true;
// Need to initialize symbols early in the process or else this fails on
// swarming (since symbols are in different directory than in the exes) and
// also release x64.
return InitializeSymbols();
}
void DisableSignalStackDump() {
g_dump_stack_in_signal_handler = false;
}
StackTrace::StackTrace() {
// When walking our own stack, use CaptureStackBackTrace().
count_ = CaptureStackBackTrace(0, arraysize(trace_), trace_, nullptr);
}
StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
InitTrace(exception_pointers->ContextRecord);
}
StackTrace::StackTrace(const CONTEXT* context) { InitTrace(context); }
void StackTrace::InitTrace(const CONTEXT* context_record) {
// StackWalk64 modifies the register context in place, so we have to copy it
// so that downstream exception handlers get the right context. The incoming
// context may have had more register state (YMM, etc) than we need to unwind
// the stack. Typically StackWalk64 only needs integer and control registers.
CONTEXT context_copy;
memcpy(&context_copy, context_record, sizeof(context_copy));
context_copy.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
// When walking an exception stack, we need to use StackWalk64().
count_ = 0;
// Initialize stack walking.
STACKFRAME64 stack_frame;
memset(&stack_frame, 0, sizeof(stack_frame));
#if defined(_WIN64)
#if defined(_M_X64)
int machine_type = IMAGE_FILE_MACHINE_AMD64;
stack_frame.AddrPC.Offset = context_record->Rip;
stack_frame.AddrFrame.Offset = context_record->Rbp;
stack_frame.AddrStack.Offset = context_record->Rsp;
#elif defined(_M_ARM64)
int machine_type = IMAGE_FILE_MACHINE_ARM64;
stack_frame.AddrPC.Offset = context_record->Pc;
stack_frame.AddrFrame.Offset = context_record->Fp;
stack_frame.AddrStack.Offset = context_record->Sp;
#else
#error Unsupported Arch
#endif
#else
int machine_type = IMAGE_FILE_MACHINE_I386;
stack_frame.AddrPC.Offset = context_record->Eip;
stack_frame.AddrFrame.Offset = context_record->Ebp;
stack_frame.AddrStack.Offset = context_record->Esp;
#endif
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Mode = AddrModeFlat;
while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
&stack_frame, &context_copy, nullptr,
&SymFunctionTableAccess64, &SymGetModuleBase64, nullptr) &&
count_ < arraysize(trace_)) {
trace_[count_++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
}
for (size_t i = count_; i < arraysize(trace_); ++i) trace_[i] = nullptr;
}
void StackTrace::Print() const { OutputToStream(&std::cerr); }
void StackTrace::OutputToStream(std::ostream* os) const {
InitializeSymbols();
if (g_init_error != ERROR_SUCCESS) {
(*os) << "Error initializing symbols (" << g_init_error
<< "). Dumping unresolved backtrace:\n";
for (size_t i = 0; (i < count_) && os->good(); ++i) {
(*os) << "\t" << trace_[i] << "\n";
}
} else {
(*os) << "\n";
(*os) << "==== C stack trace ===============================\n";
(*os) << "\n";
OutputTraceToStream(trace_, count_, os);
}
}
} // namespace debug
} // namespace base
} // namespace v8

View File

@ -0,0 +1,123 @@
// Copyright (c) 2024 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Slightly adapted for inclusion in V8.
// Copyright 2024 the V8 project authors. All rights reserved.
#include <signal.h>
#include "src/base/build_config.h"
#include "src/base/debug/stack_trace.h"
#include "src/base/free_deleter.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
namespace v8 {
namespace base {
namespace debug {
namespace {
bool IsDumpStackInSignalHandler = true;
bool StartThread(void* (*threadEntry)(void*)) {
// based on Thread::Start()
int result;
pthread_attr_t attr;
memset(&attr, 0, sizeof(attr));
result = pthread_attr_init(&attr);
if (result != 0) return false;
constexpr size_t kDefaultStackSize = 4 * 1024 * 1024;
size_t stack_size;
result = pthread_attr_getstacksize(&attr, &stack_size);
DCHECK_EQ(0, result);
if (stack_size < kDefaultStackSize) stack_size = kDefaultStackSize;
result = pthread_attr_setstacksize(&attr, stack_size);
if (result != 0) return pthread_attr_destroy(&attr), false;
{
std::mutex lock_guard;
pthread_t thread_;
result = pthread_create(&thread_, &attr, threadEntry, nullptr);
if (result != 0) {
perror("pthread_create");
return pthread_attr_destroy(&attr), false;
}
}
result = pthread_attr_destroy(&attr);
return result == 0;
}
void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
fprintf(stderr, "Received signal %d\n", signal);
if (signal == SIGABRT) {
// From third_party/zoslib, will first call __display_traceback().
abort();
}
if (IsDumpStackInSignalHandler) __display_backtrace(STDERR_FILENO);
raise(signal);
}
void* StackDumpingSignalThread(void* data) {
struct sigaction sigpipe_action;
memset(&sigpipe_action, 0, sizeof(sigpipe_action));
sigpipe_action.sa_handler = SIG_IGN;
sigemptyset(&sigpipe_action.sa_mask);
bool success = (sigaction(SIGPIPE, &sigpipe_action, nullptr) == 0);
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_RESETHAND | SA_SIGINFO | SA_ONSTACK;
action.sa_sigaction = &StackDumpSignalHandler;
sigemptyset(&action.sa_mask);
success &= (sigaction(SIGILL, &action, nullptr) == 0);
success &= (sigaction(SIGABRT, &action, nullptr) == 0);
success &= (sigaction(SIGFPE, &action, nullptr) == 0);
success &= (sigaction(SIGBUS, &action, nullptr) == 0);
success &= (sigaction(SIGSEGV, &action, nullptr) == 0);
success &= (sigaction(SIGSYS, &action, nullptr) == 0);
success &= (sigaction(SIGINT, &action, nullptr) == 0);
success &= (sigaction(SIGTERM, &action, nullptr) == 0);
CHECK_EQ(true, success);
while (1) {
CHECK_EQ(pause(), -1);
CHECK_EQ(errno, EINTR);
}
}
} // namespace
bool EnableInProcessStackDumping() {
IsDumpStackInSignalHandler = true;
bool success = StartThread(StackDumpingSignalThread);
CHECK_EQ(true, success);
// Block all signals on the main thread:
sigset_t set;
sigfillset(&set);
CHECK_EQ(0, pthread_sigmask(SIG_BLOCK, &set, NULL));
return success;
}
void DisableSignalStackDump() {
IsDumpStackInSignalHandler = false;
// zoslib's abort() displays backtrace by default, so disable it:
__set_backtrace_on_abort(false);
}
StackTrace::StackTrace() {}
void StackTrace::Print() const { __display_backtrace(STDERR_FILENO); }
void StackTrace::OutputToStream(std::ostream* os) const {
// TODO(gabylb): zos - pending std::osstream version in zoslib:
// __display_backtrace(os);
UNREACHABLE();
}
} // namespace debug
} // namespace base
} // namespace v8

120
deps/v8/src/base/discriminated-union.h vendored Normal file
View File

@ -0,0 +1,120 @@
// Copyright 2023 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_DISCRIMINATED_UNION_H_
#define V8_BASE_DISCRIMINATED_UNION_H_
#include <type_traits>
#include <utility>
#include "src/base/compiler-specific.h"
#include "src/base/template-utils.h"
namespace v8 {
namespace base {
// A variant-like discriminated union type, which takes a discriminating enum
// and a set of types. The enum must have as many elements as the number of
// types, with each enum value corresponding to one type in the set.
//
// Example usage:
//
// enum class FooType {
// kBar,
// kBaz
// }
// class Bar { ... };
// class Baz { ... };
//
// // FooType::kBar and FooType::kBaz match Bar and Baz, respectively.
// DiscriminatedUnion<FooType, Bar, Baz> union;
//
// switch (union.tag()) {
// case FooType::kBar:
// return process_bar(union.get<FooType::kBar>);
// case FooType::kBaz:
// return process_baz(union.get<FooType::kBaz>);
// }
template <typename TagEnum, typename... Ts>
class DiscriminatedUnion {
public:
// All Ts must be trivially destructible to avoid DiscriminatedUnion needing a
// destructor.
static_assert((std::is_trivially_destructible_v<Ts> && ...));
using Tag = TagEnum;
DiscriminatedUnion(DiscriminatedUnion&& other) V8_NOEXCEPT = default;
DiscriminatedUnion(const DiscriminatedUnion& other) V8_NOEXCEPT = default;
DiscriminatedUnion& operator=(DiscriminatedUnion&& other)
V8_NOEXCEPT = default;
DiscriminatedUnion& operator=(const DiscriminatedUnion& other)
V8_NOEXCEPT = default;
// TODO(leszeks): Add in-place constructor.
// Construct with known tag and type (the tag is DCHECKed).
template <typename T>
constexpr explicit DiscriminatedUnion(Tag tag, T&& data) V8_NOEXCEPT {
constexpr size_t index = index_of_type_v<std::decay_t<T>, Ts...>;
static_assert(index < sizeof...(Ts));
static_assert(index < std::numeric_limits<uint8_t>::max());
// TODO(leszeks): Support unions with repeated types.
DCHECK_EQ(tag, static_cast<Tag>(index));
tag_ = static_cast<uint8_t>(index);
new (data_) T(std::forward<T>(data));
}
// Construct with known type.
template <typename T>
constexpr explicit DiscriminatedUnion(T&& data) V8_NOEXCEPT {
constexpr size_t index = index_of_type_v<std::decay_t<T>, Ts...>;
static_assert(index < sizeof...(Ts));
static_assert(index < std::numeric_limits<uint8_t>::max());
tag_ = static_cast<uint8_t>(index);
new (data_) T(std::forward<T>(data));
}
constexpr Tag tag() const { return static_cast<Tag>(tag_); }
// Get union member by tag.
template <Tag tag>
constexpr const auto& get() const {
using T = nth_type_t<static_cast<size_t>(tag), Ts...>;
DCHECK_EQ(tag, this->tag());
return *reinterpret_cast<const T*>(data_);
}
// Get union member by tag.
template <Tag tag>
constexpr auto& get() {
using T = nth_type_t<static_cast<size_t>(tag), Ts...>;
DCHECK_EQ(tag, this->tag());
return *reinterpret_cast<T*>(data_);
}
// Get union member by type.
template <typename T>
constexpr const auto& get() const {
DCHECK_EQ(static_cast<Tag>(index_of_type_v<T, Ts...>), this->tag());
return *reinterpret_cast<const T*>(data_);
}
// Get union member by type.
template <typename T>
constexpr auto& get() {
DCHECK_EQ(static_cast<Tag>(index_of_type_v<T, Ts...>), this->tag());
return *reinterpret_cast<T*>(data_);
}
private:
alignas(std::max({alignof(Ts)...})) char data_[std::max({sizeof(Ts)...})];
static_assert(sizeof...(Ts) <= std::numeric_limits<uint8_t>::max());
uint8_t tag_;
};
} // namespace base
} // namespace v8
#endif // V8_BASE_DISCRIMINATED_UNION_H_

115
deps/v8/src/base/division-by-constant.cc vendored Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2014 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.
#include "src/base/division-by-constant.h"
#include <stdint.h>
#include <type_traits>
#include "src/base/logging.h"
#include "src/base/macros.h"
namespace v8 {
namespace base {
template <typename T>
requires std::unsigned_integral<T>
MagicNumbersForDivision<T> SignedDivisionByConstant(T d) {
DCHECK(d != static_cast<T>(-1) && d != 0 && d != 1);
const unsigned bits = static_cast<unsigned>(sizeof(T)) * 8;
const T min = (static_cast<T>(1) << (bits - 1));
const bool neg = (min & d) != 0;
const T ad = neg ? (0 - d) : d;
const T t = min + (d >> (bits - 1));
const T anc = t - 1 - t % ad; // Absolute value of nc
unsigned p = bits - 1; // Init. p.
T q1 = min / anc; // Init. q1 = 2**p/|nc|.
T r1 = min - q1 * anc; // Init. r1 = rem(2**p, |nc|).
T q2 = min / ad; // Init. q2 = 2**p/|d|.
T r2 = min - q2 * ad; // Init. r2 = rem(2**p, |d|).
T delta;
do {
p = p + 1;
q1 = 2 * q1; // Update q1 = 2**p/|nc|.
r1 = 2 * r1; // Update r1 = rem(2**p, |nc|).
if (r1 >= anc) { // Must be an unsigned comparison here.
q1 = q1 + 1;
r1 = r1 - anc;
}
q2 = 2 * q2; // Update q2 = 2**p/|d|.
r2 = 2 * r2; // Update r2 = rem(2**p, |d|).
if (r2 >= ad) { // Must be an unsigned comparison here.
q2 = q2 + 1;
r2 = r2 - ad;
}
delta = ad - r2;
} while (q1 < delta || (q1 == delta && r1 == 0));
T mul = q2 + 1;
return MagicNumbersForDivision<T>(neg ? (0 - mul) : mul, p - bits, false);
}
template <class T>
MagicNumbersForDivision<T> UnsignedDivisionByConstant(T d,
unsigned leading_zeros) {
static_assert(std::is_unsigned_v<T>);
DCHECK_NE(d, 0);
const unsigned bits = static_cast<unsigned>(sizeof(T)) * 8;
const T ones = ~static_cast<T>(0) >> leading_zeros;
const T min = static_cast<T>(1) << (bits - 1);
const T max = ~static_cast<T>(0) >> 1;
const T nc = ones - (ones - d) % d;
bool a = false; // Init. "add" indicator.
unsigned p = bits - 1; // Init. p.
T q1 = min / nc; // Init. q1 = 2**p/nc
T r1 = min - q1 * nc; // Init. r1 = rem(2**p,nc)
T q2 = max / d; // Init. q2 = (2**p - 1)/d.
T r2 = max - q2 * d; // Init. r2 = rem(2**p - 1, d).
T delta;
do {
p = p + 1;
if (r1 >= nc - r1) {
q1 = 2 * q1 + 1;
r1 = 2 * r1 - nc;
} else {
q1 = 2 * q1;
r1 = 2 * r1;
}
if (r2 + 1 >= d - r2) {
if (q2 >= max) a = true;
q2 = 2 * q2 + 1;
r2 = 2 * r2 + 1 - d;
} else {
if (q2 >= min) a = true;
q2 = 2 * q2;
r2 = 2 * r2 + 1;
}
delta = d - 1 - r2;
} while (p < bits * 2 && (q1 < delta || (q1 == delta && r1 == 0)));
return MagicNumbersForDivision<T>(q2 + 1, p - bits, a);
}
// -----------------------------------------------------------------------------
// Instantiations.
template struct EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint32_t>;
template struct EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint64_t>;
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint32_t> SignedDivisionByConstant(uint32_t d);
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint64_t> SignedDivisionByConstant(uint64_t d);
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint32_t> UnsignedDivisionByConstant(
uint32_t d, unsigned leading_zeros);
template EXPORT_TEMPLATE_DEFINE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint64_t> UnsignedDivisionByConstant(
uint64_t d, unsigned leading_zeros);
} // namespace base
} // namespace v8

84
deps/v8/src/base/division-by-constant.h vendored Normal file
View File

@ -0,0 +1,84 @@
// Copyright 2014 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_DIVISION_BY_CONSTANT_H_
#define V8_BASE_DIVISION_BY_CONSTANT_H_
#include <stdint.h>
#include <concepts>
#include <tuple>
#include <type_traits>
#include "src/base/base-export.h"
#include "src/base/export-template.h"
namespace v8 {
namespace base {
// ----------------------------------------------------------------------------
// The magic numbers for division via multiplication, see Warren's "Hacker's
// Delight", chapter 10.
template <class T>
struct EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT) MagicNumbersForDivision {
static_assert(std::is_integral_v<T>);
MagicNumbersForDivision(T m, unsigned s, bool a)
: multiplier(m), shift(s), add(a) {}
bool operator==(const MagicNumbersForDivision& rhs) const {
return multiplier == rhs.multiplier && shift == rhs.shift && add == rhs.add;
}
T multiplier;
unsigned shift;
bool add;
};
// Calculate the multiplier and shift for signed division via multiplication.
// The divisor must not be -1, 0 or 1 when interpreted as a signed value.
template <typename T>
requires std::unsigned_integral<T>
EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<T> SignedDivisionByConstant(T d);
template <typename T>
requires std::signed_integral<T>
MagicNumbersForDivision<T> SignedDivisionByConstant(T d) {
using Unsigned = std::make_unsigned_t<T>;
MagicNumbersForDivision<Unsigned> magic =
SignedDivisionByConstant(static_cast<Unsigned>(d));
return {static_cast<T>(magic.multiplier), magic.shift, magic.add};
}
// Calculate the multiplier and shift for unsigned division via multiplication,
// see Warren's "Hacker's Delight", chapter 10. The divisor must not be 0 and
// leading_zeros can be used to speed up the calculation if the given number of
// upper bits of the dividend value are known to be zero.
template <class T>
EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<T> UnsignedDivisionByConstant(
T d, unsigned leading_zeros = 0);
// Explicit instantiation declarations.
extern template struct EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint32_t>;
extern template struct EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint64_t>;
extern template EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint32_t> SignedDivisionByConstant(uint32_t d);
extern template EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint64_t> SignedDivisionByConstant(uint64_t d);
extern template EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint32_t> UnsignedDivisionByConstant(
uint32_t d, unsigned leading_zeros);
extern template EXPORT_TEMPLATE_DECLARE(V8_BASE_EXPORT)
MagicNumbersForDivision<uint64_t> UnsignedDivisionByConstant(
uint64_t d, unsigned leading_zeros);
} // namespace base
} // namespace v8
#endif // V8_BASE_DIVISION_BY_CONSTANT_H_

153
deps/v8/src/base/doubly-threaded-list.h vendored Normal file
View File

@ -0,0 +1,153 @@
// Copyright 2023 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_DOUBLY_THREADED_LIST_H_
#define V8_BASE_DOUBLY_THREADED_LIST_H_
#include "src/base/compiler-specific.h"
#include "src/base/iterator.h"
#include "src/base/logging.h"
namespace v8::base {
template <typename T>
struct DoublyThreadedListTraits {
static T** prev(T t) { return t->prev(); }
static T* next(T t) { return t->next(); }
static bool non_empty(T t) { return t != nullptr; }
};
// `DoublyThreadedList` is an intrusive doubly-linked list that threads through
// its nodes, somewhat like `v8::base::ThreadedList`.
//
// Of interest is the fact that instead of having regular next/prev pointers,
// nodes have a regular "next" pointer, but their "prev" pointer contains the
// address of the "next" of the previous element. This way, removing an element
// doesn't require special treatment for the head of the list, and does not
// even require to know the head of the list.
template <class T, class DTLTraits = DoublyThreadedListTraits<T>>
class DoublyThreadedList {
public:
// Since C++17, it is possible to have a sentinel end-iterator that is not an
// iterator itself.
class end_iterator {};
class iterator : public base::iterator<std::forward_iterator_tag, T> {
public:
explicit iterator(T head) : curr_(head) {}
T operator*() { return curr_; }
iterator& operator++() {
DCHECK(DTLTraits::non_empty(curr_));
curr_ = *DTLTraits::next(curr_);
return *this;
}
iterator operator++(int) {
DCHECK(DTLTraits::non_empty(curr_));
iterator tmp(*this);
operator++();
return tmp;
}
bool operator==(end_iterator) { return !DTLTraits::non_empty(curr_); }
bool operator!=(end_iterator) { return DTLTraits::non_empty(curr_); }
private:
friend DoublyThreadedList;
T curr_;
};
// Removes `x` from the list. Iterators that are currently on `x` are
// invalidated. To remove while iterating, use RemoveAt.
static void Remove(T x) {
if (*DTLTraits::prev(x) == nullptr) {
DCHECK(empty(*DTLTraits::next(x)));
// {x} already removed from the list.
return;
}
T** prev = DTLTraits::prev(x);
T* next = DTLTraits::next(x);
**prev = *next;
if (DTLTraits::non_empty(*next)) *DTLTraits::prev(*next) = *prev;
*DTLTraits::prev(x) = nullptr;
*DTLTraits::next(x) = {};
}
DoublyThreadedList() = default;
// Defining move constructor so that when resizing container, the prev pointer
// of the next(head_) doesn't point to the old head_ but rather to the new
// one.
DoublyThreadedList(DoublyThreadedList&& other) V8_NOEXCEPT {
head_ = other.head_;
if (DTLTraits::non_empty(head_)) {
*DTLTraits::prev(head_) = &head_;
}
other.head_ = {};
}
// Add `x` at the beginning of the list. `x` will not be visible to any
// existing iterator. Does not invalidate any existing iterator.
void PushFront(T x) {
DCHECK(empty(*DTLTraits::next(x)));
DCHECK_EQ(*DTLTraits::prev(x), nullptr);
*DTLTraits::next(x) = head_;
*DTLTraits::prev(x) = &head_;
if (DTLTraits::non_empty(head_)) {
*DTLTraits::prev(head_) = DTLTraits::next(x);
}
head_ = x;
}
T Front() const {
DCHECK(!empty());
return *begin();
}
void PopFront() {
DCHECK(!empty());
Remove(Front());
}
bool empty() const { return !DTLTraits::non_empty(head_); }
iterator begin() const { return iterator{head_}; }
end_iterator end() const { return end_iterator{}; }
// Removes the element at `it`, and make `it` point to the next element.
// Iterators on the same element as `it` are invalidated. Other iterators are
// not affected.
iterator RemoveAt(iterator& it) {
DCHECK(DTLTraits::non_empty(it.curr_));
T curr = *it;
T next = *DTLTraits::next(curr);
Remove(curr);
return iterator{next};
}
bool Contains(T needle) const {
const bool in_use = DTLTraits::in_use(needle);
DCHECK_EQ(in_use, ContainsSlow(needle));
return in_use;
}
bool ContainsSlow(T needle) const {
for (T element : *this) {
if (element == needle) {
return true;
}
}
return false;
}
private:
static bool empty(T x) { return !DTLTraits::non_empty(x); }
T head_{};
};
} // namespace v8::base
#endif // V8_BASE_DOUBLY_THREADED_LIST_H_

View File

@ -0,0 +1,199 @@
// Copyright 2021 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.
#include "src/base/emulated-virtual-address-subspace.h"
#include "src/base/bits.h"
#include "src/base/platform/platform.h"
namespace v8 {
namespace base {
EmulatedVirtualAddressSubspace::EmulatedVirtualAddressSubspace(
VirtualAddressSpace* parent_space, Address base, size_t mapped_size,
size_t total_size)
: VirtualAddressSpace(parent_space->page_size(),
parent_space->allocation_granularity(), base,
total_size, parent_space->max_page_permissions()),
mapped_size_(mapped_size),
parent_space_(parent_space),
region_allocator_(base, mapped_size, parent_space_->page_size()) {
// For simplicity, we currently require both the mapped and total size to be
// a power of two. This simplifies some things later on, for example, random
// addresses can be generated with a simply bitmask, and will then be inside
// the unmapped space with a probability >= 50% (mapped size == unmapped
// size) or never (mapped size == total size).
DCHECK(base::bits::IsPowerOfTwo(mapped_size));
DCHECK(base::bits::IsPowerOfTwo(total_size));
}
EmulatedVirtualAddressSubspace::~EmulatedVirtualAddressSubspace() {
parent_space_->FreePages(base(), mapped_size_);
}
void EmulatedVirtualAddressSubspace::SetRandomSeed(int64_t seed) {
MutexGuard guard(&mutex_);
rng_.SetSeed(seed);
}
Address EmulatedVirtualAddressSubspace::RandomPageAddress() {
MutexGuard guard(&mutex_);
Address addr = base() + (static_cast<uint64_t>(rng_.NextInt64()) % size());
return RoundDown(addr, allocation_granularity());
}
Address EmulatedVirtualAddressSubspace::AllocatePages(
Address hint, size_t size, size_t alignment, PagePermissions permissions) {
if (hint == kNoHint || MappedRegionContains(hint, size)) {
MutexGuard guard(&mutex_);
// Attempt to find a region in the mapped region.
Address address = region_allocator_.AllocateRegion(hint, size, alignment);
if (address != RegionAllocator::kAllocationFailure) {
// Success. Only need to adjust the page permissions.
if (parent_space_->SetPagePermissions(address, size, permissions)) {
return address;
}
// Probably ran out of memory, but still try to allocate in the unmapped
// space.
CHECK_EQ(size, region_allocator_.FreeRegion(address));
}
}
// No luck or hint is outside of the mapped region. Try to allocate pages in
// the unmapped space using page allocation hints instead.
if (!IsUsableSizeForUnmappedRegion(size)) return kNullAddress;
static constexpr int kMaxAttempts = 10;
for (int i = 0; i < kMaxAttempts; i++) {
// If an unmapped region exists, it must cover at least 50% of the whole
// space (unmapped + mapped region). Since we limit the size of allocation
// to 50% of the unmapped region (see IsUsableSizeForUnmappedRegion), a
// random page address has at least a 25% chance of being a usable base. As
// such, this loop should usually terminate quickly.
DCHECK_GE(unmapped_size(), mapped_size());
while (!UnmappedRegionContains(hint, size)) {
hint = RandomPageAddress();
}
hint = RoundDown(hint, alignment);
const Address result =
parent_space_->AllocatePages(hint, size, alignment, permissions);
if (UnmappedRegionContains(result, size)) {
return result;
} else if (result) {
parent_space_->FreePages(result, size);
}
// Retry at a different address.
hint = RandomPageAddress();
}
return kNullAddress;
}
void EmulatedVirtualAddressSubspace::FreePages(Address address, size_t size) {
if (MappedRegionContains(address, size)) {
MutexGuard guard(&mutex_);
CHECK_EQ(size, region_allocator_.FreeRegion(address));
CHECK(parent_space_->DecommitPages(address, size));
} else {
DCHECK(UnmappedRegionContains(address, size));
parent_space_->FreePages(address, size);
}
}
Address EmulatedVirtualAddressSubspace::AllocateSharedPages(
Address hint, size_t size, PagePermissions permissions,
PlatformSharedMemoryHandle handle, uint64_t offset) {
// Can only allocate shared pages in the unmapped region.
if (!IsUsableSizeForUnmappedRegion(size)) return kNullAddress;
static constexpr int kMaxAttempts = 10;
for (int i = 0; i < kMaxAttempts; i++) {
// See AllocatePages() for why this loop usually terminates quickly.
DCHECK_GE(unmapped_size(), mapped_size());
while (!UnmappedRegionContains(hint, size)) {
hint = RandomPageAddress();
}
Address region = parent_space_->AllocateSharedPages(hint, size, permissions,
handle, offset);
if (UnmappedRegionContains(region, size)) {
return region;
} else if (region) {
parent_space_->FreeSharedPages(region, size);
}
hint = RandomPageAddress();
}
return kNullAddress;
}
void EmulatedVirtualAddressSubspace::FreeSharedPages(Address address,
size_t size) {
DCHECK(UnmappedRegionContains(address, size));
parent_space_->FreeSharedPages(address, size);
}
bool EmulatedVirtualAddressSubspace::SetPagePermissions(
Address address, size_t size, PagePermissions permissions) {
DCHECK(Contains(address, size));
return parent_space_->SetPagePermissions(address, size, permissions);
}
bool EmulatedVirtualAddressSubspace::AllocateGuardRegion(Address address,
size_t size) {
if (MappedRegionContains(address, size)) {
MutexGuard guard(&mutex_);
return region_allocator_.AllocateRegionAt(address, size);
}
if (!UnmappedRegionContains(address, size)) return false;
return parent_space_->AllocateGuardRegion(address, size);
}
void EmulatedVirtualAddressSubspace::FreeGuardRegion(Address address,
size_t size) {
if (MappedRegionContains(address, size)) {
MutexGuard guard(&mutex_);
CHECK_EQ(size, region_allocator_.FreeRegion(address));
} else {
DCHECK(UnmappedRegionContains(address, size));
parent_space_->FreeGuardRegion(address, size);
}
}
bool EmulatedVirtualAddressSubspace::CanAllocateSubspaces() {
// This is not supported, mostly because it's not (yet) needed in practice.
return false;
}
std::unique_ptr<v8::VirtualAddressSpace>
EmulatedVirtualAddressSubspace::AllocateSubspace(
Address hint, size_t size, size_t alignment,
PagePermissions max_page_permissions) {
UNREACHABLE();
}
bool EmulatedVirtualAddressSubspace::RecommitPages(
Address address, size_t size, PagePermissions permissions) {
DCHECK(Contains(address, size));
return parent_space_->RecommitPages(address, size, permissions);
}
bool EmulatedVirtualAddressSubspace::DiscardSystemPages(Address address,
size_t size) {
DCHECK(Contains(address, size));
return parent_space_->DiscardSystemPages(address, size);
}
bool EmulatedVirtualAddressSubspace::DecommitPages(Address address,
size_t size) {
DCHECK(Contains(address, size));
return parent_space_->DecommitPages(address, size);
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,134 @@
// Copyright 2021 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_EMULATED_VIRTUAL_ADDRESS_SUBSPACE_H_
#define V8_BASE_EMULATED_VIRTUAL_ADDRESS_SUBSPACE_H_
#include "include/v8-platform.h"
#include "src/base/base-export.h"
#include "src/base/compiler-specific.h"
#include "src/base/platform/mutex.h"
#include "src/base/region-allocator.h"
#include "src/base/virtual-address-space.h"
namespace v8 {
namespace base {
/**
* Emulates a virtual address subspace.
*
* This class is (optionally) backed by a page allocation and emulates a virtual
* address space that is potentially larger than that mapping. It generally
* first attempts to satisfy page allocation requests from its backing mapping,
* but will also attempt to obtain new page mappings inside the unmapped space
* through page allocation hints if necessary.
*
* Caveat: an emulated subspace violates the invariant that page allocations in
* an address space will never end up inside a child space and so does not
* provide the same security gurarantees.
*/
class V8_BASE_EXPORT EmulatedVirtualAddressSubspace final
: public NON_EXPORTED_BASE(::v8::VirtualAddressSpace) {
public:
// Construct an emulated virtual address subspace of the specified total size,
// potentially backed by a page allocation from the parent space. The newly
// created instance takes ownership of the page allocation (if any) and frees
// it during destruction.
EmulatedVirtualAddressSubspace(v8::VirtualAddressSpace* parent_space,
Address base, size_t mapped_size,
size_t total_size);
~EmulatedVirtualAddressSubspace() override;
void SetRandomSeed(int64_t seed) override;
Address RandomPageAddress() override;
Address AllocatePages(Address hint, size_t size, size_t alignment,
PagePermissions permissions) override;
void FreePages(Address address, size_t size) override;
Address AllocateSharedPages(Address hint, size_t size,
PagePermissions permissions,
PlatformSharedMemoryHandle handle,
uint64_t offset) override;
void FreeSharedPages(Address address, size_t size) override;
bool SetPagePermissions(Address address, size_t size,
PagePermissions permissions) override;
bool AllocateGuardRegion(Address address, size_t size) override;
void FreeGuardRegion(Address address, size_t size) override;
bool CanAllocateSubspaces() override;
std::unique_ptr<v8::VirtualAddressSpace> AllocateSubspace(
Address hint, size_t size, size_t alignment,
PagePermissions max_page_permissions) override;
bool RecommitPages(Address address, size_t size,
PagePermissions permissions) override;
bool DiscardSystemPages(Address address, size_t size) override;
bool DecommitPages(Address address, size_t size) override;
private:
size_t mapped_size() const { return mapped_size_; }
size_t unmapped_size() const { return size() - mapped_size_; }
Address mapped_base() const { return base(); }
Address unmapped_base() const { return base() + mapped_size_; }
bool Contains(Address outer_start, size_t outer_size, Address inner_start,
size_t inner_size) const {
return (inner_start >= outer_start) &&
((inner_start + inner_size) <= (outer_start + outer_size));
}
bool Contains(Address addr, size_t length) const {
return Contains(base(), size(), addr, length);
}
bool MappedRegionContains(Address addr, size_t length) const {
return Contains(mapped_base(), mapped_size(), addr, length);
}
bool UnmappedRegionContains(Address addr, size_t length) const {
return Contains(unmapped_base(), unmapped_size(), addr, length);
}
// Helper function to define a limit for the size of allocations in the
// unmapped region. This limit makes it possible to estimate the expected
// runtime of some loops in the Allocate methods.
bool IsUsableSizeForUnmappedRegion(size_t size) const {
return size <= (unmapped_size() / 2);
}
// Size of the mapped region located at the beginning of this address space.
const size_t mapped_size_;
// Pointer to the parent space from which the backing pages were allocated.
// Must be kept alive by the owner of this instance.
v8::VirtualAddressSpace* parent_space_;
// Mutex guarding the non-threadsafe RegionAllocator and
// RandomNumberGenerator.
Mutex mutex_;
// RegionAllocator to manage the page allocation and divide it into further
// regions as necessary.
RegionAllocator region_allocator_;
// Random number generator for generating random addresses.
RandomNumberGenerator rng_;
};
} // namespace base
} // namespace v8
#endif // V8_BASE_EMULATED_VIRTUAL_ADDRESS_SUBSPACE_H_

124
deps/v8/src/base/enum-set.h vendored Normal file
View File

@ -0,0 +1,124 @@
// Copyright 2019 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_ENUM_SET_H_
#define V8_BASE_ENUM_SET_H_
#include <ostream>
#include <type_traits>
#include "src/base/bits.h"
#include "src/base/logging.h"
namespace v8 {
namespace base {
// A poor man's version of STL's bitset: A bit set of enums E (without explicit
// values), fitting into an integral type T.
template <class E, class T = int>
class EnumSet {
static_assert(std::is_enum<E>::value, "EnumSet can only be used with enums");
public:
using StorageType = T;
constexpr EnumSet() = default;
constexpr EnumSet(std::initializer_list<E> init) {
T bits = 0;
for (E e : init) bits |= Mask(e);
bits_ = bits;
}
constexpr bool empty() const { return bits_ == 0; }
constexpr bool contains(E element) const {
return (bits_ & Mask(element)) != 0;
}
constexpr bool contains_all(EnumSet set) const {
return (bits_ & set.bits_) == set.bits_;
}
constexpr bool contains_any(EnumSet set) const {
return (bits_ & set.bits_) != 0;
}
constexpr bool contains_only(E element) const {
return bits_ == Mask(element);
}
constexpr bool is_subset_of(EnumSet set) const {
return (bits_ & set.bits_) == bits_;
}
constexpr void Add(E element) { bits_ |= Mask(element); }
constexpr void Add(EnumSet set) { bits_ |= set.bits_; }
constexpr void Remove(E element) { bits_ &= ~Mask(element); }
constexpr void Remove(EnumSet set) { bits_ &= ~set.bits_; }
constexpr void RemoveAll() { bits_ = 0; }
constexpr void Intersect(EnumSet set) { bits_ &= set.bits_; }
constexpr T ToIntegral() const { return bits_; }
constexpr EnumSet operator~() const { return EnumSet(~bits_); }
constexpr bool operator==(EnumSet set) const { return bits_ == set.bits_; }
constexpr bool operator!=(EnumSet set) const { return bits_ != set.bits_; }
constexpr EnumSet operator|(EnumSet set) const {
return EnumSet(bits_ | set.bits_);
}
constexpr EnumSet operator&(EnumSet set) const {
return EnumSet(bits_ & set.bits_);
}
constexpr EnumSet operator-(EnumSet set) const {
return EnumSet(bits_ & ~set.bits_);
}
EnumSet& operator|=(EnumSet set) { return *this = *this | set; }
EnumSet& operator&=(EnumSet set) { return *this = *this & set; }
EnumSet& operator-=(EnumSet set) { return *this = *this - set; }
constexpr EnumSet operator|(E element) const {
return EnumSet(bits_ | Mask(element));
}
constexpr EnumSet operator&(E element) const {
return EnumSet(bits_ & Mask(element));
}
constexpr EnumSet operator-(E element) const {
return EnumSet(bits_ & ~Mask(element));
}
EnumSet& operator|=(E element) { return *this = *this | element; }
EnumSet& operator&=(E element) { return *this = *this & element; }
EnumSet& operator-=(E element) { return *this = *this - element; }
static constexpr EnumSet FromIntegral(T bits) { return EnumSet{bits}; }
private:
explicit constexpr EnumSet(T bits) : bits_(bits) {}
static constexpr T Mask(E element) {
DCHECK_GT(sizeof(T) * 8, static_cast<size_t>(element));
return T{1} << static_cast<typename std::underlying_type<E>::type>(element);
}
T bits_ = 0;
};
template <typename E, typename T>
std::ostream& operator<<(std::ostream& os, EnumSet<E, T> set) {
os << "{";
bool first = true;
while (!set.empty()) {
if (!first) os << ", ";
first = false;
T bits = set.ToIntegral();
E element = static_cast<E>(bits::CountTrailingZerosNonZero(bits));
os << element;
set.Remove(element);
}
os << "}";
return os;
}
} // namespace base
} // namespace v8
#endif // V8_BASE_ENUM_SET_H_

163
deps/v8/src/base/export-template.h vendored Normal file
View File

@ -0,0 +1,163 @@
// Copyright 2017 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_EXPORT_TEMPLATE_H_
#define V8_BASE_EXPORT_TEMPLATE_H_
// Synopsis
//
// This header provides macros for using FOO_EXPORT macros with explicit
// template instantiation declarations and definitions.
// Generally, the FOO_EXPORT macros are used at declarations,
// and GCC requires them to be used at explicit instantiation declarations,
// but MSVC requires __declspec(dllexport) to be used at the explicit
// instantiation definitions instead.
// Usage
//
// In a header file, write:
//
// extern template class EXPORT_TEMPLATE_DECLARE(FOO_EXPORT) foo<bar>;
//
// In a source file, write:
//
// template class EXPORT_TEMPLATE_DEFINE(FOO_EXPORT) foo<bar>;
// Implementation notes
//
// The implementation of this header uses some subtle macro semantics to
// detect what the provided FOO_EXPORT value was defined as and then
// to dispatch to appropriate macro definitions. Unfortunately,
// MSVC's C preprocessor is rather non-compliant and requires special
// care to make it work.
//
// Issue 1.
//
// #define F(x)
// F()
//
// MSVC emits warning C4003 ("not enough actual parameters for macro
// 'F'), even though it's a valid macro invocation. This affects the
// macros below that take just an "export" parameter, because export
// may be empty.
//
// As a workaround, we can add a dummy parameter and arguments:
//
// #define F(x,_)
// F(,)
//
// Issue 2.
//
// #define F(x) G##x
// #define Gj() ok
// F(j())
//
// The correct replacement for "F(j())" is "ok", but MSVC replaces it
// with "Gj()". As a workaround, we can pass the result to an
// identity macro to force MSVC to look for replacements again. (This
// is why EXPORT_TEMPLATE_STYLE_3 exists.)
#define EXPORT_TEMPLATE_DECLARE(export) \
EXPORT_TEMPLATE_INVOKE(DECLARE, EXPORT_TEMPLATE_STYLE(export, ), export)
#define EXPORT_TEMPLATE_DEFINE(export) \
EXPORT_TEMPLATE_INVOKE(DEFINE, EXPORT_TEMPLATE_STYLE(export, ), export)
// INVOKE is an internal helper macro to perform parameter replacements
// and token pasting to chain invoke another macro. E.g.,
// EXPORT_TEMPLATE_INVOKE(DECLARE, DEFAULT, FOO_EXPORT)
// will export to call
// EXPORT_TEMPLATE_DECLARE_DEFAULT(FOO_EXPORT, )
// (but with FOO_EXPORT expanded too).
#define EXPORT_TEMPLATE_INVOKE(which, style, export) \
EXPORT_TEMPLATE_INVOKE_2(which, style, export)
#define EXPORT_TEMPLATE_INVOKE_2(which, style, export) \
EXPORT_TEMPLATE_##which##_##style(export, )
// Default style is to apply the FOO_EXPORT macro at declaration sites.
#define EXPORT_TEMPLATE_DECLARE_DEFAULT(export, _) export
#define EXPORT_TEMPLATE_DEFINE_DEFAULT(export, _)
// The "MSVC hack" style is used when FOO_EXPORT is defined
// as __declspec(dllexport), which MSVC requires to be used at
// definition sites instead.
#define EXPORT_TEMPLATE_DECLARE_MSVC_HACK(export, _)
#define EXPORT_TEMPLATE_DEFINE_MSVC_HACK(export, _) export
// EXPORT_TEMPLATE_STYLE is an internal helper macro that identifies which
// export style needs to be used for the provided FOO_EXPORT macro definition.
// "", "__attribute__(...)", and "__declspec(dllimport)" are mapped
// to "DEFAULT"; while "__declspec(dllexport)" is mapped to "MSVC_HACK".
//
// It's implemented with token pasting to transform the __attribute__ and
// __declspec annotations into macro invocations. E.g., if FOO_EXPORT is
// defined as "__declspec(dllimport)", it undergoes the following sequence of
// macro substitutions:
// EXPORT_TEMPLATE_STYLE(FOO_EXPORT, )
// EXPORT_TEMPLATE_STYLE_2(__declspec(dllimport), )
// EXPORT_TEMPLATE_STYLE_3(EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport))
// EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport)
// EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport
// DEFAULT
#define EXPORT_TEMPLATE_STYLE(export, _) EXPORT_TEMPLATE_STYLE_2(export, )
#define EXPORT_TEMPLATE_STYLE_2(export, _) \
EXPORT_TEMPLATE_STYLE_3( \
EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##export)
#define EXPORT_TEMPLATE_STYLE_3(style) style
// Internal helper macros for EXPORT_TEMPLATE_STYLE.
//
// XXX: C++ reserves all identifiers containing "__" for the implementation,
// but "__attribute__" and "__declspec" already contain "__" and the token-paste
// operator can only add characters; not remove them. To minimize the risk of
// conflict with implementations, we include "foj3FJo5StF0OvIzl7oMxA" (a random
// 128-bit string, encoded in Base64) in the macro name.
#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA DEFAULT
#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__attribute__(...) \
DEFAULT
#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__declspec(arg) \
EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_##arg
// Internal helper macros for EXPORT_TEMPLATE_STYLE.
#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllexport MSVC_HACK
#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport DEFAULT
// Sanity checks.
//
// EXPORT_TEMPLATE_TEST uses the same macro invocation pattern as
// EXPORT_TEMPLATE_DECLARE and EXPORT_TEMPLATE_DEFINE do to check that they're
// working correctly. When they're working correctly, the sequence of macro
// replacements should go something like:
//
// EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
//
// static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT,
// EXPORT_TEMPLATE_STYLE(__declspec(dllimport), ),
// __declspec(dllimport)), "__declspec(dllimport)");
//
// static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT,
// DEFAULT, __declspec(dllimport)), "__declspec(dllimport)");
//
// static_assert(EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(
// __declspec(dllimport)), "__declspec(dllimport)");
//
// static_assert(true, "__declspec(dllimport)");
//
// When they're not working correctly, a syntax error should occur instead.
#define EXPORT_TEMPLATE_TEST(want, export) \
static_assert(EXPORT_TEMPLATE_INVOKE( \
TEST_##want, EXPORT_TEMPLATE_STYLE(export, ), export), \
#export)
#define EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(...) true
#define EXPORT_TEMPLATE_TEST_MSVC_HACK_MSVC_HACK(...) true
EXPORT_TEMPLATE_TEST(DEFAULT, );
EXPORT_TEMPLATE_TEST(DEFAULT, __attribute__((visibility("default"))));
EXPORT_TEMPLATE_TEST(MSVC_HACK, __declspec(dllexport));
EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
#undef EXPORT_TEMPLATE_TEST
#undef EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT
#undef EXPORT_TEMPLATE_TEST_MSVC_HACK_MSVC_HACK
#endif // V8_BASE_EXPORT_TEMPLATE_H_

30
deps/v8/src/base/file-utils.cc vendored Normal file
View File

@ -0,0 +1,30 @@
// Copyright 2016 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.
#include "src/base/file-utils.h"
#include <stdlib.h>
#include <string.h>
#include "src/base/platform/platform.h"
namespace v8 {
namespace base {
std::unique_ptr<char[]> RelativePath(const char* exec_path, const char* name) {
DCHECK(exec_path);
size_t basename_start = strlen(exec_path);
while (basename_start > 0 &&
!OS::isDirectorySeparator(exec_path[basename_start - 1])) {
--basename_start;
}
size_t name_length = strlen(name);
auto buffer = std::make_unique<char[]>(basename_start + name_length + 1);
if (basename_start > 0) memcpy(buffer.get(), exec_path, basename_start);
memcpy(buffer.get() + basename_start, name, name_length);
return buffer;
}
} // namespace base
} // namespace v8

23
deps/v8/src/base/file-utils.h vendored Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2016 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_FILE_UTILS_H_
#define V8_BASE_FILE_UTILS_H_
#include <memory>
#include "src/base/base-export.h"
namespace v8 {
namespace base {
// Helper functions to manipulate file paths.
V8_BASE_EXPORT
std::unique_ptr<char[]> RelativePath(const char* exec_path, const char* name);
} // namespace base
} // namespace v8
#endif // V8_BASE_FILE_UTILS_H_

138
deps/v8/src/base/flags.h vendored Normal file
View File

@ -0,0 +1,138 @@
// Copyright 2014 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_FLAGS_H_
#define V8_BASE_FLAGS_H_
#include <cstddef>
#include "src/base/compiler-specific.h"
namespace v8 {
namespace base {
// The Flags class provides a type-safe way of storing OR-combinations of enum
// values.
//
// The traditional C++ approach for storing OR-combinations of enum values is to
// use an int or unsigned int variable. The inconvenience with this approach is
// that there's no type checking at all; any enum value can be OR'd with any
// other enum value and passed on to a function that takes an int or unsigned
// int.
template <typename EnumT, typename BitfieldT = int,
typename BitfieldStorageT = BitfieldT>
class Flags final {
public:
static_assert(sizeof(BitfieldStorageT) >= sizeof(BitfieldT));
using flag_type = EnumT;
using mask_type = BitfieldT;
constexpr Flags() : mask_(0) {}
constexpr Flags(flag_type flag) // NOLINT(runtime/explicit)
: mask_(static_cast<mask_type>(flag)) {}
constexpr explicit Flags(mask_type mask)
: mask_(static_cast<mask_type>(mask)) {}
constexpr bool operator==(flag_type flag) const {
return mask_ == static_cast<mask_type>(flag);
}
constexpr bool operator!=(flag_type flag) const {
return mask_ != static_cast<mask_type>(flag);
}
Flags& operator&=(const Flags& flags) {
mask_ &= flags.mask_;
return *this;
}
Flags& operator|=(const Flags& flags) {
mask_ |= flags.mask_;
return *this;
}
Flags& operator^=(const Flags& flags) {
mask_ ^= flags.mask_;
return *this;
}
constexpr Flags operator&(const Flags& flags) const {
return Flags(mask_ & flags.mask_);
}
constexpr Flags operator|(const Flags& flags) const {
return Flags(mask_ | flags.mask_);
}
constexpr Flags operator^(const Flags& flags) const {
return Flags(mask_ ^ flags.mask_);
}
Flags& operator&=(flag_type flag) { return operator&=(Flags(flag)); }
Flags& operator|=(flag_type flag) { return operator|=(Flags(flag)); }
Flags& operator^=(flag_type flag) { return operator^=(Flags(flag)); }
// Sets or clears given flag.
Flags& set(flag_type flag, bool value) {
if (value) return operator|=(Flags(flag));
return operator&=(~Flags(flag));
}
constexpr Flags operator&(flag_type flag) const {
return operator&(Flags(flag));
}
constexpr Flags operator|(flag_type flag) const {
return operator|(Flags(flag));
}
constexpr Flags operator^(flag_type flag) const {
return operator^(Flags(flag));
}
constexpr Flags operator~() const { return Flags(~mask_); }
constexpr operator mask_type() const { return mask_; }
constexpr bool operator!() const { return !mask_; }
Flags without(flag_type flag) const { return *this & (~Flags(flag)); }
friend size_t hash_value(const Flags& flags) { return flags.mask_; }
private:
BitfieldStorageT mask_;
};
#define DEFINE_OPERATORS_FOR_FLAGS(Type) \
V8_ALLOW_UNUSED V8_WARN_UNUSED_RESULT inline constexpr Type operator&( \
Type::flag_type lhs, Type::flag_type rhs) { \
return Type(lhs) & rhs; \
} \
V8_ALLOW_UNUSED V8_WARN_UNUSED_RESULT inline constexpr Type operator&( \
Type::flag_type lhs, const Type& rhs) { \
return rhs & lhs; \
} \
V8_ALLOW_UNUSED inline void operator&(Type::flag_type lhs, \
Type::mask_type rhs) {} \
V8_ALLOW_UNUSED V8_WARN_UNUSED_RESULT inline constexpr Type operator|( \
Type::flag_type lhs, Type::flag_type rhs) { \
return Type(lhs) | rhs; \
} \
V8_ALLOW_UNUSED V8_WARN_UNUSED_RESULT inline constexpr Type operator|( \
Type::flag_type lhs, const Type& rhs) { \
return rhs | lhs; \
} \
V8_ALLOW_UNUSED inline void operator|(Type::flag_type lhs, \
Type::mask_type rhs) {} \
V8_ALLOW_UNUSED V8_WARN_UNUSED_RESULT inline constexpr Type operator^( \
Type::flag_type lhs, Type::flag_type rhs) { \
return Type(lhs) ^ rhs; \
} \
V8_ALLOW_UNUSED V8_WARN_UNUSED_RESULT inline constexpr Type operator^( \
Type::flag_type lhs, const Type& rhs) { \
return rhs ^ lhs; \
} \
V8_ALLOW_UNUSED inline void operator^(Type::flag_type lhs, \
Type::mask_type rhs) {} \
V8_ALLOW_UNUSED inline constexpr Type operator~(Type::flag_type val) { \
return ~Type(val); \
}
} // namespace base
} // namespace v8
#endif // V8_BASE_FLAGS_H_

101
deps/v8/src/base/fpu.cc vendored Normal file
View File

@ -0,0 +1,101 @@
// Copyright 2025 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.
#include "src/base/fpu.h"
#include <float.h>
#include "include/v8config.h"
namespace v8::base {
#if defined(V8_HOST_ARCH_X64) || defined(V8_HOST_ARCH_IA32)
#if defined(V8_CC_GNU)
namespace {
// Two bits on Intel CPUs, for FTZ (flush denormalized results to zero) and DAZ
// (flush denormalized inputs to zero).
constexpr int kFlushDenormToZeroBits = 0x8040;
int GetCSR() {
int result;
asm volatile("stmxcsr %0" : "=m"(result));
return result;
}
void SetCSR(int a) {
int temp = a;
asm volatile("ldmxcsr %0" : : "m"(temp));
}
} // namespace
bool FPU::GetFlushDenormals() {
int csr = GetCSR();
return csr & kFlushDenormToZeroBits;
}
void FPU::SetFlushDenormals(bool value) {
int old_csr = GetCSR();
int new_csr = value ? old_csr | kFlushDenormToZeroBits
: old_csr & ~kFlushDenormToZeroBits;
SetCSR(new_csr);
}
#elif defined(V8_CC_MSVC)
bool FPU::GetFlushDenormals() {
unsigned int csr;
_controlfp_s(&csr, 0, 0);
return (csr & _MCW_DN) == _DN_FLUSH;
}
void FPU::SetFlushDenormals(bool value) {
unsigned int csr;
_controlfp_s(&csr, value ? _DN_FLUSH : _DN_SAVE, _MCW_DN);
}
#else
#error "Unsupported compiler"
#endif
#elif defined(V8_HOST_ARCH_ARM64) || defined(V8_HOST_ARCH_ARM)
namespace {
// Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes
// denormals to 0.
constexpr int kFlushDenormToZeroBit = (1 << 24);
int GetStatusWord() {
int result;
#if defined(V8_HOST_ARCH_ARM64)
asm volatile("mrs %x[result], FPCR" : [result] "=r"(result));
#else
asm volatile("vmrs %[result], FPSCR" : [result] "=r"(result));
#endif
return result;
}
void SetStatusWord(int a) {
#if defined(V8_HOST_ARCH_ARM64)
asm volatile("msr FPCR, %x[src]" : : [src] "r"(a));
#else
asm volatile("vmsr FPSCR, %[src]" : : [src] "r"(a));
#endif
}
} // namespace
bool FPU::GetFlushDenormals() {
int csr = GetStatusWord();
return csr & kFlushDenormToZeroBit;
}
void FPU::SetFlushDenormals(bool value) {
int old_csr = GetStatusWord();
int new_csr = value ? old_csr | kFlushDenormToZeroBit
: old_csr & ~kFlushDenormToZeroBit;
SetStatusWord(new_csr);
}
#else
bool FPU::GetFlushDenormals() { return false; }
void FPU::SetFlushDenormals(bool value) {}
#endif
} // namespace v8::base

29
deps/v8/src/base/fpu.h vendored Normal file
View File

@ -0,0 +1,29 @@
// Copyright 2025 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_FPU_H_
#define V8_BASE_FPU_H_
#include "src/base/base-export.h"
namespace v8::base {
class FPU final {
public:
V8_BASE_EXPORT static bool GetFlushDenormals();
V8_BASE_EXPORT static void SetFlushDenormals(bool);
};
class V8_BASE_EXPORT FlushDenormalsScope final {
public:
explicit FlushDenormalsScope(bool value)
: old_flush_state_(FPU::GetFlushDenormals()) {
FPU::SetFlushDenormals(value);
}
~FlushDenormalsScope() { FPU::SetFlushDenormals(old_flush_state_); }
private:
bool old_flush_state_;
};
} // namespace v8::base
#endif // V8_BASE_FPU_H_

32
deps/v8/src/base/free_deleter.h vendored Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Slightly adapted for inclusion in V8.
// Copyright 2016 the V8 project authors. All rights reserved.
#ifndef V8_BASE_FREE_DELETER_H_
#define V8_BASE_FREE_DELETER_H_
#include <stdlib.h>
#include <memory>
#include "src/base/platform/memory.h"
namespace v8 {
namespace base {
// Function object which invokes 'free' on its parameter, which must be
// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr:
//
// std::unique_ptr<int, base::FreeDeleter> foo_ptr(
// static_cast<int*>(malloc(sizeof(int))));
struct FreeDeleter {
inline void operator()(void* ptr) const { base::Free(ptr); }
};
} // namespace base
} // namespace v8
#endif // V8_BASE_FREE_DELETER_H_

371
deps/v8/src/base/hashing.h vendored Normal file
View File

@ -0,0 +1,371 @@
// Copyright 2014 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_HASHING_H_
#define V8_BASE_HASHING_H_
#include <stddef.h>
#include <stdint.h>
#include <cstddef>
#include <cstring>
#include <functional>
#include <type_traits>
#include <utility>
#include "src/base/base-export.h"
#include "src/base/bits.h"
#include "src/base/macros.h"
namespace v8::base {
// base::hash is an implementation of the hash function object specified by
// C++11. It was designed to be compatible with std::hash (in C++11) and
// boost:hash (which in turn is based on the hash function object specified by
// the Draft Technical Report on C++ Library Extensions (TR1)).
//
// base::hash is implemented by calling either the hash_value function or the
// hash_value member function. In the first case, the namespace is not specified
// so that it can detect overloads via argument dependent lookup. So if there is
// a free function hash_value in the same namespace as a custom type, it will
// get called.
//
// If users are asked to implement a hash function for their own types with no
// guidance, they generally write bad hash functions. Instead, we provide a
// base::Hasher class to pass hash-relevant member variables into, in order to
// define a decent hash function.
//
// Consider the following example:
//
// namespace v8 {
// namespace bar {
// struct Coordinate {
// int val;
// size_t hash_value() const { return hash_value(val); }
// };
// struct Point {
// Coordinate x;
// Coordinate y;
// };
// size_t hash_value(Point const& p) {
// return base::Hasher::Combine(p.x, p.y);
// }
// }
//
// namespace foo {
// void DoSomeWork(bar::Point const& p) {
// base::hash<bar::Point> h;
// ...
// size_t hash = h(p); // calls bar::hash_value(Point const&), which
// // calls p.x.hash_value() and p.y.hash_value().
// ...
// }
// }
// }
//
// This header also provides implementations of hash_value for basic types.
//
// Based on the "Hashing User-Defined Types in C++1y" proposal from Jeffrey
// Yasskin and Chandler Carruth, see
// http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2012/n3333.html.
template <typename>
struct hash;
// Combine two hash values together. This code was taken from MurmurHash.
V8_INLINE size_t hash_combine(size_t seed, size_t hash) {
#if V8_HOST_ARCH_32_BIT
const uint32_t c1 = 0xCC9E2D51;
const uint32_t c2 = 0x1B873593;
hash *= c1;
hash = bits::RotateRight32(hash, 15);
hash *= c2;
seed ^= hash;
seed = bits::RotateRight32(seed, 13);
seed = seed * 5 + 0xE6546B64;
#else
const uint64_t m = uint64_t{0xC6A4A7935BD1E995};
const uint32_t r = 47;
hash *= m;
hash ^= hash >> r;
hash *= m;
seed ^= hash;
seed *= m;
#endif // V8_HOST_ARCH_32_BIT
return seed;
}
// base::Hasher makes it easier to combine multiple fields into one hash and
// avoids the ambiguity of the different {hash_combine} methods.
class Hasher {
public:
constexpr Hasher() = default;
constexpr explicit Hasher(size_t seed) : hash_(seed) {}
// Retrieve the current hash.
constexpr size_t hash() const { return hash_; }
// Combine an existing hash value into this hasher's hash.
Hasher& AddHash(size_t other_hash) {
hash_ = hash_combine(hash_, other_hash);
return *this;
}
// Hash a value {t} and combine its hash into this hasher's hash.
template <typename T>
Hasher& Add(const T& t) {
return AddHash(base::hash<T>{}(t));
}
// Hash a range of values and combine the hashes into this hasher's hash.
template <typename Iterator>
Hasher& AddRange(Iterator first, Iterator last) {
// TODO(clemensb): If the iterator returns an integral or POD value smaller
// than size_t we can combine multiple elements together to get better
// hashing performance.
for (; first != last; ++first) Add(*first);
return *this;
}
// Hash a collection of values and combine the hashes into this hasher's hash.
template <typename C>
auto AddRange(C collection)
-> decltype(AddRange(std::begin(collection), std::end(collection))) {
return AddRange(std::begin(collection), std::end(collection));
}
// Hash multiple values and combine their hashes.
template <typename... T>
constexpr static size_t Combine(const T&... ts) {
Hasher hasher;
(..., hasher.Add(ts));
return hasher.hash();
}
private:
size_t hash_ = 0;
};
// Thomas Wang, Integer Hash Functions.
// https://gist.github.com/badboy/6267743
template <typename T>
V8_INLINE size_t hash_value_unsigned_impl(T v) {
switch (sizeof(T)) {
case 4: {
// "32 bit Mix Functions"
v = ~v + (v << 15); // v = (v << 15) - v - 1;
v = v ^ (v >> 12);
v = v + (v << 2);
v = v ^ (v >> 4);
v = v * 2057; // v = (v + (v << 3)) + (v << 11);
v = v ^ (v >> 16);
return static_cast<size_t>(v);
}
case 8: {
switch (sizeof(size_t)) {
case 4: {
// "64 bit to 32 bit Hash Functions"
v = ~v + (v << 18); // v = (v << 18) - v - 1;
v = v ^ (v >> 31);
v = v * 21; // v = (v + (v << 2)) + (v << 4);
v = v ^ (v >> 11);
v = v + (v << 6);
v = v ^ (v >> 22);
return static_cast<size_t>(v);
}
case 8: {
// "64 bit Mix Functions"
v = ~v + (v << 21); // v = (v << 21) - v - 1;
v = v ^ (v >> 24);
v = (v + (v << 3)) + (v << 8); // v * 265
v = v ^ (v >> 14);
v = (v + (v << 2)) + (v << 4); // v * 21
v = v ^ (v >> 28);
v = v + (v << 31);
return static_cast<size_t>(v);
}
}
}
}
UNREACHABLE();
}
#define V8_BASE_HASH_VALUE_TRIVIAL(type) \
V8_INLINE size_t hash_value(type v) { return static_cast<size_t>(v); }
V8_BASE_HASH_VALUE_TRIVIAL(bool)
V8_BASE_HASH_VALUE_TRIVIAL(unsigned char)
V8_BASE_HASH_VALUE_TRIVIAL(unsigned short) // NOLINT(runtime/int)
#undef V8_BASE_HASH_VALUE_TRIVIAL
V8_INLINE size_t hash_value(unsigned int v) {
return hash_value_unsigned_impl(v);
}
V8_INLINE size_t hash_value(unsigned long v) { // NOLINT(runtime/int)
return hash_value_unsigned_impl(v);
}
V8_INLINE size_t hash_value(unsigned long long v) { // NOLINT(runtime/int)
return hash_value_unsigned_impl(v);
}
#define V8_BASE_HASH_VALUE_SIGNED(type) \
V8_INLINE size_t hash_value(signed type v) { \
return hash_value(base::bit_cast<unsigned type>(v)); \
}
V8_BASE_HASH_VALUE_SIGNED(char)
V8_BASE_HASH_VALUE_SIGNED(short) // NOLINT(runtime/int)
V8_BASE_HASH_VALUE_SIGNED(int) // NOLINT(runtime/int)
V8_BASE_HASH_VALUE_SIGNED(long) // NOLINT(runtime/int)
V8_BASE_HASH_VALUE_SIGNED(long long) // NOLINT(runtime/int)
#undef V8_BASE_HASH_VALUE_SIGNED
V8_INLINE size_t hash_value(float v) {
// 0 and -0 both hash to zero.
return v != 0.0f ? hash_value(base::bit_cast<uint32_t>(v)) : 0;
}
V8_INLINE size_t hash_value(double v) {
// 0 and -0 both hash to zero.
return v != 0.0 ? hash_value(base::bit_cast<uint64_t>(v)) : 0;
}
template <typename T, size_t N>
V8_INLINE size_t hash_value(const T (&v)[N]) {
return Hasher{}.AddRange(v, v + N).hash();
}
template <typename T, size_t N>
V8_INLINE size_t hash_value(T (&v)[N]) {
return Hasher{}.AddRange(v, v + N).hash();
}
template <typename T>
V8_INLINE size_t hash_value(T* const& v) {
return hash_value(reinterpret_cast<uintptr_t>(v));
}
template <typename T1, typename T2>
V8_INLINE size_t hash_value(std::pair<T1, T2> const& v) {
return Hasher::Combine(v.first, v.second);
}
template <typename... T, size_t... I>
V8_INLINE size_t hash_value_impl(std::tuple<T...> const& v,
std::index_sequence<I...>) {
return Hasher::Combine(std::get<I>(v)...);
}
template <typename... T>
V8_INLINE size_t hash_value(std::tuple<T...> const& v) {
return hash_value_impl(v, std::make_index_sequence<sizeof...(T)>());
}
template <typename T>
V8_INLINE size_t hash_value(T v)
requires std::is_enum<T>::value
{
return hash_value(static_cast<std::underlying_type_t<T>>(v));
}
// Provide a hash_value function for each T with a hash_value member function.
template <typename T>
requires requires(const T& t) {
{ t.hash_value() } -> std::convertible_to<size_t>;
}
V8_INLINE size_t hash_value(const T& v) {
return v.hash_value();
}
template <typename T>
concept Hashable = requires(const T& t) {
{ hash_value(t) } -> std::convertible_to<size_t>;
};
// Define base::hash to call the hash_value function.
template <Hashable T>
struct hash<T> {
V8_INLINE constexpr size_t operator()(const T& v) const {
return hash_value(v);
}
};
// TODO(clemensb): Depending on the types in this template the compiler might
// pick {hash_combine(size_t, size_t)} instead. Thus remove this template and
// switch callers to {Hasher::Combine}.
template <typename... Ts>
V8_INLINE size_t hash_combine(Ts const&... vs) {
return Hasher{}.Combine(vs...);
}
// TODO(clemensb): Switch users to {Hasher{}.AddRange(first, last).hash()}.
template <typename Iterator>
V8_INLINE size_t hash_range(Iterator first, Iterator last) {
return Hasher{}.AddRange(first, last).hash();
}
// base::bit_equal_to is a function object class for bitwise equality
// comparison, similar to std::equal_to, except that the comparison is performed
// on the bit representation of the operands.
//
// base::bit_hash is a function object class for bitwise hashing, similar to
// base::hash. It can be used together with base::bit_equal_to to implement a
// hash data structure based on the bitwise representation of types.
template <typename T>
struct bit_equal_to {};
template <typename T>
struct bit_hash {};
#define V8_BASE_BIT_SPECIALIZE_TRIVIAL(type) \
template <> \
struct bit_equal_to<type> : public std::equal_to<type> {}; \
template <> \
struct bit_hash<type> : public hash<type> {};
V8_BASE_BIT_SPECIALIZE_TRIVIAL(signed char)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(unsigned char)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(short) // NOLINT(runtime/int)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(unsigned short) // NOLINT(runtime/int)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(int)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(unsigned int)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(long) // NOLINT(runtime/int)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(unsigned long) // NOLINT(runtime/int)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(long long) // NOLINT(runtime/int)
V8_BASE_BIT_SPECIALIZE_TRIVIAL(unsigned long long) // NOLINT(runtime/int)
#undef V8_BASE_BIT_SPECIALIZE_TRIVIAL
#define V8_BASE_BIT_SPECIALIZE_BIT_CAST(type, btype) \
template <> \
struct bit_equal_to<type> { \
V8_INLINE bool operator()(type lhs, type rhs) const { \
return base::bit_cast<btype>(lhs) == base::bit_cast<btype>(rhs); \
} \
}; \
template <> \
struct bit_hash<type> { \
V8_INLINE size_t operator()(type v) const { \
hash<btype> h; \
return h(base::bit_cast<btype>(v)); \
} \
};
V8_BASE_BIT_SPECIALIZE_BIT_CAST(float, uint32_t)
V8_BASE_BIT_SPECIALIZE_BIT_CAST(double, uint64_t)
#undef V8_BASE_BIT_SPECIALIZE_BIT_CAST
} // namespace v8::base
// Also define std::hash for all classes that can be hashed via v8::base::hash.
namespace std {
template <typename T>
requires requires { typename v8::base::hash<T>; }
struct hash<T> : v8::base::hash<T> {};
} // namespace std
#endif // V8_BASE_HASHING_H_

117
deps/v8/src/base/hashmap-entry.h vendored Normal file
View File

@ -0,0 +1,117 @@
// Copyright 2016 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_HASHMAP_ENTRY_H_
#define V8_BASE_HASHMAP_ENTRY_H_
#include <cstdint>
#include <type_traits>
#include "src/base/memory.h"
namespace v8 {
namespace base {
// Marker type for hashmaps without a value (i.e. hashsets). These won't
// allocate space for the value in the entry.
struct NoHashMapValue {};
// HashMap entries are (key, value, hash) triplets, with a boolean indicating if
// they are an empty entry. Some clients may not need to use the value slot
// (e.g. implementers of sets, where the key is the value), in which case they
// should use NoHashMapValue.
template <typename Key, typename Value>
struct TemplateHashMapEntry {
static_assert((!std::is_same<Value, NoHashMapValue>::value));
Key key;
Value value;
uint32_t hash; // The full hash value for key
TemplateHashMapEntry(Key key, Value value, uint32_t hash)
: key(key), value(value), hash(hash), exists_(true) {}
bool exists() const { return exists_; }
void clear() { exists_ = false; }
private:
bool exists_;
};
// Specialization for pointer-valued keys
template <typename Key, typename Value>
struct TemplateHashMapEntry<Key*, Value> {
static_assert((!std::is_same<Value, NoHashMapValue>::value));
Key* key;
Value value;
uint32_t hash; // The full hash value for key
TemplateHashMapEntry(Key* key, Value value, uint32_t hash)
: key(key), value(value), hash(hash) {}
bool exists() const { return key != nullptr; }
void clear() { key = nullptr; }
};
// Specialization for Address-valued keys
template <typename Value>
struct TemplateHashMapEntry<Address, Value> {
static_assert((!std::is_same<Value, NoHashMapValue>::value));
Address key;
Value value;
uint32_t hash; // The full hash value for key
TemplateHashMapEntry(Address key, Value value, uint32_t hash)
: key(key), value(value), hash(hash) {}
bool exists() const { return key != -1u; }
void clear() { key = -1u; }
};
// Specialization for no value.
template <typename Key>
struct TemplateHashMapEntry<Key, NoHashMapValue> {
union {
Key key;
NoHashMapValue value; // Value in union with key to not take up space.
};
uint32_t hash; // The full hash value for key
TemplateHashMapEntry(Key key, NoHashMapValue value, uint32_t hash)
: key(key), hash(hash), exists_(true) {}
bool exists() const { return exists_; }
void clear() { exists_ = false; }
private:
bool exists_;
};
// Specialization for pointer-valued keys and no value.
template <typename Key>
struct TemplateHashMapEntry<Key*, NoHashMapValue> {
union {
Key* key;
NoHashMapValue value; // Value in union with key to not take up space.
};
uint32_t hash; // The full hash value for key
TemplateHashMapEntry(Key* key, NoHashMapValue value, uint32_t hash)
: key(key), hash(hash) {}
bool exists() const { return key != nullptr; }
void clear() { key = nullptr; }
};
} // namespace base
} // namespace v8
#endif // V8_BASE_HASHMAP_ENTRY_H_

578
deps/v8/src/base/hashmap.h vendored Normal file
View File

@ -0,0 +1,578 @@
// Copyright 2012 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_HASHMAP_H_
#define V8_BASE_HASHMAP_H_
// The reason we write our own hash map instead of using unordered_map in STL,
// is that STL containers use a mutex pool on debug build, which will lead to
// deadlock when we are using async signal handler.
#include <stdlib.h>
#include "src/base/bits.h"
#include "src/base/hashmap-entry.h"
#include "src/base/logging.h"
#include "src/base/platform/memory.h"
namespace v8 {
namespace base {
class DefaultAllocationPolicy {
public:
template <typename T, typename TypeTag = T[]>
V8_INLINE T* AllocateArray(size_t length) {
return static_cast<T*>(base::Malloc(length * sizeof(T)));
}
template <typename T, typename TypeTag = T[]>
V8_INLINE void DeleteArray(T* p, size_t length) {
base::Free(p);
}
};
template <typename Key, typename Value, class MatchFun, class AllocationPolicy>
class TemplateHashMapImpl {
public:
using Entry = TemplateHashMapEntry<Key, Value>;
// The default capacity. This is used by the call sites which want
// to pass in a non-default AllocationPolicy but want to use the
// default value of capacity specified by the implementation.
static const uint32_t kDefaultHashMapCapacity = 8;
// initial_capacity is the size of the initial hash map;
// it must be a power of 2 (and thus must not be 0).
explicit TemplateHashMapImpl(uint32_t capacity = kDefaultHashMapCapacity,
MatchFun match = MatchFun(),
AllocationPolicy allocator = AllocationPolicy());
TemplateHashMapImpl(const TemplateHashMapImpl&) = delete;
TemplateHashMapImpl& operator=(const TemplateHashMapImpl&) = delete;
// Clones the given hashmap and creates a copy with the same entries.
explicit TemplateHashMapImpl(const TemplateHashMapImpl* original,
AllocationPolicy allocator = AllocationPolicy());
TemplateHashMapImpl(TemplateHashMapImpl&& other) V8_NOEXCEPT = default;
~TemplateHashMapImpl();
TemplateHashMapImpl& operator=(TemplateHashMapImpl&& other)
V8_NOEXCEPT = default;
// If an entry with matching key is found, returns that entry.
// Otherwise, nullptr is returned.
Entry* Lookup(const Key& key, uint32_t hash) const;
// If an entry with matching key is found, returns that entry.
// If no matching entry is found, a new entry is inserted with
// corresponding key, key hash, and default initialized value.
Entry* LookupOrInsert(const Key& key, uint32_t hash);
// If an entry with matching key is found, returns that entry.
// If no matching entry is found, a new entry is inserted with
// corresponding key, key hash, and value created by func.
template <typename Func>
Entry* LookupOrInsert(const Key& key, uint32_t hash, const Func& value_func);
// Heterogeneous version of LookupOrInsert, which allows a
// different lookup key type than the hashmap's key type.
// The requirement is that MatchFun has an overload:
//
// operator()(const LookupKey& lookup_key, const Key& entry_key)
//
// If an entry with matching key is found, returns that entry.
// If no matching entry is found, a new entry is inserted with
// a key created by key_func, key hash, and value created by
// value_func.
template <typename LookupKey, typename KeyFunc, typename ValueFunc>
Entry* LookupOrInsert(const LookupKey& lookup_key, uint32_t hash,
const KeyFunc& key_func, const ValueFunc& value_func);
Entry* InsertNew(const Key& key, uint32_t hash);
// Removes the entry with matching key.
// It returns the value of the deleted entry
// or null if there is no value for such key.
Value Remove(const Key& key, uint32_t hash);
// Empties the hash map (occupancy() == 0).
void Clear();
// Empties the map and makes it unusable for allocation.
void Invalidate() {
DCHECK_NOT_NULL(impl_.map_);
impl_.allocator().DeleteArray(impl_.map_, capacity());
impl_ = Impl(impl_.match(), AllocationPolicy());
}
// The number of (non-empty) entries in the table.
uint32_t occupancy() const { return impl_.occupancy_; }
// The capacity of the table. The implementation
// makes sure that occupancy is at most 80% of
// the table capacity.
uint32_t capacity() const { return impl_.capacity_; }
// Iteration
//
// for (Entry* p = map.Start(); p != nullptr; p = map.Next(p)) {
// ...
// }
//
// If entries are inserted during iteration, the effect of
// calling Next() is undefined.
Entry* Start() const;
Entry* Next(Entry* entry) const;
AllocationPolicy allocator() const { return impl_.allocator(); }
protected:
void Initialize(uint32_t capacity);
private:
Entry* map_end() const { return impl_.map_ + impl_.capacity_; }
template <typename LookupKey>
Entry* Probe(const LookupKey& key, uint32_t hash) const;
Entry* FillEmptyEntry(Entry* entry, const Key& key, const Value& value,
uint32_t hash);
void Resize();
// To support matcher and allocator that may not be possible to
// default-construct, we have to store their instances. Using this to store
// all internal state of the hash map and using private inheritance to store
// matcher and allocator lets us take advantage of an empty base class
// optimization to avoid extra space in the common case when MatchFun and
// AllocationPolicy have no state.
// TODO(ishell): Once we reach C++20, consider removing the Impl struct and
// adding match and allocator as [[no_unique_address]] fields.
struct Impl : private MatchFun, private AllocationPolicy {
Impl(MatchFun match, AllocationPolicy allocator)
: MatchFun(std::move(match)), AllocationPolicy(std::move(allocator)) {}
Impl() = default;
Impl(const Impl&) V8_NOEXCEPT = default;
Impl(Impl&& other) V8_NOEXCEPT { *this = std::move(other); }
Impl& operator=(const Impl& other) V8_NOEXCEPT = default;
Impl& operator=(Impl&& other) V8_NOEXCEPT {
MatchFun::operator=(std::move(other));
AllocationPolicy::operator=(std::move(other));
map_ = other.map_;
capacity_ = other.capacity_;
occupancy_ = other.occupancy_;
other.map_ = nullptr;
other.capacity_ = 0;
other.occupancy_ = 0;
return *this;
}
const MatchFun& match() const { return *this; }
MatchFun& match() { return *this; }
const AllocationPolicy& allocator() const { return *this; }
AllocationPolicy& allocator() { return *this; }
Entry* map_ = nullptr;
uint32_t capacity_ = 0;
uint32_t occupancy_ = 0;
} impl_;
};
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::
TemplateHashMapImpl(uint32_t initial_capacity, MatchFun match,
AllocationPolicy allocator)
: impl_(std::move(match), std::move(allocator)) {
Initialize(initial_capacity);
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::
TemplateHashMapImpl(const TemplateHashMapImpl* original,
AllocationPolicy allocator)
: impl_(original->impl_.match(), std::move(allocator)) {
impl_.capacity_ = original->capacity();
impl_.occupancy_ = original->occupancy();
impl_.map_ = impl_.allocator().template AllocateArray<Entry>(capacity());
memcpy(impl_.map_, original->impl_.map_, capacity() * sizeof(Entry));
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
TemplateHashMapImpl<Key, Value, MatchFun,
AllocationPolicy>::~TemplateHashMapImpl() {
if (impl_.map_) impl_.allocator().DeleteArray(impl_.map_, capacity());
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Lookup(
const Key& key, uint32_t hash) const {
Entry* entry = Probe(key, hash);
return entry->exists() ? entry : nullptr;
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
const Key& key, uint32_t hash) {
return LookupOrInsert(key, hash, []() { return Value(); });
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
template <typename Func>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
const Key& key, uint32_t hash, const Func& value_func) {
return LookupOrInsert(
key, hash, [&key]() { return key; }, value_func);
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
template <typename LookupKey, typename KeyFunc, typename ValueFunc>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::LookupOrInsert(
const LookupKey& lookup_key, uint32_t hash, const KeyFunc& key_func,
const ValueFunc& value_func) {
// Find a matching entry.
Entry* entry = Probe(lookup_key, hash);
if (entry->exists()) {
return entry;
}
return FillEmptyEntry(entry, key_func(), value_func(), hash);
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::InsertNew(
const Key& key, uint32_t hash) {
Entry* entry = Probe(key, hash);
return FillEmptyEntry(entry, key, Value(), hash);
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
Value TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Remove(
const Key& key, uint32_t hash) {
// Lookup the entry for the key to remove.
Entry* p = Probe(key, hash);
if (!p->exists()) {
// Key not found nothing to remove.
return nullptr;
}
Value value = p->value;
// To remove an entry we need to ensure that it does not create an empty
// entry that will cause the search for another entry to stop too soon. If all
// the entries between the entry to remove and the next empty slot have their
// initial position inside this interval, clearing the entry to remove will
// not break the search. If, while searching for the next empty entry, an
// entry is encountered which does not have its initial position between the
// entry to remove and the position looked at, then this entry can be moved to
// the place of the entry to remove without breaking the search for it. The
// entry made vacant by this move is now the entry to remove and the process
// starts over.
// Algorithm from http://en.wikipedia.org/wiki/Open_addressing.
// This guarantees loop termination as there is at least one empty entry so
// eventually the removed entry will have an empty entry after it.
DCHECK(occupancy() < capacity());
// p is the candidate entry to clear. q is used to scan forwards.
Entry* q = p; // Start at the entry to remove.
while (true) {
// Move q to the next entry.
q = q + 1;
if (q == map_end()) {
q = impl_.map_;
}
// All entries between p and q have their initial position between p and q
// and the entry p can be cleared without breaking the search for these
// entries.
if (!q->exists()) {
break;
}
// Find the initial position for the entry at position q.
Entry* r = impl_.map_ + (q->hash & (capacity() - 1));
// If the entry at position q has its initial position outside the range
// between p and q it can be moved forward to position p and will still be
// found. There is now a new candidate entry for clearing.
if ((q > p && (r <= p || r > q)) || (q < p && (r <= p && r > q))) {
*p = *q;
p = q;
}
}
// Clear the entry which is allowed to en emptied.
p->clear();
impl_.occupancy_--;
return value;
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Clear() {
// Mark all entries as empty.
for (size_t i = 0; i < capacity(); ++i) {
impl_.map_[i].clear();
}
impl_.occupancy_ = 0;
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Start() const {
return Next(impl_.map_ - 1);
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Next(
Entry* entry) const {
const Entry* end = map_end();
DCHECK(impl_.map_ - 1 <= entry && entry < end);
for (entry++; entry < end; entry++) {
if (entry->exists()) {
return entry;
}
}
return nullptr;
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
template <typename LookupKey>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Probe(
const LookupKey& key, uint32_t hash) const {
DCHECK(base::bits::IsPowerOfTwo(capacity()));
size_t i = hash & (capacity() - 1);
DCHECK(i < capacity());
DCHECK(occupancy() < capacity()); // Guarantees loop termination.
Entry* map = impl_.map_;
while (map[i].exists() &&
!impl_.match()(hash, map[i].hash, key, map[i].key)) {
i = (i + 1) & (capacity() - 1);
}
return &map[i];
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
typename TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Entry*
TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::FillEmptyEntry(
Entry* entry, const Key& key, const Value& value, uint32_t hash) {
DCHECK(!entry->exists());
new (entry) Entry(key, value, hash);
impl_.occupancy_++;
// Grow the map if we reached >= 80% occupancy.
if (occupancy() + occupancy() / 4 >= capacity()) {
Resize();
entry = Probe(key, hash);
}
return entry;
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Initialize(
uint32_t capacity) {
DCHECK(base::bits::IsPowerOfTwo(capacity));
impl_.map_ = impl_.allocator().template AllocateArray<Entry>(capacity);
if (impl_.map_ == nullptr) {
FATAL("Out of memory: HashMap::Initialize");
return;
}
impl_.capacity_ = capacity;
Clear();
}
template <typename Key, typename Value, typename MatchFun,
class AllocationPolicy>
void TemplateHashMapImpl<Key, Value, MatchFun, AllocationPolicy>::Resize() {
Entry* old_map = impl_.map_;
uint32_t old_capacity = capacity();
uint32_t n = occupancy();
// Allocate larger map.
Initialize(capacity() * 2);
// Rehash all current entries.
for (Entry* entry = old_map; n > 0; entry++) {
if (entry->exists()) {
Entry* new_entry = Probe(entry->key, entry->hash);
new_entry =
FillEmptyEntry(new_entry, entry->key, entry->value, entry->hash);
n--;
}
}
// Delete old map.
impl_.allocator().DeleteArray(old_map, old_capacity);
}
// Match function which compares hashes before executing a (potentially
// expensive) key comparison.
template <typename Key, typename MatchFun>
struct HashEqualityThenKeyMatcher {
explicit HashEqualityThenKeyMatcher(MatchFun match) : match_(match) {}
bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1,
const Key& key2) const {
return hash1 == hash2 && match_(key1, key2);
}
private:
MatchFun match_;
};
// Hashmap<void*, void*> which takes a custom key comparison function pointer.
template <typename AllocationPolicy>
class CustomMatcherTemplateHashMapImpl
: public TemplateHashMapImpl<
void*, void*,
HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>,
AllocationPolicy> {
using Base = TemplateHashMapImpl<
void*, void*, HashEqualityThenKeyMatcher<void*, bool (*)(void*, void*)>,
AllocationPolicy>;
public:
using MatchFun = bool (*)(void*, void*);
explicit CustomMatcherTemplateHashMapImpl(
MatchFun match, uint32_t capacity = Base::kDefaultHashMapCapacity,
AllocationPolicy allocator = AllocationPolicy())
: Base(capacity, HashEqualityThenKeyMatcher<void*, MatchFun>(match),
allocator) {}
explicit CustomMatcherTemplateHashMapImpl(
const CustomMatcherTemplateHashMapImpl* original,
AllocationPolicy allocator = AllocationPolicy())
: Base(original, allocator) {}
CustomMatcherTemplateHashMapImpl(const CustomMatcherTemplateHashMapImpl&) =
delete;
CustomMatcherTemplateHashMapImpl& operator=(
const CustomMatcherTemplateHashMapImpl&) = delete;
};
using CustomMatcherHashMap =
CustomMatcherTemplateHashMapImpl<DefaultAllocationPolicy>;
// Match function which compares keys directly by equality.
template <typename Key>
struct KeyEqualityMatcher {
bool operator()(uint32_t hash1, uint32_t hash2, const Key& key1,
const Key& key2) const {
return key1 == key2;
}
};
// Hashmap<void*, void*> which compares the key pointers directly.
template <typename AllocationPolicy>
class PointerTemplateHashMapImpl
: public TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>,
AllocationPolicy> {
using Base = TemplateHashMapImpl<void*, void*, KeyEqualityMatcher<void*>,
AllocationPolicy>;
public:
explicit PointerTemplateHashMapImpl(
uint32_t capacity = Base::kDefaultHashMapCapacity,
AllocationPolicy allocator = AllocationPolicy())
: Base(capacity, KeyEqualityMatcher<void*>(), allocator) {}
PointerTemplateHashMapImpl(const PointerTemplateHashMapImpl& other,
AllocationPolicy allocator = AllocationPolicy())
: Base(&other, allocator) {}
PointerTemplateHashMapImpl(PointerTemplateHashMapImpl&& other) V8_NOEXCEPT
: Base(std::move(other)) {}
PointerTemplateHashMapImpl& operator=(PointerTemplateHashMapImpl&& other)
V8_NOEXCEPT {
static_cast<Base&>(*this) = std::move(other);
return *this;
}
};
using HashMap = PointerTemplateHashMapImpl<DefaultAllocationPolicy>;
// A hash map for pointer keys and values with an STL-like interface.
template <class Key, class Value, class MatchFun, class AllocationPolicy>
class TemplateHashMap
: private TemplateHashMapImpl<void*, void*,
HashEqualityThenKeyMatcher<void*, MatchFun>,
AllocationPolicy> {
using Base = TemplateHashMapImpl<void*, void*,
HashEqualityThenKeyMatcher<void*, MatchFun>,
AllocationPolicy>;
public:
static_assert(sizeof(Key*) == sizeof(void*));
static_assert(sizeof(Value*) == sizeof(void*));
struct value_type {
Key* first;
Value* second;
};
class Iterator {
public:
Iterator& operator++() {
entry_ = map_->Next(entry_);
return *this;
}
value_type* operator->() { return reinterpret_cast<value_type*>(entry_); }
bool operator!=(const Iterator& other) { return entry_ != other.entry_; }
private:
Iterator(const Base* map, typename Base::Entry* entry)
: map_(map), entry_(entry) {}
const Base* map_;
typename Base::Entry* entry_;
friend class TemplateHashMap;
};
explicit TemplateHashMap(MatchFun match,
AllocationPolicy allocator = AllocationPolicy())
: Base(Base::kDefaultHashMapCapacity,
HashEqualityThenKeyMatcher<void*, MatchFun>(match), allocator) {}
Iterator begin() const { return Iterator(this, this->Start()); }
Iterator end() const { return Iterator(this, nullptr); }
Iterator find(Key* key, bool insert = false) {
if (insert) {
return Iterator(this, this->LookupOrInsert(key, key->Hash()));
}
return Iterator(this, this->Lookup(key, key->Hash()));
}
};
} // namespace base
} // namespace v8
#endif // V8_BASE_HASHMAP_H_

3036
deps/v8/src/base/ieee754.cc vendored Normal file

File diff suppressed because it is too large Load Diff

114
deps/v8/src/base/ieee754.h vendored Normal file
View File

@ -0,0 +1,114 @@
// Copyright 2016 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_IEEE754_H_
#define V8_BASE_IEEE754_H_
#include "src/base/base-export.h"
#if defined(V8_USE_LIBM_TRIG_FUNCTIONS)
#include "third_party/glibc/src/sysdeps/ieee754/dbl-64/trig.h" // nogncheck
#endif
namespace v8 {
namespace base {
namespace ieee754 {
// Returns the arc cosine of |x|; that is the value whose cosine is |x|.
V8_BASE_EXPORT double acos(double x);
// Returns the inverse hyperbolic cosine of |x|; that is the value whose
// hyperbolic cosine is |x|.
V8_BASE_EXPORT double acosh(double x);
// Returns the arc sine of |x|; that is the value whose sine is |x|.
V8_BASE_EXPORT double asin(double x);
// Returns the inverse hyperbolic sine of |x|; that is the value whose
// hyperbolic sine is |x|.
V8_BASE_EXPORT double asinh(double x);
// Returns the principal value of the arc tangent of |x|; that is the value
// whose tangent is |x|.
V8_BASE_EXPORT double atan(double x);
// Returns the principal value of the arc tangent of |y/x|, using the signs of
// the two arguments to determine the quadrant of the result.
V8_BASE_EXPORT double atan2(double y, double x);
#if defined(V8_USE_LIBM_TRIG_FUNCTIONS)
// To ensure there aren't problems with libm's sin/cos, both implementations
// are shipped. The plan is to transition to libm once we ensure there are no
// compatibility or performance issues.
V8_BASE_EXPORT double fdlibm_sin(double x);
V8_BASE_EXPORT double fdlibm_cos(double x);
#if !defined(BUILDING_V8_BASE_SHARED) && !defined(USING_V8_BASE_SHARED)
inline double libm_sin(double x) { return glibc_sin(x); }
inline double libm_cos(double x) { return glibc_cos(x); }
#else
V8_BASE_EXPORT double libm_sin(double x);
V8_BASE_EXPORT double libm_cos(double x);
#endif
#else
V8_BASE_EXPORT double cos(double x);
V8_BASE_EXPORT double sin(double x);
#endif
// Returns the base-e exponential of |x|.
V8_BASE_EXPORT double exp(double x);
V8_BASE_EXPORT double atanh(double x);
// Returns the natural logarithm of |x|.
V8_BASE_EXPORT double log(double x);
// Returns a value equivalent to |log(1+x)|, but computed in a way that is
// accurate even if the value of |x| is near zero.
V8_BASE_EXPORT double log1p(double x);
// Returns the base 2 logarithm of |x|.
V8_BASE_EXPORT double log2(double x);
// Returns the base 10 logarithm of |x|.
V8_BASE_EXPORT double log10(double x);
// Returns the cube root of |x|.
V8_BASE_EXPORT double cbrt(double x);
// Returns exp(x)-1, the exponential of |x| minus 1.
V8_BASE_EXPORT double expm1(double x);
namespace legacy {
// This function should not be used directly. Instead, use
// v8::internal::math::pow.
// Returns |x| to the power of |y|.
// The result of base ** exponent when base is 1 or -1 and exponent is
// +Infinity or -Infinity differs from IEEE 754-2008. The first edition
// of ECMAScript specified a result of NaN for this operation, whereas
// later versions of IEEE 754-2008 specified 1. The historical ECMAScript
// behaviour is preserved for compatibility reasons.
V8_BASE_EXPORT double pow(double x, double y);
} // namespace legacy
// Returns the tangent of |x|, where |x| is given in radians.
V8_BASE_EXPORT double tan(double x);
// Returns the hyperbolic cosine of |x|, where |x| is given radians.
V8_BASE_EXPORT double cosh(double x);
// Returns the hyperbolic sine of |x|, where |x| is given radians.
V8_BASE_EXPORT double sinh(double x);
// Returns the hyperbolic tangent of |x|, where |x| is given radians.
V8_BASE_EXPORT double tanh(double x);
} // namespace ieee754
} // namespace base
} // namespace v8
#endif // V8_BASE_IEEE754_H_

185
deps/v8/src/base/immediate-crash.h vendored Normal file
View File

@ -0,0 +1,185 @@
// Copyright 2021 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_IMMEDIATE_CRASH_H_
#define V8_BASE_IMMEDIATE_CRASH_H_
#include "include/v8config.h"
#include "src/base/build_config.h"
// Crashes in the fastest possible way with no attempt at logging.
// There are several constraints; see http://crbug.com/664209 for more context.
//
// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
// resulting exception or simply hit 'continue' to skip over it in a debugger.
// - Different instances of TRAP_SEQUENCE_() must not be folded together, to
// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
// blocks will not be folded together.
// Note: TRAP_SEQUENCE_() previously required an instruction with a unique
// nonce since unlike clang, GCC folds together identical asm volatile
// blocks.
// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
// memory access.
// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
// __builtin_unreachable() is used to provide that hint here. clang also uses
// this as a heuristic to pack the instructions in the function epilogue to
// improve code density.
//
// Additional properties that are nice to have:
// - TRAP_SEQUENCE_() should be as compact as possible.
// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid
// shifting crash reporting clusters. As a consequence of this, explicit
// assembly is preferred over intrinsics.
// Note: this last bullet point may no longer be true, and may be removed in
// the future.
// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact
// that clang emits an actual instruction for __builtin_unreachable() on certain
// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will
// be removed in followups, so splitting it up like this now makes it easy to
// land the followups.
#if V8_CC_GNU
#if V8_HOST_ARCH_X64 || V8_HOST_ARCH_IA32
// TODO(https://crbug.com/958675): In theory, it should be possible to use just
// int3. However, there are a number of crashes with SIGILL as the exception
// code, so it seems likely that there's a signal handler that allows execution
// to continue after SIGTRAP.
#define TRAP_SEQUENCE1_() asm volatile("int3")
#if V8_OS_DARWIN
// Intentionally empty: __builtin_unreachable() is always part of the sequence
// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
#define TRAP_SEQUENCE2_() asm volatile("")
#else
#define TRAP_SEQUENCE2_() asm volatile("ud2")
#endif // V8_OS_DARWIN
#elif V8_HOST_ARCH_ARM
// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
// cause a SIGTRAP from userspace without using a syscall (which would be a
// problem for sandboxing).
// TODO(https://crbug.com/958675): Remove bkpt from this sequence.
#define TRAP_SEQUENCE1_() asm volatile("bkpt #0")
#define TRAP_SEQUENCE2_() asm volatile("udf #0")
#elif V8_HOST_ARCH_ARM64
// This will always generate a SIGTRAP on arm64.
// TODO(https://crbug.com/958675): Remove brk from this sequence.
#define TRAP_SEQUENCE1_() asm volatile("brk #0")
#define TRAP_SEQUENCE2_() asm volatile("hlt #0")
#elif V8_HOST_ARCH_PPC64
// GDB software breakpoint instruction.
// Same as `bkpt` under the assembler.
#if V8_OS_AIX
#define TRAP_SEQUENCE1_() asm volatile(".vbyte 4,0x7D821008");
#else
#define TRAP_SEQUENCE1_() asm volatile(".4byte 0x7D821008");
#endif
#define TRAP_SEQUENCE2_() asm volatile("")
#elif V8_OS_ZOS
#define TRAP_SEQUENCE1_() __builtin_trap()
#define TRAP_SEQUENCE2_() asm volatile("")
#elif V8_HOST_ARCH_S390X
// GDB software breakpoint instruction.
// Same as `bkpt` under the assembler.
#define TRAP_SEQUENCE1_() asm volatile(".2byte 0x0001");
#define TRAP_SEQUENCE2_() asm volatile("")
#else
// Crash report accuracy will not be guaranteed on other architectures, but at
// least this will crash as expected.
#define TRAP_SEQUENCE1_() __builtin_trap()
#define TRAP_SEQUENCE2_() asm volatile("")
#endif // V8_HOST_ARCH_*
#elif V8_CC_MSVC
#if !defined(__clang__)
// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
#define TRAP_SEQUENCE1_() __debugbreak()
#define TRAP_SEQUENCE2_()
#elif V8_HOST_ARCH_ARM64
// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
// __debugbreak() generates that in both VC++ and clang.
#define TRAP_SEQUENCE1_() __debugbreak()
// Intentionally empty: __builtin_unreachable() is always part of the sequence
// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
// https://crbug.com/958373
#define TRAP_SEQUENCE2_() __asm volatile("")
#else
#define TRAP_SEQUENCE1_() asm volatile("int3")
#define TRAP_SEQUENCE2_() asm volatile("ud2")
#endif // __clang__
#else
#error No supported trap sequence!
#endif // V8_CC_GNU
#define TRAP_SEQUENCE_() \
do { \
TRAP_SEQUENCE1_(); \
TRAP_SEQUENCE2_(); \
} while (false)
// CHECK() and the trap sequence can be invoked from a constexpr function.
// This could make compilation fail on GCC, as it forbids directly using inline
// asm inside a constexpr function. However, it allows calling a lambda
// expression including the same asm.
// The side effect is that the top of the stacktrace will not point to the
// calling function, but to this anonymous lambda. This is still useful as the
// full name of the lambda will typically include the name of the function that
// calls CHECK() and the debugger will still break at the right line of code.
#if !V8_CC_GNU
#define WRAPPED_TRAP_SEQUENCE_() TRAP_SEQUENCE_()
#else
#define WRAPPED_TRAP_SEQUENCE_() \
do { \
[] { TRAP_SEQUENCE_(); }(); \
} while (false)
#endif // !V8_CC_GNU
#if defined(__clang__) || V8_CC_GNU
// __builtin_unreachable() hints to the compiler that this is noreturn and can
// be packed in the function epilogue.
#define IMMEDIATE_CRASH() \
({ \
WRAPPED_TRAP_SEQUENCE_(); \
__builtin_unreachable(); \
})
#else
// This is supporting build with MSVC where there is no __builtin_unreachable().
#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE_()
#endif // defined(__clang__) || defined(COMPILER_GCC)
#endif // V8_BASE_IMMEDIATE_CRASH_H_

126
deps/v8/src/base/intrusive-set.h vendored Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2023 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_INTRUSIVE_SET_H_
#define V8_BASE_INTRUSIVE_SET_H_
#include <iterator>
#include <limits>
#include <type_traits>
#include "src/base/logging.h"
namespace v8::base {
class IntrusiveSetIndex {
private:
template <class T, class GetIntrusiveSetIndex, class Container>
friend class IntrusiveSet;
static constexpr size_t kNotInSet = std::numeric_limits<size_t>::max();
size_t value = kNotInSet;
};
// A set of pointer-like values (`T`) that point to memory containing the
// position inside of the set (`IntrusiveSetIndex`), to allow for O(1) insertion
// and removal without using a hash table. This set is intrusive in the sense
// that elements need to know their position inside of the set by storing an
// `IntrusiveSetIndex` somewhere. In particular, all copies of a `T` value
// should point to the same `IntrusiveSetIndex` instance. `GetIntrusiveSetIndex`
// has to be a functor that produces `IntrusiveSetIndex&` given a `T`. The
// reference has to remain valid and refer to the same memory location while the
// element is in the set and until we finish iterating over the data structure
// if the element is removed during iteration.
//
// Add(T): amortized O(1)
// Contain(T): O(1)
// Remove(T): O(1)
template <class T, class GetIntrusiveSetIndex, class Container>
class IntrusiveSet {
public:
// This is not needed for soundness, but rather serves as a hint that `T`
// should be a lightweight pointer-like value.
static_assert(std::is_trivially_copyable_v<T>);
explicit IntrusiveSet(Container container,
GetIntrusiveSetIndex index_functor = {})
: elements_(std::move(container)), index_functor_(index_functor) {
static_assert(std::is_same_v<decltype(index_functor(std::declval<T>())),
IntrusiveSetIndex&>);
}
bool Contains(T x) const { return Index(x) != IntrusiveSetIndex::kNotInSet; }
// Adding elements while iterating is allowed.
void Add(T x) {
DCHECK(!Contains(x));
Index(x) = elements_.size();
elements_.push_back(x);
}
// Removing while iterating is allowed under very specific circumstances. See
// comment on `IntrusiveSet::iterator`.
void Remove(T x) {
DCHECK(Contains(x));
size_t& index = Index(x);
DCHECK_EQ(x, elements_[index]);
Index(elements_.back()) = index;
elements_[index] = elements_.back();
index = IntrusiveSetIndex::kNotInSet;
elements_.pop_back();
}
// Since C++17, it is possible to have a sentinel end-iterator that is not an
// iterator itself.
class end_iterator {};
// This iterator supports insertion (newly inserted elements will be visited
// as part of the iteration) and removal of the current element while
// iterating. Removing previously visited elements is undefined behavior.
// ATTENTION! The memory the removed element points to needs to remain alive
// until the end of the iteration.
class iterator {
public:
explicit iterator(const IntrusiveSet& set) : set_(set) {}
T operator*() {
T result = set_.elements_[index_];
last_index_location_ = &set_.Index(result);
return result;
}
iterator& operator++() {
// This iterator requires `operator*` being used before `operator++`.
DCHECK_NOT_NULL(last_index_location_);
if (index_ < set_.elements_.size() &&
last_index_location_ == &set_.Index(set_.elements_[index_])) {
index_++;
}
return *this;
}
bool operator!=(end_iterator) const {
return index_ < set_.elements_.size();
}
private:
const IntrusiveSet& set_;
size_t index_ = 0;
// If the current element is removed, another element is swapped in to the
// same position. We notice this by remembering the index memory location of
// the last retrieved element.
const size_t* last_index_location_ = nullptr;
};
// These iterators are only intended for range-based for loops.
iterator begin() const { return iterator{*this}; }
end_iterator end() const { return end_iterator{}; }
private:
Container elements_;
GetIntrusiveSetIndex index_functor_;
size_t& Index(T x) const { return index_functor_(x).value; }
};
} // namespace v8::base
#endif // V8_BASE_INTRUSIVE_SET_H_

31
deps/v8/src/base/ios-headers.h vendored Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2023 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_IOS_HEADERS_H_
#define V8_BASE_IOS_HEADERS_H_
// This file includes the necessary headers that are not part of the
// iOS public SDK in order to support memory allocation on iOS.
#include <mach/mach.h>
#include <mach/vm_map.h>
__BEGIN_DECLS
kern_return_t mach_vm_remap(
vm_map_t target_task, mach_vm_address_t* target_address,
mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src_task,
mach_vm_address_t src_address, boolean_t copy, vm_prot_t* cur_protection,
vm_prot_t* max_protection, vm_inherit_t inheritance);
kern_return_t mach_vm_map(vm_map_t target_task, mach_vm_address_t* address,
mach_vm_size_t size, mach_vm_offset_t mask, int flags,
mem_entry_name_port_t object,
memory_object_offset_t offset, boolean_t copy,
vm_prot_t cur_protection, vm_prot_t max_protection,
vm_inherit_t inheritance);
__END_DECLS
#endif // V8_BASE_IOS_HEADERS_H_

218
deps/v8/src/base/iterator.h vendored Normal file
View File

@ -0,0 +1,218 @@
// Copyright 2014 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_ITERATOR_H_
#define V8_BASE_ITERATOR_H_
#include <iterator>
#include <tuple>
#include <utility>
#include "src/base/logging.h"
namespace v8 {
namespace base {
template <class Category, class Type, class Diff = std::ptrdiff_t,
class Pointer = Type*, class Reference = Type&>
struct iterator {
using iterator_category = Category;
using value_type = Type;
using difference_type = Diff;
using pointer = Pointer;
using reference = Reference;
};
// The intention of the base::iterator_range class is to encapsulate two
// iterators so that the range defined by the iterators can be used like
// a regular STL container (actually only a subset of the full container
// functionality is available usually).
template <typename ForwardIterator>
class iterator_range {
public:
using iterator = ForwardIterator;
using const_iterator = ForwardIterator;
using pointer = typename std::iterator_traits<iterator>::pointer;
using reference = typename std::iterator_traits<iterator>::reference;
using value_type = typename std::iterator_traits<iterator>::value_type;
using difference_type =
typename std::iterator_traits<iterator>::difference_type;
iterator_range() : begin_(), end_() {}
iterator_range(ForwardIterator begin, ForwardIterator end)
: begin_(begin), end_(end) {}
iterator begin() const { return begin_; }
iterator end() const { return end_; }
const_iterator cbegin() const { return begin_; }
const_iterator cend() const { return end_; }
auto rbegin() const { return std::make_reverse_iterator(end_); }
auto rend() const { return std::make_reverse_iterator(begin_); }
bool empty() const { return cbegin() == cend(); }
// Random Access iterators only.
reference operator[](difference_type n) { return begin()[n]; }
difference_type size() const { return cend() - cbegin(); }
private:
const_iterator const begin_;
const_iterator const end_;
};
template <typename ForwardIterator>
auto make_iterator_range(ForwardIterator begin, ForwardIterator end) {
return iterator_range<ForwardIterator>{begin, end};
}
template <class T>
struct DerefPtrIterator : base::iterator<std::bidirectional_iterator_tag, T> {
T* const* ptr;
explicit DerefPtrIterator(T* const* ptr) : ptr(ptr) {}
T& operator*() const { return **ptr; }
DerefPtrIterator& operator++() {
++ptr;
return *this;
}
DerefPtrIterator& operator--() {
--ptr;
return *this;
}
bool operator!=(const DerefPtrIterator& other) const {
return ptr != other.ptr;
}
bool operator==(const DerefPtrIterator& other) const {
return ptr == other.ptr;
}
};
// {Reversed} returns a container adapter usable in a range-based "for"
// statement for iterating a reversible container in reverse order.
//
// Example:
//
// std::vector<int> v = ...;
// for (int i : base::Reversed(v)) {
// // iterates through v from back to front
// }
//
// The signature avoids binding to temporaries (T&& / const T&) on purpose. The
// lifetime of a temporary would not extend to a range-based for loop using it.
template <typename T>
auto Reversed(T& t) {
return make_iterator_range(std::rbegin(t), std::rend(t));
}
// This overload of `Reversed` is safe even when the argument is a temporary,
// because we rely on the wrapped iterators instead of the `iterator_range`
// object itself.
template <typename T>
auto Reversed(const iterator_range<T>& t) {
return make_iterator_range(std::rbegin(t), std::rend(t));
}
// {IterateWithoutLast} returns a container adapter usable in a range-based
// "for" statement for iterating all elements without the last in a forward
// order. It performs a check whether the container is empty.
//
// Example:
//
// std::vector<int> v = ...;
// for (int i : base::IterateWithoutLast(v)) {
// // iterates through v front to --back
// }
//
// The signature avoids binding to temporaries, see the remark in {Reversed}.
template <typename T>
auto IterateWithoutLast(T& t) {
DCHECK_NE(std::begin(t), std::end(t));
auto new_end = std::end(t);
return make_iterator_range(std::begin(t), --new_end);
}
template <typename T>
auto IterateWithoutLast(const iterator_range<T>& t) {
iterator_range<T> range_copy = {t.begin(), t.end()};
return IterateWithoutLast(range_copy);
}
// {IterateWithoutFirst} returns a container adapter usable in a range-based
// "for" statement for iterating all elements without the first in a forward
// order. It performs a check whether the container is empty.
template <typename T>
auto IterateWithoutFirst(T& t) {
DCHECK_NE(std::begin(t), std::end(t));
auto new_begin = std::begin(t);
return make_iterator_range(++new_begin, std::end(t));
}
template <typename T>
auto IterateWithoutFirst(const iterator_range<T>& t) {
iterator_range<T> range_copy = {t.begin(), t.end()};
return IterateWithoutFirst(range_copy);
}
// TupleIterator is an iterator wrapping around multiple iterators. It is use by
// the `zip` function below to iterate over multiple containers at once.
template <class... Iterators>
class TupleIterator
: public base::iterator<
std::bidirectional_iterator_tag,
std::tuple<typename std::iterator_traits<Iterators>::reference...>> {
public:
using value_type =
std::tuple<typename std::iterator_traits<Iterators>::reference...>;
explicit TupleIterator(Iterators... its) : its_(its...) {}
TupleIterator& operator++() {
std::apply([](auto&... iterators) { (++iterators, ...); }, its_);
return *this;
}
template <class Other>
bool operator!=(const Other& other) const {
return not_equal_impl(other, std::index_sequence_for<Iterators...>{});
}
value_type operator*() const {
return std::apply(
[](auto&... this_iterators) { return value_type{*this_iterators...}; },
its_);
}
private:
template <class Other, size_t... indices>
bool not_equal_impl(const Other& other,
std::index_sequence<indices...>) const {
return (... || (std::get<indices>(its_) != std::get<indices>(other.its_)));
}
std::tuple<Iterators...> its_;
};
// `zip` creates an iterator_range from multiple containers. It can be used to
// iterate over multiple containers at once. For instance:
//
// std::vector<int> arr = { 2, 4, 6 };
// std::set<double> set = { 3.5, 4.5, 5.5 };
// for (auto [i, d] : base::zip(arr, set)) {
// std::cout << i << " and " << d << std::endl;
// }
//
// Prints "2 and 3.5", "4 and 4.5" and "6 and 5.5".
template <class... Containers>
auto zip(Containers&... containers) {
using TupleIt =
TupleIterator<decltype(std::declval<Containers>().begin())...>;
return base::make_iterator_range(TupleIt(containers.begin()...),
TupleIt(containers.end()...));
}
} // namespace base
} // namespace v8
#endif // V8_BASE_ITERATOR_H_

257
deps/v8/src/base/lazy-instance.h vendored Normal file
View File

@ -0,0 +1,257 @@
// Copyright 2012 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_LAZY_INSTANCE_H_
#define V8_BASE_LAZY_INSTANCE_H_
// The LazyInstance<Type, Traits> class manages a single instance of Type,
// which will be lazily created on the first time it's accessed. This class is
// useful for places you would normally use a function-level static, but you
// need to have guaranteed thread-safety. The Type constructor will only ever
// be called once, even if two threads are racing to create the object. Get()
// and Pointer() will always return the same, completely initialized instance.
//
// LazyInstance is completely thread safe, assuming that you create it safely.
// The class was designed to be POD initialized, so it shouldn't require a
// static constructor. It really only makes sense to declare a LazyInstance as
// a global variable using the LAZY_INSTANCE_INITIALIZER initializer.
//
// LazyInstance is similar to Singleton, except it does not have the singleton
// property. You can have multiple LazyInstance's of the same type, and each
// will manage a unique instance. It also preallocates the space for Type, as
// to avoid allocating the Type instance on the heap. This may help with the
// performance of creating the instance, and reducing heap fragmentation. This
// requires that Type be a complete type so we can determine the size. See
// notes for advanced users below for more explanations.
//
// Example usage:
// static LazyInstance<MyClass>::type my_instance = LAZY_INSTANCE_INITIALIZER;
// void SomeMethod() {
// my_instance.Get().SomeMethod(); // MyClass::SomeMethod()
//
// MyClass* ptr = my_instance.Pointer();
// ptr->DoDoDo(); // MyClass::DoDoDo
// }
//
// Additionally you can override the way your instance is constructed by
// providing your own trait:
// Example usage:
// struct MyCreateTrait {
// static void Construct(void* allocated_ptr) {
// new (allocated_ptr) MyClass(/* extra parameters... */);
// }
// };
// static LazyInstance<MyClass, MyCreateTrait>::type my_instance =
// LAZY_INSTANCE_INITIALIZER;
//
// WARNINGS:
// - This implementation of LazyInstance IS THREAD-SAFE by default. See
// SingleThreadInitOnceTrait if you don't care about thread safety.
// - Lazy initialization comes with a cost. Make sure that you don't use it on
// critical path. Consider adding your initialization code to a function
// which is explicitly called once.
//
// Notes for advanced users:
// LazyInstance can actually be used in two different ways:
//
// - "Static mode" which is the default mode since it is the most efficient
// (no extra heap allocation). In this mode, the instance is statically
// allocated (stored in the global data section at compile time).
// The macro LAZY_STATIC_INSTANCE_INITIALIZER (= LAZY_INSTANCE_INITIALIZER)
// must be used to initialize static lazy instances.
//
// - "Dynamic mode". In this mode, the instance is dynamically allocated and
// constructed (using new) by default. This mode is useful if you have to
// deal with some code already allocating the instance for you (e.g.
// OS::Mutex() which returns a new private OS-dependent subclass of Mutex).
// The macro LAZY_DYNAMIC_INSTANCE_INITIALIZER must be used to initialize
// dynamic lazy instances.
#include <type_traits>
#include "src/base/macros.h"
#include "src/base/once.h"
namespace v8 {
namespace base {
#define LAZY_STATIC_INSTANCE_INITIALIZER { V8_ONCE_INIT, { {} } }
#define LAZY_DYNAMIC_INSTANCE_INITIALIZER { V8_ONCE_INIT, 0 }
// Default to static mode.
#define LAZY_INSTANCE_INITIALIZER LAZY_STATIC_INSTANCE_INITIALIZER
template <typename T>
struct LeakyInstanceTrait {
static void Destroy(T* /* instance */) {}
};
// Traits that define how an instance is allocated and accessed.
template <typename T>
struct StaticallyAllocatedInstanceTrait {
using StorageType = char[sizeof(T)];
using AlignmentType = T;
static T* MutableInstance(StorageType* storage) {
return reinterpret_cast<T*>(storage);
}
template <typename ConstructTrait>
static void InitStorageUsingTrait(StorageType* storage) {
ConstructTrait::Construct(storage);
}
};
template <typename T>
struct DynamicallyAllocatedInstanceTrait {
using StorageType = T*;
using AlignmentType = T*;
static T* MutableInstance(StorageType* storage) {
return *storage;
}
template <typename CreateTrait>
static void InitStorageUsingTrait(StorageType* storage) {
*storage = CreateTrait::Create();
}
};
template <typename T>
struct DefaultConstructTrait {
// Constructs the provided object which was already allocated.
static void Construct(void* allocated_ptr) { new (allocated_ptr) T(); }
};
template <typename T>
struct DefaultCreateTrait {
static T* Create() {
return new T();
}
};
struct ThreadSafeInitOnceTrait {
template <typename Function, typename Storage>
static void Init(OnceType* once, Function function, Storage storage) {
CallOnce(once, function, storage);
}
};
// Initialization trait for users who don't care about thread-safety.
struct SingleThreadInitOnceTrait {
template <typename Function, typename Storage>
static void Init(OnceType* once, Function function, Storage storage) {
if (*once == ONCE_STATE_UNINITIALIZED) {
function(storage);
*once = ONCE_STATE_DONE;
}
}
};
// TODO(pliard): Handle instances destruction (using global destructors).
template <typename T, typename AllocationTrait, typename CreateTrait,
typename InitOnceTrait, typename DestroyTrait /* not used yet. */>
struct LazyInstanceImpl {
public:
using StorageType = typename AllocationTrait::StorageType;
using AlignmentType = typename AllocationTrait::AlignmentType;
private:
static void InitInstance(void* storage) {
AllocationTrait::template InitStorageUsingTrait<CreateTrait>(
static_cast<StorageType*>(storage));
}
void Init() const {
InitOnceTrait::Init(&once_, &InitInstance, static_cast<void*>(&storage_));
}
public:
T* Pointer() {
Init();
return AllocationTrait::MutableInstance(&storage_);
}
const T& Get() const {
Init();
return *AllocationTrait::MutableInstance(&storage_);
}
mutable OnceType once_;
alignas(AlignmentType) mutable StorageType storage_;
};
template <typename T,
typename CreateTrait = DefaultConstructTrait<T>,
typename InitOnceTrait = ThreadSafeInitOnceTrait,
typename DestroyTrait = LeakyInstanceTrait<T> >
struct LazyStaticInstance {
using type = LazyInstanceImpl<T, StaticallyAllocatedInstanceTrait<T>,
CreateTrait, InitOnceTrait, DestroyTrait>;
};
template <typename T,
typename CreateTrait = DefaultConstructTrait<T>,
typename InitOnceTrait = ThreadSafeInitOnceTrait,
typename DestroyTrait = LeakyInstanceTrait<T> >
struct LazyInstance {
// A LazyInstance is a LazyStaticInstance.
using type = typename LazyStaticInstance<T, CreateTrait, InitOnceTrait,
DestroyTrait>::type;
};
template <typename T,
typename CreateTrait = DefaultCreateTrait<T>,
typename InitOnceTrait = ThreadSafeInitOnceTrait,
typename DestroyTrait = LeakyInstanceTrait<T> >
struct LazyDynamicInstance {
using type = LazyInstanceImpl<T, DynamicallyAllocatedInstanceTrait<T>,
CreateTrait, InitOnceTrait, DestroyTrait>;
};
// LeakyObject<T> wraps an object of type T, which is initialized in the
// constructor but never destructed. Thus LeakyObject<T> is trivially
// destructible and can be used in static (lazily initialized) variables.
template <typename T>
class LeakyObject {
public:
template <typename... Args>
explicit LeakyObject(Args&&... args) {
new (storage_) T(std::forward<Args>(args)...);
}
LeakyObject(const LeakyObject&) = delete;
LeakyObject& operator=(const LeakyObject&) = delete;
T* get() { return reinterpret_cast<T*>(storage_); }
private:
alignas(T) char storage_[sizeof(T)];
};
// Define a function which returns a pointer to a lazily initialized and never
// destructed object of type T.
#define DEFINE_LAZY_LEAKY_OBJECT_GETTER(T, FunctionName, ...) \
T* FunctionName() { \
static ::v8::base::LeakyObject<T> object{__VA_ARGS__}; \
return object.get(); \
}
} // namespace base
} // namespace v8
#endif // V8_BASE_LAZY_INSTANCE_H_

228
deps/v8/src/base/logging.cc vendored Normal file
View File

@ -0,0 +1,228 @@
// Copyright 2006-2008 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.
#include "src/base/logging.h"
#include <cctype>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include "src/base/debug/stack_trace.h"
#include "src/base/platform/platform.h"
namespace v8 {
namespace base {
namespace {
void DefaultDcheckHandler(const char* file, int line, const char* message);
void (*g_print_stack_trace)() = nullptr;
void (*g_dcheck_function)(const char*, int, const char*) = DefaultDcheckHandler;
void (*g_fatal_function)(const char*, int, const char*) = nullptr;
std::string PrettyPrintChar(int ch) {
std::ostringstream oss;
switch (ch) {
#define CHAR_PRINT_CASE(ch) \
case ch: \
oss << #ch; \
break;
CHAR_PRINT_CASE('\0')
CHAR_PRINT_CASE('\'')
CHAR_PRINT_CASE('\\')
CHAR_PRINT_CASE('\a')
CHAR_PRINT_CASE('\b')
CHAR_PRINT_CASE('\f')
CHAR_PRINT_CASE('\n')
CHAR_PRINT_CASE('\r')
CHAR_PRINT_CASE('\t')
CHAR_PRINT_CASE('\v')
#undef CHAR_PRINT_CASE
default:
if (std::isprint(ch)) {
oss << '\'' << ch << '\'';
} else {
oss << std::hex << "\\x" << static_cast<unsigned int>(ch);
}
}
return oss.str();
}
void DefaultDcheckHandler(const char* file, int line, const char* message) {
#ifdef DEBUG
V8_Fatal(file, line, "Debug check failed: %s.", message);
#else
// This case happens only for unit tests.
V8_Fatal("Debug check failed: %s.", message);
#endif
}
} // namespace
void SetPrintStackTrace(void (*print_stack_trace)()) {
g_print_stack_trace = print_stack_trace;
}
void SetDcheckFunction(void (*dcheck_function)(const char*, int, const char*)) {
g_dcheck_function = dcheck_function ? dcheck_function : &DefaultDcheckHandler;
}
void SetFatalFunction(void (*fatal_function)(const char*, int, const char*)) {
g_fatal_function = fatal_function;
}
void FatalOOM(OOMType type, const char* msg) {
// Instead of directly aborting here with a message, it could make sense to
// call a global callback function that would then in turn call (the
// equivalent of) V8::FatalProcessOutOfMemory. This way, calling this
// function directly would not bypass any OOM handler installed by the
// embedder. We might still want to keep a function like this though that
// contains the fallback implementation if no callback has been installed.
const char* type_str = type == OOMType::kProcess ? "process" : "JavaScript";
OS::PrintError("\n\n#\n# Fatal %s out of memory: %s\n#", type_str, msg);
if (g_print_stack_trace) v8::base::g_print_stack_trace();
fflush(stderr);
#ifdef V8_FUZZILLI
// When fuzzing, we generally want to ignore OOM failures.
// It's important that we exit with a non-zero exit status here so that the
// fuzzer treats it as a failed execution.
_exit(1);
#else
OS::Abort();
#endif // V8_FUZZILLI
}
// Define specialization to pretty print characters (escaping non-printable
// characters) and to print c strings as pointers instead of strings.
#define DEFINE_PRINT_CHECK_OPERAND_CHAR(type) \
template <> \
std::string PrintCheckOperand<type>(type ch) { \
return PrettyPrintChar(ch); \
} \
template <> \
std::string PrintCheckOperand<type*>(type * cstr) { \
return PrintCheckOperand<void*>(cstr); \
} \
template <> \
std::string PrintCheckOperand<const type*>(const type* cstr) { \
return PrintCheckOperand<const void*>(cstr); \
}
DEFINE_PRINT_CHECK_OPERAND_CHAR(char)
DEFINE_PRINT_CHECK_OPERAND_CHAR(signed char)
DEFINE_PRINT_CHECK_OPERAND_CHAR(unsigned char)
#undef DEFINE_PRINT_CHECK_OPERAND_CHAR
// Explicit instantiations for commonly used comparisons.
#define DEFINE_MAKE_CHECK_OP_STRING(type) \
template std::string* MakeCheckOpString<type, type>(type, type, \
char const*); \
template std::string PrintCheckOperand<type>(type);
DEFINE_MAKE_CHECK_OP_STRING(int)
DEFINE_MAKE_CHECK_OP_STRING(long) // NOLINT(runtime/int)
DEFINE_MAKE_CHECK_OP_STRING(long long) // NOLINT(runtime/int)
DEFINE_MAKE_CHECK_OP_STRING(unsigned int)
DEFINE_MAKE_CHECK_OP_STRING(unsigned long) // NOLINT(runtime/int)
DEFINE_MAKE_CHECK_OP_STRING(unsigned long long) // NOLINT(runtime/int)
DEFINE_MAKE_CHECK_OP_STRING(void const*)
#undef DEFINE_MAKE_CHECK_OP_STRING
} // namespace base
} // namespace v8
namespace {
// FailureMessage is a stack allocated object which has a special marker field
// at the start and at the end. This makes it possible to retrieve the embedded
// message from the stack.
//
class FailureMessage {
public:
explicit FailureMessage(const char* format, va_list arguments) {
memset(&message_, 0, arraysize(message_));
v8::base::OS::VSNPrintF(&message_[0], arraysize(message_), format,
arguments);
}
static const uintptr_t kStartMarker = 0xdecade10;
static const uintptr_t kEndMarker = 0xdecade11;
static const int kMessageBufferSize = 512;
uintptr_t start_marker_ = kStartMarker;
char message_[kMessageBufferSize];
uintptr_t end_marker_ = kEndMarker;
};
} // namespace
#ifdef DEBUG
void V8_Fatal(const char* file, int line, const char* format, ...) {
#else
void V8_Fatal(const char* format, ...) {
const char* file = "";
int line = 0;
#endif
va_list arguments;
va_start(arguments, format);
// Format the error message into a stack object for later retrieveal by the
// crash processor.
FailureMessage message(format, arguments);
va_end(arguments);
if (v8::base::g_fatal_function != nullptr) {
v8::base::g_fatal_function(file, line, message.message_);
}
fflush(stdout);
fflush(stderr);
// Print the formatted message to stdout without cropping the output.
if (v8::base::ControlledCrashesAreHarmless()) {
// In this case, instead of crashing the process will be terminated
// normally by OS::Abort. Make this clear in the output printed to stderr.
v8::base::OS::PrintError(
"\n\n#\n# Safely terminating process due to error in %s, line %d\n# ",
file, line);
// Also prefix the error message (printed below). This has two purposes:
// (1) it makes it clear that this error is deemed "safe" (2) it causes
// fuzzers that pattern-match on stderr output to ignore these failures.
v8::base::OS::PrintError("The following harmless error was encountered: ");
} else {
v8::base::OS::PrintError("\n\n#\n# Fatal error in %s, line %d\n# ", file,
line);
}
// Print the error message.
va_start(arguments, format);
v8::base::OS::VPrintError(format, arguments);
va_end(arguments);
// Print the message object's address to force stack allocation.
v8::base::OS::PrintError("\n#\n#\n#\n#FailureMessage Object: %p", &message);
if (v8::base::g_print_stack_trace) v8::base::g_print_stack_trace();
fflush(stderr);
v8::base::OS::Abort();
}
void V8_Dcheck(const char* file, int line, const char* message) {
if (v8::base::DcheckFailuresAreIgnored()) {
// In this mode, DCHECK failures don't lead to process termination.
v8::base::OS::PrintError(
"# Ignoring debug check failure in %s, line %d: %s\n", file, line,
message);
return;
}
v8::base::g_dcheck_function(file, line, message);
}

Some files were not shown because too many files have changed in this diff Show More