From 8661b8557e370b839af7192277e3a8c3568f68ba Mon Sep 17 00:00:00 2001 From: "chenghuai.dtc" Date: Wed, 1 Dec 2021 19:14:53 +0800 Subject: [PATCH] fix: fix nativeString memory leaks. --- bridge/bindings/qjs/dom/custom_event.cc | 2 +- bridge/bindings/qjs/dom/document.cc | 4 ++-- bridge/bindings/qjs/dom/element.cc | 12 +++++----- .../qjs/dom/elements/image_element.cc | 16 +++++++------- bridge/bindings/qjs/dom/event.cc | 6 ++--- bridge/bindings/qjs/dom/event_target.cc | 8 +++---- bridge/bindings/qjs/dom/events/touch_event.cc | 2 +- bridge/bindings/qjs/dom/node.cc | 14 ++++++------ bridge/bindings/qjs/dom/style_declaration.cc | 8 +++---- bridge/bindings/qjs/dom/text_node.cc | 6 ++--- bridge/bindings/qjs/js_context.cc | 19 ++++++++-------- bridge/bindings/qjs/js_context.h | 22 +++++++++++++++---- bridge/bindings/qjs/js_context_test.cc | 6 ++--- bridge/bindings/qjs/module_manager.cc | 21 +++++++++--------- bridge/bindings/qjs/native_value.cc | 11 ++++++---- bridge/bindings/qjs/qjs_patch.cc | 16 +++++++------- bridge/bindings/qjs/qjs_patch_test.cc | 1 + bridge/bridge_test_qjs.cc | 13 +++++------ bridge/include/kraken_bridge.h | 2 +- bridge/kraken_bridge.cc | 10 +++------ .../code_generator/src/genereate_source.ts | 12 +++++----- kraken/lib/src/bridge/from_native.dart | 2 +- 22 files changed, 113 insertions(+), 100 deletions(-) diff --git a/bridge/bindings/qjs/dom/custom_event.cc b/bridge/bindings/qjs/dom/custom_event.cc index 19e703e50b..c1f3727fac 100644 --- a/bridge/bindings/qjs/dom/custom_event.cc +++ b/bridge/bindings/qjs/dom/custom_event.cc @@ -27,7 +27,7 @@ JSValue CustomEvent::initCustomEvent(QjsContext* ctx, JSValue this_val, int argc } JSValue typeValue = argv[0]; - eventInstance->nativeEvent->type = jsValueToNativeString(ctx, typeValue); + eventInstance->nativeEvent->type = jsValueToNativeString(ctx, typeValue).release(); if (argc <= 2) { bool canBubble = JS_ToBool(ctx, argv[1]); diff --git a/bridge/bindings/qjs/dom/document.cc b/bridge/bindings/qjs/dom/document.cc index b7f5cc0ece..f440c1ccdd 100644 --- a/bridge/bindings/qjs/dom/document.cc +++ b/bridge/bindings/qjs/dom/document.cc @@ -140,8 +140,8 @@ JSValue Document::createEvent(QjsContext* ctx, JSValue this_val, int argc, JSVal JS_FreeCString(ctx, c_eventType); std::string eventType = std::string(c_eventType); if (eventType == "Event") { - NativeString* nativeEventType = jsValueToNativeString(ctx, eventTypeValue); - auto nativeEvent = new NativeEvent{nativeEventType}; + std::unique_ptr nativeEventType = jsValueToNativeString(ctx, eventTypeValue); + auto nativeEvent = new NativeEvent{nativeEventType.release()}; auto document = static_cast(JS_GetOpaque(this_val, Document::classId())); auto e = Event::buildEventInstance(eventType, document->context(), nativeEvent, false); diff --git a/bridge/bindings/qjs/dom/element.cc b/bridge/bindings/qjs/dom/element.cc index 8d3b3a55ef..5734b9f57c 100644 --- a/bridge/bindings/qjs/dom/element.cc +++ b/bridge/bindings/qjs/dom/element.cc @@ -213,8 +213,8 @@ JSValue Element::setAttribute(QjsContext* ctx, JSValue this_val, int argc, JSVal element->_didModifyAttribute(name, JS_ATOM_NULL, attributeAtom); } - NativeString* args_01 = stringToNativeString(name); - NativeString* args_02 = jsValueToNativeString(ctx, attributeString); + std::unique_ptr args_01 = stringToNativeString(name); + std::unique_ptr args_02 = jsValueToNativeString(ctx, attributeString); ::foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); @@ -267,7 +267,7 @@ JSValue Element::removeAttribute(QjsContext* ctx, JSValue this_val, int argc, JS element->m_attributes->removeAttribute(name); element->_didModifyAttribute(name, id, JS_ATOM_NULL); - NativeString* args_01 = stringToNativeString(name); + std::unique_ptr args_01 = stringToNativeString(name); ::foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::removeProperty, *args_01, nullptr); } @@ -399,8 +399,8 @@ PROP_SETTER(ElementInstance, className)(QjsContext* ctx, JSValue this_val, int a auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); JSAtom atom = JS_ValueToAtom(ctx, argv[0]); element->m_attributes->setAttribute("class", atom); - NativeString* args_01 = stringToNativeString("class"); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString("class"); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); ::foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); JS_FreeAtom(ctx, atom); return JS_NULL; @@ -846,7 +846,7 @@ ElementInstance::ElementInstance(Element* element, std::string tagName, bool sho JS_DefinePropertyValueStr(m_ctx, instanceObject, "attributes", m_attributes->jsObject, JS_PROP_C_W_E); if (shouldAddUICommand) { - NativeString* args_01 = stringToNativeString(tagName); + std::unique_ptr args_01 = stringToNativeString(tagName); ::foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::createElement, *args_01, nativeEventTarget); } } diff --git a/bridge/bindings/qjs/dom/elements/image_element.cc b/bridge/bindings/qjs/dom/elements/image_element.cc index 6c237107bb..6d61ddd260 100644 --- a/bridge/bindings/qjs/dom/elements/image_element.cc +++ b/bridge/bindings/qjs/dom/elements/image_element.cc @@ -31,8 +31,8 @@ PROP_GETTER(ImageElementInstance, width)(QjsContext* ctx, JSValue this_val, int PROP_SETTER(ImageElementInstance, width)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "width"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } @@ -44,8 +44,8 @@ PROP_GETTER(ImageElementInstance, height)(QjsContext* ctx, JSValue this_val, int PROP_SETTER(ImageElementInstance, height)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "height"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } @@ -73,8 +73,8 @@ PROP_GETTER(ImageElementInstance, src)(QjsContext* ctx, JSValue this_val, int ar PROP_SETTER(ImageElementInstance, src)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "src"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } @@ -86,8 +86,8 @@ PROP_GETTER(ImageElementInstance, loading)(QjsContext* ctx, JSValue this_val, in PROP_SETTER(ImageElementInstance, loading)(QjsContext* ctx, JSValue this_val, int argc, JSValue* argv) { auto* element = static_cast(JS_GetOpaque(this_val, Element::classId())); std::string key = "loading"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(element->m_context->getContextId())->addCommand(element->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL; } diff --git a/bridge/bindings/qjs/dom/event.cc b/bridge/bindings/qjs/dom/event.cc index 1e3790ce75..677c08f580 100644 --- a/bridge/bindings/qjs/dom/event.cc +++ b/bridge/bindings/qjs/dom/event.cc @@ -32,7 +32,7 @@ JSValue Event::instanceConstructor(QjsContext* ctx, JSValue func_obj, JSValue th JSValue eventTypeValue = argv[0]; std::string eventType = jsValueToStdString(ctx, eventTypeValue); - auto* nativeEvent = new NativeEvent{stringToNativeString(eventType)}; + auto* nativeEvent = new NativeEvent{stringToNativeString(eventType).release()}; auto* event = Event::buildEventInstance(eventType, m_context, nativeEvent, false); return event->instanceObject; } @@ -200,7 +200,7 @@ JSValue Event::initEvent(QjsContext* ctx, JSValue this_val, int argc, JSValue* a } auto* event = static_cast(JS_GetOpaque(this_val, Event::kEventClassID)); - event->nativeEvent->type = jsValueToNativeString(ctx, typeValue); + event->nativeEvent->type = jsValueToNativeString(ctx, typeValue).release(); if (!JS_IsNull(bubblesValue)) { event->nativeEvent->bubbles = JS_IsBool(bubblesValue) ? 1 : 0; @@ -218,7 +218,7 @@ EventInstance* EventInstance::fromNativeEvent(Event* event, NativeEvent* nativeE EventInstance::EventInstance(Event* event, NativeEvent* nativeEvent) : nativeEvent(nativeEvent), Instance(event, "Event", nullptr, Event::kEventClassID, finalizer) {} EventInstance::EventInstance(Event* jsEvent, JSAtom eventType, JSValue eventInit) : Instance(jsEvent, "Event", nullptr, Event::kEventClassID, finalizer) { JSValue v = JS_AtomToValue(m_ctx, eventType); - nativeEvent = new NativeEvent{jsValueToNativeString(m_ctx, v)}; + nativeEvent = new NativeEvent{jsValueToNativeString(m_ctx, v).release()}; JS_FreeValue(m_ctx, v); auto ms = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); diff --git a/bridge/bindings/qjs/dom/event_target.cc b/bridge/bindings/qjs/dom/event_target.cc index 3728e29857..05a5fcfeeb 100644 --- a/bridge/bindings/qjs/dom/event_target.cc +++ b/bridge/bindings/qjs/dom/event_target.cc @@ -367,8 +367,8 @@ int EventTargetInstance::setProperty(QjsContext* ctx, JSValue obj, JSAtom atom, JS_SetProperty(ctx, eventTarget->m_properties, atom, JS_DupValue(ctx, value)); if (isJavaScriptExtensionElementInstance(eventTarget->context(), eventTarget->instanceObject) && !p->is_wide_char && p->u.str8[0] != '_') { - NativeString* args_01 = atomToNativeString(ctx, atom); - NativeString* args_02 = jsValueToNativeString(ctx, value); + std::unique_ptr args_01 = atomToNativeString(ctx, atom); + std::unique_ptr args_02 = jsValueToNativeString(ctx, value); foundation::UICommandBuffer::instance(eventTarget->m_contextId)->addCommand(eventTarget->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); } } @@ -390,7 +390,7 @@ JSValue EventTargetInstance::callNativeMethods(const char* method, int32_t argc, std::u16string methodString; fromUTF8(method, methodString); - NativeString m{reinterpret_cast(methodString.c_str()), static_cast(methodString.size())}; + NativeString m{reinterpret_cast(methodString.c_str()), static_cast(methodString.size())}; NativeValue nativeValue{}; nativeEventTarget->callNativeMethods(nativeEventTarget, &nativeValue, &m, argc, argv); @@ -425,7 +425,7 @@ void EventTargetInstance::setPropertyHandler(JSString* p, JSValue value) { int32_t eventHandlerLen = arrayGetLength(m_ctx, m_eventHandlers); if (eventHandlerLen == 0) { int32_t contextId = m_context->getContextId(); - NativeString* args_01 = atomToNativeString(m_ctx, atom); + std::unique_ptr args_01 = atomToNativeString(m_ctx, atom); int32_t type = JS_IsFunction(m_ctx, value) ? UICommand::addEvent : UICommand::removeEvent; foundation::UICommandBuffer::instance(contextId)->addCommand(m_eventTargetId, type, *args_01, nullptr); } diff --git a/bridge/bindings/qjs/dom/events/touch_event.cc b/bridge/bindings/qjs/dom/events/touch_event.cc index fee9b6c550..87abbece5a 100644 --- a/bridge/bindings/qjs/dom/events/touch_event.cc +++ b/bridge/bindings/qjs/dom/events/touch_event.cc @@ -162,7 +162,7 @@ JSValue TouchEvent::instanceConstructor(QjsContext* ctx, JSValue func_obj, JSVal } auto* nativeEvent = new NativeTouchEvent(); - nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue); + nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue).release(); if (JS_IsObject(eventInit)) { JSAtom touchesAtom = JS_NewAtom(m_ctx, "touches"); diff --git a/bridge/bindings/qjs/dom/node.cc b/bridge/bindings/qjs/dom/node.cc index 08e2bf08fa..5d83973894 100644 --- a/bridge/bindings/qjs/dom/node.cc +++ b/bridge/bindings/qjs/dom/node.cc @@ -275,7 +275,7 @@ JSValue Node::copyNodeValue(QjsContext* ctx, NodeInstance* node) { ElementInstance::copyNodeProperties(newElement, element); std::string newNodeEventTargetId = std::to_string(newElement->m_eventTargetId); - NativeString* args_01 = stringToNativeString(newNodeEventTargetId); + std::unique_ptr args_01 = stringToNativeString(newNodeEventTargetId); foundation::UICommandBuffer::instance(newElement->context()->getContextId())->addCommand(element->m_eventTargetId, UICommand::cloneNode, *args_01, nullptr); return newElement->instanceObject; @@ -443,8 +443,8 @@ void NodeInstance::internalAppendChild(NodeInstance* node) { std::string nodeEventTargetId = std::to_string(node->m_eventTargetId); std::string position = std::string("beforeend"); - NativeString* args_01 = stringToNativeString(nodeEventTargetId); - NativeString* args_02 = stringToNativeString(position); + std::unique_ptr args_01 = stringToNativeString(nodeEventTargetId); + std::unique_ptr args_02 = stringToNativeString(position); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); } @@ -505,8 +505,8 @@ JSValue NodeInstance::internalInsertBefore(NodeInstance* node, NodeInstance* ref std::string nodeEventTargetId = std::to_string(node->m_eventTargetId); std::string position = std::string("beforebegin"); - NativeString* args_01 = stringToNativeString(nodeEventTargetId); - NativeString* args_02 = stringToNativeString(position); + std::unique_ptr args_01 = stringToNativeString(nodeEventTargetId); + std::unique_ptr args_02 = stringToNativeString(position); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(referenceNode->m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); } @@ -537,8 +537,8 @@ JSValue NodeInstance::internalReplaceChild(NodeInstance* newChild, NodeInstance* std::string newChildEventTargetId = std::to_string(newChild->m_eventTargetId); std::string position = std::string("afterend"); - NativeString* args_01 = stringToNativeString(newChildEventTargetId); - NativeString* args_02 = stringToNativeString(position); + std::unique_ptr args_01 = stringToNativeString(newChildEventTargetId); + std::unique_ptr args_02 = stringToNativeString(position); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(oldChild->m_eventTargetId, UICommand::insertAdjacentNode, *args_01, *args_02, nullptr); diff --git a/bridge/bindings/qjs/dom/style_declaration.cc b/bridge/bindings/qjs/dom/style_declaration.cc index 1e0c7ba8af..f4aa051677 100644 --- a/bridge/bindings/qjs/dom/style_declaration.cc +++ b/bridge/bindings/qjs/dom/style_declaration.cc @@ -123,8 +123,8 @@ bool StyleDeclarationInstance::internalSetProperty(std::string& name, JSValue va properties[name] = jsValueToStdString(m_ctx, value); if (ownerEventTarget != nullptr) { - NativeString* args_01 = stringToNativeString(name); - NativeString* args_02 = jsValueToNativeString(m_ctx, value); + std::unique_ptr args_01 = stringToNativeString(name); + std::unique_ptr args_02 = jsValueToNativeString(m_ctx, value); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(ownerEventTarget->eventTargetId(), UICommand::setStyle, *args_01, *args_02, nullptr); } @@ -141,8 +141,8 @@ void StyleDeclarationInstance::internalRemoveProperty(std::string& name) { properties.erase(name); if (ownerEventTarget != nullptr) { - NativeString* args_01 = stringToNativeString(name); - NativeString* args_02 = jsValueToNativeString(m_ctx, JS_NULL); + std::unique_ptr args_01 = stringToNativeString(name); + std::unique_ptr args_02 = jsValueToNativeString(m_ctx, JS_NULL); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(ownerEventTarget->eventTargetId(), UICommand::setStyle, *args_01, *args_02, nullptr); } } diff --git a/bridge/bindings/qjs/dom/text_node.cc b/bridge/bindings/qjs/dom/text_node.cc index bbd4b660d7..b72a6996f7 100644 --- a/bridge/bindings/qjs/dom/text_node.cc +++ b/bridge/bindings/qjs/dom/text_node.cc @@ -65,7 +65,7 @@ PROP_SETTER(TextNodeInstance, nodeName)(QjsContext* ctx, JSValue this_val, int a TextNodeInstance::TextNodeInstance(TextNode* textNode, JSValue text) : NodeInstance(textNode, NodeType::TEXT_NODE, DocumentInstance::instance(Document::instance(textNode->m_context)), TextNode::classId(), "TextNode"), m_data(JS_DupValue(m_ctx, text)) { - NativeString* args_01 = jsValueToNativeString(m_ctx, m_data); + std::unique_ptr args_01 = jsValueToNativeString(m_ctx, m_data); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::createTextNode, *args_01, nativeEventTarget); } @@ -91,8 +91,8 @@ void TextNodeInstance::internalSetTextContent(JSValue content) { m_data = JS_DupValue(m_ctx, content); std::string key = "data"; - NativeString* args_01 = stringToNativeString(key); - NativeString* args_02 = jsValueToNativeString(m_ctx, content); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(m_ctx, content); foundation::UICommandBuffer::instance(m_context->getContextId())->addCommand(m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); } } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/js_context.cc b/bridge/bindings/qjs/js_context.cc index b76463ebc3..499fbaf1f5 100644 --- a/bridge/bindings/qjs/js_context.cc +++ b/bridge/bindings/qjs/js_context.cc @@ -332,7 +332,7 @@ void JSContext::promiseRejectTracker(QjsContext* ctx, JSValue promise, JSValue r context->dispatchGlobalPromiseRejectionEvent(promise, reason); } -NativeString* jsValueToNativeString(QjsContext* ctx, JSValue value) { +std::unique_ptr jsValueToNativeString(QjsContext* ctx, JSValue value) { bool isValueString = true; if (JS_IsNull(value)) { value = JS_NewString(ctx, ""); @@ -344,15 +344,14 @@ NativeString* jsValueToNativeString(QjsContext* ctx, JSValue value) { uint32_t length; uint16_t* buffer = JS_ToUnicode(ctx, value, &length); - NativeString tmp{}; - tmp.string = buffer; - tmp.length = length; - NativeString* cloneString = tmp.clone(); + std::unique_ptr ptr = std::make_unique(); + ptr->string = buffer; + ptr->length = length; if (!isValueString) { JS_FreeValue(ctx, value); } - return cloneString; + return ptr; } void buildUICommandArgs(QjsContext* ctx, JSValue key, NativeString& args_01) { @@ -365,18 +364,18 @@ void buildUICommandArgs(QjsContext* ctx, JSValue key, NativeString& args_01) { args_01.length = length; } -NativeString* stringToNativeString(const std::string& string) { +std::unique_ptr stringToNativeString(const std::string& string) { std::u16string utf16; fromUTF8(string, utf16); NativeString tmp{}; tmp.string = reinterpret_cast(utf16.c_str()); tmp.length = utf16.size(); - return tmp.clone(); + return std::unique_ptr(tmp.clone()); } -NativeString* atomToNativeString(QjsContext* ctx, JSAtom atom) { +std::unique_ptr atomToNativeString(QjsContext* ctx, JSAtom atom) { JSValue stringValue = JS_AtomToString(ctx, atom); - NativeString* string = jsValueToNativeString(ctx, stringValue); + std::unique_ptr string = jsValueToNativeString(ctx, stringValue); JS_FreeValue(ctx, stringValue); return string; } diff --git a/bridge/bindings/qjs/js_context.h b/bridge/bindings/qjs/js_context.h index d482d9dc62..5ab4b24f83 100644 --- a/bridge/bindings/qjs/js_context.h +++ b/bridge/bindings/qjs/js_context.h @@ -193,19 +193,33 @@ class JSValueHolder { }; std::unique_ptr createJSContext(int32_t contextId, const JSExceptionHandler& handler, void* owner); -NativeString* jsValueToNativeString(QjsContext* ctx, JSValue value); + +// Convert to string and return a full copy of NativeString from JSValue. +std::unique_ptr jsValueToNativeString(QjsContext* ctx, JSValue value); + void buildUICommandArgs(QjsContext* ctx, JSValue key, NativeString& args_01); -NativeString* stringToNativeString(const std::string& string); -NativeString* atomToNativeString(QjsContext* ctx, JSAtom atom); + +// Encode utf-8 to utf-16, and return a full copy of NativeString. +std::unique_ptr stringToNativeString(const std::string& string); + +// Return a full copy of NativeString form JSAtom. +std::unique_ptr atomToNativeString(QjsContext* ctx, JSAtom atom); + +// Convert to string and return a full copy of std::string from JSValue. std::string jsValueToStdString(QjsContext* ctx, JSValue& value); + +// Return a full copy of std::string form JSAtom. std::string jsAtomToStdString(QjsContext* ctx, JSAtom atom); -void extractErrorInfo(JSValueConst error); + +// JS array operation utilities. void arrayPushValue(QjsContext* ctx, JSValue array, JSValue val); void arrayInsert(QjsContext* ctx, JSValue array, uint32_t start, JSValue targetValue); int32_t arrayGetLength(QjsContext* ctx, JSValue array); int32_t arrayFindIdx(QjsContext* ctx, JSValue array, JSValue target); void arraySpliceValue(QjsContext* ctx, JSValue array, uint32_t start, uint32_t deleteCount); void arraySpliceValue(QjsContext* ctx, JSValue array, uint32_t start, uint32_t deleteCount, JSValue replacedValue); + +// JS object operation utilities. JSValue objectGetKeys(QjsContext* ctx, JSValue obj); } // namespace kraken::binding::qjs diff --git a/bridge/bindings/qjs/js_context_test.cc b/bridge/bindings/qjs/js_context_test.cc index fb8dd8f881..52d2aa4a3b 100644 --- a/bridge/bindings/qjs/js_context_test.cc +++ b/bridge/bindings/qjs/js_context_test.cc @@ -143,7 +143,7 @@ TEST(Context, evaluateByteCode) { TEST(jsValueToNativeString, utf8String) { auto bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) {}); JSValue str = JS_NewString(bridge->getContext()->ctx(), "helloworld"); - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); EXPECT_EQ(nativeString->length, 10); uint8_t expectedString[10] = {104, 101, 108, 108, 111, 119, 111, 114, 108, 100}; for (int i = 0; i < 10; i++) { @@ -156,7 +156,7 @@ TEST(jsValueToNativeString, utf8String) { TEST(jsValueToNativeString, unicodeChinese) { auto bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) {}); JSValue str = JS_NewString(bridge->getContext()->ctx(), "这是你的优乐美"); - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); std::u16string expectedString = u"这是你的优乐美"; EXPECT_EQ(nativeString->length, expectedString.size()); for (int i = 0; i < nativeString->length; i++) { @@ -169,7 +169,7 @@ TEST(jsValueToNativeString, unicodeChinese) { TEST(jsValueToNativeString, emoji) { auto bridge = new kraken::JSBridge(0, [](int32_t contextId, const char* errmsg) {}); JSValue str = JS_NewString(bridge->getContext()->ctx(), "……🤪"); - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(bridge->getContext()->ctx(), str); std::u16string expectedString = u"……🤪"; EXPECT_EQ(nativeString->length, expectedString.length()); for (int i = 0; i < nativeString->length; i++) { diff --git a/bridge/bindings/qjs/module_manager.cc b/bridge/bindings/qjs/module_manager.cc index 3b4d217392..a6f20bdc1b 100644 --- a/bridge/bindings/qjs/module_manager.cc +++ b/bridge/bindings/qjs/module_manager.cc @@ -108,9 +108,9 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV #endif } - NativeString* moduleName = jsValueToNativeString(ctx, moduleNameValue); - NativeString* method = jsValueToNativeString(ctx, methodValue); - NativeString* params = nullptr; + std::unique_ptr moduleName = jsValueToNativeString(ctx, moduleNameValue); + std::unique_ptr method = jsValueToNativeString(ctx, methodValue); + std::unique_ptr params; if (!JS_IsNull(paramsValue)) { JSValue stringifyedValue = JS_JSONStringify(ctx, paramsValue, JS_NULL, JS_NULL); params = jsValueToNativeString(ctx, stringifyedValue); @@ -130,9 +130,15 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV NativeString* result; if (!JS_IsNull(callbackValue)) { - result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName, method, params, handleInvokeModuleTransientCallback); + result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName.get(), method.get(), params.get(), handleInvokeModuleTransientCallback); } else { - result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName, method, params, handleInvokeModuleUnexpectedCallback); + result = getDartMethod()->invokeModule(moduleContext, context->getContextId(), moduleName.get(), method.get(), params.get(), handleInvokeModuleUnexpectedCallback); + } + + moduleName->free(); + method->free(); + if (params != nullptr) { + params->free(); } if (result == nullptr) { @@ -141,11 +147,6 @@ JSValue krakenInvokeModule(QjsContext* ctx, JSValueConst this_val, int argc, JSV JSValue resultString = JS_NewUnicodeString(context->runtime(), ctx, result->string, result->length); result->free(); - moduleName->free(); - method->free(); - if (params != nullptr) { - params->free(); - } return resultString; } diff --git a/bridge/bindings/qjs/native_value.cc b/bridge/bindings/qjs/native_value.cc index 164caf3154..9cb38e289f 100644 --- a/bridge/bindings/qjs/native_value.cc +++ b/bridge/bindings/qjs/native_value.cc @@ -28,8 +28,9 @@ NativeValue Native_NewString(NativeString* string) { } NativeValue Native_NewCString(std::string string) { - NativeString* nativeString = stringToNativeString(string); - return Native_NewString(nativeString); + std::unique_ptr nativeString = stringToNativeString(string); + // NativeString owned by NativeValue will be freed by users. + return Native_NewString(nativeString.release()); } NativeValue Native_NewFloat64(double value) { @@ -62,7 +63,8 @@ NativeValue Native_NewInt32(int32_t value) { NativeValue Native_NewJSON(JSContext* context, JSValue& value) { JSValue stringifiedValue = JS_JSONStringify(context->ctx(), value, JS_UNDEFINED, JS_UNDEFINED); - NativeString* string = jsValueToNativeString(context->ctx(), stringifiedValue); + // NativeString owned by NativeValue will be freed by users. + NativeString* string = jsValueToNativeString(context->ctx(), stringifiedValue).release(); NativeValue result = (NativeValue){ 0, .u = {.ptr = static_cast(string)}, @@ -110,7 +112,8 @@ NativeValue jsValueToNativeValue(QjsContext* ctx, JSValue& value) { return Native_NewInt32(v); } } else if (JS_IsString(value)) { - NativeString* string = jsValueToNativeString(ctx, value); + // NativeString owned by NativeValue will be freed by users. + NativeString* string = jsValueToNativeString(ctx, value).release(); return Native_NewString(string); } else if (JS_IsFunction(ctx, value)) { auto* context = static_cast(JS_GetContextOpaque(ctx)); diff --git a/bridge/bindings/qjs/qjs_patch.cc b/bridge/bindings/qjs/qjs_patch.cc index cacd8d9174..646ac1c490 100644 --- a/bridge/bindings/qjs/qjs_patch.cc +++ b/bridge/bindings/qjs/qjs_patch.cc @@ -233,30 +233,30 @@ uint16_t* JS_ToUnicode(JSContext* ctx, JSValueConst value, uint32_t* length) { if (JS_VALUE_GET_TAG(value) != JS_TAG_STRING) { value = JS_ToString(ctx, value); if (JS_IsException(value)) - return NULL; + return nullptr; } else { value = JS_DupValue(ctx, value); } + uint16_t* buffer; JSString* string = JS_VALUE_GET_STRING(value); if (!string->is_wide_char) { uint8_t* p = string->u.str8; uint32_t len = *length = string->len; - auto* newBuf = (uint16_t*)malloc(sizeof(uint16_t) * len * 2); + buffer = (uint16_t*)malloc(sizeof(uint16_t) * len * 2); for (size_t i = 0; i < len; i++) { - newBuf[i] = p[i]; - newBuf[i + 1] = 0x00; + buffer[i] = p[i]; + buffer[i + 1] = 0x00; } - JS_FreeValue(ctx, value); - return newBuf; } else { *length = string->len; + buffer = (uint16_t*)malloc(sizeof(uint16_t) * string->len); + memcpy(buffer, string->u.str16, sizeof(uint16_t) * string->len); } JS_FreeValue(ctx, value); - - return string->u.str16; + return buffer; } static JSString* js_alloc_string_rt(JSRuntime* rt, int max_len, int is_wide_char) { diff --git a/bridge/bindings/qjs/qjs_patch_test.cc b/bridge/bindings/qjs/qjs_patch_test.cc index ff3ebd2f7b..8ef564d28c 100644 --- a/bridge/bindings/qjs/qjs_patch_test.cc +++ b/bridge/bindings/qjs/qjs_patch_test.cc @@ -21,6 +21,7 @@ TEST(JS_ToUnicode, asciiWords) { JS_FreeValue(ctx, value); JS_FreeContext(ctx); JS_FreeRuntime(runtime); + delete buffer; } TEST(JS_ToUnicode, chineseWords) { diff --git a/bridge/bridge_test_qjs.cc b/bridge/bridge_test_qjs.cc index 35ddc8d226..9142d45f45 100644 --- a/bridge/bridge_test_qjs.cc +++ b/bridge/bridge_test_qjs.cc @@ -64,7 +64,7 @@ static JSValue matchImageSnapshot(QjsContext* ctx, JSValueConst this_val, int ar return JS_ThrowTypeError(ctx, "Failed to execute '__kraken_match_image_snapshot__': dart method (matchImageSnapshot) is not registered."); } - NativeString* screenShotNativeString = kraken::binding::qjs::jsValueToNativeString(ctx, screenShotValue); + std::unique_ptr screenShotNativeString = kraken::binding::qjs::jsValueToNativeString(ctx, screenShotValue); auto bridge = static_cast(static_cast(context->getOwner())->owner); auto* callbackContext = new ImageSnapShotContext{JS_DupValue(ctx, callbackValue), context}; list_add_tail(&callbackContext->link, &bridge->image_link); @@ -90,7 +90,7 @@ static JSValue matchImageSnapshot(QjsContext* ctx, JSValueConst this_val, int ar list_del(&callbackContext->link); }; - getDartMethod()->matchImageSnapshot(callbackContext, context->getContextId(), blob->bytes(), blob->size(), screenShotNativeString, fn); + getDartMethod()->matchImageSnapshot(callbackContext, context->getContextId(), blob->bytes(), blob->size(), screenShotNativeString.get(), fn); return JS_NULL; } @@ -178,8 +178,8 @@ static JSValue simulateInputText(QjsContext* ctx, JSValueConst this_val, int arg return JS_ThrowTypeError(ctx, "Failed to execute '__kraken_simulate_keypress__': first arguments should be a string"); } - NativeString* nativeString = kraken::binding::qjs::jsValueToNativeString(ctx, charStringValue); - getDartMethod()->simulateInputText(nativeString); + std::unique_ptr nativeString = kraken::binding::qjs::jsValueToNativeString(ctx, charStringValue); + getDartMethod()->simulateInputText(nativeString.get()); nativeString->free(); return JS_NULL; }; @@ -244,9 +244,8 @@ void JSBridgeTest::invokeExecuteTest(ExecuteCallback executeCallback) { return JS_ThrowTypeError(ctx, "failed to execute 'done': parameter 1 (status) is not a string"); } - NativeString* status = kraken::binding::qjs::jsValueToNativeString(ctx, statusValue); - callbackContext->executeCallback(callbackContext->context->getContextId(), status); - status->free(); + std::unique_ptr status = kraken::binding::qjs::jsValueToNativeString(ctx, statusValue); + callbackContext->executeCallback(callbackContext->context->getContextId(), status.get()); return JS_NULL; }; auto* callbackContext = new ExecuteCallbackContext(context.get(), executeCallback); diff --git a/bridge/include/kraken_bridge.h b/bridge/include/kraken_bridge.h index e7a375fc78..8cfb60928b 100644 --- a/bridge/include/kraken_bridge.h +++ b/bridge/include/kraken_bridge.h @@ -25,7 +25,7 @@ std::thread::id getUIThreadId(); struct NativeString { const uint16_t* string; - int32_t length; + uint32_t length; NativeString* clone(); void free(); diff --git a/bridge/kraken_bridge.cc b/bridge/kraken_bridge.cc index 8705282c8b..1ac0026bb4 100644 --- a/bridge/kraken_bridge.cc +++ b/bridge/kraken_bridge.cc @@ -251,13 +251,10 @@ int32_t profileModeEnabled() { } NativeString* NativeString::clone() { - NativeString* newNativeString = new NativeString(); - uint16_t* newString = new uint16_t[length]; - - for (size_t i = 0; i < length; i++) { - newString[i] = string[i]; - } + auto* newNativeString = new NativeString(); + auto* newString = new uint16_t[length]; + memcpy(newString, string, length * sizeof(uint16_t)); newNativeString->string = newString; newNativeString->length = length; return newNativeString; @@ -265,5 +262,4 @@ NativeString* NativeString::clone() { void NativeString::free() { delete[] string; - delete this; } diff --git a/bridge/scripts/code_generator/src/genereate_source.ts b/bridge/scripts/code_generator/src/genereate_source.ts index 2384dd3789..e3523a3177 100644 --- a/bridge/scripts/code_generator/src/genereate_source.ts +++ b/bridge/scripts/code_generator/src/genereate_source.ts @@ -27,7 +27,7 @@ JSValue ${object.name}::callNativeMethods(const char *method, int32_t argc, NativeString m{ reinterpret_cast(methodString.c_str()), - static_cast(methodString.size()) + static_cast(methodString.size()) }; NativeValue nativeValue{}; @@ -135,8 +135,8 @@ function generatePropsSetter(object: ClassObject, type: PropType, p: PropsDeclar let setterCode = ''; if (object.type == 'Element') { setterCode = `std::string key = "${p.name}"; - NativeString *args_01 = stringToNativeString(key); - NativeString *args_02 = jsValueToNativeString(ctx, argv[0]); + std::unique_ptr args_01 = stringToNativeString(key); + std::unique_ptr args_02 = jsValueToNativeString(ctx, argv[0]); foundation::UICommandBuffer::instance(${instanceName}->m_context->getContextId()) ->addCommand(${instanceName}->m_eventTargetId, UICommand::setProperty, *args_01, *args_02, nullptr); return JS_NULL;`; @@ -309,7 +309,7 @@ function generateEventConstructorCode(object: ClassObject) { } auto *nativeEvent = new Native${object.name}(); - nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue); + nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue).release(); ${generateEventInstanceConstructorCode(object)} @@ -333,14 +333,14 @@ function generateEventInstanceConstructorCode(object: ClassObject) { propApplyCode = `JS_ToInt32(m_ctx, reinterpret_cast(&nativeEvent->${p.name}), JS_GetProperty(m_ctx, eventInit, ${p.name}Atom));` } else if (p.kind === PropsDeclarationKind.string) { propApplyCode = addIndent(`JSValue v = JS_GetProperty(m_ctx, eventInit, ${p.name}Atom); - nativeEvent->${p.name} = jsValueToNativeString(m_ctx, v); + nativeEvent->${p.name} = jsValueToNativeString(m_ctx, v).release(); JS_FreeValue(m_ctx, v);`, 0); } else if (p.kind === PropsDeclarationKind.double) { propApplyCode = `JS_ToFloat64(m_ctx, &nativeEvent->${p.name}, JS_GetProperty(m_ctx, eventInit, ${p.name}Atom));`; } else if (p.kind === PropsDeclarationKind.object) { propApplyCode = addIndent(`JSValue v = JS_GetProperty(m_ctx, eventInit, ${p.name}Atom); JSValue json = JS_JSONStringify(m_ctx, v, JS_NULL, JS_NULL); - nativeEvent->${p.name} = jsValueToNativeString(m_ctx, json); + nativeEvent->${p.name} = jsValueToNativeString(m_ctx, json).release(); JS_FreeValue(m_ctx, json); JS_FreeValue(m_ctx, v);`, 0); } diff --git a/kraken/lib/src/bridge/from_native.dart b/kraken/lib/src/bridge/from_native.dart index 1e350abb59..6f23a4d843 100644 --- a/kraken/lib/src/bridge/from_native.dart +++ b/kraken/lib/src/bridge/from_native.dart @@ -25,7 +25,7 @@ import 'platform.dart'; class NativeString extends Struct { external Pointer string; - @Int32() + @Uint32() external int length; }