diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bindings/quickjs.nim | 11 | ||||
-rw-r--r-- | src/html/env.nim | 2 | ||||
-rw-r--r-- | src/js/arraybuffer.nim | 18 | ||||
-rw-r--r-- | src/js/encoding.nim | 112 | ||||
-rw-r--r-- | src/js/fromjs.nim | 32 | ||||
-rw-r--r-- | src/js/javascript.nim | 1 | ||||
-rw-r--r-- | src/js/opaque.nim | 4 | ||||
-rw-r--r-- | src/js/tojs.nim | 11 | ||||
-rw-r--r-- | src/local/client.nim | 2 | ||||
-rw-r--r-- | src/version.nim | 2 |
10 files changed, 194 insertions, 1 deletions
diff --git a/src/bindings/quickjs.nim b/src/bindings/quickjs.nim index 17bac15a..a401bf1b 100644 --- a/src/bindings/quickjs.nim +++ b/src/bindings/quickjs.nim @@ -87,6 +87,8 @@ type opaque: pointer): JSModuleDef {.cdecl.} JSJobFunc* = proc (ctx: JSContext, argc: cint, argv: ptr JSValue): JSValue {.cdecl.} JSGCObjectHeader* {.importc, header: qjsheader.} = object + JSFreeArrayBufferDataFunc* = proc (rt: JSRuntime, + opaque, p: pointer) {.cdecl.} JSPropertyDescriptor* {.importc, header: qjsheader.} = object flags*: cint @@ -342,10 +344,19 @@ proc JS_SetOpaque*(obj: JSValue, opaque: pointer) proc JS_GetOpaque*(obj: JSValue, class_id: JSClassID): pointer proc JS_GetOpaque2*(ctx: JSContext, obj: JSValue, class_id: JSClassID): pointer proc JS_GetClassID*(obj: JSValue): JSClassID + proc JS_ParseJSON*(ctx: JSContext, buf: cstring, buf_len: csize_t, filename: cstring): JSValue proc JS_ParseJSON2*(ctx: JSContext, buf: cstring, buf_len: csize_t, filename: cstring, flags: cint): JSValue +proc JS_NewArrayBuffer*(ctx: JSContext, buf: ptr UncheckedArray[uint8], + len: csize_t, free_func: JSFreeArrayBufferDataFunc, opaque: pointer, + is_shared: JS_BOOL): JSValue +proc JS_GetArrayBuffer*(ctx: JSContext, psize: ptr csize_t, obj: JSValue): ptr uint8 +proc JS_GetUint8Array*(ctx: JSContext, psize: ptr csize_t, obj: JSValue): ptr uint8 +proc JS_GetTypedArrayBuffer*(ctx: JSContext, obj: JSValue, pbyte_offset, + pbyte_length, pbytes_per_element: ptr csize_t): JSValue + proc JS_NewClassID*(pclass_id: ptr JSClassID): JSClassID proc JS_NewClass*(rt: JSRuntime, class_id: JSClassID, class_def: ptr JSClassDef): cint proc JS_IsRegisteredClass*(rt: JSRuntime, class_id: JSClassID): cint diff --git a/src/html/env.nim b/src/html/env.nim index 6046ee2b..d300400b 100644 --- a/src/html/env.nim +++ b/src/html/env.nim @@ -9,6 +9,7 @@ import io/promise import js/base64 import js/console import js/domexception +import js/encoding import js/error import js/intl import js/javascript @@ -144,6 +145,7 @@ proc addScripting*(window: Window, selector: Selector[int]) = ctx.addHeadersModule() ctx.addRequestModule() ctx.addResponseModule() + ctx.addEncodingModule() proc runJSJobs*(window: Window) = window.jsrt.runJSJobs(window.console.err) diff --git a/src/js/arraybuffer.nim b/src/js/arraybuffer.nim new file mode 100644 index 00000000..ff9fa24d --- /dev/null +++ b/src/js/arraybuffer.nim @@ -0,0 +1,18 @@ +import bindings/quickjs + +type + JSArrayBuffer* = object + p*: ptr UncheckedArray[uint8] + len*: csize_t + dealloc*: JSFreeArrayBufferDataFunc + + JSArrayBufferView* = object + abuf*: JSArrayBuffer + offset*: csize_t # offset into the buffer + nmemb*: csize_t # number of members + nsize*: csize_t # member size + + JSUint8Array* = object + abuf*: JSArrayBuffer + offset*: csize_t # offset into the buffer + nmemb*: csize_t # number of members diff --git a/src/js/encoding.nim b/src/js/encoding.nim new file mode 100644 index 00000000..31f6f575 --- /dev/null +++ b/src/js/encoding.nim @@ -0,0 +1,112 @@ +import std/streams + +import js/arraybuffer +import js/dict +import js/error +import js/javascript + +import chakasu/charset +import chakasu/decoderstream +import chakasu/encoderstream + +type + TextEncoder = ref object + + TextDecoder = ref object + encoding: Charset + errorMode: DecoderErrorMode + ignoreBOM {.jsget.}: bool + doNotFlush: bool + bomSeen: bool + decoder: DecoderStream + encoder: EncoderStream # to return the string to JS + istream: StringStream + +jsDestructor(TextDecoder) +jsDestructor(TextEncoder) + +type TextDecoderOptions = object of JSDict + fatal: bool + ignoreBOM: bool + +func newTextDecoder(label = "utf-8", options = TextDecoderOptions()): + JSResult[TextDecoder] {.jsctor.} = + let errorMode = if options.fatal: + DECODER_ERROR_MODE_FATAL + else: + DECODER_ERROR_MODE_REPLACEMENT + let encoding = getCharset(label) + if encoding in {CHARSET_UNKNOWN, CHARSET_REPLACEMENT}: + return err(newRangeError("Invalid encoding label")) + return ok(TextDecoder( + errorMode: errorMode, + ignoreBOM: options.ignoreBOM, + encoding: encoding + )) + +type TextDecodeOptions = object of JSDict + stream: bool + +#TODO AllowSharedBufferSource +proc decode(this: TextDecoder, input = none(JSArrayBufferView), + options = TextDecodeOptions()): string {.jsfunc.} = + if not this.doNotFlush: + if this.istream != nil: + this.istream.close() + if this.decoder != nil: + this.decoder.close() + if this.encoder != nil: + this.encoder.close() + this.istream = newStringStream() + this.decoder = newDecoderStream(this.istream, cs = this.encoding, + errormode = this.errorMode) + this.encoder = newEncoderStream(this.decoder, cs = CHARSET_UTF_8) + this.bomSeen = false + if this.doNotFlush != options.stream: + this.doNotFlush = options.stream + this.decoder.setInhibitCheckEnd(options.stream) + if input.isSome: + let input = input.get + let pos = this.istream.getPosition() + #TODO input offset? + this.istream.writeData(input.abuf.p, int(input.abuf.len)) + this.istream.setPosition(pos) + #TODO this should return a JSString, so we do not needlessly re-encode + # the output. (Right now we do, implicitly through toJS.) + return this.encoder.readAll() + +func jencoding(this: TextDecoder): string {.jsfget: "encoding".} = + return $this.encoding + +func fatal(this: TextDecoder): bool {.jsfget.} = + return this.errorMode == DECODER_ERROR_MODE_FATAL + +func newTextEncoder(): TextEncoder {.jsctor.} = + return TextEncoder() + +func jencoding(this: TextEncoder): string {.jsfget: "encoding".} = + return "utf-8" + +proc dealloc_wrap(rt: JSRuntime, opaque, p: pointer) {.cdecl.} = + dealloc(p) + +proc encode(this: TextEncoder, input = ""): JSUint8Array {.jsfunc.} = + # input is already UTF-8 here :P + let buf = cast[ptr UncheckedArray[uint8]](alloc(input.len)) + copyMem(buf, unsafeAddr input[0], input.len) + let abuf = JSArrayBuffer( + p: buf, + len: csize_t(input.len), + dealloc: dealloc_wrap + ) + return JSUint8Array( + abuf: abuf, + offset: 0, + nmemb: csize_t(input.len) + ) + +#TODO encodeInto + +proc addEncodingModule*(ctx: JSContext) = + ctx.registerType(TextDecoder) + ctx.registerType(TextEncoder) diff --git a/src/js/fromjs.nim b/src/js/fromjs.nim index dbc009bc..b2e1cc91 100644 --- a/src/js/fromjs.nim +++ b/src/js/fromjs.nim @@ -5,6 +5,7 @@ import tables import unicode import bindings/quickjs +import js/arraybuffer import js/dict import js/error import js/opaque @@ -416,6 +417,33 @@ proc fromJSDict[T: JSDict](ctx: JSContext, val: JSValue): JSResult[T] = v = ?fromJS[typeof(v)](ctx, esm) return ok(d) +proc fromJSArrayBuffer(ctx: JSContext, val: JSValue): JSResult[JSArrayBuffer] = + var len: csize_t + let p = JS_GetArrayBuffer(ctx, addr len, val) + if p == nil: + return err() + let abuf = JSArrayBuffer( + len: len, + p: cast[ptr UncheckedArray[uint8]](p) + ) + return ok(abuf) + +proc fromJSArrayBufferView(ctx: JSContext, val: JSValue): + JSResult[JSArrayBufferView] = + var offset: csize_t + var nmemb: csize_t + var nsize: csize_t + let jsbuf = JS_GetTypedArrayBuffer(ctx, val, addr offset, addr nmemb, + addr nsize) + let abuf = ?fromJSArrayBuffer(ctx, jsbuf) + let view = JSArrayBufferView( + abuf: abuf, + offset: offset, + nmemb: nmemb, + nsize: nsize + ) + return ok(view) + type FromJSAllowedT = (object and not (Result|Option|Table|JSValue|JSDict)) proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T] = @@ -456,6 +484,10 @@ proc fromJS*[T](ctx: JSContext, val: JSValue): JSResult[T] = return fromJSVoid(ctx, val) elif T is JSDict: return fromJSDict[T](ctx, val) + elif T is JSArrayBuffer: + return fromJSArrayBuffer(ctx, val) + elif T is JSArrayBufferView: + return fromJSArrayBufferView(ctx, val) elif compiles(fromJS2(ctx, val, result)): fromJS2(ctx, val, result) else: diff --git a/src/js/javascript.nim b/src/js/javascript.nim index c744345d..996137cd 100644 --- a/src/js/javascript.nim +++ b/src/js/javascript.nim @@ -144,6 +144,7 @@ proc free*(ctx: var JSContext) = JS_FreeValue(ctx, v) JS_FreeValue(ctx, opaque.Array_prototype_values) JS_FreeValue(ctx, opaque.Object_prototype_valueOf) + JS_FreeValue(ctx, opaque.Uint8Array_ctor) for v in opaque.err_ctors: JS_FreeValue(ctx, v) GC_unref(opaque) diff --git a/src/js/opaque.nim b/src/js/opaque.nim index bdc84b5a..30456602 100644 --- a/src/js/opaque.nim +++ b/src/js/opaque.nim @@ -29,6 +29,7 @@ type str_refs*: array[JSStrRefs, JSAtom] Array_prototype_values*: JSValue Object_prototype_valueOf*: JSValue + Uint8Array_ctor*: JSValue err_ctors*: array[JSErrorEnum, JSValue] htmldda*: JSClassID # only one of these exists: document.all. @@ -64,6 +65,9 @@ func newJSContextOpaque*(ctx: JSContext): JSContextOpaque = let objproto = JS_GetClassProto(ctx, JS_CLASS_OBJECT) opaque.Object_prototype_valueOf = JS_GetPropertyStr(ctx, objproto, "valueOf") JS_FreeValue(ctx, objproto) + block: + let u8actor = JS_GetPropertyStr(ctx, global, "Uint8Array") + opaque.Uint8Array_ctor = u8actor for e in JSErrorEnum: let s = $e let err = JS_GetPropertyStr(ctx, global, cstring(s)) diff --git a/src/js/tojs.nim b/src/js/tojs.nim index 89ca4cd4..e614c451 100644 --- a/src/js/tojs.nim +++ b/src/js/tojs.nim @@ -4,6 +4,7 @@ import unicode import bindings/quickjs import io/promise +import js/arraybuffer import js/dict import js/error import js/opaque @@ -259,6 +260,16 @@ proc toJS*(ctx: JSContext, err: JSError): JSValue = proc toJS*(ctx: JSContext, f: JSCFunction): JSValue = return JS_NewCFunction(ctx, f, cstring"", 0) +proc toJS*(ctx: JSContext, abuf: JSArrayBuffer): JSValue = + return JS_NewArrayBuffer(ctx, abuf.p, abuf.len, abuf.dealloc, nil, false) + +proc toJS*(ctx: JSContext, u8a: JSUint8Array): JSValue = + var jsabuf = toJS(ctx, u8a.abuf) + let ctor = ctx.getOpaque().Uint8Array_ctor + let ret = JS_CallConstructor(ctx, ctor, 1, addr jsabuf) + JS_FreeValue(ctx, jsabuf) + return ret + proc toJSP(ctx: JSContext, parent: ref object, child: var object): JSValue = let p = addr child # Save parent as the original ancestor for this tree. diff --git a/src/local/client.nim b/src/local/client.nim index 91b111d9..9cff0f2f 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -28,6 +28,7 @@ import io/socketstream import js/base64 import js/console import js/domexception +import js/encoding import js/error import js/fromjs import js/intl @@ -640,6 +641,7 @@ proc addJSModules(client: Client, ctx: JSContext) = ctx.addConfigModule() ctx.addPagerModule() ctx.addContainerModule() + ctx.addEncodingModule() func getClient(client: Client): Client {.jsfget: "client".} = return client diff --git a/src/version.nim b/src/version.nim index e4205bc2..e2a35886 100644 --- a/src/version.nim +++ b/src/version.nim @@ -27,5 +27,5 @@ tryImport chakasu/version, "chakasu" tryImport chame/version, "chame" static: - checkVersion("chakasu", 0, 2, 1) + checkVersion("chakasu", 0, 3, 0) checkVersion("chame", 0, 11, 2) |