Skip to content

Commit

Permalink
Merge pull request #1182 from openkraken/fix/event_crash_on_32bit
Browse files Browse the repository at this point in the history
fix: fix native event memory align on 32 bit devices.
  • Loading branch information
answershuto committed Mar 2, 2022
2 parents f1ce4a5 + e5d554c commit 6e10c87
Show file tree
Hide file tree
Showing 17 changed files with 122 additions and 33 deletions.
5 changes: 5 additions & 0 deletions bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ list(APPEND BRIDGE_LINK_LIBS gumbo_parse_static)

if (${IS_ANDROID})
find_library(log-lib log)

if (${ANDROID_ABI} MATCHES "armeabi-v7a" OR ${ANDROID_ABI} MATCHES "x86")
add_definitions(-DANDROID_32_BIT=1)
endif()

add_definitions(-DIS_ANDROID=1)
list(APPEND BRIDGE_LINK_LIBS ${log-lib})
endif ()
Expand Down
4 changes: 1 addition & 3 deletions bridge/bindings/qjs/bom/window_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ window.postMessage({
EXPECT_EQ(logCalled, true);
}
// Use block scope to release previous page, and allocate new page.
{
TEST_init();
}
{ TEST_init(); }
}

TEST(Window, location) {
Expand Down
7 changes: 4 additions & 3 deletions bridge/bindings/qjs/dom/custom_event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ JSValue CustomEvent::initCustomEvent(JSContext* ctx, JSValue this_val, int argc,
}

JSValue typeValue = argv[0];
eventInstance->nativeEvent->type = jsValueToNativeString(ctx, typeValue).release();
eventInstance->setType(jsValueToNativeString(ctx, typeValue).release());

if (argc <= 2) {
bool canBubble = JS_ToBool(ctx, argv[1]);
Expand Down Expand Up @@ -83,8 +83,9 @@ CustomEventInstance::CustomEventInstance(CustomEvent* jsCustomEvent, JSAtom cust

CustomEventInstance::CustomEventInstance(CustomEvent* jsCustomEvent, NativeCustomEvent* nativeCustomEvent)
: nativeCustomEvent(nativeCustomEvent), EventInstance(jsCustomEvent, reinterpret_cast<NativeEvent*>(nativeCustomEvent)) {
JSValue newDetail = JS_NewUnicodeString(jsCustomEvent->context()->runtime(), jsCustomEvent->context()->ctx(), nativeCustomEvent->detail->string, nativeCustomEvent->detail->length);
nativeCustomEvent->detail->free();
auto* detail = reinterpret_cast<NativeString*>(nativeCustomEvent->detail);
JSValue newDetail = JS_NewUnicodeString(jsCustomEvent->context()->runtime(), jsCustomEvent->context()->ctx(), detail->string, detail->length);
detail->free();
m_detail.value(newDetail);
JS_FreeValue(m_ctx, newDetail);
}
Expand Down
2 changes: 1 addition & 1 deletion bridge/bindings/qjs/dom/custom_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void bindCustomEvent(ExecutionContext* context);

struct NativeCustomEvent {
NativeEvent nativeEvent;
NativeString* detail{nullptr};
int64_t detail{0};
};

class CustomEventInstance;
Expand Down
4 changes: 4 additions & 0 deletions bridge/bindings/qjs/dom/document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ JSValue Document::createEvent(JSContext* ctx, JSValue this_val, int argc, JSValu
std::string eventType = std::string(c_eventType);
if (eventType == "Event") {
std::unique_ptr<NativeString> nativeEventType = jsValueToNativeString(ctx, eventTypeValue);
#if ANDROID_32_BIT
auto nativeEvent = new NativeEvent{reinterpret_cast<int64_t>(nativeEventType.release())};
#else
auto nativeEvent = new NativeEvent{nativeEventType.release()};
#endif

auto document = static_cast<DocumentInstance*>(JS_GetOpaque(this_val, Document::classId()));
auto e = Event::buildEventInstance(eventType, document->context(), nativeEvent, false);
Expand Down
2 changes: 1 addition & 1 deletion bridge/bindings/qjs/dom/elements/image_element.cc
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ ImageElementInstance::ImageElementInstance(ImageElement* element) : ElementInsta
}

bool ImageElementInstance::dispatchEvent(EventInstance* event) {
std::u16string u16EventType = std::u16string(reinterpret_cast<const char16_t*>(event->nativeEvent->type->string), event->nativeEvent->type->length);
std::u16string u16EventType = std::u16string(reinterpret_cast<const char16_t*>(event->type()->string), event->type()->length);
std::string eventType = toUTF8(u16EventType);
bool result = EventTargetInstance::dispatchEvent(event);

Expand Down
50 changes: 39 additions & 11 deletions bridge/bindings/qjs/dom/event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ JSValue Event::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValue thi
JSValue eventTypeValue = argv[0];
std::string eventType = jsValueToStdString(ctx, eventTypeValue);

#if ANDROID_32_BIT
auto* nativeEvent = new NativeEvent{reinterpret_cast<int64_t>(stringToNativeString(eventType).release())};
#else
auto* nativeEvent = new NativeEvent{stringToNativeString(eventType).release()};
#endif
auto* event = Event::buildEventInstance(eventType, m_context, nativeEvent, false);
return event->jsObject;
}
Expand All @@ -42,7 +46,8 @@ std::unordered_map<std::string, EventCreator> Event::m_eventCreatorMap{};

IMPL_PROPERTY_GETTER(Event, type)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* eventInstance = static_cast<EventInstance*>(JS_GetOpaque(this_val, Event::kEventClassID));
return JS_NewUnicodeString(ExecutionContext::runtime(), eventInstance->context()->ctx(), eventInstance->nativeEvent->type->string, eventInstance->nativeEvent->type->length);
auto* pType = reinterpret_cast<NativeString*>(eventInstance->nativeEvent->type);
return JS_NewUnicodeString(ExecutionContext::runtime(), eventInstance->context()->ctx(), pType->string, pType->length);
}

IMPL_PROPERTY_GETTER(Event, bubbles)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
Expand All @@ -68,29 +73,26 @@ IMPL_PROPERTY_GETTER(Event, defaultPrevented)(JSContext* ctx, JSValue this_val,
IMPL_PROPERTY_GETTER(Event, target)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* eventInstance = static_cast<EventInstance*>(JS_GetOpaque(this_val, Event::kEventClassID));

if (eventInstance->nativeEvent->target != nullptr) {
auto instance = reinterpret_cast<EventTargetInstance*>(eventInstance->nativeEvent->target);
return JS_DupValue(ctx, ensureWindowIsGlobal(instance));
if (eventInstance->target() != nullptr) {
return JS_DupValue(ctx, ensureWindowIsGlobal(eventInstance->target()));
}
return JS_NULL;
}

IMPL_PROPERTY_GETTER(Event, srcElement)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* eventInstance = static_cast<EventInstance*>(JS_GetOpaque(this_val, Event::kEventClassID));

if (eventInstance->nativeEvent->target != nullptr) {
auto instance = reinterpret_cast<EventTargetInstance*>(eventInstance->nativeEvent->target);
return JS_DupValue(ctx, ensureWindowIsGlobal(instance));
if (eventInstance->target() != nullptr) {
return JS_DupValue(ctx, ensureWindowIsGlobal(eventInstance->target()));
}
return JS_NULL;
}

IMPL_PROPERTY_GETTER(Event, currentTarget)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* eventInstance = static_cast<EventInstance*>(JS_GetOpaque(this_val, Event::kEventClassID));

if (eventInstance->nativeEvent->currentTarget != nullptr) {
auto instance = reinterpret_cast<EventTargetInstance*>(eventInstance->nativeEvent->currentTarget);
return JS_DupValue(ctx, ensureWindowIsGlobal(instance));
if (eventInstance->currentTarget() != nullptr) {
return JS_DupValue(ctx, ensureWindowIsGlobal(eventInstance->currentTarget()));
}
return JS_NULL;
}
Expand Down Expand Up @@ -164,7 +166,7 @@ JSValue Event::initEvent(JSContext* ctx, JSValue this_val, int argc, JSValue* ar
}

auto* event = static_cast<EventInstance*>(JS_GetOpaque(this_val, Event::kEventClassID));
event->nativeEvent->type = jsValueToNativeString(ctx, typeValue).release();
event->setType(jsValueToNativeString(ctx, typeValue).release());

if (!JS_IsNull(bubblesValue)) {
event->nativeEvent->bubbles = JS_IsBool(bubblesValue) ? 1 : 0;
Expand All @@ -179,10 +181,36 @@ EventInstance* EventInstance::fromNativeEvent(Event* event, NativeEvent* nativeE
return new EventInstance(event, nativeEvent);
}

void EventInstance::setType(NativeString* type) const {
#if ANDROID_32_BIT
nativeEvent->type = reinterpret_cast<int64_t>(type);
#else
nativeEvent->type = type;
#endif
}
void EventInstance::setTarget(EventTargetInstance* target) const {
#if ANDROID_32_BIT
nativeEvent->target = reinterpret_cast<int64_t>(target);
#else
nativeEvent->target = target;
#endif
}
void EventInstance::setCurrentTarget(EventTargetInstance* currentTarget) const {
#if ANDROID_32_BIT
nativeEvent->currentTarget = reinterpret_cast<int64_t>(currentTarget);
#else
nativeEvent->currentTarget = currentTarget;
#endif
}

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);
#if ANDROID_32_BIT
nativeEvent = new NativeEvent{reinterpret_cast<int64_t>(jsValueToNativeString(m_ctx, v).release())};
#else
nativeEvent = new NativeEvent{jsValueToNativeString(m_ctx, v).release()};
#endif
JS_FreeValue(m_ctx, v);

auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
Expand Down
37 changes: 33 additions & 4 deletions bridge/bindings/qjs/dom/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ namespace kraken::binding::qjs {
void bindEvent(ExecutionContext* context);

class EventInstance;
class EventTargetInstance;

using EventCreator = EventInstance* (*)(ExecutionContext* context, void* nativeEvent);

Expand Down Expand Up @@ -96,6 +97,21 @@ class Event : public HostClass {
friend EventInstance;
};

// Dart generated nativeEvent member are force align to 64-bit system. So all members in NativeEvent should have 64 bit width.
#if ANDROID_32_BIT
struct NativeEvent {
int64_t type{0};
int64_t bubbles{0};
int64_t cancelable{0};
int64_t timeStamp{0};
int64_t defaultPrevented{0};
// The pointer address of target EventTargetInstance object.
int64_t target{0};
// The pointer address of current target EventTargetInstance object.
int64_t currentTarget{0};
};
#else
// Use pointer instead of int64_t on 64 bit system can help compiler to choose best register for better running performance.
struct NativeEvent {
NativeString* type{nullptr};
int64_t bubbles{0};
Expand All @@ -107,6 +123,7 @@ struct NativeEvent {
// The pointer address of current target EventTargetInstance object.
void* currentTarget{nullptr};
};
#endif

struct RawEvent {
uint64_t* bytes;
Expand All @@ -121,10 +138,22 @@ class EventInstance : public Instance {
static EventInstance* fromNativeEvent(Event* event, NativeEvent* nativeEvent);
NativeEvent* nativeEvent{nullptr};

inline const bool propagationStopped() { return m_propagationStopped; }
inline const bool cancelled() { return m_cancelled; }
inline void cancelled(bool v) { m_cancelled = v; }
inline const bool propagationImmediatelyStopped() { return m_propagationImmediatelyStopped; }
FORCE_INLINE const bool propagationStopped() { return m_propagationStopped; }
FORCE_INLINE const bool cancelled() { return m_cancelled; }
FORCE_INLINE void cancelled(bool v) { m_cancelled = v; }
FORCE_INLINE const bool propagationImmediatelyStopped() { return m_propagationImmediatelyStopped; }
FORCE_INLINE NativeString* type() {
#if ANDROID_32_BIT
return reinterpret_cast<NativeString*>(nativeEvent->type);
#else
return nativeEvent->type;
#endif
};
void setType(NativeString* type) const;
FORCE_INLINE EventTargetInstance* target() { return reinterpret_cast<EventTargetInstance*>(nativeEvent->target); }
void setTarget(EventTargetInstance* target) const;
FORCE_INLINE EventTargetInstance* currentTarget() { return reinterpret_cast<EventTargetInstance*>(nativeEvent->currentTarget); }
void setCurrentTarget(EventTargetInstance* target) const;

protected:
explicit EventInstance(Event* jsEvent, JSAtom eventType, JSValue eventInit);
Expand Down
14 changes: 10 additions & 4 deletions bridge/bindings/qjs/dom/event_target.cc
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,18 @@ JSValue EventTarget::dispatchEvent(JSContext* ctx, JSValue this_val, int argc, J

JSValue eventValue = argv[0];
auto eventInstance = reinterpret_cast<EventInstance*>(JS_GetOpaque(eventValue, EventTarget::classId(eventValue)));
#if ANDROID_32_BIT
eventInstance->nativeEvent->target = reinterpret_cast<int64_t>(eventTargetInstance);
#else
eventInstance->nativeEvent->target = eventTargetInstance;
#endif
return JS_NewBool(ctx, eventTargetInstance->dispatchEvent(eventInstance));
}

bool EventTargetInstance::dispatchEvent(EventInstance* event) {
std::u16string u16EventType = std::u16string(reinterpret_cast<const char16_t*>(event->nativeEvent->type->string), event->nativeEvent->type->length);
auto* pEventType = reinterpret_cast<NativeString*>(event->nativeEvent->type);

std::u16string u16EventType = std::u16string(reinterpret_cast<const char16_t*>(pEventType->string), pEventType->length);
std::string eventType = toUTF8(u16EventType);

// protect this util event trigger finished.
Expand Down Expand Up @@ -184,12 +190,12 @@ bool EventTargetInstance::dispatchEvent(EventInstance* event) {
}

bool EventTargetInstance::internalDispatchEvent(EventInstance* eventInstance) {
std::u16string u16EventType = std::u16string(reinterpret_cast<const char16_t*>(eventInstance->nativeEvent->type->string), eventInstance->nativeEvent->type->length);
std::u16string u16EventType = std::u16string(reinterpret_cast<const char16_t*>(eventInstance->type()->string), eventInstance->type()->length);
std::string eventTypeStr = toUTF8(u16EventType);
JSAtom eventType = JS_NewAtom(m_ctx, eventTypeStr.c_str());

// Modify the currentTarget to this.
eventInstance->nativeEvent->currentTarget = this;
eventInstance->setCurrentTarget(this);

// Dispatch event listeners writen by addEventListener
auto _dispatchEvent = [&eventInstance, this](JSValue handler) {
Expand Down Expand Up @@ -496,7 +502,7 @@ void NativeEventTarget::dispatchEventImpl(int32_t contextId, NativeEventTarget*
// So we can reinterpret_cast raw bytes pointer to NativeEvent type directly.
auto* nativeEvent = reinterpret_cast<NativeEvent*>(raw->bytes);
EventInstance* eventInstance = Event::buildEventInstance(eventType, context, nativeEvent, isCustomEvent == 1);
eventInstance->nativeEvent->target = eventTargetInstance;
eventInstance->setTarget(eventTargetInstance);
eventTargetInstance->dispatchEvent(eventInstance);
JS_FreeValue(context->ctx(), eventInstance->jsObject);
}
Expand Down
4 changes: 4 additions & 0 deletions bridge/bindings/qjs/dom/events/touch_event.cc
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ JSValue TouchEvent::instanceConstructor(JSContext* ctx, JSValue func_obj, JSValu
}

auto* nativeEvent = new NativeTouchEvent();
#if ANDROID_32_BIT
nativeEvent->nativeEvent.type = reinterpret_cast<int64_t>(jsValueToNativeString(ctx, eventTypeValue).release());
#else
nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue).release();
#endif

if (JS_IsObject(eventInit)) {
JSAtom touchesAtom = JS_NewAtom(m_ctx, "touches");
Expand Down
4 changes: 2 additions & 2 deletions bridge/bindings/qjs/executing_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ void ExecutionContext::dispatchGlobalErrorEvent(ExecutionContext* context, JSVal
}

auto* errorEvent = static_cast<EventInstance*>(JS_GetOpaque(errorEventValue, Event::kEventClassID));
errorEvent->nativeEvent->target = window;
errorEvent->setTarget(window);
context->dispatchErrorEvent(errorEvent);

JS_FreeValue(ctx, errorEventConstructor);
Expand Down Expand Up @@ -378,7 +378,7 @@ static void dispatchPromiseRejectionEvent(const char* eventType, ExecutionContex
}

auto* rejectEvent = static_cast<EventInstance*>(JS_GetOpaque(rejectEventValue, Event::kEventClassID));
rejectEvent->nativeEvent->target = window;
rejectEvent->setTarget(window);
window->dispatchEvent(rejectEvent);

JS_FreeValue(ctx, errorType);
Expand Down
4 changes: 4 additions & 0 deletions bridge/scripts/code_generator/src/genereate_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,11 @@ function generateEventConstructorCode(object: ClassObject) {
}
auto *nativeEvent = new Native${object.name}();
#if ANDROID_32_BIT
nativeEvent->nativeEvent.type = reinterpret_cast<int64_t>(jsValueToNativeString(ctx, eventTypeValue).release());
#else
nativeEvent->nativeEvent.type = jsValueToNativeString(ctx, eventTypeValue).release();
#endif
${generateEventInstanceConstructorCode(object)}
Expand Down
8 changes: 7 additions & 1 deletion bridge/test/kraken_test_env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ void TEST_initWindow(int32_t contextId, void* nativePtr) {}
void TEST_initDocument(int32_t contextId, void* nativePtr) {}

#if ENABLE_PROFILE
NativePerformanceEntryList* TEST_getPerformanceEntries(int32_t) {}
NativePerformanceEntryList* TEST_getPerformanceEntries(int32_t) {
return nullptr;
}
#endif

std::once_flag testInitOnceFlag;
Expand Down Expand Up @@ -258,7 +260,11 @@ void TEST_dispatchEvent(int32_t contextId, EventTargetInstance* eventTarget, con
auto nativeEventType = stringToNativeString(type);
NativeString* rawEventType = nativeEventType.release();

#if ANDROID_32_BIT
NativeEvent* nativeEvent = new NativeEvent{reinterpret_cast<int64_t>(rawEventType)};
#else
NativeEvent* nativeEvent = new NativeEvent{rawEventType};
#endif

RawEvent* rawEvent = new RawEvent{reinterpret_cast<uint64_t*>(nativeEvent)};

Expand Down
1 change: 1 addition & 0 deletions kraken/android/jniLibs/x86/libc++_shared.so
1 change: 1 addition & 0 deletions kraken/android/jniLibs/x86/libkraken.so
1 change: 1 addition & 0 deletions kraken/android/jniLibs/x86/libquickjs.so
7 changes: 4 additions & 3 deletions scripts/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ task('build-ios-frameworks', (done) => {
task('build-linux-kraken-lib', (done) => {
const buildType = buildMode == 'Release' ? 'Release' : 'Relwithdebinfo';
const cmakeGeneratorTemplate = platform == 'win32' ? 'Ninja' : 'Unix Makefiles';

const soBinaryDirectory = path.join(paths.bridge, `build/linux/lib/`);
const bridgeCmakeDir = path.join(paths.bridge, 'cmake-build-linux');
// generate project
Expand Down Expand Up @@ -630,10 +630,11 @@ task('build-android-kraken-lib', (done) => {
}
}

const archs = ['arm64-v8a', 'armeabi-v7a'];
const archs = ['arm64-v8a', 'armeabi-v7a', 'x86'];
const toolChainMap = {
'arm64-v8a': 'aarch64-linux-android',
'armeabi-v7a': 'arm-linux-androideabi'
'armeabi-v7a': 'arm-linux-androideabi',
'x86': 'i686-linux-android'
};
const buildType = (buildMode === 'Release' || buildMode == 'Relwithdebinfo') ? 'Relwithdebinfo' : 'Debug';
let externCmakeArgs = [];
Expand Down

0 comments on commit 6e10c87

Please sign in to comment.