diff options
author | bptato <nincsnevem662@gmail.com> | 2023-11-30 11:50:58 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-11-30 11:50:58 +0100 |
commit | 06326cdd0ef3381a5d4fe621c10b2754225bb0c8 (patch) | |
tree | e0d20eba673bda32f0237b6d71d24f4f9b3d39e3 /src/js | |
parent | 12aa7f1a48fe83a690e25ada0f0f2a390b12e981 (diff) | |
download | chawan-06326cdd0ef3381a5d4fe621c10b2754225bb0c8.tar.gz |
js: allow subclassing platform objects in JS
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/javascript.nim | 2 | ||||
-rw-r--r-- | src/js/opaque.nim | 1 | ||||
-rw-r--r-- | src/js/tojs.nim | 52 |
3 files changed, 50 insertions, 5 deletions
diff --git a/src/js/javascript.nim b/src/js/javascript.nim index 8c05c40c..e3406048 100644 --- a/src/js/javascript.nim +++ b/src/js/javascript.nim @@ -890,7 +890,7 @@ proc makeCtorJSCallAndRet(gen: var JSFuncGenerator, errstmt: NimNode) = let dl = gen.dielabel gen.jsCallAndRet = quote do: block `dl`: - let val = ctx.toJS(`jfcl`) + let val = ctx.toJSNew(`jfcl`, this) defineUnforgeable(ctx, val) return val `errstmt` diff --git a/src/js/opaque.nim b/src/js/opaque.nim index 1f0ad529..ae79481b 100644 --- a/src/js/opaque.nim +++ b/src/js/opaque.nim @@ -14,6 +14,7 @@ type DONE = "done" VALUE = "value" NEXT = "next" + PROTOTYPE = "prototype" JSContextOpaque* = ref object creg*: Table[string, JSClassID] diff --git a/src/js/tojs.nim b/src/js/tojs.nim index 5ad495af..581534a0 100644 --- a/src/js/tojs.nim +++ b/src/js/tojs.nim @@ -51,6 +51,11 @@ proc toJS*(ctx: JSContext, dict: JSDict): JSValue proc toJSP*(ctx: JSContext, parent: ref object, child: var object): JSValue proc toJSP*(ctx: JSContext, parent: ptr object, child: var object): JSValue +# Same as toJSP, but used in constructors. ctor contains the target prototype, +# used for subclassing from JS. +proc toJSNew*(ctx: JSContext, obj: ref object, ctor: JSValue): JSValue +proc toJSNew*[T, E](ctx: JSContext, opt: Result[T, E], ctor: JSValue): JSValue + # Avoid accidentally calling toJSP on objects that we have explicit toJS # converters for. template makeToJSP(typ: untyped) = @@ -167,13 +172,27 @@ proc defineUnforgeable*(ctx: JSContext, this: JSValue) = ctxOpaque.unforgeable.withValue(classid, uf): JS_SetPropertyFunctionList(ctx, this, addr uf[][0], cint(uf[].len)) -proc toJSP0(ctx: JSContext, p, tp: pointer, needsref: var bool): JSValue = +proc toJSP0(ctx: JSContext, p, tp: pointer, ctor: JSValue, + needsref: var bool): JSValue = JS_GetRuntime(ctx).getOpaque().plist.withValue(p, obj): # a JSValue already points to this object. return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, obj[])) let ctxOpaque = ctx.getOpaque() let clazz = ctxOpaque.typemap[tp] - let jsObj = JS_NewObjectClass(ctx, clazz) + let jsObj = if JS_IsUndefined(ctor): + JS_NewObjectClass(ctx, clazz) + else: + let proto = JS_GetProperty(ctx, ctor, ctxOpaque.str_refs[PROTOTYPE]) + if JS_IsException(proto): + return proto + if not JS_IsObject(proto): + JS_FreeValue(ctx, proto) + #TODO switch ctx to ctor realm + JS_NewObjectClass(ctx, clazz) + else: + let x = JS_NewObjectProtoClass(ctx, proto, clazz) + JS_FreeValue(ctx, proto) + x setOpaque(ctx, jsObj, p) # We are "constructing" a new JS object, so we must add unforgeable # properties here. @@ -189,7 +208,7 @@ proc toJSRefObj(ctx: JSContext, obj: ref object): JSValue = let p = cast[pointer](obj) let tp = getTypePtr(obj) var needsref = false - let val = toJSP0(ctx, p, tp, needsref) + let val = toJSP0(ctx, p, tp, JS_UNDEFINED, needsref) if needsref: GC_ref(obj) return val @@ -197,6 +216,31 @@ proc toJSRefObj(ctx: JSContext, obj: ref object): JSValue = proc toJS*(ctx: JSContext, obj: ref object): JSValue = return toJSRefObj(ctx, obj) +proc toJSNew*(ctx: JSContext, obj: ref object, ctor: JSValue): JSValue = + if obj == nil: + return JS_NULL + let p = cast[pointer](obj) + let tp = getTypePtr(obj) + var needsref = false + let val = toJSP0(ctx, p, tp, ctor, needsref) + if needsref: + GC_ref(obj) + return val + +proc toJSNew[T, E](ctx: JSContext, opt: Result[T, E], ctor: JSValue): JSValue = + if opt.isSome: + when not (T is void): + return toJSNew(ctx, opt.get, ctor) + else: + return JS_UNDEFINED + else: + when not (E is void): + let res = toJS(ctx, opt.error) + if not JS_IsNull(res): + return JS_Throw(ctx, res) + else: + return JS_NULL + proc toJS(ctx: JSContext, e: enum): JSValue = return toJS(ctx, $e) @@ -302,7 +346,7 @@ proc toJSP(ctx: JSContext, parent: ref object, child: var object): JSValue = ) let tp = getTypePtr(child) var needsref = false - let val = toJSP0(ctx, p, tp, needsref) + let val = toJSP0(ctx, p, tp, JS_UNDEFINED, needsref) if needsref: GC_ref(parent) return val |