dart-native / dart_native

Write iOS&macOS&Android Code using Dart. This package liberates you from redundant glue code and low performance of Flutter Channel.
BSD 3-Clause "New" or "Revised" License
951 stars 77 forks source link

【android】native_basic_type.dart定义的long、double类型数据,在安卓仅构建armeabi-v7a架构的包时,传参数据存在转换丢失的情况 #70

Closed ddrccw closed 3 years ago

ddrccw commented 3 years ago

问题

native_basic_type定义的long类型数据,在安卓仅构建armeabi-v7a架构的包时,如果long类型数据值大于2147483647(2^31-1),java侧获取的数据存在转换丢失的情况

double类型数据也不太对

具体案例

Dart_Native 版本:0.3.22

flutter环境 Flutter version 1.22.6 Dart version 2.10.5

修改如下几个文件


* lib/src/android/runtime/jobject.dart

NativeArguments _parseNativeArguments(List args, {List argsSignature}) { Pointer<Pointer> pointers = nullptr.cast();

/// extend a bit for string
Pointer<Pointer<Utf8>> typePointers =
    allocate<Pointer<Utf8>>(count: (args?.length ?? 0) + 1);
int stringTypeBitmask = 0;
if (args != null) {
  pointers = allocate<Pointer<Void>>(count: args.length);

  for (var i = 0; i < args.length; i++) {
    var arg = args[i];
    if (arg == null) {
      throw 'One of args list is null';
    }

    Pointer<Utf8> argSignature =
        argsSignature == null || !(argsSignature[i] is Pointer<Utf8>)
            ? null
            : argsSignature[i];

    if (arg is String) {
      stringTypeBitmask |= (0x1 << i);
    }

    storeValueToPointer(arg, pointers.elementAt(i),
        typePtr: typePointers.elementAt(i), argSignature: argSignature);

    //方便打印看save后再load的值
    if (arg is long) {
      int value = pointers.elementAt(i).cast<Int64>().value;
      var loadValue = loadValueFromPointer(pointers.elementAt(i).value, "J", typePtr: typePointers);
      print("DartNative _parseNativeArguments arg=$arg, storeValueToPointer value=$value, loadValueFromPointer value=$loadValue");
    }
  }
}
typePointers.elementAt(args?.length ?? 0).value = Utf8.toUtf8("0");
return NativeArguments(pointers, typePointers, stringTypeBitmask);

}


打印结果

2021-09-06 22:19:29.891 15223-16988/com.dartnative.dart_native_example I/flutter: DartNative _parseNativeArguments arg=2147483647, storeValueToPointer value=2147483647, loadValueFromPointer value=2147483647 2021-09-06 22:19:29.891 15223-16988/com.dartnative.dart_native_example D/dart_java: tag :test + 2147483647 + a + 2097152.0 + 12.0 + 1 + 2 + 2147483647 + false 2021-09-06 22:19:29.891 15223-16988/com.dartnative.dart_native_example I/flutter: call result:true 2021-09-06 22:19:29.891 15223-16988/com.dartnative.dart_native_example I/flutter: DartNative _parseNativeArguments arg=2147483648, storeValueToPointer value=2147483648, loadValueFromPointer value=2147483648 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example D/dart_java: tag :test + -2147483648 + a + 2097152.0 + 12.0 + 1 + 2 + -2147483648 + false 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example I/flutter: call result:true 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example I/flutter: DartNative _parseNativeArguments arg=4294967295, storeValueToPointer value=4294967295, loadValueFromPointer value=4294967295 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example D/dart_java: tag :test + -1 + a + 2097152.0 + 12.0 + 1 + 2 + -1 + false 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example I/flutter: call result:true 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example I/flutter: DartNative _parseNativeArguments arg=4294967296, storeValueToPointer value=4294967296, loadValueFromPointer value=0 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example D/dart_java: tag :test + 0 + a + 2097152.0 + 12.0 + 1 + 2 + 0 + false 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example I/flutter: call result:true 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example I/flutter: DartNative _parseNativeArguments arg=42949672960, storeValueToPointer value=42949672960, loadValueFromPointer value=0 2021-09-06 22:19:29.892 15223-16988/com.dartnative.dart_native_example D/dart_java: tag :test + 0 + a + 2097152.0 + 12.0 + 1 + 2 + 0 + false

