From 780a79b567f6d4819bad3ae758b41e9d2a213e57 Mon Sep 17 00:00:00 2001 From: Jake Wharton Date: Wed, 28 Aug 2024 14:58:21 -0400 Subject: [PATCH] Expose GC to guest code (#1410) --- zipline/build.gradle.kts | 1 + zipline/native/Context.cpp | 3 ++ zipline/native/common/global-gc.c | 29 +++++++++++++++++ zipline/native/common/global-gc.h | 31 +++++++++++++++++++ .../kotlin/app/cash/zipline/QuickJsTest.kt | 4 +++ .../kotlin/app/cash/zipline/QuickJs.kt | 3 ++ 6 files changed, 71 insertions(+) create mode 100644 zipline/native/common/global-gc.c create mode 100644 zipline/native/common/global-gc.h diff --git a/zipline/build.gradle.kts b/zipline/build.gradle.kts index 07b1b04b87..d42cf6ad28 100644 --- a/zipline/build.gradle.kts +++ b/zipline/build.gradle.kts @@ -137,6 +137,7 @@ kotlin { header(file("native/quickjs/quickjs.h")) header(file("native/common/context-no-eval.h")) header(file("native/common/finalization-registry.h")) + header(file("native/common/global-gc.h")) packageName("app.cash.zipline.quickjs") } } diff --git a/zipline/native/Context.cpp b/zipline/native/Context.cpp index d82a8a6c07..a61c25c4a3 100644 --- a/zipline/native/Context.cpp +++ b/zipline/native/Context.cpp @@ -22,6 +22,7 @@ #include "ExceptionThrowers.h" #include "common/context-no-eval.h" #include "common/finalization-registry.h" +#include "common/global-gc.h" #include "quickjs/quickjs.h" /** @@ -95,6 +96,8 @@ Context::Context(JNIEnv* env) JS_SetRuntimeOpaque(jsRuntime, this); JS_SetInterruptHandler(jsRuntime, &jsInterruptHandlerPoll, this); + JS_AddGlobalThisGc(jsContext); + if (installFinalizationRegistry(jsContext, jsContextForCompiling) < 0) { throwJavaException(env, "java/lang/IllegalStateException", "Failed to install FinalizationRegistry"); diff --git a/zipline/native/common/global-gc.c b/zipline/native/common/global-gc.c new file mode 100644 index 0000000000..a7034864dd --- /dev/null +++ b/zipline/native/common/global-gc.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Block, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "../quickjs/quickjs.h" +#include "global-gc.h" + +static JSValue js_global_gc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +void JS_AddGlobalThisGc(JSContext *jsContext) { + JSValue gc = JS_NewCFunction(jsContext, js_global_gc, "gc", 0); + JSValue globalThis = JS_GetGlobalObject(jsContext); + JS_SetPropertyStr(jsContext, globalThis, "gc", gc); + JS_FreeValue(jsContext, globalThis); +} diff --git a/zipline/native/common/global-gc.h b/zipline/native/common/global-gc.h new file mode 100644 index 0000000000..dd8090b13c --- /dev/null +++ b/zipline/native/common/global-gc.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 Block, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef QUICKJS_ANDROID_GLOBALGC_H +#define QUICKJS_ANDROID_GLOBALGC_H + +#include "../quickjs/quickjs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void JS_AddGlobalThisGc(JSContext *jsContext); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif //QUICKJS_ANDROID_GLOBALGC_H diff --git a/zipline/src/hostTest/kotlin/app/cash/zipline/QuickJsTest.kt b/zipline/src/hostTest/kotlin/app/cash/zipline/QuickJsTest.kt index dfef542889..7d3827294f 100644 --- a/zipline/src/hostTest/kotlin/app/cash/zipline/QuickJsTest.kt +++ b/zipline/src/hostTest/kotlin/app/cash/zipline/QuickJsTest.kt @@ -65,4 +65,8 @@ class QuickJsTest { quickJs.evaluate("""["test", true, false, 1, 1.123, undefined, null];""") as Array, ) } + + @Test fun gc() { + assertNull(quickJs.evaluate("""globalThis.gc();""")) + } } diff --git a/zipline/src/nativeMain/kotlin/app/cash/zipline/QuickJs.kt b/zipline/src/nativeMain/kotlin/app/cash/zipline/QuickJs.kt index a4eb9e3b77..84b93d39b0 100644 --- a/zipline/src/nativeMain/kotlin/app/cash/zipline/QuickJs.kt +++ b/zipline/src/nativeMain/kotlin/app/cash/zipline/QuickJs.kt @@ -26,6 +26,7 @@ import app.cash.zipline.quickjs.JSContext import app.cash.zipline.quickjs.JSMemoryUsage import app.cash.zipline.quickjs.JSRuntime import app.cash.zipline.quickjs.JSValue +import app.cash.zipline.quickjs.JS_AddGlobalThisGc import app.cash.zipline.quickjs.JS_ComputeMemoryUsage import app.cash.zipline.quickjs.JS_EVAL_FLAG_COMPILE_ONLY import app.cash.zipline.quickjs.JS_EVAL_FLAG_STRICT @@ -152,6 +153,8 @@ actual class QuickJs private constructor( init { JS_SetRuntimeOpaque(runtime, thisPtr.asCPointer()) JS_SetInterruptHandler(runtime, jsInterruptHandlerCFunction, thisPtr.asCPointer()) + + JS_AddGlobalThisGc(context) } private var closed = false