about summary refs log tree commit diff stats
path: root/src/buffer
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2022-12-31 02:14:34 +0100
committerbptato <nincsnevem662@gmail.com>2022-12-31 02:14:34 +0100
commit80b45c0df3f5bbbedb9abfb02fdef608113958e1 (patch)
tree475cd89df09b4147b8ee6ce9a1ec28954eb6dc30 /src/buffer
parent310254b09ee2dd3045648848ca9f60f9f1f7c769 (diff)
downloadchawan-80b45c0df3f5bbbedb9abfb02fdef608113958e1.tar.gz
Add promise support to JS
Diffstat (limited to 'src/buffer')
-rw-r--r--src/buffer/buffer.nim119
-rw-r--r--src/buffer/container.nim14
2 files changed, 47 insertions, 86 deletions
diff --git a/src/buffer/buffer.nim b/src/buffer/buffer.nim
index 17b6e4b8..b4a1c202 100644
--- a/src/buffer/buffer.nim
+++ b/src/buffer/buffer.nim
@@ -26,6 +26,7 @@ import html/tags
 import io/loader
 import io/request
 import io/posixstream
+import io/promise
 import io/teestream
 import ips/serialize
 import ips/serversocket
@@ -96,78 +97,36 @@ type
     savetask: bool
     hovertext: array[HoverType, string]
 
-  # async, but worse
-  EmptyPromise = ref object of RootObj
-    cb: (proc())
-    next: EmptyPromise
+  InterfaceOpaque = ref object
     stream: Stream
-
-  Promise*[T] = ref object of EmptyPromise
-    res: T
+    len: int
 
   BufferInterface* = ref object
-    stream*: Stream
+    map: PromiseMap
     packetid: int
-    promises: Table[int, EmptyPromise]
+    opaque: InterfaceOpaque
+    stream*: Stream
 
-proc newBufferInterface*(ostream: Stream): BufferInterface =
+proc getFromOpaque[T](opaque: pointer, res: var T) =
+  let opaque = cast[InterfaceOpaque](opaque)
+  if opaque.len != 0:
+    opaque.stream.sread(res)
+
+proc newBufferInterface*(stream: Stream): BufferInterface =
+  let opaque = InterfaceOpaque(stream: stream)
   result = BufferInterface(
-    stream: ostream,
-    packetid: 1 # ids below 1 are invalid
+    map: newPromiseMap(cast[pointer](opaque)),
+    packetid: 1, # ids below 1 are invalid
+    opaque: opaque,
+    stream: stream
   )
 
-proc fulfill*(iface: BufferInterface, packetid, len: int) =
-  var promise: EmptyPromise
-  if iface.promises.pop(packetid, promise):
-    if promise.stream != nil and promise.cb == nil and len != 0:
-      var abc = alloc(len)
-      var x = 0
-      while x < len:
-        x += promise.stream.readData(abc, len)
-      dealloc(abc)
-    while promise != nil:
-      if promise.cb != nil:
-        promise.cb()
-      promise = promise.next
+proc resolve*(iface: BufferInterface, packetid, len: int) =
+  iface.opaque.len = len
+  iface.map.resolve(packetid)
 
 proc hasPromises*(iface: BufferInterface): bool =
-  return iface.promises.len > 0
-
-proc then*(promise: EmptyPromise, cb: (proc())): EmptyPromise {.discardable.} =
-  if promise == nil: return
-  promise.cb = cb
-  promise.next = EmptyPromise()
-  return promise.next
-
-proc then*[T](promise: Promise[T], cb: (proc(x: T))): EmptyPromise {.discardable.} =
-  if promise == nil: return
-  return promise.then(proc() =
-    if promise.stream != nil:
-      promise.stream.sread(promise.res)
-    cb(promise.res))
-
-# Warning: we assume these aren't discarded.
-proc then*[T](promise: EmptyPromise, cb: (proc(): Promise[T])): Promise[T] =
-  if promise == nil: return
-  let next = Promise[T]()
-  promise.then(proc() =
-    let p2 = cb()
-    if p2 != nil:
-      p2.then(proc(x: T) =
-        next.res = x
-        next.cb()))
-  return next
-
-proc then*[T, U](promise: Promise[T], cb: (proc(x: T): Promise[U])): Promise[U] =
-  if promise == nil: return
-  let next = Promise[U]()
-  promise.then(proc(x: T) =
-    let p2 = cb(x)
-    if p2 != nil:
-      p2.then(proc(y: U) =
-        next.res = y
-        next.cb()))
-  return next
+  return not iface.map.empty()
 
 # get enum identifier of proxy function
 func getFunId(fun: NimNode): string =