// java long赋值打印结果 2021-09-06 22:34:20.989 16940-19032/com.dartnative.dart_native_example D/dart_java: java long:2147483647 + 2147483648 + 4294967295 + 4294967295 + 4294967296 + 42949672960

yulingtianxia commented 3 years ago

@hui19 麻烦看下整型数精度问题

hui19 commented 3 years ago

32位系统下,long、double为8字节,void指针为4字节,值传递过程中造成了精度问题,对32位64位中的long、double进行不同的处理,该问题已修复见 #71

ddrccw commented 3 years ago

实测代码里还有两处问题,已反馈到 #71

ddrccw commented 3 years ago

问题未修复,测试设备vivo x9 可以看一下代码里的case bool resultCall = stub.complexCall("test", 10, 'a', 10.0, 12.0, 1, 2, 10000, false); print('call result:$resultCall');

可以看到输出的日志

2021-10-22 14:29:28.398 25768-25925/com.dartnative.example D/dart_java: tag :test + 10 + a + 2097152.0 + 12.0 + 1 + 2 + 10000 + false

比较诡异 lib/src/android/runtime/jobject.dart 在storeValueToPointer 12.0 过后,再通过loadValueFromPointer可以看到pointers.elementAt(3).value的数据发生变化,从10.0 变成了2097152

yulingtianxia commented 3 years ago

@ddrccw 感谢反馈,我们继续查下

hui19 commented 3 years ago

问题未修复,测试设备vivo x9 可以看一下代码里的case bool resultCall = stub.complexCall("test", 10, 'a', 10.0, 12.0, 1, 2, 10000, false); print('call result:$resultCall');

可以看到输出的日志

2021-10-22 14:29:28.398 25768-25925/com.dartnative.example D/dart_java: tag :test + 10 + a + 2097152.0 + 12.0 + 1 + 2 + 10000 + false

比较诡异 lib/src/android/runtime/jobject.dart 在storeValueToPointer 12.0 过后,再通过loadValueFromPointer可以看到pointers.elementAt(3).value的数据发生变化,从10.0 变成了2097152

@ddrccw 是指在dart这边主动调用loadValueFromPointer解析第三个value吗?如果是这样, dart 传递给 native 的数据是保存到指针里面,native 在取指针的值。而 native 传递基本类型给 dart 的值是作为地址值传递的,dart 通过读取地址来还原基本类型值。所以基本类型在 storeValueToPointer 的数据不能直接用 loadValueFromPointer 读出来,可以参考这里

hui19 commented 3 years ago

@ddrccw pointers.elementAt(3).cast < Double > ().value 试试这样取值

ddrccw commented 3 years ago

@hui19

换种取法也是一样的,我用华为手机P40 (ANA-AN00) 也是一样的结果

