about summary refs log tree commit diff stats
path: root/src/js
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-10-25 14:27:20 +0200
committerbptato <nincsnevem662@gmail.com>2023-10-25 14:48:31 +0200
commit34cb7ac286ed5e37c6ff3774387b6a3eee6ab046 (patch)
tree3712f5a22fcc0802a36d5cfe91aa6513ac9a633d /src/js
parent80ff0251133d20a11d2ab428ac6f27aa14c45782 (diff)
downloadchawan-34cb7ac286ed5e37c6ff3774387b6a3eee6ab046.tar.gz
js: refine isInstanceOf check in functions
Special case the global object, check for inheritance, etc.
Diffstat (limited to 'src/js')
-rw-r--r--src/js/fromjs.nim29
-rw-r--r--src/js/javascript.nim13
-rw-r--r--src/js/opaque.nim1
3 files changed, 30 insertions, 13 deletions
diff --git a/src/js/fromjs.nim b/src/js/fromjs.nim
index f5fc7d31..1d455090 100644
--- a/src/js/fromjs.nim
+++ b/src/js/fromjs.nim
@@ -14,7 +14,7 @@ import types/opt
 
 proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T]
 
-func isInstanceOf*(ctx: JSContext, val: JSValue, class: string): bool =
+func isInstanceOfNonGlobal(ctx: JSContext, val: JSValue, class: string): bool =
   let ctxOpaque = ctx.getOpaque()
   var classid = JS_GetClassID(val)
   let tclassid = ctxOpaque.creg[class]
@@ -31,6 +31,28 @@ func isInstanceOf*(ctx: JSContext, val: JSValue, class: string): bool =
       break
   return found
 
+func isInstanceOfGlobal(ctx: JSContext, val: JSValue, class: string): bool =
+  let ctxOpaque = ctx.getOpaque()
+  #TODO gparent only works for a single level. (But this is not really a
+  # problem right now, because our global objects have at most one inheritance
+  # level.)
+  if ctx.isGlobal(class) or ctxOpaque.creg[class] == ctxOpaque.gparent:
+    # undefined -> global
+    if JS_IsUndefined(val):
+      return true
+    if JS_IsObject(val):
+      let global = JS_GetGlobalObject(ctx)
+      let p0 = JS_VALUE_GET_PTR(global)
+      let p1 = JS_VALUE_GET_PTR(val)
+      JS_FreeValue(ctx, global)
+      if p0 == p1:
+        return true
+  return false
+
+func isInstanceOf*(ctx: JSContext, val: JSValue, class: string): bool =
+  return ctx.isInstanceOfGlobal(val, class) or
+    ctx.isInstanceOfNonGlobal(val, class)
+
 func toString(ctx: JSContext, val: JSValue): Opt[string] =
   var plen: csize_t
   let outp = JS_ToCStringLen(ctx, addr plen, val) # cstring
@@ -367,12 +389,11 @@ proc fromJSPObj0(ctx: JSContext, val: JSValue, t: string):
     return err(nil)
   if JS_IsNull(val):
     return ok(nil)
-  let ctxOpaque = ctx.getOpaque()
-  if ctxOpaque.gclaz == t:
+  if ctx.isInstanceOfGlobal(val, t):
     return ok(?getGlobalOpaque0(ctx, val))
   if not JS_IsObject(val):
     return err(newTypeError("Value is not an object"))
-  if not isInstanceOf(ctx, val, t):
+  if not isInstanceOfNonGlobal(ctx, val, t):
     let errmsg = t & " expected"
     JS_ThrowTypeError(ctx, cstring(errmsg))
     return err(newTypeError(errmsg))
diff --git a/src/js/javascript.nim b/src/js/javascript.nim
index fefc3ed8..3f6de0a2 100644
--- a/src/js/javascript.nim
+++ b/src/js/javascript.nim
@@ -255,6 +255,7 @@ func newJSClass*(ctx: JSContext, cdef: JSClassDefConst, tname: string,
     let global = JS_GetGlobalObject(ctx)
     assert ctxOpaque.gclaz == ""
     ctxOpaque.gclaz = tname
+    ctxOpaque.gparent = parent
     if JS_SetPrototype(ctx, global, proto) != 1:
       raise newException(Defect, "Failed to set global prototype: " &
         $cdef.class_name)
@@ -780,9 +781,7 @@ proc newJSProcBody(gen: var JSFuncGenerator, isva: bool): NimNode =
     let tn = ident(gen.thisname.get)
     let ev = gen.errval
     result.add(quote do:
-      if not (JS_IsUndefined(`tn`) or ctx.isGlobal(`tt`)) and
-          not isInstanceOf(ctx, `tn`, `tt`):
-        # undefined -> global.
+      if not ctx.isInstanceOf(`tn`, `tt`):
         discard JS_ThrowTypeError(ctx,
           "'%s' called on an object that is not an instance of %s", `fn`, `tt`)
         return `ev`
@@ -1315,9 +1314,7 @@ proc registerGetters(stmts: NimNode, info: RegistryInfo,
     let id = ident($GETTER & "_" & tname & "_" & fn)
     stmts.add(quote do:
       proc `id`(ctx: JSContext, this: JSValue): JSValue {.cdecl.} =
-        if not (JS_IsUndefined(this) or ctx.isGlobal(`tname`)) and
-            not ctx.isInstanceOf(this, `tname`):
-          # undefined -> global.
+        if not ctx.isInstanceOf(this, `tname`):
           return JS_ThrowTypeError(ctx,
             "'%s' called on an object that is not an instance of %s", `fn`,
             `jsname`)
@@ -1342,9 +1339,7 @@ proc registerSetters(stmts: NimNode, info: RegistryInfo,
     stmts.add(quote do:
       proc `id`(ctx: JSContext, this: JSValue, val: JSValue): JSValue
           {.cdecl.} =
-        if not (JS_IsUndefined(this) or ctx.isGlobal(`tname`)) and
-            not ctx.isInstanceOf(this, `tname`):
-          # undefined -> global.
+        if not ctx.isInstanceOf(this, `tname`):
           return JS_ThrowTypeError(ctx,
             "'%s' called on an object that is not an instance of %s", `fn`,
             `jsname`)
diff --git a/src/js/opaque.nim b/src/js/opaque.nim
index 30456602..1f0ad529 100644
--- a/src/js/opaque.nim
+++ b/src/js/opaque.nim
@@ -25,6 +25,7 @@ type
     # `unforgeable[classid]'.)
     unforgeable*: Table[JSClassID, seq[JSCFunctionListEntry]]
     gclaz*: string
+    gparent*: JSClassID
     sym_refs*: array[JSSymbolRefs, JSAtom]
     str_refs*: array[JSStrRefs, JSAtom]
     Array_prototype_values*: JSValue