@@ -190,9 +149,14 @@ proc buildInterfaceProc(fun: NimNode, funid: string): tuple[fun, name: NimNode]
     `thisval`.stream.swrite(`thisval`.packetid))
   var params2: seq[NimNode]
   var retval2: NimNode
+  var addfun: NimNode
   if retval.kind == nnkEmpty:
+    addfun = quote do:
+      `thisval`.map.addEmptyPromise(`thisval`.packetid)
     retval2 = ident("EmptyPromise")
   else:
+    addfun = quote do:
+      addPromise[`retval`](`thisval`.map, `thisval`.packetid, getFromOpaque[`retval`])
     retval2 = newNimNode(nnkBracketExpr).add(
       ident("Promise"),
       retval)
@@ -210,16 +174,13 @@ proc buildInterfaceProc(fun: NimNode, funid: string): tuple[fun, name: NimNode]
   body.add(quote do:
     `thisval`.stream.flush())
   body.add(quote do:
-    `thisval`.promises[`thisval`.packetid] = `retval2`(stream: `thisval`.stream)
-    inc `thisval`.packetid)
+    let promise = `addfun`
+    inc `thisval`.packetid
+    return promise)
   var pragmas: NimNode
   if retval.kind == nnkEmpty:
-    body.add(quote do:
-      return `thisval`.promises[`thisval`.packetid - 1])
     pragmas = newNimNode(nnkPragma).add(ident("discardable"))
   else:
-    body.add(quote do:
-      return `retval2`(`thisval`.promises[`thisval`.packetid - 1]))
     pragmas = newEmptyNode()
   return (newProc(name, params2, body, pragmas = pragmas), nup)
 
@@ -659,10 +620,10 @@ proc load*(buffer: Buffer): LoadResult {.proxy, task.} =
   else:
     buffer.savetask = true
 
-proc fulfillTask[T](buffer: Buffer, cmd: BufferCommand, res: T) =
+proc resolveTask[T](buffer: Buffer, cmd: BufferCommand, res: T) =
   let packetid = buffer.tasks[cmd]
   if packetid == 0:
-    return # no task to fulfill (TODO this is kind of inefficient)
+    return # no task to resolve (TODO this is kind of inefficient)
   let len = slen(buffer.tasks[cmd]) + slen(res)
   buffer.pstream.swrite(len)
   buffer.pstream.swrite(packetid)
@@ -673,7 +634,7 @@ proc fulfillTask[T](buffer: Buffer, cmd: BufferCommand, res: T) =
 proc onload(buffer: Buffer) =
   var res: LoadResult = (false, buffer.lines.len, -1)
   if buffer.loaded:
-    buffer.fulfillTask(LOAD, res)
+    buffer.resolveTask(LOAD, res)
     return
   let op = buffer.sstream.getPosition()
   var s = newString(buffer.readbufsize)
@@ -691,11 +652,11 @@ proc onload(buffer: Buffer) =
       res.bytes = buffer.available
     else:
       buffer.do_reshape()
-    buffer.fulfillTask(LOAD, res)
+    buffer.resolveTask(LOAD, res)
   except EOFError:
     res.atend = true
     buffer.finishLoad()
-    buffer.fulfillTask(LOAD, res)
+    buffer.resolveTask(LOAD, res)
   except ErrorAgain, ErrorWouldBlock:
     if buffer.readbufsize > 1:
       buffer.readbufsize = buffer.readbufsize div 2
