sdk icon indicating copy to clipboard operation
sdk copied to clipboard

[JS-interop] Extension for JSBigint

Open redDwarf03 opened this issue 1 year ago • 13 comments
trafficstars

Why JSBigint and Bigint have no extension in js-interop like


/// [JSBigInt] <-> [BigInt]
extension JSBigIntToBigInt on JSBigInt {
  external BigInt get toDart;
}

extension BigIntToJSBigInt on BigInt {
  external JSBigInt get toJS;
}

I try to create in my project these extensions but i have this error:

Error: Type 'BigInt' is not a valid type in the signature of `dart:js_interop` external APIs or APIs converted via `toJS`. The only valid types are: JS types from `dart:js_interop`, @staticInterop types, void, bool, num, double, int, String, extension types that erases to one of these types, or a type parameter that is bound to a static interop type.
[        ]  - 'BigInt' is from 'dart:core'.
[        ] Use one of the valid types instead.
[        ]   external BigInt get toDart;
[        ]                       ^
[        ] lib/main.dart:224:25: Error: JS interop or Native class required for 'external' extension members.
[        ] Try adding a JS interop annotation to the on type class of the extension.
[        ]   external JSBigInt get toJS;
[        ]                         ^

redDwarf03 avatar Aug 21 '24 13:08 redDwarf03

Summary: The user is requesting extensions for JSBigInt and BigInt in js-interop to enable seamless conversion between these types. They are encountering errors related to the use of BigInt in the external extension members, which is not allowed in dart:js_interop.

dart-github-bot avatar Aug 21 '24 13:08 dart-github-bot

@redDwarf03 have you tried using js- like js: ^0.6.4 and creating an interop?

import 'package:js/js.dart';

@JS('BigInt')
class JSBigInt {
  external JSBigInt(String value);
  external JSBigInt add(JSBigInt other);
  external JSBigInt multiply(JSBigInt other);
}

class MyBigInt extends JSBigInt {
  MyBigInt(String value) : super(value);

  MyBigInt addWithDart(MyBigInt other) {
    return this.add(other);
  }

  MyBigInt multiplyWithDart(MyBigInt other) {
    return this.multiply(other);
  }
}

void main() {
  var a = MyBigInt('123456789123456789');
  var b = MyBigInt('987654321987654321');

  var resultAdd = a.addWithDart(b);
  var resultMultiply = a.multiplyWithDart(b);

  print('Addition Result: ${resultAdd.toString()}');
  print('Multiplication Result: ${resultMultiply.toString()}');
}

kaumudpa avatar Aug 21 '24 13:08 kaumudpa

@redDwarf03 have you tried using js- like js: ^0.6.4 and creating an interop?

import 'package:js/js.dart';

@JS('BigInt')
class JSBigInt {
  external JSBigInt(String value);
  external JSBigInt add(JSBigInt other);
  external JSBigInt multiply(JSBigInt other);
}

class MyBigInt extends JSBigInt {
  MyBigInt(String value) : super(value);

  MyBigInt addWithDart(MyBigInt other) {
    return this.add(other);
  }

  MyBigInt multiplyWithDart(MyBigInt other) {
    return this.multiply(other);
  }
}

void main() {
  var a = MyBigInt('123456789123456789');
  var b = MyBigInt('987654321987654321');

  var resultAdd = a.addWithDart(b);
  var resultMultiply = a.multiplyWithDart(b);

  print('Addition Result: ${resultAdd.toString()}');
  print('Multiplication Result: ${resultMultiply.toString()}');
}

No because js is not supported with new JS Interoperability and WASM https://dart.dev/interop/js-interop

So i don't want to use js. Sorry

redDwarf03 avatar Aug 21 '24 13:08 redDwarf03

I create this dart class based on dart test classes but i think dart core team should expose a method to manipulate Bigint and JSBigInt

import 'dart:js_interop';

@JS()
external JSBigInt bigInt;

@JS('BigInt')
external JSBigInt createBigInt(String value);

extension on JSBigInt {
  @JS('toString')
  external String toStringExternal();
}

redDwarf03 avatar Aug 21 '24 15:08 redDwarf03

Great request, @redDwarf03.

kevmoo avatar Aug 21 '24 16:08 kevmoo

@kevmoo The same with JSFunction if possible :)

redDwarf03 avatar Aug 21 '24 16:08 redDwarf03

The toDart case is fairly straightforward:

JSBigInt b = ...;
BigInt b2 = BigInt.parse(b.toString()); // toString calls the external toString

The toJS case requires a few more lines but is also straightforward:

@JS('BigInt')
external JSBigInt bigInt(String s);

...

BigInt b = ...;
bigInt(b.toString());

Of course, it'd be useful to add functions that do this for you and have them in dart:js_interop, so we can track this request.

The same with JSFunction if possible :)

Can you elaborate a bit more here? We have Function.toJS and JSExportedDartFunction.toDart today. JSExportedDartFunction is a separate type since not all JSFunctions were converted Dart functions. If you want to call arbitrary JSFunctions, one solution is using JSFunction.callAsFunction.

