diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index e21283ee30..5aafe56f45 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -85,21 +85,21 @@ 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_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 + foundation/ui_command_buffer.cc + foundation/ui_command_strategy.cc # polyfill/dist/polyfill.cc -# multiple_threading/dispatcher.cc -# multiple_threading/looper.cc + multiple_threading/dispatcher.cc + multiple_threading/looper.cc ${CMAKE_CURRENT_LIST_DIR}/third_party/dart/include/dart_api_dl.c ) @@ -650,8 +650,16 @@ elseif (${WEBF_JS_ENGINE} MATCHES "v8") bindings/v8/scoped_persistent.h bindings/v8/v8_per_context_data.cc bindings/v8/v8_script_state.cc + bindings/v8/binding_initializer.cc + bindings/v8/generated_code_helper.h + bindings/v8/member_installer.cc + ) + # Gen sources. + list(APPEND BRIDGE_SOURCE + out/names_installer.cc out/built_in_string.cc + out/v8_window_or_worker_global_scope.cc ) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -671,11 +679,11 @@ endif() list(APPEND BRIDGE_SOURCE # Core sources -# webf_bridge.cc -# core/executing_context.cc + webf_bridge.cc + core/executing_context.cc # core/script_forbidden_scope.cc # core/script_state.cc -# core/page.cc + core/page.cc # core/dart_methods.cc # core/api/exception_state.cc # core/api/event_target.cc @@ -693,8 +701,8 @@ list(APPEND BRIDGE_SOURCE # core/api/comment.cc # core/api/character_data.cc # core/api/script_value_ref.cc -# core/dart_isolate_context.cc -# core/dart_context_data.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 @@ -702,7 +710,7 @@ list(APPEND BRIDGE_SOURCE # 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/window_or_worker_global_scope.cc # core/frame/module_listener.cc # core/frame/module_listener_container.cc # core/frame/module_manager.cc diff --git a/bridge/bindings/v8/binding_initializer.cc b/bridge/bindings/v8/binding_initializer.cc new file mode 100644 index 0000000000..0e0122df99 --- /dev/null +++ b/bridge/bindings/v8/binding_initializer.cc @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + + +#include "binding_initializer.h" +#include "v8_window_or_worker_global_scope.h" + +namespace webf { + +void InstallBindings(ExecutingContext* context) { + // Must follow the inheritance order when install. + // Exp: Node extends EventTarget, EventTarget must be install first. + V8WindowOrWorkerGlobalScope::Install(context); + /*TODO support DOM API + QJSLocation::Install(context); + QJSModuleManager::Install(context); + QJSConsole::Install(context); + QJSEventTarget::Install(context); + QJSWindow::Install(context); + QJSEvent::Install(context); + QJSUIEvent::Install(context); + QJSErrorEvent::Install(context); + QJSPromiseRejectionEvent::Install(context); + QJSMessageEvent::Install(context); + QJSAnimationEvent::Install(context); + QJSCloseEvent::Install(context); + QJSHybridRouterChangeEvent::Install(context); + QJSFocusEvent::Install(context); + QJSGestureEvent::Install(context); + QJSHashchangeEvent::Install(context); + QJSInputEvent::Install(context); + QJSCustomEvent::Install(context); + QJSMouseEvent::Install(context); + QJSPointerEvent::Install(context); + QJSTouchEvent::Install(context); + QJSPopStateEvent::Install(context); + QJSTransitionEvent::Install(context); + QJSIntersectionChangeEvent::Install(context); + QJSKeyboardEvent::Install(context); + QJSNode::Install(context); + QJSNodeList::Install(context); + QJSDocument::Install(context); + QJSDocumentFragment::Install(context); + QJSCharacterData::Install(context); + QJSText::Install(context); + QJSComment::Install(context); + QJSElement::Install(context); + QJSHTMLElement::Install(context); + QJSWidgetElement::Install(context); + QJSHTMLDivElement::Install(context); + QJSHTMLHeadElement::Install(context); + QJSHTMLBodyElement::Install(context); + QJSHTMLHtmlElement::Install(context); + QJSHTMLIFrameElement::Install(context); + QJSHTMLAnchorElement::Install(context); + QJSHTMLImageElement::Install(context); + QJSHTMLInputElement::Install(context); + QJSHTMLTextareaElement::Install(context); + QJSHTMLButtonElement::Install(context); + QJSHTMLFormElement::Install(context); + QJSImage::Install(context); + QJSHTMLScriptElement::Install(context); + QJSHTMLLinkElement::Install(context); + QJSHTMLUnknownElement::Install(context); + QJSHTMLTemplateElement::Install(context); + QJSHTMLCanvasElement::Install(context); + QJSCanvasRenderingContext::Install(context); + QJSCanvasRenderingContext2D::Install(context); + QJSCanvasPattern::Install(context); + QJSCanvasGradient::Install(context); + QJSPath2D::Install(context); + QJSDOMMatrixReadOnly::Install(context); + QJSDOMMatrix::Install(context); + QJSDOMPointReadOnly::Install(context); + QJSDOMPoint::Install(context); + QJSCSSStyleDeclaration::Install(context); + QJSInlineCssStyleDeclaration::Install(context); + QJSComputedCssStyleDeclaration::Install(context); + QJSBoundingClientRect::Install(context); + QJSScreen::Install(context); + QJSBlob::Install(context); + QJSTouch::Install(context); + QJSTouchList::Install(context); + QJSDOMStringMap::Install(context); + QJSMutationObserver::Install(context); + QJSMutationRecord::Install(context); + QJSMutationObserverRegistration::Install(context); + QJSDOMTokenList::Install(context); + QJSPerformance::Install(context); + QJSPerformanceEntry::Install(context); + QJSPerformanceMark::Install(context); + QJSPerformanceMeasure::Install(context); + QJSHTMLCollection::Install(context); + QJSHTMLAllCollection::Install(context); + + // SVG + QJSSVGElement::Install(context); + QJSSVGGraphicsElement::Install(context); + QJSSVGGeometryElement::Install(context); + QJSSVGSVGElement::Install(context); + QJSSVGRectElement::Install(context); + QJSSVGTextContentElement::Install(context); + QJSSVGTextPositioningElement::Install(context); + QJSSVGPathElement::Install(context); + QJSSVGTextElement::Install(context); + QJSSVGGElement::Install(context); + QJSSVGCircleElement::Install(context); + QJSSVGEllipseElement::Install(context); + QJSSVGStyleElement::Install(context); + QJSSVGLineElement::Install(context); + QJSNativeLoader::Install(context); + + // Legacy bindings, not standard. + QJSElementAttributes::Install(context); + */ +} + +} // namespace webf \ No newline at end of file diff --git a/bridge/bindings/v8/binding_initializer.h b/bridge/bindings/v8/binding_initializer.h new file mode 100644 index 0000000000..a5a99e1267 --- /dev/null +++ b/bridge/bindings/v8/binding_initializer.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 WEBF_BINDING_INITIALIZER_H +#define WEBF_BINDING_INITIALIZER_H + +namespace webf { + +class ExecutingContext; + +void InstallBindings(ExecutingContext* context); + +} // namespace webf + +#endif //WEBF_BINDING_INITIALIZER_H diff --git a/bridge/bindings/v8/generated_code_helper.h b/bridge/bindings/v8/generated_code_helper.h new file mode 100644 index 0000000000..3c04c65d1f --- /dev/null +++ b/bridge/bindings/v8/generated_code_helper.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 WEBF_BRIDGE_BINDINGS_V8_GENERATED_CODE_HELPER_H_ +#define WEBF_BRIDGE_BINDINGS_V8_GENERATED_CODE_HELPER_H_ + +#include "atomic_string.h" +#include "script_value.h" + +#endif //WEBF_BRIDGE_BINDINGS_V8_GENERATED_CODE_HELPER_H_ diff --git a/bridge/bindings/v8/member_installer.cc b/bridge/bindings/v8/member_installer.cc new file mode 100644 index 0000000000..8b2590d9aa --- /dev/null +++ b/bridge/bindings/v8/member_installer.cc @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "member_installer.h" +#include "core/executing_context.h" + +namespace webf { + +void MemberInstaller::InstallFunctions(ExecutingContext* context, + std::initializer_list config) { + v8::Isolate* isolate = context->ctx(); + v8::Local v8_context = isolate->GetCurrentContext(); + v8::Local global = v8_context->Global(); + + for (const auto& function : config) { + v8::Local function_template = v8::FunctionTemplate::New(isolate, function.callback); + v8::Local v8_function = function_template->GetFunction(v8_context).ToLocalChecked(); + + global->Set(v8_context, v8::String::NewFromUtf8(isolate, function.name).ToLocalChecked(), v8_function).Check(); + } +} + +} // namespace webf diff --git a/bridge/bindings/v8/member_installer.h b/bridge/bindings/v8/member_installer.h new file mode 100644 index 0000000000..fb836a7401 --- /dev/null +++ b/bridge/bindings/v8/member_installer.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019-2022 The Kraken authors. All rights reserved. + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef WEBF_BRIDGE_BINDINGS_V8_MEMBER_INSTALLER_H_ +#define WEBF_BRIDGE_BINDINGS_V8_MEMBER_INSTALLER_H_ + +#include +#include + +namespace webf { + +class ExecutingContext; + +// A set of utility functions to define attributes members as ES properties. +class MemberInstaller { + public: + struct FunctionConfig { + FunctionConfig& operator=(const FunctionConfig&) = delete; + const char* name; + v8::FunctionCallback callback; + }; + + static void InstallFunctions(ExecutingContext* context, std::initializer_list config); +}; + +} // namespace webf + +#endif //WEBF_BRIDGE_BINDINGS_V8_MEMBER_INSTALLER_H_ diff --git a/bridge/core/dart_isolate_context.cc b/bridge/core/dart_isolate_context.cc index 464010c83b..0a4867c621 100644 --- a/bridge/core/dart_isolate_context.cc +++ b/bridge/core/dart_isolate_context.cc @@ -8,11 +8,11 @@ #endif #include #include "dart_isolate_context.h" -#include "event_factory.h" -#include "html_element_factory.h" +//#include "event_factory.h" +//#include "html_element_factory.h" #include "names_installer.h" #include "page.h" -#include "svg_element_factory.h" +//#include "svg_element_factory.h" namespace webf { @@ -129,9 +129,11 @@ void DartIsolateContext::FinalizeJSRuntime() { // Prebuilt strings stored in JSRuntime. Only needs to dispose when runtime disposed. names_installer::Dispose(); + /*TODO support HTMLElementFactory SVGElementFactory EventFactory HTMLElementFactory::Dispose(); SVGElementFactory::Dispose(); EventFactory::Dispose(); + */ #if WEBF_QUICKJS_JS_ENGINE ClearUpWires(runtime_); @@ -147,9 +149,11 @@ void DartIsolateContext::FinalizeJSRuntime() { DartIsolateContext::DartIsolateContext(const uint64_t* dart_methods, int32_t dart_methods_length, bool profile_enabled) : is_valid_(true), - running_thread_(std::this_thread::get_id()), - // TODO v8 suppport profiler_(std::make_unique(profile_enabled)), + running_thread_(std::this_thread::get_id()) { + /* TODO v8 suppport + profiler_(std::make_unique(profile_enabled)), dart_method_ptr_(std::make_unique(this, dart_methods, dart_methods_length)) { + */ is_valid_ = true; running_dart_isolates++; } diff --git a/bridge/core/dart_isolate_context.h b/bridge/core/dart_isolate_context.h index 1a8345e6c3..cfe4e57cc5 100644 --- a/bridge/core/dart_isolate_context.h +++ b/bridge/core/dart_isolate_context.h @@ -14,8 +14,8 @@ #include #include "dart_context_data.h" -#include "dart_methods.h" /* TODO support V8 +#include "dart_methods.h" #include "foundation/profiler.h" */ #include "multiple_threading/dispatcher.h" @@ -67,7 +67,9 @@ class DartIsolateContext { v8::Isolate* isolate(); #endif FORCE_INLINE bool valid() { return is_valid_; } + /* TODO support FORCE_INLINE DartMethodPointer* dartMethodPtr() const { return dart_method_ptr_.get(); } + */ FORCE_INLINE const std::unique_ptr& dispatcher() const { return dispatcher_; } FORCE_INLINE void SetDispatcher(std::unique_ptr&& dispatcher) { dispatcher_ = std::move(dispatcher); @@ -128,7 +130,7 @@ class DartIsolateContext { std::unordered_set> pages_in_ui_thread_; std::unique_ptr dispatcher_ = nullptr; // Dart methods ptr should keep alive when ExecutingContext is disposing. - const std::unique_ptr dart_method_ptr_ = nullptr; + // TODO const std::unique_ptr dart_method_ptr_ = nullptr; }; } // namespace webf diff --git a/bridge/core/executing_context.cc b/bridge/core/executing_context.cc index 6979e7bc38..602438d10d 100644 --- a/bridge/core/executing_context.cc +++ b/bridge/core/executing_context.cc @@ -4,19 +4,24 @@ */ #include "executing_context.h" -#include -#include "bindings/qjs/converter_impl.h" -#include "built_in_string.h" +//#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 "event_type_names.h" +//#include "event_type_names.h" #include "foundation/logging.h" -#include "polyfill.h" -#include "qjs_window.h" -#include "script_forbidden_scope.h" -#include "timing/performance.h" +//#include "polyfill.h" +//#include "qjs_window.h" +//#include "script_forbidden_scope.h" +//#include "timing/performance.h" +#if WEBF_QUICKJS_JS_ENGINE + +#elif WEBF_V8_JS_ENGINE + +#endif namespace webf { @@ -36,7 +41,7 @@ ExecutingContext::ExecutingContext(DartIsolateContext* dart_isolate_context, context_id_(context_id), dart_error_report_handler_(std::move(handler)), owner_(owner), - public_method_ptr_(std::make_unique()), + // TODO public_method_ptr_(std::make_unique()), is_dedicated_(is_dedicated), unique_id_(context_unique_id++), is_context_valid_(true) { @@ -69,11 +74,14 @@ ExecutingContext::ExecutingContext(DartIsolateContext* dart_isolate_context, #elif WEBF_V8_JS_ENGINE #endif + /*TODO support dart_isolate_context->profiler()->StartTrackSteps("ExecutingContext::InstallBindings"); + */ // Register all built-in native bindings. InstallBindings(this); + /* TODO support dart_isolate_context->profiler()->FinishTrackSteps(); dart_isolate_context->profiler()->StartTrackSteps("ExecutingContext::InstallDocument"); @@ -112,6 +120,7 @@ ExecutingContext::ExecutingContext(DartIsolateContext* dart_isolate_context, dart_isolate_context->profiler()->FinishTrackSteps(); ui_command_buffer_.AddCommand(UICommand::kFinishRecordingCommand, nullptr, nullptr, nullptr); + */ } ExecutingContext::~ExecutingContext() { @@ -119,6 +128,7 @@ ExecutingContext::~ExecutingContext() { valid_contexts[context_id_] = false; executing_context_status_->disposed = true; + /*TODO support // Check if current context have unhandled exceptions. JSValue exception = JS_GetException(script_state_.ctx()); if (JS_IsObject(exception) || JS_IsException(exception)) { @@ -133,18 +143,29 @@ ExecutingContext::~ExecutingContext() { for (auto& active_wrapper : active_wrappers_) { JS_FreeValue(ctx(), active_wrapper->ToQuickJSUnsafe()); } + */ } +#if WEBF_QUICKJS_JS_ENGINE ExecutingContext* ExecutingContext::From(JSContext* ctx) { return static_cast(JS_GetContextOpaque(ctx)); } +#elif WEBF_V8_JS_ENGINE +ExecutingContext* From(v8::Isolate* isolate) { + // TODO support + return nullptr; +} + +#endif + bool ExecutingContext::EvaluateJavaScript(const char* code, size_t code_len, uint8_t** parsed_bytecodes, uint64_t* bytecode_len, const char* sourceURL, int startLine) { + /*TODO support if (ScriptForbiddenScope::IsScriptForbidden()) { return false; } @@ -191,26 +212,35 @@ bool ExecutingContext::EvaluateJavaScript(const char* code, dart_isolate_context_->profiler()->FinishTrackSteps(); return success; + */ + return false; } bool ExecutingContext::EvaluateJavaScript(const char16_t* code, size_t length, const char* sourceURL, int startLine) { + /*TODO support std::string utf8Code = toUTF8(std::u16string(reinterpret_cast(code), length)); JSValue result = JS_Eval(script_state_.ctx(), utf8Code.c_str(), utf8Code.size(), sourceURL, JS_EVAL_TYPE_GLOBAL); DrainMicrotasks(); bool success = HandleException(&result); JS_FreeValue(script_state_.ctx(), result); return success; + */ + return false; } bool ExecutingContext::EvaluateJavaScript(const char* code, size_t codeLength, const char* sourceURL, int startLine) { + /*TODO support JSValue result = JS_Eval(script_state_.ctx(), code, codeLength, sourceURL, JS_EVAL_TYPE_GLOBAL); DrainMicrotasks(); bool success = HandleException(&result); JS_FreeValue(script_state_.ctx(), result); return success; + */ + return false; } bool ExecutingContext::EvaluateByteCode(uint8_t* bytes, size_t byteLength) { + /*TODO support dart_isolate_context_->profiler()->StartTrackSteps("ExecutingContext::EvaluateByteCode"); JSValue obj, val; @@ -240,6 +270,8 @@ bool ExecutingContext::EvaluateByteCode(uint8_t* bytes, size_t byteLength) { JS_FreeValue(script_state_.ctx(), val); dart_isolate_context_->profiler()->FinishTrackSteps(); return true; + */ + return false; } bool ExecutingContext::IsContextValid() const { @@ -251,13 +283,18 @@ void ExecutingContext::SetContextInValid() { } bool ExecutingContext::IsCtxValid() const { + /* TODO return script_state_.Invalid(); + */ + return false; } void* ExecutingContext::owner() { return owner_; } +#if WEBF_QUICKJS_JS_ENGINE + bool ExecutingContext::HandleException(JSValue* exc) { if (JS_IsException(*exc)) { JSValue error = JS_GetException(script_state_.ctx()); @@ -484,6 +521,11 @@ static void DispatchPromiseRejectionEvent(const AtomicString& event_type, } } +#elif WEBF_V8_JS_ENGINE + +#endif + +/* TODO support void ExecutingContext::FlushUICommand(const BindingObject* self, uint32_t reason) { std::vector deps; FlushUICommand(self, reason, deps); @@ -657,7 +699,7 @@ void ExecutingContext::RegisterActiveScriptWrappers(ScriptWrappable* script_wrap void ExecutingContext::InActiveScriptWrappers(ScriptWrappable* script_wrappable) { active_wrappers_.erase(script_wrappable); } - +*/ // A lock free context validator. bool isContextValid(double contextId) { if (contextId > running_context_list) diff --git a/bridge/core/executing_context.h b/bridge/core/executing_context.h index cb6099d91c..c138bdfb9e 100644 --- a/bridge/core/executing_context.h +++ b/bridge/core/executing_context.h @@ -7,9 +7,15 @@ #if WEBF_V8_JS_ENGINE #include +#include "bindings/v8/binding_initializer.h" +#include "bindings/v8/script_value.h" #elif WEBF_QUICKJS_JS_ENGINE #include #include +#include "bindings/qjs/binding_initializer.h" +#include "bindings/qjs/rejected_promises.h" +#include "bindings/qjs/script_value.h" +#include "native/native_loader.h" #endif #include @@ -23,20 +29,19 @@ #include #include #include -#include "bindings/qjs/binding_initializer.h" -#include "bindings/qjs/rejected_promises.h" -#include "bindings/qjs/script_value.h" #include "foundation/macros.h" #include "foundation/ui_command_buffer.h" -#include "native/native_loader.h" #include "plugin_api/executing_context.h" #include "dart_isolate_context.h" #include "dart_methods.h" +/*TODO support ExecutionContextData / frame #include "executing_context_data.h" #include "frame/dom_timer_coordinator.h" #include "frame/module_context_coordinator.h" #include "frame/module_listener_container.h" +*/ + #include "script_state.h" #include "shared_ui_command.h" @@ -52,7 +57,7 @@ class ExecutingContext; class Document; class Window; class Performance; -class MemberMutationScope; +// TODO class MemberMutationScope; class ErrorEvent; class DartContext; class MutationObserver; @@ -97,11 +102,11 @@ class ExecutingContext { bool HandleException(ScriptValue* exc); bool HandleException(ExceptionState& exception_state); bool HandleException(ExceptionState& exception_state, char** rust_error_msg, uint32_t* rust_errmsg_len); - void ReportError(JSValueConst error); - void ReportError(JSValueConst error, char** rust_errmsg, uint32_t* rust_errmsg_length); void DrainMicrotasks(); void EnqueueMicrotask(MicrotaskCallback callback, void* data = nullptr); +/*TODO support ExecutionContextData ExecutionContextData* contextData(); +*/ uint8_t* DumpByteCode(const char* code, uint32_t codeLength, const char* sourceURL, uint64_t* bytecodeLength); #if WEBF_QUICKJS_JS_ENGINE @@ -110,6 +115,7 @@ class ExecutingContext { JSContext* ctx(); bool HandleException(JSValue* exc); void ReportError(JSValueConst error); + void ReportError(JSValueConst error, char** rust_errmsg, uint32_t* rust_errmsg_length); void DefineGlobalProperty(const char* prop, JSValueConst value); static void DispatchGlobalUnhandledRejectionEvent(ExecutingContext* context, JSValueConst promise, @@ -124,7 +130,7 @@ class ExecutingContext { #elif WEBF_V8_JS_ENGINE static ExecutingContext* From(v8::Isolate* isolate); v8::Local Global(); - v8::Isolate ctx(); + v8::Isolate* ctx(); bool HandleException(v8::Local exc); void ReportError(v8::Local error); void DefineGlobalProperty(const char* prop, v8::Local value); @@ -137,6 +143,7 @@ class ExecutingContext { void RegisterActiveScriptWrappers(ScriptWrappable* script_wrappable); void InActiveScriptWrappers(ScriptWrappable* script_wrappable); + /*TODO support frame // Gets the DOMTimerCoordinator which maintains the "active timer // list" of tasks created by setTimeout and setInterval. The // DOMTimerCoordinator is owned by the ExecutionContext and should @@ -148,7 +155,9 @@ class ExecutingContext { // Gets the ModuleCallbacks which from the 4th parameter of `webf.invokeModule` function. ModuleContextCoordinator* ModuleContexts(); + */ + /* TODO support // Get current script state. ScriptState* GetScriptState() { return &script_state_; } @@ -156,21 +165,28 @@ class ExecutingContext { bool HasMutationScope() const { return active_mutation_scope != nullptr; } MemberMutationScope* mutationScope() const { return active_mutation_scope; } void ClearMutationScope(); - FORCE_INLINE Document* document() const { return document_; }; FORCE_INLINE Window* window() const { return window_; } + */ FORCE_INLINE DartIsolateContext* dartIsolateContext() const { return dart_isolate_context_; }; + /* TODO support FORCE_INLINE Performance* performance() const { return performance_; } + */ FORCE_INLINE SharedUICommand* uiCommandBuffer() { return &ui_command_buffer_; }; + /* TODO support FORCE_INLINE DartMethodPointer* dartMethodPtr() const { assert(dart_isolate_context_->valid()); return dart_isolate_context_->dartMethodPtr(); } + */ FORCE_INLINE WebFValueStatus* status() const { return executing_context_status_; } + /* TODO support FORCE_INLINE ExecutingContextWebFMethods* publicMethodPtr() const { return public_method_ptr_.get(); } + */ FORCE_INLINE bool isDedicated() { return is_dedicated_; } FORCE_INLINE std::chrono::time_point timeOrigin() const { return time_origin_; } + /*TODO support // Force dart side to execute the pending ui commands. void FlushUICommand(const BindingObject* self, uint32_t reason); void FlushUICommand(const BindingObject* self, uint32_t reason, std::vector& deps); @@ -189,7 +205,7 @@ class ExecutingContext { static std::unordered_map plugin_byte_code; // Raw string codes which registered by webf plugins. static std::unordered_map plugin_string_code; - + */ private: std::chrono::time_point time_origin_; int32_t unique_id_; @@ -199,11 +215,13 @@ class ExecutingContext { void InstallNativeLoader(); void DrainPendingPromiseJobs(); - static void promiseRejectTracker(JSContext* ctx, +#if WEBF_QUICKJS_JS_ENGINE +static void promiseRejectTracker(JSContext* ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void* opaque); +#endif // Warning: Don't change the orders of members in ExecutingContext if you really know what are you doing. // From C++ standard, https://isocpp.org/wiki/faq/dtors#order-dtors-for-members // Members first initialized and destructed at the last. @@ -217,7 +235,9 @@ class ExecutingContext { // ---------------------------------------------------------------------- // All members above ScriptState will be freed after ScriptState freed // ---------------------------------------------------------------------- + /*TODO ScriptState script_state_{dart_isolate_context_}; + */ // ---------------------------------------------------------------------- // All members below will be free before ScriptState freed. // ---------------------------------------------------------------------- @@ -230,6 +250,7 @@ class ExecutingContext { #elif WEBF_V8_JS_ENGINE v8::Local global_object_; #endif +/* TODO support Document* document_{nullptr}; Window* window_{nullptr}; NativeLoader* native_loader_{nullptr}; @@ -238,15 +259,20 @@ class ExecutingContext { 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_; - MemberMutationScope* active_mutation_scope{nullptr}; +#endif + // TODO MemberMutationScope* active_mutation_scope{nullptr}; std::unordered_set active_wrappers_; WebFValueStatus* executing_context_status_{new WebFValueStatus()}; bool is_dedicated_; + /*TODO // Rust methods ptr should keep alive when ExecutingContext is disposing. const std::unique_ptr public_method_ptr_ = nullptr; + */ }; } // namespace webf diff --git a/bridge/core/frame/window_or_worker_global_scope.cc b/bridge/core/frame/window_or_worker_global_scope.cc index b6f3ba3d51..2c109444f1 100644 --- a/bridge/core/frame/window_or_worker_global_scope.cc +++ b/bridge/core/frame/window_or_worker_global_scope.cc @@ -3,10 +3,19 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ #include "window_or_worker_global_scope.h" -#include "core/frame/dom_timer.h" +//TODO #include "core/frame/dom_timer.h" + +#if WEBF_QUICKJS_JS_ENGINE + +#include "bindings/qjs/exception_state.h" +#include "bindings/qjs/qjs_function.h" + +#elif WEBF_V8_JS_ENGINE +#endif namespace webf { +/*TODO support static void handleTimerCallback(DOMTimer* timer, char* errmsg) { auto* context = timer->context(); @@ -109,11 +118,13 @@ int WindowOrWorkerGlobalScope::setTimeout(ExecutingContext* context, ExceptionState& exception) { return setTimeout(context, handler, 0.0, exception); } +*/ int WindowOrWorkerGlobalScope::setTimeout(ExecutingContext* context, const std::shared_ptr& handler, int32_t timeout, ExceptionState& exception) { + /*TODO support if (handler == nullptr) { exception.ThrowException(context->ctx(), ErrorType::InternalError, "Timeout callback is null"); return -1; @@ -130,8 +141,11 @@ int WindowOrWorkerGlobalScope::setTimeout(ExecutingContext* context, context->Timers()->installNewTimer(context, timer_id, timer); return timer_id; + */ + return 0; } +/*TODO support int WindowOrWorkerGlobalScope::setInterval(ExecutingContext* context, std::shared_ptr handler, ExceptionState& exception) { @@ -187,5 +201,5 @@ ScriptValue WindowOrWorkerGlobalScope::__memory_usage__(ExecutingContext* contex return ScriptValue::CreateJsonObject(context->ctx(), buff, strlen(buff)); } - +*/ } // namespace webf diff --git a/bridge/core/frame/window_or_worker_global_scope.d.ts b/bridge/core/frame/window_or_worker_global_scope.d.ts index 26274ae3d0..42bc828290 100644 --- a/bridge/core/frame/window_or_worker_global_scope.d.ts +++ b/bridge/core/frame/window_or_worker_global_scope.d.ts @@ -1,6 +1,7 @@ // @ts-ignore declare const setTimeout: (callback: Function, timeout?: double) => int64; +/*TODO suppport // @ts-ignore declare const setInterval: (callback: Function, timeout?: double) => int64; @@ -15,5 +16,5 @@ declare const clearInterval: (handle: double) => void; declare const __gc__: () => void; declare const __memory_usage__: () => any; - +*/ diff --git a/bridge/core/frame/window_or_worker_global_scope.h b/bridge/core/frame/window_or_worker_global_scope.h index 69ffeff787..c6c4947ece 100644 --- a/bridge/core/frame/window_or_worker_global_scope.h +++ b/bridge/core/frame/window_or_worker_global_scope.h @@ -5,8 +5,6 @@ #ifndef BRIDGE_WINDOW_OR_WORKER_GLOBAL_SCROPE_H #define BRIDGE_WINDOW_OR_WORKER_GLOBAL_SCROPE_H -#include "bindings/qjs/exception_state.h" -#include "bindings/qjs/qjs_function.h" #include "core/executing_context.h" #include "foundation/function.h" diff --git a/bridge/core/page.cc b/bridge/core/page.cc index 6d04460dfd..6ce836886c 100644 --- a/bridge/core/page.cc +++ b/bridge/core/page.cc @@ -5,16 +5,21 @@ #include #include +#if WEBF_QUICKJS_JS_ENGINE #include "bindings/qjs/atomic_string.h" #include "bindings/qjs/binding_initializer.h" +#elif WEBF_V8_JS_ENGINE +#include "bindings/v8/atomic_string.h" +#include "bindings/v8/binding_initializer.h" +#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 "foundation/native_value_converter.h" #include "page.h" #include "polyfill.h" @@ -42,7 +47,7 @@ WebFPage::WebFPage(DartIsolateContext* dart_isolate_context, bool WebFPage::parseHTML(const char* code, size_t length) { if (!context_->IsContextValid()) return false; - +/*TODO { MemberMutationScope scope{context_}; @@ -57,7 +62,7 @@ bool WebFPage::parseHTML(const char* code, size_t length) { } context_->uiCommandBuffer()->AddCommand(UICommand::kFinishRecordingCommand, nullptr, nullptr, nullptr); - +*/ return true; } @@ -65,6 +70,8 @@ NativeValue* WebFPage::invokeModuleEvent(SharedNativeString* native_module_name, const char* eventType, void* ptr, NativeValue* extra) { + return nullptr; +/*TODO if (!context_->IsContextValid()) return nullptr; @@ -105,6 +112,7 @@ NativeValue* WebFPage::invokeModuleEvent(SharedNativeString* native_module_name, memcpy(return_value, &tmp, sizeof(NativeValue)); return return_value; +*/ } bool WebFPage::evaluateScript(const char* script, @@ -174,11 +182,11 @@ void WebFPage::EvaluateScriptsInternal(void* page_, auto page = reinterpret_cast(page_); assert(std::this_thread::get_id() == page->currentThread()); - page->dartIsolateContext()->profiler()->StartTrackEvaluation(profile_id); + // TODO page->dartIsolateContext()->profiler()->StartTrackEvaluation(profile_id); bool is_success = page->evaluateScript(code, code_len, parsed_bytecodes, bytecode_len, bundleFilename, startLine); - page->dartIsolateContext()->profiler()->FinishTrackEvaluation(profile_id); + // TODO page->dartIsolateContext()->profiler()->FinishTrackEvaluation(profile_id); page->dartIsolateContext()->dispatcher()->PostToDart(page->isDedicated(), ReturnEvaluateScriptsInternal, persistent_handle, result_callback, is_success); @@ -201,11 +209,11 @@ void WebFPage::EvaluateQuickjsByteCodeInternal(void* page_, auto page = reinterpret_cast(page_); assert(std::this_thread::get_id() == page->currentThread()); - page->dartIsolateContext()->profiler()->StartTrackEvaluation(profile_id); + // TODO page->dartIsolateContext()->profiler()->StartTrackEvaluation(profile_id); bool is_success = page->evaluateByteCode(bytes, byteLen); - page->dartIsolateContext()->profiler()->FinishTrackEvaluation(profile_id); + // TODO page->dartIsolateContext()->profiler()->FinishTrackEvaluation(profile_id); page->dartIsolateContext()->dispatcher()->PostToDart(page->isDedicated(), ReturnEvaluateQuickjsByteCodeResultToDart, persistent_handle, result_callback, is_success); @@ -226,12 +234,12 @@ void WebFPage::ParseHTMLInternal(void* page_, auto page = reinterpret_cast(page_); assert(std::this_thread::get_id() == page->currentThread()); - page->dartIsolateContext()->profiler()->StartTrackEvaluation(profile_id); + // TODO page->dartIsolateContext()->profiler()->StartTrackEvaluation(profile_id); page->parseHTML(code, length); dart_free(code); - page->dartIsolateContext()->profiler()->FinishTrackEvaluation(profile_id); + // TODO page->dartIsolateContext()->profiler()->FinishTrackEvaluation(profile_id); page->dartIsolateContext()->dispatcher()->PostToDart(page->isDedicated(), ReturnParseHTMLToDart, dart_handle, result_callback); @@ -256,12 +264,12 @@ void WebFPage::InvokeModuleEventInternal(void* page_, auto dart_isolate_context = page->executingContext()->dartIsolateContext(); assert(std::this_thread::get_id() == page->currentThread()); - page->dartIsolateContext()->profiler()->StartTrackAsyncEvaluation(); + // TODO page->dartIsolateContext()->profiler()->StartTrackAsyncEvaluation(); auto* result = page->invokeModuleEvent(reinterpret_cast(module_name), eventType, event, reinterpret_cast(extra)); - page->dartIsolateContext()->profiler()->FinishTrackAsyncEvaluation(); + // TODO page->dartIsolateContext()->profiler()->FinishTrackAsyncEvaluation(); dart_isolate_context->dispatcher()->PostToDart(page->isDedicated(), ReturnInvokeEventResultToDart, persistent_handle, result_callback, result); @@ -285,13 +293,13 @@ void WebFPage::DumpQuickJsByteCodeInternal(void* page_, auto page = reinterpret_cast(page_); auto dart_isolate_context = page->executingContext()->dartIsolateContext(); - dart_isolate_context->profiler()->StartTrackEvaluation(profile_id); + // TODO dart_isolate_context->profiler()->StartTrackEvaluation(profile_id); assert(std::this_thread::get_id() == page->currentThread()); uint8_t* bytes = page->dumpByteCode(code, code_len, url, bytecode_len); *parsed_bytecodes = bytes; - dart_isolate_context->profiler()->FinishTrackEvaluation(profile_id); + // TODO dart_isolate_context->profiler()->FinishTrackEvaluation(profile_id); dart_isolate_context->dispatcher()->PostToDart(page->isDedicated(), ReturnDumpByteCodeResultToDart, persistent_handle, result_callback); diff --git a/bridge/foundation/native_type.h b/bridge/foundation/native_type.h index a905dc1f03..49f50f6b32 100644 --- a/bridge/foundation/native_type.h +++ b/bridge/foundation/native_type.h @@ -12,6 +12,7 @@ #include "bindings/qjs/script_value.h" #else #include "bindings/v8/atomic_string.h" +#include "bindings/v8/script_value.h" #endif #include "foundation/native_string.h" diff --git a/bridge/foundation/shared_ui_command.cc b/bridge/foundation/shared_ui_command.cc index b0649f1cc6..ea3a4c5860 100644 --- a/bridge/foundation/shared_ui_command.cc +++ b/bridge/foundation/shared_ui_command.cc @@ -17,6 +17,7 @@ SharedUICommand::SharedUICommand(ExecutingContext* context) ui_command_sync_strategy_(std::make_unique(this)), is_blocking_writing_(false) {} +/* TODO support void SharedUICommand::AddCommand(UICommand type, std::unique_ptr&& args_01, NativeBindingObject* native_binding_object, @@ -42,6 +43,7 @@ void SharedUICommand::AddCommand(UICommand type, ui_command_sync_strategy_->RecordUICommand(type, args_01, native_binding_object, nativePtr2, request_ui_update); } +*/ // first called by dart to being read commands. void* SharedUICommand::data() { diff --git a/bridge/foundation/shared_ui_command.h b/bridge/foundation/shared_ui_command.h index bd39d32a5b..d159e98329 100644 --- a/bridge/foundation/shared_ui_command.h +++ b/bridge/foundation/shared_ui_command.h @@ -10,6 +10,7 @@ #include "foundation/native_type.h" #include "foundation/ui_command_buffer.h" #include "foundation/ui_command_strategy.h" +#include "dart_readable.h" namespace webf { @@ -19,11 +20,13 @@ class SharedUICommand : public DartReadable { public: SharedUICommand(ExecutingContext* context); + /*TODO support void AddCommand(UICommand type, std::unique_ptr&& args_01, NativeBindingObject* native_binding_object, void* nativePtr2, bool request_ui_update = true); + */ void* data(); uint32_t kindFlag(); diff --git a/bridge/foundation/ui_command_buffer.h b/bridge/foundation/ui_command_buffer.h index dcfdf0352f..0f2b974b75 100644 --- a/bridge/foundation/ui_command_buffer.h +++ b/bridge/foundation/ui_command_buffer.h @@ -7,7 +7,7 @@ #define BRIDGE_FOUNDATION_UI_COMMAND_BUFFER_H_ #include -#include "bindings/qjs/native_string_utils.h" +#include "foundation/native_string.h" namespace webf { diff --git a/bridge/foundation/ui_command_strategy.cc b/bridge/foundation/ui_command_strategy.cc index 8fddd6ca32..838d53d179 100644 --- a/bridge/foundation/ui_command_strategy.cc +++ b/bridge/foundation/ui_command_strategy.cc @@ -4,7 +4,6 @@ #include "ui_command_strategy.h" #include -#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 3b1ddf127a..bb0ade0905 100644 --- a/bridge/scripts/code_generator/bin/code_generator.js +++ b/bridge/scripts/code_generator/bin/code_generator.js @@ -58,9 +58,10 @@ function genCodeFromTypeDefine() { }); let blobs = typeFiles.map(file => { - let filename = generatePlatformPrefix() + '_' + file.split('/').slice(-1)[0].replace('.d.ts', ''); + let platformPrefix = generatePlatformPrefix(); + let filename = platformPrefix + '_' + 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); + return new IDLBlob(path.join(source, file), dist, filename, implement, platformPrefix); }); ClassObject.globalClassMap = Object.create(null); diff --git a/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateHeader.ts b/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateHeader.ts new file mode 100644 index 0000000000..fbc88805ac --- /dev/null +++ b/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateHeader.ts @@ -0,0 +1,97 @@ +import {ClassObject, ClassObjectKind, FunctionObject} from "../../declaration"; +import _ from "lodash"; +import {IDLBlob} from "../../IDLBlob"; +import {getClassName} from "../../utils"; +import fs from 'fs'; +import path from 'path'; +import { + generateCoreTypeValue, + generateRawTypeValue, + getPointerType, + isPointerType, + isTypeHaveNull +} from "./generateSource"; +import {GenerateOptions, readTemplate} from "../../generator"; + +export enum TemplateKind { + globalFunction, + Dictionary, + Interface, + null +} + +export function getTemplateKind(object: ClassObject | FunctionObject | null): TemplateKind { + if (object instanceof FunctionObject) { + return TemplateKind.globalFunction; + } else if (object instanceof ClassObject) { + if (object.kind === ClassObjectKind.dictionary) { + return TemplateKind.Dictionary; + } else if(object.kind === ClassObjectKind.mixin) { + return TemplateKind.null; + } + return TemplateKind.Interface; + } + return TemplateKind.null; +} + +export function generateV8CppHeader(blob: IDLBlob, options: GenerateOptions) { + const baseTemplate = fs.readFileSync(path.join(__dirname, '../../../../templates/idl_templates/v8/base.h.tpl'), {encoding: 'utf-8'}); + let headerOptions = { + interface: false, + dictionary: false, + global_function: false, + }; + const contents = blob.objects.map(object => { + const templateKind = getTemplateKind(object); + if (templateKind === TemplateKind.null) return ''; + + switch(templateKind) { + case TemplateKind.Interface: { + if (!headerOptions.interface) { + object = (object as ClassObject); + headerOptions.interface = true; + return _.template(readTemplate('v8', 'interface'))({ + className: getClassName(blob), + parentClassName: object.parent, + blob: blob, + object, + generateRawTypeValue, + ...options + }); + } + return ''; + } + case TemplateKind.Dictionary: { + if (!headerOptions.dictionary) { + headerOptions.dictionary = true; + let props = (object as ClassObject).props; + return _.template(readTemplate('v8', 'dictionary'))({ + className: getClassName(blob), + blob: blob, + object: object, + props, + generateCoreTypeValue + }); + } + return ''; + } + case TemplateKind.globalFunction: { + if (!headerOptions.global_function) { + headerOptions.global_function = true; + return _.template(readTemplate('v8', 'global_function'))({ + className: getClassName(blob), + blob: blob + }); + } + return ''; + } + } + }); + + return _.template(baseTemplate)({ + content: contents.join('\n'), + blob: blob + }).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/IDLAPIGenerator/v8/generateSource.ts b/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateSource.ts new file mode 100644 index 0000000000..9a0771e583 --- /dev/null +++ b/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateSource.ts @@ -0,0 +1,730 @@ +import {IDLBlob} from "../../IDLBlob"; +import { + ClassObject, + FunctionArguments, + FunctionArgumentType, + FunctionDeclaration, + FunctionObject, + ParameterMode, + PropsDeclaration, +} 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, generateUnionTypeFileName} from "../../generator"; +import { + generateTypeRawChecker, + generateUnionConstructorImpl, + generateUnionMemberName, + generateUnionTypeClassName, + generateUnionTypeClear, + generateUnionTypeSetter, + getUnionTypeName +} from "./generateUnionTypes"; + +const dictionaryClasses: string[] = []; + +function generateMethodArgumentsCheck(m: FunctionDeclaration) { + if (m.args.length == 0) return ''; + + let requiredArgsCount = 0; + m.args.forEach(m => { + if (m.required) requiredArgsCount++; + }); + + if (requiredArgsCount > 0 && m.args[0].isDotDotDot) { + return ''; + } + + return ` if (argc < ${requiredArgsCount}) { + return JS_ThrowTypeError(ctx, "Failed to execute '${m.name}' : ${requiredArgsCount} argument required, but %d present.", argc); + } +`; +} + +export function isTypeNeedAllocate(type: ParameterType) { + switch (type.value) { + case FunctionArgumentType.undefined: + case FunctionArgumentType.null: + case FunctionArgumentType.int32: + case FunctionArgumentType.int64: + case FunctionArgumentType.boolean: + case FunctionArgumentType.double: + return false; + default: + return true; + } +} + +export function generateCoreTypeValue(type: ParameterType): string { + switch (type.value) { + case FunctionArgumentType.int64: { + return 'int64_t'; + } + case FunctionArgumentType.int32: { + return 'int32_t'; + } + case FunctionArgumentType.void: { + return 'void'; + } + case FunctionArgumentType.double: { + return 'double'; + } + case FunctionArgumentType.boolean: { + return 'bool'; + } + case FunctionArgumentType.dom_string: + case FunctionArgumentType.legacy_dom_string: { + return 'AtomicString'; + } + case FunctionArgumentType.any: { + return 'ScriptValue'; + } + } + + if (isDictionary(type)) { + return `std::shared_ptr<${getPointerType(type)}>`; + } + + if (isPointerType(type)) { + return getPointerType(type) + '*'; + } + + if (type.isArray && typeof type.value === 'object' && !Array.isArray(type.value)) { + return `std::vector<${generateCoreTypeValue(type.value)}>`; + } + + return ''; +} + +export function generateRawTypeValue(type: ParameterType, is32Bit: boolean = false): string { + switch (type.value) { + case FunctionArgumentType.int64: { + return 'int64_t'; + } + case FunctionArgumentType.int32: { + return 'int64_t'; + } + case FunctionArgumentType.double: { + return 'double'; + } + case FunctionArgumentType.boolean: { + return 'int64_t'; + } + case FunctionArgumentType.dom_string: + case FunctionArgumentType.legacy_dom_string: { + if (is32Bit) { + return 'int64_t'; + } + + return 'SharedNativeString*'; + } + default: + if (is32Bit) { + return 'int64_t'; + } + return 'void*'; + } + + if (isPointerType(type)) { + if (is32Bit) { + return 'int64_t'; + } + return 'NativeBindingObject*'; + } + + return ''; +} + +export function isTypeHaveNull(type: ParameterType): boolean { + if (type.isArray) return false; + if (!Array.isArray(type.value)) { + return type.value === FunctionArgumentType.null; + } + return type.value.some(t => t.value === FunctionArgumentType.null); +} + +export function isTypeHaveString(types: ParameterType[] | ParameterType): boolean { + if (Array.isArray(types)) { + return types.some(t => { + if (t.isArray) return isTypeHaveString(t.value as ParameterType); + if (!Array.isArray(t.value)) { + return t.value === FunctionArgumentType.dom_string; + } + return t.value.some(t => t.value === FunctionArgumentType.dom_string); + }); + } + + return types.value === FunctionArgumentType.dom_string; +} + +export function isPointerType(type: ParameterType): boolean { + if (type.isArray) return false; + if (typeof type.value === 'string') { + return true; + } + if (Array.isArray(type.value)) { + return type.value.some(t => typeof t.value === 'string'); + } + return false; +} + +export function isDictionary(type: ParameterType): boolean { + if (type.isArray) return false; + if (typeof type.value === 'string') { + return dictionaryClasses.indexOf(type.value) >= 0; + } + return false; +} + +export function getPointerType(type: ParameterType): string { + if (typeof type.value === 'string') { + return type.value; + } + if (Array.isArray(type.value)) { + for (let i = 0; i < type.value.length; i++) { + let childValue = type.value[i]; + if (typeof childValue.value === 'string') { + return childValue.value; + } + } + } + return ''; +} + +export function isUnionType(type: ParameterType): boolean { + if (type.isArray || !Array.isArray(type.value)) { + return false; + } + + const trimedType = trimNullTypeFromType(type); + return Array.isArray(trimedType.value); +} + +export function trimNullTypeFromType(type: ParameterType): ParameterType { + let types = type.value; + if (!Array.isArray(types)) return type; + let trimed = types.filter(t => t.value != FunctionArgumentType.null); + + if (trimed.length === 1) { + return { + isArray: false, + value: trimed[0].value + } + } + + return { + isArray: type.isArray, + value: trimed + }; +} + +export function generateIDLTypeConverter(type: ParameterType, isOptional?: boolean): string { + let haveNull = isTypeHaveNull(type); + let returnValue = ''; + + if (type.isArray) { + returnValue = `IDLSequence<${generateIDLTypeConverter(type.value as ParameterType, isOptional)}>`; + } else if (isUnionType(type) && Array.isArray(type.value)) { + returnValue = generateUnionTypeClassName(type.value); + } else if (isPointerType(type)) { + returnValue = getPointerType(type); + } else { + type = trimNullTypeFromType(type); + switch (type.value) { + case FunctionArgumentType.int32: + returnValue = `IDLInt32`; + break; + case FunctionArgumentType.int64: + returnValue = 'IDLInt64'; + break; + case FunctionArgumentType.double: + returnValue = `IDLDouble`; + break; + case FunctionArgumentType.function: + returnValue = `IDLCallback`; + break; + case FunctionArgumentType.boolean: + returnValue = `IDLBoolean`; + break; + case FunctionArgumentType.dom_string: + returnValue = `IDLDOMString`; + break; + case FunctionArgumentType.object: + returnValue = `IDLObject`; + break; + case FunctionArgumentType.promise: + returnValue = 'IDLPromise'; + break; + case FunctionArgumentType.legacy_dom_string: + // TODO: legacy is now allowed with nullable + returnValue = 'IDLLegacyDOMString' + break; + default: + case FunctionArgumentType.any: + returnValue = `IDLAny`; + break; + } + } + + if (haveNull) { + returnValue = `IDLNullable<${returnValue}>`; + } else if (isOptional) { + returnValue = `IDLOptional<${returnValue}>`; + } + + return returnValue; +} + +function isDOMStringType(type: ParameterType) { + return type.value == FunctionArgumentType.dom_string || type.value == FunctionArgumentType.legacy_dom_string; +} + +function generateNativeValueTypeConverter(type: ParameterType): string { + let returnValue = ''; + + if (isPointerType(type)) { + return `NativeTypePointer<${getPointerType(type)}>`; + } + + switch (type.value) { + case FunctionArgumentType.int32: + returnValue = `NativeTypeInt64`; + break; + case FunctionArgumentType.int64: + returnValue = 'NativeTypeInt64'; + break; + case FunctionArgumentType.double: + returnValue = `NativeTypeDouble`; + break; + case FunctionArgumentType.boolean: + returnValue = `NativeTypeBool`; + break; + case FunctionArgumentType.dom_string: + case FunctionArgumentType.legacy_dom_string: + returnValue = `NativeTypeString`; + break; + } + + return returnValue; +} + +function generateRequiredInitBody(argument: FunctionArguments, argsIndex: number) { + let type = generateIDLTypeConverter(argument.type, !argument.required); + + let hasArgumentCheck = type.indexOf('Element') >= 0 || type.indexOf('Node') >= 0 || type === 'EventTarget' || type.indexOf('DOMMatrix') >= 0 || type.indexOf('Path2D') >= 0; + + let body = ''; + if (argument.isDotDotDot) { + body = `Converter<${type}>::FromValue(ctx, argv + ${argsIndex}, argc - ${argsIndex}, exception_state)` + } else if (hasArgumentCheck) { + body = `Converter<${type}>::ArgumentsValue(context, argv[${argsIndex}], ${argsIndex}, exception_state)`; + } else { + body = `Converter<${type}>::FromValue(ctx, argv[${argsIndex}], exception_state)`; + } + + return `auto&& args_${argument.name} = ${body}; +if (UNLIKELY(exception_state.HasException())) { + return exception_state.ToQuickJS(); +}`; +} + +function generateCallMethodName(name: string) { + if (name === 'constructor') return 'Create'; + return name; +} + +function generateDartImplCallCode(blob: IDLBlob, declare: FunctionDeclaration, isLayoutIndependent: boolean, args: FunctionArguments[]): string { + let nativeArguments = args.map(i => { + return `NativeValueConverter<${generateNativeValueTypeConverter(i.type)}>::ToNativeValue(${isDOMStringType(i.type) ? 'ctx, ' : ''}args_${i.name})`; + }); + + let returnValueAssignment = ''; + + if (declare.returnType.value != FunctionArgumentType.void) { + returnValueAssignment = 'auto&& native_value ='; + } + + return ` +auto* self = toScriptWrappable<${getClassName(blob)}>(JS_IsUndefined(this_val) ? context->Global() : this_val); +${nativeArguments.length > 0 ? `NativeValue arguments[] = { + ${nativeArguments.join(',\n')} +}` : 'NativeValue* arguments = nullptr;'}; +${returnValueAssignment}self->InvokeBindingMethod(binding_call_methods::k${declare.name}, ${nativeArguments.length}, arguments, FlushUICommandReason::kDependentsOnElement${isLayoutIndependent ? '| FlushUICommandReason::kDependentsOnLayout' : ''}, exception_state); +${returnValueAssignment.length > 0 ? `return Converter<${generateIDLTypeConverter(declare.returnType)}>::ToValue(NativeValueConverter<${generateNativeValueTypeConverter(declare.returnType)}>::FromNativeValue(native_value))` : ''}; + `.trim(); +} + +function generateOptionalInitBody(blob: IDLBlob, declare: FunctionDeclaration, argument: FunctionArguments, argsIndex: number, argsLength: number, previousArguments: string[], options: GenFunctionBodyOptions) { + let call = ''; + let returnValueAssignment = ''; + if (declare.returnType.value != FunctionArgumentType.void) { + returnValueAssignment = 'return_value ='; + } + if (declare.returnTypeMode?.dartImpl) { + call = generateDartImplCallCode(blob, declare, declare.returnTypeMode?.layoutDependent ?? false, declare.args.slice(0, argsIndex + 1)); + } else if (options.isInstanceMethod) { + call = `auto* self = toScriptWrappable<${getClassName(blob)}>(JS_IsUndefined(this_val) ? context->Global() : this_val); +${returnValueAssignment} self->${generateCallMethodName(declare.name)}(${[...previousArguments, `args_${argument.name}`, 'exception_state'].join(',')});`; + } else { + call = `${returnValueAssignment} ${getClassName(blob)}::${generateCallMethodName(declare.name)}(context, ${[...previousArguments, `args_${argument.name}`].join(',')}, exception_state);`; + } + + + return `auto&& args_${argument.name} = Converter>::FromValue(ctx, argv[${argsIndex}], exception_state); +if (UNLIKELY(exception_state.HasException())) { + return exception_state.ToQuickJS(); +} + +if (argc <= ${argsIndex + 1}) { + ${call} + break; +} +${(argsIndex) + 1 == argsLength ? call : ''} +`; +} + +function generateFunctionCallBody(blob: IDLBlob, declaration: FunctionDeclaration, options: GenFunctionBodyOptions = { + isConstructor: false, + isInstanceMethod: false +}) { + if (options.isConstructor && declaration.returnType.value == FunctionArgumentType.void) { + return 'return JS_ThrowTypeError(ctx, "Illegal constructor");'; + } + + let minimalRequiredArgc = 0; + declaration.args.forEach(m => { + if (m.required) minimalRequiredArgc++; + }); + + let requiredArguments: string[] = []; + let requiredArgumentsInit: string[] = []; + if (minimalRequiredArgc > 0) { + requiredArgumentsInit = declaration.args.filter((a, i) => a.required).map((a, i) => { + requiredArguments.push(`args_${a.name}`); + return generateRequiredInitBody(a, i); + }); + } + + let optionalArgumentsInit: string[] = []; + let totalArguments: string[] = requiredArguments.slice(); + + for (let i = minimalRequiredArgc; i < declaration.args.length; i++) { + optionalArgumentsInit.push(generateOptionalInitBody(blob, declaration, declaration.args[i], i, declaration.args.length, totalArguments, options)); + totalArguments.push(`args_${declaration.args[i].name}`); + } + + requiredArguments.push('exception_state'); + + let call = ''; + let returnValueAssignment = ''; + if (declaration.returnType.value != FunctionArgumentType.void) { + returnValueAssignment = 'return_value ='; + } + if (declaration.returnTypeMode?.dartImpl) { + call = generateDartImplCallCode(blob, declaration, declaration.returnTypeMode?.layoutDependent ?? false, declaration.args.slice(0, minimalRequiredArgc)); + } else if (options.isInstanceMethod) { + call = `auto* self = toScriptWrappable<${getClassName(blob)}>(JS_IsUndefined(this_val) ? context->Global() : this_val); +${returnValueAssignment} self->${generateCallMethodName(declaration.name)}(${minimalRequiredArgc > 0 ? `${requiredArguments.join(',')}` : 'exception_state'});`; + } else { + call = `${returnValueAssignment} ${getClassName(blob)}::${generateCallMethodName(declaration.name)}(context, ${requiredArguments.join(',')});`; + } + + let minimalRequiredCall = (declaration.args.length == 0 || (declaration.args[0].isDotDotDot)) ? call : `if (argc <= ${minimalRequiredArgc}) { + ${call} + break; +}`; + + return `${requiredArgumentsInit.join('\n')} +${minimalRequiredCall} + +${optionalArgumentsInit.join('\n')} +`; +} + +function generateOverLoadSwitchBody(overloadMethods: FunctionDeclaration[]) { + let callBodyList = overloadMethods.map((overload, index) => { + return `if (${overload.args.length} == argc) { + return ${overload.name}_overload_${index}(ctx, this_val, argc, argv); +} + `; + }); + + return ` +${callBodyList.join('\n')} + +return ${overloadMethods[0].name}_overload_${0}(ctx, this_val, argc, argv); +`; +} + +function isJSArrayBuiltInProps(prop: PropsDeclaration) { + return prop.type.value == FunctionArgumentType.js_array_proto_methods; +} + +function generateDictionaryInit(blob: IDLBlob, props: PropsDeclaration[]) { + let initExpression = props.map(prop => { + switch (prop.type.value) { + case FunctionArgumentType.boolean: { + return `${prop.name}_(false)`; + } + } + return '' + }); + + // Remove empty. + initExpression = initExpression.filter(i => !!i); + + if (initExpression.length == 0) return ''; + + return ': ' + initExpression.join(','); +} + +function generateReturnValueInit(blob: IDLBlob, type: ParameterType, options: GenFunctionBodyOptions = { + isConstructor: false, + isInstanceMethod: false +}) { + if (type.value == FunctionArgumentType.void) return ''; + + if (options.isConstructor) { + return `${getClassName(blob)}* return_value = nullptr;` + } + if (isPointerType(type)) { + if (getPointerType(type) === 'Promise') { + return 'ScriptPromise return_value;'; + } else { + return `${getPointerType(type)}* return_value = nullptr;`; + } + } + return `Converter<${generateIDLTypeConverter(type)}>::ImplType return_value;`; +} + +function generateReturnValueResult(blob: IDLBlob, type: ParameterType, mode?: ParameterMode, options: GenFunctionBodyOptions = { + isConstructor: false, + isInstanceMethod: false +}): string { + if (type.value == FunctionArgumentType.void) return 'JS_NULL'; + let method = 'ToQuickJS'; + + if (options.isConstructor) { + return `return_value->${method}()`; + } + + return `Converter<${generateIDLTypeConverter(type)}>::ToValue(ctx, std::move(return_value))`; +} + +type GenFunctionBodyOptions = { isConstructor?: boolean, isInstanceMethod?: boolean }; + +function generateFunctionBody(blob: IDLBlob, declare: FunctionDeclaration, options: GenFunctionBodyOptions = { + isConstructor: false, + isInstanceMethod: false +}) { + let paramCheck = generateMethodArgumentsCheck(declare); + let callBody = generateFunctionCallBody(blob, declare, options); + let returnValueInit = generateReturnValueInit(blob, declare.returnType, options); + let returnValueResult = generateReturnValueResult(blob, declare.returnType, declare.returnTypeMode, options); + + let constructorPrototypeInit = (options.isConstructor && returnValueInit.length > 0) ? `JSValue proto = JS_GetPropertyStr(ctx, this_val, "prototype"); + JS_SetPrototype(ctx, return_value->ToQuickJSUnsafe(), proto); + JS_FreeValue(ctx, proto);` : ''; + + return `/*${paramCheck} + + ExceptionState exception_state; + ExecutingContext* context = ExecutingContext::From(ctx); + if (!context->IsContextValid()) return JS_NULL; + + context->dartIsolateContext()->profiler()->StartTrackSteps("${getClassName(blob)}::${declare.name}"); + + MemberMutationScope scope{context}; + ${returnValueInit} + + do { // Dummy loop for use of 'break'. +${addIndent(callBody, 4)} + } while (false); + + context->dartIsolateContext()->profiler()->FinishTrackSteps(); + + if (UNLIKELY(exception_state.HasException())) { + return exception_state.ToQuickJS(); + } + ${constructorPrototypeInit} + return ${returnValueResult}; +*/`; +} + +function readTemplate(name: string) { + return fs.readFileSync(path.join(__dirname, '../../../../templates/idl_templates/v8/' + name + '.cc.tpl'), {encoding: 'utf-8'}); +} + +export function generateV8CppSource(blob: IDLBlob, options: GenerateOptions) { + const baseTemplate = fs.readFileSync(path.join(__dirname, '../../../../templates/idl_templates/v8/base.cc.tpl'), {encoding: 'utf-8'}); + const className = getClassName(blob) + + const contents = blob.objects.map(object => { + const templateKind = getTemplateKind(object); + if (templateKind === TemplateKind.null) return ''; + + switch (templateKind) { + case TemplateKind.Interface: { + object = object as ClassObject; + + function addObjectProps(prop: PropsDeclaration) { + if (prop.isSymbol) { + options.classMethodsInstallList.push(`{JS_ATOM_${prop.name}, ${prop.name}AttributeGetCallback, ${prop.readonly ? 'nullptr' : `${prop.name}AttributeSetCallback`}}`) + } else { + options.classMethodsInstallList.push(`{defined_properties::k${prop.name}.Impl(), ${prop.name}AttributeGetCallback, ${prop.readonly ? 'nullptr' : `${prop.name}AttributeSetCallback`}}`) + } + } + function addObjectMethods(method: FunctionDeclaration, i: number) { + if (overloadMethods.hasOwnProperty(method.name)) { + overloadMethods[method.name].push(method) + } else { + overloadMethods[method.name] = [method]; + filtedMethods.push(method); + options.classPropsInstallList.push(`{"${method.name}", ${method.name}, ${method.args.length}}`) + } + } + + function addObjectStaticMethods(method: FunctionDeclaration, i: number) { + options.staticMethodsInstallList.push(`{"${method.name}", ${method.name}, ${method.args.length}}`); + } + + object.props.forEach(addObjectProps); + + let overloadMethods: {[key: string]: FunctionDeclaration[] } = {}; + let filtedMethods: FunctionDeclaration[] = []; + object.methods.forEach(addObjectMethods); + object.staticMethods.forEach(addObjectStaticMethods); + + if (object.construct) { + options.constructorInstallList.push(`{defined_properties::k${className}.Impl(), nullptr, nullptr, constructor}`) + } + + let wrapperTypeRegisterList = [ + `JS_CLASS_${getWrapperTypeInfoNameOfClassName(className)}`, // ClassId + `"${className}"`, // ClassName + object.parent != null ? `${object.parent}::GetStaticWrapperTypeInfo()` : 'nullptr', // parentClassWrapper + object.construct ? `V8${className}::ConstructorCallback` : 'nullptr', // ConstructorCallback + ]; + + // Generate indexed property callback. + if (object.indexedProp) { + if (object.indexedProp.indexKeyType == 'number') { + wrapperTypeRegisterList.push(`IndexedPropertyGetterCallback`); + if (!object.indexedProp.readonly) { + wrapperTypeRegisterList.push(`IndexedPropertySetterCallback`); + } else { + wrapperTypeRegisterList.push('nullptr'); + } + wrapperTypeRegisterList.push('nullptr'); + wrapperTypeRegisterList.push('nullptr'); + } else { + wrapperTypeRegisterList.push('nullptr'); + wrapperTypeRegisterList.push('nullptr'); + + wrapperTypeRegisterList.push(`StringPropertyGetterCallback`); + if (!object.indexedProp.readonly) { + wrapperTypeRegisterList.push(`StringPropertySetterCallback`); + } else { + wrapperTypeRegisterList.push('nullptr'); + } + } + + wrapperTypeRegisterList.push('PropertyCheckerCallback'); + wrapperTypeRegisterList.push('PropertyEnumerateCallback'); + if (!object.indexedProp.readonly) { + wrapperTypeRegisterList.push('StringPropertyDeleterCallback') + } else { + wrapperTypeRegisterList.push('nullptr'); + } + } + + let mixinParent = object.mixinParent; + let mixinObjects: ClassObject[] | null = null; + if (mixinParent) { + mixinObjects = mixinParent.map(mixinName => ClassObject.globalClassMap[mixinName]).filter(o => !!o); + + mixinObjects.forEach(mixinObject => { + mixinObject.methods.forEach(addObjectMethods); + mixinObject.props.forEach(addObjectProps); + }); + } + + options.wrapperTypeInfoInit = ` +const WrapperTypeInfo V8${className}::wrapper_type_info_ {${wrapperTypeRegisterList.join(', ')}}; +const WrapperTypeInfo& ${className}::wrapper_type_info_ = V8${className}::wrapper_type_info_;`; + return _.template(readTemplate('interface'))({ + className, + blob: blob, + object: object, + mixinObjects, + generateFunctionBody, + generateCoreTypeValue, + generateRawTypeValue, + generateOverLoadSwitchBody, + isTypeNeedAllocate, + overloadMethods, + isJSArrayBuiltInProps, + filtedMethods, + generateIDLTypeConverter, + generateNativeValueTypeConverter, + isDOMStringType, + }); + } + case TemplateKind.Dictionary: { + dictionaryClasses.push(className); + let props = (object as ClassObject).props; + return _.template(readTemplate('dictionary'))({ + className, + blob: blob, + props: props, + object: object, + generateIDLTypeConverter, + generateDictionaryInit + }); + } + case TemplateKind.globalFunction: { + object = object as FunctionObject; + options.globalFunctionInstallList.push(` {"${object.declare.name}", ${object.declare.name}, ${object.declare.args.length}}`); + return _.template(readTemplate('global_function'))({ + className, + blob: blob, + object: object, + generateFunctionBody + }); + } + } + return ''; + }); + + return _.template(baseTemplate)({ + content: contents.join('\n'), + className, + blob: blob, + ...options + }).split('\n').filter(str => { + return str.trim().length > 0; + }).join('\n'); +} + +export function generateUnionTypeSource(unionType: ParameterType): string { + return _.template(readTemplate('union'))({ + unionType, + generateUnionTypeClassName, + generateUnionTypeFileName, + generateTypeRawChecker, + generateUnionMemberName, + generateUnionTypeClear, + generateIDLTypeConverter, + generateUnionConstructorImpl, + generateUnionTypeSetter, + getUnionTypeName, + isTypeHaveNull, + isTypeHaveString + }).split('\n').filter(str => { + return str.trim().length > 0; + }).join('\n'); +} diff --git a/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateUnionTypes.ts b/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateUnionTypes.ts new file mode 100644 index 0000000000..8cb5b443ee --- /dev/null +++ b/bridge/scripts/code_generator/src/idl/IDLAPIGenerator/v8/generateUnionTypes.ts @@ -0,0 +1,208 @@ +import {ParameterType} from "../../analyzer"; +import {FunctionArgumentType} from "../../declaration"; +import _ from "lodash"; +import { + generateCoreTypeValue, + generateUnionTypeSource, isDictionary, + isPointerType, + isTypeHaveNull, isUnionType, + trimNullTypeFromType +} from "./generateSource"; + +export function generateUnionTypeClassName(unionTypes: ParameterType[]) { + let className = 'V8Union'; + for (let i = 0; i < unionTypes.length; i++) { + if (isTypeHaveNull(unionTypes[i])) continue; + className += getUnionTypeName(unionTypes[i]); + } + return className; +} + +export function getUnionTypeName(unionType: ParameterType) { + let v = unionType.value; + if (typeof v == 'number') { + return _.upperFirst(_.camelCase(FunctionArgumentType[v])); + } else if (unionType.isArray && typeof v == 'object' && !Array.isArray(v)) { + return 'Sequence' + _.upperFirst(_.camelCase(FunctionArgumentType[v.value as number])); + } else if (typeof v === 'string') { + return v; + } + return ''; +} + +export function generateUnionContentType(unionTypes: ParameterType[]) { + let result = []; + for (let i = 0; i < unionTypes.length; i++) { + if (isTypeHaveNull(unionTypes[i])) continue; + result.push('k' + getUnionTypeName(unionTypes[i])); + } + return result.join(','); +} + +export function generateUnionMemberName(unionType: ParameterType) { + let v = unionType.value; + if (typeof v == 'number') { + return FunctionArgumentType[v]; + } else if (unionType.isArray && typeof v == 'object' && !Array.isArray(v)) { + return 'sequence' + typeof v.value === 'number' ? FunctionArgumentType[v.value as number] : v.value; + } else if (typeof v == 'string') { + return v; + } + return ''; +} + +export function generateUnionMemberType(unionType: ParameterType) { + if (isDictionary(unionType)) { + return generateCoreTypeValue(unionType); + } + + if (isPointerType(unionType)) { + return `Member<${generateCoreTypeValue(unionType).replace('*', '')}>`; + } + return generateCoreTypeValue(unionType); +} + +export function generateUnionConstructor(className: string, unionType: ParameterType) { + if (isTypeHaveNull(unionType)) return ''; + if (isDictionary(unionType)) { + return `explicit ${className}(${generateCoreTypeValue(unionType)})`; + } + + if (isPointerType(unionType)) { + return `explicit ${className}(${generateCoreTypeValue(unionType)})`; + } + return `explicit ${className}(const ${generateCoreTypeValue(unionType)}& value)`; +} + +export function generateUnionConstructorImpl(className: string, unionType: ParameterType) { + if (isTypeHaveNull(unionType)) return ''; + if (isPointerType(unionType)) { + return `${className}::${className}(${generateCoreTypeValue(unionType)} value): member_${generateUnionMemberName(unionType)}_(value), content_type_(ContentType::k${getUnionTypeName(unionType)}) {}`; + } + return `${className}::${className}(const ${generateCoreTypeValue(unionType)}& value): member_${generateUnionMemberName(unionType)}_(value), content_type_(ContentType::k${getUnionTypeName(unionType)}) {}`; +} + +export function generateUnionTypeSetter(className: string, unionType: ParameterType): string { + if (isTypeHaveNull(unionType)) return ''; + if (isPointerType(unionType)) { + return `void ${className}::Set(${generateCoreTypeValue(unionType)} value) { + Clear(); + member_${generateUnionMemberName(unionType)}_ = value; + content_type_ = ContentType::k${getUnionTypeName(unionType)}; +}`; + } + return `void ${className}::Set(${generateCoreTypeValue(unionType)}&& value) { + Clear(); + member_${generateUnionMemberName(unionType)}_ = value; + content_type_ = ContentType::k${getUnionTypeName(unionType)}; +} + +void ${className}::Set(const ${generateCoreTypeValue(unionType)}& value) { + Clear(); + member_${generateUnionMemberName(unionType)}_ = value; + content_type_ = ContentType::k${getUnionTypeName(unionType)}; +}`; +} + +export function generateTypeRawChecker(unionType: ParameterType): string { + let returnValue = ''; + + if (unionType.isArray) { + return `JS_IsArray(ctx, value)`; + } else if (isPointerType(unionType)) { + return `JS_IsObject(value)`; + } else { + unionType = trimNullTypeFromType(unionType); + switch (unionType.value) { + case FunctionArgumentType.int32: + returnValue = `JS_IsNumber(value)`; + break; + case FunctionArgumentType.int64: + returnValue = 'JS_IsNumber(value)'; + break; + case FunctionArgumentType.double: + returnValue = `JS_IsNumber(value)`; + break; + case FunctionArgumentType.function: + returnValue = `JS_IsFunction(ctx, value)`; + break; + case FunctionArgumentType.boolean: + returnValue = `JS_IsBool(value)`; + break; + case FunctionArgumentType.dom_string: + returnValue = `JS_IsString(value)`; + break; + case FunctionArgumentType.object: + returnValue = `JS_IsObject(ctx, value)`; + break; + case FunctionArgumentType.null: + returnValue = `JS_IsNull(value)`; + break; + default: + case FunctionArgumentType.any: + throw new Error('Can not generate type checker code for any type'); + } + } + + return returnValue; +} + +export function generateUnionTypeClear(unionType: ParameterType): string { + let returnValue = ''; + + if (unionType.isArray) { + return `.clear()`; + } else if (isPointerType(unionType)) { + return `= nullptr`; + } else { + unionType = trimNullTypeFromType(unionType); + switch (unionType.value) { + case FunctionArgumentType.int32: + returnValue = `= 0`; + break; + case FunctionArgumentType.int64: + returnValue = '= 0'; + break; + case FunctionArgumentType.double: + returnValue = `= 0.0`; + break; + case FunctionArgumentType.boolean: + returnValue = `= false`; + break; + case FunctionArgumentType.dom_string: + returnValue = `= AtomicString::Empty()`; + break; + case FunctionArgumentType.object: + returnValue = `= ScriptValue::Empty(ctx)`; + break; + default: + case FunctionArgumentType.any: + throw new Error('Can not generate type checker code for any type'); + } + } + + return returnValue; +} + +export function generateUnionPropertyHeaders(unionTypes: ParameterType[]) { + return unionTypes.map(unionType => { + let nativeType = ''; + let setter = ''; + if (isTypeHaveNull(unionType)) return ''; + if (isPointerType(unionType)) { + nativeType = `${generateCoreTypeValue(unionType)}`; + setter = `void Set(${generateCoreTypeValue(unionType)} value);`; + } else { + nativeType = `const ${generateCoreTypeValue(unionType)}&`; + setter = `void Set(const ${generateCoreTypeValue(unionType)}& value); + void Set(${generateCoreTypeValue(unionType)}&& value);`; + } + + return `bool Is${getUnionTypeName(unionType)}() const { return content_type_ == ContentType::k${getUnionTypeName(unionType)}; } + ${nativeType} GetAs${getUnionTypeName(unionType)}() const { + assert(content_type_ == ContentType::k${getUnionTypeName(unionType)}); + return member_${generateUnionMemberName(unionType)}_; + } + ${setter}`; + }).join('\n '); +} diff --git a/bridge/scripts/code_generator/src/idl/IDLBlob.ts b/bridge/scripts/code_generator/src/idl/IDLBlob.ts index da74425840..50154c18ea 100644 --- a/bridge/scripts/code_generator/src/idl/IDLBlob.ts +++ b/bridge/scripts/code_generator/src/idl/IDLBlob.ts @@ -7,13 +7,15 @@ export class IDLBlob { source: string; filename: string; implement: string; + platformPrefix: string; objects: (ClassObject | FunctionObject)[]; - constructor(source: string, dist: string, filename: string, implement: string) { + constructor(source: string, dist: string, filename: string, implement: string, platformPrefix: string) { this.source = source; this.raw = fs.readFileSync(source, {encoding: 'utf-8'}); this.dist = dist; this.filename = filename; this.implement = implement; + this.platformPrefix = platformPrefix; } } diff --git a/bridge/scripts/code_generator/src/idl/generator.ts b/bridge/scripts/code_generator/src/idl/generator.ts index 027cd10912..ac945d695d 100644 --- a/bridge/scripts/code_generator/src/idl/generator.ts +++ b/bridge/scripts/code_generator/src/idl/generator.ts @@ -1,11 +1,15 @@ import {IDLBlob} from './IDLBlob'; import {generateQuickJSCppHeader} from "./IDLAPIGenerator/quickjs/generateHeader"; +import {generateV8CppHeader} from "./IDLAPIGenerator/v8/generateHeader"; import { generateCoreTypeValue, generateQuickJSCppSource, generateUnionTypeSource, getPointerType, isPointerType, isTypeHaveNull } from "./IDLAPIGenerator/quickjs/generateSource"; +import { + generateV8CppSource +} from "./IDLAPIGenerator/v8/generateSource"; import {ParameterType} from "./analyzer"; import {FunctionArgumentType} from "./declaration"; import _ from "lodash"; @@ -50,8 +54,16 @@ export type GenerateOptions = { export function generatorSource(blob: IDLBlob) { let options = generateSupportedOptions(); - let source = generateQuickJSCppSource(blob, options); - let header = generateQuickJSCppHeader(blob, options); + let source = ''; + let header = ''; + if (blob.platformPrefix == 'qjs') { + source = generateQuickJSCppSource(blob, options); + header = generateQuickJSCppHeader(blob, options); + } else { + source = generateV8CppSource(blob, options); + header = generateV8CppHeader(blob, options); + } + return { header, source diff --git a/bridge/scripts/code_generator/src/idl/utils.ts b/bridge/scripts/code_generator/src/idl/utils.ts index fb968f047f..244fb7a14a 100644 --- a/bridge/scripts/code_generator/src/idl/utils.ts +++ b/bridge/scripts/code_generator/src/idl/utils.ts @@ -12,8 +12,15 @@ export function addIndent(str: String, space: number) { return lines.join('\n'); } +function getUniversalPlatformFilename(blob: IDLBlob) { + const prefix = blob.platformPrefix + '_'; + if (blob.filename.startsWith(prefix)) { + return blob.filename.substring(prefix.length); + } + return blob.filename; +} export function getClassName(blob: IDLBlob) { - let raw = camelCase(blob.filename[4].toUpperCase() + blob.filename.slice(5)); + let raw = camelCase(getUniversalPlatformFilename(blob)); if (raw.slice(0, 3) == 'dom') { if (raw === 'domMatrixReadonly') { return `DOMMatrixReadOnly`; 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 index 7368244cf0..0625cd6720 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/v8/base.cc.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/v8/base.cc.tpl @@ -4,25 +4,25 @@ */ #include "<%= blob.filename %>.h" -#include "foundation/native_value_converter.h" +//#include "foundation/native_value_converter.h" #include "binding_call_methods.h" -#include "bindings/qjs/member_installer.h" -#include "bindings/qjs/qjs_function.h" -#include "bindings/qjs/converter_impl.h" -#include "bindings/qjs/script_wrappable.h" -#include "bindings/qjs/script_promise.h" -#include "bindings/qjs/cppgc/mutation_scope.h" +#include "bindings/v8/member_installer.h" +//#include "bindings/qjs/qjs_function.h" +//#include "bindings/qjs/converter_impl.h" +//#include "bindings/qjs/script_wrappable.h" +//#include "bindings/qjs/script_promise.h" +//#include "bindings/qjs/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/geometry/dom_matrix.h" -#include "core/geometry/dom_point.h" -#include "core/input/touch_list.h" -#include "core/dom/static_node_list.h" -#include "core/html/html_all_collection.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/geometry/dom_matrix.h" +//#include "core/geometry/dom_point.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 { @@ -33,7 +33,7 @@ namespace webf { <%= content %> <% if (globalFunctionInstallList.length > 0 || classPropsInstallList.length > 0 || classMethodsInstallList.length > 0 || constructorInstallList.length > 0) { %> -void QJS<%= className %>::Install(ExecutingContext* context) { +void V8<%= className %>::Install(ExecutingContext* context) { <% if (globalFunctionInstallList.length > 0) { %> InstallGlobalFunctions(context); <% } %> <% if(classPropsInstallList.length > 0) { %> InstallPrototypeProperties(context); <% } %> <% if(classMethodsInstallList.length > 0) { %> InstallPrototypeMethods(context); <% } %> @@ -44,7 +44,7 @@ void QJS<%= className %>::Install(ExecutingContext* context) { <% } %> <% if(globalFunctionInstallList.length > 0) { %> -void QJS<%= className %>::InstallGlobalFunctions(ExecutingContext* context) { +void V8<%= className %>::InstallGlobalFunctions(ExecutingContext* context) { std::initializer_list functionConfig { <%= globalFunctionInstallList.join(',\n') %> }; @@ -53,7 +53,7 @@ void QJS<%= className %>::InstallGlobalFunctions(ExecutingContext* context) { <% } %> <% if(classPropsInstallList.length > 0) { %> -void QJS<%= className %>::InstallPrototypeProperties(ExecutingContext* context) { +void V8<%= className %>::InstallPrototypeProperties(ExecutingContext* context) { const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); JSValue prototype = context->contextData()->prototypeForType(wrapperTypeInfo); std::initializer_list functionConfig { @@ -64,7 +64,7 @@ void QJS<%= className %>::InstallPrototypeProperties(ExecutingContext* context) <% } %> <% if(classMethodsInstallList.length > 0) { %> -void QJS<%= className %>::InstallPrototypeMethods(ExecutingContext* context) { +void V8<%= className %>::InstallPrototypeMethods(ExecutingContext* context) { const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); JSValue prototype = context->contextData()->prototypeForType(wrapperTypeInfo); @@ -77,7 +77,7 @@ void QJS<%= className %>::InstallPrototypeMethods(ExecutingContext* context) { <% } %> <% if(staticMethodsInstallList.length > 0) { %> -void QJS<%= className %>::InstallStaticMethods(ExecutingContext* context) { +void V8<%= className %>::InstallStaticMethods(ExecutingContext* context) { const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); JSValue constructor = context->contextData()->constructorForType(wrapperTypeInfo); std::initializer_list functionConfig { @@ -88,7 +88,7 @@ void QJS<%= className %>::InstallStaticMethods(ExecutingContext* context) { <% } %> <% if (constructorInstallList.length > 0) { %> -void QJS<%= className %>::InstallConstructor(ExecutingContext* context) { +void V8<%= className %>::InstallConstructor(ExecutingContext* context) { const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); JSValue constructor = context->contextData()->constructorForType(wrapperTypeInfo); 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 index 23b75c5e73..aa0fd18ba8 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/v8/base.h.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/v8/base.h.tpl @@ -6,9 +6,9 @@ #ifndef BRIDGE_<%= blob.filename.toUpperCase() %>_H #define BRIDGE_<%= blob.filename.toUpperCase() %>_H -#include -#include "bindings/qjs/wrapper_type_info.h" -#include "bindings/qjs/generated_code_helper.h" +#include +//#include "bindings/v8/wrapper_type_info.h" +#include "bindings/v8/generated_code_helper.h" <%= content %> 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 index 4b38cfe531..20163bba04 100644 --- 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 @@ -4,7 +4,7 @@ namespace webf { class ExecutingContext; -class QJS<%= className %> final { +class V8<%= className %> final { public: static void Install(ExecutingContext* context); private: 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 index 3e88a8bd7f..4ec2b96ed5 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/v8/interface.cc.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/v8/interface.cc.tpl @@ -5,7 +5,7 @@ JSValue QJS<%= className %>::ConstructorCallback(JSContext* ctx, JSValue func_ob <% } %> <% if (object.indexedProp) { %> - bool QJS<%= className %>::PropertyCheckerCallback(JSContext* ctx, JSValueConst obj, JSAtom key) { + bool V8<%= className %>::PropertyCheckerCallback(JSContext* ctx, JSValueConst obj, JSAtom key) { auto* self = toScriptWrappable<<%= className %>>(obj); ExceptionState exception_state; ExecutingContext* context = ExecutingContext::From(ctx); 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 index 2e92ca19a4..7ecb547fd7 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/v8/interface.h.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/v8/interface.h.tpl @@ -1,7 +1,7 @@ #include "core/<%= blob.implement %>.h" <% if(parentClassName) { %> -#include "qjs_<%= _.snakeCase(parentClassName) %>.h" +#include "v8_<%= _.snakeCase(parentClassName) %>.h" <% } %> namespace webf { @@ -33,7 +33,7 @@ Native<%= parentClassName %> native_event; #endif <% } %> -class QJS<%= className %> : public QJSInterfaceBridge, <%= className%>> { +class V8<%= className %> : public V8InterfaceBridge, <%= className%>> { public: static void Install(ExecutingContext* context); static WrapperTypeInfo* GetWrapperTypeInfo() { 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 index ef88ee482e..e39bc78fe0 100644 --- 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 @@ -1,23 +1,25 @@ // Generated from template: // code_generator/src/json/templates/names_installer.cc.tmpl -<% names.forEach(function(k) { %> -#include "<%= k %>.h" -<% }); %> +//<% names.forEach(function(k) { %> +//#include "<%= k %>.h" +//<% }); %> + +#include "<%= name %>.h" namespace webf { namespace <%= name %> { -void Init(JSContext* ctx) { -<% names.forEach(function(k) { %> - <%= k %>::Init(ctx); -<% }); %> +void Init(v8::Isolate* ctx) { +//<% names.forEach(function(k) { %> +// <%= k %>::Init(ctx); +//<% }); %> } void Dispose() { -<% names.forEach(function(k) { %> - <%= k %>::Dispose(); -<% }); %> +//<% names.forEach(function(k) { %> +// <%= k %>::Dispose(); +//<% }); %> } } 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 index 1b5effbfd5..762ac645f1 100644 --- 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 @@ -10,7 +10,7 @@ namespace webf { namespace <%= name %> { -void Init(JSContext* ctx); +void Init(v8::Isolate* ctx); void Dispose(); }