@@ -858,7 +819,7 @@ proc submitForm(form: HTMLFormElement, submitter: Element): Option[Request] =
     of FORM_ENCODING_TYPE_TEXT_PLAIN:
       body = serializePlainTextFormData(entrylist).some
       mimetype = $enctype
-    return newRequest(parsedaction, httpmethod, {"Content-Type": mimetype}, body, multipart).some
+    return newRequest(parsedaction, httpmethod, @{"Content-Type": mimetype}, body, multipart).some
 
   template getActionUrl() =
     return newRequest(parsedaction).some
@@ -1099,15 +1060,15 @@ macro bufferDispatcher(funs: static ProxyMap, buffer: Buffer, cmd: BufferCommand
       rval = ident("retval")
       stmts.add(quote do:
         let `rval` = `call`)
-    var fulfill = newStmtList()
+    var resolve = newStmtList()
     if rval == nil:
-      fulfill.add(quote do:
+      resolve.add(quote do:
         let len = slen(`packetid`)
         buffer.pstream.swrite(len)
         buffer.pstream.swrite(`packetid`)
         buffer.pstream.flush())
     else:
-      fulfill.add(quote do:
+      resolve.add(quote do:
         let len = slen(`packetid`) + slen(`rval`)
         buffer.pstream.swrite(len)
         buffer.pstream.swrite(`packetid`)
@@ -1120,9 +1081,9 @@ macro bufferDispatcher(funs: static ProxyMap, buffer: Buffer, cmd: BufferCommand
           buffer.savetask = false
           buffer.tasks[BufferCommand.`en`] = `packetid`
         else:
-          `fulfill`)
+          `resolve`)
     else:
-      stmts.add(fulfill)
+      stmts.add(resolve)
     ofbranch.add(stmts)
     switch.add(ofbranch)
   return switch
diff --git a/src/buffer/container.nim b/src/buffer/container.nim
index 9963f98e..a3406463 100644
--- a/src/buffer/container.nim
+++ b/src/buffer/container.nim
@@ -11,6 +11,7 @@ when defined(posix):
 import buffer/buffer
 import buffer/cell
 import config/config
+import io/promise
 import io/request
 import io/window
 import ips/forkserver
@@ -279,7 +280,7 @@ proc setNumLines(container: Container, lines: int, finish = false) =
     container.triggerEvent(STATUS)
 
 proc requestLines*(container: Container, w = container.lineWindow): auto {.discardable.} =
-  container.iface.getLines(w).then(proc(res: tuple[numLines: int, lines: seq[SimpleFlexibleLine]]) =
+  return container.iface.getLines(w).then(proc(res: tuple[numLines: int, lines: seq[SimpleFlexibleLine]]) =
     container.lines.setLen(w.len)
     container.lineshift = w.a
     for y in 0 ..< min(res.lines.len, w.len):
@@ -724,11 +725,10 @@ proc readSuccess*(container: Container, s: string) =
     if res.open.isSome:
       container.triggerEvent(ContainerEvent(t: OPEN, request: res.open.get)))
 
-proc reshape(container: Container, noreq = false) {.jsfunc.} =
-  container.iface.render().then(proc(lines: int) =
-    container.setNumLines(lines))
-  if not noreq:
-    container.needslines = true
+proc reshape(container: Container): EmptyPromise {.discardable, jsfunc.} =
+  return container.iface.render().then(proc(lines: int): auto =
+    container.setNumLines(lines)
+    return container.requestLines())
 
 proc dupeBuffer*(dispatcher: Dispatcher, container: Container, config: Config, location = none(URL), contenttype = none(string)): Container =
   let source = BufferSource(
@@ -806,7 +806,7 @@ proc handleCommand(container: Container) =
   var packetid, len: int
   container.iface.stream.sread(len)
   container.iface.stream.sread(packetid)
-  container.iface.fulfill(packetid, len - slen(packetid))
+  container.iface.resolve(packetid, len - slen(packetid))
 
 proc setStream*(container: Container, stream: Stream) =
   container.iface = newBufferInterface(stream)