Skip to content

Commit

Permalink
feat: use garbageCollection to replace HostClass.
Browse files Browse the repository at this point in the history
  • Loading branch information
andycall committed Jan 6, 2022
1 parent fb232ed commit d6f00d7
Show file tree
Hide file tree
Showing 20 changed files with 549 additions and 406 deletions.
3 changes: 3 additions & 0 deletions bridge/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ if ($ENV{KRAKEN_JS_ENGINE} MATCHES "quickjs")
bindings/qjs/garbage_collected.h
bindings/qjs/executing_context.cc
bindings/qjs/executing_context.h
bindings/qjs/executing_context_data.cc
bindings/qjs/executing_context_data.h
bindings/qjs/wrapper_type_info.h
bindings/qjs/heap_hashmap.h
bindings/qjs/native_value.cc
bindings/qjs/native_value.h
Expand Down
23 changes: 22 additions & 1 deletion bridge/bindings/qjs/bom/location.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,37 @@

namespace kraken::binding::qjs {

void bindLocation(std::unique_ptr<ExecutionContext>& context) {
auto* contextData = context->contextData();
JSValue classObject = contextData->constructorForType(&locationTypeInfo);
JSValue prototypeObject = contextData->prototypeForType(&locationTypeInfo);

// Install methods
INSTALL_FUNCTION(Location, prototypeObject, reload, 0);

context->defineGlobalProperty("Location", classObject);
}

JSClassID Location::classId{0};

Location* Location::create(JSContext* ctx) {
return makeGarbageCollected<Location>()->initialize<Location>(ctx, &classId);
}

JSValue Location::reload(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* location = static_cast<Location*>(JS_GetOpaque(this_val, ExecutionContext::kHostObjectClassId));
if (getDartMethod()->reloadApp == nullptr) {
return JS_ThrowTypeError(ctx, "Failed to execute 'reload': dart method (reloadApp) is not registered.");
}

getDartMethod()->flushUICommand();
getDartMethod()->reloadApp(location->m_context->getContextId());
getDartMethod()->reloadApp(location->context()->getContextId());

return JS_NULL;
}

void Location::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const {}

void Location::dispose() const {}

} // namespace kraken::binding::qjs
33 changes: 26 additions & 7 deletions bridge/bindings/qjs/bom/location.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,38 @@
#define KRAKENBRIDGE_LOCATION_H

#include "bindings/qjs/executing_context.h"
#include "bindings/qjs/host_object.h"
#include "bindings/qjs/garbage_collected.h"
#include "bindings/qjs/wrapper_type_info.h"

namespace kraken::binding::qjs {

class Location : public HostObject {
public:
Location() = delete;
explicit Location(ExecutionContext* context) : HostObject(context, "Location") {}
void bindLocation(std::unique_ptr<ExecutionContext>& context);

class Location : public GarbageCollected<Location> {
public:
static JSClassID classId;
static Location* create(JSContext* ctx);
static JSValue reload(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);

private:
DEFINE_FUNCTION(reload, 0);
void trace(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) const override;
void dispose() const override;
};

auto locationCreator = [](JSContext* ctx, JSValueConst func_obj, JSValueConst this_val, int argc, JSValueConst* argv, int flags) -> JSValue {
auto* type = static_cast<const WrapperTypeInfo*>(JS_GetOpaque(func_obj, JSValueGetClassId(func_obj)));
auto* location = Location::create(ctx);
auto* context = static_cast<ExecutionContext*>(JS_GetContextOpaque(ctx));
JSValue prototype = context->contextData()->prototypeForType(type);

// Let eventTarget instance inherit EventTarget prototype methods.
JS_SetPrototype(ctx, location->toQuickJS(), prototype);
return location->toQuickJS();
};

const WrapperTypeInfo locationTypeInfo = {
"Location",
nullptr,
locationCreator
};

} // namespace kraken::binding::qjs
Expand Down
131 changes: 75 additions & 56 deletions bridge/bindings/qjs/bom/window.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,50 @@

namespace kraken::binding::qjs {

std::once_flag kWindowInitOnceFlag;

void bindWindow(std::unique_ptr<ExecutionContext>& context) {
auto* contextData = context->contextData();
JSValue classObject = contextData->constructorForType(&windowTypeInfo);
JSValue prototypeObject = contextData->prototypeForType(&windowTypeInfo);

// Install methods.
INSTALL_FUNCTION(Window, prototypeObject, open, 1);
installFunctionProperty(context.get(), prototypeObject, "scroll", Window::m_scrollTo_, 2);
INSTALL_FUNCTION(Window, prototypeObject, scrollTo, 2);
INSTALL_FUNCTION(Window, prototypeObject, scrollBy, 2);
INSTALL_FUNCTION(Window, prototypeObject, postMessage, 3);
INSTALL_FUNCTION(Window, prototypeObject, requestAnimationFrame, 1);
INSTALL_FUNCTION(Window, prototypeObject, cancelAnimationFrame, 1);

// Install Getter and Setter properties.
INSTALL_READONLY_PROPERTY(Window, prototypeObject, devicePixelRatio);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, colorScheme);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, __location__);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, location);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, window);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, parent);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, scrollX);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, scrollY);
INSTALL_READONLY_PROPERTY(Window, prototypeObject, self);

