Skip to content

Commit 3126c44

Browse files
committed
primjs: port host objects support
1 parent afc7197 commit 3126c44

File tree

3 files changed

+217
-2
lines changed

3 files changed

+217
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ dist_v8
1818
dist_quickjs
1919
dist_hermes
2020
dist_jsc
21+
dist_*

test-app/runtime/src/main/cpp/napi/primjs/primjs-api.cc

Lines changed: 200 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "napi_env_quickjs.h"
2727
#include "quickjs/include/quickjs-inner.h"
2828

29+
30+
std::unordered_map<LEPUSRuntime *, napi_env> napi_context__::rt_to_env_cache;
2931
struct napi_callback_info__ {
3032
napi_value newTarget;
3133
napi_value thisArg;
@@ -2622,7 +2624,6 @@ napi_status napi_get_instance_data(napi_env env, uint64_t key, void **data) {
26222624
void napi_attach_quickjs(napi_env env, LEPUSContext *context) {
26232625
env->ctx = new napi_context__(env, context);
26242626

2625-
26262627
InitNapiScope(context);
26272628
}
26282629

@@ -2678,5 +2679,203 @@ napi_status primjs_execute_pending_jobs(napi_env env) {
26782679
}
26792680
} while (error != 0);
26802681

2682+
return napi_clear_last_error(env);
2683+
}
2684+
2685+
2686+
typedef struct NapiHostObjectInfo {
2687+
void *data;
2688+
napi_ref ref;
2689+
napi_finalize finalize_cb;
2690+
bool is_array;
2691+
napi_ref getter;
2692+
napi_ref setter;
2693+
} NapiHostObjectInfo;
2694+
2695+
void host_object_finalizer(LEPUSRuntime *rt, LEPUSValue value) {
2696+
napi_env env = (napi_env) napi_context__::GetEnv(rt);
2697+
NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(value,
2698+
env->ctx->napiHostObjectClassId);
2699+
if (info->finalize_cb) {
2700+
info->finalize_cb(env, info->data, NULL);
2701+
}
2702+
if (info->is_array) {
2703+
napi_delete_reference(env, info->getter);
2704+
napi_delete_reference(env, info->setter);
2705+
}
2706+
2707+
napi_delete_reference(env, info->ref);
2708+
delete info;
2709+
}
2710+
2711+
int host_object_set(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom,
2712+
LEPUSValue value, LEPUSValue receiver, int flags) {
2713+
napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx));
2714+
NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj,
2715+
env->ctx->napiHostObjectClassId);
2716+
if (info != NULL) {
2717+
auto *target = reinterpret_cast<qjsimpl::Reference *>(info->ref);
2718+
if (info->is_array) {
2719+
LEPUSValue atom_val = LEPUS_AtomToValue(ctx, atom);
2720+
2721+
auto *setter = reinterpret_cast<qjsimpl::Reference *>(info->setter);
2722+
2723+
LEPUSValue argv[4] = {
2724+
ToJSValue(target->Get()),
2725+
atom_val,
2726+
value,
2727+
obj
2728+
};
2729+
2730+
LEPUSValue result = LEPUS_Call(ctx, ToJSValue(setter->Get()), LEPUS_UNDEFINED, 4, argv);
2731+
2732+
JS_FreeValue_Comp(ctx, atom_val);
2733+
2734+
if (LEPUS_IsException(result)) return -1;
2735+
2736+
return true;
2737+
}
2738+
return LEPUS_SetProperty(ctx, ToJSValue(target->Get()), atom, JS_DupValue_Comp(ctx, value));
2739+
}
2740+
return true;
2741+
}
2742+
2743+
LEPUSValue host_object_get(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom, LEPUSValue receiver) {
2744+
napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx));
2745+
NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj,
2746+
env->ctx->napiHostObjectClassId);
2747+
if (info != NULL) {
2748+
auto *target = reinterpret_cast<qjsimpl::Reference *>(info->ref);
2749+
if (info->is_array) {
2750+
LEPUSValue atom_val = LEPUS_AtomToValue(ctx, atom);
2751+
auto *getter = reinterpret_cast<qjsimpl::Reference *>(info->getter);
2752+
LEPUSValue argv[3] = {
2753+
ToJSValue(target->Get()),
2754+
atom_val,
2755+
obj
2756+
};
2757+
LEPUSValue value = LEPUS_Call(ctx, ToJSValue(getter->Get()), LEPUS_UNDEFINED, 3, argv);
2758+
JS_FreeValue_Comp(ctx, atom_val);
2759+
return value;
2760+
}
2761+
return LEPUS_GetProperty(ctx, ToJSValue(target->Get()), atom);
2762+
}
2763+
return LEPUS_UNDEFINED;
2764+
}
2765+
2766+
int host_object_has(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom) {
2767+
napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx));
2768+
NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj,
2769+
env->ctx->napiHostObjectClassId);
2770+
if (info != NULL) {
2771+
auto *target = reinterpret_cast<qjsimpl::Reference *>(info->ref);
2772+
return LEPUS_HasProperty(ctx, ToJSValue(target->Get()), atom);
2773+
}
2774+
return false;
2775+
}
2776+
2777+
static int host_object_delete(LEPUSContext *ctx, LEPUSValue obj, JSAtom atom) {
2778+
napi_env env = (napi_env) napi_context__::GetEnv(LEPUS_GetRuntime(ctx));
2779+
NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(obj,
2780+
env->ctx->napiHostObjectClassId);
2781+
if (info != NULL) {
2782+
auto *target = reinterpret_cast<qjsimpl::Reference *>(info->ref);
2783+
return LEPUS_DeleteProperty(ctx, ToJSValue(target->Get()), atom, 0);
2784+
}
2785+
return true;
2786+
}
2787+
2788+
LEPUSClassExoticMethods NapiHostObjectExoticMethods = {
2789+
.get_own_property = nullptr,
2790+
.get_own_property_names = nullptr,
2791+
.delete_property = host_object_delete,
2792+
.define_own_property = nullptr,
2793+
.has_property = host_object_has,
2794+
.get_property = host_object_get,
2795+
.set_property = host_object_set,
2796+
};
2797+
2798+
napi_status
2799+
napi_create_host_object(napi_env env, napi_value value, napi_finalize finalize, void *data,
2800+
bool is_array, napi_value getter, napi_value setter, napi_value *result) {
2801+
CHECK_ARG(env, result);
2802+
2803+
if (env->ctx->napi_host_object_class_init == 0) {
2804+
LEPUSClassDef NapiHostObjectClassDef = {"NapiHostObject", host_object_finalizer, NULL, NULL,
2805+
&NapiHostObjectExoticMethods};
2806+
LEPUS_NewClass(env->ctx->rt, env->ctx->napiHostObjectClassId, &NapiHostObjectClassDef);
2807+
env->ctx->napi_host_object_class_init = 1;
2808+
}
2809+
2810+
napi_value constructor;
2811+
napi_get_named_property(env, value, "constructor", &constructor);
2812+
2813+
napi_value prototype;
2814+
napi_get_named_property(env, constructor, "prototype", &prototype);
2815+
2816+
LEPUSValue jsValue = LEPUS_NewObjectClass(env->ctx->ctx, env->ctx->napiHostObjectClassId);
2817+
LEPUS_SetPrototype(env->ctx->ctx, jsValue, ToJSValue(prototype));
2818+
2819+
NapiHostObjectInfo *info = new NapiHostObjectInfo;
2820+
info->data = data;
2821+
if (finalize) {
2822+
info->finalize_cb = finalize;
2823+
} else {
2824+
info->finalize_cb = NULL;
2825+
}
2826+
info->is_array = is_array;
2827+
2828+
if (is_array) {
2829+
if (getter) napi_create_reference(env, getter, 1, &info->getter);
2830+
if (setter) napi_create_reference(env, setter, 1, &info->setter);
2831+
}
2832+
2833+
napi_create_reference(env, value, 1, &info->ref);
2834+
2835+
LEPUS_SetOpaque(jsValue, info);
2836+
2837+
*result = env->ctx->CreateHandle(jsValue);
2838+
return napi_ok;
2839+
}
2840+
2841+
napi_status napi_get_host_object_data(napi_env env, napi_value object, void **data) {
2842+
CHECK_ARG(env, object);
2843+
CHECK_ARG(env, data);
2844+
2845+
LEPUSValue jsValue = ToJSValue(object);
2846+
2847+
2848+
if (!LEPUS_IsObject(jsValue)) {
2849+
return napi_set_last_error(env, napi_object_expected);
2850+
}
2851+
2852+
NapiHostObjectInfo *info = (NapiHostObjectInfo *) LEPUS_GetOpaque(jsValue,
2853+
env->ctx->napiHostObjectClassId);
2854+
if (info) {
2855+
*data = info->data;
2856+
} else {
2857+
*data = NULL;
2858+
}
2859+
2860+
return napi_clear_last_error(env);
2861+
}
2862+
2863+
napi_status napi_is_host_object(napi_env env, napi_value object, bool *result) {
2864+
CHECK_ARG(env, object);
2865+
2866+
LEPUSValue jsValue = ToJSValue(object);
2867+
2868+
if (!LEPUS_IsObject(jsValue)) {
2869+
return napi_set_last_error(env, napi_object_expected);
2870+
}
2871+
2872+
void *data = LEPUS_GetOpaque(jsValue,
2873+
env->ctx->napiHostObjectClassId);
2874+
if (data != NULL) {
2875+
*result = true;
2876+
} else {
2877+
*result = false;
2878+
}
2879+
26812880
return napi_clear_last_error(env);
26822881
}

