about summary refs log tree commit diff stats
path: root/src/js/javascript.nim
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-08-23 22:01:30 +0200
committerbptato <nincsnevem662@gmail.com>2023-08-23 22:06:32 +0200
commitc7b208ed8f99cdeaf93f7141197b549b32f88165 (patch)
tree5f7d84e7583c9a4831bb0d66611f58d1bc18f72c /src/js/javascript.nim
parent33388f9c5fab20a910353da58214b72681f9a8ff (diff)
downloadchawan-c7b208ed8f99cdeaf93f7141197b549b32f88165.tar.gz
javascript: optimize unforgeable definition
Walking the prototype chain for every single type just so we can define
the few LegacyUnforgeable properties we need is not the greatest idea.

Instead, we now merge LegacyUnforgeable lists with those of ancestor
prototypes, which reduces the entire ceremony to a single `get'
from the global table.
Diffstat (limited to 'src/js/javascript.nim')
-rw-r--r--src/js/javascript.nim42
1 files changed, 26 insertions, 16 deletions
diff --git a/src/js/javascript.nim b/src/js/javascript.nim
index bcea8801..345054dd 100644
--- a/src/js/javascript.nim
+++ b/src/js/javascript.nim
@@ -98,7 +98,9 @@ type
     typemap: Table[pointer, JSClassID]
     ctors: Table[JSClassID, JSValue]
     parents: Table[JSClassID, JSClassID]
-    # Note: we assume no zero-list entry is added here.
+    # Parent unforgeables are merged on class creation.
+    # (i.e. to set all unforgeables on the prototype chain, it is enough to set)
+    # `unforgeable[classid]'.)
     unforgeable: Table[JSClassID, seq[JSCFunctionListEntry]]
     funmap: Table[pointer, pointer]
     gclaz: string
@@ -408,6 +410,22 @@ func getTypePtr(t: type): pointer =
   new(x)
   return getTypePtr(x)
 
+# Add all LegacyUnforgeable functions defined on the prototype chain to
+# the opaque.
+# Since every prototype has a list of all its ancestor's LegacyUnforgeable
+# functions, it is sufficient to simply merge the new list of new classes
+# with their parent's list to achieve this.
+proc addClassUnforgeable(ctx: JSContext, proto: JSValue,
+    classid, parent: JSClassID, ourUnforgeable: JSFunctionList) =
+  let ctxOpaque = ctx.getOpaque()
+  var merged = @ourUnforgeable
+  ctxOpaque.unforgeable.withValue(parent, uf):
+    merged.add(uf[])
+  if merged.len > 0:
+    let ufp = addr merged[0]
+    ctxOpaque.unforgeable[classid] = merged
+    JS_SetPropertyFunctionList(ctx, proto, ufp, cint(merged.len))
+
 func newJSClass*(ctx: JSContext, cdef: JSClassDefConst, tname: string,
     nimt: pointer, ctor: JSCFunction, funcs: JSFunctionList, parent: JSClassID,
     asglobal: bool, nointerface: bool, finalizer: proc(val: JSValue),
@@ -422,8 +440,6 @@ func newJSClass*(ctx: JSContext, cdef: JSClassDefConst, tname: string,
   ctxOpaque.typemap[nimt] = result
   ctxOpaque.creg[tname] = result
   ctxOpaque.parents[result] = parent
-  if unforgeable.len != 0:
-    ctxOpaque.unforgeable[result] = @unforgeable
   if finalizer != nil:
     rtOpaque.fins[result] = finalizer
   var proto: JSValue
@@ -447,6 +463,7 @@ func newJSClass*(ctx: JSContext, cdef: JSClassDefConst, tname: string,
   let news = JS_NewString(ctx, cdef.class_name)
   doAssert JS_SetProperty(ctx, proto, toStringTag, news) == 1
   JS_SetClassProto(ctx, result, proto)
+  ctx.addClassUnforgeable(proto, result, parent, unforgeable)
   if asglobal:
     let global = JS_GetGlobalObject(ctx)
     assert ctxOpaque.gclaz == ""
@@ -454,9 +471,9 @@ func newJSClass*(ctx: JSContext, cdef: JSClassDefConst, tname: string,
     if JS_SetPrototype(ctx, global, proto) != 1:
       raise newException(Defect, "Failed to set global prototype: " &
         $cdef.class_name)
-    if unforgeable.len != 0:
-      let p = addr ctxOpaque.unforgeable[result][0]
-      JS_SetPropertyFunctionList(ctx, global, p, cint(unforgeable.len))
+    # Global already exists, so set unforgeable functions here
+    ctxOpaque.unforgeable.withValue(result, uf):
+      JS_SetPropertyFunctionList(ctx, global, addr uf[][0], cint(uf[].len))
     JS_FreeValue(ctx, global)
   let jctor = ctx.newJSCFunction($cdef.class_name, ctor, 0, JS_CFUNC_constructor)
   JS_SetConstructor(ctx, jctor, proto)
@@ -537,16 +554,9 @@ proc defineUnforgeable*(ctx: JSContext, this: JSValue) =
   if unlikely(JS_IsException(this)):
     return
   let ctxOpaque = ctx.getOpaque()
-  var classid = JS_GetClassID(this)
-  while true:
-    ctxOpaque.unforgeable.withValue(classid, uf):
-      JS_SetPropertyFunctionList(ctx, this, addr uf[][0], cint(uf[].len))
-    ctxOpaque.parents.withValue(classid, val):
-      classid = val[]
-    do:
-      classid = 0 # not defined by Chawan; assume parent is Object.
-    if classid == 0:
-      break
+  let classid = JS_GetClassID(this)
+  ctxOpaque.unforgeable.withValue(classid, uf):
+    JS_SetPropertyFunctionList(ctx, this, addr uf[][0], cint(uf[].len))
 
 func fromJSString(ctx: JSContext, val: JSValue): Result[string, JSError] =
   var plen: csize_t