INSTALL_PROPERTY(Window, prototypeObject, onerror);

// Set globalThis and Window's prototype to EventTarget's prototype to support EventTarget methods in global.
auto* windowConstructor = new Window(context.get());
JS_SetPrototype(context->ctx(), context->global(), windowConstructor->prototype());
context->defineGlobalProperty("Window", windowConstructor->jsObject);
JS_SetPrototype(context->ctx(), context->global(), prototypeObject);
context->defineGlobalProperty("Window", classObject);

auto* window = new WindowInstance(windowConstructor);
// Hide window instance to global object, to get access to window when get property on globalObject.
auto* window = makeGarbageCollected<Window>()->initialize<Window>(context->ctx(), &Window::classId);
JS_SetOpaque(context->global(), window);
context->defineGlobalProperty("__window__", window->jsObject);
}

JSClassID Window::kWindowClassId{0};

Window::Window(ExecutionContext* context) : EventTarget(context, "Window") {
std::call_once(kWindowInitOnceFlag, []() { JS_NewClassID(&kWindowClassId); });
JS_SetPrototype(m_ctx, m_prototypeObject, EventTarget::instance(m_context)->prototype());
context->defineGlobalProperty("__window__", window->toQuickJS());
}

JSClassID Window::classId() {
return 1;
}

JSValue Window::open(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, Window::classId()));
IMPL_FUNCTION(Window, open)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto window = static_cast<Window*>(JS_GetOpaque(this_val, JSValueGetClassId(this_val)));
NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0])};
return window->callNativeMethods("open", 1, arguments);
}
JSValue Window::scrollTo(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {

IMPL_FUNCTION(Window, scrollTo)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
#if FLUTTER_BACKEND
auto window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, Window::classId()));
NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0]), jsValueToNativeValue(ctx, argv[1])};
Expand All @@ -50,23 +64,24 @@ JSValue Window::scrollTo(JSContext* ctx, JSValue this_val, int argc, JSValue* ar
return JS_UNDEFINED;
#endif
}
JSValue Window::scrollBy(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, Window::classId()));

IMPL_FUNCTION(Window, scrollBy)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto window = static_cast<Window*>(JS_GetOpaque(this_val, JSValueGetClassId(this_val)));
NativeValue arguments[] = {jsValueToNativeValue(ctx, argv[0]), jsValueToNativeValue(ctx, argv[1])};
return window->callNativeMethods("scrollBy", 2, arguments);
}

JSValue Window::postMessage(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
IMPL_FUNCTION(Window, postMessage)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
JSValue messageValue = argv[0];
JSValue originValue = argv[1];
JSValue globalObjectValue = JS_GetGlobalObject(ctx);
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(globalObjectValue, Window::classId()));
auto* window = static_cast<Window*>(JS_GetOpaque(globalObjectValue, JSValueGetClassId(this_val)));

JSValue messageEventInitValue = JS_NewObject(ctx);
JS_SetPropertyStr(ctx, messageEventInitValue, "data", JS_DupValue(ctx, messageValue));
JS_SetPropertyStr(ctx, originValue, "origin", JS_DupValue(ctx, originValue));

JSValue messageEventValue = JS_CallConstructor(ctx, MessageEvent::instance(window->m_context)->jsObject, 1, &messageEventInitValue);
JSValue messageEventValue = JS_CallConstructor(ctx, MessageEvent::instance(window->context())->jsObject, 1, &messageEventInitValue);
auto* event = static_cast<MessageEventInstance*>(JS_GetOpaque(messageEventValue, Event::kEventClassID));
window->dispatchEvent(event);

Expand All @@ -76,13 +91,13 @@ JSValue Window::postMessage(JSContext* ctx, JSValue this_val, int argc, JSValue*
return JS_NULL;
}

