import std/macros import std/options import std/tables import std/unicode import bindings/quickjs import io/promise import js/error import js/jstypes import js/opaque import js/tojs import types/opt import utils/twtstr proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T] func isInstanceOfNonGlobal(ctx: JSContext, val: JSValue, class: string): bool = let ctxOpaque = ctx.getOpaque() var classid = JS_GetClassID(val) let tclassid = ctxOpaque.creg[class] var found = false while true: if classid == tclassid: found = true break ctxOpaque.parents.withValue(classid, val): classid = val[] do: classid = 0 # not defined by Chawan; assume parent is Object. if classid == 0: 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 if outp != nil: var ret = newString(plen) if plen != 0: prepareMutation(ret) copyMem(addr ret[0], outp, plen) result = ok(ret) JS_FreeCString(ctx, outp) func fromJSString(ctx: JSContext, val: JSValue): JSResult[string] = var plen: csize_t let outp = JS_ToCStringLen(ctx, addr plen, val) # cstring if outp == nil: return err() var ret = newString(plen) if plen != 0: prepareMutation(ret) copyMem(addr ret[0], outp, plen) JS_FreeCString(ctx, outp) return ok(ret) func fromJSInt[T: SomeInteger](ctx: JSContext, val: JSValue): JSResult[T] = when T is int: # Always int32, so we don't risk 32-bit only breakage. # If int64 is needed, specify it explicitly. var ret: int32 if JS_ToInt32(ctx, addr ret, val) < 0: return err() return ok(int(ret)) elif T is uint: var ret: uint32 if JS_ToUint32(ctx, addr ret, val) < 0: return err() return ok(uint(ret)) elif T is int32: var ret: int32 if JS_ToInt32(ctx, addr ret, val) < 0: return err() return ok(ret) elif T is int64: var ret: int64 if JS_ToInt64(ctx, addr ret, val) < 0: return err() return ok(ret) elif T is uint32: var ret: uint32 if JS_ToUint32(ctx, addr ret, val) < 0: return err() return ok(ret) elif T is uint64: var ret: uint32 if JS_ToUint32(ctx, addr ret, val) < 0: return err() return ok(cast[uint64](ret)) proc fromJSFloat64(ctx: JSContext, val: JSValue): JSResult[float64] = var f64: float64 if JS_ToFloat64(ctx, addr f64, val) < 0: return err() return ok(f64) macro fromJSTupleBody(a: tuple) = let len = a.getType().len - 1 let done = ident("done") result = newStmtList(quote do: var `done`: bool) for i in 0..