jobject.dart代码

  NativeArguments _parseNativeArguments(List args, {List argsSignature}) {
    Pointer<Pointer<Void>> pointers = nullptr.cast();
    /// extend a bit for string
    Pointer<Pointer<Utf8>> typePointers =
        allocate<Pointer<Utf8>>(count: (args?.length ?? 0) + 1);
    int stringTypeBitmask = 0;
    if (args != null) {
      pointers = allocate<Pointer<Void>>(count: args.length);

      final bool _is64Bit = sizeOf<IntPtr>() == 8;
      var pVoid = sizeOf<Pointer<Void>>();
      var pDouble = sizeOf<Pointer<Double>>();
      var pFloat = sizeOf<Pointer<Float>>();

      for (var i = 0; i < args.length; i++) {
        var arg = args[i];
        if (arg == null) {
          throw 'One of args list is null';
        }

        Pointer<Utf8> argSignature =
            argsSignature == null || !(argsSignature[i] is Pointer<Utf8>)
                ? null
                : argsSignature[i];

        if (arg is String) {
          stringTypeBitmask |= (0x1 << i);
        }

        if (i == 4) {
          int index = 0;
          Pointer<Void> ptr0 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr0 = typePointers.elementAt(index);
          var ret0;
          Pointer<Uint16> tmp = pointers.elementAt(index).cast<Pointer<Uint16>>().value;
          var uint16Ptr = tmp.cast<Void>();
          int length = 0;
          for (int i = 0; i < 2; i++) {
            length += uint16Ptr.cast<Uint16>().elementAt(i).value;
          }
          Uint16List list = uint16Ptr.cast<Uint16>().asTypedList(length + 3);
          //free(uint16Ptr);
          final codes = String.fromCharCodes(list.sublist(2, length + 2));
          ret0 = codes;

          index = 1;
          Pointer<Void> ptr1 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr1 = typePointers.elementAt(index);
          var ret1 = ptr1.cast<Int32>().value;

          index = 2;
          Pointer<Void> ptr2 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr2 = typePointers.elementAt(index);
          var tmp2 = ptr2.cast<Uint16>().value;
          var ret2 = utf8.decode([tmp2]);

          // var ret20 = loadValueFromPointer(ptr2, "Ljava/lang/String;", typePtr:typePtr2);
          // var ret21 = loadValueFromPointer(ptr2, "Ljava/lang/String;", typePtr:typePtr2);

          index = 3;
          Pointer<Void> ptr3 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr3 = typePointers.elementAt(index);
          var ret = ptr3.cast<Double>().value;

          // var ret30 = loadValueFromPointer(ptr3, "D", typePtr:typePtr3);
          // var ret31 = loadValueFromPointer(ptr3, "D", typePtr: typePtr3);
          print("index 4 before storeValueToPointer," + ret0 + "," + ret1.toString() + "," + ret2 + "," + ret.toString() + ",");
        }

        storeValueToPointer(arg, pointers.elementAt(i),
            typePtr: typePointers.elementAt(i), argSignature: argSignature);

        if (arg is double && i == 3) {
          int index = 0;
          Pointer<Void> ptr0 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr0 = typePointers.elementAt(index);
          var ret0;
          Pointer<Uint16> tmp = pointers.elementAt(index).cast<Pointer<Uint16>>().value;
          var uint16Ptr = tmp.cast<Void>();
          int length = 0;
          for (int i = 0; i < 2; i++) {
            length += uint16Ptr.cast<Uint16>().elementAt(i).value;
          }
          Uint16List list = uint16Ptr.cast<Uint16>().asTypedList(length + 3);
          //free(uint16Ptr);
          final codes = String.fromCharCodes(list.sublist(2, length + 2));
          ret0 = codes;

          index = 1;
          Pointer<Void> ptr1 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr1 = typePointers.elementAt(index);
          var ret1 = ptr1.cast<Int32>().value;

          index = 2;
          Pointer<Void> ptr2 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr2 = typePointers.elementAt(index);
          var tmp2 = ptr2.cast<Uint16>().value;
          var ret2 = utf8.decode([tmp2]);

          // var ret20 = loadValueFromPointer(ptr2, "Ljava/lang/String;", typePtr:typePtr2);
          // var ret21 = loadValueFromPointer(ptr2, "Ljava/lang/String;", typePtr:typePtr2);

          index = 3;
          Pointer<Void> ptr3 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr3 = typePointers.elementAt(index);
          var ret = ptr3.cast<Double>().value;

          var floatValue = 10.0;
          // var bytes = Uint8List(4)
          //   ..buffer.asByteData().setFloat32(0, floatValue, Endian.little);
          var bytes = Float32List.fromList([floatValue]).buffer.asUint8List();
          print(bytes);

          // var ret30 = loadValueFromPointer(ptr3, "D", typePtr:typePtr3);
          // var ret31 = loadValueFromPointer(ptr3, "D", typePtr: typePtr3);
          print("index 3 after storeValueToPointer," + ret0 + "," + ret1.toString() + "," + ret2 + "," + ret.toString() + ",");
        }

        if (i == 4) {
          int index = 0;
          Pointer<Void> ptr0 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr0 = typePointers.elementAt(index);
          var ret0;
          Pointer<Uint16> tmp = pointers.elementAt(index).cast<Pointer<Uint16>>().value;
          var uint16Ptr = tmp.cast<Void>();
          int length = 0;
          for (int i = 0; i < 2; i++) {
            length += uint16Ptr.cast<Uint16>().elementAt(i).value;
          }
          Uint16List list = uint16Ptr.cast<Uint16>().asTypedList(length + 3);
          //free(uint16Ptr);
          final codes = String.fromCharCodes(list.sublist(2, length + 2));
          ret0 = codes;

          index = 1;
          Pointer<Void> ptr1 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr1 = typePointers.elementAt(index);
          var ret1 = ptr1.cast<Int32>().value;

          index = 2;
          Pointer<Void> ptr2 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr2 = typePointers.elementAt(index);
          var tmp2 = ptr2.cast<Uint16>().value;
          var ret2 = utf8.decode([tmp2]);

          // var ret20 = loadValueFromPointer(ptr2, "Ljava/lang/String;", typePtr:typePtr2);
          // var ret21 = loadValueFromPointer(ptr2, "Ljava/lang/String;", typePtr:typePtr2);

          index = 3;
          Pointer<Void> ptr3 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr3 = typePointers.elementAt(index);
          var ret = ptr3.cast<Double>().value;

          var floatValue = ret;
          // var bytes = Uint8List(4)
          //   ..buffer.asByteData().setFloat32(0, floatValue, Endian.little);
          var bytes = Float32List.fromList([floatValue]).buffer.asUint8List();
          print(bytes);

          index = 4;
          Pointer<Void> ptr4 = pointers.elementAt(index).cast<Void>();
          Pointer<Pointer<Utf8>> typePtr4 = typePointers.elementAt(index);
          var ret4 = ptr4.cast<Float>().value;
          print("index 4 after storeValueToPointer," + ret0 + "," + ret1.toString() + "," + ret2 + "," + ret.toString() + "," + ret4.toString());
        }
      }
    }
    typePointers.elementAt(args?.length ?? 0).value = Utf8.toUtf8("0");
    return NativeArguments(pointers, typePointers, stringTypeBitmask);
  }