srujzs avatar Aug 21 '24 21:08 srujzs

thx. i will explore your comment on JSFunction

redDwarf03 avatar Aug 22 '24 08:08 redDwarf03

@srujzs about JSBigint and JSArray:

I have an JSArray<JSObject>

2 cases:

1 - I manage conversion directly in dart

@JS()
extension type WriteContractParameters._(JSObject _) implements JSObject {
  external WriteContractParameters({
    JSArray? args,
  });

  external JSArray? args;
}

@JS()
external JSBigInt bigInt;

@JS('BigInt')
external JSBigInt createBigInt(String value);

extension on JSBigInt {
  @JS('toString')
  external String toStringExternal();
}
final writeContractParameters = WriteContractParameters(
   args: [
       '0x08Bfc8BA9fD137Fb632F79548B150FE0Be493254'.toJS,
       createBigInt('498500000000000'),
   ].toJS,
);

When i debug javascript, i have my bigint correctly managed

args: Array(2)
0: "0x08Bfc8BA9fD137Fb632F79548B150FE0Be493254"
1: 498500000000000n

2 - I don't manage conversion directly in dart

class WriteContractParameters {
  WriteContractParameters({
    this.args,
  });

  List<dynamic>? args;

  JSWriteContractParameters get toJS => JSWriteContractParameters(
        args: args?.jsify() as JSArray<JSObject>?,
       );
}
final writeContractParameters = wagmi.WriteContractParameters(
   args: [
      '0x08Bfc8BA9fD137Fb632F79548B150FE0Be493254',
      BigInt.from(498500000000000),
    ],
);

When i debug javascript, i have my bigint NOT correctly managed

args: 
Array(2)
0: "0x08Bfc8BA9fD137Fb632F79548B150FE0Be493254"
1: core._BigIntImpl.__ {Symbol(_used): 4, Symbol(_digits): Uint16Array(4), Symbol(_isNegative): false}
length: 2
Symbol(dartx.arrayRti): (...)
Symbol(dartx.first): (...)
Symbol(dartx.hashCode): (...)
Symbol(dartx.isEmpty): (...)
Symbol(dartx.isNotEmpty): (...)
Symbol(dartx.iterator): (...)
Symbol(dartx.last): (...)
Symbol(dartx.length): (...)
Symbol(dartx.reversed): (...)
Symbol(dartx.runtimeType): (...)
Symbol(dartx.single): (...)
[[Prototype]]: Array(0)

So i would like to fix my case 2

redDwarf03 avatar Aug 22 '24 11:08 redDwarf03

Yeah, we currently don't treat BigInts specially in jsify and treat them as other Dart members, so it'll be a bit more of a manual process. There's a blanket bug here: https://github.com/dart-lang/sdk/issues/55222 to be clear about what jsify/dartify can and can't do (right now we only technically support whatever's in the doc comment) as well as make it consistent. One of the options there is allow users to expose the low-level conversion methods they need so they can write their own variants that convert types as they need.

srujzs avatar Aug 22 '24 15:08 srujzs

@srujzs i tried something like that but the add method seems to add nothing...


  JSArray<JSObject>? _convertArgs(List<dynamic>? args) {
    if (args == null) {
      return null;
    }
    final jsArgs = JSArray<JSObject>();
    for (final arg in args) {
      if (arg is String) {
        jsArgs.add(arg.toJS);
      } else if (arg is int) {
        jsArgs.add(arg.toJS);
      } else if (arg is bool) {
        jsArgs.add(arg.toJS);
      }
    }
    return jsArgs;
  }

redDwarf03 avatar Aug 23 '24 07:08 redDwarf03

@srujzs i tried something like that but the add method seems to add nothing...


  JSArray<JSObject>? _convertArgs(List<dynamic>? args) {
    if (args == null) {
      return null;
    }
    final jsArgs = JSArray<JSObject>();
    for (final arg in args) {
      if (arg is String) {
        jsArgs.add(arg.toJS);
      } else if (arg is int) {
        jsArgs.add(arg.toJS);
      } else if (arg is bool) {
        jsArgs.add(arg.toJS);
      }
    }
    return jsArgs;
  }

@srujzs is it possible to add BigInt in the condition https://github.com/dart-lang/sdk/blob/ee0b971dbd6ff51d8ec740294fb0a2730df6bb2b/sdk/lib/_internal/wasm/lib/js_util_patch.dart#L32

redDwarf03 avatar Aug 25 '24 06:08 redDwarf03

i tried something like that but the add method seems to add nothing...

There's no add method on a JSArray. You may want push e.g.

extension JSArrayExtension<T extends JSAny?> on JSArray<T> {
  external void push(T _);
}

Besides that, the approach seems fine, but it is a bit of reinventing jsify alas.

is it possible to add BigInt in the condition

One of the things we want to do for jsify/dartify is expose it in such a way that users can define their own conversions if needed. Maybe a callback would be useful here for types that aren't supported by default, but it'd be nice to customize jsify/dartify as needed at any rate.

srujzs avatar Aug 27 '24 00:08 srujzs