JSValue Window::requestAnimationFrame(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
IMPL_FUNCTION(Window, requestAnimationFrame)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
if (argc <= 0) {
return JS_ThrowTypeError(ctx, "Failed to execute 'requestAnimationFrame': 1 argument required, but only 0 present.");
}

auto* context = static_cast<ExecutionContext*>(JS_GetContextOpaque(ctx));
auto window = static_cast<WindowInstance*>(JS_GetOpaque(context->global(), Window::classId()));
auto window = static_cast<Window*>(JS_GetOpaque(context->global(), JSValueGetClassId(this_val)));

JSValue callbackValue = argv[0];

Expand All @@ -107,7 +122,7 @@ JSValue Window::requestAnimationFrame(JSContext* ctx, JSValue this_val, int argc
}
#endif

auto* frameCallback = makeGarbageCollected<FrameCallback>(JS_DupValue(ctx, callbackValue))->initialize(ctx, &FrameCallback::classId);
auto* frameCallback = makeGarbageCollected<FrameCallback>(JS_DupValue(ctx, callbackValue))->initialize<FrameCallback>(ctx, &FrameCallback::classId);

int32_t requestId = window->document()->requestAnimationFrame(frameCallback);

Expand All @@ -121,13 +136,13 @@ JSValue Window::requestAnimationFrame(JSContext* ctx, JSValue this_val, int argc
return JS_NewUint32(ctx, requestId);
}

JSValue Window::cancelAnimationFrame(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
IMPL_FUNCTION(Window, cancelAnimationFrame)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
if (argc <= 0) {
return JS_ThrowTypeError(ctx, "Failed to execute 'cancelAnimationFrame': 1 argument required, but only 0 present.");
}

auto context = static_cast<ExecutionContext*>(JS_GetContextOpaque(ctx));
auto window = static_cast<WindowInstance*>(JS_GetOpaque(context->global(), Window::classId()));
auto window = static_cast<Window*>(JS_GetOpaque(context->global(), JSValueGetClassId(this_val)));

JSValue requestIdValue = argv[0];
if (!JS_IsNumber(requestIdValue)) {
Expand All @@ -146,6 +161,27 @@ JSValue Window::cancelAnimationFrame(JSContext* ctx, JSValue this_val, int argc,
return JS_NULL;
}

Window* Window::create(JSContext* ctx) {
return makeGarbageCollected<Window>()->initialize<Window>(ctx, &classId, nullptr);
}

DocumentInstance* Window::document() {
return context()->document();
}

Window::Window() {
if (getDartMethod()->initWindow != nullptr) {
getDartMethod()->initWindow(context()->getContextId(), nativeEventTarget);
}

m_location = makeGarbageCollected<Location>()->initialize<Location>(m_ctx, &Location::classId);
}

void Window::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) const {
EventTarget::trace(rt, val, mark_func);
JS_MarkValue(rt, onerror, mark_func);
}

IMPL_PROPERTY_GETTER(Window, devicePixelRatio)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
if (getDartMethod()->devicePixelRatio == nullptr) {
return JS_ThrowTypeError(ctx, "Failed to read devicePixelRatio: dart method (devicePixelRatio) is not register.");
Expand All @@ -167,15 +203,15 @@ IMPL_PROPERTY_GETTER(Window, colorScheme)(JSContext* ctx, JSValue this_val, int
}

IMPL_PROPERTY_GETTER(Window, __location__)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, 1));
auto* window = static_cast<Window*>(JS_GetOpaque(this_val, JSValueGetClassId(this_val)));
if (window == nullptr)
return JS_UNDEFINED;
return JS_DupValue(ctx, window->m_location.value());
return JS_DupValue(ctx, window->m_location->toQuickJS());
}

IMPL_PROPERTY_GETTER(Window, location)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, 1));
return JS_GetPropertyStr(ctx, window->m_context->global(), "location");
auto* window = static_cast<Window*>(JS_GetOpaque(this_val, 1));
return JS_GetPropertyStr(ctx, window->context()->global(), "location");
}

IMPL_PROPERTY_GETTER(Window, window)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
Expand All @@ -187,21 +223,21 @@ IMPL_PROPERTY_GETTER(Window, parent)(JSContext* ctx, JSValue this_val, int argc,
}

IMPL_PROPERTY_GETTER(Window, scrollX)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, 1));
auto* window = static_cast<Window*>(JS_GetOpaque(this_val, 1));
return window->callNativeMethods("scrollX", 0, nullptr);
}

IMPL_PROPERTY_GETTER(Window, scrollY)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, 1));
auto* window = static_cast<Window*>(JS_GetOpaque(this_val, 1));
return window->callNativeMethods("scrollY", 0, nullptr);
}

IMPL_PROPERTY_GETTER(Window, onerror)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, 1));
auto* window = static_cast<Window*>(JS_GetOpaque(this_val, 1));
return JS_DupValue(ctx, window->onerror);
}
IMPL_PROPERTY_SETTER(Window, onerror)(JSContext* ctx, JSValue this_val, int argc, JSValue* argv) {
auto* window = static_cast<WindowInstance*>(JS_GetOpaque(this_val, 1));
auto* window = static_cast<Window*>(JS_GetOpaque(this_val, 1));
JSValue eventString = JS_NewString(ctx, "onerror");
JSString* p = JS_VALUE_GET_STRING(eventString);
JSValue onerrorHandler = argv[0];
Expand All @@ -220,21 +256,4 @@ IMPL_PROPERTY_GETTER(Window, self)(JSContext* ctx, JSValue this_val, int argc, J
return JS_GetGlobalObject(ctx);
}

WindowInstance::WindowInstance(Window* window) : EventTargetInstance(window, Window::kWindowClassId, "window", WINDOW_TARGET_ID) {
if (getDartMethod()->initWindow != nullptr) {
getDartMethod()->initWindow(context()->getContextId(), nativeEventTarget);
}
m_context->m_window = this;
}

void WindowInstance::trace(JSRuntime* rt, JSValue val, JS_MarkFunc* mark_func) {
EventTargetInstance::trace(rt, val, mark_func);

JS_MarkValue(rt, onerror, mark_func);
}

DocumentInstance* WindowInstance::document() {
return m_context->m_document;
}

} // namespace kraken::binding::qjs
Loading

0 comments on commit d6f00d7

Please sign in to comment.