test-app/runtime/src/main/cpp/napi/primjs/primjs-api.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ namespace qjsimpl {
335335

336336
inline void reset_napi_env(napi_env env, napi_handle_scope__ *scope);
337337

338+
339+
338340
struct napi_context__ {
339341
napi_env env;
340342
LEPUSRuntime *rt{};
@@ -360,6 +362,7 @@ struct napi_context__ {
360362
PROP_CTOR_MAGIC(env, ctx, "@#ctor@#"),
361363
gc_enable(LEPUS_IsGCModeRT(rt)) {
362364
env->ctx = this;
365+
napi_context__::rt_to_env_cache.emplace(rt, env);
363366
handle_scope = new napi_handle_scope__(env, ctx, reset_napi_env);
364367

365368
LEPUSValue gc = LEPUS_NewCFunction(ctx, [](LEPUSContext *ctx, LEPUSValueConst this_val,
@@ -370,17 +373,19 @@ struct napi_context__ {
370373
auto globalValue = LEPUS_GetGlobalObject(ctx);
371374
LEPUS_SetPropertyStr(ctx, globalValue, "gc", gc);
372375
JS_FreeValue_Comp(ctx, globalValue);
376+
LEPUS_NewClassID(&napiHostObjectClassId);
373377

374378
// TODO primjs doesn't expose DupContext
375379
}
376380

377381
~napi_context__() {
378382
qjsimpl::RefTracker::FinalizeAll(&finalizing_reflist);
379383
qjsimpl::RefTracker::FinalizeAll(&reflist);
380-
384+
381385
// root handle scope may be used during FinalizeAll
382386
// must delete at last
383387
delete handle_scope;
388+
rt_to_env_cache.erase(rt);
384389
}
385390

386391
inline void Ref() { refs++; }
@@ -415,6 +420,16 @@ struct napi_context__ {
415420

416421
std::unordered_map<uint64_t, void *> instance_data_registry;
417422

423+
int napi_host_object_class_init = 0;
424+
LEPUSClassID napiHostObjectClassId = 0;
425+
static std::unordered_map<LEPUSRuntime *, napi_env> rt_to_env_cache;
426+
427+
static napi_env GetEnv(LEPUSRuntime* rt) {
428+
auto it = rt_to_env_cache.find(rt);
429+
if (it == rt_to_env_cache.end()) return nullptr;
430+
return it->second;
431+
}
432+
418433
int open_handle_scopes = 0;
419434

420435
const qjsimpl::Atom PROP_NAME;

0 commit comments

Comments
 (0)