about summary refs log tree commit diff stats
path: root/src/js
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-11-30 11:50:58 +0100
committerbptato <nincsnevem662@gmail.com>2023-11-30 11:50:58 +0100
commit06326cdd0ef3381a5d4fe621c10b2754225bb0c8 (patch)
treee0d20eba673bda32f0237b6d71d24f4f9b3d39e3 /src/js
parent12aa7f1a48fe83a690e25ada0f0f2a390b12e981 (diff)
downloadchawan-06326cdd0ef3381a5d4fe621c10b2754225bb0c8.tar.gz
js: allow subclassing platform objects in JS
Diffstat (limited to 'src/js')
-rw-r--r--src/js/javascript.nim2
-rw-r--r--src/js/opaque.nim1
-rw-r--r--src/js/tojs.nim52
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