对应日志 2021-10-29 14:35:52.276 26793-31510/com.dartnative.example I/flutter: [0, 0, 32, 65] 2021-10-29 14:35:52.276 26793-31510/com.dartnative.example I/flutter: index 3 after storeValueToPointer,test,10,a,10.0, 2021-10-29 14:35:52.276 26793-31510/com.dartnative.example I/flutter: index 4 before storeValueToPointer,test,10,a,10.0, 2021-10-29 14:35:52.277 26793-31510/com.dartnative.example I/flutter: [0, 0, 0, 74] 2021-10-29 14:35:52.277 26793-31510/com.dartnative.example I/flutter: index 4 after storeValueToPointer,test,10,a,2097152.0,12.0 2021-10-29 14:35:52.277 26793-31510/com.dartnative.example I/flutter: ret 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: invokeNativeMethod methodName=complexCall, returnType=Z 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=0, type=L 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=1, type=I 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=2, type=C 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=3, type=D 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=4, type=F 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=5, type=B 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=6, type=S 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=7, type=J 2021-10-29 14:35:52.278 26793-31510/com.dartnative.example D/DartNative: _fillArgs index=8, type=Z 2021-10-29 14:35:52.279 26793-31510/com.dartnative.example D/dart_java: tag :test + 10 + a + 2097152.0 + 12.0 + 1 + 2 + 10000 + false

hui19 commented 3 years ago

@ddrccw 抱歉我的理解错了,确实32位,dart的转换有精度损失,修复可以看这次提交

ddrccw commented 3 years ago

@ddrccw 抱歉我的理解错了,确实32位,dart的转换有精度损失,修复可以看这次提交

@hui19 精度验证修复了,赞👍

但是代码里还有两处可能还得改改,一个是jni_error_overflow image 另外一个是 image