From e52752f4a645f53141d1ba03eae846c44a6f5090 Mon Sep 17 00:00:00 2001 From: andycall Date: Sat, 30 Mar 2024 20:40:32 +0800 Subject: [PATCH 01/35] feat: add v8 release and atomicString impl --- .gitmodules | 4 + bridge/CMakeLists.txt | 206 ++++++++++++++++--- bridge/bindings/v8/atomic_string.cc | 304 ++++++++++++++++++++++++++++ bridge/bindings/v8/atomic_string.h | 108 ++++++++++ bridge/core/dart_isolate_context.cc | 46 ++++- bridge/core/dart_isolate_context.h | 12 ++ bridge/core/dom/element_data.h | 4 + bridge/core/executing_context.cc | 13 +- bridge/core/executing_context.h | 67 +++--- bridge/core/script_state.cc | 19 ++ bridge/core/script_state.h | 21 +- bridge/test/test.cmake | 5 +- bridge/third_party/v8 | 1 + scripts/migrate_to_v8_support.js | 24 +++ scripts/tasks.js | 7 + 15 files changed, 769 insertions(+), 72 deletions(-) create mode 100644 bridge/bindings/v8/atomic_string.cc create mode 100644 bridge/bindings/v8/atomic_string.h create mode 160000 bridge/third_party/v8 create mode 100644 scripts/migrate_to_v8_support.js diff --git a/.gitmodules b/.gitmodules index 20b258b67a..55e2163811 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,7 @@ [submodule "bridge/third_party/quickjs/compat/win32/pthread-win32"] path = bridge/third_party/quickjs/compat/win32/pthread-win32 url = git@github.com:GerHobbelt/pthread-win32.git +[submodule "bridge/third_party/v8"] + path = bridge/third_party/v8 + url = git@github.com:openwebf/v8-release.git + branch = 11.9.169.6 \ No newline at end of file diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index b63cf703c8..8ea0b88e21 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -59,18 +59,6 @@ list(APPEND WEBF_PUBLIC_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/webf_bridge.h ) -set(QUICKJS_PUBLIC_HEADERS - third_party/quickjs/cutils.h - third_party/quickjs/libregexp.h - third_party/quickjs/libregexp-opcode.h - third_party/quickjs/libunicode.h - third_party/quickjs/libunicode-table.h - third_party/quickjs/list.h - third_party/quickjs/quickjs.h - third_party/quickjs/quickjs-atom.h - third_party/quickjs/quickjs-opcode.h -) - if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") add_compile_options(-fPIC) endif() @@ -142,6 +130,18 @@ list(APPEND BRIDGE_INCLUDE ) if (${WEBF_JS_ENGINE} MATCHES "quickjs") + set(QUICKJS_PUBLIC_HEADERS + third_party/quickjs/cutils.h + third_party/quickjs/libregexp.h + third_party/quickjs/libregexp-opcode.h + third_party/quickjs/libunicode.h + third_party/quickjs/libunicode-table.h + third_party/quickjs/list.h + third_party/quickjs/quickjs.h + third_party/quickjs/quickjs-atom.h + third_party/quickjs/quickjs-opcode.h + ) + add_compile_options(-DWEBF_QUICK_JS_ENGINE=1) execute_process( @@ -262,7 +262,6 @@ if (${WEBF_JS_ENGINE} MATCHES "quickjs") list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party) list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/modp_b64/include) list(APPEND BRIDGE_LINK_LIBS quickjs) - list(APPEND BRIDGE_LINK_LIBS modb) list(APPEND BRIDGE_SOURCE # Binding files @@ -608,23 +607,7 @@ if (${WEBF_JS_ENGINE} MATCHES "quickjs") out/qjs_svg_ellipse_element.cc out/qjs_svg_style_element.cc out/qjs_svg_line_element.cc - - # plugin api - out/plugin_api_event.cc - out/plugin_api_animation_event.cc - out/plugin_api_close_event.cc - out/plugin_api_focus_event.cc - out/plugin_api_gesture_event.cc - out/plugin_api_hashchange_event.cc - out/plugin_api_input_event.cc - out/plugin_api_intersection_change_event.cc - out/plugin_api_mouse_event.cc - out/plugin_api_pointer_event.cc - out/plugin_api_transition_event.cc - out/plugin_api_ui_event.cc - out/plugin_api_custom_event.cc - ) - + ) if (NOT MSVC) # Quickjs use __builtin_frame_address() to get stack pointer, we should add follow options to get it work with -O2 @@ -633,7 +616,168 @@ if (${WEBF_JS_ENGINE} MATCHES "quickjs") endif() target_compile_options(quickjs PUBLIC -DCONFIG_VERSION=${\"QUICKJS_VERSION\"}) -endif () +elseif (${WEBF_JS_ENGINE} MATCHES "v8") + + add_compile_options(-DWEBF_V8_JS_ENGINE=1) + + list(APPEND BRIDGE_SOURCE + # Binding files + bindings/v8/atomic_string.cc + ) + + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + add_library(v8 SHARED IMPORTED) + set_target_properties(v8 PROPERTIES IMPORTED_LOCATION "third_party/v8/lib/macos/arm64/libv8.dylib") + add_library(v8_platform SHARED IMPORTED) + set_target_properties(v8_platform PROPERTIES IMPORTED_LOCATION "third_party/v8/lib/macos/arm64/libv8_libplatform.dylib") + list(APPEND BRIDGE_LINK_LIBS v8 v8_platform) + endif() + list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/include) + list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/include/v8) + list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/include/v8/cppgc) + list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/include/v8/libplatform) +endif() + +list(APPEND BRIDGE_LINK_LIBS modb) + +list(APPEND BRIDGE_SOURCE + # Core sources + webf_bridge.cc + core/api/api.cc + core/executing_context.cc + core/script_forbidden_scope.cc + core/script_state.cc + core/page.cc + core/dart_methods.cc + core/dart_isolate_context.cc + core/dart_context_data.cc + core/executing_context_data.cc + core/fileapi/blob.cc + core/fileapi/blob_part.cc + core/fileapi/blob_property_bag.cc + core/frame/console.cc + core/frame/dom_timer.cc + core/frame/dom_timer_coordinator.cc + core/frame/window_or_worker_global_scope.cc + core/frame/module_listener.cc + core/frame/module_listener_container.cc + core/frame/module_manager.cc + core/frame/module_callback.cc + core/frame/module_context_coordinator.cc + core/frame/window.cc + core/frame/screen.cc + core/frame/legacy/location.cc + core/timing/performance.cc + core/timing/performance_mark.cc + core/timing/performance_entry.cc + core/timing/performance_measure.cc + core/css/css_style_declaration.cc + core/css/inline_css_style_declaration.cc + core/css/computed_css_style_declaration.cc + core/dom/frame_request_callback_collection.cc + core/dom/events/registered_eventListener.cc + core/dom/events/event_listener_map.cc + core/dom/events/event.cc + core/dom/events/custom_event.cc + core/dom/events/event_target.cc + core/dom/events/event_listener_map.cc + core/dom/events/event_target_impl.cc + core/binding_object.cc + core/dom/node.cc + core/dom/node_list.cc + core/dom/static_node_list.cc + core/dom/node_traversal.cc + core/dom/live_node_list_base.cc + core/dom/character_data.cc + core/dom/comment.cc + core/dom/text.cc + core/dom/tree_scope.cc + core/dom/element.cc + core/dom/parent_node.cc + core/dom/element_data.cc + core/dom/document.cc + core/dom/dom_token_list.cc + core/dom/dom_string_map.cc + core/dom/space_split_string.cc + core/dom/scripted_animation_controller.cc + core/dom/node_data.cc + core/dom/document_fragment.cc + core/dom/child_node_list.cc + core/dom/empty_node_list.cc + core/dom/mutation_observer.cc + core/dom/mutation_observer_registration.cc + core/dom/mutation_observer_interest_group.cc + core/dom/mutation_record.cc + core/dom/child_list_mutation_scope.cc + core/dom/container_node.cc + core/html/custom/widget_element.cc + core/events/error_event.cc + core/events/message_event.cc + core/events/animation_event.cc + core/events/close_event.cc + core/events/ui_event.cc + core/events/focus_event.cc + core/events/gesture_event.cc + core/events/input_event.cc + core/events/touch_event.cc + core/events/mouse_event.cc + core/events/pop_state_event.cc + core/events/pointer_event.cc + core/events/transition_event.cc + core/events/intersection_change_event.cc + core/events/keyboard_event.cc + core/events/promise_rejection_event.cc + core/html/parser/html_parser.cc + core/html/html_element.cc + core/html/html_div_element.cc + core/html/html_head_element.cc + core/html/html_body_element.cc + core/html/html_html_element.cc + core/html/html_template_element.cc + core/html/html_all_collection.cc + core/html/html_anchor_element.cc + core/html/html_image_element.cc + core/html/html_script_element.cc + core/html/html_iframe_element.cc + core/html/html_link_element.cc + core/html/html_unknown_element.cc + core/html/image.cc + core/html/html_collection.cc + core/html/canvas/html_canvas_element.cc + core/html/canvas/canvas_rendering_context.cc + core/html/canvas/canvas_rendering_context_2d.cc + core/html/canvas/canvas_gradient.cc + core/html/canvas/canvas_pattern.cc + core/geometry/dom_matrix.cc + core/geometry/dom_matrix_readonly.cc + core/html/forms/html_button_element.cc + core/html/forms/html_input_element.cc + core/html/forms/html_form_element.cc + core/html/forms/html_textarea_element.cc + + # SVG files + core/svg/svg_element.cc + core/svg/svg_graphics_element.cc + core/svg/svg_geometry_element.cc + core/svg/svg_text_content_element.cc + core/svg/svg_text_positioning_element.cc + + core/svg/svg_svg_element.cc + core/svg/svg_path_element.cc + core/svg/svg_rect_element.cc + core/svg/svg_text_element.cc + core/svg/svg_g_element.cc + core/svg/svg_circle_element.cc + core/svg/svg_ellipse_element.cc + core/svg/svg_style_element.cc + core/svg/svg_line_element.cc + + # Legacy implements, should remove them in the future. + core/dom/legacy/element_attributes.cc + core/dom/legacy/bounding_client_rect.cc + core/input/touch.cc + core/input/touch_list.cc +) list(APPEND PUBLIC_HEADER include/webf_bridge.h diff --git a/bridge/bindings/v8/atomic_string.cc b/bridge/bindings/v8/atomic_string.cc new file mode 100644 index 0000000000..c7bc050269 --- /dev/null +++ b/bridge/bindings/v8/atomic_string.cc @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "atomic_string.h" +#include +#include +#include "built_in_string.h" +#include "foundation/native_string.h" + +namespace webf { + +AtomicString AtomicString::Empty() { + return built_in_string::kempty_string; +} + +AtomicString AtomicString::Null() { + return built_in_string::kNULL; +} + +namespace { + +AtomicString::StringKind GetStringKind(const std::string& string, size_t length) { + char first_char = string[0]; + + if (first_char < 0 || first_char > 255) { + return AtomicString::StringKind::kUnknown; + } + + AtomicString::StringKind predictKind = + std::islower(string[0]) ? AtomicString::StringKind::kIsLowerCase : AtomicString::StringKind::kIsUpperCase; + for (int i = 0; i < length; i++) { + char c = string[i]; + + if (c < 0 || c > 255) { + return AtomicString::StringKind::kUnknown; + } + + if (predictKind == AtomicString::StringKind::kIsUpperCase && !std::isupper(c)) { + return AtomicString::StringKind::kIsMixed; + } else if (predictKind == AtomicString::StringKind::kIsLowerCase && !std::islower(c)) { + return AtomicString::StringKind::kIsMixed; + } + } + return predictKind; +} + +AtomicString::StringKind GetStringKind(const SharedNativeString* native_string) { + if (!native_string->length()) { + return AtomicString::StringKind::kIsMixed; + } + + AtomicString::StringKind predictKind = std::islower(native_string->string()[0]) + ? AtomicString::StringKind::kIsLowerCase + : AtomicString::StringKind::kIsUpperCase; + for (int i = 0; i < native_string->length(); i++) { + uint16_t c = native_string->string()[i]; + if (predictKind == AtomicString::StringKind::kIsUpperCase && !std::isupper(c)) { + return AtomicString::StringKind::kIsMixed; + } else if (predictKind == AtomicString::StringKind::kIsLowerCase && !std::islower(c)) { + return AtomicString::StringKind::kIsMixed; + } + } + + return predictKind; +} + +} // namespace + +class AtomicStringOneByteResource : public v8::String::ExternalOneByteStringResource { + public: + AtomicStringOneByteResource(const std::string& string) : string_(string){}; + + const char* data() const override { return string_.data(); }; + size_t length() const override { return string_.length(); }; + + private: + std::string string_; +}; + +class AtomicStringTwoByteResource : public v8::String::ExternalStringResource { + public: + AtomicStringTwoByteResource(std::unique_ptr&& native_string) + : string_(std::move(native_string)) {} + + const uint16_t* data() const override { return string_->string(); } + size_t length() const override { return string_->length(); } + + private: + std::unique_ptr string_; +}; + +AtomicString::AtomicString(v8::Isolate* isolate, const std::string& string) + : kind_(GetStringKind(string, string.size())) { + auto* external_string_resource = new AtomicStringOneByteResource(string); + string_ = v8::String::NewExternalOneByte(isolate, external_string_resource).ToLocalChecked(); +} + +AtomicString::AtomicString(v8::Isolate* isolate, const char* str, size_t length) : kind_(GetStringKind(str, length)) { + auto* external_string_resource = new AtomicStringOneByteResource(std::string(str, length)); + string_ = v8::String::NewExternalOneByte(isolate, external_string_resource).ToLocalChecked(); +} + +AtomicString::AtomicString(v8::Isolate* isolate, std::unique_ptr&& native_string) { + auto* external_resource = new AtomicStringTwoByteResource(std::move(native_string)); + kind_ = GetStringKind(native_string.get()); + string_ = v8::String::NewExternalTwoByte(isolate, external_resource).ToLocalChecked(); +} + +AtomicString::AtomicString(v8::Isolate* isolate, const uint16_t* str, size_t length) { + auto native_string = std::unique_ptr( + reinterpret_cast(new SharedNativeString(str, length))); + kind_ = GetStringKind(native_string.get()); + auto* external_resource = new AtomicStringTwoByteResource(std::move(native_string)); + string_ = v8::String::NewExternalTwoByte(isolate, external_resource).ToLocalChecked(); +} + +bool AtomicString::IsEmpty() const { + return *this == built_in_string::kempty_string || IsNull(); +} + +bool AtomicString::IsNull() const { + return string_->IsNull(); +} + +bool AtomicString::Is8Bit() const { + return string_->IsExternalOneByte(); +} + +const uint8_t* AtomicString::Character8() const { + assert(string_->IsExternal()); + return reinterpret_cast(string_->GetExternalOneByteStringResource()->data()); +} + +const uint16_t* AtomicString::Character16() const { + assert(string_->IsExternal()); + return string_->GetExternalStringResource()->data(); +} + +int AtomicString::Find(bool (*CharacterMatchFunction)(char)) const { +// return JS_FindCharacterInAtom(runtime_, atom_, CharacterMatchFunction); +} + +int AtomicString::Find(bool (*CharacterMatchFunction)(uint16_t)) const { +// return JS_FindWCharacterInAtom(runtime_, atom_, CharacterMatchFunction); +} + +std::string AtomicString::ToStdString(v8::Isolate* isolate) const { + if (IsEmpty()) + return ""; + + if (string_->IsExternalOneByte()) { + return {string_->GetExternalOneByteStringResource()->data()}; + } + + std::string result; + size_t length = string_->Utf8Length(isolate); + result.reserve(length); + + string_->WriteUtf8(isolate, result.data(), length); + return result; +} + +std::unique_ptr AtomicString::ToNativeString(v8::Isolate* isolate) const { + if (IsNull()) { + // Null string is same like empty string + return built_in_string::kempty_string.ToNativeString(isolate); + } + + if (string_->IsExternalTwoByte()) { + auto* resource = string_->GetExternalStringResource(); + return SharedNativeString::FromTemporaryString(resource->data(), resource->length()); + } + + size_t length = string_->Length(); + std::vector buffer; + buffer.reserve(length); + + string_->Write(isolate, buffer.data(), length); + return SharedNativeString::FromTemporaryString(buffer.data(), buffer.size()); +} + +StringView AtomicString::ToStringView() const { + if (IsNull()) { + return built_in_string::kempty_string.ToStringView(); + } + + if (string_->IsExternalOneByte()) { + auto* resource = string_->GetExternalOneByteStringResource(); + return StringView((void*)(resource->data()), resource->length(), false); + } + + auto* resource = string_->GetExternalStringResource(); + + return StringView((void*)(resource->data()), resource->length(), true); +} + +AtomicString AtomicString::ToUpperIfNecessary(v8::Isolate* isolate) const { + if (kind_ == StringKind::kIsUpperCase) { + return *this; + } + if (!string_upper_->IsNull() || IsNull()) + return *this; + AtomicString upperString = ToUpperSlow(isolate); + string_upper_ = v8::Local(upperString.string_); + return upperString; +} + +AtomicString AtomicString::ToUpperSlow(v8::Isolate* isolate) const { + std::string str = ToStdString(isolate); + std::transform(str.begin(), str.end(), str.begin(), toupper); + return {isolate, str}; +} + +AtomicString AtomicString::ToLowerIfNecessary(v8::Isolate* isolate) const { + if (kind_ == StringKind::kIsLowerCase) { + return *this; + } + if (!string_lower_->IsNull() || IsNull()) + return *this; + AtomicString lowerString = ToLowerSlow(isolate); + string_lower_ = lowerString.string_; + return lowerString; +} + +AtomicString AtomicString::ToLowerSlow(v8::Isolate* isolate) const { + std::string str = ToStdString(isolate); + std::transform(str.begin(), str.end(), str.begin(), tolower); + return {isolate, str}; +} + +template +inline AtomicString RemoveCharactersInternal(v8::Isolate* isolate, + const AtomicString& self, + const CharType* characters, + size_t len, + CharacterMatchFunctionPtr find_match) { + const CharType* from = characters; + const CharType* fromend = from + len; + + // Assume the common case will not remove any characters + while (from != fromend && !find_match(*from)) + ++from; + if (from == fromend) + return self; + + auto* to = (CharType*)malloc(len); + size_t outc = static_cast(from - characters); + + if (outc) + memcpy(to, characters, outc * sizeof(CharType)); + + while (true) { + while (from != fromend && find_match(*from)) + ++from; + while (from != fromend && !find_match(*from)) + to[outc++] = *from++; + if (from == fromend) + break; + } + + AtomicString str; + + if (outc == 0) { + return AtomicString::Empty(); + } + + auto data = (CharType*)malloc(outc); + memcpy(data, to, outc); + free(to); + if (self.Is8Bit()) { + str = AtomicString(isolate, reinterpret_cast(data), outc); + } else { + str = AtomicString(isolate, reinterpret_cast(data), outc); + } + + free(data); + return str; +} + +AtomicString AtomicString::RemoveCharacters(v8::Isolate* isolate, CharacterMatchFunctionPtr find_match) { + if (IsEmpty()) + return AtomicString::Empty(); + if (Is8Bit()) + return RemoveCharactersInternal(isolate, *this, Character8(), string_->Utf8Length(isolate), find_match); + return RemoveCharactersInternal(isolate, *this, Character16(), string_->Length(), find_match); +} + +AtomicString::AtomicString(const webf::AtomicString& value) { + string_ = v8::Local(value.string_); +} +AtomicString& AtomicString::operator=(const webf::AtomicString& other) { + string_ = v8::Local(other.string_); +} + +AtomicString::AtomicString(webf::AtomicString&& value) noexcept { + string_ = v8::Local(value.string_); +} +AtomicString& AtomicString::operator=(webf::AtomicString&& value) noexcept { + string_ = v8::Local(value.string_); +} + +} // namespace webf diff --git a/bridge/bindings/v8/atomic_string.h b/bridge/bindings/v8/atomic_string.h new file mode 100644 index 0000000000..5b2a5ae26a --- /dev/null +++ b/bridge/bindings/v8/atomic_string.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef BRIDGE_BINDINGS_QJS_ATOMIC_STRING_H_ +#define BRIDGE_BINDINGS_QJS_ATOMIC_STRING_H_ + +#include +#include +#include +#include +#include "foundation/macros.h" +#include "foundation/native_string.h" +#include "foundation/string_view.h" + +namespace webf { + +typedef bool (*CharacterMatchFunctionPtr)(char); + +// An AtomicString instance represents a string, and multiple AtomicString +// instances can share their string storage if the strings are +// identical. Comparing two AtomicString instances is much faster than comparing +// two String instances because we just check string storage identity. +class AtomicString { + WEBF_DISALLOW_NEW(); + + public: + enum class StringKind { kIsLowerCase, kIsUpperCase, kIsMixed, kUnknown }; + + struct KeyHasher { + std::size_t operator()(const AtomicString& k) const { return k.string_->GetIdentityHash(); } + }; + + static AtomicString Empty(); + static AtomicString Null(); + + AtomicString() = default; + AtomicString(v8::Isolate* isolate, const std::string& string); + AtomicString(v8::Isolate* isolate, const char* str, size_t length); + AtomicString(v8::Isolate* isolate, std::unique_ptr&& native_string); + AtomicString(v8::Isolate* isolate, const uint16_t* str, size_t length); + + // Return the undefined string value from atom key. + v8::Local ToV8(v8::Isolate* isolate) const { + return string_.As(); + } + + bool IsEmpty() const; + bool IsNull() const; + + int64_t length() const { return string_->Length(); } + + bool Is8Bit() const; + const uint8_t* Character8() const; + const uint16_t* Character16() const; + + int Find(bool (*CharacterMatchFunction)(char)) const; + int Find(bool (*CharacterMatchFunction)(uint16_t)) const; + + [[nodiscard]] std::string ToStdString(v8::Isolate* isolate) const; + [[nodiscard]] std::unique_ptr ToNativeString(v8::Isolate* isolate) const; + + StringView ToStringView() const; + + AtomicString ToUpperIfNecessary(v8::Isolate* isolate) const; + AtomicString ToUpperSlow(v8::Isolate* isolate) const; + + AtomicString ToLowerIfNecessary(v8::Isolate* isolate) const; + AtomicString ToLowerSlow(v8::Isolate* isolate) const; + + inline bool ContainsOnlyLatin1OrEmpty() const; + AtomicString RemoveCharacters(v8::Isolate* isolate, CharacterMatchFunctionPtr find_match); + + // Copy assignment + AtomicString(AtomicString const& value); + AtomicString& operator=(const AtomicString& other); + + // Move assignment + AtomicString(AtomicString&& value) noexcept; + AtomicString& operator=(AtomicString&& value) noexcept; + + bool operator==(const AtomicString& other) const { return other.string_->StringEquals(string_); } + bool operator!=(const AtomicString& other) const { return !other.string_->StringEquals(string_); }; + + protected: + StringKind kind_; + v8::Local string_; + mutable v8::Local string_upper_; + mutable v8::Local string_lower_; +}; + +bool AtomicString::ContainsOnlyLatin1OrEmpty() const { + if (IsEmpty()) + return true; + + if (Is8Bit()) + return true; + + const uint16_t* characters = Character16(); + uint16_t ored = 0; + for (size_t i = 0; i < string_->Length(); ++i) + ored |= characters[i]; + return !(ored & 0xFF00); +} +} // namespace webf + +#endif // BRIDGE_BINDINGS_QJS_ATOMIC_STRING_H_ diff --git a/bridge/core/dart_isolate_context.cc b/bridge/core/dart_isolate_context.cc index 3787b1e533..dabdba0c74 100644 --- a/bridge/core/dart_isolate_context.cc +++ b/bridge/core/dart_isolate_context.cc @@ -2,13 +2,14 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ +#if WEBF_V8_JS_ENGINE +#include +#include "v8/libplatform/libplatform.h" +#endif #include "dart_isolate_context.h" #include -#include "defined_properties_initializer.h" #include "event_factory.h" #include "html_element_factory.h" -#include "logging.h" -#include "multiple_threading/looper.h" #include "names_installer.h" #include "page.h" #include "svg_element_factory.h" @@ -60,18 +61,33 @@ const std::unique_ptr& DartIsolateContext::EnsureData() const { return data_; } +#if WEBF_V8_JS_ENGINE + std::unique_ptr platform = nullptr; + thread_local v8::Isolate* isolate_{nullptr}; +#elif WEBF_QUICKJS_JS_ENGINE thread_local JSRuntime* runtime_{nullptr}; +#endif thread_local uint32_t running_dart_isolates = 0; thread_local bool is_name_installed_ = false; +#if WEBF_QUICKJS_JS_ENGINE void InitializeBuiltInStrings(JSContext* ctx) { if (!is_name_installed_) { names_installer::Init(ctx); is_name_installed_ = true; } } +#elif WEBF_V8_JS_ENGINE +void InitializeBuiltInStrings(v8::Isolate* isolate) { + if (!is_name_installed_) { + names_installer::Init(isolate); + is_name_installed_ = true; + } +} +#endif void DartIsolateContext::InitializeJSRuntime() { +#if WEBF_QUICKJS_JS_ENGINE if (runtime_ != nullptr) return; runtime_ = JS_NewRuntime(); @@ -82,6 +98,18 @@ void DartIsolateContext::InitializeJSRuntime() { JSClassID id{0}; JS_NewClassID(&id); } +#elif WEBF_V8_JS_ENGINE + if (platform == nullptr) { + platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(platform.get()); + v8::V8::Initialize(); + } + // Create a new Isolate and make it the current one. + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = + v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + isolate_ = v8::Isolate::New(create_params); +#endif } void DartIsolateContext::FinalizeJSRuntime() { @@ -95,9 +123,15 @@ void DartIsolateContext::FinalizeJSRuntime() { SVGElementFactory::Dispose(); EventFactory::Dispose(); ClearUpWires(runtime_); + +#if WEBF_QUICKJS_JS_ENGINE JS_TurnOnGC(runtime_); JS_FreeRuntime(runtime_); runtime_ = nullptr; +#elif WEBF_V8_JS_ENGINE + isolate_->Dispose(); + isolate_ = nullptr; +#endif is_name_installed_ = false; } @@ -110,10 +144,16 @@ DartIsolateContext::DartIsolateContext(const uint64_t* dart_methods, int32_t dar running_dart_isolates++; } +#if WEBF_QUICKJS_JS_ENGINE JSRuntime* DartIsolateContext::runtime() { assert_m(runtime_ != nullptr, "nullptr is unsafe"); return runtime_; } +#elif WEBF_V8_JS_ENGINE +v8::Isolate* DartIsolateContext::isolate() { + return isolate_; +} +#endif DartIsolateContext::~DartIsolateContext() {} diff --git a/bridge/core/dart_isolate_context.h b/bridge/core/dart_isolate_context.h index e9a733dfa8..8aad09d3e3 100644 --- a/bridge/core/dart_isolate_context.h +++ b/bridge/core/dart_isolate_context.h @@ -5,6 +5,10 @@ #ifndef WEBF_DART_CONTEXT_H_ #define WEBF_DART_CONTEXT_H_ +#if WEBF_V8_JS_ENGINE +#include +#endif + #include #include "bindings/qjs/script_value.h" #include "dart_context_data.h" @@ -38,7 +42,11 @@ struct DartWireContext { multi_threading::Dispatcher* dispatcher; }; +#if WEBF_QUICKJS_JS_ENGINE void InitializeBuiltInStrings(JSContext* ctx); +#elif WEBF_V8_JS_ENGINE +void InitializeBuiltInStrings(v8::Isolate* isolate); +#endif void WatchDartWire(DartWireContext* wire); bool IsDartWireAlive(DartWireContext* wire); @@ -49,7 +57,11 @@ class DartIsolateContext { public: explicit DartIsolateContext(const uint64_t* dart_methods, int32_t dart_methods_length, bool profile_enabled); +#if WEBF_QUICKJS_JS_ENGINE JSRuntime* runtime(); +#elif WEBF_V8_JS_ENGINE + v8::Isolate* isolate(); +#endif FORCE_INLINE bool valid() { return is_valid_; } FORCE_INLINE DartMethodPointer* dartMethodPtr() const { return dart_method_ptr_.get(); } FORCE_INLINE const std::unique_ptr& dispatcher() const { return dispatcher_; } diff --git a/bridge/core/dom/element_data.h b/bridge/core/dom/element_data.h index 59384ed588..cdbcf7d325 100644 --- a/bridge/core/dom/element_data.h +++ b/bridge/core/dom/element_data.h @@ -6,9 +6,13 @@ #ifndef WEBF_CORE_DOM_ELEMENT_DATA_H_ #define WEBF_CORE_DOM_ELEMENT_DATA_H_ +#if WEBF_V8_JS_ENGINE +#include "bindings/v8/atomic_string.h" +#elif WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/atomic_string.h" #include "bindings/qjs/cppgc/gc_visitor.h" #include "bindings/qjs/cppgc/member.h" +#endif #include "dom_string_map.h" #include "dom_token_list.h" diff --git a/bridge/core/executing_context.cc b/bridge/core/executing_context.cc index 66eff49727..f07236e199 100644 --- a/bridge/core/executing_context.cc +++ b/bridge/core/executing_context.cc @@ -7,10 +7,10 @@ #include #include "bindings/qjs/converter_impl.h" #include "built_in_string.h" -#include "core/dom/document.h" -#include "core/dom/mutation_observer.h" -#include "core/events/error_event.h" -#include "core/events/promise_rejection_event.h" +//#include "core/dom/document.h" +//#include "core/dom/mutation_observer.h" +//#include "core/events/error_event.h" +//#include "core/events/promise_rejection_event.h" #include "event_type_names.h" #include "foundation/logging.h" #include "polyfill.h" @@ -55,6 +55,8 @@ ExecutingContext::ExecutingContext(DartIsolateContext* dart_isolate_context, time_origin_ = std::chrono::system_clock::now(); +#if WEBF_QUICKJS_JS_ENGINE + JSContext* ctx = script_state_.ctx(); global_object_ = JS_GetGlobalObject(script_state_.ctx()); @@ -64,6 +66,9 @@ ExecutingContext::ExecutingContext(DartIsolateContext* dart_isolate_context, JS_SetContextOpaque(ctx, this); JS_SetHostPromiseRejectionTracker(script_state_.runtime(), promiseRejectTracker, nullptr); +#elif WEBF_V8_JS_ENGINE +#endif + dart_isolate_context->profiler()->StartTrackSteps("ExecutingContext::InstallBindings"); // Register all built-in native bindings. diff --git a/bridge/core/executing_context.h b/bridge/core/executing_context.h index 9f3766cdeb..eba520187c 100644 --- a/bridge/core/executing_context.h +++ b/bridge/core/executing_context.h @@ -5,8 +5,13 @@ #ifndef BRIDGE_JS_CONTEXT_H #define BRIDGE_JS_CONTEXT_H +#if WEBF_V8_JS_ENGINE +#include +#elif WEBF_QUICKJS_JS_ENGINE #include #include +#endif + #include #include #include @@ -74,8 +79,6 @@ class ExecutingContext { void* owner); ~ExecutingContext(); - static ExecutingContext* From(JSContext* ctx); - bool EvaluateJavaScript(const char* code, size_t codeLength, uint8_t** parsed_bytecodes, @@ -88,12 +91,9 @@ class ExecutingContext { bool IsContextValid() const; void SetContextInValid(); bool IsCtxValid() const; - JSValue Global(); - JSContext* ctx(); FORCE_INLINE double contextId() const { return context_id_; }; FORCE_INLINE int32_t uniqueId() const { return unique_id_; } void* owner(); - bool HandleException(JSValue* exc); bool HandleException(ScriptValue* exc); bool HandleException(ExceptionState& exception_state); bool HandleException(ExceptionState& exception_state, char** rust_error_msg, uint32_t* rust_errmsg_len); @@ -101,10 +101,36 @@ class ExecutingContext { void ReportError(JSValueConst error, char** rust_errmsg, uint32_t* rust_errmsg_length); void DrainMicrotasks(); void EnqueueMicrotask(MicrotaskCallback callback, void* data = nullptr); - void DefineGlobalProperty(const char* prop, JSValueConst value); ExecutionContextData* contextData(); uint8_t* DumpByteCode(const char* code, uint32_t codeLength, const char* sourceURL, uint64_t* bytecodeLength); + +#if WEBF_QUICKJS_JS_ENGINE + static ExecutingContext* From(JSContext* ctx); + JSValue Global(); + JSContext* ctx(); + bool HandleException(JSValue* exc); + void ReportError(JSValueConst error); + void DefineGlobalProperty(const char* prop, JSValueConst value); + static void DispatchGlobalUnhandledRejectionEvent(ExecutingContext* context, + JSValueConst promise, + JSValueConst error); + static void DispatchGlobalRejectionHandledEvent(ExecutingContext* context, JSValueConst promise, JSValueConst error); + static void DispatchGlobalErrorEvent(ExecutingContext* context, JSValueConst error); + static void promiseRejectTracker(JSContext* ctx, + JSValueConst promise, + JSValueConst reason, + JS_BOOL is_handled, + void* opaque); +#elif WEBF_V8_JS_ENGINE + static ExecutingContext* From(v8::Isolate* isolate); + v8::Local Global(); + v8::Isolate ctx(); + bool HandleException(v8::Local exc); + void ReportError(v8::Local error); + void DefineGlobalProperty(const char* prop, v8::Local value); +#endif + // Make global object inherit from WindowProperties. void InstallGlobal(); @@ -160,12 +186,6 @@ class ExecutingContext { void DispatchErrorEventInterval(ErrorEvent* error_event); void ReportErrorEvent(ErrorEvent* error_event); - static void DispatchGlobalUnhandledRejectionEvent(ExecutingContext* context, - JSValueConst promise, - JSValueConst error); - static void DispatchGlobalRejectionHandledEvent(ExecutingContext* context, JSValueConst promise, JSValueConst error); - static void DispatchGlobalErrorEvent(ExecutingContext* context, JSValueConst error); - // Bytecodes which registered by webf plugins. static std::unordered_map plugin_byte_code; // Raw string codes which registered by webf plugins. @@ -180,7 +200,6 @@ class ExecutingContext { void InstallNativeLoader(); void DrainPendingPromiseJobs(); - static void promiseRejectTracker(JSContext* ctx, JSValueConst promise, JSValueConst reason, @@ -207,7 +226,11 @@ class ExecutingContext { double context_id_; JSExceptionHandler dart_error_report_handler_; void* owner_; +#if WEBF_QUICKJS_JS_ENGINE JSValue global_object_{JS_NULL}; +#elif WEBF_V8_JS_ENGINE + v8::Local global_object_; +#endif Document* document_{nullptr}; Window* window_{nullptr}; NativeLoader* native_loader_{nullptr}; @@ -227,24 +250,6 @@ class ExecutingContext { const std::unique_ptr public_method_ptr_ = nullptr; }; -class ObjectProperty { - WEBF_DISALLOW_COPY_ASSIGN_AND_MOVE(ObjectProperty); - - public: - ObjectProperty() = delete; - - // Define an property on object with a JSValue. - explicit ObjectProperty(ExecutingContext* context, JSValueConst thisObject, const char* property, JSValue value) - : m_value(value) { - JS_DefinePropertyValueStr(context->ctx(), thisObject, property, value, JS_PROP_ENUMERABLE); - } - - JSValue value() const { return m_value; } - - private: - JSValue m_value{JS_NULL}; -}; - } // namespace webf #endif // BRIDGE_JS_CONTEXT_H diff --git a/bridge/core/script_state.cc b/bridge/core/script_state.cc index bf14a97cd8..9e8405ccd6 100644 --- a/bridge/core/script_state.cc +++ b/bridge/core/script_state.cc @@ -13,15 +13,27 @@ thread_local std::atomic runningContexts{0}; ScriptState::ScriptState(DartIsolateContext* dart_context) : dart_isolate_context_(dart_context) { runningContexts++; +#if WEBF_QUICKJS_JS_ENGINE // Avoid stack overflow when running in multiple threads. ctx_ = JS_NewContext(dart_isolate_context_->runtime()); InitializeBuiltInStrings(ctx_); +#elif WEBF_V8_JS_ENGINE + ctx_ = v8::Context::New(dart_context->isolate()); + InitializeBuiltInStrings(dart_context->isolate()); +#endif } +#if WEBF_QUICKJS_JS_ENGINE JSRuntime* ScriptState::runtime() { return dart_isolate_context_->runtime(); } +#elif WEBF_V8_JS_ENGINE +v8::Isolate* ScriptState::isolate() { + return dart_isolate_context_->isolate(); +} +#endif +#if WEBF_QUICKJS_JS_ENGINE ScriptState::~ScriptState() { ctx_invalid_ = true; JSRuntime* rt = JS_GetRuntime(ctx_); @@ -33,4 +45,11 @@ ScriptState::~ScriptState() { ctx_ = nullptr; } + +#elif WEBF_V8_JS_ENGINE +ScriptState::~ScriptState() { + ctx_invalid_ = true; +} +#endif + } // namespace webf diff --git a/bridge/core/script_state.h b/bridge/core/script_state.h index 5762f6d76e..9820909eb7 100644 --- a/bridge/core/script_state.h +++ b/bridge/core/script_state.h @@ -5,7 +5,12 @@ #ifndef BRIDGE_CORE_SCRIPT_STATE_H_ #define BRIDGE_CORE_SCRIPT_STATE_H_ +#if WEBF_V8_JS_ENGINE +#include +#elif WEBF_QUICKJS_JS_ENGINE #include +#endif + #include namespace webf { @@ -23,15 +28,27 @@ class ScriptState { ~ScriptState(); inline bool Invalid() const { return !ctx_invalid_; } +#if WEBF_QUICKJS_JS_ENGINE inline JSContext* ctx() { assert(!ctx_invalid_ && "executingContext has been released"); return ctx_; } - JSRuntime* runtime(); +#elif WEBF_V8_JS_ENGINE + inline v8::Local ctx() { + assert(!ctx_invalid_ && "executingContext has been released"); + return ctx_; + } + v8::Isolate* isolate(); +#endif private: - bool ctx_invalid_{false}; +#if WEBF_QUICKJS_JS_ENGINE JSContext* ctx_{nullptr}; +#elif WEBF_V8_JS_ENGINE + v8::Local ctx_; +#endif + + bool ctx_invalid_{false}; DartIsolateContext* dart_isolate_context_{nullptr}; }; diff --git a/bridge/test/test.cmake b/bridge/test/test.cmake index d9904d7a00..4367b80161 100644 --- a/bridge/test/test.cmake +++ b/bridge/test/test.cmake @@ -47,7 +47,10 @@ add_executable(webf_unit_test target_include_directories(webf_unit_test PUBLIC ./third_party/googletest/googletest/include ${BRIDGE_INCLUDE} ./test) target_link_libraries(webf_unit_test gtest gtest_main ${BRIDGE_LINK_LIBS}) -target_compile_options(quickjs PUBLIC -DDUMP_LEAKS=1) +if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") + target_compile_options(quickjs PUBLIC -DDUMP_LEAKS=1) +endif() + target_compile_options(webf PUBLIC -DDUMP_LEAKS=1) target_compile_definitions(webf_unit_test PUBLIC -DFLUTTER_BACKEND=0) diff --git a/bridge/third_party/v8 b/bridge/third_party/v8 new file mode 160000 index 0000000000..97f877a83c --- /dev/null +++ b/bridge/third_party/v8 @@ -0,0 +1 @@ +Subproject commit 97f877a83cdd712f6c440efdb25ac0a265657d11 diff --git a/scripts/migrate_to_v8_support.js b/scripts/migrate_to_v8_support.js new file mode 100644 index 0000000000..a95d3336b1 --- /dev/null +++ b/scripts/migrate_to_v8_support.js @@ -0,0 +1,24 @@ +/** + * Build script for iOS + */ + +const { paths } = require('./tasks'); +const chalk = require('chalk'); +const { program } = require('commander'); +const minimist = require('minimist'); +const { series, parallel, task } = require('gulp'); +const { execSync } = require('child_process'); +const buildMode = process.env.KRAKEN_BUILD || 'Debug'; + +process.env.PATCH_PROMISE_POLYFILL = 'true'; + +// Run tasks +series( + 'migrate_to_v8' +)((err) => { + if (err) { + console.log(err); + } else { + console.log(chalk.green('Success.')); + } +}); diff --git a/scripts/tasks.js b/scripts/tasks.js index 01771831be..8177f4061f 100644 --- a/scripts/tasks.js +++ b/scripts/tasks.js @@ -829,6 +829,13 @@ task('run-benchmark', async (done) => { done(); }); +task('migrate_to_v8', async (done) => { + + + + done(); +}); + function getDevicesInfo() { let output = JSON.parse(execSync('flutter devices --machine', {stdio: 'pipe', encoding: 'utf-8'})); let androidDevices = output.filter(device => { From 3c56237bb0b0e42d386dc3c7b635888785668cc4 Mon Sep 17 00:00:00 2001 From: openwebf-bot Date: Sat, 30 Mar 2024 12:41:28 +0000 Subject: [PATCH 02/35] Committing clang-format changes --- bridge/bindings/v8/atomic_string.cc | 4 ++-- bridge/bindings/v8/atomic_string.h | 4 +--- bridge/core/dart_isolate_context.cc | 9 ++++----- bridge/core/executing_context.h | 1 - 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/bridge/bindings/v8/atomic_string.cc b/bridge/bindings/v8/atomic_string.cc index c7bc050269..8f6c18e8f4 100644 --- a/bridge/bindings/v8/atomic_string.cc +++ b/bridge/bindings/v8/atomic_string.cc @@ -139,11 +139,11 @@ const uint16_t* AtomicString::Character16() const { } int AtomicString::Find(bool (*CharacterMatchFunction)(char)) const { -// return JS_FindCharacterInAtom(runtime_, atom_, CharacterMatchFunction); + // return JS_FindCharacterInAtom(runtime_, atom_, CharacterMatchFunction); } int AtomicString::Find(bool (*CharacterMatchFunction)(uint16_t)) const { -// return JS_FindWCharacterInAtom(runtime_, atom_, CharacterMatchFunction); + // return JS_FindWCharacterInAtom(runtime_, atom_, CharacterMatchFunction); } std::string AtomicString::ToStdString(v8::Isolate* isolate) const { diff --git a/bridge/bindings/v8/atomic_string.h b/bridge/bindings/v8/atomic_string.h index 5b2a5ae26a..04a37462e3 100644 --- a/bridge/bindings/v8/atomic_string.h +++ b/bridge/bindings/v8/atomic_string.h @@ -42,9 +42,7 @@ class AtomicString { AtomicString(v8::Isolate* isolate, const uint16_t* str, size_t length); // Return the undefined string value from atom key. - v8::Local ToV8(v8::Isolate* isolate) const { - return string_.As(); - } + v8::Local ToV8(v8::Isolate* isolate) const { return string_.As(); } bool IsEmpty() const; bool IsNull() const; diff --git a/bridge/core/dart_isolate_context.cc b/bridge/core/dart_isolate_context.cc index dabdba0c74..077ccacc35 100644 --- a/bridge/core/dart_isolate_context.cc +++ b/bridge/core/dart_isolate_context.cc @@ -6,8 +6,8 @@ #include #include "v8/libplatform/libplatform.h" #endif -#include "dart_isolate_context.h" #include +#include "dart_isolate_context.h" #include "event_factory.h" #include "html_element_factory.h" #include "names_installer.h" @@ -62,8 +62,8 @@ const std::unique_ptr& DartIsolateContext::EnsureData() const { } #if WEBF_V8_JS_ENGINE - std::unique_ptr platform = nullptr; - thread_local v8::Isolate* isolate_{nullptr}; +std::unique_ptr platform = nullptr; +thread_local v8::Isolate* isolate_{nullptr}; #elif WEBF_QUICKJS_JS_ENGINE thread_local JSRuntime* runtime_{nullptr}; #endif @@ -106,8 +106,7 @@ void DartIsolateContext::InitializeJSRuntime() { } // Create a new Isolate and make it the current one. v8::Isolate::CreateParams create_params; - create_params.array_buffer_allocator = - v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); isolate_ = v8::Isolate::New(create_params); #endif } diff --git a/bridge/core/executing_context.h b/bridge/core/executing_context.h index eba520187c..cb6099d91c 100644 --- a/bridge/core/executing_context.h +++ b/bridge/core/executing_context.h @@ -104,7 +104,6 @@ class ExecutingContext { ExecutionContextData* contextData(); uint8_t* DumpByteCode(const char* code, uint32_t codeLength, const char* sourceURL, uint64_t* bytecodeLength); - #if WEBF_QUICKJS_JS_ENGINE static ExecutingContext* From(JSContext* ctx); JSValue Global(); From 06629e05eecbe559611693c7bd1d5f0d8cc15c06 Mon Sep 17 00:00:00 2001 From: david <> Date: Wed, 3 Apr 2024 13:42:59 +0800 Subject: [PATCH 03/35] update submodule url --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 55e2163811..e2e73c9c18 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,5 +7,5 @@ url = git@github.com:GerHobbelt/pthread-win32.git [submodule "bridge/third_party/v8"] path = bridge/third_party/v8 - url = git@github.com:openwebf/v8-release.git - branch = 11.9.169.6 \ No newline at end of file + url = https://github.com/openwebf/v8-release.git + branch = 11.9.169.6 From b9e311e0c779d5ffe1969ca7960abeecfe4a15cd Mon Sep 17 00:00:00 2001 From: andycall Date: Thu, 18 Apr 2024 20:05:55 +0800 Subject: [PATCH 04/35] feat: split v8 and quickjs templates. --- bridge/CMakeLists.txt | 269 +++++++++--------- bridge/bindings/qjs/native_string_utils.h | 4 + bridge/bindings/v8/atomic_string_test.cc | 116 ++++++++ bridge/core/binding_object.h | 2 + bridge/core/dart_context_data.h | 5 + bridge/core/dart_isolate_context.h | 12 +- bridge/core/dom/document.h | 2 + bridge/core/executing_context.cc | 4 +- bridge/core/executing_context.h | 17 +- bridge/core/executing_context_data.h | 10 +- bridge/core/frame/dom_timer_coordinator.h | 1 - bridge/core/frame/module_callback.h | 5 +- .../core/frame/module_context_coordinator.h | 1 - bridge/core/page.cc | 108 +++---- bridge/core/page.h | 2 + bridge/core/script_state.cc | 5 +- bridge/foundation/native_string.h | 1 - bridge/foundation/native_type.h | 8 +- bridge/foundation/native_value.cc | 6 + bridge/foundation/native_value.h | 6 + bridge/foundation/native_value_converter.h | 197 ++++++------- bridge/foundation/profiler.h | 6 + bridge/foundation/ui_command_strategy.cc | 2 +- .../code_generator/bin/code_generator.js | 29 +- .../code_generator/src/idl/analyzer.ts | 2 +- .../{ => generate/quickjs}/generateHeader.ts | 44 +-- .../{ => generate/quickjs}/generateSource.ts | 17 +- .../quickjs}/generateUnionTypes.ts | 30 +- .../code_generator/src/idl/generator.ts | 71 ++++- .../idl_templates/{ => quickjs}/base.cc.tpl | 0 .../idl_templates/{ => quickjs}/base.h.tpl | 0 .../{ => quickjs}/dictionary.cc.tpl | 0 .../{ => quickjs}/dictionary.h.tpl | 0 .../{ => quickjs}/global_function.cc.tpl | 0 .../{ => quickjs}/global_function.h.tpl | 0 .../{ => quickjs}/interface.cc.tpl | 0 .../{ => quickjs}/interface.h.tpl | 0 .../idl_templates/{ => quickjs}/union.cc.tpl | 2 +- .../idl_templates/{ => quickjs}/union.h.tpl | 0 .../templates/idl_templates/v8/base.cc.tpl | 88 ++++++ .../templates/idl_templates/v8/base.h.tpl | 15 + .../idl_templates/v8/dictionary.cc.tpl | 0 .../idl_templates/v8/dictionary.h.tpl | 0 .../idl_templates/v8/global_function.cc.tpl | 0 .../idl_templates/v8/global_function.h.tpl | 0 .../idl_templates/v8/interface.cc.tpl | 0 .../idl_templates/v8/interface.h.tpl | 0 .../templates/idl_templates/v8/union.cc.tpl | 0 .../templates/idl_templates/v8/union.h.tpl | 0 .../defined_properties_initializer.cc.tpl | 0 .../defined_properties_initializer.h.tpl | 0 .../{ => quickjs}/element_factory.cc.tpl | 0 .../{ => quickjs}/element_factory.h.tpl | 0 .../quickjs/element_type_helper.h.tpl | 62 ++++ .../{ => quickjs}/event_factory.cc.tpl | 0 .../{ => quickjs}/event_factory.h.tpl | 0 .../{ => quickjs}/event_type_helper.h.tpl | 0 .../{ => quickjs}/make_names.cc.tpl | 0 .../{ => quickjs}/make_names.h.tpl | 0 .../{ => quickjs}/names_installer.cc.tpl | 0 .../{ => quickjs}/names_installer.h.tpl | 0 .../v8/defined_properties_initializer.cc.tpl | 27 ++ .../v8/defined_properties_initializer.h.tpl | 22 ++ .../json_templates/v8/element_factory.cc.tpl | 105 +++++++ .../json_templates/v8/element_factory.h.tpl | 29 ++ .../{ => v8}/element_type_helper.h.tpl | 0 .../json_templates/v8/event_factory.cc.tpl | 112 ++++++++ .../json_templates/v8/event_factory.h.tpl | 23 ++ .../json_templates/v8/event_type_helper.h.tpl | 50 ++++ .../json_templates/v8/make_names.cc.tpl | 89 ++++++ .../json_templates/v8/make_names.h.tpl | 41 +++ .../json_templates/v8/names_installer.cc.tpl | 25 ++ .../json_templates/v8/names_installer.h.tpl | 20 ++ bridge/test/test.cmake | 143 ++++++---- ...st_context.cc => webf_test_context_qjs.cc} | 2 +- ...test_context.h => webf_test_context_qjs.h} | 0 bridge/test/webf_test_context_v8.cc | 134 +++++++++ bridge/test/webf_test_context_v8.h | 46 +++ bridge/test/webf_test_env.cc | 105 ++++--- bridge/test/webf_test_env.h | 14 +- bridge/webf_bridge_test.cc | 6 +- scripts/tasks.js | 4 +- 82 files changed, 1647 insertions(+), 499 deletions(-) create mode 100644 bridge/bindings/v8/atomic_string_test.cc rename bridge/scripts/code_generator/src/idl/{ => generate/quickjs}/generateHeader.ts (66%) rename bridge/scripts/code_generator/src/idl/{ => generate/quickjs}/generateSource.ts (97%) rename bridge/scripts/code_generator/src/idl/{ => generate/quickjs}/generateUnionTypes.ts (88%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/base.cc.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/base.h.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/dictionary.cc.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/dictionary.h.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/global_function.cc.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/global_function.h.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/interface.cc.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/interface.h.tpl (100%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/union.cc.tpl (97%) rename bridge/scripts/code_generator/templates/idl_templates/{ => quickjs}/union.h.tpl (100%) create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/base.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/base.h.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/dictionary.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/dictionary.h.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/global_function.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/global_function.h.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/interface.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/interface.h.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/union.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/idl_templates/v8/union.h.tpl rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/defined_properties_initializer.cc.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/defined_properties_initializer.h.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/element_factory.cc.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/element_factory.h.tpl (100%) create mode 100644 bridge/scripts/code_generator/templates/json_templates/quickjs/element_type_helper.h.tpl rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/event_factory.cc.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/event_factory.h.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/event_type_helper.h.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/make_names.cc.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/make_names.h.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/names_installer.cc.tpl (100%) rename bridge/scripts/code_generator/templates/json_templates/{ => quickjs}/names_installer.h.tpl (100%) create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.h.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/element_factory.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/element_factory.h.tpl rename bridge/scripts/code_generator/templates/json_templates/{ => v8}/element_type_helper.h.tpl (100%) create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/event_factory.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/event_factory.h.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/event_type_helper.h.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/make_names.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/make_names.h.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/names_installer.cc.tpl create mode 100644 bridge/scripts/code_generator/templates/json_templates/v8/names_installer.h.tpl rename bridge/test/{webf_test_context.cc => webf_test_context_qjs.cc} (99%) rename bridge/test/{webf_test_context.h => webf_test_context_qjs.h} (100%) create mode 100644 bridge/test/webf_test_context_v8.cc create mode 100644 bridge/test/webf_test_context_v8.h diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index 8ea0b88e21..ada3b4bc6d 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -85,18 +85,18 @@ list(APPEND BRIDGE_SOURCE foundation/logging.cc foundation/native_string.cc foundation/ui_task_queue.cc - foundation/shared_ui_command.cc +# foundation/shared_ui_command.cc foundation/inspector_task_queue.cc foundation/task_queue.cc foundation/string_view.cc foundation/native_value.cc foundation/native_type.cc foundation/stop_watch.cc - foundation/profiler.cc +# foundation/profiler.cc foundation/dart_readable.cc foundation/rust_readable.cc - foundation/ui_command_buffer.cc - foundation/ui_command_strategy.cc +# foundation/ui_command_buffer.cc +# foundation/ui_command_strategy.cc polyfill/dist/polyfill.cc multiple_threading/dispatcher.cc multiple_threading/looper.cc @@ -607,6 +607,21 @@ if (${WEBF_JS_ENGINE} MATCHES "quickjs") out/qjs_svg_ellipse_element.cc out/qjs_svg_style_element.cc out/qjs_svg_line_element.cc + + # plugin api + out/plugin_api_event.cc + out/plugin_api_animation_event.cc + out/plugin_api_close_event.cc + out/plugin_api_focus_event.cc + out/plugin_api_gesture_event.cc + out/plugin_api_hashchange_event.cc + out/plugin_api_input_event.cc + out/plugin_api_intersection_change_event.cc + out/plugin_api_mouse_event.cc + out/plugin_api_pointer_event.cc + out/plugin_api_transition_event.cc + out/plugin_api_ui_event.cc + out/plugin_api_custom_event.cc ) if (NOT MSVC) @@ -627,9 +642,9 @@ elseif (${WEBF_JS_ENGINE} MATCHES "v8") if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") add_library(v8 SHARED IMPORTED) - set_target_properties(v8 PROPERTIES IMPORTED_LOCATION "third_party/v8/lib/macos/arm64/libv8.dylib") + set_target_properties(v8 PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/lib/macos/arm64/libv8.dylib") add_library(v8_platform SHARED IMPORTED) - set_target_properties(v8_platform PROPERTIES IMPORTED_LOCATION "third_party/v8/lib/macos/arm64/libv8_libplatform.dylib") + set_target_properties(v8_platform PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/lib/macos/arm64/libv8_libplatform.dylib") list(APPEND BRIDGE_LINK_LIBS v8 v8_platform) endif() list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/include) @@ -652,131 +667,131 @@ list(APPEND BRIDGE_SOURCE core/dart_isolate_context.cc core/dart_context_data.cc core/executing_context_data.cc - core/fileapi/blob.cc - core/fileapi/blob_part.cc - core/fileapi/blob_property_bag.cc - core/frame/console.cc - core/frame/dom_timer.cc - core/frame/dom_timer_coordinator.cc - core/frame/window_or_worker_global_scope.cc - core/frame/module_listener.cc - core/frame/module_listener_container.cc - core/frame/module_manager.cc - core/frame/module_callback.cc - core/frame/module_context_coordinator.cc - core/frame/window.cc - core/frame/screen.cc - core/frame/legacy/location.cc - core/timing/performance.cc - core/timing/performance_mark.cc - core/timing/performance_entry.cc - core/timing/performance_measure.cc - core/css/css_style_declaration.cc - core/css/inline_css_style_declaration.cc - core/css/computed_css_style_declaration.cc - core/dom/frame_request_callback_collection.cc - core/dom/events/registered_eventListener.cc - core/dom/events/event_listener_map.cc - core/dom/events/event.cc - core/dom/events/custom_event.cc - core/dom/events/event_target.cc - core/dom/events/event_listener_map.cc - core/dom/events/event_target_impl.cc - core/binding_object.cc - core/dom/node.cc - core/dom/node_list.cc - core/dom/static_node_list.cc - core/dom/node_traversal.cc - core/dom/live_node_list_base.cc - core/dom/character_data.cc - core/dom/comment.cc - core/dom/text.cc - core/dom/tree_scope.cc - core/dom/element.cc - core/dom/parent_node.cc - core/dom/element_data.cc - core/dom/document.cc - core/dom/dom_token_list.cc - core/dom/dom_string_map.cc - core/dom/space_split_string.cc - core/dom/scripted_animation_controller.cc - core/dom/node_data.cc - core/dom/document_fragment.cc - core/dom/child_node_list.cc - core/dom/empty_node_list.cc - core/dom/mutation_observer.cc - core/dom/mutation_observer_registration.cc - core/dom/mutation_observer_interest_group.cc - core/dom/mutation_record.cc - core/dom/child_list_mutation_scope.cc - core/dom/container_node.cc - core/html/custom/widget_element.cc - core/events/error_event.cc - core/events/message_event.cc - core/events/animation_event.cc - core/events/close_event.cc - core/events/ui_event.cc - core/events/focus_event.cc - core/events/gesture_event.cc - core/events/input_event.cc - core/events/touch_event.cc - core/events/mouse_event.cc - core/events/pop_state_event.cc - core/events/pointer_event.cc - core/events/transition_event.cc - core/events/intersection_change_event.cc - core/events/keyboard_event.cc - core/events/promise_rejection_event.cc - core/html/parser/html_parser.cc - core/html/html_element.cc - core/html/html_div_element.cc - core/html/html_head_element.cc - core/html/html_body_element.cc - core/html/html_html_element.cc - core/html/html_template_element.cc - core/html/html_all_collection.cc - core/html/html_anchor_element.cc - core/html/html_image_element.cc - core/html/html_script_element.cc - core/html/html_iframe_element.cc - core/html/html_link_element.cc - core/html/html_unknown_element.cc - core/html/image.cc - core/html/html_collection.cc - core/html/canvas/html_canvas_element.cc - core/html/canvas/canvas_rendering_context.cc - core/html/canvas/canvas_rendering_context_2d.cc - core/html/canvas/canvas_gradient.cc - core/html/canvas/canvas_pattern.cc - core/geometry/dom_matrix.cc - core/geometry/dom_matrix_readonly.cc - core/html/forms/html_button_element.cc - core/html/forms/html_input_element.cc - core/html/forms/html_form_element.cc - core/html/forms/html_textarea_element.cc +# core/fileapi/blob.cc +# core/fileapi/blob_part.cc +# core/fileapi/blob_property_bag.cc +# core/frame/console.cc +# core/frame/dom_timer.cc +# core/frame/dom_timer_coordinator.cc +# core/frame/window_or_worker_global_scope.cc +# core/frame/module_listener.cc +# core/frame/module_listener_container.cc +# core/frame/module_manager.cc +# core/frame/module_callback.cc +# core/frame/module_context_coordinator.cc +# core/frame/window.cc +# core/frame/screen.cc +# core/frame/legacy/location.cc +# core/timing/performance.cc +# core/timing/performance_mark.cc +# core/timing/performance_entry.cc +# core/timing/performance_measure.cc +# core/css/css_style_declaration.cc +# core/css/inline_css_style_declaration.cc +# core/css/computed_css_style_declaration.cc +# core/dom/frame_request_callback_collection.cc +# core/dom/events/registered_eventListener.cc +# core/dom/events/event_listener_map.cc +# core/dom/events/event.cc +# core/dom/events/custom_event.cc +# core/dom/events/event_target.cc +# core/dom/events/event_listener_map.cc +# core/dom/events/event_target_impl.cc +# core/binding_object.cc +# core/dom/node.cc +# core/dom/node_list.cc +# core/dom/static_node_list.cc +# core/dom/node_traversal.cc +# core/dom/live_node_list_base.cc +# core/dom/character_data.cc +# core/dom/comment.cc +# core/dom/text.cc +# core/dom/tree_scope.cc +# core/dom/element.cc +# core/dom/parent_node.cc +# core/dom/element_data.cc +# core/dom/document.cc +# core/dom/dom_token_list.cc +# core/dom/dom_string_map.cc +# core/dom/space_split_string.cc +# core/dom/scripted_animation_controller.cc +# core/dom/node_data.cc +# core/dom/document_fragment.cc +# core/dom/child_node_list.cc +# core/dom/empty_node_list.cc +# core/dom/mutation_observer.cc +# core/dom/mutation_observer_registration.cc +# core/dom/mutation_observer_interest_group.cc +# core/dom/mutation_record.cc +# core/dom/child_list_mutation_scope.cc +# core/dom/container_node.cc +# core/html/custom/widget_element.cc +# core/events/error_event.cc +# core/events/message_event.cc +# core/events/animation_event.cc +# core/events/close_event.cc +# core/events/ui_event.cc +# core/events/focus_event.cc +# core/events/gesture_event.cc +# core/events/input_event.cc +# core/events/touch_event.cc +# core/events/mouse_event.cc +# core/events/pop_state_event.cc +# core/events/pointer_event.cc +# core/events/transition_event.cc +# core/events/intersection_change_event.cc +# core/events/keyboard_event.cc +# core/events/promise_rejection_event.cc +# core/html/parser/html_parser.cc +# core/html/html_element.cc +# core/html/html_div_element.cc +# core/html/html_head_element.cc +# core/html/html_body_element.cc +# core/html/html_html_element.cc +# core/html/html_template_element.cc +# core/html/html_all_collection.cc +# core/html/html_anchor_element.cc +# core/html/html_image_element.cc +# core/html/html_script_element.cc +# core/html/html_iframe_element.cc +# core/html/html_link_element.cc +# core/html/html_unknown_element.cc +# core/html/image.cc +# core/html/html_collection.cc +# core/html/canvas/html_canvas_element.cc +# core/html/canvas/canvas_rendering_context.cc +# core/html/canvas/canvas_rendering_context_2d.cc +# core/html/canvas/canvas_gradient.cc +# core/html/canvas/canvas_pattern.cc +# core/geometry/dom_matrix.cc +# core/geometry/dom_matrix_readonly.cc +# core/html/forms/html_button_element.cc +# core/html/forms/html_input_element.cc +# core/html/forms/html_form_element.cc +# core/html/forms/html_textarea_element.cc # SVG files - core/svg/svg_element.cc - core/svg/svg_graphics_element.cc - core/svg/svg_geometry_element.cc - core/svg/svg_text_content_element.cc - core/svg/svg_text_positioning_element.cc - - core/svg/svg_svg_element.cc - core/svg/svg_path_element.cc - core/svg/svg_rect_element.cc - core/svg/svg_text_element.cc - core/svg/svg_g_element.cc - core/svg/svg_circle_element.cc - core/svg/svg_ellipse_element.cc - core/svg/svg_style_element.cc - core/svg/svg_line_element.cc +# core/svg/svg_element.cc +# core/svg/svg_graphics_element.cc +# core/svg/svg_geometry_element.cc +# core/svg/svg_text_content_element.cc +# core/svg/svg_text_positioning_element.cc +# +# core/svg/svg_svg_element.cc +# core/svg/svg_path_element.cc +# core/svg/svg_rect_element.cc +# core/svg/svg_text_element.cc +# core/svg/svg_g_element.cc +# core/svg/svg_circle_element.cc +# core/svg/svg_ellipse_element.cc +# core/svg/svg_style_element.cc +# core/svg/svg_line_element.cc # Legacy implements, should remove them in the future. - core/dom/legacy/element_attributes.cc - core/dom/legacy/bounding_client_rect.cc - core/input/touch.cc - core/input/touch_list.cc +# core/dom/legacy/element_attributes.cc +# core/dom/legacy/bounding_client_rect.cc +# core/input/touch.cc +# core/input/touch_list.cc ) list(APPEND PUBLIC_HEADER diff --git a/bridge/bindings/qjs/native_string_utils.h b/bridge/bindings/qjs/native_string_utils.h index b9e83a1ff7..1760d20f43 100644 --- a/bridge/bindings/qjs/native_string_utils.h +++ b/bridge/bindings/qjs/native_string_utils.h @@ -6,7 +6,9 @@ #ifndef BRIDGE_NATIVE_STRING_UTILS_H #define BRIDGE_NATIVE_STRING_UTILS_H +#if WEBF_QUICKJS_JS_ENGINE #include +#endif #include #include #include @@ -16,8 +18,10 @@ namespace webf { +#if WEBF_QUICKJS_JS_ENGINE // Convert to string and return a full copy of NativeString from JSValue. std::unique_ptr jsValueToNativeString(JSContext* ctx, JSValue value); +#endif // Encode utf-8 to utf-16, and return a full copy of NativeString. std::unique_ptr stringToNativeString(const std::string& string); diff --git a/bridge/bindings/v8/atomic_string_test.cc b/bridge/bindings/v8/atomic_string_test.cc new file mode 100644 index 0000000000..fda1cbf8c9 --- /dev/null +++ b/bridge/bindings/v8/atomic_string_test.cc @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "atomic_string.h" +#include +#include "built_in_string.h" +#include "event_type_names.h" +#include "gtest/gtest.h" + +using namespace webf; + +//using TestCallback = void (*)(JSContext* ctx); +// +//void TestAtomicString(TestCallback callback) { +// JSRuntime* runtime = JS_NewRuntime(); +// JSContext* ctx = JS_NewContext(runtime); +// +// built_in_string::Init(ctx); +// +// callback(ctx); +// +// JS_FreeContext(ctx); +// +// built_in_string::Dispose(); +// JS_FreeRuntime(runtime); +//} + +TEST(AtomicString, Empty) { +// TestAtomicString([](JSContext* ctx) { +// AtomicString atomic_string = AtomicString::Empty(); +// EXPECT_STREQ(atomic_string.ToStdString(ctx).c_str(), ""); +// }); +} + +TEST(AtomicString, FromNativeString) { +// TestAtomicString([](JSContext* ctx) { +// auto nativeString = stringToNativeString("helloworld"); +// AtomicString value = AtomicString( +// ctx, std::unique_ptr(static_cast(nativeString.release()))); +// +// EXPECT_STREQ(value.ToStdString(ctx).c_str(), "helloworld"); +// }); +} +// +//TEST(AtomicString, CreateFromStdString) { +// TestAtomicString([](JSContext* ctx) { +// AtomicString&& value = AtomicString(ctx, "helloworld"); +// EXPECT_STREQ(value.ToStdString(ctx).c_str(), "helloworld"); +// }); +//} +// +//TEST(AtomicString, CreateFromJSValue) { +// TestAtomicString([](JSContext* ctx) { +// JSValue string = JS_NewString(ctx, "helloworld"); +// AtomicString&& value = AtomicString(ctx, string); +// EXPECT_STREQ(value.ToStdString(ctx).c_str(), "helloworld"); +// JS_FreeValue(ctx, string); +// }); +//} +// +//TEST(AtomicString, ToQuickJS) { +// TestAtomicString([](JSContext* ctx) { +// AtomicString&& value = AtomicString(ctx, "helloworld"); +// JSValue qjs_value = value.ToQuickJS(ctx); +// const char* buffer = JS_ToCString(ctx, qjs_value); +// EXPECT_STREQ(buffer, "helloworld"); +// JS_FreeValue(ctx, qjs_value); +// JS_FreeCString(ctx, buffer); +// }); +//} +// +//TEST(AtomicString, ToNativeString) { +// TestAtomicString([](JSContext* ctx) { +// AtomicString&& value = AtomicString(ctx, "helloworld"); +// auto native_string = value.ToNativeString(ctx); +// const uint16_t* p = native_string->string(); +// EXPECT_EQ(native_string->length(), 10); +// +// uint16_t result[10] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}; +// for (int i = 0; i < native_string->length(); i++) { +// EXPECT_EQ(result[i], p[i]); +// } +// }); +//} +// +//TEST(AtomicString, CopyAssignment) { +// TestAtomicString([](JSContext* ctx) { +// AtomicString str = AtomicString(ctx, "helloworld"); +// struct P { +// AtomicString str; +// }; +// P p{AtomicString::Empty()}; +// p.str = str; +// EXPECT_EQ(p.str == str, true); +// }); +//} +// +//TEST(AtomicString, MoveAssignment) { +// TestAtomicString([](JSContext* ctx) { +// auto&& str = AtomicString(ctx, "helloworld"); +// auto&& str2 = AtomicString(std::move(str)); +// EXPECT_STREQ(str2.ToStdString(ctx).c_str(), "helloworld"); +// }); +//} +// +//TEST(AtomicString, CopyToRightReference) { +// TestAtomicString([](JSContext* ctx) { +// AtomicString str = AtomicString::Empty(); +// if (1 + 1 == 2) { +// str = AtomicString(ctx, "helloworld"); +// } +// EXPECT_STREQ(str.ToStdString(ctx).c_str(), "helloworld"); +// }); +//} diff --git a/bridge/core/binding_object.h b/bridge/core/binding_object.h index 57369e7ce4..c12029a12b 100644 --- a/bridge/core/binding_object.h +++ b/bridge/core/binding_object.h @@ -9,8 +9,10 @@ #include #include #include +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/atomic_string.h" #include "bindings/qjs/script_wrappable.h" +#endif #include "core/dart_methods.h" #include "foundation/native_type.h" #include "foundation/native_value.h" diff --git a/bridge/core/dart_context_data.h b/bridge/core/dart_context_data.h index b8d37b2b98..1c0eb26852 100644 --- a/bridge/core/dart_context_data.h +++ b/bridge/core/dart_context_data.h @@ -9,7 +9,12 @@ #include #include #include + +#if WEBF_V8_JS_ENGINE +#include "bindings/v8/atomic_string.h" +#elif WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/atomic_string.h" +#endif namespace webf { diff --git a/bridge/core/dart_isolate_context.h b/bridge/core/dart_isolate_context.h index 8aad09d3e3..f6a94df0bd 100644 --- a/bridge/core/dart_isolate_context.h +++ b/bridge/core/dart_isolate_context.h @@ -7,13 +7,15 @@ #if WEBF_V8_JS_ENGINE #include +#elif WEBF_QUICKJS_JS_ENGINE +#include "bindings/qjs/script_value.h" #endif #include -#include "bindings/qjs/script_value.h" + #include "dart_context_data.h" #include "dart_methods.h" -#include "foundation/profiler.h" +//#include "foundation/profiler.h" #include "multiple_threading/dispatcher.h" namespace webf { @@ -35,7 +37,7 @@ class PageGroup { }; struct DartWireContext { - ScriptValue jsObject; +// ScriptValue jsObject; bool is_dedicated; double context_id; bool disposed; @@ -68,7 +70,7 @@ class DartIsolateContext { FORCE_INLINE void SetDispatcher(std::unique_ptr&& dispatcher) { dispatcher_ = std::move(dispatcher); } - FORCE_INLINE WebFProfiler* profiler() const { return profiler_.get(); }; +// FORCE_INLINE WebFProfiler* profiler() const { return profiler_.get(); }; const std::unique_ptr& EnsureData() const; @@ -114,7 +116,7 @@ class DartIsolateContext { Dart_Handle persistent_handle, DisposePageCallback result_callback); - std::unique_ptr profiler_; +// std::unique_ptr profiler_; int is_valid_{false}; std::thread::id running_thread_; mutable std::unique_ptr data_; diff --git a/bridge/core/dom/document.h b/bridge/core/dom/document.h index a7654240e7..df5b6f41aa 100644 --- a/bridge/core/dom/document.h +++ b/bridge/core/dom/document.h @@ -5,7 +5,9 @@ #ifndef BRIDGE_DOCUMENT_H #define BRIDGE_DOCUMENT_H +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/cppgc/local_handle.h" +#endif #include "container_node.h" #include "event_type_names.h" #include "plugin_api/document.h" diff --git a/bridge/core/executing_context.cc b/bridge/core/executing_context.cc index f07236e199..e3953af9d1 100644 --- a/bridge/core/executing_context.cc +++ b/bridge/core/executing_context.cc @@ -5,7 +5,7 @@ #include "executing_context.h" #include -#include "bindings/qjs/converter_impl.h" +//#include "bindings/qjs/converter_impl.h" #include "built_in_string.h" //#include "core/dom/document.h" //#include "core/dom/mutation_observer.h" @@ -14,7 +14,7 @@ #include "event_type_names.h" #include "foundation/logging.h" #include "polyfill.h" -#include "qjs_window.h" +//#include "qjs_window.h" #include "script_forbidden_scope.h" #include "timing/performance.h" diff --git a/bridge/core/executing_context.h b/bridge/core/executing_context.h index cb6099d91c..f80f6ebae7 100644 --- a/bridge/core/executing_context.h +++ b/bridge/core/executing_context.h @@ -23,9 +23,12 @@ #include #include #include +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/binding_initializer.h" #include "bindings/qjs/rejected_promises.h" #include "bindings/qjs/script_value.h" +#elif WEBF_V8_JS_ENGINE +#endif #include "foundation/macros.h" #include "foundation/ui_command_buffer.h" #include "native/native_loader.h" @@ -35,8 +38,8 @@ #include "dart_methods.h" #include "executing_context_data.h" #include "frame/dom_timer_coordinator.h" -#include "frame/module_context_coordinator.h" -#include "frame/module_listener_container.h" +//#include "frame/module_context_coordinator.h" +//#include "frame/module_listener_container.h" #include "script_state.h" #include "shared_ui_command.h" @@ -144,10 +147,10 @@ class ExecutingContext { DOMTimerCoordinator* Timers(); // Gets the ModuleListeners which registered by `webf.addModuleListener API`. - ModuleListenerContainer* ModuleListeners(); +// ModuleListenerContainer* ModuleListeners(); // Gets the ModuleCallbacks which from the 4th parameter of `webf.invokeModule` function. - ModuleContextCoordinator* ModuleContexts(); +// ModuleContextCoordinator* ModuleContexts(); // Get current script state. ScriptState* GetScriptState() { return &script_state_; } @@ -235,11 +238,13 @@ class ExecutingContext { NativeLoader* native_loader_{nullptr}; Performance* performance_{nullptr}; DOMTimerCoordinator timers_; - ModuleListenerContainer module_listener_container_; - ModuleContextCoordinator module_contexts_; +// ModuleListenerContainer module_listener_container_; +// ModuleContextCoordinator module_contexts_; ExecutionContextData context_data_{this}; bool in_dispatch_error_event_{false}; +#if WEBF_QUICKJS_JS_ENGINE RejectedPromises rejected_promises_; +#endif MemberMutationScope* active_mutation_scope{nullptr}; std::unordered_set active_wrappers_; WebFValueStatus* executing_context_status_{new WebFValueStatus()}; diff --git a/bridge/core/executing_context_data.h b/bridge/core/executing_context_data.h index 0ec9e24330..d65915e29c 100644 --- a/bridge/core/executing_context_data.h +++ b/bridge/core/executing_context_data.h @@ -5,9 +5,13 @@ #ifndef BRIDGE_CONTEXT_DATA_H #define BRIDGE_CONTEXT_DATA_H +#if WEBF_V8_JS_ENGINE +#include +#elif WEBF_QUICKJS_JS_ENGINE #include -#include #include "bindings/qjs/wrapper_type_info.h" +#endif +#include namespace webf { @@ -21,17 +25,21 @@ class ExecutionContextData final { ExecutionContextData(const ExecutionContextData&) = delete; ExecutionContextData& operator=(const ExecutionContextData&) = delete; +#if WEBF_QUICKJS_JS_ENGINE // Returns the constructor object that is appropriately initialized. JSValue constructorForType(const WrapperTypeInfo* type); // Returns the prototype object that is appropriately initialized. JSValue prototypeForType(const WrapperTypeInfo* type); +#endif void Dispose(); private: +#if WEBF_QUICKJS_JS_ENGINE JSValue constructorForIdSlowCase(const WrapperTypeInfo* type); std::unordered_map constructor_map_; std::unordered_map prototype_map_; +#endif ExecutingContext* m_context; }; diff --git a/bridge/core/frame/dom_timer_coordinator.h b/bridge/core/frame/dom_timer_coordinator.h index f8feed0970..34c22ddacf 100644 --- a/bridge/core/frame/dom_timer_coordinator.h +++ b/bridge/core/frame/dom_timer_coordinator.h @@ -6,7 +6,6 @@ #ifndef BRIDGE_BINDINGS_QJS_BOM_DOM_TIMER_COORDINATOR_H_ #define BRIDGE_BINDINGS_QJS_BOM_DOM_TIMER_COORDINATOR_H_ -#include #include #include #include diff --git a/bridge/core/frame/module_callback.h b/bridge/core/frame/module_callback.h index 7e7fbc4d7a..bfd4fdc2e4 100644 --- a/bridge/core/frame/module_callback.h +++ b/bridge/core/frame/module_callback.h @@ -5,8 +5,11 @@ #ifndef BRIDGE_MODULE_CALLBACK_H #define BRIDGE_MODULE_CALLBACK_H -#include +#if WEBF_V8_JS_ENGINE +//#include "bindings/v8/qjs_function.h" +#elif WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/qjs_function.h" +#endif namespace webf { diff --git a/bridge/core/frame/module_context_coordinator.h b/bridge/core/frame/module_context_coordinator.h index 46533cad55..78ae24b3e0 100644 --- a/bridge/core/frame/module_context_coordinator.h +++ b/bridge/core/frame/module_context_coordinator.h @@ -7,7 +7,6 @@ #include // Quickjs's linked-list are more efficient than STL forward_list. -#include #include "module_callback.h" #include "module_manager.h" diff --git a/bridge/core/page.cc b/bridge/core/page.cc index 6d04460dfd..cf3467584a 100644 --- a/bridge/core/page.cc +++ b/bridge/core/page.cc @@ -5,14 +5,18 @@ #include #include +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/atomic_string.h" #include "bindings/qjs/binding_initializer.h" +#elif WEBF_V8_JS_ENGINE +#endif + #include "core/dart_methods.h" -#include "core/dom/document.h" -#include "core/frame/window.h" -#include "core/html/html_html_element.h" -#include "core/html/parser/html_parser.h" -#include "event_factory.h" +//#include "core/dom/document.h" +//#include "core/frame/window.h" +//#include "core/html/html_html_element.h" +//#include "core/html/parser/html_parser.h" +//#include "event_factory.h" #include "foundation/logging.h" #include "foundation/native_value_converter.h" #include "page.h" @@ -46,14 +50,14 @@ bool WebFPage::parseHTML(const char* code, size_t length) { { MemberMutationScope scope{context_}; - auto document_element = context_->document()->documentElement(); - if (!document_element) { - return false; - } +// auto document_element = context_->document()->documentElement(); +// if (!document_element) { +// return false; +// } - context_->dartIsolateContext()->profiler()->StartTrackSteps("HTMLParser::parseHTML"); - HTMLParser::parseHTML(code, length, context_->document()->documentElement()); - context_->dartIsolateContext()->profiler()->FinishTrackSteps(); +// context_->dartIsolateContext()->profiler()->StartTrackSteps("HTMLParser::parseHTML"); +// HTMLParser::parseHTML(code, length, context_->document()->documentElement()); +// context_->dartIsolateContext()->profiler()->FinishTrackSteps(); } context_->uiCommandBuffer()->AddCommand(UICommand::kFinishRecordingCommand, nullptr, nullptr, nullptr); @@ -65,46 +69,46 @@ NativeValue* WebFPage::invokeModuleEvent(SharedNativeString* native_module_name, const char* eventType, void* ptr, NativeValue* extra) { - if (!context_->IsContextValid()) - return nullptr; - - MemberMutationScope scope{context_}; - - JSContext* ctx = context_->ctx(); - Event* event = nullptr; - if (ptr != nullptr) { - std::string type = std::string(eventType); - auto* raw_event = static_cast(ptr); - event = EventFactory::Create(context_, AtomicString(ctx, type), raw_event); - delete raw_event; - } - - ScriptValue extraObject = ScriptValue(ctx, const_cast(*extra)); - AtomicString module_name = AtomicString( - ctx, std::unique_ptr(reinterpret_cast(native_module_name))); - auto listener = context_->ModuleListeners()->listener(module_name); - - if (listener == nullptr) { - return nullptr; - } - - ScriptValue arguments[] = {event != nullptr ? event->ToValue() : ScriptValue::Empty(ctx), extraObject}; - ScriptValue result = listener->value()->Invoke(ctx, ScriptValue::Empty(ctx), 2, arguments); - if (result.IsException()) { - context_->HandleException(&result); - return nullptr; - } - - ExceptionState exception_state; - auto* return_value = static_cast(malloc(sizeof(NativeValue))); - NativeValue tmp = result.ToNative(ctx, exception_state); - if (exception_state.HasException()) { - context_->HandleException(exception_state); - return nullptr; - } - - memcpy(return_value, &tmp, sizeof(NativeValue)); - return return_value; +// if (!context_->IsContextValid()) +// return nullptr; +// +// MemberMutationScope scope{context_}; +// +// JSContext* ctx = context_->ctx(); +// Event* event = nullptr; +// if (ptr != nullptr) { +// std::string type = std::string(eventType); +// auto* raw_event = static_cast(ptr); +// event = EventFactory::Create(context_, AtomicString(ctx, type), raw_event); +// delete raw_event; +// } +// +// ScriptValue extraObject = ScriptValue(ctx, const_cast(*extra)); +// AtomicString module_name = AtomicString( +// ctx, std::unique_ptr(reinterpret_cast(native_module_name))); +// auto listener = context_->ModuleListeners()->listener(module_name); +// +// if (listener == nullptr) { +// return nullptr; +// } +// +// ScriptValue arguments[] = {event != nullptr ? event->ToValue() : ScriptValue::Empty(ctx), extraObject}; +// ScriptValue result = listener->value()->Invoke(ctx, ScriptValue::Empty(ctx), 2, arguments); +// if (result.IsException()) { +// context_->HandleException(&result); +// return nullptr; +// } +// +// ExceptionState exception_state; +// auto* return_value = static_cast(malloc(sizeof(NativeValue))); +// NativeValue tmp = result.ToNative(ctx, exception_state); +// if (exception_state.HasException()) { +// context_->HandleException(exception_state); +// return nullptr; +// } +// +// memcpy(return_value, &tmp, sizeof(NativeValue)); +// return return_value; } bool WebFPage::evaluateScript(const char* script, diff --git a/bridge/core/page.h b/bridge/core/page.h index 4accd2fd19..83f8978e75 100644 --- a/bridge/core/page.h +++ b/bridge/core/page.h @@ -6,7 +6,9 @@ #ifndef WEBF_JS_QJS_BRIDGE_H_ #define WEBF_JS_QJS_BRIDGE_H_ +#if WEBF_QUICKJS_JS_ENGINE #include +#endif #include #include #include diff --git a/bridge/core/script_state.cc b/bridge/core/script_state.cc index 9e8405ccd6..db747a0de8 100644 --- a/bridge/core/script_state.cc +++ b/bridge/core/script_state.cc @@ -3,9 +3,10 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ #include "script_state.h" -#include "event_factory.h" +//#include "event_factory.h" #include "html_element_factory.h" -#include "names_installer.h" +//#include "names_installer.h" +#include "dart_isolate_context.h" namespace webf { diff --git a/bridge/foundation/native_string.h b/bridge/foundation/native_string.h index ae723623be..8839903406 100644 --- a/bridge/foundation/native_string.h +++ b/bridge/foundation/native_string.h @@ -5,7 +5,6 @@ #ifndef BRIDGE_NATIVE_STRING_H #define BRIDGE_NATIVE_STRING_H -#include #include #include #include diff --git a/bridge/foundation/native_type.h b/bridge/foundation/native_type.h index 71862071ab..aa421b4dd0 100644 --- a/bridge/foundation/native_type.h +++ b/bridge/foundation/native_type.h @@ -7,8 +7,12 @@ #include #include +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/qjs_function.h" #include "bindings/qjs/script_value.h" +#else +#include "bindings/v8/atomic_string.h" +#endif #include "foundation/native_string.h" namespace webf { @@ -51,10 +55,10 @@ template struct NativeTypePointer final : public NativeTypeBaseHelper {}; // Sync function -struct NativeTypeFunction final : public NativeTypeBaseHelper> {}; +//struct NativeTypeFunction final : public NativeTypeBaseHelper> {}; // Async function -struct NativeTypeAsyncFunction final : public NativeTypeBaseHelper> {}; +//struct NativeTypeAsyncFunction final : public NativeTypeBaseHelper> {}; } // namespace webf diff --git a/bridge/foundation/native_value.cc b/bridge/foundation/native_value.cc index f2ac0a63e3..a7af53b670 100644 --- a/bridge/foundation/native_value.cc +++ b/bridge/foundation/native_value.cc @@ -3,8 +3,10 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ #include "native_value.h" +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/qjs_engine_patch.h" #include "bindings/qjs/script_value.h" +#endif #include "core/executing_context.h" namespace webf { @@ -118,6 +120,8 @@ NativeValue Native_NewList(uint32_t argc, NativeValue* argv) { #endif } +#if WEBF_QUICKJS_JS_ENGINE + NativeValue Native_NewJSON(JSContext* ctx, const ScriptValue& value, ExceptionState& exception_state) { ScriptValue json = value.ToJSONStringify(ctx, &exception_state); if (exception_state.HasException()) { @@ -142,6 +146,8 @@ NativeValue Native_NewJSON(JSContext* ctx, const ScriptValue& value, ExceptionSt #endif } +#endif + JSPointerType GetPointerTypeOfNativePointer(NativeValue native_value) { assert(native_value.tag == NativeTag::TAG_POINTER); return static_cast(native_value.uint32); diff --git a/bridge/foundation/native_value.h b/bridge/foundation/native_value.h index be7ffdd164..fc801bd994 100644 --- a/bridge/foundation/native_value.h +++ b/bridge/foundation/native_value.h @@ -6,8 +6,10 @@ #ifndef BRIDGE_NATIVE_VALUE_H #define BRIDGE_NATIVE_VALUE_H +#if WEBF_QUICKJS_JS_ENGINE #include #include +#endif #include #include #include "bindings/qjs/native_string_utils.h" @@ -58,6 +60,7 @@ static void call_native_function(NativeFunctionContext* functionContext, NativeValue* argv, NativeValue* returnValue); +#if WEBF_QUICKJS_JS_ENGINE struct NativeFunctionContext { CallNativeFunction call; NativeFunctionContext(ExecutingContext* context, JSValue callback); @@ -67,6 +70,7 @@ struct NativeFunctionContext { JSContext* m_ctx{nullptr}; list_head link; }; +#endif NativeValue Native_NewNull(); NativeValue Native_NewString(SharedNativeString* string); @@ -76,7 +80,9 @@ NativeValue Native_NewBool(bool value); NativeValue Native_NewInt64(int64_t value); NativeValue Native_NewList(uint32_t argc, NativeValue* argv); NativeValue Native_NewPtr(JSPointerType pointerType, void* ptr); +#if WEBF_QUICKJS_JS_ENGINE NativeValue Native_NewJSON(JSContext* ctx, const ScriptValue& value, ExceptionState& exception_state); +#endif JSPointerType GetPointerTypeOfNativePointer(NativeValue native_value); diff --git a/bridge/foundation/native_value_converter.h b/bridge/foundation/native_value_converter.h index af6432d9c1..1118722b8a 100644 --- a/bridge/foundation/native_value_converter.h +++ b/bridge/foundation/native_value_converter.h @@ -5,8 +5,13 @@ #ifndef BRIDGE_FOUNDATION_NATIVE_VALUE_CONVERTER_H_ #define BRIDGE_FOUNDATION_NATIVE_VALUE_CONVERTER_H_ + +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/script_wrappable.h" -#include "core/binding_object.h" +#elif WEBF_V8_JS_ENGINE +#endif + +//#include "core/binding_object.h" #include "native_type.h" #include "native_value.h" @@ -23,37 +28,37 @@ template struct NativeValueConverterBase { using ImplType = typename T::ImplType; }; - -template <> -struct NativeValueConverter : public NativeValueConverterBase { - static NativeValue ToNativeValue() { return Native_NewNull(); } - - static ImplType FromNativeValue(JSContext* ctx) { return ScriptValue::Empty(ctx); } -}; - -template <> -struct NativeValueConverter : public NativeValueConverterBase { - static NativeValue ToNativeValue(JSContext* ctx, const ImplType& value) { - return Native_NewString(value.ToNativeString(ctx).release()); - } - static NativeValue ToNativeValue(const std::string& value) { return Native_NewCString(value); } - - static ImplType FromNativeValue(JSContext* ctx, NativeValue&& value) { - if (value.tag == NativeTag::TAG_NULL) { - return AtomicString::Empty(); - } - assert(value.tag == NativeTag::TAG_STRING); - return {ctx, std::unique_ptr(reinterpret_cast(value.u.ptr))}; - } - - static ImplType FromNativeValue(JSContext* ctx, NativeValue& value) { - if (value.tag == NativeTag::TAG_NULL) { - return AtomicString::Empty(); - } - assert(value.tag == NativeTag::TAG_STRING); - return {ctx, std::unique_ptr(reinterpret_cast(value.u.ptr))}; - } -}; +// +//template <> +//struct NativeValueConverter : public NativeValueConverterBase { +// static NativeValue ToNativeValue() { return Native_NewNull(); } +// +// static ImplType FromNativeValue(JSContext* ctx) { return ScriptValue::Empty(ctx); } +//}; +// +//template <> +//struct NativeValueConverter : public NativeValueConverterBase { +// static NativeValue ToNativeValue(JSContext* ctx, const ImplType& value) { +// return Native_NewString(value.ToNativeString(ctx).release()); +// } +// static NativeValue ToNativeValue(const std::string& value) { return Native_NewCString(value); } +// +// static ImplType FromNativeValue(JSContext* ctx, NativeValue&& value) { +// if (value.tag == NativeTag::TAG_NULL) { +// return AtomicString::Empty(); +// } +// assert(value.tag == NativeTag::TAG_STRING); +// return {ctx, std::unique_ptr(reinterpret_cast(value.u.ptr))}; +// } +// +// static ImplType FromNativeValue(JSContext* ctx, NativeValue& value) { +// if (value.tag == NativeTag::TAG_NULL) { +// return AtomicString::Empty(); +// } +// assert(value.tag == NativeTag::TAG_STRING); +// return {ctx, std::unique_ptr(reinterpret_cast(value.u.ptr))}; +// } +//}; template <> struct NativeValueConverter : public NativeValueConverterBase { @@ -98,22 +103,22 @@ struct NativeValueConverter : public NativeValueConverterBase< return result; } }; - -template <> -struct NativeValueConverter : public NativeValueConverterBase { - static NativeValue ToNativeValue(JSContext* ctx, ImplType value, ExceptionState& exception_state) { - return Native_NewJSON(ctx, value, exception_state); - } - static ImplType FromNativeValue(JSContext* ctx, NativeValue value) { - if (value.tag == NativeTag::TAG_NULL) { - return ScriptValue::Empty(ctx); - } - - assert(value.tag == NativeTag::TAG_JSON); - auto* str = static_cast(value.u.ptr); - return ScriptValue::CreateJsonObject(ctx, str, strlen(str)); - } -}; +// +//template <> +//struct NativeValueConverter : public NativeValueConverterBase { +// static NativeValue ToNativeValue(JSContext* ctx, ImplType value, ExceptionState& exception_state) { +// return Native_NewJSON(ctx, value, exception_state); +// } +// static ImplType FromNativeValue(JSContext* ctx, NativeValue value) { +// if (value.tag == NativeTag::TAG_NULL) { +// return ScriptValue::Empty(ctx); +// } +// +// assert(value.tag == NativeTag::TAG_JSON); +// auto* str = static_cast(value.u.ptr); +// return ScriptValue::CreateJsonObject(ctx, str, strlen(str)); +// } +//}; class BindingObject; struct DartReadable; @@ -130,7 +135,7 @@ struct NativeValueConverter, std::enable_if_t(value.u.ptr); } - static T* FromNativeValue(JSContext* ctx, NativeValue value) { + static T* FromNativeValue(v8::Isolate* isolate, NativeValue value) { if (value.tag == NativeTag::TAG_NULL) { return nullptr; } @@ -152,7 +157,7 @@ struct NativeValueConverter, std::enable_if_t(value.u.ptr); } - static T* FromNativeValue(JSContext* ctx, NativeValue value) { + static T* FromNativeValue(v8::Isolate* isolate, NativeValue value) { if (value.tag == NativeTag::TAG_NULL) { return nullptr; } @@ -161,50 +166,50 @@ struct NativeValueConverter, std::enable_if_t(value.u.ptr); } }; - -template -struct NativeValueConverter, std::enable_if_t>> - : public NativeValueConverterBase { - static NativeValue ToNativeValue(T* value) { - return Native_NewPtr(JSPointerType::NativeBindingObject, value->bindingObject()); - } - static T* FromNativeValue(JSContext* ctx, NativeValue value) { - if (value.tag == NativeTag::TAG_NULL) { - return nullptr; - } - assert(value.tag == NativeTag::TAG_POINTER); - assert(value.uint32 == static_cast(JSPointerType::NativeBindingObject)); - return DynamicTo(BindingObject::From(static_cast(value.u.ptr))); - } -}; - -template <> -struct NativeValueConverter : public NativeValueConverterBase { - static NativeValue ToNativeValue(ImplType value) { - // Not supported. - assert(false); - return Native_NewNull(); - } - - static ImplType FromNativeValue(JSContext* ctx, NativeValue value) { - assert(value.tag == NativeTag::TAG_FUNCTION); - return QJSFunction::Create(ctx, BindingObject::AnonymousFunctionCallback, 4, value.u.ptr); - }; -}; - -template <> -struct NativeValueConverter : public NativeValueConverterBase { - static NativeValue ToNativeValue(ImplType value) { - // Not supported. - assert(false); - return Native_NewNull(); - } - - static ImplType FromNativeValue(JSContext* ctx, NativeValue value) { - assert(value.tag == NativeTag::TAG_ASYNC_FUNCTION); - return QJSFunction::Create(ctx, BindingObject::AnonymousAsyncFunctionCallback, 4, value.u.ptr); - } -}; +// +//template +//struct NativeValueConverter, std::enable_if_t>> +// : public NativeValueConverterBase { +// static NativeValue ToNativeValue(T* value) { +// return Native_NewPtr(JSPointerType::NativeBindingObject, value->bindingObject()); +// } +// static T* FromNativeValue(JSContext* ctx, NativeValue value) { +// if (value.tag == NativeTag::TAG_NULL) { +// return nullptr; +// } +// assert(value.tag == NativeTag::TAG_POINTER); +// assert(value.uint32 == static_cast(JSPointerType::NativeBindingObject)); +// return DynamicTo(BindingObject::From(static_cast(value.u.ptr))); +// } +//}; +// +//template <> +//struct NativeValueConverter : public NativeValueConverterBase { +// static NativeValue ToNativeValue(ImplType value) { +// // Not supported. +// assert(false); +// return Native_NewNull(); +// } +// +// static ImplType FromNativeValue(JSContext* ctx, NativeValue value) { +// assert(value.tag == NativeTag::TAG_FUNCTION); +// return QJSFunction::Create(ctx, BindingObject::AnonymousFunctionCallback, 4, value.u.ptr); +// }; +//}; +// +//template <> +//struct NativeValueConverter : public NativeValueConverterBase { +// static NativeValue ToNativeValue(ImplType value) { +// // Not supported. +// assert(false); +// return Native_NewNull(); +// } +// +// static ImplType FromNativeValue(JSContext* ctx, NativeValue value) { +// assert(value.tag == NativeTag::TAG_ASYNC_FUNCTION); +// return QJSFunction::Create(ctx, BindingObject::AnonymousAsyncFunctionCallback, 4, value.u.ptr); +// } +//}; template struct NativeValueConverter> : public NativeValueConverterBase> { @@ -217,7 +222,7 @@ struct NativeValueConverter> : public NativeValueConverterBas return Native_NewList(value.size(), ptr); } - static ImplType FromNativeValue(JSContext* ctx, NativeValue native_value) { + static ImplType FromNativeValue(v8::Isolate* isolate, NativeValue native_value) { if (native_value.tag == NativeTag::TAG_NULL) { return std::vector(); } @@ -229,7 +234,7 @@ struct NativeValueConverter> : public NativeValueConverterBas vec.reserve(length); for (int i = 0; i < length; i++) { NativeValue v = arr[i]; - vec.emplace_back(NativeValueConverter::FromNativeValue(ctx, std::move(v))); + vec.emplace_back(NativeValueConverter::FromNativeValue(isolate, std::move(v))); } return vec; } diff --git a/bridge/foundation/profiler.h b/bridge/foundation/profiler.h index 4ff96c367f..f6396825d4 100644 --- a/bridge/foundation/profiler.h +++ b/bridge/foundation/profiler.h @@ -11,7 +11,13 @@ #include #include #include + +#if WEBF_V8_JS_ENGINE +//#include "bindings/v8/atomic_string.h" +#elif WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/script_value.h" +#endif + #include "foundation/stop_watch.h" namespace webf { diff --git a/bridge/foundation/ui_command_strategy.cc b/bridge/foundation/ui_command_strategy.cc index 8fddd6ca32..98125592f8 100644 --- a/bridge/foundation/ui_command_strategy.cc +++ b/bridge/foundation/ui_command_strategy.cc @@ -4,7 +4,7 @@ #include "ui_command_strategy.h" #include -#include "core/binding_object.h" +//#include "core/binding_object.h" #include "logging.h" #include "shared_ui_command.h" diff --git a/bridge/scripts/code_generator/bin/code_generator.js b/bridge/scripts/code_generator/bin/code_generator.js index 13a4198132..f7adf088e1 100644 --- a/bridge/scripts/code_generator/bin/code_generator.js +++ b/bridge/scripts/code_generator/bin/code_generator.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const { program } = require('commander'); +const {program} = require('commander'); const packageJSON = require('../package.json'); const path = require('path'); const glob = require('glob'); @@ -10,7 +10,7 @@ const { JSONBlob } = require('../dist/json/JSONBlob'); const { JSONTemplate } = require('../dist/json/JSONTemplate'); const { analyzer, buildClassRelationship } = require('../dist/idl/analyzer'); const { generatorSource } = require('../dist/idl/generator') -const { generateUnionTypes, generateUnionTypeFileName } = require('../dist/idl/generateUnionTypes') +const { generateUnionTypes, generateUnionTypeFileName } = require('../dist/idl/generator') const { generateJSONTemplate } = require('../dist/json/generator'); const { generateNamesInstaller } = require("../dist/json/generator"); const { generatePluginAPI } = require("../dist/idl/pluginAPIGenerator/cppGen"); @@ -21,12 +21,13 @@ const { ClassObject } = require('../dist/idl/declaration'); program .version(packageJSON.version) .description('WebF code generator.') + .requiredOption('-p, --platform ', 'the target JS engine platform', 'quickjs') .requiredOption('-s, --source ', 'source directory.') .requiredOption('-d, --dist ', 'destionation directory.') program.parse(process.argv); -let {source, dist} = program.opts(); +let {source, dist, platform} = program.opts(); if (!path.isAbsolute(source)) { source = path.join(process.cwd(), source); @@ -46,6 +47,10 @@ function wirteFileIfChanged(filePath, content) { fs.writeFileSync(filePath, content, 'utf-8'); } +function generatePlatformPrefix() { + return platform === 'quickjs' ? 'qjs' : 'v8'; +} + function genCodeFromTypeDefine() { // Generate code from type defines. let typeFiles = glob.sync("**/*.d.ts", { @@ -53,7 +58,7 @@ function genCodeFromTypeDefine() { }); let blobs = typeFiles.map(file => { - let filename = 'qjs_' + file.split('/').slice(-1)[0].replace('.d.ts', ''); + let filename = generatePlatformPrefix() + '_' + file.split('/').slice(-1)[0].replace('.d.ts', ''); let implement = file.replace(path.join(__dirname, '../../')).replace('.d.ts', ''); return new IDLBlob(path.join(source, file), dist, filename, implement); }); @@ -61,12 +66,12 @@ function genCodeFromTypeDefine() { ClassObject.globalClassMap = Object.create(null); // Analyze all files first. - for (let i = 0; i < blobs.length; i ++) { + for (let i = 0; i < blobs.length; i++) { let b = blobs[i]; analyzer(b, definedPropertyCollector, unionTypeCollector); } - for (let i = 0; i < blobs.length; i ++) { + for (let i = 0; i < blobs.length; i++) { let b = blobs[i]; let result = generatorSource(b); @@ -87,9 +92,9 @@ function genCodeFromTypeDefine() { return -(n.value - p.value); }) }); - for(let i = 0; i < unionTypes.length; i ++) { - let result = generateUnionTypes(unionTypes[i]); - let filename = generateUnionTypeFileName(unionTypes[i]); + for (let i = 0; i < unionTypes.length; i++) { + let result = generateUnionTypes(platform, unionTypes[i]); + let filename = generateUnionTypeFileName(platform, unionTypes[i]); wirteFileIfChanged(path.join(dist, filename) + '.h', result.header); wirteFileIfChanged(path.join(dist, filename) + '.cc', result.source); } @@ -101,7 +106,7 @@ function genCodeFromJSONData() { cwd: source }); let templateFiles = glob.sync('**/*.tpl', { - cwd: path.join(__dirname, '../templates/json_templates') + cwd: path.join(__dirname, '../templates/json_templates/', platform) }); let blobs = jsonFiles.map(file => { @@ -111,10 +116,10 @@ function genCodeFromJSONData() { let templates = templateFiles.map(template => { let filename = template.split(path.sep).slice(-1)[0].replace('.tpl', ''); - return new JSONTemplate(path.join(path.join(__dirname, '../templates/json_templates'), template), filename); + return new JSONTemplate(path.join(path.join(__dirname, '../templates/json_templates/', platform), template), filename); }); - for (let i = 0; i < blobs.length; i ++) { + for (let i = 0; i < blobs.length; i++) { let blob = blobs[i]; blob.json.metadata.templates.forEach((targetTemplate) => { if (targetTemplate.template === 'make_names') { diff --git a/bridge/scripts/code_generator/src/idl/analyzer.ts b/bridge/scripts/code_generator/src/idl/analyzer.ts index 7a32fb455d..9ea2dab448 100644 --- a/bridge/scripts/code_generator/src/idl/analyzer.ts +++ b/bridge/scripts/code_generator/src/idl/analyzer.ts @@ -11,7 +11,7 @@ import { ParameterMode, PropsDeclaration, } from './declaration'; -import {isUnionType} from "./generateSource"; +import {isUnionType} from "./generate/quickjs/generateSource"; interface DefinedPropertyCollector { properties: Set; diff --git a/bridge/scripts/code_generator/src/idl/generateHeader.ts b/bridge/scripts/code_generator/src/idl/generate/quickjs/generateHeader.ts similarity index 66% rename from bridge/scripts/code_generator/src/idl/generateHeader.ts rename to bridge/scripts/code_generator/src/idl/generate/quickjs/generateHeader.ts index 1c70832a09..fab3568690 100644 --- a/bridge/scripts/code_generator/src/idl/generateHeader.ts +++ b/bridge/scripts/code_generator/src/idl/generate/quickjs/generateHeader.ts @@ -1,7 +1,7 @@ -import {ClassObject, ClassObjectKind, FunctionObject} from "./declaration"; +import {ClassObject, ClassObjectKind, FunctionObject} from "../../declaration"; import _ from "lodash"; -import {IDLBlob} from "./IDLBlob"; -import {getClassName} from "./utils"; +import {IDLBlob} from "../../IDLBlob"; +import {getClassName} from "../../utils"; import fs from 'fs'; import path from 'path'; import { @@ -11,13 +11,12 @@ import { isPointerType, isTypeHaveNull } from "./generateSource"; -import {GenerateOptions} from "./generator"; -import {ParameterType} from "./analyzer"; +import {GenerateOptions, generateUnionTypeFileName, readTemplate} from "../../generator"; +import {ParameterType} from "../../analyzer"; import { generateUnionConstructor, generateUnionContentType, generateUnionMemberName, generateUnionMemberType, generateUnionPropertyHeaders, generateUnionTypeClassName, - generateUnionTypeFileName } from "./generateUnionTypes"; export enum TemplateKind { @@ -41,12 +40,8 @@ export function getTemplateKind(object: ClassObject | FunctionObject | null): Te return TemplateKind.null; } -function readTemplate(name: string) { - return fs.readFileSync(path.join(__dirname, '../../templates/idl_templates/' + name + '.h.tpl'), {encoding: 'utf-8'}); -} - -export function generateCppHeader(blob: IDLBlob, options: GenerateOptions) { - const baseTemplate = fs.readFileSync(path.join(__dirname, '../../templates/idl_templates/base.h.tpl'), {encoding: 'utf-8'}); +export function generateQuickJSCppHeader(blob: IDLBlob, options: GenerateOptions) { + const baseTemplate = fs.readFileSync(path.join(__dirname, '../../../../templates/idl_templates/quickjs/base.h.tpl'), {encoding: 'utf-8'}); let headerOptions = { interface: false, dictionary: false, @@ -61,7 +56,7 @@ export function generateCppHeader(blob: IDLBlob, options: GenerateOptions) { if (!headerOptions.interface) { object = (object as ClassObject); headerOptions.interface = true; - return _.template(readTemplate('interface'))({ + return _.template(readTemplate('quickjs', 'interface'))({ className: getClassName(blob), parentClassName: object.parent, blob: blob, @@ -76,7 +71,7 @@ export function generateCppHeader(blob: IDLBlob, options: GenerateOptions) { if (!headerOptions.dictionary) { headerOptions.dictionary = true; let props = (object as ClassObject).props; - return _.template(readTemplate('dictionary'))({ + return _.template(readTemplate('quickjs', 'dictionary'))({ className: getClassName(blob), blob: blob, object: object, @@ -89,7 +84,7 @@ export function generateCppHeader(blob: IDLBlob, options: GenerateOptions) { case TemplateKind.globalFunction: { if (!headerOptions.global_function) { headerOptions.global_function = true; - return _.template(readTemplate('global_function'))({ + return _.template(readTemplate('quickjs', 'global_function'))({ className: getClassName(blob), blob: blob }); @@ -105,23 +100,4 @@ export function generateCppHeader(blob: IDLBlob, options: GenerateOptions) { }).split('\n').filter(str => { return str.trim().length > 0; }).join('\n'); -} - -export function generateUnionTypeHeader(unionType: ParameterType): string { - return _.template(readTemplate('union'))({ - unionType, - generateUnionTypeClassName, - generateUnionTypeFileName, - generateUnionContentType, - generateUnionConstructor, - generateUnionPropertyHeaders, - generateCoreTypeValue, - generateUnionMemberType, - generateUnionMemberName, - isTypeHaveNull, - isPointerType, - getPointerType, - }).split('\n').filter(str => { - return str.trim().length > 0; - }).join('\n'); } \ No newline at end of file diff --git a/bridge/scripts/code_generator/src/idl/generateSource.ts b/bridge/scripts/code_generator/src/idl/generate/quickjs/generateSource.ts similarity index 97% rename from bridge/scripts/code_generator/src/idl/generateSource.ts rename to bridge/scripts/code_generator/src/idl/generate/quickjs/generateSource.ts index d7922f60f1..8c497886a7 100644 --- a/bridge/scripts/code_generator/src/idl/generateSource.ts +++ b/bridge/scripts/code_generator/src/idl/generate/quickjs/generateSource.ts @@ -1,4 +1,4 @@ -import {IDLBlob} from "./IDLBlob"; +import {IDLBlob} from "../../IDLBlob"; import { ClassObject, FunctionArguments, @@ -7,21 +7,20 @@ import { FunctionObject, ParameterMode, PropsDeclaration, -} from "./declaration"; -import {addIndent, getClassName, getWrapperTypeInfoNameOfClassName} from "./utils"; -import {ParameterType} from "./analyzer"; +} from "../../declaration"; +import {addIndent, getClassName, getWrapperTypeInfoNameOfClassName} from "../../utils"; +import {ParameterType} from "../../analyzer"; import _ from 'lodash'; import fs from 'fs'; import path from 'path'; import {getTemplateKind, TemplateKind} from "./generateHeader"; -import {GenerateOptions} from "./generator"; +import {GenerateOptions, generateUnionTypeFileName} from "../../generator"; import { generateTypeRawChecker, generateUnionConstructorImpl, generateUnionMemberName, generateUnionTypeClassName, generateUnionTypeClear, - generateUnionTypeFileName, generateUnionTypeSetter, getUnionTypeName } from "./generateUnionTypes"; @@ -557,11 +556,11 @@ ${addIndent(callBody, 4)} } function readTemplate(name: string) { - return fs.readFileSync(path.join(__dirname, '../../templates/idl_templates/' + name + '.cc.tpl'), {encoding: 'utf-8'}); + return fs.readFileSync(path.join(__dirname, '../../../../templates/idl_templates/quickjs/' + name + '.cc.tpl'), {encoding: 'utf-8'}); } -export function generateCppSource(blob: IDLBlob, options: GenerateOptions) { - const baseTemplate = fs.readFileSync(path.join(__dirname, '../../templates/idl_templates/base.cc.tpl'), {encoding: 'utf-8'}); +export function generateQuickJSCppSource(blob: IDLBlob, options: GenerateOptions) { + const baseTemplate = fs.readFileSync(path.join(__dirname, '../../../../templates/idl_templates/quickjs/base.cc.tpl'), {encoding: 'utf-8'}); const className = getClassName(blob) const contents = blob.objects.map(object => { diff --git a/bridge/scripts/code_generator/src/idl/generateUnionTypes.ts b/bridge/scripts/code_generator/src/idl/generate/quickjs/generateUnionTypes.ts similarity index 88% rename from bridge/scripts/code_generator/src/idl/generateUnionTypes.ts rename to bridge/scripts/code_generator/src/idl/generate/quickjs/generateUnionTypes.ts index 81c6e9c9ac..303b0059f5 100644 --- a/bridge/scripts/code_generator/src/idl/generateUnionTypes.ts +++ b/bridge/scripts/code_generator/src/idl/generate/quickjs/generateUnionTypes.ts @@ -1,7 +1,6 @@ -import {ParameterType} from "./analyzer"; -import {FunctionArgumentType} from "./declaration"; +import {ParameterType} from "../../analyzer"; +import {FunctionArgumentType} from "../../declaration"; import _ from "lodash"; -import {generateUnionTypeHeader} from "./generateHeader"; import { generateCoreTypeValue, generateUnionTypeSource, isDictionary, @@ -10,22 +9,6 @@ import { trimNullTypeFromType } from "./generateSource"; -export function generateUnionTypeFileName(unionType: ParameterType[]) { - let filename = 'qjs_union'; - for (let i = 0; i < unionType.length; i++) { - let v = unionType[i].value; - if (isTypeHaveNull(unionType[i])) continue; - if (typeof v == 'number') { - filename += '_' + FunctionArgumentType[v]; - } else if (unionType[i].isArray && typeof v == 'object' && !Array.isArray(v)) { - filename += '_' + 'sequence' + FunctionArgumentType[v.value as number]; - } else if (typeof v == 'string') { - filename += _.snakeCase(v); - } - } - return filename; -} - export function generateUnionTypeClassName(unionTypes: ParameterType[]) { let className = 'QJSUnion'; for (let i = 0; i < unionTypes.length; i++) { @@ -35,15 +18,6 @@ export function generateUnionTypeClassName(unionTypes: ParameterType[]) { return className; } -export function generateUnionTypes(unionType: ParameterType) { - let header = generateUnionTypeHeader(unionType); - let source = generateUnionTypeSource(unionType); - return { - header, - source - } -} - export function getUnionTypeName(unionType: ParameterType) { let v = unionType.value; if (typeof v == 'number') { diff --git a/bridge/scripts/code_generator/src/idl/generator.ts b/bridge/scripts/code_generator/src/idl/generator.ts index 210f49378d..cdea99964e 100644 --- a/bridge/scripts/code_generator/src/idl/generator.ts +++ b/bridge/scripts/code_generator/src/idl/generator.ts @@ -1,6 +1,21 @@ import {IDLBlob} from './IDLBlob'; -import {generateCppHeader} from "./generateHeader"; -import {generateCppSource} from "./generateSource"; +import {generateQuickJSCppHeader} from "./generate/quickjs/generateHeader"; +import { + generateCoreTypeValue, + generateQuickJSCppSource, + generateUnionTypeSource, getPointerType, isPointerType, + isTypeHaveNull +} from "./generate/quickjs/generateSource"; +import {ParameterType} from "./analyzer"; +import {FunctionArgumentType} from "./declaration"; +import _ from "lodash"; +import { + generateUnionConstructor, + generateUnionContentType, generateUnionMemberName, generateUnionMemberType, generateUnionPropertyHeaders, + generateUnionTypeClassName +} from "./generate/quickjs/generateUnionTypes"; +import fs from "fs"; +import path from "path"; export function generateSupportedOptions(): GenerateOptions { let globalFunctionInstallList: string[] = []; @@ -35,10 +50,58 @@ export type GenerateOptions = { export function generatorSource(blob: IDLBlob) { let options = generateSupportedOptions(); - let source = generateCppSource(blob, options); - let header = generateCppHeader(blob, options); + let source = generateQuickJSCppSource(blob, options); + let header = generateQuickJSCppHeader(blob, options); return { header, source }; } + +export function readTemplate(platform: string, name: string) { + return fs.readFileSync(path.join(__dirname, `../../templates/idl_templates/${platform}/` + name + '.h.tpl'), {encoding: 'utf-8'}); +} + +export function generateUnionTypeHeader(platform: string, unionType: ParameterType): string { + return _.template(readTemplate(platform, 'union'))({ + unionType, + generateUnionTypeClassName, + generateUnionTypeFileName, + generateUnionContentType, + generateUnionConstructor, + generateUnionPropertyHeaders, + generateCoreTypeValue, + generateUnionMemberType, + generateUnionMemberName, + isTypeHaveNull, + isPointerType, + getPointerType, + }).split('\n').filter(str => { + return str.trim().length > 0; + }).join('\n'); +} + +export function generateUnionTypes(platform: string, unionType: ParameterType) { + let header = generateUnionTypeHeader(platform, unionType); + let source = generateUnionTypeSource(unionType); + return { + header, + source + } +} + +export function generateUnionTypeFileName(platform: string, unionType: ParameterType[]) { + let filename = `${platform == 'quickjs' ? 'qjs' : 'v8'}_union`; + for (let i = 0; i < unionType.length; i++) { + let v = unionType[i].value; + if (isTypeHaveNull(unionType[i])) continue; + if (typeof v == 'number') { + filename += '_' + FunctionArgumentType[v]; + } else if (unionType[i].isArray && typeof v == 'object' && !Array.isArray(v)) { + filename += '_' + 'sequence' + FunctionArgumentType[v.value as number]; + } else if (typeof v == 'string') { + filename += _.snakeCase(v); + } + } + return filename; +} \ No newline at end of file diff --git a/bridge/scripts/code_generator/templates/idl_templates/base.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/base.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/base.cc.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/base.cc.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/base.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/base.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/base.h.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/base.h.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/dictionary.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/dictionary.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/dictionary.cc.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/dictionary.cc.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/dictionary.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/dictionary.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/dictionary.h.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/dictionary.h.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/global_function.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/global_function.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/global_function.cc.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/global_function.cc.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/global_function.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/global_function.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/global_function.h.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/global_function.h.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/interface.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/interface.cc.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/interface.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/interface.h.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/union.cc.tpl similarity index 97% rename from bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/union.cc.tpl index 730c31db3f..1585d85550 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/quickjs/union.cc.tpl @@ -6,7 +6,7 @@ #include "core/html/html_image_element.h" #include "core/html/canvas/html_canvas_element.h" #include "core/html/canvas/canvas_gradient.h" -#include "<%= generateUnionTypeFileName(unionType) %>.h" +#include "<%= generateUnionTypeFileName('quickjs', unionType) %>.h" #include "bindings/qjs/converter_impl.h" #include "core/html/html_image_element.h" #include "core/html/canvas/html_canvas_element.h" diff --git a/bridge/scripts/code_generator/templates/idl_templates/union.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/quickjs/union.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/idl_templates/union.h.tpl rename to bridge/scripts/code_generator/templates/idl_templates/quickjs/union.h.tpl diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/base.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/base.cc.tpl new file mode 100644 index 0000000000..f87ef82338 --- /dev/null +++ b/bridge/scripts/code_generator/templates/idl_templates/v8/base.cc.tpl @@ -0,0 +1,88 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "<%= blob.filename %>.h" +#include "foundation/native_value_converter.h" +#include "binding_call_methods.h" +#include "bindings/v8/member_installer.h" +#include "bindings/v8/qjs_function.h" +#include "bindings/v8/converter_impl.h" +#include "bindings/v8/script_wrappable.h" +#include "bindings/v8/script_promise.h" +#include "bindings/v8/cppgc/mutation_scope.h" +#include "core/executing_context.h" +#include "core/dom/element.h" +#include "core/dom/text.h" +#include "core/dom/document.h" +#include "core/dom/document_fragment.h" +#include "core/dom/comment.h" +#include "core/input/touch_list.h" +#include "core/dom/static_node_list.h" +#include "core/html/html_all_collection.h" +#include "defined_properties.h" + +namespace webf { + +<% if (wrapperTypeInfoInit) { %> +<%= wrapperTypeInfoInit %> +<% } %> +<%= content %> + +<% if (globalFunctionInstallList.length > 0 || classPropsInstallList.length > 0 || classMethodsInstallList.length > 0 || constructorInstallList.length > 0) { %> +void QJS<%= className %>::Install(ExecutingContext* context) { + <% if (globalFunctionInstallList.length > 0) { %> InstallGlobalFunctions(context); <% } %> + <% if(classPropsInstallList.length > 0) { %> InstallPrototypeProperties(context); <% } %> + <% if(classMethodsInstallList.length > 0) { %> InstallPrototypeMethods(context); <% } %> + <% if(constructorInstallList.length > 0) { %> InstallConstructor(context); <% } %> +} + +<% } %> + +<% if(globalFunctionInstallList.length > 0) { %> +void QJS<%= className %>::InstallGlobalFunctions(ExecutingContext* context) { + std::initializer_list functionConfig { + <%= globalFunctionInstallList.join(',\n') %> + }; + MemberInstaller::InstallFunctions(context, context->Global(), functionConfig); +} +<% } %> + +<% if(classPropsInstallList.length > 0) { %> +void QJS<%= className %>::InstallPrototypeProperties(ExecutingContext* context) { + const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); + JSValue prototype = context->contextData()->prototypeForType(wrapperTypeInfo); + std::initializer_list functionConfig { + <%= classPropsInstallList.join(',\n') %> + }; + MemberInstaller::InstallFunctions(context, prototype, functionConfig); +} +<% } %> + +<% if(classMethodsInstallList.length > 0) { %> +void QJS<%= className %>::InstallPrototypeMethods(ExecutingContext* context) { + const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); + JSValue prototype = context->contextData()->prototypeForType(wrapperTypeInfo); + + std::initializer_list attributesConfig { + <%= classMethodsInstallList.join(',\n') %> + }; + + MemberInstaller::InstallAttributes(context, prototype, attributesConfig); +} +<% } %> + +<% if (constructorInstallList.length > 0) { %> +void QJS<%= className %>::InstallConstructor(ExecutingContext* context) { + const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); + JSValue constructor = context->contextData()->constructorForType(wrapperTypeInfo); + + std::initializer_list attributeConfig { + <%= constructorInstallList.join(',\n') %> + }; + MemberInstaller::InstallAttributes(context, context->Global(), attributeConfig); +} +<% } %> + +} diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/base.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/base.h.tpl new file mode 100644 index 0000000000..3de9e9bb82 --- /dev/null +++ b/bridge/scripts/code_generator/templates/idl_templates/v8/base.h.tpl @@ -0,0 +1,15 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BRIDGE_<%= blob.filename.toUpperCase() %>_H +#define BRIDGE_<%= blob.filename.toUpperCase() %>_H + +#include +#include "bindings/v8/wrapper_type_info.h" +#include "bindings/v8/generated_code_helper.h" + +<%= content %> + +#endif // BRIDGE_<%= blob.filename.toUpperCase() %>_H diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/dictionary.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/dictionary.cc.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/dictionary.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/dictionary.h.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/global_function.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/global_function.cc.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/global_function.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/global_function.h.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/interface.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/interface.cc.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/interface.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/interface.h.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/union.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/union.cc.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/idl_templates/v8/union.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/v8/union.h.tpl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bridge/scripts/code_generator/templates/json_templates/defined_properties_initializer.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/defined_properties_initializer.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/defined_properties_initializer.cc.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/defined_properties_initializer.cc.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/defined_properties_initializer.h.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/defined_properties_initializer.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/defined_properties_initializer.h.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/defined_properties_initializer.h.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/element_factory.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/element_factory.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/element_factory.cc.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/element_factory.cc.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/element_factory.h.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/element_factory.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/element_factory.h.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/element_factory.h.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/quickjs/element_type_helper.h.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/element_type_helper.h.tpl new file mode 100644 index 0000000000..a5e248765c --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/quickjs/element_type_helper.h.tpl @@ -0,0 +1,62 @@ + // Generated from template: + // code_generator/src/json/templates/element_type_helper.h.tpl + // and input files: + // <%= template_path %> + +#ifndef BRIDGE_CORE_HTML_TYPE_HELPER_H_ +#define BRIDGE_CORE_HTML_TYPE_HELPER_H_ + + +#include "core/dom/element.h" +#include "html_names.h" + +<% _.forEach(data, (item, index) => { %> + <% if (_.isString(item)) { %> +#include "core/html/html_<%= item %>_element.h" + <% } else if (_.isObject(item)) { %> + <% if (item.interfaceHeaderDir) { %> +#include "<%= item.interfaceHeaderDir %>/html_<%= item.filename ? item.filename : item.name %>_element.h" + <% } else if (item.interfaceName != 'HTMLElement'){ %> +#include "core/html/<%= item.filename ? item.filename : `html_${item.name}_element` %>.h" + <% } %> + <% } %> +<% }); %> + +namespace webf { + + +<% function generateTypeHelperTemplate(name) { + return ` +class HTML${_.upperFirst(name)}Element; +template <> +inline bool IsElementOfType(const Node& node) { + return IsA(node); +} +template <> +inline bool IsElementOfType(const HTMLElement& element) { + return IsA(element); +} +template <> +struct DowncastTraits { + static bool AllowFrom(const Element& element) { + return element.HasTagName(html_names::k${name}); + } + static bool AllowFrom(const Node& node) { + return node.IsHTMLElement() && IsA(To(node)); + } +}; +`; +} %> + +<% _.forEach(data, (item, index) => { %> + <% if (_.isString(item)) { %> + <%= generateTypeHelperTemplate(item) %> + <% } else if (_.isObject(item)) { %> + <%= generateTypeHelperTemplate(item.name) %> + <% } %> +<% }) %> + +} + + +#endif diff --git a/bridge/scripts/code_generator/templates/json_templates/event_factory.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/event_factory.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/event_factory.cc.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/event_factory.cc.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/event_factory.h.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/event_factory.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/event_factory.h.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/event_factory.h.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/event_type_helper.h.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/event_type_helper.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/event_type_helper.h.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/event_type_helper.h.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/make_names.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/make_names.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/make_names.cc.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/make_names.cc.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/make_names.h.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/make_names.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/make_names.h.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/make_names.h.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/names_installer.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/names_installer.cc.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/names_installer.cc.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/names_installer.cc.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/names_installer.h.tpl b/bridge/scripts/code_generator/templates/json_templates/quickjs/names_installer.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/names_installer.h.tpl rename to bridge/scripts/code_generator/templates/json_templates/quickjs/names_installer.h.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.cc.tpl new file mode 100644 index 0000000000..6d8d1c740a --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.cc.tpl @@ -0,0 +1,27 @@ +// Generated from template: +// code_generator/src/json/templates/defined_properties_initializer.cc.tpl +// and input files: +// <%= template_path %> + +#include "defined_properties_initializer.h" + +<% data.filenames.forEach(filename => { %> +#include "<%= filename %>.h" +<% }) %> + +namespace webf { + +void DefinedPropertiesInitializer::Init() { + <% data.interfaces.forEach(interfaceName => { %> + <%= interfaceName %>::InitAttributeMap(); + <% }) %> +} + +void DefinedPropertiesInitializer::Dispose() { + <% data.interfaces.forEach(interfaceName => { %> + <%= interfaceName %>::DisposeAttributeMap(); + <% }) %> +} + + +} \ No newline at end of file diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.h.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.h.tpl new file mode 100644 index 0000000000..33fa257f06 --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/defined_properties_initializer.h.tpl @@ -0,0 +1,22 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BRIDGE_DEFINED_PROPERTIES_INTIALIZER_H_ +#define BRIDGE_DEFINED_PROPERTIES_INTIALIZER_H_ + +#include "bindings/v8/atomic_string.h" + +namespace webf { + +class DefinedPropertiesInitializer { + public: + static void Init(); + static void Dispose(); +}; + + +} // namespace webf + +#endif // BRIDGE_DEFINED_PROPERTIES_INTIALIZER_H_ diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/element_factory.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/element_factory.cc.tpl new file mode 100644 index 0000000000..949c0a4a4b --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/element_factory.cc.tpl @@ -0,0 +1,105 @@ +<% +const lprefix = options.prefix.toLowerCase() +const uprefix = options.prefix.toUpperCase() +const items = data.map((item, index) => { + let name + let headerPath + let interfaceName + if (_.isString(item)) { + name = item + headerPath = `core/${lprefix}/${lprefix}_${item}_element.h` + interfaceName = `${uprefix}${_.upperFirst(item)}Element` + } else if (_.isObject(item)) { + name = item.name + if (item.interfaceHeaderDir) { + headerPath = `${item.interfaceHeaderDir}/${lprefix}_${item.filename ? item.filename : item.name}_element.h` + } else { + headerPath = `core/${lprefix}/${item.filename ? item.filename : `${lprefix}_${item.name}_element`}.h` + } + + if (item.interfaceName) { + interfaceName = item.interfaceName + } else { + interfaceName = `${uprefix}${_.upperFirst(item.name)}Element` + } + } + + return { + name, + headerPath, + interfaceName, + } +}) +%> +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + + // Generated from template: + // code_generator/src/json/templates/element_factory.cc.tmp + // and input files: + // <%= template_path %> + +#include "<%=lprefix%>_element_factory.h" +#include +#include "<%=lprefix%>_names.h" +#include "bindings/qjs/cppgc/garbage_collected.h" + +<% _.forEach(items, (item, index) => { %> +#include "<%= item.headerPath %>" +<% }); %> + +namespace webf { + +using ElementType = <%=uprefix%>Element; + +using ConstructorFunction = ElementType* (*)(Document&); + +using FunctionMap = std::unordered_map; + +static thread_local FunctionMap* g_constructors = nullptr; + +struct CreateFunctionMapData { + const AtomicString& tag; + ConstructorFunction func; +}; + +<% _.forEach(items, (item, index) => { %> +static ElementType* <%= item.interfaceName %>Constructor(Document& document) { + return MakeGarbageCollected<<%= item.interfaceName %>>(document); +} +<% }); %> + +static void CreateFunctionMap() { + assert(!g_constructors); + g_constructors = new FunctionMap(); + // Empty array initializer lists are illegal [dcl.init.aggr] and will not + // compile in MSVC. If tags list is empty, add check to skip this. + + const CreateFunctionMapData data[] = { + <% _.forEach(items, (item, index) => { %> + {<%= lprefix %>_names::k<%= item.name %>, <%= item.interfaceName %>Constructor}, + <% }); %> + }; + + for (size_t i = 0; i < std::size(data); i++) + g_constructors->insert(std::make_pair(data[i].tag, data[i].func)); +} + +ElementType* <%= uprefix %>ElementFactory::Create(const AtomicString& name, Document& document) { + if (!g_constructors) + CreateFunctionMap(); + auto it = g_constructors->find(name); + if (it == g_constructors->end()) + return nullptr; + ConstructorFunction function = it->second; + return function(document); +} + +void <%= uprefix %>ElementFactory::Dispose() { + delete g_constructors; + g_constructors = nullptr; +} + +} // namespace webf diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/element_factory.h.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/element_factory.h.tpl new file mode 100644 index 0000000000..5af96443cd --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/element_factory.h.tpl @@ -0,0 +1,29 @@ +<% +const lprefix = options.prefix.toLowerCase() +const uprefix = options.prefix.toUpperCase() +%> +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BRIDGE_CORE_<%=uprefix%>_ELEMENT_FACTORY_H_ +#define BRIDGE_CORE_<%=uprefix%>_ELEMENT_FACTORY_H_ + +#include "bindings/v8/atomic_string.h" + +namespace webf { + +class Document; +class <%=uprefix%>Element; + +class <%=uprefix%>ElementFactory { + public: + // If |local_name| is unknown, nullptr is returned. + static <%=uprefix%>Element* Create(const AtomicString& local_name, Document&); + static void Dispose(); +}; + +} // namespace webf + +#endif // BRIDGE_CORE_<%=uprefix%>_ELEMENT_FACTORY_H_ diff --git a/bridge/scripts/code_generator/templates/json_templates/element_type_helper.h.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/element_type_helper.h.tpl similarity index 100% rename from bridge/scripts/code_generator/templates/json_templates/element_type_helper.h.tpl rename to bridge/scripts/code_generator/templates/json_templates/v8/element_type_helper.h.tpl diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/event_factory.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/event_factory.cc.tpl new file mode 100644 index 0000000000..d78e78318a --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/event_factory.cc.tpl @@ -0,0 +1,112 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + + // Generated from template: + // code_generator/src/json/templates/event_factory.cc.tmp + // and input files: + // <%= template_path %> + +#include "event_factory.h" +#include +#include "event_type_names.h" +#include "bindings/qjs/cppgc/garbage_collected.h" +#include "core/dom/events/custom_event.h" + +<% _.forEach(data, (item, index) => { %> +<% if (_.isString(item)) { %> +#include "qjs_<%= item %>_event.h" +<% } else if (_.isObject(item)) { %> +#include "qjs_<%= _.snakeCase(item.class) %>.h" +<% } %> +<% }); %> + + +namespace webf { + +using EventConstructorFunction = Event* (*)(ExecutingContext* context, const AtomicString& type, RawEvent* raw_event); + +using EventMap = std::unordered_map; + +static thread_local EventMap* g_event_constructors = nullptr; + +struct CreateEventFunctionMapData { + const AtomicString& tag; + EventConstructorFunction func; +}; + +<% _.forEach(data, (item, index) => { %> + <% if (_.isString(item)) { %> + + static Event* <%= _.upperFirst(item) %>EventConstructor(ExecutingContext* context, const AtomicString& type, RawEvent* raw_event) { + if (raw_event == nullptr) { + return MakeGarbageCollected<<%= _.upperFirst(_.camelCase(item)) %>Event>(context, type, ASSERT_NO_EXCEPTION()); + } + if (raw_event->length == sizeof(Native<%= _.upperFirst(item) %>Event) / sizeof(int64_t)) { + return MakeGarbageCollected<<%= _.upperFirst(_.camelCase(item)) %>Event>(context, type, toNativeEventEvent>(raw_event)); + } + return MakeGarbageCollected(context, type, toNativeEvent(raw_event)); + } + <% } else if (_.isObject(item)) { %> + static Event* <%= item.class %>Constructor(ExecutingContext* context, const AtomicString& type, RawEvent* raw_event) { + if (raw_event == nullptr) { + return MakeGarbageCollected<<%= item.class %>>(context, type, ASSERT_NO_EXCEPTION()); + } + if (raw_event->length == sizeof(Native<%= _.upperFirst(item.class) %>) / sizeof(int64_t)) { + return MakeGarbageCollected<<%= item.class %>>(context, type, toNativeEvent>(raw_event)); + } + return MakeGarbageCollected(context, type, toNativeEvent(raw_event)); + } + <% } %> +<% }); %> + +static void CreateEventFunctionMap() { + assert(!g_event_constructors); + g_event_constructors = new EventMap(); + // Empty array initializer lists are illegal [dcl.init.aggr] and will not + // compile in MSVC. If tags list is empty, add check to skip this. + + const CreateEventFunctionMapData data[] = { + + <% _.forEach(data, (item, index) => { %> + <% if (_.isString(item)) { %> + {event_type_names::k<%= item %>, <%= _.upperFirst(item) %>EventConstructor}, + <% } else if (_.isObject(item)) { %> + <% _.forEach(item.types, function(type) { %> + {event_type_names::k<%= type %>, <%= item.class %>Constructor}, + <% }) %> + <% } %> + <% }); %> + + }; + + for (size_t i = 0; i < std::size(data); i++) + g_event_constructors->insert(std::make_pair(data[i].tag, data[i].func)); +} + +Event* EventFactory::Create(ExecutingContext* context, const AtomicString& type, RawEvent* raw_event) { + if (!g_event_constructors) + CreateEventFunctionMap(); + + if (raw_event != nullptr && raw_event->is_custom_event) { + return MakeGarbageCollected(context, type, toNativeEvent(raw_event)); + } + + auto it = g_event_constructors->find(type); + if (it == g_event_constructors->end()) { + if (raw_event == nullptr) { + return MakeGarbageCollected(context, type); + } + return MakeGarbageCollected(context, type, toNativeEvent(raw_event)); + } + EventConstructorFunction function = it->second; + return function(context, type, raw_event); +} + +void EventFactory::Dispose() { + delete g_event_constructors; + g_event_constructors = nullptr; +} + +} // namespace webf diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/event_factory.h.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/event_factory.h.tpl new file mode 100644 index 0000000000..b8d3d7bf8c --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/event_factory.h.tpl @@ -0,0 +1,23 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BRIDGE_CORE_EVENT_FACTORY_H_ +#define BRIDGE_CORE_EVENT_FACTORY_H_ + +#include "bindings/qjs/atomic_string.h" +#include "core/dom/events/event.h" + +namespace webf { + +class EventFactory { + public: + // If |local_name| is unknown, nullptr is returned. + static Event* Create(ExecutingContext* context, const AtomicString& type, RawEvent* raw_event); + static void Dispose(); +}; + +} // namespace webf + +#endif // BRIDGE_CORE_EVENT_FACTORY_H_ diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/event_type_helper.h.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/event_type_helper.h.tpl new file mode 100644 index 0000000000..7f82137a64 --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/event_type_helper.h.tpl @@ -0,0 +1,50 @@ + // Generated from template: + // code_generator/src/json/templates/event_type_helper.h.tpl + // and input files: + // <%= template_path %> + +#ifndef BRIDGE_CORE_EVENT_TYPE_HELPER_H_ +#define BRIDGE_CORE_EVENT_TYPE_HELPER_H_ + +#include "core/dom/events/event.h" +#include "event_type_names.h" + +<% _.forEach(data, (item, index) => { %> + <% if (_.isString(item)) { %> +#include "core/events/<%= item %>_event.h" + <% } else if (_.isObject(item)) { %> +#include "core/events/<%= _.snakeCase(item.class) %>.h" + <% } %> +<% }); %> + +namespace webf { + +<% function generateTypeHelperTemplate(name) { + return ` + class ${_.upperFirst(_.camelCase(name))}; + template <> + inline bool IsEventOfType(const Event& event) { + return IsA<${_.upperFirst(_.camelCase(name))}>(event); + } + template <> + struct DowncastTraits<${_.upperFirst(_.camelCase(name))}> { + static bool AllowFrom(const Event& event) { + return event.Is${_.upperFirst(_.camelCase(name))}(); + } + }; + `; +} %> + + <% _.forEach(data, (item, index) => { %> + <% if (_.isString(item)) { %> + <%= generateTypeHelperTemplate(item + 'Event') %> + <% } else if (_.isObject(item)) { %> + <%= generateTypeHelperTemplate(item.class) %> + <% } %> + <% }) %> + + +} + + +#endif diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/make_names.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/make_names.cc.tpl new file mode 100644 index 0000000000..6f3aaba613 --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/make_names.cc.tpl @@ -0,0 +1,89 @@ +// Generated from template: +// code_generator/src/json/templates/make_names.h.tmpl +// and input files: +// <%= template_path %> + +#include "<%= name %>.h" + +namespace webf { +namespace <%= name %> { + +thread_local void* names_storage[kNamesCount * ((sizeof(AtomicString) + sizeof(void *) - 1) / sizeof(void *))]; + +<% if (deps && deps.html_attribute_names) { %> +thread_local void* html_attribute_names_storage[kHtmlAttributeNamesCount * ((sizeof(AtomicString) + sizeof(void *) - 1) / sizeof(void *))]; +<% } %> + + +<% _.forEach(data, function(name, index) { %> + <% if (_.isArray(name)) { %> +thread_local const AtomicString& k<%= name[0] %> = reinterpret_cast(&names_storage)[<%= index %>]; + <% } else if (_.isObject(name)) { %> +thread_local const AtomicString& k<%= name.name %> = reinterpret_cast(&names_storage)[<%= index %>]; + <% } else { %> +thread_local const AtomicString& k<%= name %> = reinterpret_cast(&names_storage)[<%= index %>];<% } %> +<% }) %> + +<% if (deps && deps.html_attribute_names) { %> + <% _.forEach(deps.html_attribute_names.data, function(name, index) { %> + thread_local const AtomicString& k<%= upperCamelCase(name) %>Attr = reinterpret_cast(&html_attribute_names_storage)[<%= index %>]; + <% }) %> +<% } %> + +void Init(v8::Isolate* isolate) { + struct NameEntry { + const char* str; + }; + + static const NameEntry kNames[] = { + <% _.forEach(data, function(name) { %> + <% if (Array.isArray(name)) { %> + { "<%= name[1] %>" }, + <% } else if(_.isObject(name)) { %> + { "<%= name.name %>" }, + <% } else { %> + { "<%= name %>" }, + <% } %> + <% }); %> + }; + + <% if (deps && deps.html_attribute_names) { %> + static const NameEntry kHtmlAttributeNames[] = { + <% _.forEach(deps.html_attribute_names.data, function(name) { %> + { "<%= name %>" }, + <% }); %> + }; + <% } %> + + for(size_t i = 0; i < std::size(kNames); i ++) { + void* address = reinterpret_cast(&names_storage) + i; + new (address) AtomicString(isolate, kNames[i].str); + } + + <% if (deps && deps.html_attribute_names) { %> + for(size_t i = 0; i < std::size(kHtmlAttributeNames); i ++) { + void* address = reinterpret_cast(&html_attribute_names_storage) + i; + new (address) AtomicString(isolate, kHtmlAttributeNames[i].str); + } + <% } %> +}; + +void Dispose(){ + for(size_t i = 0; i < kNamesCount; i ++) { + AtomicString* atomic_string = reinterpret_cast(&names_storage) + i; + atomic_string->~AtomicString(); + } + memset(names_storage, 0x00, sizeof(AtomicString) * kNamesCount); + + <% if (deps && deps.html_attribute_names) { %> + for(size_t i = 0; i < kHtmlAttributeNamesCount; i ++) { + AtomicString* atomic_string = reinterpret_cast(&html_attribute_names_storage) + i; + atomic_string->~AtomicString(); + } + memset(html_attribute_names_storage, 0x00, sizeof(AtomicString) * kHtmlAttributeNamesCount); + <% } %> +}; + + +} +} // webf diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/make_names.h.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/make_names.h.tpl new file mode 100644 index 0000000000..b7d24358e1 --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/make_names.h.tpl @@ -0,0 +1,41 @@ +// Generated from template: +// code_generator/src/json/templates/make_names.h.tmpl +// and input files: +// <%= template_path %> + + +#ifndef <%= _.snakeCase(name).toUpperCase() %>_H_ +#define <%= _.snakeCase(name).toUpperCase() %>_H_ + +#include "bindings/v8/atomic_string.h" + +namespace webf { +namespace <%= name %> { + +<% _.forEach(data, function(name, index) { %> + <% if (_.isArray(name)) { %> + extern thread_local const AtomicString& k<%= name[0] %>; + <% } else if (_.isObject(name)) { %> + extern thread_local const AtomicString& k<%= name.name %>; + <% } else { %> + extern thread_local const AtomicString& k<%= name %>; + <% } %> +<% }) %> + +<% if (deps && deps.html_attribute_names) { %> + constexpr unsigned kHtmlAttributeNamesCount = <%= deps.html_attribute_names.data.length %>; + <% _.forEach(deps.html_attribute_names.data, function(name, index) { %> + extern thread_local const AtomicString& k<%= upperCamelCase(name) %>Attr; + <% }) %> +<% } %> + +constexpr unsigned kNamesCount = <%= data.length %>; + +void Init(v8::Isolate* isolate); +void Dispose(); + +} + +} // webf + +#endif // #define <%= _.snakeCase(name).toUpperCase() %> diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/names_installer.cc.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/names_installer.cc.tpl new file mode 100644 index 0000000000..ef88ee482e --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/names_installer.cc.tpl @@ -0,0 +1,25 @@ +// Generated from template: +// code_generator/src/json/templates/names_installer.cc.tmpl + +<% names.forEach(function(k) { %> +#include "<%= k %>.h" +<% }); %> + +namespace webf { +namespace <%= name %> { + +void Init(JSContext* ctx) { +<% names.forEach(function(k) { %> + <%= k %>::Init(ctx); +<% }); %> +} + +void Dispose() { +<% names.forEach(function(k) { %> + <%= k %>::Dispose(); +<% }); %> +} + +} + +} // webf \ No newline at end of file diff --git a/bridge/scripts/code_generator/templates/json_templates/v8/names_installer.h.tpl b/bridge/scripts/code_generator/templates/json_templates/v8/names_installer.h.tpl new file mode 100644 index 0000000000..de63ab7fdd --- /dev/null +++ b/bridge/scripts/code_generator/templates/json_templates/v8/names_installer.h.tpl @@ -0,0 +1,20 @@ +// Generated from template: +// code_generator/src/json/templates/names_installer.h.tmpl + + +#ifndef <%= _.snakeCase(name).toUpperCase() %>_H_ +#define <%= _.snakeCase(name).toUpperCase() %>_H_ + +#include "bindings/qjs/atomic_string.h" + +namespace webf { +namespace <%= name %> { + +void Init(JSContext* ctx); +void Dispose(); + +} + +} // webf + +#endif // #define <%= _.snakeCase(name).toUpperCase() %> diff --git a/bridge/test/test.cmake b/bridge/test/test.cmake index 4367b80161..61fe03bb2d 100644 --- a/bridge/test/test.cmake +++ b/bridge/test/test.cmake @@ -1,7 +1,7 @@ list(APPEND WEBF_TEST_SOURCE - include/webf_bridge_test.h - webf_bridge_test.cc - polyfill/dist/testframework.cc + include/webf_bridge_test.h + webf_bridge_test.cc + polyfill/dist/testframework.cc ) set(gtest_disable_pthreads ON) @@ -9,47 +9,64 @@ set(gtest_disable_pthreads ON) add_subdirectory(./third_party/googletest) add_subdirectory(./third_party/benchmark) -list(APPEND WEBF_TEST_SOURCE - test/webf_test_context.cc - test/webf_test_context.h - ) -list(APPEND WEBF_UNIT_TEST_SOURCEURCE - ./test/webf_test_env.cc - ./test/webf_test_env.h - ./bindings/qjs/atomic_string_test.cc - ./bindings/qjs/script_value_test.cc - ./bindings/qjs/qjs_engine_patch_test.cc - ./core/dom/events/custom_event_test.cc - ./core/executing_context_test.cc - ./core/frame/console_test.cc - ./core/frame/module_manager_test.cc - ./core/dom/events/event_target_test.cc - ./core/dom/document_test.cc - ./core/dom/legacy/element_attribute_test.cc - ./core/dom/node_test.cc - ./core/html/html_collection_test.cc - ./core/dom/element_test.cc - ./core/frame/dom_timer_test.cc - ./core/frame/window_test.cc - ./core/css/inline_css_style_declaration_test.cc - ./core/html/html_element_test.cc - ./core/html/custom/widget_element_test.cc - ./core/timing/performance_test.cc -) + +if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") + + list(APPEND WEBF_TEST_SOURCE + test/webf_test_context_qjs.cc + test/webf_test_context_qjs.h + ) + + list(APPEND WEBF_UNIT_TEST_SOURCEURCE + ./test/webf_test_env.cc + ./test/webf_test_env.h + ./bindings/qjs/atomic_string_test.cc + ./bindings/qjs/script_value_test.cc + ./bindings/qjs/qjs_engine_patch_test.cc + ./core/dom/events/custom_event_test.cc + ./core/executing_context_test.cc + ./core/frame/console_test.cc + ./core/frame/module_manager_test.cc + ./core/dom/events/event_target_test.cc + ./core/dom/document_test.cc + ./core/dom/legacy/element_attribute_test.cc + ./core/dom/node_test.cc + ./core/html/html_collection_test.cc + ./core/dom/element_test.cc + ./core/frame/dom_timer_test.cc + ./core/frame/window_test.cc + ./core/css/inline_css_style_declaration_test.cc + ./core/html/html_element_test.cc + ./core/html/custom/widget_element_test.cc + ./core/timing/performance_test.cc + ) + +elseif ($ENV{WEBF_JS_ENGINE} MATCHES "v8") + list(APPEND WEBF_TEST_SOURCE + test/webf_test_context_v8.cc + test/webf_test_context_v8.h + ) + + list(APPEND WEBF_UNIT_TEST_SOURCEURCE + ./test/webf_test_env.cc + ./test/webf_test_env.h + ./bindings/v8/atomic_string_test.cc + ) +endif () ### webf_unit_test executable add_executable(webf_unit_test - ${WEBF_UNIT_TEST_SOURCEURCE} - ${WEBF_TEST_SOURCE} - ${BRIDGE_SOURCE} + ${WEBF_UNIT_TEST_SOURCEURCE} + ${WEBF_TEST_SOURCE} + ${BRIDGE_SOURCE} ) target_include_directories(webf_unit_test PUBLIC ./third_party/googletest/googletest/include ${BRIDGE_INCLUDE} ./test) target_link_libraries(webf_unit_test gtest gtest_main ${BRIDGE_LINK_LIBS}) if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") - target_compile_options(quickjs PUBLIC -DDUMP_LEAKS=1) -endif() + target_compile_options(quickjs PUBLIC -DDUMP_LEAKS=1) +endif () target_compile_options(webf PUBLIC -DDUMP_LEAKS=1) @@ -59,19 +76,19 @@ target_compile_definitions(webf_unit_test PUBLIC -DUNIT_TEST=1) if (DEFINED ENV{LIBRARY_OUTPUT_DIR}) set_target_properties(webf_unit_test - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" - ) -endif() + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" + ) +endif () # Run webf integration without flutter. add_executable(webf_integration_test - ${WEBF_TEST_SOURCE} - ${BRIDGE_SOURCE} - ./test/webf_test_env.cc - ./test/webf_test_env.h - ./test/run_integration_test.cc - ) + ${WEBF_TEST_SOURCE} + ${BRIDGE_SOURCE} + ./test/webf_test_env.cc + ./test/webf_test_env.h + ./test/run_integration_test.cc +) target_include_directories(webf_integration_test PUBLIC ./third_party/googletest/googletest/include ${BRIDGE_INCLUDE} ./test) target_link_libraries(webf_integration_test gtest gtest_main ${BRIDGE_LINK_LIBS}) target_compile_definitions(webf_integration_test PUBLIC -DFLUTTER_BACKEND=0) @@ -80,18 +97,18 @@ target_compile_definitions(webf_integration_test PUBLIC -DSPEC_FILE_PATH="${CMAK # Benchmark test add_executable(webf_benchmark - ${WEBF_TEST_SOURCE} - ${BRIDGE_SOURCE} - ./test/webf_test_env.cc - ./test/webf_test_env.h - ./test/benchmark/create_element.cc + ${WEBF_TEST_SOURCE} + ${BRIDGE_SOURCE} + ./test/webf_test_env.cc + ./test/webf_test_env.h + ./test/benchmark/create_element.cc ) target_include_directories(webf_benchmark PUBLIC - ./third_party/googletest/googletest/include - ./third_party/benchmark/include/ - ${BRIDGE_INCLUDE} - ./test) -target_link_libraries(webf_benchmark gtest gtest_main benchmark::benchmark ${BRIDGE_LINK_LIBS}) + ./third_party/googletest/googletest/include + ./third_party/benchmark/include/ + ${BRIDGE_INCLUDE} + ./test) +target_link_libraries(webf_benchmark gtest gtest_main benchmark::benchmark ${BRIDGE_LINK_LIBS}) target_compile_definitions(webf_benchmark PUBLIC -DFLUTTER_BACKEND=0) target_compile_definitions(webf_benchmark PUBLIC -DUNIT_TEST=1) @@ -99,14 +116,14 @@ target_compile_definitions(webf_benchmark PUBLIC -DUNIT_TEST=1) add_library(webf_test SHARED ${WEBF_TEST_SOURCE}) target_link_libraries(webf_test PRIVATE ${BRIDGE_LINK_LIBS} webf) target_include_directories(webf_test PRIVATE - ${BRIDGE_INCLUDE} - ./test - ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ./include) + ${BRIDGE_INCLUDE} + ./test + ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ./include) if (DEFINED ENV{LIBRARY_OUTPUT_DIR}) set_target_properties(webf_test - PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" - RUNTIME_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" - ) -endif() + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" + RUNTIME_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" + ) +endif () diff --git a/bridge/test/webf_test_context.cc b/bridge/test/webf_test_context_qjs.cc similarity index 99% rename from bridge/test/webf_test_context.cc rename to bridge/test/webf_test_context_qjs.cc index 4c4df8227d..6fca01abc0 100644 --- a/bridge/test/webf_test_context.cc +++ b/bridge/test/webf_test_context_qjs.cc @@ -3,7 +3,7 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ -#include "webf_test_context.h" +#include "webf_test_context_qjs.h" #include "bindings/qjs/member_installer.h" #include "bindings/qjs/qjs_interface_bridge.h" #include "core/dom/document.h" diff --git a/bridge/test/webf_test_context.h b/bridge/test/webf_test_context_qjs.h similarity index 100% rename from bridge/test/webf_test_context.h rename to bridge/test/webf_test_context_qjs.h diff --git a/bridge/test/webf_test_context_v8.cc b/bridge/test/webf_test_context_v8.cc new file mode 100644 index 0000000000..5cdbb0b476 --- /dev/null +++ b/bridge/test/webf_test_context_v8.cc @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "webf_test_context_v8.h" +//#include "bindings/qjs/member_installer.h" +//#include "bindings/qjs/qjs_interface_bridge.h" +//#include "core/dom/document.h" +//#include "core/fileapi/blob.h" +//#include "core/frame/window.h" +//#include "core/html/html_body_element.h" +//#include "core/html/html_html_element.h" +//#include "core/html/parser/html_parser.h" +//#include "qjs_blob.h" +#include "testframework.h" + +namespace webf { + +struct ExecuteCallbackContext { + ExecuteCallbackContext() = delete; + + explicit ExecuteCallbackContext(ExecutingContext* context, + ExecuteResultCallback executeCallback, + WebFTestContext* webf_context, + Dart_PersistentHandle persistent_handle) + : executeCallback(executeCallback), + context(context), + webf_context(webf_context), + persistent_handle(persistent_handle){}; + ExecuteResultCallback executeCallback; + ExecutingContext* context; + WebFTestContext* webf_context; + Dart_PersistentHandle persistent_handle; +}; + +void WebFTestContext::invokeExecuteTest(Dart_PersistentHandle persistent_handle, + ExecuteResultCallback executeCallback) { +// if (execute_test_callback_ == nullptr) { +// return; +// } +// +// auto done = [](JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic, +// JSValue* func_data) -> JSValue { +// JSValue& statusValue = argv[0]; +// JSValue proxyObject = func_data[0]; +// auto* callbackContext = static_cast(JS_GetOpaque(proxyObject, 1)); +// +// if (!JS_IsString(statusValue)) { +// return JS_ThrowTypeError(ctx, "failed to execute 'done': parameter 1 (status) is not a string"); +// } +// +// WEBF_LOG(VERBOSE) << "Done.."; +// +// std::unique_ptr status = webf::jsValueToNativeString(ctx, statusValue); +// +// callbackContext->context->dartIsolateContext()->dispatcher()->PostToDart( +// callbackContext->context->isDedicated(), +// [](ExecuteCallbackContext* callback_context, SharedNativeString* status) { +// Dart_Handle handle = Dart_HandleFromPersistent_DL(callback_context->persistent_handle); +// callback_context->executeCallback(handle, status); +// Dart_DeletePersistentHandle_DL(callback_context->persistent_handle); +// callback_context->webf_context->execute_test_proxy_object_ = JS_NULL; +// }, +// callbackContext, status.release()); +// JS_FreeValue(ctx, proxyObject); +// return JS_NULL; +// }; +// +// auto* callbackContext = new ExecuteCallbackContext(context_, executeCallback, this, persistent_handle); +// execute_test_proxy_object_ = JS_NewObject(context_->ctx()); +// JS_SetOpaque(execute_test_proxy_object_, callbackContext); +// JSValue callbackData[]{execute_test_proxy_object_}; +// JSValue callback = JS_NewCFunctionData(context_->ctx(), done, 0, 0, 1, callbackData); +// +// ScriptValue arguments[] = {ScriptValue(context_->ctx(), callback)}; +// ScriptValue result = +// execute_test_callback_->Invoke(context_->ctx(), ScriptValue::Empty(context_->ctx()), 1, arguments); +// context_->HandleException(&result); +// context_->DrainMicrotasks(); +// JS_FreeValue(context_->ctx(), callback); +// execute_test_callback_ = nullptr; +} + +WebFTestContext::WebFTestContext(ExecutingContext* context) + : context_(context), page_(static_cast(context->owner())) { +// context->dartIsolateContext()->profiler()->StartTrackInitialize(); + +// page_->owner = this; +// page_->disposeCallback = [](WebFPage* bridge) { delete static_cast(bridge->owner); }; + +// std::initializer_list functionConfig{ +// {"__webf_execute_test__", executeTest, 1}, +// {"__webf_match_image_snapshot__", matchImageSnapshot, 3}, +// {"__webf_environment__", environment, 0}, +// {"__webf_simulate_pointer__", simulatePointer, 3}, +// {"__webf_simulate_inputtext__", simulateInputText, 1}, +// {"__webf_trigger_global_error__", triggerGlobalError, 0}, +// {"__webf_parse_html__", parseHTML, 1}, +// }; + +// MemberInstaller::InstallFunctions(context, context->Global(), functionConfig); +// initWebFTestFramework(context); +// +// context->dartIsolateContext()->profiler()->FinishTrackInitialize(); +} + +WebFTestContext::~WebFTestContext() { +// JS_FreeValue(context_->ctx(), execute_test_proxy_object_); +} + +bool WebFTestContext::parseTestHTML(const uint16_t* code, size_t codeLength) { + if (!context_->IsContextValid()) + return false; + std::string utf8Code = toUTF8(std::u16string(reinterpret_cast(code), codeLength)); + return page_->parseHTML(utf8Code.c_str(), utf8Code.length()); +} + +void WebFTestContext::registerTestEnvDartMethods(uint64_t* methodBytes, int32_t length) { + size_t i = 0; + + auto dartMethodPtr = context_->dartMethodPtr(); + +// dartMethodPtr->SetOnJSError(reinterpret_cast(methodBytes[i++])); +// dartMethodPtr->SetMatchImageSnapshot(reinterpret_cast(methodBytes[i++])); +// dartMethodPtr->SetMatchImageSnapshotBytes(reinterpret_cast(methodBytes[i++])); +// dartMethodPtr->SetEnvironment(reinterpret_cast(methodBytes[i++])); +// dartMethodPtr->SetSimulatePointer(reinterpret_cast(methodBytes[i++])); +// dartMethodPtr->SetSimulateInputText(reinterpret_cast(methodBytes[i++])); + + assert_m(i == length, "Dart native methods count is not equal with C++ side method registrations."); +} + +} // namespace webf diff --git a/bridge/test/webf_test_context_v8.h b/bridge/test/webf_test_context_v8.h new file mode 100644 index 0000000000..e6c91f0492 --- /dev/null +++ b/bridge/test/webf_test_context_v8.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef BRIDGE_WEBF_TEST_CONTEXT_H +#define BRIDGE_WEBF_TEST_CONTEXT_H + + +#include "core/executing_context.h" +#include "core/page.h" +#include "webf_bridge_test.h" + +namespace webf { + +//struct ImageSnapShotContext { +// JSValue callback; +// ExecutingContext* context; +// list_head link; +//}; + +class WebFTestContext final { + public: + explicit WebFTestContext() = delete; + explicit WebFTestContext(ExecutingContext* context); + ~WebFTestContext(); + + /// Evaluate JavaScript source code with build-in test frameworks, use in test only. + bool parseTestHTML(const uint16_t* code, size_t codeLength); + void invokeExecuteTest(Dart_PersistentHandle persistent_handle, ExecuteResultCallback executeCallback); + void registerTestEnvDartMethods(uint64_t* methodBytes, int32_t length); + + WebFPage* page() const { return page_; } + +// std::shared_ptr execute_test_callback_{nullptr}; +// JSValue execute_test_proxy_object_{JS_NULL}; + + private: + /// the pointer of JSContext, ownership belongs to JSContext + ExecutingContext* context_{nullptr}; + WebFPage* page_; +}; + +} // namespace webf + +#endif // BRIDGE_WEBF_TEST_CONTEXT_H diff --git a/bridge/test/webf_test_env.cc b/bridge/test/webf_test_env.cc index ef8c57baa7..fe03302830 100644 --- a/bridge/test/webf_test_env.cc +++ b/bridge/test/webf_test_env.cc @@ -7,20 +7,27 @@ #include #include "bindings/qjs/native_string_utils.h" -#include "core/dom/frame_request_callback_collection.h" -#include "core/frame/dom_timer.h" +//#include "core/dom/frame_request_callback_collection.h" +//#include "core/frame/dom_timer.h" #include "core/page.h" #include "foundation/native_string.h" -#include "foundation/native_value_converter.h" +//#include "foundation/native_value_converter.h" #include "webf_bridge_test.h" -#include "webf_test_context.h" #include "webf_test_env.h" +#if WEBF_QUICKJS_JS_ENGINE +#include "webf_test_context_qjs.h" +#else +#include "webf_test_context_v8.h" +#endif + namespace webf { class WebFTestContext; std::unordered_map test_context_map; +#if WEBF_QUICKJS_JS_ENGINE + typedef struct { struct list_head link; int64_t timeout; @@ -51,6 +58,8 @@ static void unlink_callback(JSThreadState* ts, JSFrameCallback* th) { ts->os_frameCallbacks.erase(th->callbackId); } +#endif + NativeValue* TEST_invokeModule(void* callbackContext, double contextId, int64_t profile_link_id, @@ -84,6 +93,8 @@ void TEST_setTimeout(int32_t new_timer_id, double contextId, AsyncCallback callback, int32_t timeout) { +#if WEBF_QUICKJS_JS_ENGINE + auto* context = timer->context(); JSRuntime* rt = context->dartIsolateContext()->runtime(); JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(rt)); @@ -97,6 +108,8 @@ void TEST_setTimeout(int32_t new_timer_id, th->isInterval = false; ts->os_timers[new_timer_id] = th; + +#endif } void TEST_setInterval(int32_t new_timer_id, @@ -104,6 +117,8 @@ void TEST_setInterval(int32_t new_timer_id, double contextId, AsyncCallback callback, int32_t timeout) { +#if WEBF_QUICKJS_JS_ENGINE + auto* context = timer->context(); JSRuntime* rt = context->dartIsolateContext()->runtime(); JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(rt)); @@ -117,39 +132,48 @@ void TEST_setInterval(int32_t new_timer_id, th->isInterval = true; ts->os_timers[new_timer_id] = th; -} -int32_t callbackId = 0; - -void TEST_requestAnimationFrame(int32_t new_id, - webf::FrameCallback* frameCallback, - double contextId, - AsyncRAFCallback handler) { - auto* context = frameCallback->context(); - JSRuntime* rt = context->dartIsolateContext()->runtime(); - JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(rt)); - JSFrameCallback* th = static_cast(js_mallocz(context->ctx(), sizeof(*th))); - th->handler = handler; - th->callback = frameCallback; - th->contextId = context->contextId(); - th->callbackId = new_id; - - ts->os_frameCallbacks[new_id] = th; +#endif } -void TEST_cancelAnimationFrame(double contextId, int32_t id) { - auto* page = test_context_map[contextId]->page(); - auto* context = page->executingContext(); - JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(context->dartIsolateContext()->runtime())); - ts->os_frameCallbacks.erase(id); -} +int32_t callbackId = 0; -void TEST_clearTimeout(double contextId, int32_t timerId) { - auto* page = test_context_map[contextId]->page(); - auto* context = page->executingContext(); - JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(context->dartIsolateContext()->runtime())); - ts->os_timers.erase(timerId); -} +//void TEST_requestAnimationFrame(int32_t new_id, +// webf::FrameCallback* frameCallback, +// double contextId, +// AsyncRAFCallback handler) { +//#if WEBF_QUICKJS_JS_ENGINE +// auto* context = frameCallback->context(); +// JSRuntime* rt = context->dartIsolateContext()->runtime(); +// JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(rt)); +// JSFrameCallback* th = static_cast(js_mallocz(context->ctx(), sizeof(*th))); +// th->handler = handler; +// th->callback = frameCallback; +// th->contextId = context->contextId(); +// th->callbackId = new_id; +// +// ts->os_frameCallbacks[new_id] = th; +// +//#endif +//} +// +//void TEST_cancelAnimationFrame(double contextId, int32_t id) { +//#if WEBF_QUICKJS_JS_ENGINE +// auto* page = test_context_map[contextId]->page(); +// auto* context = page->executingContext(); +// JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(context->dartIsolateContext()->runtime())); +// ts->os_frameCallbacks.erase(id); +//#endif +//} +// +//void TEST_clearTimeout(double contextId, int32_t timerId) { +//#if WEBF_QUICKJS_JS_ENGINE +// auto* page = test_context_map[contextId]->page(); +// auto* context = page->executingContext(); +// JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(context->dartIsolateContext()->runtime())); +// ts->os_timers.erase(timerId); +//#endif +//} NativeScreen* TEST_getScreen(double contextId) { return nullptr; @@ -200,11 +224,11 @@ double contextId = -1; WebFTestEnv::WebFTestEnv(DartIsolateContext* owner_isolate_context, webf::WebFPage* page) : page_(page), isolate_context_(owner_isolate_context) { - owner_isolate_context->profiler()->StartTrackInitialize(); +// owner_isolate_context->profiler()->StartTrackInitialize(); } WebFTestEnv::~WebFTestEnv() { - isolate_context_->profiler()->FinishTrackInitialize(); +// isolate_context_->profiler()->FinishTrackInitialize(); delete isolate_context_; } @@ -216,10 +240,12 @@ std::unique_ptr TEST_init(OnJSError onJsError) { void* testContext = initTestFramework(page); test_context_map[pageContextId] = reinterpret_cast(testContext); TEST_mockTestEnvDartMethods(testContext, onJsError); +#if WEBF_QUICKJS_JS_ENGINE JS_TurnOnGC(static_cast(dart_isolate_context)->runtime()); JSThreadState* th = new JSThreadState(); JS_SetRuntimeOpaque( reinterpret_cast(testContext)->page()->executingContext()->dartIsolateContext()->runtime(), th); +#endif return std::make_unique((webf::DartIsolateContext*)dart_isolate_context, (webf::WebFPage*)page); } @@ -240,6 +266,7 @@ std::unique_ptr TEST_allocateNewPage(OnJSError onJsError) { } static bool jsPool(webf::ExecutingContext* context) { +#if WEBF_QUICKJS_JS_ENGINE JSRuntime* rt = context->dartIsolateContext()->runtime(); JSThreadState* ts = static_cast(JS_GetRuntimeOpaque(rt)); int64_t cur_time, delay; @@ -284,6 +311,8 @@ static bool jsPool(webf::ExecutingContext* context) { } } +#endif + return false; } @@ -329,9 +358,9 @@ std::vector TEST_getMockDartMethods(OnJSError onJSError) { reinterpret_cast(TEST_reloadApp), reinterpret_cast(TEST_setTimeout), reinterpret_cast(TEST_setInterval), - reinterpret_cast(TEST_clearTimeout), - reinterpret_cast(TEST_requestAnimationFrame), - reinterpret_cast(TEST_cancelAnimationFrame), +// reinterpret_cast(TEST_clearTimeout), +// reinterpret_cast(TEST_requestAnimationFrame), +// reinterpret_cast(TEST_cancelAnimationFrame), reinterpret_cast(TEST_toBlob), reinterpret_cast(TEST_flushUICommand), reinterpret_cast(TEST_CreateBindingObject), diff --git a/bridge/test/webf_test_env.h b/bridge/test/webf_test_env.h index fa3d81e8bb..984aafa679 100644 --- a/bridge/test/webf_test_env.h +++ b/bridge/test/webf_test_env.h @@ -7,7 +7,7 @@ #define BRIDGE_TEST_WEBF_TEST_ENV_H_ #include -#include "bindings/qjs/cppgc/mutation_scope.h" +//#include "bindings/qjs/cppgc/mutation_scope.h" #include "core/dart_methods.h" #include "core/executing_context.h" #include "core/page.h" @@ -16,10 +16,10 @@ using namespace webf; // Trigger a callbacks before GC free the eventTargets. -using TEST_OnEventTargetDisposed = void (*)(EventTarget* event_target); -struct UnitTestEnv { - TEST_OnEventTargetDisposed on_event_target_disposed{nullptr}; -}; +//using TEST_OnEventTargetDisposed = void (*)(EventTarget* event_target); +//struct UnitTestEnv { +// TEST_OnEventTargetDisposed on_event_target_disposed{nullptr}; +//}; // Mock dart methods and add async timer to emulate webf environment in C++ unit test. @@ -43,8 +43,8 @@ std::unique_ptr TEST_allocateNewPage(OnJSError onJsError); void TEST_runLoop(ExecutingContext* context); std::vector TEST_getMockDartMethods(OnJSError onJSError); void TEST_mockTestEnvDartMethods(void* testContext, OnJSError onJSError); -void TEST_registerEventTargetDisposedCallback(int32_t context_unique_id, TEST_OnEventTargetDisposed callback); -std::shared_ptr TEST_getEnv(int32_t context_unique_id); +//void TEST_registerEventTargetDisposedCallback(int32_t context_unique_id, TEST_OnEventTargetDisposed callback); +//std::shared_ptr TEST_getEnv(int32_t context_unique_id); } // namespace webf // void TEST_dispatchEvent(int32_t contextId, EventTarget* eventTarget, const std::string type); // void TEST_callNativeMethod(void* nativePtr, void* returnValue, void* method, int32_t argc, void* argv); diff --git a/bridge/webf_bridge_test.cc b/bridge/webf_bridge_test.cc index f4341129cc..9ecfac0907 100644 --- a/bridge/webf_bridge_test.cc +++ b/bridge/webf_bridge_test.cc @@ -10,7 +10,11 @@ #include #include "bindings/qjs/native_string_utils.h" #include "logging.h" -#include "webf_test_context.h" +#if WEBF_QUICKJS_JS_ENGINE +#include "webf_test_context_qjs.h" +#elif WEBF_V8_JS_ENGINE +#include "webf_test_context_v8.h" +#endif std::unordered_map testContextPool = std::unordered_map(); diff --git a/scripts/tasks.js b/scripts/tasks.js index 8177f4061f..bf8f42c40a 100644 --- a/scripts/tasks.js +++ b/scripts/tasks.js @@ -21,7 +21,7 @@ program .option('--enable-log', 'Enable log printing') .parse(process.argv); -const SUPPORTED_JS_ENGINES = ['jsc', 'quickjs']; +const SUPPORTED_JS_ENGINES = ['v8', 'quickjs']; const targetJSEngine = process.env.WEBF_JS_ENGINE || 'quickjs'; if (SUPPORTED_JS_ENGINES.indexOf(targetJSEngine) < 0) { @@ -559,7 +559,7 @@ task('generate-bindings-code', (done) => { return done(buildResult.status); } - let compileResult = spawnSync('node', ['bin/code_generator', '-s', '../../core', '-d', '../../out'], { + let compileResult = spawnSync('node', ['bin/code_generator', '-s', '../../core', '-d', '../../out', '-p', targetJSEngine], { cwd: paths.codeGen, env: { ...process.env, From baf64ff03169ac22757b1986965f8f1e6411546a Mon Sep 17 00:00:00 2001 From: andycall Date: Fri, 19 Apr 2024 00:29:58 +0800 Subject: [PATCH 05/35] test: add atomic string testing. --- bridge/CMakeLists.txt | 51 ++--- bridge/bindings/qjs/native_string_utils.h | 2 - bridge/bindings/v8/atomic_string.cc | 35 ++-- bridge/bindings/v8/atomic_string.h | 4 +- bridge/bindings/v8/atomic_string_test.cc | 226 ++++++++++++---------- bridge/bindings/v8/native_string_utils.cc | 43 ++++ bridge/bindings/v8/native_string_utils.h | 45 +++++ bridge/test/test.cmake | 40 ++-- 8 files changed, 291 insertions(+), 155 deletions(-) create mode 100644 bridge/bindings/v8/native_string_utils.cc create mode 100644 bridge/bindings/v8/native_string_utils.h diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index ada3b4bc6d..de01a2530b 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -18,7 +18,7 @@ if(MSVC) endif() if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") + set(CMAKE_OSX_ARCHITECTURES "arm64") endif() set(WEBF_JS_ENGINE $ENV{WEBF_JS_ENGINE}) @@ -71,7 +71,7 @@ endif () if (NOT MSVC) if (${CMAKE_BUILD_TYPE} STREQUAL "Debug") # Avoid quickjs stackoverflow. - add_compile_options(-O1) +# add_compile_options(-O1) endif() endif() @@ -82,24 +82,24 @@ if (DEFINED PLATFORM) endif() list(APPEND BRIDGE_SOURCE - foundation/logging.cc +# foundation/logging.cc foundation/native_string.cc - foundation/ui_task_queue.cc +# foundation/ui_task_queue.cc # foundation/shared_ui_command.cc - foundation/inspector_task_queue.cc - foundation/task_queue.cc +# foundation/inspector_task_queue.cc +# foundation/task_queue.cc foundation/string_view.cc - foundation/native_value.cc - foundation/native_type.cc - foundation/stop_watch.cc +# foundation/native_value.cc +# foundation/native_type.cc +# foundation/stop_watch.cc # foundation/profiler.cc - foundation/dart_readable.cc +# foundation/dart_readable.cc foundation/rust_readable.cc # foundation/ui_command_buffer.cc # foundation/ui_command_strategy.cc - polyfill/dist/polyfill.cc - multiple_threading/dispatcher.cc - multiple_threading/looper.cc +# polyfill/dist/polyfill.cc +# multiple_threading/dispatcher.cc +# multiple_threading/looper.cc ${CMAKE_CURRENT_LIST_DIR}/third_party/dart/include/dart_api_dl.c ) @@ -638,6 +638,9 @@ elseif (${WEBF_JS_ENGINE} MATCHES "v8") list(APPEND BRIDGE_SOURCE # Binding files bindings/v8/atomic_string.cc + bindings/v8/native_string_utils.cc + + out/built_in_string.cc ) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -653,20 +656,20 @@ elseif (${WEBF_JS_ENGINE} MATCHES "v8") list(APPEND BRIDGE_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/third_party/v8/include/v8/libplatform) endif() -list(APPEND BRIDGE_LINK_LIBS modb) +#list(APPEND BRIDGE_LINK_LIBS modb) list(APPEND BRIDGE_SOURCE # Core sources - webf_bridge.cc - core/api/api.cc - core/executing_context.cc - core/script_forbidden_scope.cc - core/script_state.cc - core/page.cc - core/dart_methods.cc - core/dart_isolate_context.cc - core/dart_context_data.cc - core/executing_context_data.cc +# webf_bridge.cc +# core/api/api.cc +# core/executing_context.cc +# core/script_forbidden_scope.cc +# core/script_state.cc +# core/page.cc +# core/dart_methods.cc +# core/dart_isolate_context.cc +# core/dart_context_data.cc +# core/executing_context_data.cc # core/fileapi/blob.cc # core/fileapi/blob_part.cc # core/fileapi/blob_property_bag.cc diff --git a/bridge/bindings/qjs/native_string_utils.h b/bridge/bindings/qjs/native_string_utils.h index 1760d20f43..b657909532 100644 --- a/bridge/bindings/qjs/native_string_utils.h +++ b/bridge/bindings/qjs/native_string_utils.h @@ -18,10 +18,8 @@ namespace webf { -#if WEBF_QUICKJS_JS_ENGINE // Convert to string and return a full copy of NativeString from JSValue. std::unique_ptr jsValueToNativeString(JSContext* ctx, JSValue value); -#endif // Encode utf-8 to utf-16, and return a full copy of NativeString. std::unique_ptr stringToNativeString(const std::string& string); diff --git a/bridge/bindings/v8/atomic_string.cc b/bridge/bindings/v8/atomic_string.cc index 8f6c18e8f4..cd3fb75a20 100644 --- a/bridge/bindings/v8/atomic_string.cc +++ b/bridge/bindings/v8/atomic_string.cc @@ -8,6 +8,7 @@ #include #include "built_in_string.h" #include "foundation/native_string.h" +#include "native_string_utils.h" namespace webf { @@ -92,23 +93,25 @@ class AtomicStringTwoByteResource : public v8::String::ExternalStringResource { }; AtomicString::AtomicString(v8::Isolate* isolate, const std::string& string) - : kind_(GetStringKind(string, string.size())) { + : kind_(GetStringKind(string, string.size())), isolate_(isolate) { auto* external_string_resource = new AtomicStringOneByteResource(string); string_ = v8::String::NewExternalOneByte(isolate, external_string_resource).ToLocalChecked(); } -AtomicString::AtomicString(v8::Isolate* isolate, const char* str, size_t length) : kind_(GetStringKind(str, length)) { +AtomicString::AtomicString(v8::Isolate* isolate, const char* str, size_t length) + : kind_(GetStringKind(str, length)), isolate_(isolate) { auto* external_string_resource = new AtomicStringOneByteResource(std::string(str, length)); string_ = v8::String::NewExternalOneByte(isolate, external_string_resource).ToLocalChecked(); } -AtomicString::AtomicString(v8::Isolate* isolate, std::unique_ptr&& native_string) { - auto* external_resource = new AtomicStringTwoByteResource(std::move(native_string)); +AtomicString::AtomicString(v8::Isolate* isolate, std::unique_ptr&& native_string) + : isolate_(isolate) { kind_ = GetStringKind(native_string.get()); + auto* external_resource = new AtomicStringTwoByteResource(std::move(native_string)); string_ = v8::String::NewExternalTwoByte(isolate, external_resource).ToLocalChecked(); } -AtomicString::AtomicString(v8::Isolate* isolate, const uint16_t* str, size_t length) { +AtomicString::AtomicString(v8::Isolate* isolate, const uint16_t* str, size_t length) : isolate_(isolate) { auto native_string = std::unique_ptr( reinterpret_cast(new SharedNativeString(str, length))); kind_ = GetStringKind(native_string.get()); @@ -116,6 +119,15 @@ AtomicString::AtomicString(v8::Isolate* isolate, const uint16_t* str, size_t len string_ = v8::String::NewExternalTwoByte(isolate, external_resource).ToLocalChecked(); } +AtomicString::AtomicString(v8::Local context, v8::Local v8_value) { + auto&& raw_native_string = jsValueToNativeString(context, v8_value); + auto native_string = + std::unique_ptr(reinterpret_cast(raw_native_string.release())); + kind_ = GetStringKind(native_string.get()); + auto* external_resource = new AtomicStringTwoByteResource(std::move(native_string)); + string_ = v8::String::NewExternalTwoByte(context->GetIsolate(), external_resource).ToLocalChecked(); +} + bool AtomicString::IsEmpty() const { return *this == built_in_string::kempty_string || IsNull(); } @@ -158,7 +170,7 @@ std::string AtomicString::ToStdString(v8::Isolate* isolate) const { size_t length = string_->Utf8Length(isolate); result.reserve(length); - string_->WriteUtf8(isolate, result.data(), length); + string_->WriteUtf8(isolate, result.data()); return result; } @@ -176,9 +188,8 @@ std::unique_ptr AtomicString::ToNativeString(v8::Isolate* is size_t length = string_->Length(); std::vector buffer; buffer.reserve(length); - - string_->Write(isolate, buffer.data(), length); - return SharedNativeString::FromTemporaryString(buffer.data(), buffer.size()); + string_->Write(isolate, buffer.data()); + return SharedNativeString::FromTemporaryString(buffer.data(), length); } StringView AtomicString::ToStringView() const { @@ -290,8 +301,9 @@ AtomicString AtomicString::RemoveCharacters(v8::Isolate* isolate, CharacterMatch AtomicString::AtomicString(const webf::AtomicString& value) { string_ = v8::Local(value.string_); } -AtomicString& AtomicString::operator=(const webf::AtomicString& other) { - string_ = v8::Local(other.string_); +AtomicString& AtomicString::operator=(const webf::AtomicString& other) noexcept { + string_ = other.string_; + return *this; } AtomicString::AtomicString(webf::AtomicString&& value) noexcept { @@ -299,6 +311,7 @@ AtomicString::AtomicString(webf::AtomicString&& value) noexcept { } AtomicString& AtomicString::operator=(webf::AtomicString&& value) noexcept { string_ = v8::Local(value.string_); + return *this; } } // namespace webf diff --git a/bridge/bindings/v8/atomic_string.h b/bridge/bindings/v8/atomic_string.h index 04a37462e3..872ab08dc1 100644 --- a/bridge/bindings/v8/atomic_string.h +++ b/bridge/bindings/v8/atomic_string.h @@ -40,6 +40,7 @@ class AtomicString { AtomicString(v8::Isolate* isolate, const char* str, size_t length); AtomicString(v8::Isolate* isolate, std::unique_ptr&& native_string); AtomicString(v8::Isolate* isolate, const uint16_t* str, size_t length); + AtomicString(v8::Local context, v8::Local v8_value); // Return the undefined string value from atom key. v8::Local ToV8(v8::Isolate* isolate) const { return string_.As(); } @@ -72,7 +73,7 @@ class AtomicString { // Copy assignment AtomicString(AtomicString const& value); - AtomicString& operator=(const AtomicString& other); + AtomicString& operator=(const AtomicString& other) noexcept; // Move assignment AtomicString(AtomicString&& value) noexcept; @@ -83,6 +84,7 @@ class AtomicString { protected: StringKind kind_; + v8::Isolate* isolate_; v8::Local string_; mutable v8::Local string_upper_; mutable v8::Local string_lower_; diff --git a/bridge/bindings/v8/atomic_string_test.cc b/bridge/bindings/v8/atomic_string_test.cc index fda1cbf8c9..59e21d49b6 100644 --- a/bridge/bindings/v8/atomic_string_test.cc +++ b/bridge/bindings/v8/atomic_string_test.cc @@ -4,113 +4,145 @@ */ #include "atomic_string.h" +#include #include +#include "bindings/v8/native_string_utils.h" #include "built_in_string.h" #include "event_type_names.h" #include "gtest/gtest.h" +#include "libplatform.h" using namespace webf; -//using TestCallback = void (*)(JSContext* ctx); -// -//void TestAtomicString(TestCallback callback) { -// JSRuntime* runtime = JS_NewRuntime(); -// JSContext* ctx = JS_NewContext(runtime); -// -// built_in_string::Init(ctx); -// -// callback(ctx); -// -// JS_FreeContext(ctx); -// -// built_in_string::Dispose(); -// JS_FreeRuntime(runtime); -//} +using TestCallback = void (*)(v8::Local ctx); + +bool v8_platform_inited = false; +std::unique_ptr platform; + +void TestAtomicString(TestCallback callback) { + if (!v8_platform_inited) { + // Initialize V8. + v8::V8::InitializeICUDefaultLocation(nullptr); + v8::V8::InitializeExternalStartupData(nullptr); + platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(platform.get()); + v8::V8::Initialize(); + v8_platform_inited = true; + } + + // Create a new Isolate and make it the current one. + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + v8::Isolate* isolate = v8::Isolate::New(create_params); + + { + v8::Isolate::Scope isolate_scope(isolate); + // Create a stack-allocated handle scope. + v8::HandleScope handle_scope(isolate); + + // Create a new context. + v8::Local context = v8::Context::New(isolate); + + // Enter the context for compiling and running the hello world script. + v8::Context::Scope context_scope(context); + + built_in_string::Init(isolate); + + callback(context); + } + + built_in_string::Dispose(); + + // Dispose the isolate and tear down V8. + isolate->Dispose(); +} TEST(AtomicString, Empty) { -// TestAtomicString([](JSContext* ctx) { -// AtomicString atomic_string = AtomicString::Empty(); -// EXPECT_STREQ(atomic_string.ToStdString(ctx).c_str(), ""); -// }); + TestAtomicString([](v8::Local ctx) { + AtomicString atomic_string = AtomicString::Empty(); + EXPECT_STREQ(atomic_string.ToStdString(ctx->GetIsolate()).c_str(), ""); + }); } TEST(AtomicString, FromNativeString) { -// TestAtomicString([](JSContext* ctx) { -// auto nativeString = stringToNativeString("helloworld"); -// AtomicString value = AtomicString( -// ctx, std::unique_ptr(static_cast(nativeString.release()))); -// -// EXPECT_STREQ(value.ToStdString(ctx).c_str(), "helloworld"); -// }); + TestAtomicString([](v8::Local ctx) { + auto nativeString = stringToNativeString("helloworld"); + AtomicString value = + AtomicString(ctx->GetIsolate(), + std::unique_ptr(static_cast(nativeString.release()))); + + EXPECT_STREQ(value.ToStdString(ctx->GetIsolate()).c_str(), "helloworld"); + }); +} + +TEST(AtomicString, CreateFromStdString) { + TestAtomicString([](v8::Local ctx) { + AtomicString&& value = AtomicString(ctx->GetIsolate(), "helloworld"); + EXPECT_STREQ(value.ToStdString(ctx->GetIsolate()).c_str(), "helloworld"); + }); +} + +TEST(AtomicString, CreateFromJSValue) { + TestAtomicString([](v8::Local ctx) { + v8::Local string = v8::String::NewFromUtf8(ctx->GetIsolate(), "helloworld").ToLocalChecked(); + AtomicString&& value = AtomicString(ctx, string); + EXPECT_STREQ(value.ToStdString(ctx->GetIsolate()).c_str(), "helloworld"); + }); +} + +TEST(AtomicString, ToV8) { + TestAtomicString([](v8::Local ctx) { + AtomicString&& value = AtomicString(ctx->GetIsolate(), "helloworld"); + v8::Local v8_string_value = value.ToV8(ctx->GetIsolate())->ToString(ctx).ToLocalChecked(); + size_t utf_len = v8_string_value->Utf8Length(ctx->GetIsolate()); + char* str_buffer = new char[utf_len]; + v8_string_value->WriteUtf8(ctx->GetIsolate(), str_buffer); + + EXPECT_STREQ(str_buffer, "helloworld"); + }); +} + +TEST(AtomicString, ToNativeString) { + TestAtomicString([](v8::Local ctx) { + AtomicString&& value = AtomicString(ctx->GetIsolate(), "helloworld"); + auto native_string = value.ToNativeString(ctx->GetIsolate()); + const uint16_t* p = native_string->string(); + EXPECT_EQ(native_string->length(), 10); + + uint16_t result[10] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}; + for (int i = 0; i < native_string->length(); i++) { + EXPECT_EQ(result[i], p[i]); + } + }); +} + +TEST(AtomicString, CopyAssignment) { + TestAtomicString([](v8::Local ctx) { + AtomicString str = AtomicString(ctx->GetIsolate(), "helloworld"); + struct P { + AtomicString str; + }; + P p{AtomicString::Empty()}; + v8::Local v = str.ToV8(ctx->GetIsolate()); + p.str = str; + EXPECT_EQ(p.str == str, true); + }); +} + +TEST(AtomicString, MoveAssignment) { + TestAtomicString([](v8::Local ctx) { + auto&& str = AtomicString(ctx->GetIsolate(), "helloworld"); + auto&& str2 = AtomicString(std::move(str)); + EXPECT_STREQ(str2.ToStdString(ctx->GetIsolate()).c_str(), "helloworld"); + }); +} + +TEST(AtomicString, CopyToRightReference) { + TestAtomicString([](v8::Local ctx) { + AtomicString str = AtomicString::Empty(); + if (1 + 1 == 2) { + str = AtomicString(ctx->GetIsolate(), "helloworld"); + } + EXPECT_STREQ(str.ToStdString(ctx->GetIsolate()).c_str(), "helloworld"); + }); } -// -//TEST(AtomicString, CreateFromStdString) { -// TestAtomicString([](JSContext* ctx) { -// AtomicString&& value = AtomicString(ctx, "helloworld"); -// EXPECT_STREQ(value.ToStdString(ctx).c_str(), "helloworld"); -// }); -//} -// -//TEST(AtomicString, CreateFromJSValue) { -// TestAtomicString([](JSContext* ctx) { -// JSValue string = JS_NewString(ctx, "helloworld"); -// AtomicString&& value = AtomicString(ctx, string); -// EXPECT_STREQ(value.ToStdString(ctx).c_str(), "helloworld"); -// JS_FreeValue(ctx, string); -// }); -//} -// -//TEST(AtomicString, ToQuickJS) { -// TestAtomicString([](JSContext* ctx) { -// AtomicString&& value = AtomicString(ctx, "helloworld"); -// JSValue qjs_value = value.ToQuickJS(ctx); -// const char* buffer = JS_ToCString(ctx, qjs_value); -// EXPECT_STREQ(buffer, "helloworld"); -// JS_FreeValue(ctx, qjs_value); -// JS_FreeCString(ctx, buffer); -// }); -//} -// -//TEST(AtomicString, ToNativeString) { -// TestAtomicString([](JSContext* ctx) { -// AtomicString&& value = AtomicString(ctx, "helloworld"); -// auto native_string = value.ToNativeString(ctx); -// const uint16_t* p = native_string->string(); -// EXPECT_EQ(native_string->length(), 10); -// -// uint16_t result[10] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}; -// for (int i = 0; i < native_string->length(); i++) { -// EXPECT_EQ(result[i], p[i]); -// } -// }); -//} -// -//TEST(AtomicString, CopyAssignment) { -// TestAtomicString([](JSContext* ctx) { -// AtomicString str = AtomicString(ctx, "helloworld"); -// struct P { -// AtomicString str; -// }; -// P p{AtomicString::Empty()}; -// p.str = str; -// EXPECT_EQ(p.str == str, true); -// }); -//} -// -//TEST(AtomicString, MoveAssignment) { -// TestAtomicString([](JSContext* ctx) { -// auto&& str = AtomicString(ctx, "helloworld"); -// auto&& str2 = AtomicString(std::move(str)); -// EXPECT_STREQ(str2.ToStdString(ctx).c_str(), "helloworld"); -// }); -//} -// -//TEST(AtomicString, CopyToRightReference) { -// TestAtomicString([](JSContext* ctx) { -// AtomicString str = AtomicString::Empty(); -// if (1 + 1 == 2) { -// str = AtomicString(ctx, "helloworld"); -// } -// EXPECT_STREQ(str.ToStdString(ctx).c_str(), "helloworld"); -// }); -//} diff --git a/bridge/bindings/v8/native_string_utils.cc b/bridge/bindings/v8/native_string_utils.cc new file mode 100644 index 0000000000..0443cfe8cb --- /dev/null +++ b/bridge/bindings/v8/native_string_utils.cc @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "native_string_utils.h" + +namespace webf { + +std::unique_ptr jsValueToNativeString(v8::Local ctx, v8::Local value) { + v8::Local string_value; + if (value->IsNull()) { + value = v8::String::NewFromUtf8(ctx->GetIsolate(), "").ToLocalChecked().As(); + } else if (!value->IsString()) { + value = value->ToString(ctx).ToLocalChecked(); + } else { + string_value = value.As(); + } + + uint32_t length = string_value->Length(); + auto* buffer = (uint16_t*) malloc(sizeof(uint16_t) * length); + + string_value->Write(ctx->GetIsolate(), buffer); + std::unique_ptr ptr = std::make_unique(buffer, length); + + return ptr; +} + +std::unique_ptr stringToNativeString(const std::string& string) { + std::u16string utf16; + fromUTF8(string, utf16); + SharedNativeString tmp{reinterpret_cast(utf16.c_str()), static_cast(utf16.size())}; + return SharedNativeString::FromTemporaryString(tmp.string(), tmp.length()); +} + +std::string nativeStringToStdString(const SharedNativeString* native_string) { + std::u16string u16EventType = + std::u16string(reinterpret_cast(native_string->string()), native_string->length()); + return toUTF8(u16EventType); +} + + +} // namespace webf diff --git a/bridge/bindings/v8/native_string_utils.h b/bridge/bindings/v8/native_string_utils.h new file mode 100644 index 0000000000..c6ac84bd24 --- /dev/null +++ b/bridge/bindings/v8/native_string_utils.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef BRIDGE_NATIVE_STRING_UTILS_H +#define BRIDGE_NATIVE_STRING_UTILS_H + +#include +#include +#include +#include +#include + +#include "foundation/native_string.h" + +namespace webf { + +// Convert to string and return a full copy of NativeString from JSValue. +std::unique_ptr jsValueToNativeString(v8::Local ctx, v8::Local value); + +// Encode utf-8 to utf-16, and return a full copy of NativeString. +std::unique_ptr stringToNativeString(const std::string& string); + +std::string nativeStringToStdString(const SharedNativeString* native_string); + +template +std::string toUTF8(const std::basic_string, std::allocator>& source) { + std::string result; + + std::wstring_convert, T> convertor; + result = convertor.to_bytes(source); + + return result; +} + +template +void fromUTF8(const std::string& source, std::basic_string, std::allocator>& result) { + std::wstring_convert, T> convertor; + result = convertor.from_bytes(source); +} + +} // namespace webf + +#endif // BRIDGE_NATIVE_STRING_UTILS_H diff --git a/bridge/test/test.cmake b/bridge/test/test.cmake index 61fe03bb2d..eb8cef8bc8 100644 --- a/bridge/test/test.cmake +++ b/bridge/test/test.cmake @@ -1,7 +1,6 @@ list(APPEND WEBF_TEST_SOURCE include/webf_bridge_test.h - webf_bridge_test.cc - polyfill/dist/testframework.cc +# polyfill/dist/testframework.cc ) set(gtest_disable_pthreads ON) @@ -15,6 +14,7 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") list(APPEND WEBF_TEST_SOURCE test/webf_test_context_qjs.cc test/webf_test_context_qjs.h + webf_bridge_test.cc ) list(APPEND WEBF_UNIT_TEST_SOURCEURCE @@ -43,13 +43,13 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") elseif ($ENV{WEBF_JS_ENGINE} MATCHES "v8") list(APPEND WEBF_TEST_SOURCE - test/webf_test_context_v8.cc - test/webf_test_context_v8.h +# test/webf_test_context_v8.cc +# test/webf_test_context_v8.h ) list(APPEND WEBF_UNIT_TEST_SOURCEURCE - ./test/webf_test_env.cc - ./test/webf_test_env.h +# ./test/webf_test_env.cc +# ./test/webf_test_env.h ./bindings/v8/atomic_string_test.cc ) endif () @@ -113,17 +113,17 @@ target_compile_definitions(webf_benchmark PUBLIC -DFLUTTER_BACKEND=0) target_compile_definitions(webf_benchmark PUBLIC -DUNIT_TEST=1) # Built libwebf_test.dylib library for integration test with flutter. -add_library(webf_test SHARED ${WEBF_TEST_SOURCE}) -target_link_libraries(webf_test PRIVATE ${BRIDGE_LINK_LIBS} webf) -target_include_directories(webf_test PRIVATE - ${BRIDGE_INCLUDE} - ./test - ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ./include) - -if (DEFINED ENV{LIBRARY_OUTPUT_DIR}) - set_target_properties(webf_test - PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" - RUNTIME_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" - ) -endif () +#add_library(webf_test SHARED ${WEBF_TEST_SOURCE}) +#target_link_libraries(webf_test PRIVATE ${BRIDGE_LINK_LIBS} webf) +#target_include_directories(webf_test PRIVATE +# ${BRIDGE_INCLUDE} +# ./test +# ${CMAKE_CURRENT_SOURCE_DIR} PUBLIC ./include) + +#if (DEFINED ENV{LIBRARY_OUTPUT_DIR}) +# set_target_properties(webf_test +# PROPERTIES +# LIBRARY_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" +# RUNTIME_OUTPUT_DIRECTORY "$ENV{LIBRARY_OUTPUT_DIR}" +# ) +#endif () From 21af6321de306c66d1bd94044a3e0073b05068af Mon Sep 17 00:00:00 2001 From: andycall Date: Fri, 19 Apr 2024 12:34:34 +0800 Subject: [PATCH 06/35] fix: fix build scripts. --- scripts/tasks.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/tasks.js b/scripts/tasks.js index bf8f42c40a..6078d4376c 100644 --- a/scripts/tasks.js +++ b/scripts/tasks.js @@ -123,12 +123,12 @@ task('build-darwin-webf-lib', done => { }); let webfTargets = ['webf']; - if (targetJSEngine === 'quickjs') { + if (targetJSEngine === 'quickjs' || targetJSEngine === 'v8') { webfTargets.push('webf_unit_test'); } - if (buildMode === 'Debug') { - webfTargets.push('webf_test'); - } + // if (buildMode === 'Debug') { + // webfTargets.push('webf_test'); + // } let cpus = os.cpus(); execSync(`cmake --build ${paths.bridge}/cmake-build-macos-x86_64 --target ${webfTargets.join(' ')} -- -j ${cpus.length}`, { From 89d1a7551d022645558c63b8458442c010a605f6 Mon Sep 17 00:00:00 2001 From: david <> Date: Sun, 21 Apr 2024 22:11:28 +0800 Subject: [PATCH 07/35] feat: add script_value --- bridge/CMakeLists.txt | 69 +- bridge/bindings/v8/atomic_string.cc | 6 +- bridge/bindings/v8/atomic_string.h | 10 +- bridge/bindings/v8/atomic_string_test.cc | 6 +- bridge/bindings/v8/base/allocator/allocator.h | 119 ++ .../src/partition_alloc/build_config.h | 12 + .../src/partition_alloc/buildflag.h | 18 + .../src/partition_alloc/flags.h | 93 ++ .../partition_alloc_base/check.cc | 91 ++ .../partition_alloc_base/check.h | 221 +++ .../partition_alloc_base/compiler_specific.h | 306 ++++ .../partition_alloc_base/component_export.h | 82 ++ .../cxx20_is_constant_evaluated.h | 36 + .../partition_alloc_base/immediate_crash.h | 165 +++ .../partition_alloc_base/log_message.cc | 6 + .../partition_alloc_base/log_message.h | 156 ++ .../partition_alloc_base/logging.cc | 117 ++ .../partition_alloc_base/logging.h | 399 ++++++ .../scoped_clear_last_error.h | 58 + .../strings/cstring_builder.cc | 216 +++ .../strings/cstring_builder.h | 64 + .../strings/safe_sprintf.cc | 712 ++++++++++ .../strings/safe_sprintf.h | 273 ++++ .../partition_alloc_base/thread_annotations.h | 235 ++++ .../partition_alloc_buildflags.h | 51 + .../partition_alloc/partition_alloc_forward.h | 85 ++ .../pointers/instance_tracer.cc | 6 + .../pointers/instance_tracer.h | 102 ++ .../src/partition_alloc/pointers/raw_ptr.h | 1253 +++++++++++++++++ .../pointers/raw_ptr_backup_ref_impl.cc | 6 + .../pointers/raw_ptr_backup_ref_impl.h | 514 +++++++ .../pointers/raw_ptr_exclusion.h | 41 + .../pointers/raw_ptr_noop_impl.h | 120 ++ .../src/partition_alloc/posix/eintr_wrapper.h | 62 + .../src/partition_alloc/tagging.cc | 7 + .../src/partition_alloc/tagging.h | 155 ++ .../bindings/v8/base/allocator/partitions.cc | 6 + .../bindings/v8/base/allocator/partitions.h | 104 ++ bridge/bindings/v8/base/bit_cast.h | 46 + bridge/bindings/v8/base/compiler_specific.h | 630 +++++++++ bridge/bindings/v8/base/memory/raw_ptr.h | 16 + .../v8/base/memory/raw_ptr_exclusion.h | 14 + .../bindings/v8/base/memory/scoped_refptr.h | 399 ++++++ .../bindings/v8/base/memory/stack_allocated.h | 60 + .../bindings/v8/base/numerics/checked_math.h | 378 +++++ .../v8/base/numerics/checked_math_impl.h | 567 ++++++++ .../v8/base/numerics/safe_conversions.h | 387 +++++ .../base/numerics/safe_conversions_arm_impl.h | 52 + .../v8/base/numerics/safe_conversions_impl.h | 839 +++++++++++ .../base/numerics/safe_math_clang_gcc_impl.h | 160 +++ .../v8/base/numerics/safe_math_shared_impl.h | 206 +++ bridge/bindings/v8/dictionary_base.cc | 6 + bridge/bindings/v8/dictionary_base.h | 55 + bridge/bindings/v8/exception_state.cc | 51 + bridge/bindings/v8/exception_state.h | 42 + bridge/bindings/v8/exception_state_test.cc | 6 + bridge/bindings/v8/for_build/build_config.h | 357 +++++ bridge/bindings/v8/for_build/buildflag.h | 48 + bridge/bindings/v8/gin/gin_export.h | 30 + .../bindings/v8/gin/public/context_holder.h | 54 + bridge/bindings/v8/gin/public/gin_embedders.h | 25 + bridge/bindings/v8/gin/public/wrapper_info.h | 33 + bridge/bindings/v8/name_client.cc | 6 + bridge/bindings/v8/name_client.h | 60 + bridge/bindings/v8/native_string_utils.cc | 6 +- bridge/bindings/v8/native_string_utils.h | 6 +- .../v8/platform/heap/garbage_collected.h | 75 + .../v8/platform/heap/heap_buildflags.h | 13 + bridge/bindings/v8/platform/heap/member.h | 249 ++++ bridge/bindings/v8/platform/heap/persistent.h | 158 +++ .../v8/platform/heap/self_keep_alive.h | 72 + .../v8/platform/heap/thread_state_storage.cc | 52 + .../v8/platform/heap/thread_state_storage.h | 118 ++ .../bindings/v8/platform/heap/write_barrier.h | 101 ++ .../bindings/v8/platform/per_context_data.cc | 6 + .../bindings/v8/platform/per_context_data.h | 126 ++ bridge/bindings/v8/platform/platform_export.h | 48 + .../bindings/v8/platform/scoped_persistent.h | 78 + bridge/bindings/v8/platform/script_state.cc | 87 ++ bridge/bindings/v8/platform/script_state.h | 292 ++++ bridge/bindings/v8/platform/util/gc_plugin.h | 30 + .../bindings/v8/platform/wtf/hash_functions.h | 130 ++ .../wtf/hash_table_deleted_value_type.h | 16 + bridge/bindings/v8/platform/wtf/hash_traits.h | 596 ++++++++ .../bindings/v8/platform/wtf/type_traits.cc | 6 + bridge/bindings/v8/platform/wtf/type_traits.h | 202 +++ .../bindings/v8/platform/wtf/vector_traits.cc | 6 + .../bindings/v8/platform/wtf/vector_traits.h | 209 +++ bridge/bindings/v8/script_value.cc | 47 + bridge/bindings/v8/script_value.h | 163 +++ bridge/bindings/v8/script_value_test.cc | 80 ++ bridge/bindings/v8/script_wrappable.cc | 6 + bridge/bindings/v8/script_wrappable.h | 189 +++ .../bindings/v8/trace_wrapper_v8_reference.cc | 6 + .../bindings/v8/trace_wrapper_v8_reference.h | 68 + bridge/bindings/v8/union_base.cc | 6 + bridge/bindings/v8/union_base.h | 47 + .../bindings/v8/v8_interface_bridge_base.cc | 6 + bridge/bindings/v8/v8_interface_bridge_base.h | 115 ++ bridge/bindings/v8/wrapper_type_info.cc | 6 + bridge/bindings/v8/wrapper_type_info.h | 208 +++ bridge/test/test.cmake | 2 + 102 files changed, 14121 insertions(+), 19 deletions(-) create mode 100644 bridge/bindings/v8/base/allocator/allocator.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/flags.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/immediate_crash.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/scoped_clear_last_error.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_forward.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_noop_impl.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/posix/eintr_wrapper.h create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.cc create mode 100644 bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.h create mode 100644 bridge/bindings/v8/base/allocator/partitions.cc create mode 100644 bridge/bindings/v8/base/allocator/partitions.h create mode 100644 bridge/bindings/v8/base/bit_cast.h create mode 100644 bridge/bindings/v8/base/compiler_specific.h create mode 100644 bridge/bindings/v8/base/memory/raw_ptr.h create mode 100644 bridge/bindings/v8/base/memory/raw_ptr_exclusion.h create mode 100644 bridge/bindings/v8/base/memory/scoped_refptr.h create mode 100644 bridge/bindings/v8/base/memory/stack_allocated.h create mode 100644 bridge/bindings/v8/base/numerics/checked_math.h create mode 100644 bridge/bindings/v8/base/numerics/checked_math_impl.h create mode 100644 bridge/bindings/v8/base/numerics/safe_conversions.h create mode 100644 bridge/bindings/v8/base/numerics/safe_conversions_arm_impl.h create mode 100644 bridge/bindings/v8/base/numerics/safe_conversions_impl.h create mode 100644 bridge/bindings/v8/base/numerics/safe_math_clang_gcc_impl.h create mode 100644 bridge/bindings/v8/base/numerics/safe_math_shared_impl.h create mode 100644 bridge/bindings/v8/dictionary_base.cc create mode 100644 bridge/bindings/v8/dictionary_base.h create mode 100644 bridge/bindings/v8/exception_state.cc create mode 100644 bridge/bindings/v8/exception_state.h create mode 100644 bridge/bindings/v8/exception_state_test.cc create mode 100644 bridge/bindings/v8/for_build/build_config.h create mode 100644 bridge/bindings/v8/for_build/buildflag.h create mode 100644 bridge/bindings/v8/gin/gin_export.h create mode 100644 bridge/bindings/v8/gin/public/context_holder.h create mode 100644 bridge/bindings/v8/gin/public/gin_embedders.h create mode 100644 bridge/bindings/v8/gin/public/wrapper_info.h create mode 100644 bridge/bindings/v8/name_client.cc create mode 100644 bridge/bindings/v8/name_client.h create mode 100644 bridge/bindings/v8/platform/heap/garbage_collected.h create mode 100644 bridge/bindings/v8/platform/heap/heap_buildflags.h create mode 100644 bridge/bindings/v8/platform/heap/member.h create mode 100644 bridge/bindings/v8/platform/heap/persistent.h create mode 100644 bridge/bindings/v8/platform/heap/self_keep_alive.h create mode 100644 bridge/bindings/v8/platform/heap/thread_state_storage.cc create mode 100644 bridge/bindings/v8/platform/heap/thread_state_storage.h create mode 100644 bridge/bindings/v8/platform/heap/write_barrier.h create mode 100644 bridge/bindings/v8/platform/per_context_data.cc create mode 100644 bridge/bindings/v8/platform/per_context_data.h create mode 100644 bridge/bindings/v8/platform/platform_export.h create mode 100644 bridge/bindings/v8/platform/scoped_persistent.h create mode 100644 bridge/bindings/v8/platform/script_state.cc create mode 100644 bridge/bindings/v8/platform/script_state.h create mode 100644 bridge/bindings/v8/platform/util/gc_plugin.h create mode 100644 bridge/bindings/v8/platform/wtf/hash_functions.h create mode 100644 bridge/bindings/v8/platform/wtf/hash_table_deleted_value_type.h create mode 100644 bridge/bindings/v8/platform/wtf/hash_traits.h create mode 100644 bridge/bindings/v8/platform/wtf/type_traits.cc create mode 100644 bridge/bindings/v8/platform/wtf/type_traits.h create mode 100644 bridge/bindings/v8/platform/wtf/vector_traits.cc create mode 100644 bridge/bindings/v8/platform/wtf/vector_traits.h create mode 100644 bridge/bindings/v8/script_value.cc create mode 100644 bridge/bindings/v8/script_value.h create mode 100644 bridge/bindings/v8/script_value_test.cc create mode 100644 bridge/bindings/v8/script_wrappable.cc create mode 100644 bridge/bindings/v8/script_wrappable.h create mode 100644 bridge/bindings/v8/trace_wrapper_v8_reference.cc create mode 100644 bridge/bindings/v8/trace_wrapper_v8_reference.h create mode 100644 bridge/bindings/v8/union_base.cc create mode 100644 bridge/bindings/v8/union_base.h create mode 100644 bridge/bindings/v8/v8_interface_bridge_base.cc create mode 100644 bridge/bindings/v8/v8_interface_bridge_base.h create mode 100644 bridge/bindings/v8/wrapper_type_info.cc create mode 100644 bridge/bindings/v8/wrapper_type_info.h diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index de01a2530b..b37a69601f 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET 10.11) if(MSVC) set(CMAKE_CXX_STANDARD 20) else() - set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD 20) endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -639,7 +639,72 @@ elseif (${WEBF_JS_ENGINE} MATCHES "v8") # Binding files bindings/v8/atomic_string.cc bindings/v8/native_string_utils.cc - + bindings/v8/script_value.cc + bindings/v8/exception_state.cc + bindings/v8/script_wrappable.cc + bindings/v8/wrapper_type_info.cc + bindings/v8/v8_interface_bridge_base.cc + bindings/v8/platform/script_state.cc + bindings/v8/platform/scoped_persistent.h + bindings/v8/platform/per_context_data.cc + bindings/v8/platform/heap/garbage_collected.h + bindings/v8/platform/heap/member.h + bindings/v8/platform/heap/self_keep_alive.h + bindings/v8/platform/heap/persistent.h + bindings/v8/platform/heap/thread_state_storage.cc + bindings/v8/platform/heap/write_barrier.h + bindings/v8/platform/util/gc_plugin.h + bindings/v8/union_base.cc + bindings/v8/name_client.cc + bindings/v8/dictionary_base.cc + bindings/v8/trace_wrapper_v8_reference.cc + bindings/v8/for_build/build_config.h + bindings/v8/for_build/buildflag.h + bindings/v8/platform/platform_export.h + bindings/v8/platform/wtf/type_traits.cc + bindings/v8/platform/wtf/vector_traits.cc + bindings/v8/platform/wtf/hash_traits.h + bindings/v8/platform/wtf/hash_functions.h + bindings/v8/platform/wtf/hash_table_deleted_value_type.h + bindings/v8/base/allocator/allocator.h + bindings/v8/base/allocator/partitions.cc + bindings/v8/base/allocator/partitions.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/flags.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.cc + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_forward.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/immediate_crash.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.cc + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.cc + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.cc + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.cc + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.cc + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/posix/eintr_wrapper.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_noop_impl.h + bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.cc + bindings/v8/base/numerics/checked_math.h + bindings/v8/base/numerics/safe_conversions.h + bindings/v8/base/numerics/safe_conversions_impl.h + bindings/v8/base/numerics/safe_conversions_arm_impl.h + bindings/v8/base/numerics/safe_math_shared_impl.h + bindings/v8/base/numerics/safe_math_clang_gcc_impl.h + bindings/v8/base/memory/raw_ptr.h + bindings/v8/base/memory/raw_ptr_exclusion.h + bindings/v8/base/memory/scoped_refptr.h + bindings/v8/base/memory/stack_allocated.h + bindings/v8/base/compiler_specific.h + bindings/v8/base/bit_cast.h + bindings/v8/gin/public/context_holder.h + bindings/v8/gin/public/gin_embedders.h + bindings/v8/gin/public/wrapper_info.h + bindings/v8/gin/gin_export.h out/built_in_string.cc ) diff --git a/bridge/bindings/v8/atomic_string.cc b/bridge/bindings/v8/atomic_string.cc index cd3fb75a20..61a1f248de 100644 --- a/bridge/bindings/v8/atomic_string.cc +++ b/bridge/bindings/v8/atomic_string.cc @@ -1,7 +1,7 @@ /* - * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. - * Copyright (C) 2022-present The WebF authors. All rights reserved. - */ +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ #include "atomic_string.h" #include diff --git a/bridge/bindings/v8/atomic_string.h b/bridge/bindings/v8/atomic_string.h index 872ab08dc1..63564bea10 100644 --- a/bridge/bindings/v8/atomic_string.h +++ b/bridge/bindings/v8/atomic_string.h @@ -1,10 +1,10 @@ /* - * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. - * Copyright (C) 2022-present The WebF authors. All rights reserved. - */ +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ -#ifndef BRIDGE_BINDINGS_QJS_ATOMIC_STRING_H_ -#define BRIDGE_BINDINGS_QJS_ATOMIC_STRING_H_ +#ifndef BRIDGE_BINDINGS_V8_ATOMIC_STRING_H_ +#define BRIDGE_BINDINGS_V8_ATOMIC_STRING_H_ #include #include diff --git a/bridge/bindings/v8/atomic_string_test.cc b/bridge/bindings/v8/atomic_string_test.cc index 59e21d49b6..6d40fe49e3 100644 --- a/bridge/bindings/v8/atomic_string_test.cc +++ b/bridge/bindings/v8/atomic_string_test.cc @@ -1,7 +1,7 @@ /* - * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. - * Copyright (C) 2022-present The WebF authors. All rights reserved. - */ +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ #include "atomic_string.h" #include diff --git a/bridge/bindings/v8/base/allocator/allocator.h b/bridge/bindings/v8/base/allocator/allocator.h new file mode 100644 index 0000000000..c2c714918e --- /dev/null +++ b/bridge/bindings/v8/base/allocator/allocator.h @@ -0,0 +1,119 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_ALLOCATOR_H +#define WEBF_ALLOCATOR_H + +#include "bindings/v8/base/memory/stack_allocated.h" +#include "foundation/macros.h" +#include "bindings/v8/base/allocator/partitions.h" + +namespace webf { + +using base::NotNullTag; + +namespace internal { +// A dummy class used in following macros. +class __thisIsHereToForceASemicolonAfterThisMacro; +} // namespace internal + +// Classes that contain references to garbage-collected objects but aren't +// themselves garbaged allocated, have some extra macros available which +// allows their use to be restricted to cases where the garbage collector +// is able to discover their references. These macros will be useful for +// non-garbage-collected objects to avoid unintended allocations. +// +// STACK_ALLOCATED() classes may contain raw pointers to garbage-collected +// objects. +// +// DISALLOW_NEW(): Cannot be allocated with new operators but can be a +// part of object, a value object in collections or stack allocated. If it has +// Members you need a trace method and the containing object needs to call that +// trace method. +// +#define DISALLOW_NEW() \ + public: \ + using IsDisallowNewMarker [[maybe_unused]] = int; \ + void* operator new(size_t, webf::NotNullTag, void* location) { \ + return location; \ + } \ + void* operator new(size_t, void* location) { \ + return location; \ + } \ + \ + private: \ + void* operator new(size_t) = delete; \ + \ + public: \ + friend class ::webf::internal::__thisIsHereToForceASemicolonAfterThisMacro + +#define STATIC_ONLY(Type) \ + Type() = delete; \ + Type(const Type&) = delete; \ + Type& operator=(const Type&) = delete; \ + void* operator new(size_t) = delete; \ + void* operator new(size_t, webf::NotNullTag, void*) = delete; \ + void* operator new(size_t, void*) = delete + +// Provides customizable overrides of fastMalloc/fastFree and operator +// new/delete +// +// Provided functionality: +// Macro: USING_FAST_MALLOC +// +// Example usage: +// class Widget { +// USING_FAST_MALLOC(Widget) +// ... +// }; +// +// struct Data { +// USING_FAST_MALLOC(Data) +// public: +// ... +// }; +// + +#define USING_FAST_MALLOC(type) \ + public: \ + void* operator new(size_t, void* p) { \ + return p; \ + } \ + void* operator new[](size_t, void* p) { \ + return p; \ + } \ + \ + void* operator new(size_t size) { \ + return ::webf::Partitions::FastMalloc(size, nullptr); \ + } \ + \ + void operator delete(void* p) { \ + ::webf::Partitions::FastFree(p); \ + } \ + \ + void* operator new[](size_t size) { \ + return ::webf::Partitions::FastMalloc(size, nullptr); \ + } \ + \ + void operator delete[](void* p) { \ + ::webf::Partitions::FastFree(p); \ + } \ + void* operator new(size_t, webf::NotNullTag, void* location) { \ + assert_m(location, "location is nullptr"); \ + return location; \ + } \ + \ + private: \ + friend class ::webf::internal::__thisIsHereToForceASemicolonAfterThisMacro + +} // namespace webf + +// This version of placement new omits a 0 check. +inline void* operator new(size_t, webf::NotNullTag, void* location) { + assert_m(location, "location is nullptr"); + return location; +} + +#endif // WEBF_ALLOCATOR_H diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h new file mode 100644 index 0000000000..28a7f86d2a --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h @@ -0,0 +1,12 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_BUILD_CONFIG_H_ +#define PARTITION_ALLOC_BUILD_CONFIG_H_ + +// TODO(https://crbug.com/41481467): Remove //build dependency. +#include "bindings/v8/for_build/build_config.h" + +#endif // PARTITION_ALLOC_BUILD_CONFIG_H_ diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h new file mode 100644 index 0000000000..2ecedbd4ec --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h @@ -0,0 +1,18 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_BUILDFLAG_H_ +#define PARTITION_ALLOC_BUILDFLAG_H_ + +// This was copied from chromium's and adapted to partition_alloc. +// Please refer to chromium's //build/buildflag.h original comments. +// +// Using a different macro and internal define allows partition_alloc and +// chromium to cohabit without affecting each other. +#define PA_BUILDFLAG_CAT_INDIRECT(a, b) a##b +#define PA_BUILDFLAG_CAT(a, b) PA_BUILDFLAG_CAT_INDIRECT(a, b) +#define PA_BUILDFLAG(flag) (PA_BUILDFLAG_CAT(PA_BUILDFLAG_INTERNAL_, flag)()) + +#endif // PARTITION_ALLOC_BUILDFLAG_H_ diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/flags.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/flags.h new file mode 100644 index 0000000000..be5e4bd735 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/flags.h @@ -0,0 +1,93 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_FLAGS_H_ +#define PARTITION_ALLOC_FLAGS_H_ + +#include + +namespace partition_alloc::internal { +// Returns `T` if and only if `EnumType` is a scoped enum. +template +using IfEnum = std::enable_if_t< + std::is_enum_v && + !std::is_convertible_v>, + T>; + +// We assume `EnumType` defines `kMaxValue` which has the largest value and all +// powers of two are represented in `EnumType`. +template +constexpr inline EnumType kAllFlags = static_cast>( + (static_cast>(EnumType::kMaxValue) << 1) - + 1); + +template +constexpr inline IfEnum AreValidFlags(EnumType flags) { + const auto raw_flags = static_cast>(flags); + const auto raw_all_flags = + static_cast>(kAllFlags); + return (raw_flags & ~raw_all_flags) == 0; +} + +// Checks `subset` is a subset of `superset` or not. +template +constexpr inline IfEnum ContainsFlags(EnumType superset, + EnumType subset) { + return (superset & subset) == subset; +} + +// Removes flags `target` from `from`. +template +constexpr inline IfEnum RemoveFlags(EnumType from, EnumType target) { + return from & ~target; +} + +// A macro to define binary arithmetic over `EnumType`. +// Use inside `namespace partition_alloc::internal`. +#define PA_DEFINE_OPERATORS_FOR_FLAGS(EnumType) \ + [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator&( \ + const EnumType& lhs, const EnumType& rhs) { \ + return static_cast( \ + static_cast>(lhs) & \ + static_cast>(rhs)); \ + } \ + [[maybe_unused]] inline constexpr EnumType& operator&=( \ + EnumType& lhs, const EnumType& rhs) { \ + lhs = lhs & rhs; \ + return lhs; \ + } \ + [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator|( \ + const EnumType& lhs, const EnumType& rhs) { \ + return static_cast( \ + static_cast>(lhs) | \ + static_cast>(rhs)); \ + } \ + [[maybe_unused]] inline constexpr EnumType& operator|=( \ + EnumType& lhs, const EnumType& rhs) { \ + lhs = lhs | rhs; \ + return lhs; \ + } \ + [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator^( \ + const EnumType& lhs, const EnumType& rhs) { \ + return static_cast( \ + static_cast>(lhs) ^ \ + static_cast>(rhs)); \ + } \ + [[maybe_unused]] inline constexpr EnumType& operator^=( \ + EnumType& lhs, const EnumType& rhs) { \ + lhs = lhs ^ rhs; \ + return lhs; \ + } \ + [[maybe_unused]] [[nodiscard]] inline constexpr EnumType operator~( \ + const EnumType& val) { \ + return static_cast( \ + static_cast>(kAllFlags) & \ + ~static_cast>(val)); \ + } \ + static_assert(true) /* semicolon here */ + +} // namespace partition_alloc::internal + +#endif // PARTITION_ALLOC_FLAGS_H_ diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.cc new file mode 100644 index 0000000000..4e4cfbcab9 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.cc @@ -0,0 +1,91 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/immediate_crash.h" + +namespace partition_alloc::internal::logging { + +// TODO(crbug.com/40158212): Make CheckError not to allocate memory. So we can +// use CHECK() inside PartitionAllocator when PartitionAllocator-Everywhere is +// enabled. (Also need to modify LogMessage). + +CheckError::CheckError(const char* file, + int line, + LogSeverity severity, + const char* condition) + : log_message_(file, line, severity) { + log_message_.stream() << "Check failed: " << condition << ". "; +} + +CheckError::CheckError(const char* file, int line, LogSeverity severity) + : log_message_(file, line, severity) {} + +CheckError::CheckError(const char* file, + int line, + LogSeverity severity, + const char* condition, + SystemErrorCode err_code) + : errno_log_message_(file, line, severity, err_code), has_errno(true) { + errno_log_message_.stream() << "Check failed: " << condition << ". "; +} + +check_error::Check::Check(const char* file, int line, const char* condition) + : CheckError(file, line, LOGGING_FATAL, condition) {} + +check_error::DCheck::DCheck(const char* file, int line, const char* condition) + : CheckError(file, line, LOGGING_DCHECK, condition) {} + +check_error::PCheck::PCheck(const char* file, int line, const char* condition) + : CheckError(file, + line, + LOGGING_FATAL, + condition, + logging::GetLastSystemErrorCode()) {} + +check_error::PCheck::PCheck(const char* file, int line) + : PCheck(file, line, "") {} + +check_error::DPCheck::DPCheck(const char* file, int line, const char* condition) + : CheckError(file, + line, + LOGGING_DCHECK, + condition, + logging::GetLastSystemErrorCode()) {} + +check_error::NotImplemented::NotImplemented(const char* file, + int line, + const char* function) + : CheckError(file, line, LOGGING_ERROR) { + stream() << "Not implemented reached in " << function; +} + +base::strings::CStringBuilder& CheckError::stream() { + return !has_errno ? log_message_.stream() : errno_log_message_.stream(); +} + +CheckError::~CheckError() { + // Note: This function ends up in crash stack traces. If its full name + // changes, the crash server's magic signature logic needs to be updated. + // See cl/306632920. + if (!has_errno) { + log_message_.~LogMessage(); + } else { +#if BUILDFLAG(IS_WIN) + errno_log_message_.~Win32ErrorLogMessage(); +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) + errno_log_message_.~ErrnoLogMessage(); +#endif // BUILDFLAG(IS_WIN) + } +} + +void RawCheckFailure(const char* message) { + RawLog(LOGGING_FATAL, message); + PA_IMMEDIATE_CRASH(); +} + +} // namespace partition_alloc::internal::logging diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.h new file mode 100644 index 0000000000..3f2e022c17 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.h @@ -0,0 +1,221 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_ + +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h" + +#define PA_STRINGIFY_IMPL(s) #s +#define PA_STRINGIFY(s) PA_STRINGIFY_IMPL(s) + +// This header defines the CHECK, DCHECK, and DPCHECK macros. +// +// CHECK dies with a fatal error if its condition is not true. It is not +// controlled by NDEBUG, so the check will be executed regardless of compilation +// mode. +// +// DCHECK, the "debug mode" check, is enabled depending on NDEBUG and +// DCHECK_ALWAYS_ON, and its severity depends on DCHECK_IS_CONFIGURABLE. +// +// (D)PCHECK is like (D)CHECK, but includes the system error code (c.f. +// perror(3)). +// +// Additional information can be streamed to these macros and will be included +// in the log output if the condition doesn't hold (you may need to include +// ): +// +// CHECK(condition) << "Additional info."; +// +// The condition is evaluated exactly once. Even in build modes where e.g. +// DCHECK is disabled, the condition and any stream arguments are still +// referenced to avoid warnings about unused variables and functions. +// +// For the (D)CHECK_EQ, etc. macros, see base/check_op.h. However, that header +// is *significantly* larger than check.h, so try to avoid including it in +// header files. + +namespace partition_alloc::internal::logging { + +// Class used to explicitly ignore an ostream, and optionally a boolean value. +class VoidifyStream { + public: + VoidifyStream() = default; + explicit VoidifyStream(bool ignored) {} + + // This operator has lower precedence than << but higher than ?: + void operator&(base::strings::CStringBuilder&) {} +}; + +// Helper macro which avoids evaluating the arguments to a stream if the +// condition is false. +#define PA_LAZY_CHECK_STREAM(stream, condition) \ + !(condition) \ + ? (void)0 \ + : ::partition_alloc::internal::logging::VoidifyStream() & (stream) + +// Macro which uses but does not evaluate expr and any stream parameters. +#define PA_EAT_CHECK_STREAM_PARAMS(expr) \ + true ? (void)0 \ + : ::partition_alloc::internal::logging::VoidifyStream(expr) & \ + (*::partition_alloc::internal::logging::g_swallow_stream) +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +extern base::strings::CStringBuilder* g_swallow_stream; + +class LogMessage; + +// Class used for raising a check error upon destruction. +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) CheckError { + public: + // Stream for adding optional details to the error message. + base::strings::CStringBuilder& stream(); + PA_NOMERGE ~CheckError(); + + protected: + CheckError(const char* file, + int line, + LogSeverity severity, + const char* condition); + CheckError(const char* file, int line, LogSeverity severity); + CheckError(const char* file, + int line, + LogSeverity severity, + const char* condition, + SystemErrorCode err_code); + + union { + LogMessage log_message_; +#if BUILDFLAG(IS_WIN) + Win32ErrorLogMessage errno_log_message_; +#else + ErrnoLogMessage errno_log_message_; +#endif + }; + + // |has_errno| describes which union member is used, |log_message_| or + // |errno_log_message_|. If |has_errno| is true, CheckError initializes + // |errno_log_message_| at its constructor and destroys at its destructor. + // (This also means the CheckError is an instance of the parent class of + // PCheck or DPCheck.) + // If false, CheckError initializes and destroys |log_message_|. + const bool has_errno = false; +}; + +namespace check_error { + +// Class used for raising a check error upon destruction. +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) Check : public CheckError { + public: + Check(const char* file, int line, const char* condition); +}; + +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) DCheck : public CheckError { + public: + DCheck(const char* file, int line, const char* condition); +}; + +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) PCheck : public CheckError { + public: + PCheck(const char* file, int line, const char* condition); + PCheck(const char* file, int line); +}; + +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) DPCheck : public CheckError { + public: + DPCheck(const char* file, int line, const char* condition); +}; + +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) NotImplemented + : public CheckError { + public: + NotImplemented(const char* file, int line, const char* function); +}; + +} // namespace check_error + +#if defined(OFFICIAL_BUILD) && !defined(NDEBUG) +#error "Debug builds are not expected to be optimized as official builds." +#endif // defined(OFFICIAL_BUILD) && !defined(NDEBUG) + +#if defined(OFFICIAL_BUILD) && !PA_BUILDFLAG(PA_DCHECK_IS_ON) + +// Discard log strings to reduce code bloat. +// +// This is not calling BreakDebugger since this is called frequently, and +// calling an out-of-line function instead of a noreturn inline macro prevents +// compiler optimizations. +#define PA_BASE_CHECK(condition) \ + PA_UNLIKELY(!(condition)) ? PA_IMMEDIATE_CRASH() \ + : PA_EAT_CHECK_STREAM_PARAMS() + +#define PA_BASE_CHECK_WILL_STREAM() false + +#define PA_BASE_PCHECK(condition) \ + PA_LAZY_CHECK_STREAM( \ + ::partition_alloc::internal::logging::check_error::PCheck(__FILE__, \ + __LINE__) \ + .stream(), \ + PA_UNLIKELY(!(condition))) + +#else + +#define PA_BASE_CHECK(condition) \ + PA_LAZY_CHECK_STREAM( \ + ::partition_alloc::internal::logging::check_error::Check( \ + __FILE__, __LINE__, #condition) \ + .stream(), \ + !PA_ANALYZER_ASSUME_TRUE(condition)) + +#define PA_BASE_CHECK_WILL_STREAM() true + +#define PA_BASE_PCHECK(condition) \ + PA_LAZY_CHECK_STREAM( \ + ::partition_alloc::internal::logging::check_error::PCheck( \ + __FILE__, __LINE__, #condition) \ + .stream(), \ + !PA_ANALYZER_ASSUME_TRUE(condition)) + +#endif + +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) + +#define PA_BASE_DCHECK(condition) \ + PA_LAZY_CHECK_STREAM( \ + ::partition_alloc::internal::logging::check_error::DCheck( \ + __FILE__, __LINE__, #condition) \ + .stream(), \ + !PA_ANALYZER_ASSUME_TRUE(condition)) + +#define PA_BASE_DPCHECK(condition) \ + PA_LAZY_CHECK_STREAM( \ + ::partition_alloc::internal::logging::check_error::DPCheck( \ + __FILE__, __LINE__, #condition) \ + .stream(), \ + !PA_ANALYZER_ASSUME_TRUE(condition)) + +#else + +#define PA_BASE_DCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) +#define PA_BASE_DPCHECK(condition) PA_EAT_CHECK_STREAM_PARAMS(!(condition)) + +#endif + +// Async signal safe checking mechanism. +[[noreturn]] PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) void RawCheckFailure( + const char* message); +#define PA_RAW_CHECK(condition) \ + do { \ + if (!(condition)) \ + ::partition_alloc::internal::logging::RawCheckFailure( \ + "Check failed: " #condition "\n"); \ + } while (0) + +} // namespace partition_alloc::internal::logging + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_ diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h new file mode 100644 index 0000000000..592d316bdf --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h @@ -0,0 +1,306 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_ + +#include "bindings/v8/for_build/build_config.h" + +// A wrapper around `__has_cpp_attribute`. +#if defined(__has_cpp_attribute) +#define PA_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define PA_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +// A wrapper around `__has_attribute`, similar to PA_HAS_CPP_ATTRIBUTE. +#if defined(__has_attribute) +#define PA_HAS_ATTRIBUTE(x) __has_attribute(x) +#else +#define PA_HAS_ATTRIBUTE(x) 0 +#endif + +// A wrapper around `__has_builtin`, similar to PA_HAS_CPP_ATTRIBUTE. +#if defined(__has_builtin) +#define PA_HAS_BUILTIN(x) __has_builtin(x) +#else +#define PA_HAS_BUILTIN(x) 0 +#endif + +// Annotate a function indicating it should not be inlined. +// Use like: +// NOINLINE void DoStuff() { ... } +#if defined(__clang__) && PA_HAS_ATTRIBUTE(noinline) +#define PA_NOINLINE [[clang::noinline]] +#elif defined(COMPILER_GCC) && PA_HAS_ATTRIBUTE(noinline) +#define PA_NOINLINE __attribute__((noinline)) +#elif defined(COMPILER_MSVC) +#define PA_NOINLINE __declspec(noinline) +#else +#define PA_NOINLINE +#endif + +#if defined(__clang__) && defined(NDEBUG) && PA_HAS_ATTRIBUTE(always_inline) +#define PA_ALWAYS_INLINE [[clang::always_inline]] inline +#elif defined(COMPILER_GCC) && defined(NDEBUG) && \ + PA_HAS_ATTRIBUTE(always_inline) +#define PA_ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif defined(COMPILER_MSVC) && defined(NDEBUG) +#define PA_ALWAYS_INLINE __forceinline +#else +#define PA_ALWAYS_INLINE inline +#endif + +// Annotate a function indicating it should never be tail called. Useful to make +// sure callers of the annotated function are never omitted from call-stacks. +// To provide the complementary behavior (prevent the annotated function from +// being omitted) look at NOINLINE. Also note that this doesn't prevent code +// folding of multiple identical caller functions into a single signature. To +// prevent code folding, see NO_CODE_FOLDING() in base/debug/alias.h. +// Use like: +// void NOT_TAIL_CALLED FooBar(); +#if defined(__clang__) && PA_HAS_ATTRIBUTE(not_tail_called) +#define PA_NOT_TAIL_CALLED [[clang::not_tail_called]] +#else +#define PA_NOT_TAIL_CALLED +#endif + +// Specify memory alignment for structs, classes, etc. +// Use like: +// class PA_ALIGNAS(16) MyClass { ... } +// PA_ALIGNAS(16) int array[4]; +// +// In most places you can use the C++11 keyword "alignas", which is preferred. +// +// Historically, compilers had trouble mixing __attribute__((...)) syntax with +// alignas(...) syntax. However, at least Clang is very accepting nowadays. It +// may be that this macro can be removed entirely. +#if defined(__clang__) +#define PA_ALIGNAS(byte_alignment) alignas(byte_alignment) +#elif defined(COMPILER_MSVC) +#define PA_ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) +#elif defined(COMPILER_GCC) && PA_HAS_ATTRIBUTE(aligned) +#define PA_ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#endif + +// In case the compiler supports it PA_NO_UNIQUE_ADDRESS evaluates to the C++20 +// attribute [[no_unique_address]]. This allows annotating data members so that +// they need not have an address distinct from all other non-static data members +// of its class. +// +// References: +// * https://en.cppreference.com/w/cpp/language/attributes/no_unique_address +// * https://wg21.link/dcl.attr.nouniqueaddr +#if defined(COMPILER_MSVC) && PA_HAS_CPP_ATTRIBUTE(msvc::no_unique_address) +// Unfortunately MSVC ignores [[no_unique_address]] (see +// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#msvc-extensions-and-abi), +// and clang-cl matches it for ABI compatibility reasons. We need to prefer +// [[msvc::no_unique_address]] when available if we actually want any effect. +#define PA_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif PA_HAS_CPP_ATTRIBUTE(no_unique_address) +#define PA_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define PA_NO_UNIQUE_ADDRESS +#endif + +// Tells 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.) +// For member functions, the implicit this parameter counts as index 1. +#if (defined(COMPILER_GCC) || defined(__clang__)) && PA_HAS_ATTRIBUTE(format) +#define PA_PRINTF_FORMAT(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define PA_PRINTF_FORMAT(format_param, dots_param) +#endif + +// Sanitizers annotations. +#if PA_HAS_ATTRIBUTE(no_sanitize) +#define PA_NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#if !defined(PA_NO_SANITIZE) +#define PA_NO_SANITIZE(what) +#endif + +// MemorySanitizer annotations. +#if defined(MEMORY_SANITIZER) +#include + +// Mark a memory region fully initialized. +// Use this to annotate code that deliberately reads uninitialized data, for +// example a GC scavenging root set pointers from the stack. +#define PA_MSAN_UNPOISON(p, size) __msan_unpoison(p, size) +#else // MEMORY_SANITIZER +#define PA_MSAN_UNPOISON(p, size) +#endif // MEMORY_SANITIZER + +// Macro for hinting that an expression is likely to be false. +#if !defined(PA_UNLIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define PA_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define PA_UNLIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(PA_UNLIKELY) + +#if !defined(PA_LIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define PA_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define PA_LIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(PA_LIKELY) + +// Compiler feature-detection. +// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension +#if defined(__has_feature) +#define PA_HAS_FEATURE(FEATURE) __has_feature(FEATURE) +#else +#define PA_HAS_FEATURE(FEATURE) 0 +#endif + +#if !defined(PA_CPU_ARM_NEON) +#if defined(__arm__) +#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \ + !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID) +#error Chromium does not support middle endian architecture +#endif +#if defined(__ARM_NEON__) +#define PA_CPU_ARM_NEON 1 +#endif +#endif // defined(__arm__) +#endif // !defined(CPU_ARM_NEON) + +#if !defined(PA_HAVE_MIPS_MSA_INTRINSICS) +#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) +#define PA_HAVE_MIPS_MSA_INTRINSICS 1 +#endif +#endif + +// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints +// to Clang which control what code paths are statically analyzed, +// and is meant to be used in conjunction with assert & assert-like functions. +// The expression is passed straight through if analysis isn't enabled. +// +// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current +// codepath and any other branching codepaths that might follow. +#if defined(__clang_analyzer__) + +namespace partition_alloc::internal { + +inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) { + return false; +} + +inline constexpr bool AnalyzerAssumeTrue(bool arg) { + // PartitionAllocAnalyzerNoReturn() is invoked and analysis is terminated if + // |arg| is false. + return arg || AnalyzerNoReturn(); +} + +} // namespace partition_alloc::internal + +#define PA_ANALYZER_ASSUME_TRUE(arg) \ + ::partition_alloc::internal::AnalyzerAssumeTrue(!!(arg)) +#define PA_ANALYZER_SKIP_THIS_PATH() \ + static_cast(::partition_alloc::internal::AnalyzerNoReturn()) + +#else // !defined(__clang_analyzer__) + +#define PA_ANALYZER_ASSUME_TRUE(arg) (arg) +#define PA_ANALYZER_SKIP_THIS_PATH() + +#endif // defined(__clang_analyzer__) + +// Use nomerge attribute to disable optimization of merging multiple same calls. +#if defined(__clang__) && PA_HAS_ATTRIBUTE(nomerge) +#define PA_NOMERGE [[clang::nomerge]] +#else +#define PA_NOMERGE +#endif + +// Marks a type as being eligible for the "trivial" ABI despite having a +// non-trivial destructor or copy/move constructor. Such types can be relocated +// after construction by simply copying their memory, which makes them eligible +// to be passed in registers. The canonical example is std::unique_ptr. +// +// Use with caution; this has some subtle effects on constructor/destructor +// ordering and will be very incorrect if the type relies on its address +// remaining constant. When used as a function argument (by value), the value +// may be constructed in the caller's stack frame, passed in a register, and +// then used and destructed in the callee's stack frame. A similar thing can +// occur when values are returned. +// +// TRIVIAL_ABI is not needed for types which have a trivial destructor and +// copy/move constructors, such as base::TimeTicks and other POD. +// +// It is also not likely to be effective on types too large to be passed in one +// or two registers on typical target ABIs. +// +// See also: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html +#if defined(__clang__) && PA_HAS_ATTRIBUTE(trivial_abi) +#define PA_TRIVIAL_ABI [[clang::trivial_abi]] +#else +#define PA_TRIVIAL_ABI +#endif + +// Requires constant initialization. See constinit in C++20. Allows to rely on a +// variable being initialized before execution, and not requiring a global +// constructor. +#if PA_HAS_ATTRIBUTE(require_constant_initialization) +#define PA_CONSTINIT __attribute__((require_constant_initialization)) +#endif +#if !defined(PA_CONSTINIT) +#define PA_CONSTINIT +#endif + +#if defined(__clang__) +#define PA_GSL_POINTER [[gsl::Pointer]] +#else +#define PA_GSL_POINTER +#endif + +// Constexpr destructors were introduced in C++20. PartitionAlloc's minimum +// supported C++ version is C++17. +#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L +#define PA_CONSTEXPR_DTOR constexpr +#else +#define PA_CONSTEXPR_DTOR +#endif + +// PA_LIFETIME_BOUND indicates that a resource owned by a function parameter or +// implicit object parameter is retained by the return value of the annotated +// function (or, for a parameter of a constructor, in the value of the +// constructed object). This attribute causes warnings to be produced if a +// temporary object does not live long enough. +// +// When applied to a reference parameter, the referenced object is assumed to be +// retained by the return value of the function. When applied to a non-reference +// parameter (for example, a pointer or a class type), all temporaries +// referenced by the parameter are assumed to be retained by the return value of +// the function. +// +// See also the upstream documentation: +// https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +// +// This attribute is based on `ABSL_ATTRIBUTE_LIFETIME_BOUND`, but: +// * A separate definition is provided to avoid PartitionAlloc => Abseil +// dependency +// * The definition is tweaked to avoid `__attribute__(lifetime))` because it +// can't be applied in the same places as `[[clang::lifetimebound]]`. In +// particular `operator T*&() && __attribute__(lifetime))` fails to compile on +// `clang` with the following error: 'lifetimebound' attribute only applies to +// parameters and implicit object parameters +#if PA_HAS_CPP_ATTRIBUTE(clang::lifetimebound) +#define PA_LIFETIME_BOUND [[clang::lifetimebound]] +#else +#define PA_LIFETIME_BOUND +#endif + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPILER_SPECIFIC_H_ diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h new file mode 100644 index 0000000000..1dab395fba --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h @@ -0,0 +1,82 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPONENT_EXPORT_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPONENT_EXPORT_H_ + +// Used to annotate symbols which are exported by the component named +// |component|. Note that this only does the right thing if the corresponding +// component target's sources are compiled with |IS_$component_IMPL| defined +// as 1. For example: +// +// class PA_COMPONENT_EXPORT(FOO) Bar {}; +// +// If IS_FOO_IMPL=1 at compile time, then Bar will be annotated using the +// PA_COMPONENT_EXPORT_ANNOTATION macro defined below. Otherwise it will be +// annotated using the PA_COMPONENT_IMPORT_ANNOTATION macro. +#define PA_COMPONENT_EXPORT(component) \ + PA_COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL, \ + PA_COMPONENT_EXPORT_ANNOTATION, \ + PA_COMPONENT_IMPORT_ANNOTATION) + +// Indicates whether the current compilation unit is being compiled as part of +// the implementation of the component named |component|. Expands to |1| if +// |IS_$component_IMPL| is defined as |1|; expands to |0| otherwise. +// +// Note in particular that if |IS_$component_IMPL| is not defined at all, it is +// still fine to test PA_INSIDE_COMPONENT_IMPL(component), which expands to |0| +// as expected. +#define PA_INSIDE_COMPONENT_IMPL(component) \ + PA_COMPONENT_MACRO_CONDITIONAL_(IS_##component##_IMPL, 1, 0) + +// Compiler-specific macros to annotate for export or import of a symbol. No-op +// in non-component builds. These should not see much if any direct use. +// Instead use the PA_COMPONENT_EXPORT macro defined above. +#if defined(COMPONENT_BUILD) +#if defined(WIN32) +#define PA_COMPONENT_EXPORT_ANNOTATION __declspec(dllexport) +#define PA_COMPONENT_IMPORT_ANNOTATION __declspec(dllimport) +#else // defined(WIN32) +#define PA_COMPONENT_EXPORT_ANNOTATION __attribute__((visibility("default"))) +#define PA_COMPONENT_IMPORT_ANNOTATION +#endif // defined(WIN32) +#else // defined(COMPONENT_BUILD) +#define PA_COMPONENT_EXPORT_ANNOTATION +#define PA_COMPONENT_IMPORT_ANNOTATION +#endif // defined(COMPONENT_BUILD) + +// Below this point are several internal utility macros used for the +// implementation of the above macros. Not intended for external use. + +// Helper for conditional expansion to one of two token strings. If |condition| +// expands to |1| then this macro expands to |consequent|; otherwise it expands +// to |alternate|. +#define PA_COMPONENT_MACRO_CONDITIONAL_(condition, consequent, alternate) \ + PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_( \ + PA_COMPONENT_MACRO_CONDITIONAL_COMMA_(condition), consequent, alternate) + +// MSVC workaround for __VA_ARGS__ expanding into one expression. +#define PA_MSVC_EXPAND_ARG(arg) arg + +// Expands to a comma (,) iff its first argument expands to |1|. Used in +// conjunction with |PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_()|, as the +// presence or absense of an extra comma can be used to conditionally shift +// subsequent argument positions and thus influence which argument is selected. +#define PA_COMPONENT_MACRO_CONDITIONAL_COMMA_(...) \ + PA_COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(__VA_ARGS__, ) +#define PA_COMPONENT_MACRO_CONDITIONAL_COMMA_IMPL_(x, ...) \ + PA_COMPONENT_MACRO_CONDITIONAL_COMMA_##x##_ +#define PA_COMPONENT_MACRO_CONDITIONAL_COMMA_1_ , + +// Helper which simply selects its third argument. Used in conjunction with +// |PA_COMPONENT_MACRO_CONDITIONAL_COMMA_()| above to implement conditional +// macro expansion. +#define PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_(...) \ + PA_MSVC_EXPAND_ARG( \ + PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(__VA_ARGS__)) +#define PA_COMPONENT_MACRO_SELECT_THIRD_ARGUMENT_IMPL_(a, b, c, ...) c + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_COMPONENT_EXPORT_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h new file mode 100644 index 0000000000..fea4f1fc91 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h @@ -0,0 +1,36 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_CXX20_IS_CONSTANT_EVALUATED_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_CXX20_IS_CONSTANT_EVALUATED_H_ + +#include + +namespace partition_alloc::internal::base { + +// std::is_constant_evaluated was introduced in C++20. PartitionAlloc's minimum +// supported C++ version is C++17. +#if defined(__cpp_lib_is_constant_evaluated) && \ + __cpp_lib_is_constant_evaluated >= 201811L + +using std::is_constant_evaluated; + +#else + +// Implementation of C++20's std::is_constant_evaluated. +// +// References: +// - https://en.cppreference.com/w/cpp/types/is_constant_evaluated +// - https://wg21.link/meta.const.eval +constexpr bool is_constant_evaluated() noexcept { + return __builtin_is_constant_evaluated(); +} + +#endif + +} // namespace partition_alloc::internal::base + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_CXX20_IS_CONSTANT_EVALUATED_H_ diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/immediate_crash.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/immediate_crash.h new file mode 100644 index 0000000000..a9c8ff0d48 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/immediate_crash.h @@ -0,0 +1,165 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_ + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/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. +// +// - PA_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 PA_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: PA_TRAP_SEQUENCE_() previously required an instruction with a unique +// nonce since unlike clang, GCC folds together identical asm volatile +// blocks. +// - PA_TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid +// memory access. +// - PA_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: +// - PA_TRAP_SEQUENCE_() should be as compact as possible. +// - The first instruction of PA_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: PA_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 defined(COMPILER_GCC) + +#if defined(ARCH_CPU_X86_FAMILY) + +// TODO(crbug.com/40625592): 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 PA_TRAP_SEQUENCE1_() asm volatile("int3") + +#if BUILDFLAG(IS_APPLE) +// Intentionally empty: __builtin_unreachable() is always part of the sequence +// (see PA_IMMEDIATE_CRASH below) and already emits a ud2 on Mac. +#define PA_TRAP_SEQUENCE2_() asm volatile("") +#else +#define PA_TRAP_SEQUENCE2_() asm volatile("ud2") +#endif // BUILDFLAG(IS_APPLE) + +#elif defined(ARCH_CPU_ARMEL) + +// 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(crbug.com/40625592): Remove bkpt from this sequence. +#define PA_TRAP_SEQUENCE1_() asm volatile("bkpt #0") +#define PA_TRAP_SEQUENCE2_() asm volatile("udf #0") + +#elif defined(ARCH_CPU_ARM64) + +// This will always generate a SIGTRAP on arm64. +// TODO(crbug.com/40625592): Remove brk from this sequence. +#define PA_TRAP_SEQUENCE1_() asm volatile("brk #0") +#define PA_TRAP_SEQUENCE2_() asm volatile("hlt #0") + +#else + +// Crash report accuracy will not be guaranteed on other architectures, but at +// least this will crash as expected. +#define PA_TRAP_SEQUENCE1_() __builtin_trap() +#define PA_TRAP_SEQUENCE2_() asm volatile("") + +#endif // ARCH_CPU_* + +#elif defined(COMPILER_MSVC) + +#if !defined(__clang__) + +// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic. +#define PA_TRAP_SEQUENCE1_() __debugbreak() +#define PA_TRAP_SEQUENCE2_() + +#elif defined(ARCH_CPU_ARM64) + +// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and +// __debugbreak() generates that in both VC++ and clang. +#define PA_TRAP_SEQUENCE1_() __debugbreak() +// Intentionally empty: __builtin_unreachable() is always part of the sequence +// (see PA_IMMEDIATE_CRASH below) and already emits a ud2 on Win64, +// https://crbug.com/958373 +#define PA_TRAP_SEQUENCE2_() __asm volatile("") + +#else + +#define PA_TRAP_SEQUENCE1_() asm volatile("int3") +#define PA_TRAP_SEQUENCE2_() asm volatile("ud2") + +#endif // __clang__ + +#else + +#error No supported trap sequence! + +#endif // COMPILER_GCC + +#define PA_TRAP_SEQUENCE_() \ + do { \ + PA_TRAP_SEQUENCE1_(); \ + PA_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 !defined(COMPILER_GCC) || defined(__clang__) + +#define PA_WRAPPED_TRAP_SEQUENCE_() PA_TRAP_SEQUENCE_() + +#else + +#define PA_WRAPPED_TRAP_SEQUENCE_() \ + do { \ + [] { PA_TRAP_SEQUENCE_(); }(); \ + } while (false) + +#endif // !defined(COMPILER_GCC) || defined(__clang__) + +#if defined(__clang__) || defined(COMPILER_GCC) + +// __builtin_unreachable() hints to the compiler that this is noreturn and can +// be packed in the function epilogue. +#define PA_IMMEDIATE_CRASH() \ + [] { \ + PA_TRAP_SEQUENCE1_(); \ + PA_TRAP_SEQUENCE2_(); \ + }(), \ + __builtin_unreachable() + +#else + +// This is supporting non-chromium user of logging.h to build with MSVC, like +// pdfium. On MSVC there is no __builtin_unreachable(). +#define PA_IMMEDIATE_CRASH() PA_WRAPPED_TRAP_SEQUENCE_() + +#endif // defined(__clang__) || defined(COMPILER_GCC) + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_IMMEDIATE_CRASH_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.cc new file mode 100644 index 0000000000..08b6ed7ad4 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.cc @@ -0,0 +1,6 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "log_message.h" diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.h new file mode 100644 index 0000000000..011bc809fc --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.h @@ -0,0 +1,156 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_LOG_MESSAGE_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_LOG_MESSAGE_H_ + +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/scoped_clear_last_error.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.h" + +namespace partition_alloc::internal::logging { + +// Sets the Log Message Handler that gets passed every log message before +// it's sent to other log destinations (if any). +// Returns true to signal that it handled the message and the message +// should not be sent to other log destinations. +typedef bool (*LogMessageHandlerFunction)(int severity, + const char* file, + int line, + size_t message_start, + const char* str); +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +void SetLogMessageHandler(LogMessageHandlerFunction handler); +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +LogMessageHandlerFunction GetLogMessageHandler(); + +using LogSeverity = int; +constexpr LogSeverity LOGGING_VERBOSE = -1; // This is level 1 verbosity +// Note: the log severities are used to index into the array of names, +// see log_severity_names. +constexpr LogSeverity LOGGING_INFO = 0; +constexpr LogSeverity LOGGING_WARNING = 1; +constexpr LogSeverity LOGGING_ERROR = 2; +constexpr LogSeverity LOGGING_FATAL = 3; +constexpr LogSeverity LOGGING_NUM_SEVERITIES = 4; + +// LOGGING_DFATAL is LOGGING_FATAL in DCHECK-enabled builds, ERROR in normal +// mode. +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) +constexpr LogSeverity LOGGING_DFATAL = LOGGING_FATAL; +#else +constexpr LogSeverity LOGGING_DFATAL = LOGGING_ERROR; +#endif + +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +extern base::strings::CStringBuilder* g_swallow_stream; + +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the PA_LOG() macro (and variants thereof) +// above. +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) LogMessage { + public: + // Used for PA_LOG(severity). + LogMessage(const char* file, int line, LogSeverity severity); + + // Used for CHECK(). Implied severity = LOGGING_FATAL. + LogMessage(const char* file, int line, const char* condition); + LogMessage(const LogMessage&) = delete; + LogMessage& operator=(const LogMessage&) = delete; + virtual ~LogMessage(); + + base::strings::CStringBuilder& stream() { return stream_; } + + LogSeverity severity() { return severity_; } + const char* c_str() { return stream_.c_str(); } + + private: + void Init(const char* file, int line); + + const LogSeverity severity_; + base::strings::CStringBuilder stream_; + size_t message_start_; // Offset of the start of the message (past prefix + // info). + // The file and line information passed in to the constructor. + const char* const file_; + const int line_; + + // This is useful since the LogMessage class uses a lot of Win32 calls + // that will lose the value of GLE and the code that called the log function + // will have lost the thread error value when the log call returns. + base::ScopedClearLastError last_error_; +}; + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() = default; + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(base::strings::CStringBuilder&) {} +}; + +#if BUILDFLAG(IS_WIN) +typedef unsigned long SystemErrorCode; +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +typedef int SystemErrorCode; +#endif + +// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to +// pull in windows.h just for GetLastError() and DWORD. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +SystemErrorCode GetLastSystemErrorCode(); + +#if BUILDFLAG(IS_WIN) +// Appends a formatted system message of the GetLastError() type. +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) Win32ErrorLogMessage + : public LogMessage { + public: + Win32ErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + Win32ErrorLogMessage(const Win32ErrorLogMessage&) = delete; + Win32ErrorLogMessage& operator=(const Win32ErrorLogMessage&) = delete; + // Appends the error message before destructing the encapsulated class. + ~Win32ErrorLogMessage() override; + + private: + SystemErrorCode err_; +}; +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +// Appends a formatted system message of the errno type +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) ErrnoLogMessage + : public LogMessage { + public: + ErrnoLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + ErrnoLogMessage(const ErrnoLogMessage&) = delete; + ErrnoLogMessage& operator=(const ErrnoLogMessage&) = delete; + // Appends the error message before destructing the encapsulated class. + ~ErrnoLogMessage() override; + + private: + SystemErrorCode err_; +}; +#endif // BUILDFLAG(IS_WIN) + +} // namespace partition_alloc::internal::logging + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_LOG_MESSAGE_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.cc new file mode 100644 index 0000000000..d41b597348 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.cc @@ -0,0 +1,117 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.h" + +// TODO(crbug.com/40158212): After finishing copying //base files to PA library, +// remove defined(BASE_CHECK_H_) from here. +#if defined( \ + BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_) || \ + defined(BASE_CHECK_H_) || \ + defined( \ + BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_CHECK_H_) +#error "logging.h should not include check.h" +#endif + +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" +//#include "partition_alloc/partition_alloc_base/debug/alias.h" +//#include "partition_alloc/partition_alloc_base/immediate_crash.h" + +#if BUILDFLAG(IS_WIN) +#include + +#include +#endif + +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#include + +#include +#include +#include +#include +#endif + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/posix/eintr_wrapper.h" + +namespace partition_alloc::internal::logging { + +namespace { + +int g_min_log_level = 0; + +#if !BUILDFLAG(IS_WIN) +void WriteToStderr(const char* data, size_t length) { + size_t bytes_written = 0; + int rv; + while (bytes_written < length) { + rv = WrapEINTR(write)(STDERR_FILENO, data + bytes_written, + length - bytes_written); + if (rv < 0) { + // Give up, nothing we can do now. + break; + } + bytes_written += rv; + } +} +#else // !BUILDFLAG(IS_WIN) +void WriteToStderr(const char* data, size_t length) { + HANDLE handle = ::GetStdHandle(STD_ERROR_HANDLE); + const char* ptr = data; + const char* ptr_end = data + length; + while (ptr < ptr_end) { + DWORD bytes_written = 0; + if (!::WriteFile(handle, ptr, ptr_end - ptr, &bytes_written, nullptr) || + bytes_written == 0) { + // Give up, nothing we can do now. + break; + } + ptr += bytes_written; + } +} +#endif // !BUILDFLAG(IS_WIN) + +} // namespace + +void SetMinLogLevel(int level) { + g_min_log_level = std::min(LOGGING_FATAL, level); +} + +int GetMinLogLevel() { + return g_min_log_level; +} + +bool ShouldCreateLogMessage(int severity) { + if (severity < g_min_log_level) { + return false; + } + + // Return true here unless we know ~LogMessage won't do anything. + return true; +} + +int GetVlogVerbosity() { + return std::max(-1, LOGGING_INFO - GetMinLogLevel()); +} + +void RawLog(int level, const char* message) { + if (level >= g_min_log_level && message) { +#if !BUILDFLAG(IS_WIN) + const size_t message_len = strlen(message); +#else // !BUILDFLAG(IS_WIN) + const size_t message_len = ::lstrlenA(message); +#endif // !BUILDFLAG(IS_WIN) + WriteToStderr(message, message_len); + + if (message_len > 0 && message[message_len - 1] != '\n') { + WriteToStderr("\n", 1); + } + } +} + +} // namespace partition_alloc::internal::logging \ No newline at end of file diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.h new file mode 100644 index 0000000000..4a804d7270 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/logging.h @@ -0,0 +1,399 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_LOGGING_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_LOGGING_H_ + +#include +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/log_message.h" + +// TODO(crbug.com/40158212): Need to update the description, because logging for +// PA standalone library was minimized. +// +// Optional message capabilities +// ----------------------------- +// Assertion failed messages and fatal errors are displayed in a dialog box +// before the application exits. However, running this UI creates a message +// loop, which causes application messages to be processed and potentially +// dispatched to existing application windows. Since the application is in a +// bad state when this assertion dialog is displayed, these messages may not +// get processed and hang the dialog, or the application might go crazy. +// +// Therefore, it can be beneficial to display the error dialog in a separate +// process from the main application. When the logging system needs to display +// a fatal error dialog box, it will look for a program called +// "DebugMessage.exe" in the same directory as the application executable. It +// will run this application with the message as the command line, and will +// not include the name of the application as is traditional for easier +// parsing. +// +// The code for DebugMessage.exe is only one line. In WinMain, do: +// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0); +// +// If DebugMessage.exe is not found, the logging code will use a normal +// MessageBox, potentially causing the problems discussed above. + +// Instructions +// ------------ +// +// Make a bunch of macros for logging. The way to log things is to stream +// things to PA_LOG(). E.g., +// +// PA_LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can also do conditional logging: +// +// PA_LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// The CHECK(condition) macro is active in both debug and release builds and +// effectively performs a PA_LOG(FATAL) which terminates the process and +// generates a crashdump unless a debugger is attached. +// +// There are also "debug mode" logging macros like the ones above: +// +// PA_DLOG(INFO) << "Found cookies"; +// +// PA_DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. PA_LOG_IF and development flags also work well together +// because the code can be compiled away sometimes. +// +// We also have +// +// PA_LOG_ASSERT(assertion); +// PA_DLOG_ASSERT(assertion); +// +// which is syntactic sugar for PA_{,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// PA_VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// PA_VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// +// There's also PA_VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (PA_VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just PA_VLOG(2) << ...; +// } +// +// There is also a PA_VLOG_IF "verbose level" condition macro for sample +// cases, when some extra computation and preparation for logs is not +// needed. +// +// PA_VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// +// We also override the standard 'assert' to use 'PA_DLOG_ASSERT'. +// +// Lastly, there is: +// +// PA_PLOG(ERROR) << "Couldn't do foo"; +// PA_DPLOG(ERROR) << "Couldn't do foo"; +// PA_PLOG_IF(ERROR, cond) << "Couldn't do foo"; +// PA_DPLOG_IF(ERROR, cond) << "Couldn't do foo"; +// PA_PCHECK(condition) << "Couldn't do foo"; +// PA_DPCHECK(condition) << "Couldn't do foo"; +// +// which append the last system error to the message in string form (taken from +// GetLastError() on Windows and errno on POSIX). +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// There is the special severity of DFATAL, which logs FATAL in DCHECK-enabled +// builds, ERROR in normal mode. +// +// Output is formatted as per the following example: +// [VERBOSE1:drm_device_handle.cc(90)] Succeeded +// authenticating /dev/dri/card0 in 0 ms with 1 attempt(s) +// +// The colon separated fields inside the brackets are as follows: +// 1. The log level +// 2. The filename and line number where the log was instantiated +// +// Additional logging-related information can be found here: +// https://chromium.googlesource.com/chromium/src/+/main/docs/linux/debugging.md#Logging + +namespace partition_alloc::internal::logging { + +// Sets the log level. Anything at or above this level will be written to the +// log file/displayed to the user (if applicable). Anything below this level +// will be silently ignored. The log level defaults to 0 (everything is logged +// up to level INFO) if this function is not called. +// Note that log messages for VLOG(x) are logged at level -x, so setting +// the min log level to negative values enables verbose logging. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) void SetMinLogLevel(int level); + +// Gets the current log level. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) int GetMinLogLevel(); + +// Used by PA_LOG_IS_ON to lazy-evaluate stream arguments. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +bool ShouldCreateLogMessage(int severity); + +// Gets the PA_VLOG default verbosity level. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) int GetVlogVerbosity(); + +// A few definitions of macros that don't generate much code. These are used +// by PA_LOG() and LOG_IF, etc. Since these are used all over our code, it's +// better to have compact code for these operations. +#define PA_COMPACT_GOOGLE_LOG_EX_INFO(ClassName) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_INFO) +#define PA_COMPACT_GOOGLE_PLOG_EX_INFO(ClassName, error_code) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_INFO, \ + error_code) +#define PA_COMPACT_GOOGLE_LOG_EX_WARNING(ClassName) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, \ + ::partition_alloc::internal::logging::LOGGING_WARNING) +#define PA_COMPACT_GOOGLE_PLOG_EX_WARNING(ClassName, error_code) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, \ + ::partition_alloc::internal::logging::LOGGING_WARNING) +#define PA_COMPACT_GOOGLE_LOG_EX_ERROR(ClassName) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_ERROR) +#define PA_COMPACT_GOOGLE_PLOG_EX_ERROR(ClassName, error_code) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_ERROR, \ + error_code) +#define PA_COMPACT_GOOGLE_LOG_EX_FATAL(ClassName) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_FATAL) +#define PA_COMPACT_GOOGLE_PLOG_EX_FATAL(ClassName, error_code) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, ::partition_alloc::internal::logging::LOGGING_FATAL, \ + error_code) +#define PA_COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, \ + ::partition_alloc::internal::logging::LOGGING_DFATAL) +#define PA_COMPACT_GOOGLE_PLOG_EX_DFATAL(ClassName, error_code) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, \ + ::partition_alloc::internal::logging::LOGGING_DFATAL, error_code) +#define PA_COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, \ + ::partition_alloc::internal::logging::LOGGING_DCHECK) +#define PA_COMPACT_GOOGLE_PLOG_EX_DCHECK(ClassName, error_code) \ + ::partition_alloc::internal::logging::ClassName( \ + __FILE__, __LINE__, \ + ::partition_alloc::internal::logging::LOGGING_DCHECK, error_code) + +#define PA_COMPACT_GOOGLE_LOG_INFO PA_COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) +#define PA_COMPACT_GOOGLE_LOG_WARNING \ + PA_COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) +#define PA_COMPACT_GOOGLE_LOG_ERROR PA_COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) +#define PA_COMPACT_GOOGLE_LOG_FATAL PA_COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) +#define PA_COMPACT_GOOGLE_LOG_DFATAL PA_COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) +#define PA_COMPACT_GOOGLE_LOG_DCHECK PA_COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage) + +#if BUILDFLAG(IS_WIN) +// wingdi.h defines ERROR to be 0. When we call PA_LOG(ERROR), it gets +// substituted with 0, and it expands to PA_COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as PA_COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that +// the Windows SDK does for consistency. +#define PA_ERROR 0 +#define PA_COMPACT_GOOGLE_LOG_EX_0(ClassName) \ + PA_COMPACT_GOOGLE_LOG_EX_ERROR(ClassName) +#define PA_COMPACT_GOOGLE_LOG_0 PA_COMPACT_GOOGLE_LOG_ERROR +// Needed for LOG_IS_ON(ERROR). +constexpr LogSeverity LOGGING_0 = LOGGING_ERROR; +#endif + +// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also, +// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will +// always fire if they fail. +#define PA_LOG_IS_ON(severity) \ + (::partition_alloc::internal::logging::ShouldCreateLogMessage( \ + ::partition_alloc::internal::logging::LOGGING_##severity)) + +// We don't do any caching tricks with VLOG_IS_ON() like the +// google-glog version since it increases binary size. This means +// that using the v-logging functions in conjunction with --vmodule +// may be slow. +#define PA_VLOG_IS_ON(verboselevel) \ + ((verboselevel) <= ::partition_alloc::internal::logging::GetVlogVerbosity()) + +// Helper macro which avoids evaluating the arguments to a stream if +// the condition doesn't hold. Condition is evaluated once and only once. +#define PA_LAZY_STREAM(stream, condition) \ + !(condition) \ + ? (void)0 \ + : ::partition_alloc::internal::logging::LogMessageVoidify() & (stream) + +// We use the preprocessor's merging operator, "##", so that, e.g., +// PA_LOG(INFO) becomes the token PA_COMPACT_GOOGLE_LOG_INFO. There's some +// funny subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define PA_LOG_STREAM(severity) PA_COMPACT_GOOGLE_LOG_##severity.stream() + +#define PA_LOG(severity) \ + PA_LAZY_STREAM(PA_LOG_STREAM(severity), PA_LOG_IS_ON(severity)) +#define PA_LOG_IF(severity, condition) \ + PA_LAZY_STREAM(PA_LOG_STREAM(severity), PA_LOG_IS_ON(severity) && (condition)) + +// The VLOG macros log with negative verbosities. +#define PA_VLOG_STREAM(verbose_level) \ + ::partition_alloc::internal::logging::LogMessage(__FILE__, __LINE__, \ + -(verbose_level)) \ + .stream() + +#define PA_VLOG(verbose_level) \ + PA_LAZY_STREAM(PA_VLOG_STREAM(verbose_level), PA_VLOG_IS_ON(verbose_level)) + +#define PA_VLOG_IF(verbose_level, condition) \ + PA_LAZY_STREAM(PA_VLOG_STREAM(verbose_level), \ + PA_VLOG_IS_ON(verbose_level) && (condition)) + +#if BUILDFLAG(IS_WIN) +#define PA_VPLOG_STREAM(verbose_level) \ + ::partition_alloc::internal::logging::Win32ErrorLogMessage( \ + __FILE__, __LINE__, -(verbose_level), \ + ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ + .stream() +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#define PA_VPLOG_STREAM(verbose_level) \ + ::partition_alloc::internal::logging::ErrnoLogMessage( \ + __FILE__, __LINE__, -(verbose_level), \ + ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ + .stream() +#endif + +#define PA_VPLOG(verbose_level) \ + PA_LAZY_STREAM(PA_VPLOG_STREAM(verbose_level), PA_VLOG_IS_ON(verbose_level)) + +#define PA_VPLOG_IF(verbose_level, condition) \ + PA_LAZY_STREAM(PA_VPLOG_STREAM(verbose_level), \ + PA_VLOG_IS_ON(verbose_level) && (condition)) + +// TODO(akalin): Add more VLOG variants, e.g. VPLOG. + +#define PA_LOG_ASSERT(condition) \ + PA_LOG_IF(FATAL, !(PA_ANALYZER_ASSUME_TRUE(condition))) \ + << "Assert failed: " #condition ". " + +#if BUILDFLAG(IS_WIN) +#define PA_PLOG_STREAM(severity) \ + PA_COMPACT_GOOGLE_PLOG_EX_##severity( \ + Win32ErrorLogMessage, \ + ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ + .stream() +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +#define PA_PLOG_STREAM(severity) \ + PA_COMPACT_GOOGLE_PLOG_EX_##severity( \ + ErrnoLogMessage, \ + ::partition_alloc::internal::logging::GetLastSystemErrorCode()) \ + .stream() +#endif + +#define PA_PLOG(severity) \ + PA_LAZY_STREAM(PA_PLOG_STREAM(severity), PA_LOG_IS_ON(severity)) + +#define PA_PLOG_IF(severity, condition) \ + PA_LAZY_STREAM(PA_PLOG_STREAM(severity), \ + PA_LOG_IS_ON(severity) && (condition)) + +// Note that g_swallow_stream is used instead of an arbitrary PA_LOG() stream to +// avoid the creation of an object with a non-trivial destructor (LogMessage). +// On MSVC x86 (checked on 2015 Update 3), this causes a few additional +// pointless instructions to be emitted even at full optimization level, even +// though the : arm of the ternary operator is clearly never executed. Using a +// simpler object to be &'d with Voidify() avoids these extra instructions. +// Using a simpler POD object with a templated operator<< also works to avoid +// these instructions. However, this causes warnings on statically defined +// implementations of operator<<(std::ostream, ...) in some .cc files, because +// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an +// ostream* also is not suitable, because some compilers warn of undefined +// behavior. +#define PA_EAT_STREAM_PARAMETERS \ + true ? (void)0 \ + : ::partition_alloc::internal::logging::LogMessageVoidify() & \ + (*::partition_alloc::internal::logging::g_swallow_stream) + +// Definitions for DLOG et al. + +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) + +#define PA_DLOG_IS_ON(severity) PA_LOG_IS_ON(severity) +#define PA_DLOG_IF(severity, condition) PA_LOG_IF(severity, condition) +#define PA_DLOG_ASSERT(condition) PA_LOG_ASSERT(condition) +#define PA_DPLOG_IF(severity, condition) PA_PLOG_IF(severity, condition) +#define PA_DVLOG_IF(verboselevel, condition) PA_VLOG_IF(verboselevel, condition) +#define PA_DVPLOG_IF(verboselevel, condition) \ + PA_VPLOG_IF(verboselevel, condition) + +#else // PA_BUILDFLAG(PA_DCHECK_IS_ON) + +// If !PA_BUILDFLAG(PA_DCHECK_IS_ON), we want to avoid emitting any references +// to |condition| (which may reference a variable defined only if +// PA_BUILDFLAG(PA_DCHECK_IS_ON)). Contrast this with DCHECK et al., which has +// different behavior. + +#define PA_DLOG_IS_ON(severity) false +#define PA_DLOG_IF(severity, condition) PA_EAT_STREAM_PARAMETERS +#define PA_DLOG_ASSERT(condition) PA_EAT_STREAM_PARAMETERS +#define PA_DPLOG_IF(severity, condition) PA_EAT_STREAM_PARAMETERS +#define PA_DVLOG_IF(verboselevel, condition) PA_EAT_STREAM_PARAMETERS +#define PA_DVPLOG_IF(verboselevel, condition) PA_EAT_STREAM_PARAMETERS + +#endif // PA_BUILDFLAG(PA_DCHECK_IS_ON) + +#define PA_DLOG(severity) \ + PA_LAZY_STREAM(PA_LOG_STREAM(severity), PA_DLOG_IS_ON(severity)) + +#define PA_DPLOG(severity) \ + PA_LAZY_STREAM(PA_PLOG_STREAM(severity), PA_DLOG_IS_ON(severity)) + +#define PA_DVLOG(verboselevel) PA_DVLOG_IF(verboselevel, true) + +#define PA_DVPLOG(verboselevel) PA_DVPLOG_IF(verboselevel, true) + +// Definitions for DCHECK et al. + +#if PA_BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE) +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) extern LogSeverity LOGGING_DCHECK; +#else +constexpr LogSeverity LOGGING_DCHECK = LOGGING_FATAL; +#endif // PA_BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE) + +// Redefine the standard assert to use our nice log files +#undef assert +#define assert(x) PA_DLOG_ASSERT(x) + +// Async signal safe logging mechanism. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +void RawLog(int level, const char* message); + +#define PA_RAW_LOG(level, message) \ + ::partition_alloc::internal::logging::RawLog( \ + ::partition_alloc::internal::logging::LOGGING_##level, message) + +} // namespace partition_alloc::internal::logging + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_LOGGING_H_ diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/scoped_clear_last_error.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/scoped_clear_last_error.h new file mode 100644 index 0000000000..402cae703c --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/scoped_clear_last_error.h @@ -0,0 +1,58 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_SCOPED_CLEAR_LAST_ERROR_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_SCOPED_CLEAR_LAST_ERROR_H_ + +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" + +namespace partition_alloc::internal::base { + +// ScopedClearLastError stores and resets the value of thread local error codes +// (errno, GetLastError()), and restores them in the destructor. This is useful +// to avoid side effects on these values in instrumentation functions that +// interact with the OS. + +// Common implementation of ScopedClearLastError for all platforms. Use +// ScopedClearLastError instead. +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) ScopedClearLastErrorBase { + public: + ScopedClearLastErrorBase() : last_errno_(errno) { errno = 0; } + ScopedClearLastErrorBase(const ScopedClearLastErrorBase&) = delete; + ScopedClearLastErrorBase& operator=(const ScopedClearLastErrorBase&) = delete; + ~ScopedClearLastErrorBase() { errno = last_errno_; } + + private: + const int last_errno_; +}; + +#if BUILDFLAG(IS_WIN) + +// Windows specific implementation of ScopedClearLastError. +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) ScopedClearLastError + : public ScopedClearLastErrorBase { + public: + ScopedClearLastError(); + ScopedClearLastError(const ScopedClearLastError&) = delete; + ScopedClearLastError& operator=(const ScopedClearLastError&) = delete; + ~ScopedClearLastError(); + + private: + const unsigned long last_system_error_; +}; + +#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) + +using ScopedClearLastError = ScopedClearLastErrorBase; + +#endif // BUILDFLAG(IS_WIN) + +} // namespace partition_alloc::internal::base + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_SCOPED_CLEAR_LAST_ERROR_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.cc new file mode 100644 index 0000000000..42eb35f5d8 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.cc @@ -0,0 +1,216 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.h" + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.h" + +#if !BUILDFLAG(IS_WIN) +#include +#endif + +#include +#include +#include + +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) +#include "partition_alloc/partition_alloc_base/check.h" +#define PA_RAW_DCHECK PA_RAW_CHECK +#else +#define PA_RAW_DCHECK(x) \ + do { \ + if (x) { \ + } \ + } while (0) +#endif + +namespace partition_alloc::internal::base::strings { + +namespace { + +constexpr size_t kNumDigits10 = 5u; + +constexpr uint64_t Pow10(unsigned exp) { + uint64_t ret = 1; + for (unsigned i = 0; i < exp; ++i) { + ret *= 10U; + } + return ret; +} + +constexpr uint64_t Log10(uint64_t value) { + uint64_t ret = 0; + while (value != 0u) { + value = value / 10u; + ++ret; + } + return ret; +} + +constexpr uint64_t GetDigits10(unsigned num_digits10) { + return Pow10(num_digits10); +} + +} // namespace + +template +void CStringBuilder::PutInteger(T value) { + // We need an array of chars whose size is: + // - floor(log10(max value)) + 1 chars for the give value, and + // - 1 char for '-' (if negative) + // - 1 char for '\0' + char buffer[Log10(std::numeric_limits::max()) + 3]; + ssize_t n = base::strings::SafeSPrintf(buffer, "%d", value); + PA_RAW_DCHECK(n >= 0); + PA_RAW_DCHECK(static_cast(n) < sizeof(buffer)); + PutText(buffer, n); +} + +CStringBuilder& CStringBuilder::operator<<(char ch) { + PutText(&ch, 1); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(const char* text) { + PutText(text); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(float value) { + PutFloatingPoint(value, kNumDigits10); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(double value) { + PutFloatingPoint(value, kNumDigits10); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(int value) { + PutInteger(value); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(unsigned int value) { + PutInteger(value); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(long value) { + PutInteger(value); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(unsigned long value) { + PutInteger(value); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(long long value) { + PutInteger(value); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(unsigned long long value) { + PutInteger(value); + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(const void* value) { + if (!value) { + PutText("(nil)"); + } else { + // We need an array of chars whose size is: + // - 2 chars per 1 byte(00-FF), totally sizeof(const void*) * 2 chars, + // - 2 chars for "0x", + // - 1 char for '\0', + char buffer[sizeof(const void*) * 2 + 2 + 1]; + ssize_t n = base::strings::SafeSPrintf(buffer, "%p", value); + PA_RAW_DCHECK(n > 0); + PA_RAW_DCHECK(static_cast(n) < sizeof(buffer)); + PutText(buffer, n); + } + return *this; +} + +CStringBuilder& CStringBuilder::operator<<(std::nullptr_t) { + PutText("nullptr"); + return *this; +} + +const char* CStringBuilder::c_str() { + PA_RAW_DCHECK(buffer_ <= ptr_ && ptr_ < buffer_ + kBufferSize); + *ptr_ = '\0'; + return buffer_; +} + +void CStringBuilder::PutFloatingPoint(double value, unsigned num_digits10) { + switch (std::fpclassify(value)) { + case FP_INFINITE: + PutText(value < 0 ? "-inf" : "inf"); + break; + case FP_NAN: + PutText("NaN"); + break; + case FP_ZERO: + PutText("0"); + break; + case FP_SUBNORMAL: + // Denormalized values are not supported. + PutNormalFloatingPoint(value > 0 ? std::numeric_limits::min() + : -std::numeric_limits::min(), + num_digits10); + break; + case FP_NORMAL: + default: + PutNormalFloatingPoint(value, num_digits10); + break; + } +} + +void CStringBuilder::PutNormalFloatingPoint(double value, + unsigned num_digits10) { + if (value < 0) { + PutText("-", 1); + value = -value; + } + + int exponent = floor(log10(value)); + double significand = value / pow(10, exponent); + + char buffer[64]; + ssize_t n = base::strings::SafeSPrintf( + buffer, "%d", lrint(significand * GetDigits10(num_digits10))); + PA_RAW_DCHECK(n > 0); + PA_RAW_DCHECK(static_cast(n) < sizeof(buffer)); + PutText(buffer, 1); + if (n > 1) { + PutText(".", 1); + PutText(buffer + 1, n - 1); + } + if (exponent != 0) { + n = base::strings::SafeSPrintf(buffer, "e%s%d", exponent > 0 ? "+" : "", + exponent); + PA_RAW_DCHECK(n > 0); + PA_RAW_DCHECK(static_cast(n) < sizeof(buffer)); + PutText(buffer, n); + } +} + +void CStringBuilder::PutText(const char* text) { + PutText(text, strlen(text)); +} + +void CStringBuilder::PutText(const char* text, size_t length) { + PA_RAW_DCHECK(buffer_ <= ptr_ && ptr_ < buffer_ + kBufferSize); + while (ptr_ < buffer_ + kBufferSize - 1 && length > 0 && *text != '\0') { + *ptr_++ = *text++; + --length; + } +} + +} // namespace partition_alloc::internal::base::strings diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.h new file mode 100644 index 0000000000..42ada804ac --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/cstring_builder.h @@ -0,0 +1,64 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_STRINGS_CSTRING_BUILDER_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_STRINGS_CSTRING_BUILDER_H_ + +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" + +#if !BUILDFLAG(IS_WIN) +#include +#endif + +namespace partition_alloc::internal::base::strings { + +// Similar to std::ostringstream, but creates a C string, i.e. nul-terminated +// char-type string, instead of std::string. To use inside memory allocation, +// this method must not allocate any memory with malloc, aligned_malloc, +// calloc, and so on. +class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) CStringBuilder { + public: + // If kBufferSize is too large, PA_LOG() and PA_BASE_*CHECK() will spend + // much more stack. This causes out-of-stack. + // ThreadTest.StartWithOptions_StackSize checks if threads can run with + // some specified stack size. If kBufferSize==1024u, the test will fail + // on 32bit bots. + static constexpr size_t kBufferSize = 256u; + + CStringBuilder() : ptr_(buffer_) {} + + CStringBuilder& operator<<(char ch); + CStringBuilder& operator<<(const char* text); + CStringBuilder& operator<<(float value); + CStringBuilder& operator<<(double value); + CStringBuilder& operator<<(int value); + CStringBuilder& operator<<(unsigned int value); + CStringBuilder& operator<<(long value); + CStringBuilder& operator<<(unsigned long value); + CStringBuilder& operator<<(long long value); + CStringBuilder& operator<<(unsigned long long value); + CStringBuilder& operator<<(const void* value); + CStringBuilder& operator<<(std::nullptr_t); + const char* c_str(); + + private: + template + void PutInteger(T value); + void PutFloatingPoint(double value, unsigned num_digits10); + void PutNormalFloatingPoint(double value, unsigned num_digits10); + void PutText(const char* text); + void PutText(const char* text, size_t length); + + char buffer_[kBufferSize]; + char* ptr_; +}; + +} // namespace partition_alloc::internal::base::strings + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_STRINGS_CSTRING_BUILDER_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.cc new file mode 100644 index 0000000000..b91cb6137b --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.cc @@ -0,0 +1,712 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.h" + +#include +#include +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" + +#if !defined(NDEBUG) +// In debug builds, we use RAW_CHECK() to print useful error messages, if +// SafeSPrintf() is called with broken arguments. +// As our contract promises that SafeSPrintf() can be called from any +// restricted run-time context, it is not actually safe to call logging +// functions from it; and we only ever do so for debug builds and hope for the +// best. We should _never_ call any logging function other than RAW_CHECK(), +// and we should _never_ include any logging code that is active in production +// builds. Most notably, we should not include these logging functions in +// unofficial release builds, even though those builds would otherwise have +// DCHECKS() enabled. +// In other words; please do not remove the #ifdef around this #include. +// Instead, in production builds we opt for returning a degraded result, +// whenever an error is encountered. +// E.g. The broken function call +// SafeSPrintf("errno = %d (%x)", errno, strerror(errno)) +// will print something like +// errno = 13, (%x) +// instead of +// errno = 13 (Access denied) +// In most of the anticipated use cases, that's probably the preferred +// behavior. +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.h" +#define DEBUG_CHECK PA_RAW_CHECK +#else +#define DEBUG_CHECK(x) \ + do { \ + if (x) { \ + } \ + } while (0) +#endif + +namespace partition_alloc::internal::base::strings { + +// The code in this file is extremely careful to be async-signal-safe. +// +// Most obviously, we avoid calling any code that could dynamically allocate +// memory. Doing so would almost certainly result in bugs and dead-locks. +// We also avoid calling any other STL functions that could have unintended +// side-effects involving memory allocation or access to other shared +// resources. +// +// But on top of that, we also avoid calling other library functions, as many +// of them have the side-effect of calling getenv() (in order to deal with +// localization) or accessing errno. The latter sounds benign, but there are +// several execution contexts where it isn't even possible to safely read let +// alone write errno. +// +// The stated design goal of the SafeSPrintf() function is that it can be +// called from any context that can safely call C or C++ code (i.e. anything +// that doesn't require assembly code). +// +// For a brief overview of some but not all of the issues with async-signal- +// safety, refer to: +// http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html + +namespace { +const size_t kSSizeMaxConst = ((size_t)(ssize_t)-1) >> 1; + +const char kUpCaseHexDigits[] = "0123456789ABCDEF"; +const char kDownCaseHexDigits[] = "0123456789abcdef"; +} // namespace + +#if defined(NDEBUG) +// We would like to define kSSizeMax as std::numeric_limits::max(), +// but C++ doesn't allow us to do that for constants. Instead, we have to +// use careful casting and shifting. We later use a static_assert to +// verify that this worked correctly. +namespace { +const size_t kSSizeMax = kSSizeMaxConst; +} +#else // defined(NDEBUG) +// For efficiency, we really need kSSizeMax to be a constant. But for unit +// tests, it should be adjustable. This allows us to verify edge cases without +// having to fill the entire available address space. As a compromise, we make +// kSSizeMax adjustable in debug builds, and then only compile that particular +// part of the unit test in debug builds. +namespace { +static size_t kSSizeMax = kSSizeMaxConst; +} + +namespace internal { +void SetSafeSPrintfSSizeMaxForTest(size_t max) { + kSSizeMax = max; +} + +size_t GetSafeSPrintfSSizeMaxForTest() { + return kSSizeMax; +} +} // namespace internal +#endif // defined(NDEBUG) + +namespace { +class Buffer { + public: + // |buffer| is caller-allocated storage that SafeSPrintf() writes to. It + // has |size| bytes of writable storage. It is the caller's responsibility + // to ensure that the buffer is at least one byte in size, so that it fits + // the trailing NUL that will be added by the destructor. The buffer also + // must be smaller or equal to kSSizeMax in size. + Buffer(char* buffer, size_t size) + : buffer_(buffer), + size_(size - 1), // Account for trailing NUL byte + count_(0) { +// MSVS2013's standard library doesn't mark max() as constexpr yet. cl.exe +// supports static_cast but doesn't really implement constexpr yet so it doesn't +// complain, but clang does. +#if __cplusplus >= 201103 && !(defined(__clang__) && BUILDFLAG(IS_WIN)) + static_assert(kSSizeMaxConst == + static_cast(std::numeric_limits::max()), + "kSSizeMaxConst should be the max value of an ssize_t"); +#endif + DEBUG_CHECK(size > 0); + DEBUG_CHECK(size <= kSSizeMax); + } + + Buffer(const Buffer&) = delete; + Buffer& operator=(const Buffer&) = delete; + + ~Buffer() { + // The code calling the constructor guaranteed that there was enough space + // to store a trailing NUL -- and in debug builds, we are actually + // verifying this with DEBUG_CHECK()s in the constructor. So, we can + // always unconditionally write the NUL byte in the destructor. We do not + // need to adjust the count_, as SafeSPrintf() copies snprintf() in not + // including the NUL byte in its return code. + *GetInsertionPoint() = '\000'; + } + + // Returns true, iff the buffer is filled all the way to |kSSizeMax-1|. The + // caller can now stop adding more data, as GetCount() has reached its + // maximum possible value. + inline bool OutOfAddressableSpace() const { + return count_ == static_cast(kSSizeMax - 1); + } + + // Returns the number of bytes that would have been emitted to |buffer_| + // if it was sized sufficiently large. This number can be larger than + // |size_|, if the caller provided an insufficiently large output buffer. + // But it will never be bigger than |kSSizeMax-1|. + inline ssize_t GetCount() const { + DEBUG_CHECK(count_ < kSSizeMax); + return static_cast(count_); + } + + // Emits one |ch| character into the |buffer_| and updates the |count_| of + // characters that are currently supposed to be in the buffer. + // Returns "false", iff the buffer was already full. + // N.B. |count_| increases even if no characters have been written. This is + // needed so that GetCount() can return the number of bytes that should + // have been allocated for the |buffer_|. + inline bool Out(char ch) { + if (size_ >= 1 && count_ < size_) { + buffer_[count_] = ch; + return IncrementCountByOne(); + } + // |count_| still needs to be updated, even if the buffer has been + // filled completely. This allows SafeSPrintf() to return the number of + // bytes that should have been emitted. + IncrementCountByOne(); + return false; + } + + // Inserts |padding|-|len| bytes worth of padding into the |buffer_|. + // |count_| will also be incremented by the number of bytes that were meant + // to be emitted. The |pad| character is typically either a ' ' space + // or a '0' zero, but other non-NUL values are legal. + // Returns "false", iff the |buffer_| filled up (i.e. |count_| + // overflowed |size_|) at any time during padding. + inline bool Pad(char pad, size_t padding, size_t len) { + DEBUG_CHECK(pad); + DEBUG_CHECK(padding <= kSSizeMax); + for (; padding > len; --padding) { + if (!Out(pad)) { + if (--padding) { + IncrementCount(padding - len); + } + return false; + } + } + return true; + } + + // POSIX doesn't define any async-signal-safe function for converting + // an integer to ASCII. Define our own version. + // + // This also gives us the ability to make the function a little more + // powerful and have it deal with |padding|, with truncation, and with + // predicting the length of the untruncated output. + // + // IToASCII() converts an integer |i| to ASCII. + // + // Unlike similar functions in the standard C library, it never appends a + // NUL character. This is left for the caller to do. + // + // While the function signature takes a signed int64_t, the code decides at + // run-time whether to treat the argument as signed (int64_t) or as unsigned + // (uint64_t) based on the value of |sign|. + // + // It supports |base|s 2 through 16. Only a |base| of 10 is allowed to have + // a |sign|. Otherwise, |i| is treated as unsigned. + // + // For bases larger than 10, |upcase| decides whether lower-case or upper- + // case letters should be used to designate digits greater than 10. + // + // Padding can be done with either '0' zeros or ' ' spaces. Padding has to + // be positive and will always be applied to the left of the output. + // + // Prepends a |prefix| to the number (e.g. "0x"). This prefix goes to + // the left of |padding|, if |pad| is '0'; and to the right of |padding| + // if |pad| is ' '. + // + // Returns "false", if the |buffer_| overflowed at any time. + bool IToASCII(bool sign, + bool upcase, + int64_t i, + size_t base, + char pad, + size_t padding, + const char* prefix); + + private: + // Increments |count_| by |inc| unless this would cause |count_| to + // overflow |kSSizeMax-1|. Returns "false", iff an overflow was detected; + // it then clamps |count_| to |kSSizeMax-1|. + inline bool IncrementCount(size_t inc) { + // "inc" is either 1 or a "padding" value. Padding is clamped at + // run-time to at most kSSizeMax-1. So, we know that "inc" is always in + // the range 1..kSSizeMax-1. + // This allows us to compute "kSSizeMax - 1 - inc" without incurring any + // integer overflows. + DEBUG_CHECK(inc <= kSSizeMax - 1); + if (count_ > kSSizeMax - 1 - inc) { + count_ = kSSizeMax - 1; + return false; + } + count_ += inc; + return true; + } + + // Convenience method for the common case of incrementing |count_| by one. + inline bool IncrementCountByOne() { return IncrementCount(1); } + + // Return the current insertion point into the buffer. This is typically + // at |buffer_| + |count_|, but could be before that if truncation + // happened. It always points to one byte past the last byte that was + // successfully placed into the |buffer_|. + inline char* GetInsertionPoint() const { + size_t idx = count_; + if (idx > size_) { + idx = size_; + } + return buffer_ + idx; + } + + // User-provided buffer that will receive the fully formatted output string. + char* buffer_; + + // Number of bytes that are available in the buffer excluding the trailing + // NUL byte that will be added by the destructor. + const size_t size_; + + // Number of bytes that would have been emitted to the buffer, if the buffer + // was sufficiently big. This number always excludes the trailing NUL byte + // and it is guaranteed to never grow bigger than kSSizeMax-1. + size_t count_; +}; + +bool Buffer::IToASCII(bool sign, + bool upcase, + int64_t i, + size_t base, + char pad, + size_t padding, + const char* prefix) { + // Sanity check for parameters. None of these should ever fail, but see + // above for the rationale why we can't call CHECK(). + DEBUG_CHECK(base >= 2); + DEBUG_CHECK(base <= 16); + DEBUG_CHECK(!sign || base == 10); + DEBUG_CHECK(pad == '0' || pad == ' '); + DEBUG_CHECK(padding <= kSSizeMax); + DEBUG_CHECK(!(sign && prefix && *prefix)); + + // Handle negative numbers, if the caller indicated that |i| should be + // treated as a signed number; otherwise treat |i| as unsigned (even if the + // MSB is set!) + // Details are tricky, because of limited data-types, but equivalent pseudo- + // code would look like: + // if (sign && i < 0) + // prefix = "-"; + // num = abs(i); + size_t minint = 0; + uint64_t num; + if (sign && i < 0) { + prefix = "-"; + + // Turn our number positive. + if (i == std::numeric_limits::min()) { + // The most negative integer needs special treatment. + minint = 1; + num = static_cast(-(i + 1)); + } else { + // "Normal" negative numbers are easy. + num = static_cast(-i); + } + } else { + num = static_cast(i); + } + + // If padding with '0' zero, emit the prefix or '-' character now. Otherwise, + // make the prefix accessible in reverse order, so that we can later output + // it right between padding and the number. + // We cannot choose the easier approach of just reversing the number, as that + // fails in situations where we need to truncate numbers that have padding + // and/or prefixes. + const char* reverse_prefix = nullptr; + if (prefix && *prefix) { + if (pad == '0') { + while (*prefix) { + if (padding) { + --padding; + } + Out(*prefix++); + } + prefix = nullptr; + } else { + for (reverse_prefix = prefix; *reverse_prefix; ++reverse_prefix) { + } + } + } else { + prefix = nullptr; + } + const size_t prefix_length = static_cast(reverse_prefix - prefix); + + // Loop until we have converted the entire number. Output at least one + // character (i.e. '0'). + size_t start = count_; + size_t discarded = 0; + bool started = false; + do { + // Make sure there is still enough space left in our output buffer. + if (count_ >= size_) { + if (start < size_) { + // It is rare that we need to output a partial number. But if asked + // to do so, we will still make sure we output the correct number of + // leading digits. + // Since we are generating the digits in reverse order, we actually + // have to discard digits in the order that we have already emitted + // them. This is essentially equivalent to: + // memmove(buffer_ + start, buffer_ + start + 1, size_ - start - 1) + for (char *move = buffer_ + start, *end = buffer_ + size_ - 1; + move < end; ++move) { + *move = move[1]; + } + ++discarded; + --count_; + } else if (count_ - size_ > 1) { + // Need to increment either |count_| or |discarded| to make progress. + // The latter is more efficient, as it eventually triggers fast + // handling of padding. But we have to ensure we don't accidentally + // change the overall state (i.e. switch the state-machine from + // discarding to non-discarding). |count_| needs to always stay + // bigger than |size_|. + --count_; + ++discarded; + } + } + + // Output the next digit and (if necessary) compensate for the most + // negative integer needing special treatment. This works because, + // no matter the bit width of the integer, the lowest-most decimal + // integer always ends in 2, 4, 6, or 8. + if (!num && started) { + if (reverse_prefix > prefix) { + Out(*--reverse_prefix); + } else { + Out(pad); + } + } else { + started = true; + Out((upcase ? kUpCaseHexDigits + : kDownCaseHexDigits)[num % base + minint]); + } + + minint = 0; + num /= base; + + // Add padding, if requested. + if (padding > 0) { + --padding; + + // Performance optimization for when we are asked to output excessive + // padding, but our output buffer is limited in size. Even if we output + // a 64bit number in binary, we would never write more than 64 plus + // prefix non-padding characters. So, once this limit has been passed, + // any further state change can be computed arithmetically; we know that + // by this time, our entire final output consists of padding characters + // that have all already been output. + if (discarded > 8 * sizeof(num) + prefix_length) { + IncrementCount(padding); + padding = 0; + } + } + } while (num || padding || (reverse_prefix > prefix)); + + if (start < size_) { + // 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). + char* front = buffer_ + start; + char* back = GetInsertionPoint(); + while (--back > front) { + char ch = *back; + *back = *front; + *front++ = ch; + } + } + IncrementCount(discarded); + return !discarded; +} + +} // anonymous namespace + +namespace internal { + +ssize_t SafeSNPrintf(char* buf, + size_t sz, + const char* fmt, + const Arg* args, + const size_t max_args) { + // Make sure that at least one NUL byte can be written, and that the buffer + // never overflows kSSizeMax. Not only does that use up most or all of the + // address space, it also would result in a return code that cannot be + // represented. + if (static_cast(sz) < 1) { + return -1; + } + sz = std::min(sz, kSSizeMax); + + // Iterate over format string and interpret '%' arguments as they are + // encountered. + Buffer buffer(buf, sz); + size_t padding; + char pad; + for (unsigned int cur_arg = 0; *fmt && !buffer.OutOfAddressableSpace();) { + if (*fmt++ == '%') { + padding = 0; + pad = ' '; + char ch = *fmt++; + format_character_found: + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + // Found a width parameter. Convert to an integer value and store in + // "padding". If the leading digit is a zero, change the padding + // character from a space ' ' to a zero '0'. + pad = ch == '0' ? '0' : ' '; + for (;;) { + const size_t digit = static_cast(ch - '0'); + // The maximum allowed padding fills all the available address + // space and leaves just enough space to insert the trailing NUL. + const size_t max_padding = kSSizeMax - 1; + if (padding > max_padding / 10 || + 10 * padding > max_padding - digit) { + DEBUG_CHECK(padding <= max_padding / 10 && + 10 * padding <= max_padding - digit); + // Integer overflow detected. Skip the rest of the width until + // we find the format character, then do the normal error + // handling. + padding_overflow: + padding = max_padding; + while ((ch = *fmt++) >= '0' && ch <= '9') { + } + if (cur_arg < max_args) { + ++cur_arg; + } + goto fail_to_expand; + } + padding = 10 * padding + digit; + if (padding > max_padding) { + // This doesn't happen for "sane" values of kSSizeMax. But once + // kSSizeMax gets smaller than about 10, our earlier range checks + // are incomplete. Unittests do trigger this artificial corner + // case. + DEBUG_CHECK(padding <= max_padding); + goto padding_overflow; + } + ch = *fmt++; + if (ch < '0' || ch > '9') { + // Reached the end of the width parameter. This is where the + // format character is found. + goto format_character_found; + } + } + case 'c': { // Output an ASCII character. + // Check that there are arguments left to be inserted. + if (cur_arg >= max_args) { + DEBUG_CHECK(cur_arg < max_args); + goto fail_to_expand; + } + + // Check that the argument has the expected type. + const Arg& arg = args[cur_arg++]; + if (arg.type != Arg::INT && arg.type != Arg::UINT) { + DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT); + goto fail_to_expand; + } + + // Apply padding, if needed. + buffer.Pad(' ', padding, 1); + + // Convert the argument to an ASCII character and output it. + char as_char = static_cast(arg.integer.i); + if (!as_char) { + goto end_of_output_buffer; + } + buffer.Out(as_char); + break; + } + case 'd': // Output a possibly signed decimal value. + case 'o': // Output an unsigned octal value. + case 'x': // Output an unsigned hexadecimal value. + case 'X': + case 'p': { // Output a pointer value. + // Check that there are arguments left to be inserted. + if (cur_arg >= max_args) { + DEBUG_CHECK(cur_arg < max_args); + goto fail_to_expand; + } + + const Arg& arg = args[cur_arg++]; + int64_t i; + const char* prefix = nullptr; + if (ch != 'p') { + // Check that the argument has the expected type. + if (arg.type != Arg::INT && arg.type != Arg::UINT) { + DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT); + goto fail_to_expand; + } + i = arg.integer.i; + + if (ch != 'd') { + // The Arg() constructor automatically performed sign expansion on + // signed parameters. This is great when outputting a %d decimal + // number, but can result in unexpected leading 0xFF bytes when + // outputting a %x hexadecimal number. Mask bits, if necessary. + // We have to do this here, instead of in the Arg() constructor, + // as the Arg() constructor cannot tell whether we will output a + // %d or a %x. Only the latter should experience masking. + if (arg.integer.width < sizeof(int64_t)) { + i &= (1LL << (8 * arg.integer.width)) - 1; + } + } + } else { + // Pointer values require an actual pointer or a string. + if (arg.type == Arg::POINTER) { + i = static_cast(reinterpret_cast(arg.ptr)); + } else if (arg.type == Arg::STRING) { + i = static_cast(reinterpret_cast(arg.str)); + } else if (arg.type == Arg::INT && + arg.integer.width == sizeof(NULL) && + arg.integer.i == 0) { // Allow C++'s version of NULL + i = 0; + } else { + DEBUG_CHECK(arg.type == Arg::POINTER || arg.type == Arg::STRING); + goto fail_to_expand; + } + + // Pointers always include the "0x" prefix. + prefix = "0x"; + } + + // Use IToASCII() to convert to ASCII representation. For decimal + // numbers, optionally print a sign. For hexadecimal numbers, + // distinguish between upper and lower case. %p addresses are always + // printed as upcase. Supports base 8, 10, and 16. Prints padding + // and/or prefixes, if so requested. + buffer.IToASCII(ch == 'd' && arg.type == Arg::INT, ch != 'x', i, + ch == 'o' ? 8 + : ch == 'd' ? 10 + : 16, + pad, padding, prefix); + break; + } + case 's': { + // Check that there are arguments left to be inserted. + if (cur_arg >= max_args) { + DEBUG_CHECK(cur_arg < max_args); + goto fail_to_expand; + } + + // Check that the argument has the expected type. + const Arg& arg = args[cur_arg++]; + const char* s; + if (arg.type == Arg::STRING) { + s = arg.str ? arg.str : ""; + } else if (arg.type == Arg::INT && + arg.integer.width == sizeof(NULL) && + arg.integer.i == 0) { // Allow C++'s version of NULL + s = ""; + } else { + DEBUG_CHECK(arg.type == Arg::STRING); + goto fail_to_expand; + } + + // Apply padding, if needed. This requires us to first check the + // length of the string that we are outputting. + if (padding) { + size_t len = 0; + for (const char* src = s; *src++;) { + ++len; + } + buffer.Pad(' ', padding, len); + } + + // Printing a string involves nothing more than copying it into the + // output buffer and making sure we don't output more bytes than + // available space; Out() takes care of doing that. + for (const char* src = s; *src;) { + buffer.Out(*src++); + } + break; + } + case '%': + // Quoted percent '%' character. + goto copy_verbatim; + fail_to_expand: + // C++ gives us tools to do type checking -- something that snprintf() + // could never really do. So, whenever we see arguments that don't + // match up with the format string, we refuse to output them. But + // since we have to be extremely conservative about being async- + // signal-safe, we are limited in the type of error handling that we + // can do in production builds (in debug builds we can use + // DEBUG_CHECK() and hope for the best). So, all we do is pass the + // format string unchanged. That should eventually get the user's + // attention; and in the meantime, it hopefully doesn't lose too much + // data. + default: + // Unknown or unsupported format character. Just copy verbatim to + // output. + buffer.Out('%'); + DEBUG_CHECK(ch); + if (!ch) { + goto end_of_format_string; + } + buffer.Out(ch); + break; + } + } else { + copy_verbatim: + buffer.Out(fmt[-1]); + } + } +end_of_format_string: +end_of_output_buffer: + return buffer.GetCount(); +} + +} // namespace internal + +ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt) { + // Make sure that at least one NUL byte can be written, and that the buffer + // never overflows kSSizeMax. Not only does that use up most or all of the + // address space, it also would result in a return code that cannot be + // represented. + if (static_cast(sz) < 1) { + return -1; + } + sz = std::min(sz, kSSizeMax); + + Buffer buffer(buf, sz); + + // In the slow-path, we deal with errors by copying the contents of + // "fmt" unexpanded. This means, if there are no arguments passed, the + // SafeSPrintf() function always degenerates to a version of strncpy() that + // de-duplicates '%' characters. + const char* src = fmt; + for (; *src; ++src) { + buffer.Out(*src); + DEBUG_CHECK(src[0] != '%' || src[1] == '%'); + if (src[0] == '%' && src[1] == '%') { + ++src; + } + } + return buffer.GetCount(); +} + +} // namespace partition_alloc::internal::base::strings diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.h new file mode 100644 index 0000000000..8e405b7c30 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/strings/safe_sprintf.h @@ -0,0 +1,273 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_STRINGS_SAFE_SPRINTF_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_STRINGS_SAFE_SPRINTF_H_ + +#include +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" + +#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) +// For ssize_t +#include +#endif + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" + +namespace partition_alloc::internal::base::strings { + +#if defined(COMPILER_MSVC) +// Define ssize_t inside of our namespace. +#if defined(_WIN64) +typedef int64_t ssize_t; +#else +typedef long ssize_t; +#endif +#endif + +// SafeSPrintf() is a type-safe and completely self-contained version of +// snprintf(). +// +// SafeSNPrintf() is an alternative function signature that can be used when +// not dealing with fixed-sized buffers. When possible, SafeSPrintf() should +// always be used instead of SafeSNPrintf() +// +// These functions allow for formatting complicated messages from contexts that +// require strict async-signal-safety. In fact, it is safe to call them from +// any low-level execution context, as they are guaranteed to make no library +// or system calls. It deliberately never touches "errno", either. +// +// The only exception to this rule is that in debug builds the code calls +// RAW_CHECK() to help diagnose problems when the format string does not +// match the rest of the arguments. In release builds, no CHECK()s are used, +// and SafeSPrintf() instead returns an output string that expands only +// those arguments that match their format characters. Mismatched arguments +// are ignored. +// +// The code currently only supports a subset of format characters: +// %c, %o, %d, %x, %X, %p, and %s. +// +// SafeSPrintf() aims to be as liberal as reasonably possible. Integer-like +// values of arbitrary width can be passed to all of the format characters +// that expect integers. Thus, it is explicitly legal to pass an "int" to +// "%c", and output will automatically look at the LSB only. It is also +// explicitly legal to pass either signed or unsigned values, and the format +// characters will automatically interpret the arguments accordingly. +// +// It is still not legal to mix-and-match integer-like values with pointer +// values. For instance, you cannot pass a pointer to %x, nor can you pass an +// integer to %p. +// +// The one exception is "0" zero being accepted by "%p". This works-around +// the problem of C++ defining NULL as an integer-like value. +// +// All format characters take an optional width parameter. This must be a +// positive integer. For %d, %o, %x, %X and %p, if the width starts with +// a leading '0', padding is done with '0' instead of ' ' characters. +// +// There are a few features of snprintf()-style format strings, that +// SafeSPrintf() does not support at this time. +// +// If an actual user showed up, there is no particularly strong reason they +// couldn't be added. But that assumes that the trade-offs between complexity +// and utility are favorable. +// +// For example, adding support for negative padding widths, and for %n are all +// likely to be viewed positively. They are all clearly useful, low-risk, easy +// to test, don't jeopardize the async-signal-safety of the code, and overall +// have little impact on other parts of SafeSPrintf() function. +// +// On the other hands, adding support for alternate forms, positional +// arguments, grouping, wide characters, localization or floating point numbers +// are all unlikely to ever be added. +// +// SafeSPrintf() and SafeSNPrintf() mimic the behavior of snprintf() and they +// return the number of bytes needed to store the untruncated output. This +// does *not* include the terminating NUL byte. +// +// They return -1, iff a fatal error happened. This typically can only happen, +// if the buffer size is a) negative, or b) zero (i.e. not even the NUL byte +// can be written). The return value can never be larger than SSIZE_MAX-1. +// This ensures that the caller can always add one to the signed return code +// in order to determine the amount of storage that needs to be allocated. +// +// While the code supports type checking and while it is generally very careful +// to avoid printing incorrect values, it tends to be conservative in printing +// as much as possible, even when given incorrect parameters. Typically, in +// case of an error, the format string will not be expanded. (i.e. something +// like SafeSPrintf(buf, "%p %d", 1, 2) results in "%p 2"). See above for +// the use of RAW_CHECK() in debug builds, though. +// +// Basic example: +// char buf[20]; +// base::strings::SafeSPrintf(buf, "The answer: %2d", 42); +// +// Example with dynamically sized buffer (async-signal-safe). This code won't +// work on Visual studio, as it requires dynamically allocating arrays on the +// stack. Consider picking a smaller value for |kMaxSize| if stack size is +// limited and known. On the other hand, if the parameters to SafeSNPrintf() +// are trusted and not controllable by the user, you can consider eliminating +// the check for |kMaxSize| altogether. The current value of SSIZE_MAX is +// essentially a no-op that just illustrates how to implement an upper bound: +// const size_t kInitialSize = 128; +// const size_t kMaxSize = std::numeric_limits::max(); +// size_t size = kInitialSize; +// for (;;) { +// char buf[size]; +// size = SafeSNPrintf(buf, size, "Error message \"%s\"\n", err) + 1; +// if (sizeof(buf) < kMaxSize && size > kMaxSize) { +// size = kMaxSize; +// continue; +// } else if (size > sizeof(buf)) +// continue; +// write(2, buf, size-1); +// break; +// } + +namespace internal { +// Helpers that use C++ overloading, templates, and specializations to deduce +// and record type information from function arguments. This allows us to +// later write a type-safe version of snprintf(). + +struct Arg { + enum Type { INT, UINT, STRING, POINTER }; + + // Any integer-like value. + Arg(signed char c) : type(INT) { + integer.i = c; + integer.width = sizeof(char); + } + Arg(unsigned char c) : type(UINT) { + integer.i = c; + integer.width = sizeof(char); + } + Arg(signed short j) : type(INT) { + integer.i = j; + integer.width = sizeof(short); + } + Arg(unsigned short j) : type(UINT) { + integer.i = j; + integer.width = sizeof(short); + } + Arg(signed int j) : type(INT) { + integer.i = j; + integer.width = sizeof(int); + } + Arg(unsigned int j) : type(UINT) { + integer.i = j; + integer.width = sizeof(int); + } + Arg(signed long j) : type(INT) { + integer.i = j; + integer.width = sizeof(long); + } + Arg(unsigned long j) : type(UINT) { + integer.i = static_cast(j); + integer.width = sizeof(long); + } + Arg(signed long long j) : type(INT) { + integer.i = j; + integer.width = sizeof(long long); + } + Arg(unsigned long long j) : type(UINT) { + integer.i = static_cast(j); + integer.width = sizeof(long long); + } + + // std::nullptr_t would be ambiguous between char* and const char*; to get + // consistent behavior with NULL, which prints with all three of %d, %p, and + // %s, treat it as an integer zero internally. + // + // Warning: don't just do Arg(NULL) here because in some libcs, NULL is an + // alias for std::nullptr! + // + // NOLINTNEXTLINE(runtime/explicit) + Arg(std::nullptr_t p) : type(INT) { + integer.i = 0; + // Internally, SafeSprintf expects to represent nulls as integers whose + // width is equal to sizeof(NULL), which is not necessarily equal to + // sizeof(std::nullptr_t) - eg, on Windows, NULL is defined to 0 (with size + // 4) while std::nullptr_t is of size 8. + integer.width = sizeof(NULL); + } + + // A C-style text string. + Arg(const char* s) : str(s), type(STRING) {} + Arg(char* s) : str(s), type(STRING) {} + + // Any pointer value that can be cast to a "void*". + template + Arg(T* p) : ptr((void*)p), type(POINTER) {} + + struct Integer { + int64_t i; + unsigned char width; + }; + + union { + // An integer-like value. + Integer integer; + + // A C-style text string. + const char* str; + + // A pointer to an arbitrary object. + const void* ptr; + }; + const enum Type type; +}; + +// This is the internal function that performs the actual formatting of +// an snprintf()-style format string. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +ssize_t SafeSNPrintf(char* buf, + size_t sz, + const char* fmt, + const Arg* args, + size_t max_args); + +#if !defined(NDEBUG) +// In debug builds, allow unit tests to artificially lower the kSSizeMax +// constant that is used as a hard upper-bound for all buffers. In normal +// use, this constant should always be std::numeric_limits::max(). +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +void SetSafeSPrintfSSizeMaxForTest(size_t max); +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +size_t GetSafeSPrintfSSizeMaxForTest(); +#endif + +} // namespace internal + +template +ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args... args) { + // Use Arg() object to record type information and then copy arguments to an + // array to make it easier to iterate over them. + const internal::Arg arg_array[] = {args...}; + return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args)); +} + +template +ssize_t SafeSPrintf(char (&buf)[N], const char* fmt, Args... args) { + // Use Arg() object to record type information and then copy arguments to an + // array to make it easier to iterate over them. + const internal::Arg arg_array[] = {args...}; + return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args)); +} + +// Fast-path when we don't actually need to substitute any arguments. +PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) +ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt); +template +inline ssize_t SafeSPrintf(char (&buf)[N], const char* fmt) { + return SafeSNPrintf(buf, N, fmt); +} + +} // namespace partition_alloc::internal::base::strings + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_STRINGS_SAFE_SPRINTF_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h new file mode 100644 index 0000000000..ea08e727c0 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h @@ -0,0 +1,235 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_THREAD_ANNOTATIONS_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_THREAD_ANNOTATIONS_H_ + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h" +//#include "partition_alloc/partition_alloc_base/debug/debugging_buildflags.h" + +#if defined(__clang__) +#define PA_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define PA_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +// PA_GUARDED_BY() +// +// Documents if a shared field or global variable needs to be protected by a +// mutex. PA_GUARDED_BY() allows the user to specify a particular mutex that +// should be held when accessing the annotated variable. +// +// Example: +// +// Mutex mu; +// int p1 PA_GUARDED_BY(mu); +#define PA_GUARDED_BY(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +// PA_PT_GUARDED_BY() +// +// Documents if the memory location pointed to by a pointer should be guarded +// by a mutex when dereferencing the pointer. +// +// Example: +// Mutex mu; +// int *p1 PA_PT_GUARDED_BY(mu); +// +// Note that a pointer variable to a shared memory location could itself be a +// shared variable. +// +// Example: +// +// // `q`, guarded by `mu1`, points to a shared memory location that is +// // guarded by `mu2`: +// int *q PA_GUARDED_BY(mu1) PA_PT_GUARDED_BY(mu2); +#define PA_PT_GUARDED_BY(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +// PA_ACQUIRED_AFTER() / PA_ACQUIRED_BEFORE() +// +// Documents the acquisition order between locks that can be held +// simultaneously by a thread. For any two locks that need to be annotated +// to establish an acquisition order, only one of them needs the annotation. +// (i.e. You don't have to annotate both locks with both PA_ACQUIRED_AFTER +// and PA_ACQUIRED_BEFORE.) +// +// Example: +// +// Mutex m1; +// Mutex m2 PA_ACQUIRED_AFTER(m1); +#define PA_ACQUIRED_AFTER(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define PA_ACQUIRED_BEFORE(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +// PA_EXCLUSIVE_LOCKS_REQUIRED() / PA_SHARED_LOCKS_REQUIRED() +// +// Documents a function that expects a mutex to be held prior to entry. +// The mutex is expected to be held both on entry to, and exit from, the +// function. +// +// Example: +// +// Mutex mu1, mu2; +// int a PA_GUARDED_BY(mu1); +// int b PA_GUARDED_BY(mu2); +// +// void foo() PA_EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... }; +#define PA_EXCLUSIVE_LOCKS_REQUIRED(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) + +#define PA_SHARED_LOCKS_REQUIRED(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// PA_LOCKS_EXCLUDED() +// +// Documents the locks acquired in the body of the function. These locks +// cannot be held when calling this function (as Abseil's `Mutex` locks are +// non-reentrant). +#define PA_LOCKS_EXCLUDED(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// PA_LOCK_RETURNED() +// +// Documents a function that returns a mutex without acquiring it. For example, +// a public getter method that returns a pointer to a private mutex should +// be annotated with PA_LOCK_RETURNED. +#define PA_LOCK_RETURNED(x) PA_THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +// PA_LOCKABLE +// +// Documents if a class/type is a lockable type (such as the `Mutex` class). +#define PA_LOCKABLE PA_THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// PA_SCOPED_LOCKABLE +// +// Documents if a class does RAII locking (such as the `MutexLock` class). +// The constructor should use `PA_*_LOCK_FUNCTION()` to specify the mutex that +// is acquired, and the destructor should use `PA_UNLOCK_FUNCTION()` with no +// arguments; the analysis will assume that the destructor unlocks whatever the +// constructor locked. +#define PA_SCOPED_LOCKABLE PA_THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// PA_EXCLUSIVE_LOCK_FUNCTION() +// +// Documents functions that acquire a lock in the body of a function, and do +// not release it. +#define PA_EXCLUSIVE_LOCK_FUNCTION(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +// PA_SHARED_LOCK_FUNCTION() +// +// Documents functions that acquire a shared (reader) lock in the body of a +// function, and do not release it. +#define PA_SHARED_LOCK_FUNCTION(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +// PA_UNLOCK_FUNCTION() +// +// Documents functions that expect a lock to be held on entry to the function, +// and release it in the body of the function. +#define PA_UNLOCK_FUNCTION(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// PA_EXCLUSIVE_TRYLOCK_FUNCTION() / PA_SHARED_TRYLOCK_FUNCTION() +// +// Documents functions that try to acquire a lock, and return success or failure +// (or a non-boolean value that can be interpreted as a boolean). +// The first argument should be `true` for functions that return `true` on +// success, or `false` for functions that return `false` on success. The second +// argument specifies the mutex that is locked on success. If unspecified, this +// mutex is assumed to be `this`. +#define PA_EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +#define PA_SHARED_TRYLOCK_FUNCTION(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +// PA_ASSERT_EXCLUSIVE_LOCK() / PA_ASSERT_SHARED_LOCK() +// +// Documents functions that dynamically check to see if a lock is held, and fail +// if it is not held. +#define PA_ASSERT_EXCLUSIVE_LOCK(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) + +#define PA_ASSERT_SHARED_LOCK(...) \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) + +// PA_NO_THREAD_SAFETY_ANALYSIS +// +// Turns off thread safety checking within the body of a particular function. +// This annotation is used to mark functions that are known to be correct, but +// the locking behavior is more complicated than the analyzer can handle. +#define PA_NO_THREAD_SAFETY_ANALYSIS \ + PA_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +//------------------------------------------------------------------------------ +// Tool-Supplied Annotations +//------------------------------------------------------------------------------ + +// PA_TS_UNCHECKED should be placed around lock expressions that are not valid +// C++ syntax, but which are present for documentation purposes. These +// annotations will be ignored by the analysis. +#define PA_TS_UNCHECKED(x) "" + +// PA_TS_FIXME is used to mark lock expressions that are not valid C++ syntax. +// It is used by automated tools to mark and disable invalid expressions. +// The annotation should either be fixed, or changed to PA_TS_UNCHECKED. +#define PA_TS_FIXME(x) "" + +// Like PA_NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of +// a particular function. However, this attribute is used to mark functions +// that are incorrect and need to be fixed. It is used by automated tools to +// avoid breaking the build when the analysis is updated. +// Code owners are expected to eventually fix the routine. +#define PA_NO_THREAD_SAFETY_ANALYSIS_FIXME PA_NO_THREAD_SAFETY_ANALYSIS + +// Similar to PA_NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a +// PA_GUARDED_BY annotation that needs to be fixed, because it is producing +// thread safety warning. It disables the PA_GUARDED_BY. +#define PA_GUARDED_BY_FIXME(x) + +// Disables warnings for a single read operation. This can be used to avoid +// warnings when it is known that the read is not actually involved in a race, +// but the compiler cannot confirm that. +#define PA_TS_UNCHECKED_READ(x) \ + partition_alloc::internal::thread_safety_analysis::ts_unchecked_read(x) + +namespace partition_alloc::internal::thread_safety_analysis { + +// Takes a reference to a guarded data member, and returns an unguarded +// reference. +template +inline const T& ts_unchecked_read(const T& v) PA_NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +template +inline T& ts_unchecked_read(T& v) PA_NO_THREAD_SAFETY_ANALYSIS { + return v; +} + +} // namespace partition_alloc::internal::thread_safety_analysis + +// The above is imported as-is from abseil-cpp. The following Chromium-specific +// synonyms are added for Chromium concepts (SequenceChecker/ThreadChecker). +//#if PA_BUILDFLAG(PA_DCHECK_IS_ON) +// +//// Equivalent to PA_GUARDED_BY for SequenceChecker/ThreadChecker. Currently, +//#define PA_GUARDED_BY_CONTEXT(name) PA_GUARDED_BY(name) +// +//// Equivalent to PA_EXCLUSIVE_LOCKS_REQUIRED for SequenceChecker/ThreadChecker. +//#define PA_VALID_CONTEXT_REQUIRED(name) PA_EXCLUSIVE_LOCKS_REQUIRED(name) +// +//#else // PA_BUILDFLAG(PA_DCHECK_IS_ON) + +#define PA_GUARDED_BY_CONTEXT(name) +#define PA_VALID_CONTEXT_REQUIRED(name) + +//#endif // PA_BUILDFLAG(PA_DCHECK_IS_ON) + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_THREAD_ANNOTATIONS_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h new file mode 100644 index 0000000000..5aa10c9699 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h @@ -0,0 +1,51 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_PARTITION_ALLOC_BUILDFLAGS_H +#define WEBF_PARTITION_ALLOC_BUILDFLAGS_H + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" // IWYU pragma: export +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h" // IWYU pragma: export + +#define PA_BUILDFLAG_INTERNAL_USE_PARTITION_ALLOC() (1) +#define PA_BUILDFLAG_INTERNAL_PA_DCHECK_IS_ON() (0) +#define PA_BUILDFLAG_INTERNAL_PA_DCHECK_IS_CONFIGURABLE() (0) + +// https://chromium.googlesource.com/chromium/src/+/refs/tags/114.0.5719.0/build_overrides/partition_alloc.gni +#define PA_BUILDFLAG_INTERNAL_USE_RAW_PTR_BACKUP_REF_IMPL() (0) +#define PA_BUILDFLAG_INTERNAL_BACKUP_REF_PTR_EXTRA_OOB_CHECKS() (0) + +#define PA_BUILDFLAG_INTERNAL_USE_ASAN_BACKUP_REF_PTR() (0) +#define PA_BUILDFLAG_INTERNAL_HAS_64_BIT_POINTERS() (1) +#define PA_BUILDFLAG_INTERNAL_USE_RAW_PTR_ASAN_UNOWNED_IMPL() (0) +#define PA_BUILDFLAG_INTERNAL_USE_RAW_PTR_HOOKABLE_IMPL() (0) + +// - enable_backup_ref_ptr_slow_checks: enable additional safety checks that +// are too expensive to have on by default. +// - enable_dangling_raw_ptr_checks: enable checking raw_ptr do not become +// dangling during their lifetime. +// - backup_ref_ptr_poison_oob_ptr: poison out-of-bounds (OOB) pointers to +// generate an exception in the event that an OOB pointer is dereferenced. +// - enable_backup_ref_ptr_instance_tracer: use a global table to track all +// live raw_ptr/raw_ref instances to help debug dangling pointers at test +// end. +#define PA_BUILDFLAG_INTERNAL_ENABLE_BACKUP_REF_PTR_SLOW_CHECKS() (0) +#define PA_BUILDFLAG_INTERNAL_BACKUP_REF_PTR_POISON_OOB_PTR() (0) + +#define PA_BUILDFLAG_INTERNAL_HAS_MEMORY_TAGGING() (0) + +// Enables a compile-time check that all raw_ptrs to which arithmetic +// operations are to be applied are annotated with the AllowPtrArithmetic +// trait, +#define PA_BUILDFLAG_INTERNAL_ENABLE_POINTER_ARITHMETIC_TRAIT_CHECK() (1) + + +#define PA_BUILDFLAG_INTERNAL_RAW_PTR_ZERO_ON_CONSTRUCT() (1) +#define PA_BUILDFLAG_INTERNAL_RAW_PTR_ZERO_ON_MOVE() (1) +#define PA_BUILDFLAG_INTERNAL_RAW_PTR_ZERO_ON_DESTRUCT() (0) + +#define PA_BUILDFLAG_INTERNAL_ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER() (0) + +#endif // WEBF_PARTITION_ALLOC_BUILDFLAGS_H diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_forward.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_forward.h new file mode 100644 index 0000000000..5731a3ba13 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_forward.h @@ -0,0 +1,85 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_PARTITION_ALLOC_FORWARD_H +#define WEBF_PARTITION_ALLOC_FORWARD_H + + +#include +#include +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/thread_annotations.h" +//#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_config.h" + +namespace partition_alloc { + +namespace internal { + +// Alignment has two constraints: +// - Alignment requirement for scalar types: alignof(std::max_align_t) +// - Alignment requirement for operator new(). +// +// The two are separate on Windows 64 bits, where the first one is 8 bytes, and +// the second one 16. We could technically return something different for +// malloc() and operator new(), but this would complicate things, and most of +// our allocations are presumably coming from operator new() anyway. +constexpr size_t kAlignment = + std::max(alignof(max_align_t), + static_cast(__STDCPP_DEFAULT_NEW_ALIGNMENT__)); +static_assert(kAlignment <= 16, + "PartitionAlloc doesn't support a fundamental alignment larger " + "than 16 bytes."); + +struct SlotSpanMetadata; +class PA_LOCKABLE Lock; + +// This type trait verifies a type can be used as a pointer offset. +// +// We support pointer offsets in signed (ptrdiff_t) or unsigned (size_t) values. +// Smaller types are also allowed. +template +static constexpr bool is_offset_type = + std::is_integral_v && sizeof(Z) <= sizeof(ptrdiff_t); + +} // namespace internal + +class PartitionStatsDumper; + +struct PartitionRoot; + +namespace internal { +// Declare PartitionRootLock() for thread analysis. Its implementation +// is defined in partition_root.h. +Lock& PartitionRootLock(PartitionRoot*); +} // namespace internal + +} // namespace partition_alloc + +// From https://clang.llvm.org/docs/AttributeReference.html#malloc: +// +// The malloc attribute indicates that the function acts like a system memory +// allocation function, returning a pointer to allocated storage disjoint from +// the storage for any other object accessible to the caller. +// +// Note that it doesn't apply to realloc()-type functions, as they can return +// the same pointer as the one passed as a parameter, as noted in e.g. stdlib.h +// on Linux systems. +#if PA_HAS_ATTRIBUTE(malloc) +#define PA_MALLOC_FN __attribute__((malloc)) +#endif + +#if !defined(PA_MALLOC_FN) +#define PA_MALLOC_FN +#endif + +#if !defined(PA_MALLOC_ALIGNED) +#define PA_MALLOC_ALIGNED +#endif + +#endif // WEBF_PARTITION_ALLOC_FORWARD_H diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.cc new file mode 100644 index 0000000000..a31d4b6998 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.cc @@ -0,0 +1,6 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "instance_tracer.h" diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.h new file mode 100644 index 0000000000..42dc6cce63 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.h @@ -0,0 +1,102 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_POINTERS_INSTANCE_TRACER_H_ +#define PARTITION_ALLOC_POINTERS_INSTANCE_TRACER_H_ + +#include +#include +#include +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" + +namespace base::internal { + +#if !PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER) + +// When the buildflag is disabled, use a minimal no-state implementation so +// sizeof(raw_ptr) == sizeof(T*). +class InstanceTracer { + public: + constexpr uint64_t owner_id() const { return 0; } + + constexpr static void Trace([[maybe_unused]] uint64_t owner_id, + [[maybe_unused]] uintptr_t address) {} + constexpr static void Untrace([[maybe_unused]] uint64_t owner_id) {} +}; + +#else + +class PA_TRIVIAL_ABI InstanceTracer { + public: + constexpr InstanceTracer() : owner_id_(CreateOwnerId()) {} + + // Copy constructing InstanceTracer should not inherit the owner ID; the new + // InstanceTracer needs a new ID to be separately tracked. + constexpr InstanceTracer(const InstanceTracer&) : InstanceTracer() {} + // Similarly, copy assigning an InstanceTracer should not take the owner ID + // from the right-hand side; it should preserve the owner ID. + constexpr InstanceTracer& operator=(const InstanceTracer&) { return *this; } + + constexpr InstanceTracer(InstanceTracer&&) : InstanceTracer() {} + constexpr InstanceTracer& operator=(InstanceTracer&&) { return *this; } + + constexpr uint64_t owner_id() const { return owner_id_; } + + constexpr static void Trace(uint64_t owner_id, + bool may_dangle, + uintptr_t address) { + if (partition_alloc::internal::base::is_constant_evaluated() || + owner_id == 0) { + return; + } + TraceImpl(owner_id, may_dangle, address); + } + constexpr static void Untrace(uint64_t owner_id) { + if (partition_alloc::internal::base::is_constant_evaluated() || + owner_id == 0) { + return; + } + UntraceImpl(owner_id); + } + + PA_COMPONENT_EXPORT(RAW_PTR) + static std::vector> GetStackTracesForDanglingRefs( + uintptr_t allocation); + + PA_COMPONENT_EXPORT(RAW_PTR) + static std::vector> + GetStackTracesForAddressForTest(const void* address); + + private: + PA_COMPONENT_EXPORT(RAW_PTR) + static void TraceImpl(uint64_t owner_id, bool may_dangle, uintptr_t address); + PA_COMPONENT_EXPORT(RAW_PTR) static void UntraceImpl(uint64_t owner_id); + + constexpr uint64_t CreateOwnerId() { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return 0; + } + return ++counter_; + } + + PA_COMPONENT_EXPORT(RAW_PTR) static std::atomic counter_; + + // 0 is treated as 'ownerless'. It is used as a sentinel for constexpr + // raw_ptrs or other places where owner tracking doesn't make sense. + uint64_t owner_id_ = 0; +}; + +#endif + +} // namespace base::internal + +#endif // PARTITION_ALLOC_POINTERS_INSTANCE_TRACER_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h new file mode 100644 index 0000000000..3394c7149e --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h @@ -0,0 +1,1253 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_POINTERS_RAW_PTR_H_ +#define PARTITION_ALLOC_POINTERS_RAW_PTR_H_ + +#include +#include +#include +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/buildflag.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/flags.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/instance_tracer.h" + +#if BUILDFLAG(IS_WIN) +#include "partition_alloc/partition_alloc_base/win/win_handle_types.h" +#endif + +#if PA_BUILDFLAG(USE_PARTITION_ALLOC) +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/check.h" +// Live implementation of MiraclePtr being built. +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || \ + PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) +#define PA_RAW_PTR_CHECK(condition) PA_BASE_CHECK(condition) +#else +// No-op implementation of MiraclePtr being built. +// Note that `PA_BASE_DCHECK()` evaporates from non-DCHECK builds, +// minimizing impact of generated code. +#define PA_RAW_PTR_CHECK(condition) PA_BASE_DCHECK(condition) +#endif // PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || + // PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR) +#else // PA_BUILDFLAG(USE_PARTITION_ALLOC) +// Without PartitionAlloc, there's no `PA_BASE_D?CHECK()` implementation +// available. +#define PA_RAW_PTR_CHECK(condition) +#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC) + +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.h" +#elif PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) +#include "partition_alloc/pointers/raw_ptr_asan_unowned_impl.h" +#elif PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) +#include "partition_alloc/pointers/raw_ptr_hookable_impl.h" +#else +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_noop_impl.h" +#endif + +namespace cc { +class Scheduler; +} +namespace base::internal { +class DelayTimerBase; +class JobTaskSource; +} +namespace base::test { +struct RawPtrCountingImplForTest; +} +namespace content::responsiveness { +class Calculator; +} +namespace v8 { +class JobTask; +} +namespace blink::scheduler { +class MainThreadTaskQueue; +class NonMainThreadTaskQueue; +} // namespace blink::scheduler +namespace base::sequence_manager::internal { +class TaskQueueImpl; +} +namespace mojo { +class Connector; +} + +namespace partition_alloc::internal { + +// NOTE: All methods should be `PA_ALWAYS_INLINE`. raw_ptr is meant to be a +// lightweight replacement of a raw pointer, hence performance is critical. + +// This is a bitfield representing the different flags that can be applied to a +// raw_ptr. +// +// Internal use only: Developers shouldn't use those values directly. +// +// Housekeeping rules: Try not to change trait values, so that numeric trait +// values stay constant across builds (could be useful e.g. when analyzing stack +// traces). A reasonable exception to this rule are `*ForTest` traits. As a +// matter of fact, we propose that new non-test traits are added before the +// `*ForTest` traits. +enum class RawPtrTraits : unsigned { + kEmpty = 0, + + // Disables dangling pointer detection, but keeps other raw_ptr protections. + // + // Don't use directly, use DisableDanglingPtrDetection or DanglingUntriaged + // instead. + kMayDangle = (1 << 0), + + // Disables any hooks, when building with + // PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL). + // + // Internal use only. + kDisableHooks = (1 << 2), + + // Pointer arithmetic is discouraged and disabled by default. + // + // Don't use directly, use AllowPtrArithmetic instead. + kAllowPtrArithmetic = (1 << 3), + + // This pointer has BRP disabled for experimental rewrites of containers. + // + // Don't use directly. + kDisableBRP = (1 << 4), + + // Uninitialized pointers are discouraged and disabled by default. + // + // Don't use directly, use AllowUninitialized instead. + kAllowUninitialized = (1 << 5), + + // *** ForTest traits below *** + + // Adds accounting, on top of the NoOp implementation, for test purposes. + // raw_ptr/raw_ref with this trait perform extra bookkeeping, e.g. to track + // the number of times the raw_ptr is wrapped, unwrapped, etc. + // + // Test only. Include raw_ptr_counting_impl_for_test.h in your test + // files when using this trait. + kUseCountingImplForTest = (1 << 10), + + // Helper trait that can be used to test raw_ptr's behaviour or conversions. + // + // Test only. + kDummyForTest = (1 << 11), + + kAllMask = kMayDangle | kDisableHooks | kAllowPtrArithmetic | kDisableBRP | + kAllowUninitialized | kUseCountingImplForTest | kDummyForTest, +}; +// Template specialization to use |PA_DEFINE_OPERATORS_FOR_FLAGS| without +// |kMaxValue| declaration. +template <> +constexpr inline RawPtrTraits kAllFlags = RawPtrTraits::kAllMask; + +PA_DEFINE_OPERATORS_FOR_FLAGS(RawPtrTraits); + +} // namespace partition_alloc::internal + +namespace base { +using partition_alloc::internal::RawPtrTraits; + +namespace raw_ptr_traits { + +// IsSupportedType::value answers whether raw_ptr: +// 1) compiles +// 2) is safe at runtime +// +// Templates that may end up using raw_ptr should use IsSupportedType to ensure +// that raw_ptr is not used with unsupported types. As an example, see how +// base::internal::Unretained(Ref)Wrapper uses IsSupportedType to decide whether +// it should use `raw_ptr` or `T*`. +template +struct IsSupportedType { + static constexpr bool value = true; +}; + +// raw_ptr is not compatible with function pointer types. Also, they don't +// even need the raw_ptr protection, because they don't point on heap. +template +struct IsSupportedType>> { + static constexpr bool value = false; +}; + +// This section excludes some types from raw_ptr to avoid them from being +// used inside base::Unretained in performance sensitive places. +// The ones below were identified from sampling profiler data. See +// crbug.com/1287151 for more info. +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +// The ones below were identified from speedometer3. See crbug.com/335556942 for +// more info. +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; +template <> +struct IsSupportedType { + static constexpr bool value = false; +}; + +#if __OBJC__ +// raw_ptr is not compatible with pointers to Objective-C classes for a +// multitude of reasons. They may fail to compile in many cases, and wouldn't +// work well with tagged pointers. Anyway, Objective-C objects have their own +// way of tracking lifespan, hence don't need the raw_ptr protection as much. +// +// Such pointers are detected by checking if they're convertible to |id| type. +template +struct IsSupportedType>> { + static constexpr bool value = false; +}; +#endif // __OBJC__ + +#if BUILDFLAG(IS_WIN) +// raw_ptr is unsafe at runtime - if the handle happens to also +// represent a valid pointer into a PartitionAlloc-managed region then it can +// lead to manipulating random memory when treating it as BackupRefPtr +// ref-count. See also https://crbug.com/1262017. +// +// TODO(crbug.com/40799223): Cover other handle types like HANDLE, +// HLOCAL, HINTERNET, or HDEVINFO. Maybe we should avoid using raw_ptr when +// T=void (as is the case in these handle types). OTOH, explicit, +// non-template-based raw_ptr should be allowed. Maybe this can be solved +// by having 2 traits: IsPointeeAlwaysSafe (to be used in templates) and +// IsPointeeUsuallySafe (to be used in the static_assert in raw_ptr). The +// upside of this approach is that it will safely handle base::Bind closing over +// HANDLE. The downside of this approach is that base::Bind closing over a +// void* pointer will not get UaF protection. +#define PA_WINDOWS_HANDLE_TYPE(name) \ + template <> \ + struct IsSupportedType { \ + static constexpr bool value = false; \ + }; +#include "partition_alloc/partition_alloc_base/win/win_handle_types_list.inc" +#undef PA_WINDOWS_HANDLE_TYPE +#endif + +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) +template +using UnderlyingImplForTraits = internal::RawPtrBackupRefImpl< + /*AllowDangling=*/partition_alloc::internal::ContainsFlags( + Traits, + RawPtrTraits::kMayDangle), + /*DisableBRP=*/partition_alloc::internal::ContainsFlags( + Traits, + RawPtrTraits::kDisableBRP)>; + +#elif PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) +template +using UnderlyingImplForTraits = internal::RawPtrAsanUnownedImpl< + partition_alloc::internal::ContainsFlags(Traits, + RawPtrTraits::kAllowPtrArithmetic), + partition_alloc::internal::ContainsFlags(Traits, RawPtrTraits::kMayDangle)>; + +#elif PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) +template +using UnderlyingImplForTraits = internal::RawPtrHookableImpl< + /*EnableHooks=*/!partition_alloc::internal::ContainsFlags( + Traits, + RawPtrTraits::kDisableHooks)>; + +#else +template +using UnderlyingImplForTraits = internal::RawPtrNoOpImpl; +#endif + +constexpr bool IsPtrArithmeticAllowed([[maybe_unused]] RawPtrTraits Traits) { +#if PA_BUILDFLAG(ENABLE_POINTER_ARITHMETIC_TRAIT_CHECK) + return partition_alloc::internal::ContainsFlags( + Traits, RawPtrTraits::kAllowPtrArithmetic); +#else + return true; +#endif +} + +// ImplForTraits is the struct that implements raw_ptr functions. Think of +// raw_ptr as a thin wrapper, that directs calls to ImplForTraits. ImplForTraits +// may be different from UnderlyingImplForTraits, because it may select a +// test impl instead. +template +using ImplForTraits = + std::conditional_t>; + +// `kTypeTraits` is a customization interface to accosiate `T` with some +// `RawPtrTraits`. Users may create specialization of this variable +// to enable some traits by default. +// Note that specialization must be declared before the first use that would +// cause implicit instantiation of `raw_ptr` or `raw_ref`, in every translation +// unit where such use occurs. +template +constexpr inline auto kTypeTraits = RawPtrTraits::kEmpty; + +} // namespace raw_ptr_traits + +// `raw_ptr` is a non-owning smart pointer that has improved memory-safety +// over raw pointers. See the documentation for details: +// https://source.chromium.org/chromium/chromium/src/+/main:base/memory/raw_ptr.md +// +// raw_ptr is marked as [[gsl::Pointer]] which allows the compiler to catch +// some bugs where the raw_ptr holds a dangling pointer to a temporary object. +// However the [[gsl::Pointer]] analysis expects that such types do not have a +// non-default move constructor/assignment. Thus, it's possible to get an error +// where the pointer is not actually dangling, and have to work around the +// compiler. We have not managed to construct such an example in Chromium yet. +template +class PA_TRIVIAL_ABI PA_GSL_POINTER raw_ptr { + public: + // Users may specify `RawPtrTraits` via raw_ptr's second template parameter + // `PointerTraits`, or specialization of `raw_ptr_traits::kTypeTraits`. + constexpr static auto Traits = PointerTraits | raw_ptr_traits::kTypeTraits; + using Impl = typename raw_ptr_traits::ImplForTraits; + // Needed to make gtest Pointee matcher work with raw_ptr. + using element_type = T; + using DanglingType = raw_ptr; + +#if !PA_BUILDFLAG(USE_PARTITION_ALLOC) + // See comment at top about `PA_RAW_PTR_CHECK()`. + static_assert(std::is_same_v); +#endif // !PA_BUILDFLAG(USE_PARTITION_ALLOC) + + static_assert(partition_alloc::internal::AreValidFlags(Traits), + "Unknown raw_ptr trait(s)"); + static_assert(raw_ptr_traits::IsSupportedType::value, + "raw_ptr doesn't work with this kind of pointee type T"); + + static constexpr bool kZeroOnConstruct = + Impl::kMustZeroOnConstruct || (PA_BUILDFLAG(RAW_PTR_ZERO_ON_CONSTRUCT) && + !partition_alloc::internal::ContainsFlags( + Traits, + RawPtrTraits::kAllowUninitialized)); + static constexpr bool kZeroOnMove = + Impl::kMustZeroOnMove || PA_BUILDFLAG(RAW_PTR_ZERO_ON_MOVE); + static constexpr bool kZeroOnDestruct = + Impl::kMustZeroOnDestruct || PA_BUILDFLAG(RAW_PTR_ZERO_ON_DESTRUCT); + +// A non-trivial default ctor is required for complex implementations (e.g. +// BackupRefPtr), or even for NoOpImpl when zeroing is requested. +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) || \ + PA_BUILDFLAG(RAW_PTR_ZERO_ON_CONSTRUCT) + PA_ALWAYS_INLINE constexpr raw_ptr() noexcept { + if constexpr (kZeroOnConstruct) { + wrapped_ptr_ = nullptr; + } + } +#else + // raw_ptr can be trivially default constructed (leaving |wrapped_ptr_| + // uninitialized). + PA_ALWAYS_INLINE constexpr raw_ptr() noexcept = default; + static_assert(!kZeroOnConstruct); +#endif // PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) || + // PA_BUILDFLAG(RAW_PTR_ZERO_ON_CONSTRUCT) + +// A non-trivial copy ctor and assignment operator are required for complex +// implementations (e.g. BackupRefPtr). Unlike the blocks around, we don't need +// these for NoOpImpl even when zeroing is requested; better to keep them +// trivial. +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) + PA_ALWAYS_INLINE constexpr raw_ptr(const raw_ptr& p) noexcept + : wrapped_ptr_(Impl::Duplicate(p.wrapped_ptr_)) { + Impl::Trace(tracer_.owner_id(), p.wrapped_ptr_); + } + + PA_ALWAYS_INLINE constexpr raw_ptr& operator=(const raw_ptr& p) noexcept { + // Duplicate before releasing, in case the pointer is assigned to itself. + // + // Unlike the move version of this operator, don't add |this != &p| branch, + // for performance reasons. Self-assignment is rare, so unconditionally + // calling `Duplicate()` is almost certainly cheaper than adding an + // additional branch, even if always correctly predicted. + T* new_ptr = Impl::Duplicate(p.wrapped_ptr_); + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + wrapped_ptr_ = new_ptr; + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + return *this; + } +#else + PA_ALWAYS_INLINE raw_ptr(const raw_ptr&) noexcept = default; + PA_ALWAYS_INLINE raw_ptr& operator=(const raw_ptr&) noexcept = default; +#endif // PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) + +// A non-trivial move ctor and assignment operator are required for complex +// implementations (e.g. BackupRefPtr), or even for NoOpImpl when zeroing is +// requested. +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) || \ + PA_BUILDFLAG(RAW_PTR_ZERO_ON_MOVE) + PA_ALWAYS_INLINE constexpr raw_ptr(raw_ptr&& p) noexcept { + wrapped_ptr_ = p.wrapped_ptr_; + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + if constexpr (kZeroOnMove) { + p.wrapped_ptr_ = nullptr; + Impl::Untrace(p.tracer_.owner_id()); + } + } + PA_ALWAYS_INLINE constexpr raw_ptr& operator=(raw_ptr&& p) noexcept { + // Unlike the the copy version of this operator, this branch is necessary + // for correctness. + if (PA_LIKELY(this != &p)) { + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + wrapped_ptr_ = p.wrapped_ptr_; + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + if constexpr (kZeroOnMove) { + p.wrapped_ptr_ = nullptr; + Impl::Untrace(p.tracer_.owner_id()); + } + } + return *this; + } +#else + PA_ALWAYS_INLINE raw_ptr(raw_ptr&&) noexcept = default; + PA_ALWAYS_INLINE raw_ptr& operator=(raw_ptr&&) noexcept = default; + static_assert(!kZeroOnMove); +#endif // PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) || + // PA_BUILDFLAG(RAW_PTR_ZERO_ON_MOVE) + +// A non-trivial default dtor is required for complex implementations (e.g. +// BackupRefPtr), or even for NoOpImpl when zeroing is requested. +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || \ + PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) || \ + PA_BUILDFLAG(RAW_PTR_ZERO_ON_DESTRUCT) + PA_ALWAYS_INLINE PA_CONSTEXPR_DTOR ~raw_ptr() noexcept { + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + // Work around external issues where raw_ptr is used after destruction. + if constexpr (kZeroOnDestruct) { + wrapped_ptr_ = nullptr; + } + } +#else + PA_ALWAYS_INLINE ~raw_ptr() noexcept = default; + static_assert(!kZeroOnDestruct); +#endif // PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_ASAN_UNOWNED_IMPL) || + // PA_BUILDFLAG(USE_RAW_PTR_HOOKABLE_IMPL) || + // PA_BUILDFLAG(RAW_PTR_ZERO_ON_DESTRUCT) + + // Cross-kind copy constructor. + // Move is not supported as different traits may use different ref-counts, so + // let move operations degrade to copy, which handles it well. + template > + PA_ALWAYS_INLINE constexpr explicit raw_ptr( + const raw_ptr& p) noexcept + : wrapped_ptr_(Impl::WrapRawPtrForDuplication( + raw_ptr_traits::ImplForTraits::Traits>:: + UnsafelyUnwrapPtrForDuplication(p.wrapped_ptr_))) { + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + // Limit cross-kind conversions only to cases where `kMayDangle` gets added, + // because that's needed for ExtractAsDangling() and Unretained(Ref)Wrapper. + // Use a static_assert, instead of disabling via SFINAE, so that the + // compiler catches other conversions. Otherwise the implicits + // `raw_ptr -> T* -> raw_ptr<>` route will be taken. + static_assert(Traits == (raw_ptr::Traits | + RawPtrTraits::kMayDangle)); + } + + // Cross-kind assignment. + // Move is not supported as different traits may use different ref-counts, so + // let move operations degrade to copy, which handles it well. + template > + PA_ALWAYS_INLINE constexpr raw_ptr& operator=( + const raw_ptr& p) noexcept { + // Limit cross-kind assignments only to cases where `kMayDangle` gets added, + // because that's needed for ExtractAsDangling() and Unretained(Ref)Wrapper. + // Use a static_assert, instead of disabling via SFINAE, so that the + // compiler catches other conversions. Otherwise the implicit + // `raw_ptr -> T* -> raw_ptr<>` route will be taken. + static_assert(Traits == (raw_ptr::Traits | + RawPtrTraits::kMayDangle)); + + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + wrapped_ptr_ = Impl::WrapRawPtrForDuplication( + raw_ptr_traits::ImplForTraits::Traits>:: + UnsafelyUnwrapPtrForDuplication(p.wrapped_ptr_)); + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + return *this; + } + + // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. + // Ignore kZeroOnConstruct, because here the caller explicitly wishes to + // initialize with nullptr. + // NOLINTNEXTLINE(google-explicit-constructor) + PA_ALWAYS_INLINE constexpr raw_ptr(std::nullptr_t) noexcept + : wrapped_ptr_(nullptr) {} + + // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. + // NOLINTNEXTLINE(google-explicit-constructor) + PA_ALWAYS_INLINE constexpr raw_ptr(T* p) noexcept + : wrapped_ptr_(Impl::WrapRawPtr(p)) { + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + } + + // Deliberately implicit in order to support implicit upcast. + template && + !std::is_void_v::type>>> + // NOLINTNEXTLINE(google-explicit-constructor) + PA_ALWAYS_INLINE constexpr raw_ptr(const raw_ptr& ptr) noexcept + : wrapped_ptr_( + Impl::Duplicate(Impl::template Upcast(ptr.wrapped_ptr_))) { + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + } + // Deliberately implicit in order to support implicit upcast. + template && + !std::is_void_v::type>>> + // NOLINTNEXTLINE(google-explicit-constructor) + PA_ALWAYS_INLINE constexpr raw_ptr(raw_ptr&& ptr) noexcept + : wrapped_ptr_(Impl::template Upcast(ptr.wrapped_ptr_)) { + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + if constexpr (kZeroOnMove) { + ptr.wrapped_ptr_ = nullptr; + Impl::Untrace(ptr.tracer_.owner_id()); + } + } + + PA_ALWAYS_INLINE constexpr raw_ptr& operator=(std::nullptr_t) noexcept { + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + wrapped_ptr_ = nullptr; + return *this; + } + PA_ALWAYS_INLINE constexpr raw_ptr& operator=(T* p) noexcept { + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + wrapped_ptr_ = Impl::WrapRawPtr(p); + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + return *this; + } + + // Upcast assignment + template && + !std::is_void_v::type>>> + PA_ALWAYS_INLINE constexpr raw_ptr& operator=( + const raw_ptr& ptr) noexcept { + // Make sure that pointer isn't assigned to itself (look at raw_ptr address, + // not its contained pointer value). The comparison is only needed when they + // are the same type, otherwise they can't be the same raw_ptr object. +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + if constexpr (std::is_same_v>) { + PA_RAW_PTR_CHECK(this != &ptr); + } +#endif + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + wrapped_ptr_ = + Impl::Duplicate(Impl::template Upcast(ptr.wrapped_ptr_)); + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + return *this; + } + template && + !std::is_void_v::type>>> + PA_ALWAYS_INLINE constexpr raw_ptr& operator=( + raw_ptr&& ptr) noexcept { + // Make sure that pointer isn't assigned to itself (look at raw_ptr address, + // not its contained pointer value). The comparison is only needed when they + // are the same type, otherwise they can't be the same raw_ptr object. +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + if constexpr (std::is_same_v>) { + PA_RAW_PTR_CHECK(this != &ptr); + } +#endif + Impl::ReleaseWrappedPtr(wrapped_ptr_); + Impl::Untrace(tracer_.owner_id()); + wrapped_ptr_ = Impl::template Upcast(ptr.wrapped_ptr_); + Impl::Trace(tracer_.owner_id(), wrapped_ptr_); + if constexpr (kZeroOnMove) { + ptr.wrapped_ptr_ = nullptr; + Impl::Untrace(ptr.tracer_.owner_id()); + } + return *this; + } + + // Avoid using. The goal of raw_ptr is to be as close to raw pointer as + // possible, so use it only if absolutely necessary (e.g. for const_cast). + PA_ALWAYS_INLINE constexpr T* get() const { return GetForExtraction(); } + + // You may use |raw_ptr::AsEphemeralRawAddr()| to obtain |T**| or |T*&| + // from |raw_ptr|, as long as you follow these requirements: + // - DO NOT carry T**/T*& obtained via AsEphemeralRawAddr() out of + // expression. + // - DO NOT use raw_ptr or T**/T*& multiple times within an expression. + // + // https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md#in_out-arguments-need-to-be-refactored + class EphemeralRawAddr { + public: + EphemeralRawAddr(const EphemeralRawAddr&) = delete; + EphemeralRawAddr& operator=(const EphemeralRawAddr&) = delete; + void* operator new(size_t) = delete; + void* operator new(size_t, void*) = delete; + PA_ALWAYS_INLINE PA_CONSTEXPR_DTOR ~EphemeralRawAddr() { original = copy; } + + PA_ALWAYS_INLINE constexpr T** operator&() && PA_LIFETIME_BOUND { + return © + } + // NOLINTNEXTLINE(google-explicit-constructor) + PA_ALWAYS_INLINE constexpr operator T*&() && PA_LIFETIME_BOUND { + return copy; + } + + private: + friend class raw_ptr; + PA_ALWAYS_INLINE constexpr explicit EphemeralRawAddr(raw_ptr& ptr) + : copy(ptr.get()), original(ptr) {} + T* copy; + raw_ptr& original; // Original pointer. + }; + PA_ALWAYS_INLINE PA_CONSTEXPR_DTOR EphemeralRawAddr AsEphemeralRawAddr() & { + return EphemeralRawAddr(*this); + } + + PA_ALWAYS_INLINE constexpr explicit operator bool() const { + return !!wrapped_ptr_; + } + + template ::type>>> + PA_ALWAYS_INLINE constexpr U& operator*() const { + return *GetForDereference(); + } + PA_ALWAYS_INLINE constexpr T* operator->() const { + return GetForDereference(); + } + + // Deliberately implicit, because raw_ptr is supposed to resemble raw ptr. + // NOLINTNEXTLINE(google-explicit-constructor) + PA_ALWAYS_INLINE constexpr operator T*() const { return GetForExtraction(); } + template + PA_ALWAYS_INLINE constexpr explicit operator U*() const { + // This operator may be invoked from static_cast, meaning the types may not + // be implicitly convertible, hence the need for static_cast here. + return static_cast(GetForExtraction()); + } + + PA_ALWAYS_INLINE constexpr raw_ptr& operator++() { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot increment raw_ptr unless AllowPtrArithmetic trait is present."); + wrapped_ptr_ = Impl::Advance(wrapped_ptr_, 1, true); + return *this; + } + PA_ALWAYS_INLINE constexpr raw_ptr& operator--() { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot decrement raw_ptr unless AllowPtrArithmetic trait is present."); + wrapped_ptr_ = Impl::Retreat(wrapped_ptr_, 1, true); + return *this; + } + PA_ALWAYS_INLINE constexpr raw_ptr operator++(int /* post_increment */) { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot increment raw_ptr unless AllowPtrArithmetic trait is present."); + raw_ptr result = *this; + ++(*this); + return result; + } + PA_ALWAYS_INLINE constexpr raw_ptr operator--(int /* post_decrement */) { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot decrement raw_ptr unless AllowPtrArithmetic trait is present."); + raw_ptr result = *this; + --(*this); + return result; + } + template < + typename Z, + typename = std::enable_if_t>> + PA_ALWAYS_INLINE constexpr raw_ptr& operator+=(Z delta_elems) { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot increment raw_ptr unless AllowPtrArithmetic trait is present."); + wrapped_ptr_ = Impl::Advance(wrapped_ptr_, delta_elems, true); + return *this; + } + template < + typename Z, + typename = std::enable_if_t>> + PA_ALWAYS_INLINE constexpr raw_ptr& operator-=(Z delta_elems) { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot decrement raw_ptr unless AllowPtrArithmetic trait is present."); + wrapped_ptr_ = Impl::Retreat(wrapped_ptr_, delta_elems, true); + return *this; + } + + template ::type> && + partition_alloc::internal::is_offset_type>> + PA_ALWAYS_INLINE constexpr U& operator[](Z delta_elems) const { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot index raw_ptr unless AllowPtrArithmetic trait is present."); + // Call SafelyUnwrapPtrForDereference() to simulate what GetForDereference() + // does, but without creating a temporary. + return *Impl::SafelyUnwrapPtrForDereference( + Impl::Advance(wrapped_ptr_, delta_elems, false)); + } + + // Do not disable operator+() and operator-(). + // They provide OOB checks, which prevent from assigning an arbitrary value to + // raw_ptr, leading BRP to modifying arbitrary memory thinking it's ref-count. + // Keep them enabled, which may be blocked later when attempting to apply the + // += or -= operation, when disabled. In the absence of operators +/-, the + // compiler is free to implicitly convert to the underlying T* representation + // and perform ordinary pointer arithmetic, thus invalidating the purpose + // behind disabling them. + // + // For example, disabling these when `!is_offset_type` would remove the + // operators for Z=uint64_t on 32-bit systems. The compiler instead would + // generate code that converts `raw_ptr` to `T*` and adds uint64_t to that, + // bypassing the OOB protection entirely. + template + PA_ALWAYS_INLINE friend constexpr raw_ptr operator+(const raw_ptr& p, + Z delta_elems) { + // Don't check `is_offset_type` here, as existence of `Advance` is + // already gated on that, and we'd get double errors. + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot add to raw_ptr unless AllowPtrArithmetic trait is present."); + raw_ptr result = Impl::Advance(p.wrapped_ptr_, delta_elems, false); + return result; + } + template + PA_ALWAYS_INLINE friend constexpr raw_ptr operator+(Z delta_elems, + const raw_ptr& p) { + return p + delta_elems; + } + template + PA_ALWAYS_INLINE friend constexpr raw_ptr operator-(const raw_ptr& p, + Z delta_elems) { + // Don't check `is_offset_type` here, as existence of `Retreat` is + // already gated on that, and we'd get double errors. + static_assert(raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot subtract from raw_ptr unless AllowPtrArithmetic " + "trait is present."); + raw_ptr result = Impl::Retreat(p.wrapped_ptr_, delta_elems, false); + return result; + } + + // The "Do not disable operator+() and operator-()" comment above doesn't + // apply to the delta operator-() below. + PA_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(const raw_ptr& p1, + const raw_ptr& p2) { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot subtract raw_ptrs unless AllowPtrArithmetic trait is present."); + return Impl::GetDeltaElems(p1.wrapped_ptr_, p2.wrapped_ptr_); + } + PA_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(T* p1, + const raw_ptr& p2) { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot subtract raw_ptrs unless AllowPtrArithmetic trait is present."); + return Impl::GetDeltaElems(p1, p2.wrapped_ptr_); + } + PA_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(const raw_ptr& p1, + T* p2) { + static_assert( + raw_ptr_traits::IsPtrArithmeticAllowed(Traits), + "cannot subtract raw_ptrs unless AllowPtrArithmetic trait is present."); + return Impl::GetDeltaElems(p1.wrapped_ptr_, p2); + } + + // Stop referencing the underlying pointer and free its memory. Compared to + // raw delete calls, this avoids the raw_ptr to be temporarily dangling + // during the free operation, which will lead to taking the slower path that + // involves quarantine. + PA_ALWAYS_INLINE constexpr void ClearAndDelete() noexcept { + delete GetForExtractionAndReset(); + } + PA_ALWAYS_INLINE constexpr void ClearAndDeleteArray() noexcept { + delete[] GetForExtractionAndReset(); + } + + // Clear the underlying pointer and return another raw_ptr instance + // that is allowed to dangle. + // This can be useful in cases such as: + // ``` + // ptr.ExtractAsDangling()->SelfDestroy(); + // ``` + // ``` + // c_style_api_do_something_and_destroy(ptr.ExtractAsDangling()); + // ``` + // NOTE, avoid using this method as it indicates an error-prone memory + // ownership pattern. If possible, use smart pointers like std::unique_ptr<> + // instead of raw_ptr<>. + // If you have to use it, avoid saving the return value in a long-lived + // variable (or worse, a field)! It's meant to be used as a temporary, to be + // passed into a cleanup & freeing function, and destructed at the end of the + // statement. + PA_ALWAYS_INLINE constexpr DanglingType ExtractAsDangling() noexcept { + DanglingType res(std::move(*this)); + // Not all implementation clear the source pointer on move. Furthermore, + // even for implemtantions that do, cross-kind conversions (that add + // kMayDangle) fall back to a copy, instead of move. So do it here just in + // case. Should be cheap. + operator=(nullptr); + return res; + } + + // Comparison operators between raw_ptr and raw_ptr/U*/std::nullptr_t. + // Strictly speaking, it is not necessary to provide these: the compiler can + // use the conversion operator implicitly to allow comparisons to fall back to + // comparisons between raw pointers. However, `operator T*`/`operator U*` may + // perform safety checks with a higher runtime cost, so to avoid this, provide + // explicit comparison operators for all combinations of parameters. + + // Comparisons between `raw_ptr`s. This unusual declaration and separate + // definition below is because `GetForComparison()` is a private method. The + // more conventional approach of defining a comparison operator between + // `raw_ptr` and `raw_ptr` in the friend declaration itself does not work, + // because a comparison operator defined inline would not be allowed to call + // `raw_ptr`'s private `GetForComparison()` method. + template + friend constexpr bool operator==(const raw_ptr& lhs, + const raw_ptr& rhs); + template + friend constexpr bool operator!=(const raw_ptr& lhs, + const raw_ptr& rhs); + template + friend constexpr bool operator<(const raw_ptr& lhs, + const raw_ptr& rhs); + template + friend constexpr bool operator>(const raw_ptr& lhs, + const raw_ptr& rhs); + template + friend constexpr bool operator<=(const raw_ptr& lhs, + const raw_ptr& rhs); + template + friend constexpr bool operator>=(const raw_ptr& lhs, + const raw_ptr& rhs); + + // Comparisons with U*. These operators also handle the case where the RHS is + // T*. + template + PA_ALWAYS_INLINE friend constexpr bool operator==(const raw_ptr& lhs, + U* rhs) { + return lhs.GetForComparison() == rhs; + } + template + PA_ALWAYS_INLINE friend constexpr bool operator!=(const raw_ptr& lhs, + U* rhs) { + return !(lhs == rhs); + } + template + PA_ALWAYS_INLINE friend constexpr bool operator==(U* lhs, + const raw_ptr& rhs) { + return rhs == lhs; // Reverse order to call the operator above. + } + template + PA_ALWAYS_INLINE friend constexpr bool operator!=(U* lhs, + const raw_ptr& rhs) { + return rhs != lhs; // Reverse order to call the operator above. + } + template + PA_ALWAYS_INLINE friend constexpr bool operator<(const raw_ptr& lhs, U* rhs) { + return lhs.GetForComparison() < rhs; + } + template + PA_ALWAYS_INLINE friend constexpr bool operator<=(const raw_ptr& lhs, + U* rhs) { + return lhs.GetForComparison() <= rhs; + } + template + PA_ALWAYS_INLINE friend constexpr bool operator>(const raw_ptr& lhs, U* rhs) { + return lhs.GetForComparison() > rhs; + } + template + PA_ALWAYS_INLINE friend constexpr bool operator>=(const raw_ptr& lhs, + U* rhs) { + return lhs.GetForComparison() >= rhs; + } + template + PA_ALWAYS_INLINE friend constexpr bool operator<(U* lhs, const raw_ptr& rhs) { + return lhs < rhs.GetForComparison(); + } + template + PA_ALWAYS_INLINE friend constexpr bool operator<=(U* lhs, + const raw_ptr& rhs) { + return lhs <= rhs.GetForComparison(); + } + template + PA_ALWAYS_INLINE friend constexpr bool operator>(U* lhs, const raw_ptr& rhs) { + return lhs > rhs.GetForComparison(); + } + template + PA_ALWAYS_INLINE friend constexpr bool operator>=(U* lhs, + const raw_ptr& rhs) { + return lhs >= rhs.GetForComparison(); + } + + // Comparisons with `std::nullptr_t`. + PA_ALWAYS_INLINE friend constexpr bool operator==(const raw_ptr& lhs, + std::nullptr_t) { + return !lhs; + } + PA_ALWAYS_INLINE friend constexpr bool operator!=(const raw_ptr& lhs, + std::nullptr_t) { + return !!lhs; // Use !! otherwise the costly implicit cast will be used. + } + PA_ALWAYS_INLINE friend constexpr bool operator==(std::nullptr_t, + const raw_ptr& rhs) { + return !rhs; + } + PA_ALWAYS_INLINE friend constexpr bool operator!=(std::nullptr_t, + const raw_ptr& rhs) { + return !!rhs; // Use !! otherwise the costly implicit cast will be used. + } + + PA_ALWAYS_INLINE friend constexpr void swap(raw_ptr& lhs, + raw_ptr& rhs) noexcept { + Impl::IncrementSwapCountForTest(); + std::swap(lhs.wrapped_ptr_, rhs.wrapped_ptr_); + } + + PA_ALWAYS_INLINE void ReportIfDangling() const noexcept { +#if PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) + Impl::ReportIfDangling(wrapped_ptr_); +#endif + } + + private: + // This getter is meant for situations where the pointer is meant to be + // dereferenced. It is allowed to crash on nullptr (it may or may not), + // because it knows that the caller will crash on nullptr. + PA_ALWAYS_INLINE constexpr T* GetForDereference() const { + return Impl::SafelyUnwrapPtrForDereference(wrapped_ptr_); + } + // This getter is meant for situations where the raw pointer is meant to be + // extracted outside of this class, but not necessarily with an intention to + // dereference. It mustn't crash on nullptr. + PA_ALWAYS_INLINE constexpr T* GetForExtraction() const { + return Impl::SafelyUnwrapPtrForExtraction(wrapped_ptr_); + } + // This getter is meant *only* for situations where the pointer is meant to be + // compared (guaranteeing no dereference or extraction outside of this class). + // Any verifications can and should be skipped for performance reasons. + PA_ALWAYS_INLINE constexpr T* GetForComparison() const { + return Impl::UnsafelyUnwrapPtrForComparison(wrapped_ptr_); + } + + PA_ALWAYS_INLINE constexpr T* GetForExtractionAndReset() { + T* ptr = GetForExtraction(); + operator=(nullptr); + return ptr; + } + + T* wrapped_ptr_; + PA_NO_UNIQUE_ADDRESS internal::InstanceTracer tracer_; + + template + friend class raw_ptr; +}; + +template +PA_ALWAYS_INLINE constexpr bool operator==(const raw_ptr& lhs, + const raw_ptr& rhs) { + return lhs.GetForComparison() == rhs.GetForComparison(); +} + +template +PA_ALWAYS_INLINE constexpr bool operator!=(const raw_ptr& lhs, + const raw_ptr& rhs) { + return !(lhs == rhs); +} + +template +PA_ALWAYS_INLINE constexpr bool operator<(const raw_ptr& lhs, + const raw_ptr& rhs) { + return lhs.GetForComparison() < rhs.GetForComparison(); +} + +template +PA_ALWAYS_INLINE constexpr bool operator>(const raw_ptr& lhs, + const raw_ptr& rhs) { + return lhs.GetForComparison() > rhs.GetForComparison(); +} + +template +PA_ALWAYS_INLINE constexpr bool operator<=(const raw_ptr& lhs, + const raw_ptr& rhs) { + return lhs.GetForComparison() <= rhs.GetForComparison(); +} + +template +PA_ALWAYS_INLINE constexpr bool operator>=(const raw_ptr& lhs, + const raw_ptr& rhs) { + return lhs.GetForComparison() >= rhs.GetForComparison(); +} + +template +struct IsRawPtr : std::false_type {}; + +template +struct IsRawPtr> : std::true_type {}; + +template +inline constexpr bool IsRawPtrV = IsRawPtr::value; + +template +inline constexpr bool IsRawPtrMayDangleV = false; + +template +inline constexpr bool IsRawPtrMayDangleV> = + partition_alloc::internal::ContainsFlags(Traits, RawPtrTraits::kMayDangle); + +// Template helpers for working with T* or raw_ptr. +template +struct IsRawPointerHelper : std::false_type {}; + +template +struct IsRawPointerHelper : std::true_type {}; + +template +struct IsRawPointerHelper> : std::true_type {}; + +template +inline constexpr bool IsRawPointer = IsRawPointerHelper::value; + +template +struct RemoveRawPointer { + using type = T; +}; + +template +struct RemoveRawPointer { + using type = T; +}; + +template +struct RemoveRawPointer> { + using type = T; +}; + +template +using RemoveRawPointerT = typename RemoveRawPointer::type; + +} // namespace base + +using base::raw_ptr; + +// DisableDanglingPtrDetection option for raw_ptr annotates +// "intentional-and-safe" dangling pointers. It is meant to be used at the +// margin, only if there is no better way to re-architecture the code. +// +// Usage: +// raw_ptr dangling_ptr; +// +// When using it, please provide a justification about what guarantees that it +// will never be dereferenced after becoming dangling. +constexpr inline auto DisableDanglingPtrDetection = + base::RawPtrTraits::kMayDangle; + +// See `docs/dangling_ptr.md` +// Annotates known dangling raw_ptr. Those haven't been triaged yet. All the +// occurrences are meant to be removed. See https://crbug.com/1291138. +constexpr inline auto DanglingUntriaged = base::RawPtrTraits::kMayDangle; + +// Unlike DanglingUntriaged, this annotates raw_ptrs that are known to +// dangle only occasionally on the CQ. +// +// These were found from CQ runs and analysed in this dashboard: +// https://docs.google.com/spreadsheets/d/1k12PQOG4y1-UEV9xDfP1F8FSk4cVFywafEYHmzFubJ8/ +// +// This is not meant to be added manually. You can ignore this flag. +constexpr inline auto FlakyDanglingUntriaged = base::RawPtrTraits::kMayDangle; + +// Dangling raw_ptr that is more likely to cause UAF: its memory was freed in +// one task, and the raw_ptr was released in a different one. +// +// This is not meant to be added manually. You can ignore this flag. +constexpr inline auto AcrossTasksDanglingUntriaged = + base::RawPtrTraits::kMayDangle; + +// The use of pointer arithmetic with raw_ptr is strongly discouraged and +// disabled by default. Usually a container like span<> should be used +// instead of the raw_ptr. +constexpr inline auto AllowPtrArithmetic = + base::RawPtrTraits::kAllowPtrArithmetic; + +// The use of uninitialized pointers is strongly discouraged. raw_ptrs will +// be initialized to nullptr by default in all cases when building against +// Chromium. However, third-party projects built in a standalone manner may +// wish to opt out where possible. One way to do this is via buildflags, +// thus affecting all raw_ptrs, but a finer-grained mechanism is the use +// of the kAllowUninitialized trait. +// +// Note that opting out may not always be effective, given that algorithms +// like BackupRefPtr require nullptr initializaion for correctness and thus +// silently enforce it. +constexpr inline auto AllowUninitialized = + base::RawPtrTraits::kAllowUninitialized; + +// This flag is used to tag a subset of dangling pointers. Similarly to +// DanglingUntriaged, those pointers are known to be dangling. However, we also +// detected that those raw_ptr's were never released (either by calling +// raw_ptr's destructor or by resetting its value), which can ultimately put +// pressure on the BRP quarantine. +// +// This is not meant to be added manually. You can ignore this flag. +constexpr inline auto LeakedDanglingUntriaged = base::RawPtrTraits::kMayDangle; + +// Temporary introduced alias in the context of rewriting std::vector into +// std::vector> and in order to temporarily bypass the dangling ptr +// checks on the CQ. This alias will be removed gradually after the cl lands and +// will be replaced by DanglingUntriaged where necessary. +constexpr inline auto VectorExperimental = base::RawPtrTraits::kMayDangle; + +// Temporary alias introduced in the context of rewriting std::set into +// std::set> and in order to temporarily bypass the dangling ptr +// checks on the CQ. This alias will be removed gradually after the rewrite cl +// lands and will be replaced by DanglingUntriaged where necessary. +constexpr inline auto SetExperimental = base::RawPtrTraits::kMayDangle; + +// Temporary alias introduced in the context of rewriting more containers and in +// order to temporarily bypass the dangling ptr checks on the CQ. This alias +// will be removed gradually after the rewrite cl lands and will be replaced by +// DanglingUntriaged where necessary. +constexpr inline auto CtnExperimental = base::RawPtrTraits::kMayDangle; + +// Public verson used in callbacks arguments when it is known that they might +// receive dangling pointers. In any other cases, please +// use one of: +// - raw_ptr +// - raw_ptr +template +using MayBeDangling = base::raw_ptr; + +namespace std { + +// Override so set/map lookups do not create extra raw_ptr. This also allows +// dangling pointers to be used for lookup. +template +struct less> { + using Impl = typename raw_ptr::Impl; + using is_transparent = void; + + bool operator()(const raw_ptr& lhs, + const raw_ptr& rhs) const { + Impl::IncrementLessCountForTest(); + return lhs < rhs; + } + + bool operator()(T* lhs, const raw_ptr& rhs) const { + Impl::IncrementLessCountForTest(); + return lhs < rhs; + } + + bool operator()(const raw_ptr& lhs, T* rhs) const { + Impl::IncrementLessCountForTest(); + return lhs < rhs; + } +}; + +template +struct hash> { + typedef raw_ptr argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type const& ptr) const { + return hash()(ptr.get()); + } +}; + +// Define for cases where raw_ptr holds a pointer to an array of type T. +// This is consistent with definition of std::iterator_traits. +// Algorithms like std::binary_search need that. +template +struct iterator_traits> { + using difference_type = ptrdiff_t; + using value_type = std::remove_cv_t; + using pointer = T*; + using reference = T&; + using iterator_category = std::random_access_iterator_tag; +}; + +// Specialize std::pointer_traits. The latter is required to obtain the +// underlying raw pointer in the std::to_address(pointer) overload. +// Implementing the pointer_traits is the standard blessed way to customize +// `std::to_address(pointer)` in C++20 [3]. +// +// [1] https://wg21.link/pointer.traits.optmem + +template +struct pointer_traits<::raw_ptr> { + using pointer = ::raw_ptr; + using element_type = T; + using difference_type = ptrdiff_t; + + template + using rebind = ::raw_ptr; + + static constexpr pointer pointer_to(element_type& r) noexcept { + return pointer(&r); + } + + static constexpr element_type* to_address(pointer p) noexcept { + return p.get(); + } +}; + +} // namespace std + +#endif // PARTITION_ALLOC_POINTERS_RAW_PTR_H_ \ No newline at end of file diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc new file mode 100644 index 0000000000..9f653d32fd --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.cc @@ -0,0 +1,6 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "raw_ptr_backup_ref_impl.h" diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.h new file mode 100644 index 0000000000..24f75dffd7 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_backup_ref_impl.h @@ -0,0 +1,514 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef PARTITION_ALLOC_POINTERS_RAW_PTR_BACKUP_REF_IMPL_H_ +#define PARTITION_ALLOC_POINTERS_RAW_PTR_BACKUP_REF_IMPL_H_ + +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/cxx20_is_constant_evaluated.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.h" + +#if !PA_BUILDFLAG(HAS_64_BIT_POINTERS) +#include "partition_alloc/address_pool_manager_bitmap.h" +#endif + +#if !PA_BUILDFLAG(USE_RAW_PTR_BACKUP_REF_IMPL) +#error "Included under wrong build option" +#endif + +namespace base::internal { + +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) +PA_COMPONENT_EXPORT(RAW_PTR) +void CheckThatAddressIsntWithinFirstPartitionPage(uintptr_t address); +#endif + +// Note that `RawPtrBackupRefImpl` itself is not thread-safe. If multiple +// threads modify the same raw_ptr object without synchronization, a data race +// will occur. +template +struct RawPtrBackupRefImpl { + // These are needed for correctness, or else we may end up manipulating + // ref-count where we shouldn't, thus affecting the BRP's integrity. Unlike + // the first two, kMustZeroOnDestruct wouldn't be needed if raw_ptr was used + // correctly, but we already caught cases where a value is written after + // destruction. + static constexpr bool kMustZeroOnConstruct = true; + static constexpr bool kMustZeroOnMove = true; + static constexpr bool kMustZeroOnDestruct = true; + + private: + PA_ALWAYS_INLINE static bool UseBrp(uintptr_t address) { + // BRP is temporarily disabled for Pointers annotated with + // DisableBRP. + if constexpr (DisableBRP) { + return false; + } +// return partition_alloc::IsManagedByPartitionAllocBRPPool(address); + return false; + } + + PA_ALWAYS_INLINE static bool IsSupportedAndNotNull(uintptr_t address) { + // There are many situations where the compiler can prove that + // `ReleaseWrappedPtr` is called on a value that is always nullptr, but the + // way `IsManagedByPartitionAllocBRPPool` is written, the compiler can't + // prove that nullptr is not managed by PartitionAlloc; and so the compiler + // has to emit a useless check and dead code. To avoid that without making + // the runtime check slower, tell the compiler to skip + // `IsManagedByPartitionAllocBRPPool` when it can statically determine that + // address is nullptr. +#if PA_HAS_BUILTIN(__builtin_constant_p) + if (__builtin_constant_p(address == 0) && (address == 0)) { +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + PA_BASE_CHECK( + !partition_alloc::IsManagedByPartitionAllocBRPPool(address)); +#endif // PA_BUILDFLAG(PA_DCHECK_IS_ON) || + // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + return false; + } +#endif // PA_HAS_BUILTIN(__builtin_constant_p) + + // This covers the nullptr case, as address 0 is never in any + // PartitionAlloc pool. + bool use_brp = UseBrp(address); + + // There may be pointers immediately after the allocation, e.g. + // { + // // Assume this allocation happens outside of PartitionAlloc. + // raw_ptr ptr = new T[20]; + // for (size_t i = 0; i < 20; i ++) { ptr++; } + // } + // + // Such pointers are *not* at risk of accidentally falling into BRP pool, + // because: + // 1) On 64-bit systems, BRP pool is preceded by a forbidden region. + // 2) On 32-bit systems, the guard pages and metadata of super pages in BRP + // pool aren't considered to be part of that pool. + // + // This allows us to make a stronger assertion that if + // IsManagedByPartitionAllocBRPPool returns true for a valid pointer, + // it must be at least partition page away from the beginning of a super + // page. +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + if (use_brp) { + CheckThatAddressIsntWithinFirstPartitionPage(address); + } +#endif + + return use_brp; + } + +#if PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + // Out-Of-Bounds (OOB) poison bit is set when the pointer has overflowed by + // one byte. +#if defined(ARCH_CPU_X86_64) + // Bit 63 is the only pointer bit that will work as the poison bit across both + // LAM48 and LAM57. It also works when all unused linear address bits are + // checked for canonicality. + static constexpr uintptr_t OOB_POISON_BIT = static_cast(1) << 63; +#else + // Avoid ARM's Top-Byte Ignore. + static constexpr uintptr_t OOB_POISON_BIT = static_cast(1) << 55; +#endif + + template + PA_ALWAYS_INLINE static T* UnpoisonPtr(T* ptr) { + return reinterpret_cast(reinterpret_cast(ptr) & + ~OOB_POISON_BIT); + } + + template + PA_ALWAYS_INLINE static bool IsPtrOOB(T* ptr) { + return (reinterpret_cast(ptr) & OOB_POISON_BIT) == + OOB_POISON_BIT; + } + + template + PA_ALWAYS_INLINE static T* PoisonOOBPtr(T* ptr) { + return reinterpret_cast(reinterpret_cast(ptr) | + OOB_POISON_BIT); + } +#else // PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + template + PA_ALWAYS_INLINE static T* UnpoisonPtr(T* ptr) { + return ptr; + } +#endif // PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + + public: + // Wraps a pointer. + template + PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return ptr; + } + uintptr_t address = partition_alloc::UntagPtr(UnpoisonPtr(ptr)); + if (IsSupportedAndNotNull(address)) { +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + PA_BASE_CHECK(ptr != nullptr); +#endif + AcquireInternal(address); + } else { +#if !PA_BUILDFLAG(HAS_64_BIT_POINTERS) +#if PA_HAS_BUILTIN(__builtin_constant_p) + // Similarly to `IsSupportedAndNotNull` above, elide the + // `BanSuperPageFromBRPPool` call if the compiler can prove that `address` + // is zero since PA won't be able to map anything at that address anyway. + bool known_constant_zero = + __builtin_constant_p(address == 0) && (address == 0); +#else // PA_HAS_BUILTIN(__builtin_constant_p) + bool known_constant_zero = false; +#endif // PA_HAS_BUILTIN(__builtin_constant_p) + + if (!known_constant_zero) { + partition_alloc::internal::AddressPoolManagerBitmap:: + BanSuperPageFromBRPPool(address); + } +#endif // !PA_BUILDFLAG(HAS_64_BIT_POINTERS) + } + + return ptr; + } + + // Notifies the allocator when a wrapped pointer is being removed or replaced. + template + PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T* wrapped_ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return; + } + uintptr_t address = partition_alloc::UntagPtr(UnpoisonPtr(wrapped_ptr)); + if (IsSupportedAndNotNull(address)) { +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + PA_BASE_CHECK(wrapped_ptr != nullptr); +#endif + ReleaseInternal(address); + } + + // We are unable to counteract BanSuperPageFromBRPPool(), called from + // WrapRawPtr(). We only use one bit per super-page and, thus can't tell if + // there's more than one associated raw_ptr at a given time. The risk of + // exhausting the entire address space is minuscule, therefore, we couldn't + // resist the perf gain of a single relaxed store (in the above mentioned + // function) over much more expensive two CAS operations, which we'd have to + // use if we were to un-ban a super-page. + } + + // Unwraps the pointer, while asserting that memory hasn't been freed. The + // function is allowed to crash on nullptr. + template + PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( + T* wrapped_ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr; + } +#if PA_BUILDFLAG(PA_DCHECK_IS_ON) || \ + PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) +#if PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + PA_BASE_CHECK(!IsPtrOOB(wrapped_ptr)); +#endif + uintptr_t address = partition_alloc::UntagPtr(wrapped_ptr); + if (IsSupportedAndNotNull(address)) { + PA_BASE_CHECK(wrapped_ptr != nullptr); + PA_BASE_CHECK(IsPointeeAlive(address)); // Detects use-after-free. + } +#endif // PA_BUILDFLAG(PA_DCHECK_IS_ON) || + // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS) + return wrapped_ptr; + } + + // Unwraps the pointer, while asserting that memory hasn't been freed. The + // function must handle nullptr gracefully. + template + PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( + T* wrapped_ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr; + } + T* unpoisoned_ptr = UnpoisonPtr(wrapped_ptr); +#if PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + // Some code uses invalid pointer values as indicators, so those values must + // be passed through unchanged during extraction. The following check will + // pass invalid values through if those values do not fall within the BRP + // pool after being unpoisoned. + if (!IsSupportedAndNotNull(partition_alloc::UntagPtr(unpoisoned_ptr))) { + return wrapped_ptr; + } + // Poison-based OOB checks do not extend to extracted pointers. The + // alternative of retaining poison on extracted pointers could introduce new + // OOB conditions, e.g., in code that extracts an end-of-allocation pointer + // for use in a loop termination condition. The poison bit would make that + // pointer appear to reference a very high address. +#endif // PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + return unpoisoned_ptr; + } + + // Unwraps the pointer, without making an assertion on whether memory was + // freed or not. + template + PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( + T* wrapped_ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr; + } + // This may be used for unwrapping an end-of-allocation pointer to be used + // as an endpoint in an iterative algorithm, so this removes the OOB poison + // bit. + return UnpoisonPtr(wrapped_ptr); + } + + // Upcasts the wrapped pointer. + template + PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { + static_assert(std::is_convertible_v, + "From must be convertible to To."); + // Note, this cast may change the address if upcasting to base that lies in + // the middle of the derived object. + return wrapped_ptr; + } + + // Verify the pointer stayed in the same slot, and return the poisoned version + // of `new_ptr` if OOB poisoning is enabled. + template + PA_ALWAYS_INLINE static T* VerifyAndPoisonPointerAfterAdvanceOrRetreat( + T* unpoisoned_ptr, + T* new_ptr) { + // First check if the new address didn't migrate in/out the BRP pool, and + // that it lands within the same allocation. An end-of-allocation address is + // ok, too, and that may lead to the pointer being poisoned if the relevant + // feature is enabled. These checks add a non-trivial cost, but they're + // cheaper and more secure than the previous implementation that rewrapped + // the pointer (wrapped the new pointer and unwrapped the old one). + // + // Note, the value of these checks goes beyond OOB protection. They're + // important for integrity of the BRP algorithm. Without these, an attacker + // could make the pointer point to another allocation, and cause its + // ref-count to go to 0 upon this pointer's destruction, even though there + // may be another pointer still pointing to it, thus making it lose the BRP + // protection prematurely. + // + // Note 2, if we ever need to restore the "before allocation" mode, we can + // run into a problem on 32-bit that the end-of-allocation address could + // fall outside of PartitionAlloc's pools, if this is the last slot of the + // super page, thus pointing to the guard page. This means the ref-count + // won't be decreased when the pointer is released (leak). This problem + // doesn't exist in the modes that involve putting extras after the + // allocation, because the end-of-allocation address belongs to the same + // slot. + const uintptr_t before_addr = partition_alloc::UntagPtr(unpoisoned_ptr); + const uintptr_t after_addr = partition_alloc::UntagPtr(new_ptr); + // TODO(bartekn): Consider adding support for non-BRP pools too (without + // removing the cross-pool migration check). + if (IsSupportedAndNotNull(before_addr)) { + constexpr size_t size = sizeof(T); + [[maybe_unused]] const bool is_end = + CheckPointerWithinSameAlloc(before_addr, after_addr, size); +#if PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + if (is_end) { + new_ptr = PoisonOOBPtr(new_ptr); + } +#endif // PA_BUILDFLAG(BACKUP_REF_PTR_POISON_OOB_PTR) + } else { + // Check that the new address didn't migrate into the BRP pool, as it + // would result in more pointers pointing to an allocation than its + // ref-count reflects. + PA_BASE_CHECK(!IsSupportedAndNotNull(after_addr)); + } + return new_ptr; + } + + // Advance the wrapped pointer by `delta_elems`. + // `is_in_pointer_modification` means that the result is intended to modify + // the pointer (as opposed to creating a new one). + template < + typename T, + typename Z, + typename = + std::enable_if_t, void>> + PA_ALWAYS_INLINE static constexpr T* + Advance(T* wrapped_ptr, Z delta_elems, bool is_in_pointer_modification) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr + delta_elems; + } + T* unpoisoned_ptr = UnpoisonPtr(wrapped_ptr); + // When modifying the pointer, we have to make sure it doesn't migrate to a + // different slot, or else ref-count integrity is at risk. This isn't needed + // if the result will be assigned to a new pointer, as it'll do ref-counting + // properly. Do it anyway if extra OOB checks are enabled. + if (PA_BUILDFLAG(BACKUP_REF_PTR_EXTRA_OOB_CHECKS) || + is_in_pointer_modification) { + return VerifyAndPoisonPointerAfterAdvanceOrRetreat( + unpoisoned_ptr, unpoisoned_ptr + delta_elems); + } + return unpoisoned_ptr + delta_elems; + } + + // Retreat the wrapped pointer by `delta_elems`. + // `is_in_pointer_modification` means that the result is intended to modify + // the pointer (as opposed to creating a new one). + template < + typename T, + typename Z, + typename = + std::enable_if_t, void>> + PA_ALWAYS_INLINE static constexpr T* + Retreat(T* wrapped_ptr, Z delta_elems, bool is_in_pointer_modification) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr - delta_elems; + } + T* unpoisoned_ptr = UnpoisonPtr(wrapped_ptr); + // When modifying the pointer, we have to make sure it doesn't migrate to a + // different slot, or else ref-count integrity is at risk. This isn't needed + // if the result will be assigned to a new pointer, as it'll do ref-counting + // properly. Do it anyway if extra OOB checks are enabled. + if (PA_BUILDFLAG(BACKUP_REF_PTR_EXTRA_OOB_CHECKS) || + is_in_pointer_modification) { + return VerifyAndPoisonPointerAfterAdvanceOrRetreat( + unpoisoned_ptr, unpoisoned_ptr - delta_elems); + } + return unpoisoned_ptr - delta_elems; + } + + template + PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, + T* wrapped_ptr2) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr1 - wrapped_ptr2; + } + + T* unpoisoned_ptr1 = UnpoisonPtr(wrapped_ptr1); + T* unpoisoned_ptr2 = UnpoisonPtr(wrapped_ptr2); +#if PA_BUILDFLAG(ENABLE_POINTER_SUBTRACTION_CHECK) + if (partition_alloc::internal::base::is_constant_evaluated()) { + return unpoisoned_ptr1 - unpoisoned_ptr2; + } + uintptr_t address1 = partition_alloc::UntagPtr(unpoisoned_ptr1); + uintptr_t address2 = partition_alloc::UntagPtr(unpoisoned_ptr2); + // Ensure that both pointers are within the same slot, and pool! + // TODO(bartekn): Consider adding support for non-BRP pool too. + if (IsSupportedAndNotNull(address1)) { + PA_BASE_CHECK(IsSupportedAndNotNull(address2)); + PA_BASE_CHECK(partition_alloc::internal::IsPtrWithinSameAlloc( + address2, address1, sizeof(T)) != + partition_alloc::internal::PtrPosWithinAlloc::kFarOOB); + } else { + PA_BASE_CHECK(!IsSupportedAndNotNull(address2)); + } +#endif // PA_BUILDFLAG(ENABLE_POINTER_SUBTRACTION_CHECK) + return unpoisoned_ptr1 - unpoisoned_ptr2; + } + + // Returns a copy of a wrapped pointer, without making an assertion on whether + // memory was freed or not. + // This method increments the reference count of the allocation slot. + template + PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr; + } + return WrapRawPtr(wrapped_ptr); + } + + // Report the current wrapped pointer if pointee isn't alive anymore. + template + PA_ALWAYS_INLINE static void ReportIfDangling(T* wrapped_ptr) { + ReportIfDanglingInternal(partition_alloc::UntagPtr(wrapped_ptr)); + } + + // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used + // to create a new raw_ptr from another raw_ptr of a different flavor. + template + PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return ptr; + } else { + return WrapRawPtr(ptr); + } + } + + template + PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( + T* wrapped_ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return wrapped_ptr; + } else { + return UnpoisonPtr(wrapped_ptr); + } + } + +#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_INSTANCE_TRACER) + template + static constexpr void Trace(uint64_t owner_id, T* wrapped_ptr) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return; + } + + uintptr_t address = partition_alloc::UntagPtr(UnpoisonPtr(wrapped_ptr)); + + if (!IsSupportedAndNotNull(address)) { + return; + } + + InstanceTracer::Trace(owner_id, AllowDangling, address); + } + + static constexpr void Untrace(uint64_t owner_id) { + if (partition_alloc::internal::base::is_constant_evaluated()) { + return; + } + + InstanceTracer::Untrace(owner_id); + } +#else + // In theory, this shouldn't be needed. In practice, the optimizer is unable + // to tell that things like `IsSupportedAndNotNull()` are side-effect free. + template + static constexpr void Trace(uint64_t owner_id, T* wrapped_ptr) {} + static constexpr void Untrace(uint64_t owner_id) {} +#endif + + // This is for accounting only, used by unit tests. + PA_ALWAYS_INLINE static constexpr void IncrementSwapCountForTest() {} + PA_ALWAYS_INLINE static constexpr void IncrementLessCountForTest() {} + + private: + // We've evaluated several strategies (inline nothing, various parts, or + // everything in |Wrap()| and |Release()|) using the Speedometer2 benchmark + // to measure performance. The best results were obtained when only the + // lightweight |IsManagedByPartitionAllocBRPPool()| check was inlined. + // Therefore, we've extracted the rest into the functions below and marked + // them as PA_NOINLINE to prevent unintended LTO effects. + PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) void AcquireInternal( + uintptr_t address); + PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) void ReleaseInternal( + uintptr_t address); + PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) bool IsPointeeAlive( + uintptr_t address); + PA_NOINLINE static PA_COMPONENT_EXPORT(RAW_PTR) void ReportIfDanglingInternal( + uintptr_t address); + + // CHECK if `before_addr` and `after_addr` are in the same allocation, for a + // given `type_size`. + // If BACKUP_REF_PTR_POISON_OOB_PTR is enabled, return whether the allocation + // is at the end. + // If BACKUP_REF_PTR_POISON_OOB_PTR is disable, return false. + PA_NOINLINE static PA_COMPONENT_EXPORT( + RAW_PTR) bool CheckPointerWithinSameAlloc(uintptr_t before_addr, + uintptr_t after_addr, + size_t type_size); +}; + +} // namespace base::internal + +#endif // PARTITION_ALLOC_POINTERS_RAW_PTR_BACKUP_REF_IMPL_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h new file mode 100644 index 0000000000..f44d326615 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h @@ -0,0 +1,41 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_ +#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_ + +// This header will be leakily included even when +// `!use_partition_alloc`, which is okay because it's a leaf header. +#include "bindings/v8/for_build/build_config.h" +#include "bindings/v8/for_build/buildflag.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h" // nogncheck +//#include "partition_alloc/partition_alloc_buildflags.h" + +#define RAW_PTR_EXCLUSION +/*TODO fix BUILDFLAG(FORCE_ENABLE_RAW_PTR_EXCLUSION) error +#if PA_HAS_ATTRIBUTE(annotate) +#if defined(OFFICIAL_BUILD) && !BUILDFLAG(FORCE_ENABLE_RAW_PTR_EXCLUSION) +// The annotation changed compiler output and increased binary size so disable +// for official builds. +// TODO(crbug.com/40836910): Remove when issue is resolved. +#define RAW_PTR_EXCLUSION +#else +// Marks a field as excluded from the `raw_ptr` usage enforcement via +// Chromium Clang plugin. +// +// Example: +// RAW_PTR_EXCLUSION Foo* foo_; +// +// `RAW_PTR_EXCLUSION` should be avoided, as exclusions makes it significantly +// easier for any bug involving the pointer to become a security vulnerability. +// For additional guidance please see the "When to use raw_ptr" section of +// `//base/memory/raw_ptr.md`. +#define RAW_PTR_EXCLUSION __attribute__((annotate("raw_ptr_exclusion"))) +#endif +#else +#define RAW_PTR_EXCLUSION +#endif +*/ +#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_EXCLUSION_H_ \ No newline at end of file diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_noop_impl.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_noop_impl.h new file mode 100644 index 0000000000..683415554e --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_noop_impl.h @@ -0,0 +1,120 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_NOOP_IMPL_H_ +#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_NOOP_IMPL_H_ + +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_forward.h" + +namespace base::internal { + +struct RawPtrNoOpImpl { + static constexpr bool kMustZeroOnConstruct = false; + static constexpr bool kMustZeroOnMove = false; + static constexpr bool kMustZeroOnDestruct = false; + + // Wraps a pointer. + template + PA_ALWAYS_INLINE static constexpr T* WrapRawPtr(T* ptr) { + return ptr; + } + + // Notifies the allocator when a wrapped pointer is being removed or + // replaced. + template + PA_ALWAYS_INLINE static constexpr void ReleaseWrappedPtr(T*) {} + + // Unwraps the pointer, while asserting that memory hasn't been freed. The + // function is allowed to crash on nullptr. + template + PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForDereference( + T* wrapped_ptr) { + return wrapped_ptr; + } + + // Unwraps the pointer, while asserting that memory hasn't been freed. The + // function must handle nullptr gracefully. + template + PA_ALWAYS_INLINE static constexpr T* SafelyUnwrapPtrForExtraction( + T* wrapped_ptr) { + return wrapped_ptr; + } + + // Unwraps the pointer, without making an assertion on whether memory was + // freed or not. + template + PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForComparison( + T* wrapped_ptr) { + return wrapped_ptr; + } + + // Upcasts the wrapped pointer. + template + PA_ALWAYS_INLINE static constexpr To* Upcast(From* wrapped_ptr) { + static_assert(std::is_convertible_v, + "From must be convertible to To."); + // Note, this cast may change the address if upcasting to base that lies + // in the middle of the derived object. + return wrapped_ptr; + } + + // Advance the wrapped pointer by `delta_elems`. + template < + typename T, + typename Z, + typename = + std::enable_if_t, void>> + PA_ALWAYS_INLINE static constexpr T* Advance(T* wrapped_ptr, Z delta_elems) { + return wrapped_ptr + delta_elems; + } + + // Retreat the wrapped pointer by `delta_elems`. + template < + typename T, + typename Z, + typename = + std::enable_if_t, void>> + PA_ALWAYS_INLINE static constexpr T* Retreat(T* wrapped_ptr, Z delta_elems) { + return wrapped_ptr - delta_elems; + } + + template + PA_ALWAYS_INLINE static constexpr ptrdiff_t GetDeltaElems(T* wrapped_ptr1, + T* wrapped_ptr2) { + return wrapped_ptr1 - wrapped_ptr2; + } + + // Returns a copy of a wrapped pointer, without making an assertion on + // whether memory was freed or not. + template + PA_ALWAYS_INLINE static constexpr T* Duplicate(T* wrapped_ptr) { + return wrapped_ptr; + } + + // `WrapRawPtrForDuplication` and `UnsafelyUnwrapPtrForDuplication` are used + // to create a new raw_ptr from another raw_ptr of a different flavor. + template + PA_ALWAYS_INLINE static constexpr T* WrapRawPtrForDuplication(T* ptr) { + return ptr; + } + + template + PA_ALWAYS_INLINE static constexpr T* UnsafelyUnwrapPtrForDuplication( + T* wrapped_ptr) { + return wrapped_ptr; + } + + // This is for accounting only, used by unit tests. + PA_ALWAYS_INLINE constexpr static void IncrementSwapCountForTest() {} + PA_ALWAYS_INLINE constexpr static void IncrementLessCountForTest() {} +}; + +} // namespace base::internal + +#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_POINTERS_RAW_PTR_NOOP_IMPL_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/posix/eintr_wrapper.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/posix/eintr_wrapper.h new file mode 100644 index 0000000000..fe1a23564d --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/posix/eintr_wrapper.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +// This provides a wrapper around system calls which may be interrupted by a +// signal and return EINTR. See man 7 signal. +// To prevent long-lasting loops (which would likely be a bug, such as a signal +// that should be masked) to go unnoticed, there is a limit after which the +// caller will nonetheless see an EINTR in Debug builds. +// +// On Windows and Fuchsia, this wrapper does nothing because there are no +// signals. +// +// Don't wrap close calls in WrapEINTR. Use IGNORE_EINTR macro if the return +// value of close is significant. See http://crbug.com/269623. + +#ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_POSIX_EINTR_WRAPPER_H_ +#define PARTITION_ALLOC_PARTITION_ALLOC_BASE_POSIX_EINTR_WRAPPER_H_ + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" + +#if BUILDFLAG(IS_POSIX) +#include +#include +#endif + +namespace partition_alloc { +#if BUILDFLAG(IS_POSIX) + +template +inline auto WrapEINTR(Fn fn) { + return [fn](auto&&... args) { + int out = -1; +#if defined(NDEBUG) + while (true) +#else + for (int retry_count = 0; retry_count < 100; ++retry_count) +#endif + { + out = fn(std::forward(args)...); + if (out != -1 || errno != EINTR) { + return out; + } + } + return out; + }; +} + +#else // !BUILDFLAG(IS_POSIX) + +template +inline auto WrapEINTR(Fn fn) { + return fn; +} + +#endif // !BUILDFLAG(IS_POSIX) + +} // namespace partition_alloc + +#endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_POSIX_EINTR_WRAPPER_H_ + diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.cc b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.cc new file mode 100644 index 0000000000..b070d9dbaf --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.cc @@ -0,0 +1,7 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + + +#include "tagging.h" diff --git a/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.h b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.h new file mode 100644 index 0000000000..3353b7fcf3 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partition_allocator/src/partition_alloc/tagging.h @@ -0,0 +1,155 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + + +#ifndef PARTITION_ALLOC_TAGGING_H_ +#define PARTITION_ALLOC_TAGGING_H_ + +// This file contains method definitions to support Armv8.5-A's memory tagging +// extension. + +#include +#include + +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/build_config.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/compiler_specific.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_base/component_export.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h" +//#include "partition_alloc/partition_alloc_config.h" + +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) && BUILDFLAG(IS_ANDROID) +#include +#endif +namespace partition_alloc { + +// Enum configures Arm's MTE extension to operate in different modes +enum class TagViolationReportingMode { + // Default settings + kUndefined, + // MTE explicitly disabled. + kDisabled, + // Precise tag violation reports, higher overhead. Good for unittests + // and security critical threads. + kSynchronous, + // Imprecise tag violation reports (async mode). Lower overhead. + kAsynchronous, +}; + +// Changes the memory tagging mode for the calling thread. +PA_COMPONENT_EXPORT(PARTITION_ALLOC) +void ChangeMemoryTaggingModeForCurrentThread(TagViolationReportingMode); + +namespace internal { + +constexpr uint64_t kMemTagGranuleSize = 16u; +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) +constexpr uint64_t kPtrTagMask = 0xff00000000000000uLL; +#else +constexpr uint64_t kPtrTagMask = 0; +#endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) +constexpr uint64_t kPtrUntagMask = ~kPtrTagMask; + +#if BUILDFLAG(IS_ANDROID) +// Changes the memory tagging mode for all threads in the current process. +// Returns true on success. Most likely reason for failure is because heap +// tagging may not be re-enabled after being disabled. +// https://android.googlesource.com/platform/bionic/+/446b4dde724ee64a336a78188c3c9a15aebca87c/libc/include/malloc.h#235 +PA_COMPONENT_EXPORT(PARTITION_ALLOC) +bool ChangeMemoryTaggingModeForAllThreadsPerProcess(TagViolationReportingMode); +#endif + +// Gets the memory tagging mode for the calling thread. Returns kUndefined if +// MTE support is not available. +PA_COMPONENT_EXPORT(PARTITION_ALLOC) +TagViolationReportingMode GetMemoryTaggingModeForCurrentThread(); + +// These forward-defined functions do not really exist in tagging.cc, they're +// resolved by the dynamic linker to MTE-capable versions on the right hardware. +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) +PA_COMPONENT_EXPORT(PARTITION_ALLOC) +void* TagMemoryRangeIncrementInternal(void* ptr, size_t size); +PA_COMPONENT_EXPORT(PARTITION_ALLOC) +void* TagMemoryRangeRandomlyInternal(void* ptr, size_t size, uint64_t mask); +PA_COMPONENT_EXPORT(PARTITION_ALLOC) +void* RemaskPointerInternal(void* ptr); +#endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) + +// Increments the tag of the memory range ptr. Useful for provable revocations +// (e.g. free). Returns the pointer with the new tag. Ensures that the entire +// range is set to the same tag. +PA_ALWAYS_INLINE void* TagMemoryRangeIncrement(void* ptr, size_t size) { +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) + return TagMemoryRangeIncrementInternal(ptr, size); +#else + return ptr; +#endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) +} + +PA_ALWAYS_INLINE void* TagMemoryRangeIncrement(uintptr_t address, size_t size) { + return TagMemoryRangeIncrement(reinterpret_cast(address), size); +} + +// Randomly changes the tag of the ptr memory range. Useful for initial random +// initialization. Returns the pointer with the new tag. Ensures that the entire +// range is set to the same tag. +PA_ALWAYS_INLINE void* TagMemoryRangeRandomly(uintptr_t address, + size_t size, + uint64_t mask = 0u) { + void* ptr = reinterpret_cast(address); +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) + return reinterpret_cast( + TagMemoryRangeRandomlyInternal(ptr, size, mask)); +#else + return ptr; +#endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) +} + +// Gets a version of ptr that's safe to dereference. +template +PA_ALWAYS_INLINE T* TagPtr(T* ptr) { +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) + return reinterpret_cast(RemaskPointerInternal(ptr)); +#else + return ptr; +#endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) +} + +// Gets a version of |address| that's safe to dereference, and casts to a +// pointer. +PA_ALWAYS_INLINE void* TagAddr(uintptr_t address) { + return TagPtr(reinterpret_cast(address)); +} + +// Strips the tag bits off |address|. +PA_ALWAYS_INLINE uintptr_t UntagAddr(uintptr_t address) { +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) + return address & internal::kPtrUntagMask; +#else + return address; +#endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) +} + +} // namespace internal + +// Strips the tag bits off |ptr|. +template +PA_ALWAYS_INLINE uintptr_t UntagPtr(T* ptr) { + return internal::UntagAddr(reinterpret_cast(ptr)); +} + +#if PA_BUILDFLAG(HAS_MEMORY_TAGGING) && BUILDFLAG(IS_ANDROID) +class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PermissiveMte { + public: + static void SetEnabled(bool enabled); + static bool HandleCrash(int signo, siginfo_t* siginfo, ucontext_t* context); + + private: + static bool enabled_; +}; +#endif // PA_BUILDFLAG(HAS_MEMORY_TAGGING) + +} // namespace partition_alloc + +#endif // PARTITION_ALLOC_TAGGING_H_ diff --git a/bridge/bindings/v8/base/allocator/partitions.cc b/bridge/bindings/v8/base/allocator/partitions.cc new file mode 100644 index 0000000000..e9298ae8f0 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partitions.cc @@ -0,0 +1,6 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#include "partitions.h" diff --git a/bridge/bindings/v8/base/allocator/partitions.h b/bridge/bindings/v8/base/allocator/partitions.h new file mode 100644 index 0000000000..a60c8f4d59 --- /dev/null +++ b/bridge/bindings/v8/base/allocator/partitions.h @@ -0,0 +1,104 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_PARTITIONS_H +#define WEBF_PARTITIONS_H + + +//#include "base/allocator/partition_allocator/src/partition_alloc/partition_alloc_forward.h" +//#include "base/check.h" +//#include "base/memory/scoped_refptr.h" +//#include "base/numerics/checked_math.h" +//#include "third_party/blink/renderer/platform/wtf/wtf_export.h" +#include "foundation/macros.h" +#include "bindings/v8/base/compiler_specific.h" +#include "bindings/v8/base/memory/scoped_refptr.h" +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc//partition_alloc_forward.h" +#include "bindings/v8/base/numerics/checked_math.h" +#include + +namespace base { +class SequencedTaskRunner; +} + +namespace webf { + +class Partitions { + public: + // Name of allocator used by tracing for marking sub-allocations while take + // memory snapshots. + static const char* const kAllocatedObjectPoolName; + + // Should be called on the thread which is or will become the main one. + static void Initialize(); + static void InitializeArrayBufferPartition(); + static void StartMemoryReclaimer( + scoped_refptr task_runner); + + // The ArrayBufferPartition is initialized separately from the other + // partitions and so may not always be available. This function can be used to + // determine whether the partition has been initialized. + ALWAYS_INLINE static bool ArrayBufferPartitionInitialized() { + return array_buffer_root_ != nullptr; + } + + ALWAYS_INLINE static partition_alloc::PartitionRoot* ArrayBufferPartition() { + assert_m(initialized_, "initialized_ is nullptr"); + assert_m(ArrayBufferPartitionInitialized(), "array_buffer_root_ is nullptr"); + return array_buffer_root_; + } + + ALWAYS_INLINE static partition_alloc::PartitionRoot* BufferPartition() { + assert_m(initialized_, "initialized_ is nullptr"); + return buffer_root_; + } + + ALWAYS_INLINE static size_t ComputeAllocationSize(size_t count, size_t size) { + base::CheckedNumeric total = count; + total *= size; + return total.ValueOrDie(); + } + + static size_t TotalSizeOfCommittedPages(); + + static size_t TotalActiveBytes(); + + static void DumpMemoryStats(bool is_light_dump, + partition_alloc::PartitionStatsDumper*); + + static void* PA_MALLOC_FN BufferMalloc(size_t n, const char* type_name); + static void* BufferTryRealloc(void* p, size_t n, const char* type_name); + static void BufferFree(void* p); + static size_t BufferPotentialCapacity(size_t n); + + static void* PA_MALLOC_FN FastMalloc(size_t n, const char* type_name); + static void* PA_MALLOC_FN FastZeroedMalloc(size_t n, const char* type_name); + static void FastFree(void* p); + + static void HandleOutOfMemory(size_t size); + + // Adjusts the size of the partitions based on process state. + static void AdjustPartitionsForForeground(); + static void AdjustPartitionsForBackground(); + + private: + ALWAYS_INLINE static partition_alloc::PartitionRoot* FastMallocPartition() { + assert_m(initialized_, "initialized_ is nullptr"); + return fast_malloc_root_; + } + + static bool InitializeOnce(); + + static bool initialized_; + static bool scan_is_enabled_; + // See Allocator.md for a description of these partitions. + static partition_alloc::PartitionRoot* fast_malloc_root_; + static partition_alloc::PartitionRoot* array_buffer_root_; + static partition_alloc::PartitionRoot* buffer_root_; +}; + +} // namespace webf + +#endif // WEBF_PARTITIONS_H diff --git a/bridge/bindings/v8/base/bit_cast.h b/bridge/bindings/v8/base/bit_cast.h new file mode 100644 index 0000000000..fac33f1131 --- /dev/null +++ b/bridge/bindings/v8/base/bit_cast.h @@ -0,0 +1,46 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_BIT_CAST_H +#define WEBF_BIT_CAST_H + +#include + +namespace base { + +// This is an equivalent to C++20's std::bit_cast<>(), but with additional +// warnings. It morally does what `*reinterpret_cast(&source)` does, but +// the cast/deref pair is undefined behavior, while bit_cast<>() isn't. +// +// This is not a magic "get out of UB free" card. This must only be used on +// values, not on references or pointers. For pointers, use +// reinterpret_cast<>(), and then look at https://eel.is/c++draft/basic.lval#11 +// as that's probably UB also. + +template +constexpr Dest bit_cast(const Source& source) { + static_assert(!std::is_pointer_v, + "bit_cast must not be used on pointer types"); + static_assert(!std::is_pointer_v, + "bit_cast must not be used on pointer types"); + static_assert(!std::is_reference_v, + "bit_cast must not be used on reference types"); + static_assert(!std::is_reference_v, + "bit_cast must not be used on reference types"); + static_assert( + sizeof(Dest) == sizeof(Source), + "bit_cast requires source and destination types to be the same size"); + static_assert(std::is_trivially_copyable_v, + "bit_cast requires the source type to be trivially copyable"); + static_assert( + std::is_trivially_copyable_v, + "bit_cast requires the destination type to be trivially copyable"); + + return __builtin_bit_cast(Dest, source); +} + +} // namespace base + +#endif // WEBF_BIT_CAST_H diff --git a/bridge/bindings/v8/base/compiler_specific.h b/bridge/bindings/v8/base/compiler_specific.h new file mode 100644 index 0000000000..6844adcbec --- /dev/null +++ b/bridge/bindings/v8/base/compiler_specific.h @@ -0,0 +1,630 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_COMPILER_SPECIFIC_H +#define WEBF_COMPILER_SPECIFIC_H + +#include "../for_build//build_config.h" + +#if defined(COMPILER_MSVC) && !defined(__clang__) +#error "Only clang-cl is supported on Windows, see https://crbug.com/988071" +#endif + +// This is a wrapper around `__has_cpp_attribute`, which can be used to test for +// the presence of an attribute. In case the compiler does not support this +// macro it will simply evaluate to 0. +// +// References: +// https://wg21.link/sd6#testing-for-the-presence-of-an-attribute-__has_cpp_attribute +// https://wg21.link/cpp.cond#:__has_cpp_attribute +#if defined(__has_cpp_attribute) +#define HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +#define HAS_CPP_ATTRIBUTE(x) 0 +#endif + +// A wrapper around `__has_attribute`, similar to HAS_CPP_ATTRIBUTE. +#if defined(__has_attribute) +#define HAS_ATTRIBUTE(x) __has_attribute(x) +#else +#define HAS_ATTRIBUTE(x) 0 +#endif + +// A wrapper around `__has_builtin`, similar to HAS_CPP_ATTRIBUTE. +#if defined(__has_builtin) +#define HAS_BUILTIN(x) __has_builtin(x) +#else +#define HAS_BUILTIN(x) 0 +#endif + +// Annotate a function indicating it should not be inlined. +// Use like: +// NOINLINE void DoStuff() { ... } +#if defined(__clang__) && HAS_ATTRIBUTE(noinline) +#define NOINLINE [[clang::noinline]] +#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(noinline) +#define NOINLINE __attribute__((noinline)) +#elif defined(COMPILER_MSVC) +#define NOINLINE __declspec(noinline) +#else +#define NOINLINE +#endif + +// Annotate a function indicating it should not be optimized. +#if defined(__clang__) && HAS_ATTRIBUTE(optnone) +#define NOOPT [[clang::optnone]] +#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(optimize) +#define NOOPT __attribute__((optimize(0))) +#else +#define NOOPT +#endif + +#if defined(__clang__) && defined(NDEBUG) && HAS_ATTRIBUTE(always_inline) +#define ALWAYS_INLINE [[clang::always_inline]] inline +#elif defined(COMPILER_GCC) && defined(NDEBUG) && HAS_ATTRIBUTE(always_inline) +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#elif defined(COMPILER_MSVC) && defined(NDEBUG) +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE inline +#endif + +// Annotate a function indicating it should never be tail called. Useful to make +// sure callers of the annotated function are never omitted from call-stacks. +// To provide the complementary behavior (prevent the annotated function from +// being omitted) look at NOINLINE. Also note that this doesn't prevent code +// folding of multiple identical caller functions into a single signature. To +// prevent code folding, see NO_CODE_FOLDING() in base/debug/alias.h. +// Use like: +// NOT_TAIL_CALLED void FooBar(); +#if defined(__clang__) && HAS_ATTRIBUTE(not_tail_called) +#define NOT_TAIL_CALLED [[clang::not_tail_called]] +#else +#define NOT_TAIL_CALLED +#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. +// +// Historically, compilers had trouble mixing __attribute__((...)) syntax with +// alignas(...) syntax. However, at least Clang is very accepting nowadays. It +// may be that this macro can be removed entirely. +#if defined(__clang__) +#define ALIGNAS(byte_alignment) alignas(byte_alignment) +#elif defined(COMPILER_MSVC) +#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) +#elif defined(COMPILER_GCC) && HAS_ATTRIBUTE(aligned) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#endif + +// In case the compiler supports it NO_UNIQUE_ADDRESS evaluates to the C++20 +// attribute [[no_unique_address]]. This allows annotating data members so that +// they need not have an address distinct from all other non-static data members +// of its class. +// +// References: +// * https://en.cppreference.com/w/cpp/language/attributes/no_unique_address +// * https://wg21.link/dcl.attr.nouniqueaddr +#if defined(COMPILER_MSVC) && HAS_CPP_ATTRIBUTE(msvc::no_unique_address) +// Unfortunately MSVC ignores [[no_unique_address]] (see +// https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#msvc-extensions-and-abi), +// and clang-cl matches it for ABI compatibility reasons. We need to prefer +// [[msvc::no_unique_address]] when available if we actually want any effect. +#define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] +#elif HAS_CPP_ATTRIBUTE(no_unique_address) +#define NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define NO_UNIQUE_ADDRESS +#endif + +// Tells 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.) +// For member functions, the implicit this parameter counts as index 1. +#if (defined(COMPILER_GCC) || defined(__clang__)) && HAS_ATTRIBUTE(format) +#define PRINTF_FORMAT(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define PRINTF_FORMAT(format_param, dots_param) +#endif + +// WPRINTF_FORMAT is the same, but for wide format strings. +// This doesn't appear to yet be implemented in any compiler. +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 . +#define WPRINTF_FORMAT(format_param, dots_param) +// If available, it would look like: +// __attribute__((format(wprintf, format_param, dots_param))) + +// Sanitizers annotations. +#if HAS_ATTRIBUTE(no_sanitize) +#define NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#if !defined(NO_SANITIZE) +#define NO_SANITIZE(what) +#endif + +// MemorySanitizer annotations. +#if defined(MEMORY_SANITIZER) && !BUILDFLAG(IS_NACL) +#include + +// Mark a memory region fully initialized. +// Use this to annotate code that deliberately reads uninitialized data, for +// example a GC scavenging root set pointers from the stack. +#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size) + +// Check a memory region for initializedness, as if it was being used here. +// If any bits are uninitialized, crash with an MSan report. +// Use this to sanitize data which MSan won't be able to track, e.g. before +// passing data to another process via shared memory. +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \ + __msan_check_mem_is_initialized(p, size) +#else // MEMORY_SANITIZER +#define MSAN_UNPOISON(p, size) +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) +#endif // MEMORY_SANITIZER + +// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons. +#if !defined(DISABLE_CFI_PERF) +#if defined(__clang__) && defined(OFFICIAL_BUILD) +#define DISABLE_CFI_PERF NO_SANITIZE("cfi") +#else +#define DISABLE_CFI_PERF +#endif +#endif + +// DISABLE_CFI_ICALL -- Disable Control Flow Integrity indirect call checks. +// Security Note: if you just need to allow calling of dlsym functions use +// DISABLE_CFI_DLSYM. +#if !defined(DISABLE_CFI_ICALL) +#if BUILDFLAG(IS_WIN) +// Windows also needs __declspec(guard(nocf)). +#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") __declspec(guard(nocf)) +#else +#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") +#endif +#endif +#if !defined(DISABLE_CFI_ICALL) +#define DISABLE_CFI_ICALL +#endif + +// DISABLE_CFI_DLSYM -- applies DISABLE_CFI_ICALL on platforms where dlsym +// functions must be called. Retains CFI checks on platforms where loaded +// modules participate in CFI (e.g. Windows). +#if !defined(DISABLE_CFI_DLSYM) +#if BUILDFLAG(IS_WIN) +// Windows modules register functions when loaded so can be checked by CFG. +#define DISABLE_CFI_DLSYM +#else +#define DISABLE_CFI_DLSYM DISABLE_CFI_ICALL +#endif +#endif +#if !defined(DISABLE_CFI_DLSYM) +#define DISABLE_CFI_DLSYM +#endif + +// Macro useful for writing cross-platform function pointers. +#if !defined(CDECL) +#if BUILDFLAG(IS_WIN) +#define CDECL __cdecl +#else // BUILDFLAG(IS_WIN) +#define CDECL +#endif // BUILDFLAG(IS_WIN) +#endif // !defined(CDECL) + +// Macro for hinting that an expression is likely to be false. +#if !defined(UNLIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define UNLIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(UNLIKELY) + +#if !defined(LIKELY) +#if defined(COMPILER_GCC) || defined(__clang__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define LIKELY(x) (x) +#endif // defined(COMPILER_GCC) +#endif // !defined(LIKELY) + +// Compiler feature-detection. +// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension +#if defined(__has_feature) +#define HAS_FEATURE(FEATURE) __has_feature(FEATURE) +#else +#define HAS_FEATURE(FEATURE) 0 +#endif + +#if defined(COMPILER_GCC) +#define PRETTY_FUNCTION __PRETTY_FUNCTION__ +#elif defined(COMPILER_MSVC) +#define PRETTY_FUNCTION __FUNCSIG__ +#else +// See https://en.cppreference.com/w/c/language/function_definition#func +#define PRETTY_FUNCTION __func__ +#endif + +#if !defined(CPU_ARM_NEON) +#if defined(__arm__) +#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \ + !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID) +#error Chromium does not support middle endian architecture +#endif +#if defined(__ARM_NEON__) +#define CPU_ARM_NEON 1 +#endif +#endif // defined(__arm__) +#endif // !defined(CPU_ARM_NEON) + +#if !defined(HAVE_MIPS_MSA_INTRINSICS) +#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) +#define HAVE_MIPS_MSA_INTRINSICS 1 +#endif +#endif + +#if defined(__clang__) && HAS_ATTRIBUTE(uninitialized) +// Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for +// the specified variable. +// Library-wide alternative is +// 'configs -= [ "//build/config/compiler:default_init_stack_vars" ]' in .gn +// file. +// +// See "init_stack_vars" in build/config/compiler/BUILD.gn and +// http://crbug.com/977230 +// "init_stack_vars" is enabled for non-official builds and we hope to enable it +// in official build in 2020 as well. The flag writes fixed pattern into +// uninitialized parts of all local variables. In rare cases such initialization +// is undesirable and attribute can be used: +// 1. Degraded performance +// In most cases compiler is able to remove additional stores. E.g. if memory is +// never accessed or properly initialized later. Preserved stores mostly will +// not affect program performance. However if compiler failed on some +// performance critical code we can get a visible regression in a benchmark. +// 2. memset, memcpy calls +// Compiler may replaces some memory writes with memset or memcpy calls. This is +// not -ftrivial-auto-var-init specific, but it can happen more likely with the +// flag. It can be a problem if code is not linked with C run-time library. +// +// Note: The flag is security risk mitigation feature. So in future the +// attribute uses should be avoided when possible. However to enable this +// mitigation on the most of the code we need to be less strict now and minimize +// number of exceptions later. So if in doubt feel free to use attribute, but +// please document the problem for someone who is going to cleanup it later. +// E.g. platform, bot, benchmark or test name in patch description or next to +// the attribute. +#define STACK_UNINITIALIZED [[clang::uninitialized]] +#else +#define STACK_UNINITIALIZED +#endif + +// Attribute "no_stack_protector" disables -fstack-protector for the specified +// function. +// +// "stack_protector" is enabled on most POSIX builds. The flag adds a canary +// to each stack frame, which on function return is checked against a reference +// canary. If the canaries do not match, it's likely that a stack buffer +// overflow has occurred, so immediately crashing will prevent exploitation in +// many cases. +// +// In some cases it's desirable to remove this, e.g. on hot functions, or if +// we have purposely changed the reference canary. +#if defined(COMPILER_GCC) || defined(__clang__) +#if HAS_ATTRIBUTE(__no_stack_protector__) +#define NO_STACK_PROTECTOR __attribute__((__no_stack_protector__)) +#else +#define NO_STACK_PROTECTOR __attribute__((__optimize__("-fno-stack-protector"))) +#endif +#else +#define NO_STACK_PROTECTOR +#endif + +// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints +// to Clang which control what code paths are statically analyzed, +// and is meant to be used in conjunction with assert & assert-like functions. +// The expression is passed straight through if analysis isn't enabled. +// +// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current +// codepath and any other branching codepaths that might follow. +#if defined(__clang_analyzer__) + +inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) { + return false; +} + +inline constexpr bool AnalyzerAssumeTrue(bool arg) { + // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is + // false. + return arg || AnalyzerNoReturn(); +} + +#define ANALYZER_ASSUME_TRUE(arg) ::AnalyzerAssumeTrue(!!(arg)) +#define ANALYZER_SKIP_THIS_PATH() static_cast(::AnalyzerNoReturn()) + +#else // !defined(__clang_analyzer__) + +#define ANALYZER_ASSUME_TRUE(arg) (arg) +#define ANALYZER_SKIP_THIS_PATH() + +#endif // defined(__clang_analyzer__) + +// Use nomerge attribute to disable optimization of merging multiple same calls. +#if defined(__clang__) && HAS_ATTRIBUTE(nomerge) +#define NOMERGE [[clang::nomerge]] +#else +#define NOMERGE +#endif + +// Marks a type as being eligible for the "trivial" ABI despite having a +// non-trivial destructor or copy/move constructor. Such types can be relocated +// after construction by simply copying their memory, which makes them eligible +// to be passed in registers. The canonical example is std::unique_ptr. +// +// Use with caution; this has some subtle effects on constructor/destructor +// ordering and will be very incorrect if the type relies on its address +// remaining constant. When used as a function argument (by value), the value +// may be constructed in the caller's stack frame, passed in a register, and +// then used and destructed in the callee's stack frame. A similar thing can +// occur when values are returned. +// +// TRIVIAL_ABI is not needed for types which have a trivial destructor and +// copy/move constructors, such as base::TimeTicks and other POD. +// +// It is also not likely to be effective on types too large to be passed in one +// or two registers on typical target ABIs. +// +// See also: +// https://clang.llvm.org/docs/AttributeReference.html#trivial-abi +// https://libcxx.llvm.org/docs/DesignDocs/UniquePtrTrivialAbi.html +#if defined(__clang__) && HAS_ATTRIBUTE(trivial_abi) +#define TRIVIAL_ABI [[clang::trivial_abi]] +#else +#define TRIVIAL_ABI +#endif + +// Detect whether a type is trivially relocatable, ie. a move-and-destroy +// sequence can replaced with memmove(). This can be used to optimise the +// implementation of containers. This is automatically true for types that were +// defined with TRIVIAL_ABI such as scoped_refptr. +// +// See also: +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1144r8.html +// https://clang.llvm.org/docs/LanguageExtensions.html#:~:text=__is_trivially_relocatable +#if defined(__clang__) && HAS_BUILTIN(__is_trivially_relocatable) +#define IS_TRIVIALLY_RELOCATABLE(t) __is_trivially_relocatable(t) +#else +#define IS_TRIVIALLY_RELOCATABLE(t) false +#endif + +// Marks a member function as reinitializing a moved-from variable. +// See also +// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/use-after-move.html#reinitialization +#if defined(__clang__) && HAS_ATTRIBUTE(reinitializes) +#define REINITIALIZES_AFTER_MOVE [[clang::reinitializes]] +#else +#define REINITIALIZES_AFTER_MOVE +#endif + +#if defined(__clang__) +#define GSL_OWNER [[gsl::Owner]] +#define GSL_POINTER [[gsl::Pointer]] +#else +#define GSL_OWNER +#define GSL_POINTER +#endif + +// Adds the "logically_const" tag to a symbol's mangled name. The "Mutable +// Constants" check [1] detects instances of constants that aren't in .rodata, +// e.g. due to a missing `const`. Using this tag suppresses the check for this +// symbol, allowing it to live outside .rodata without a warning. +// +// [1]: +// https://crsrc.org/c/docs/speed/binary_size/android_binary_size_trybot.md#Mutable-Constants +#if defined(COMPILER_GCC) || defined(__clang__) +#define LOGICALLY_CONST [[gnu::abi_tag("logically_const")]] +#else +#define LOGICALLY_CONST +#endif + +// preserve_most clang's calling convention. Reduces register pressure for the +// caller and as such can be used for cold calls. Support for the +// "preserve_most" attribute is limited: +// - 32-bit platforms do not implement it, +// - component builds fail because _dl_runtime_resolve() clobbers registers, +// - there are crashes on arm64 on Windows (https://crbug.com/v8/14065), which +// can hopefully be fixed in the future. +// Additionally, the initial implementation in clang <= 16 overwrote the return +// register(s) in the epilogue of a preserve_most function, so we only use +// preserve_most in clang >= 17 (see https://reviews.llvm.org/D143425). +// Clang only supports preserve_most on X86-64 and AArch64 for now. +// See https://clang.llvm.org/docs/AttributeReference.html#preserve-most for +// more details. +#if (defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_X86_64)) && \ + !(BUILDFLAG(IS_WIN) && defined(ARCH_CPU_ARM64)) && \ + !defined(COMPONENT_BUILD) && defined(__clang__) && \ + __clang_major__ >= 17 && HAS_ATTRIBUTE(preserve_most) +#define PRESERVE_MOST __attribute__((preserve_most)) +#else +#define PRESERVE_MOST +#endif + +// Mark parameters or return types as having a lifetime attached to the class. +// +// When used to mark a method's pointer/reference parameter, the compiler is +// made aware that it will be stored internally in the class and the pointee +// must outlive the class. Typically used on constructor arguments. It should +// appear to the right of the parameter's variable name. +// +// Example: +// ``` +// struct S { +// S(int* p LIFETIME_BOUND) : ptr_(p) {} +// +// int* ptr_; +// }; +// ``` +// +// When used on a method with a return value, the compiler is made aware that +// the returned type is/has a pointer to the internals of the class, and must +// not outlive the class object. It should appear after any method qualifiers. +// +// Example: +// ``` +// struct S { +// int* GetPtr() const LIFETIME_BOUND { return i_; }; +// +// int i_; +// }; +// ``` +// +// This allows the compiler to warn in (a limited set of) cases where the +// pointer would otherwise be left dangling, especially in cases where the +// pointee would be a destroyed temporary. +// +// Docs: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound +#if defined(__clang__) +#define LIFETIME_BOUND [[clang::lifetimebound]] +#else +#define LIFETIME_BOUND +#endif + +// Mark a function as pure, meaning that it does not have side effects, meaning +// that it does not write anything external to the function's local variables +// and return value. +// +// WARNING: If this attribute is mis-used it will result in UB and +// miscompilation, as the optimizator may fold multiple calls into one and +// reorder them inappropriately. This shouldn't appear outside of key vocabulary +// types. It allows callers to work with the vocab type directly, and call its +// methods without having to worry about caching things into local variables in +// hot code. +// +// This attribute must not appear on functions that make use of function +// pointers, virtual methods, or methods of templates (including operators like +// comparison), as the "pure" function can not know what those functions do and +// can not guarantee there will never be sideeffects. +#if defined(COMPILER_GCC) || defined(__clang__) +#define PURE_FUNCTION [[gnu::pure]] +#else +#define PURE_FUNCTION +#endif + +// Functions should be marked with UNSAFE_BUFFER_USAGE when they lead to +// out-of-bounds bugs when called with incorrect inputs. +// +// Ideally such functions should be paired with a safer version that works with +// safe primitives like `base::span`. Otherwise, another safer coding pattern +// should be documented along side the use of `UNSAFE_BUFFER_USAGE`. +// +// All functions marked with UNSAFE_BUFFER_USAGE should come with a safety +// comment that explains the requirements of the function to prevent an +// out-of-bounds bug. For example: +// ``` +// // Function to do things between `input` and `end`. +// // +// // # Safety +// // The `input` must point to an array with size at least 5. The `end` must +// // point within the same allocation of `input` and not come before `input`. +// ``` +// +// The requirements described in the safety comment must be sufficient to +// guarantee that the function never goes out of bounds. Annotating a function +// in this way means that all callers will be required to wrap the call in an +// `UNSAFE_BUFFERS()` macro (see below), with a comment justifying how it meets +// the requirements. +#if defined(__clang__) && HAS_ATTRIBUTE(unsafe_buffer_usage) +#define UNSAFE_BUFFER_USAGE [[clang::unsafe_buffer_usage]] +#else +#define UNSAFE_BUFFER_USAGE +#endif + +// UNSAFE_BUFFERS() wraps code that violates the -Wunsafe-buffer-usage warning, +// such as: +// - pointer arithmetic, +// - pointer subscripting, and +// - calls to functions annotated with UNSAFE_BUFFER_USAGE. +// +// This indicates code whose bounds correctness cannot be ensured +// systematically, and thus requires manual review. +// +// ** USE OF THIS MACRO SHOULD BE VERY RARE.** This should only be used when +// strictly necessary. Prefer to use `base::span` instead of pointers, or other +// safer coding patterns (like std containers) that avoid the opportunity for +// out-of-bounds bugs to creep into the code. Any use of UNSAFE_BUFFERS() can +// lead to a critical security bug if any assumptions are wrong, or ever become +// wrong in the future. +// +// The macro should be used to wrap the minimum necessary code, to make it clear +// what is unsafe, and prevent accidentally opting extra things out of the +// warning. +// +// All usage of UNSAFE_BUFFERS() should come with a `// SAFETY: ...` comment +// that explains how we have guaranteed that the pointer usage can never go +// out-of-bounds, or that the requirements of the UNSAFE_BUFFER_USAGE function +// are met. The safety comment should allow a reader to check that all +// requirements have been met, using only local invariants. Examples of local +// invariants include: +// - Runtime conditions or CHECKs near the UNSAFE_BUFFERS macros +// - Invariants guaranteed by types in the surrounding code +// - Invariants guaranteed by function calls in the surrounding code +// - Caller requirements, if the containing function is itself marked with +// UNSAFE_BUFFER_USAGE +// +// The last case should be an option of last resort. It is less safe and will +// require the caller also use the UNSAFE_BUFFERS() macro. Prefer directly +// capturing such invariants in types like `base::span`. +// +// Safety explanations may not rely on invariants that are not fully +// encapsulated close to the UNSAFE_BUFFERS() usage. Instead, use safer coding +// patterns or stronger invariants. +#if defined(__clang__) +// clang-format off +// Formatting is off so that we can put each _Pragma on its own line, as +// recommended by the gcc docs. +#define UNSAFE_BUFFERS(...) \ + _Pragma("clang unsafe_buffer_usage begin") \ + __VA_ARGS__ \ + _Pragma("clang unsafe_buffer_usage end") +// clang-format on +#else +#define UNSAFE_BUFFERS(...) __VA_ARGS__ +#endif + +// Defines a condition for a function to be checked at compile time if the +// parameter's value is known at compile time. If the condition is failed, the +// function is omitted from the overload set resolution, much like `requires`. +// +// If the parameter is a runtime value, then the condition is unable to be +// checked and the function will be omitted from the overload set resolution. +// This ensures the function can only be called with values known at compile +// time. This is a clang extension. +// +// Example: +// ``` +// void f(int a) ENABLE_IF_ATTR(a > 0) {} +// f(1); // Ok. +// f(0); // Error: no valid f() found. +// ``` +// +// The `ENABLE_IF_ATTR` annotation is preferred over `consteval` with a check +// that breaks compile because metaprogramming does not observe such checks. So +// with `consteval`, the function looks callable to concepts/type_traits but is +// not and will fail to compile even though it reports it's usable. Whereas +// `ENABLE_IF_ATTR` interacts correctly with metaprogramming. This is especially +// painful for constructors. See also +// https://github.com/chromium/subspace/issues/266. +#if defined(__clang__) +#define ENABLE_IF_ATTR(cond, msg) __attribute__((enable_if(cond, msg))) +#else +#define ENABLE_IF_ATTR(cond, msg) +#endif + +#endif // WEBF_COMPILER_SPECIFIC_H diff --git a/bridge/bindings/v8/base/memory/raw_ptr.h b/bridge/bindings/v8/base/memory/raw_ptr.h new file mode 100644 index 0000000000..38afa69ab4 --- /dev/null +++ b/bridge/bindings/v8/base/memory/raw_ptr.h @@ -0,0 +1,16 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BASE_MEMORY_RAW_PTR_H_ +#define BASE_MEMORY_RAW_PTR_H_ + +#include "bindings/v8/base/compiler_specific.h" + +// Although `raw_ptr` is part of the standalone PA distribution, it is +// easier to use the shorter path in `//base/memory`. We retain this +// facade header for ease of typing. +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h" // IWYU pragma: export + +#endif // BASE_MEMORY_RAW_PTR_H_ \ No newline at end of file diff --git a/bridge/bindings/v8/base/memory/raw_ptr_exclusion.h b/bridge/bindings/v8/base/memory/raw_ptr_exclusion.h new file mode 100644 index 0000000000..a2b9640db7 --- /dev/null +++ b/bridge/bindings/v8/base/memory/raw_ptr_exclusion.h @@ -0,0 +1,14 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef BASE_MEMORY_RAW_PTR_EXCLUSION_H_ +#define BASE_MEMORY_RAW_PTR_EXCLUSION_H_ + +// Although `raw_ptr` is part of the standalone PA distribution, it is +// easier to use the shorter path in `//base/memory`. We retain this +// facade header for ease of typing. +#include "bindings/v8/base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr_exclusion.h" // IWYU pragma: export + +#endif // BASE_MEMORY_RAW_PTR_EXCLUSION_H_ \ No newline at end of file diff --git a/bridge/bindings/v8/base/memory/scoped_refptr.h b/bridge/bindings/v8/base/memory/scoped_refptr.h new file mode 100644 index 0000000000..6e9a3f69d2 --- /dev/null +++ b/bridge/bindings/v8/base/memory/scoped_refptr.h @@ -0,0 +1,399 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_SCOPED_REFPTR_H +#define WEBF_SCOPED_REFPTR_H + +#include + +#include +#include +#include +#include +#include + +//#include "base/check.h" +#include "../compiler_specific.h" +#include "bindings/v8/base/memory/raw_ptr_exclusion.h" +//#include "base/memory/raw_ptr_exclusion.h" + +template +class scoped_refptr; + +namespace base { + +template +class RefCounted; +template +class RefCountedThreadSafe; +template +class RefCountedDeleteOnSequence; +class SequencedTaskRunner; + +template +scoped_refptr AdoptRef(T* t); + +namespace subtle { + +enum AdoptRefTag { kAdoptRefTag }; +enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag }; +enum StartRefCountFromOneTag { kStartRefCountFromOneTag }; + +template +struct RefCountPreferenceTagTraits; + +template <> +struct RefCountPreferenceTagTraits { + static constexpr StartRefCountFromZeroTag kTag = kStartRefCountFromZeroTag; +}; + +template <> +struct RefCountPreferenceTagTraits { + static constexpr StartRefCountFromOneTag kTag = kStartRefCountFromOneTag; +}; + +template +constexpr Tag GetRefCountPreference() { + return RefCountPreferenceTagTraits::kTag; +} + +// scoped_refptr is typically used with one of several RefCounted base +// classes or with custom AddRef and Release methods. These overloads dispatch +// on which was used. + +template +constexpr bool IsRefCountPreferenceOverridden(const T*, + const RefCounted*) { + return !std::same_as())>, + std::decay_t())>>; +} + +template +constexpr bool IsRefCountPreferenceOverridden( + const T*, + const RefCountedThreadSafe*) { + return !std::same_as())>, + std::decay_t())>>; +} + +template +constexpr bool IsRefCountPreferenceOverridden( + const T*, + const RefCountedDeleteOnSequence*) { + return !std::same_as())>, + std::decay_t())>>; +} + +constexpr bool IsRefCountPreferenceOverridden(...) { + return false; +} + +template +constexpr void AssertRefCountBaseMatches(const T*, const RefCounted*) { + static_assert(std::derived_from, + "T implements RefCounted, but U is not a base of T."); +} + +template +constexpr void AssertRefCountBaseMatches(const T*, + const RefCountedThreadSafe*) { + static_assert( + std::derived_from, + "T implements RefCountedThreadSafe, but U is not a base of T."); +} + +template +constexpr void AssertRefCountBaseMatches(const T*, + const RefCountedDeleteOnSequence*) { + static_assert( + std::derived_from, + "T implements RefCountedDeleteOnSequence, but U is not a base of T."); +} + +constexpr void AssertRefCountBaseMatches(...) {} + +} // namespace subtle + +// Creates a scoped_refptr from a raw pointer without incrementing the reference +// count. Use this only for a newly created object whose reference count starts +// from 1 instead of 0. +template +scoped_refptr AdoptRef(T* obj) { + using Tag = std::decay_t())>; + static_assert(std::same_as, + "Use AdoptRef only if the reference count starts from one."); + + DCHECK(obj); + DCHECK(obj->HasOneRef()); + obj->Adopted(); + return scoped_refptr(obj, subtle::kAdoptRefTag); +} + +namespace subtle { + +template +scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromZeroTag) { + return scoped_refptr(obj); +} + +template +scoped_refptr AdoptRefIfNeeded(T* obj, StartRefCountFromOneTag) { + return AdoptRef(obj); +} + +} // namespace subtle + +// Constructs an instance of T, which is a ref counted type, and wraps the +// object into a scoped_refptr. +template +scoped_refptr MakeRefCounted(Args&&... args) { + T* obj = new T(std::forward(args)...); + return subtle::AdoptRefIfNeeded(obj, subtle::GetRefCountPreference()); +} + +// Takes an instance of T, which is a ref counted type, and wraps the object +// into a scoped_refptr. +template +scoped_refptr WrapRefCounted(T* t) { + return scoped_refptr(t); +} + +} // namespace base + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// private: +// friend class RefCounted; // Allow destruction by RefCounted<>. +// ~MyFoo(); // Destructor must be private/protected. +// }; +// +// void some_function() { +// scoped_refptr foo = MakeRefCounted(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = MakeRefCounted(); +// ... +// foo.reset(); // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = MakeRefCounted(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references nullptr. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = MakeRefCounted(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// +// Also see Chromium's ownership and calling conventions: +// https://chromium.googlesource.com/chromium/src/+/lkgr/styleguide/c++/c++.md#object-ownership-and-calling-conventions +// Specifically: +// If the function (at least sometimes) takes a ref on a refcounted object, +// declare the param as scoped_refptr. The caller can decide whether it +// wishes to transfer ownership (by calling std::move(t) when passing t) or +// retain its ref (by simply passing t directly). +// In other words, use scoped_refptr like you would a std::unique_ptr except +// in the odd case where it's required to hold on to a ref while handing one +// to another component (if a component merely needs to use t on the stack +// without keeping a ref: pass t as a raw T*). +template +class TRIVIAL_ABI scoped_refptr { + public: + typedef T element_type; + + constexpr scoped_refptr() = default; + + // Allow implicit construction from nullptr. + constexpr scoped_refptr(std::nullptr_t) {} + + // Constructs from a raw pointer. Note that this constructor allows implicit + // conversion from T* to scoped_refptr which is strongly discouraged. If + // you are creating a new ref-counted object please use + // base::MakeRefCounted() or base::WrapRefCounted(). Otherwise you + // should move or copy construct from an existing scoped_refptr to the + // ref-counted object. + scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + AddRef(ptr_); + } + + // Copy constructor. This is required in addition to the copy conversion + // constructor below. + scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} + + // Copy conversion constructor. + template + requires(std::convertible_to) + scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {} + + // Move constructor. This is required in addition to the move conversion + // constructor below. + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; } + + // Move conversion constructor. + template + requires(std::convertible_to) + scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { + r.ptr_ = nullptr; + } + + ~scoped_refptr() { + static_assert(!base::subtle::IsRefCountPreferenceOverridden( + static_cast(nullptr), static_cast(nullptr)), + "It's unsafe to override the ref count preference." + " Please remove REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE" + " from subclasses."); + if (ptr_) + Release(ptr_); + } + + T* get() const { return ptr_; } + + T& operator*() const { + DCHECK(ptr_); + return *ptr_; + } + + T* operator->() const { + DCHECK(ptr_); + return ptr_; + } + + scoped_refptr& operator=(std::nullptr_t) { + reset(); + return *this; + } + + scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); } + + // Unified assignment operator. + scoped_refptr& operator=(scoped_refptr r) noexcept { + swap(r); + return *this; + } + + // Sets managed object to null and releases reference to the previous managed + // object, if it existed. + void reset() { scoped_refptr().swap(*this); } + + // Returns the owned pointer (if any), releasing ownership to the caller. The + // caller is responsible for managing the lifetime of the reference. + [[nodiscard]] T* release(); + + void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); } + + explicit operator bool() const { return ptr_ != nullptr; } + + template + friend bool operator==(const scoped_refptr& lhs, + const scoped_refptr& rhs) { + return lhs.ptr_ == rhs.ptr_; + } + + // This operator is an optimization to avoid implicitly constructing a + // scoped_refptr when comparing scoped_refptr against raw pointer. If the + // implicit conversion is ever removed this operator can also be removed. + template + friend bool operator==(const scoped_refptr& lhs, const U* rhs) { + return lhs.ptr_ == rhs; + } + + friend bool operator==(const scoped_refptr& lhs, std::nullptr_t null) { + return !static_cast(lhs); + } + + template + friend auto operator<=>(const scoped_refptr& lhs, + const scoped_refptr& rhs) { + return lhs.ptr_ <=> rhs.ptr_; + } + + friend auto operator<=>(const scoped_refptr& lhs, std::nullptr_t null) { + return lhs.ptr_ <=> static_cast(nullptr); + } + + protected: + // RAW_PTR_EXCLUSION: scoped_refptr<> has its own UaF prevention mechanism. + // Given how widespread it is, we it'll likely a perf regression for no + // additional security benefit. + RAW_PTR_EXCLUSION T* ptr_ = nullptr; + + private: + template + friend scoped_refptr base::AdoptRef(U*); + friend class ::base::SequencedTaskRunner; + + scoped_refptr(T* p, base::subtle::AdoptRefTag) : ptr_(p) {} + + // Friend required for move constructors that set r.ptr_ to null. + template + friend class scoped_refptr; + + // Non-inline helpers to allow: + // class Opaque; + // extern template class scoped_refptr; + // Otherwise the compiler will complain that Opaque is an incomplete type. + static void AddRef(T* ptr); + static void Release(T* ptr); +}; + +template +T* scoped_refptr::release() { + T* ptr = ptr_; + ptr_ = nullptr; + return ptr; +} + +// static +template +void scoped_refptr::AddRef(T* ptr) { + base::subtle::AssertRefCountBaseMatches(ptr, ptr); + ptr->AddRef(); +} + +// static +template +void scoped_refptr::Release(T* ptr) { + base::subtle::AssertRefCountBaseMatches(ptr, ptr); + ptr->Release(); +} + +template +std::ostream& operator<<(std::ostream& out, const scoped_refptr& p) { + return out << p.get(); +} + +template +void swap(scoped_refptr& lhs, scoped_refptr& rhs) noexcept { + lhs.swap(rhs); +} + +#endif // WEBF_SCOPED_REFPTR_H diff --git a/bridge/bindings/v8/base/memory/stack_allocated.h b/bridge/bindings/v8/base/memory/stack_allocated.h new file mode 100644 index 0000000000..e57df0f8a7 --- /dev/null +++ b/bridge/bindings/v8/base/memory/stack_allocated.h @@ -0,0 +1,60 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_STACK_ALLOCATED_H +#define WEBF_STACK_ALLOCATED_H + +#include + +#if defined(__clang__) +#define STACK_ALLOCATED_IGNORE(reason) \ + __attribute__((annotate("stack_allocated_ignore"))) +#else // !defined(__clang__) +#define STACK_ALLOCATED_IGNORE(reason) +#endif // !defined(__clang__) + +// If a class or one of its ancestor classes is annotated with STACK_ALLOCATED() +// in its class definition, then instances of the class may not be allocated on +// the heap or as a member variable of a non-stack-allocated class. +#define STACK_ALLOCATED() \ + public: \ + using IsStackAllocatedTypeMarker [[maybe_unused]] = int; \ + \ + private: \ + void* operator new(size_t) = delete; \ + void* operator new(size_t, ::base::NotNullTag, void*) = delete; \ + void* operator new(size_t, void*) = delete + +namespace base { + +// NotNullTag was originally added to WebKit here: +// https://trac.webkit.org/changeset/103243/webkit +// ...with the stated goal of improving the performance of the placement new +// operator and potentially enabling the -fomit-frame-pointer compiler flag. +// +// TODO(szager): The placement new operator which uses this tag is currently +// defined in third_party/blink/renderer/platform/wtf/allocator/allocator.h, +// in the global namespace. It should probably move to /base. +// +// It's unknown at the time of writing whether it still provides any benefit +// (or if it ever did). It is used by placing the kNotNull tag before the +// address of the object when calling placement new. +// +// If the kNotNull tag is specified to placement new for a null pointer, +// Undefined Behaviour can result. +// +// Example: +// +// union { int i; } u; +// +// // Typically placement new looks like this. +// new (&u.i) int(3); +// // But we can promise `&u.i` is not null like this. +// new (base::NotNullTag::kNotNull, &u.i) int(3); +enum class NotNullTag { kNotNull }; + +} // namespace base + +#endif // WEBF_STACK_ALLOCATED_H diff --git a/bridge/bindings/v8/base/numerics/checked_math.h b/bridge/bindings/v8/base/numerics/checked_math.h new file mode 100644 index 0000000000..92a1c50dbd --- /dev/null +++ b/bridge/bindings/v8/base/numerics/checked_math.h @@ -0,0 +1,378 @@ +/* +* Copyright (C) 2019-2022 The Kraken authors. All rights reserved. +* Copyright (C) 2022-present The WebF authors. All rights reserved. +*/ + +#ifndef WEBF_CHECKED_MATH_H +#define WEBF_CHECKED_MATH_H + +#include +#include +#include + +#include "bindings/v8/base/numerics/checked_math_impl.h" // IWYU pragma: export +#include "bindings/v8/base/numerics/safe_conversions.h" +#include "bindings/v8/base/numerics/safe_math_shared_impl.h" // IWYU pragma: export + +namespace base { +namespace internal { + +template +class CheckedNumeric { + static_assert(std::is_arithmetic_v, + "CheckedNumeric: T must be a numeric type."); + + public: + template + friend class CheckedNumeric; + + using type = T; + + constexpr CheckedNumeric() = default; + + // Copy constructor. + template + constexpr CheckedNumeric(const CheckedNumeric& rhs) + : state_(rhs.state_.value(), rhs.IsValid()) {} + + // Strictly speaking, this is not necessary, but declaring this allows class + // template argument deduction to be used so that it is possible to simply + // write `CheckedNumeric(777)` instead of `CheckedNumeric(777)`. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr CheckedNumeric(T value) : state_(value) {} + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template + requires(std::is_arithmetic_v) + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr CheckedNumeric(Src value) : state_(value) {} + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr CheckedNumeric(StrictNumeric value) + : state_(static_cast(value)) {} + + // IsValid() - The public API to test if a CheckedNumeric is currently valid. + // A range checked destination type can be supplied using the Dst template + // parameter. + template + constexpr bool IsValid() const { + return state_.is_valid() && + IsValueInRangeForNumericType(state_.value()); + } + + // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid + // and is within the range supported by the destination type. Returns true if + // successful and false otherwise. + template +#if defined(__clang__) || defined(__GNUC__) + __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) + _Check_return_ +#endif + constexpr bool + AssignIfValid(Dst* result) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? ((*result = static_cast(state_.value())), true) + : false; + } + + // ValueOrDie() - The primary accessor for the underlying value. If the + // current state is not valid it will CHECK and crash. + // A range checked destination type can be supplied using the Dst template + // parameter, which will trigger a CHECK if the value is not in bounds for + // the destination. + // The CHECK behavior can be overridden by supplying a handler as a + // template parameter, for test code, etc. However, the handler cannot access + // the underlying value, and it is not available through other means. + template + constexpr StrictNumeric ValueOrDie() const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : CheckHandler::template HandleFailure(); + } + + // ValueOrDefault(T default_value) - A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + // A range checked destination type can be supplied using the Dst template + // parameter. WARNING: This function may fail to compile or CHECK at runtime + // if the supplied default_value is not within range of the destination type. + template + constexpr StrictNumeric ValueOrDefault(const Src default_value) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : checked_cast(default_value); + } + + // Returns a checked numeric of the specified type, cast from the current + // CheckedNumeric. If the current state is invalid or the destination cannot + // represent the result then the returned CheckedNumeric will be invalid. + template + constexpr CheckedNumeric::type> Cast() const { + return *this; + } + + // This friend method is available solely for providing more detailed logging + // in the tests. Do not implement it in production code, because the + // underlying values may change at any time. + template + friend U GetNumericValueForTest(const CheckedNumeric& src); + + // Prototypes for the supported arithmetic operator overloads. + template + constexpr CheckedNumeric& operator+=(const Src rhs); + template + constexpr CheckedNumeric& operator-=(const Src rhs); + template + constexpr CheckedNumeric& operator*=(const Src rhs); + template + constexpr CheckedNumeric& operator/=(const Src rhs); + template + constexpr CheckedNumeric& operator%=(const Src rhs); + template + constexpr CheckedNumeric& operator<<=(const Src rhs); + template + constexpr CheckedNumeric& operator>>=(const Src rhs); + template + constexpr CheckedNumeric& operator&=(const Src rhs); + template + constexpr CheckedNumeric& operator|=(const Src rhs); + template + constexpr CheckedNumeric& operator^=(const Src rhs); + + constexpr CheckedNumeric operator-() const { + // Use an optimized code path for a known run-time variable. + if (!IsConstantEvaluated() && std::is_signed_v && + std::is_floating_point_v) { + return FastRuntimeNegate(); + } + // The negation of two's complement int min is int min. + const bool is_valid = + IsValid() && + (!std::is_signed_v || std::is_floating_point_v || + NegateWrapper(state_.value()) != std::numeric_limits::lowest()); + return CheckedNumeric(NegateWrapper(state_.value()), is_valid); + } + + constexpr CheckedNumeric operator~() const { + return CheckedNumeric( + InvertWrapper(state_.value()), IsValid()); + } + + constexpr CheckedNumeric Abs() const { + return !IsValueNegative(state_.value()) ? *this : -*this; + } + + template + constexpr CheckedNumeric::type> Max( + const U rhs) const { + return CheckMax(*this, rhs); + } + + template + constexpr CheckedNumeric::type> Min( + const U rhs) const { + return CheckMin(*this, rhs); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + constexpr CheckedNumeric::type> + UnsignedAbs() const { + return CheckedNumeric::type>( + SafeUnsignedAbs(state_.value()), state_.is_valid()); + } + + constexpr CheckedNumeric& operator++() { + *this += 1; + return *this; + } + + constexpr CheckedNumeric operator++(int) { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + constexpr CheckedNumeric& operator--() { + *this -= 1; + return *this; + } + + constexpr CheckedNumeric operator--(int) { + // TODO(pkasting): Consider std::exchange() once it's constexpr in C++20. + const CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These perform the actual math operations on the CheckedNumerics. + // Binary arithmetic operations. + template