11import 'dart:async' ;
22import 'dart:js_interop' ;
33import 'dart:js_interop_unsafe' ;
4+ import 'dart:typed_data' ;
45
56import 'package:sqlite3/common.dart' ;
67import 'package:sqlite3/wasm.dart' as wasm_vfs;
8+ // ignore: implementation_imports
9+ import 'package:sqlite3/src/wasm/js_interop/core.dart' ;
710import 'package:web/web.dart' ;
811
912import 'types.dart' ;
@@ -69,6 +72,7 @@ class _UniqueFieldNames {
6972 static const returnRows = 'r' ;
7073 static const updateRowId = 'r' ;
7174 static const rows = 'r' ; // no clash, used on different message types
75+ static const typeVector = 'v' ;
7276}
7377
7478sealed class Message {
@@ -450,15 +454,25 @@ final class RunQuery extends Request {
450454 });
451455
452456 factory RunQuery .deserialize (JSObject object) {
457+ final rawParameters =
458+ (object[_UniqueFieldNames .parameters] as JSArray ).toDart;
459+ final typeVector = switch (object[_UniqueFieldNames .typeVector]) {
460+ final types? => (types as JSArrayBuffer ).toDart.asUint8List (),
461+ null => null ,
462+ };
463+
464+ final parameters = List <Object ?>.filled (rawParameters.length, null );
465+ for (var i = 0 ; i < parameters.length; i++ ) {
466+ final typeCode =
467+ typeVector != null ? TypeCode .of (typeVector[i]) : TypeCode .unknown;
468+ parameters[i] = typeCode.decodeColumn (rawParameters[i]);
469+ }
470+
453471 return RunQuery (
454472 requestId: object.requestId,
455473 databaseId: object.databaseId,
456474 sql: (object[_UniqueFieldNames .sql] as JSString ).toDart,
457- parameters: [
458- for (final raw
459- in (object[_UniqueFieldNames .parameters] as JSArray ).toDart)
460- raw.dartify ()
461- ],
475+ parameters: parameters,
462476 returnRows: (object[_UniqueFieldNames .returnRows] as JSBoolean ).toDart,
463477 );
464478 }
@@ -470,9 +484,25 @@ final class RunQuery extends Request {
470484 void serialize (JSObject object, List <JSObject > transferred) {
471485 super .serialize (object, transferred);
472486 object[_UniqueFieldNames .sql] = sql.toJS;
473- object[_UniqueFieldNames .parameters] =
474- < JSAny ? > [for (final parameter in parameters) parameter.jsify ()].toJS;
475487 object[_UniqueFieldNames .returnRows] = returnRows.toJS;
488+
489+ if (parameters.isNotEmpty) {
490+ final jsParams = < JSAny ? > [];
491+ final typeCodes = Uint8List (parameters.length);
492+ for (var i = 0 ; i < parameters.length; i++ ) {
493+ final (code, jsParam) = TypeCode .encodeValue (parameters[i]);
494+ typeCodes[i] = code.index;
495+ jsParams.add (jsParam);
496+ }
497+
498+ final jsTypes = typeCodes.buffer.toJS;
499+ transferred.add (jsTypes);
500+
501+ object[_UniqueFieldNames .parameters] = jsParams.toJS;
502+ object[_UniqueFieldNames .typeVector] = jsTypes;
503+ } else {
504+ object[_UniqueFieldNames .parameters] = JSArray ();
505+ }
476506 }
477507}
478508
@@ -557,6 +587,81 @@ final class EndpointResponse extends Response {
557587 }
558588}
559589
590+ enum TypeCode {
591+ unknown,
592+ integer,
593+ bigInt,
594+ float,
595+ text,
596+ blob,
597+ $null,
598+ boolean;
599+
600+ static TypeCode of (int i) {
601+ return i >= TypeCode .values.length ? TypeCode .unknown : TypeCode .values[i];
602+ }
603+
604+ Object ? decodeColumn (JSAny ? column) {
605+ const hasNativeInts = ! identical (0 , 0.0 );
606+
607+ return switch (this ) {
608+ TypeCode .unknown => column.dartify (),
609+ TypeCode .integer => (column as JSNumber ).toDartInt,
610+ TypeCode .bigInt => hasNativeInts
611+ ? (column as JsBigInt ).asDartInt
612+ : (column as JsBigInt ).asDartBigInt,
613+ TypeCode .float => (column as JSNumber ).toDartDouble,
614+ TypeCode .text => (column as JSString ).toDart,
615+ TypeCode .blob => (column as JSUint8Array ).toDart,
616+ TypeCode .boolean => (column as JSBoolean ).toDart,
617+ TypeCode .$null => null ,
618+ };
619+ }
620+
621+ static (TypeCode , JSAny ? ) encodeValue (Object ? dart) {
622+ // In previous clients/workers, values were encoded with dartify() and
623+ // jsify() only. For backwards-compatibility, this value must be compatible
624+ // with dartify() used on the other end.
625+ // An exception are BigInts, which have not been sent correctly before this
626+ // encoder.
627+ // The reasons for adopting a custom format are: Being able to properly
628+ // serialize BigInts, possible dartify/jsify incompatibilities between
629+ // dart2js and dart2wasm and most importantly, being able to keep 1 and 1.0
630+ // apart in dart2wasm when the worker is compiled with dart2js.
631+ final JSAny ? value;
632+ final TypeCode code;
633+
634+ switch (dart) {
635+ case null :
636+ value = null ;
637+ code = TypeCode .$null;
638+ case final int integer:
639+ value = integer.toJS;
640+ code = TypeCode .integer;
641+ case final BigInt bi:
642+ value = JsBigInt .fromBigInt (bi);
643+ code = TypeCode .bigInt;
644+ case final double d:
645+ value = d.toJS;
646+ code = TypeCode .float;
647+ case final String s:
648+ value = s.toJS;
649+ code = TypeCode .text;
650+ case final Uint8List blob:
651+ value = blob.toJS;
652+ code = TypeCode .blob;
653+ case final bool boolean:
654+ value = boolean.toJS;
655+ code = TypeCode .boolean;
656+ case final other:
657+ value = other.jsify ();
658+ code = TypeCode .unknown;
659+ }
660+
661+ return (code, value);
662+ }
663+ }
664+
560665final class RowsResponse extends Response {
561666 final ResultSet resultSet;
562667
@@ -576,12 +681,20 @@ final class RowsResponse extends Response {
576681 ]
577682 : null ;
578683
684+ final typeVector = switch (object[_UniqueFieldNames .typeVector]) {
685+ final types? => (types as JSArrayBuffer ).toDart.asUint8List (),
686+ null => null ,
687+ };
579688 final rows = < List <Object ?>> [];
689+ var i = 0 ;
580690 for (final row in (object[_UniqueFieldNames .rows] as JSArray ).toDart) {
581691 final dartRow = < Object ? > [];
582692
583693 for (final column in (row as JSArray ).toDart) {
584- dartRow.add (column.dartify ());
694+ final typeCode =
695+ typeVector != null ? TypeCode .of (typeVector[i]) : TypeCode .unknown;
696+ dartRow.add (typeCode.decodeColumn (column));
697+ i++ ;
585698 }
586699
587700 rows.add (dartRow);
@@ -599,6 +712,29 @@ final class RowsResponse extends Response {
599712 @override
600713 void serialize (JSObject object, List <JSObject > transferred) {
601714 super .serialize (object, transferred);
715+ final jsRows = < JSArray > [];
716+ final columns = resultSet.columnNames.length;
717+ final typeVector = Uint8List (resultSet.length * columns);
718+
719+ for (var i = 0 ; i < resultSet.length; i++ ) {
720+ final row = resultSet.rows[i];
721+ assert (row.length == columns);
722+ final jsRow = List <JSAny ?>.filled (row.length, null );
723+
724+ for (var j = 0 ; j < columns; j++ ) {
725+ final (code, value) = TypeCode .encodeValue (row[j]);
726+
727+ jsRow[j] = value;
728+ typeVector[i * columns + j] = code.index;
729+ }
730+
731+ jsRows.add (jsRow.toJS);
732+ }
733+
734+ final jsTypes = typeVector.buffer.toJS;
735+ object[_UniqueFieldNames .typeVector] = jsTypes;
736+ transferred.add (jsTypes);
737+
602738 object[_UniqueFieldNames .rows] = < JSArray > [
603739 for (final row in resultSet.rows)
604740 < JSAny ? > [
0 commit comments