Tuesday, September 10, 2013

Dart Compiler bug

There is a strange behavior in Dart's setter declaration.
It should detect an error at compilation time(dart2js) or editing time using Eclipse dart plugin (or Dart Editor), but it is only detected at runtime.

let see following class definition.
In the class A, rev0 is defined without parameter type specification.
but only possible type allowed 'void' since the type of expression '_rev = value' must be equal to the setter's return type 'void', and that assignment expression's type is the type of value. so teh value type must be void.
This already quite ridiculous situation for setter  since we cannot declare type of a variable as void!
but aside from this issue, if we call a function in which  the return type is 'void', it works as (3).
and the null value's type seems void! anyway null is assigned to _rev.

So these things does not make sense, and practically, it introduce an error which can be detected at compilation time, but defered at the runtime.
Since this is so wird, people may not easily spot the bug in the setter definition!
As a workaround, we should always define type for the setter parameter(no dynamic also).

String _rev = null;
class A {
  String get id => _id;
  void set id(String i) { _id = i; }
  String _id = null;

  String _rev = null;
  String get rev => _rev;
  void set rev(String value) { _rev = value; }// (0) normal way..

  // void a; // compilation error

  void f(_) {}
  void set rev0(value) => _rev = value; // buggy, but no compile time error
  void set rev00(dynamic value) => _rev = value; // buggy, but no compile time error
  void set rev1(String value) => f(value); // (1) this is OK
  void set rev2(String value) => f(_rev = value); //(2)
  // void set rev3(String value) => _rev = value;  // compilation error
  String rev3(String value) => (_rev = value);

  factory A.fromJson(String json) {
    var map = parse(json);
    return new A.fromMap(map);
  }
....
}
main() {
    A e = new A.fromJson('{"id":null, "_rev":"NNNNN"}');
    log.info("1===> ${e.toJson()} ${e.rev}");
    e.id = e.rev = "ooo";
    log.info("2===>  ${e.toJson()} ${e.rev}");
    e.rev0 = e.f(1); // works!! but wird.. (3)
    log.info("3===>  ${e.toJson()} ${e.rev}");
    e.rev1 = "aa"; // works!! but wird..
    log.info("4===> ${e.toJson()} ${e.rev}");
    e.rev2 = "aa"; // works!! but wird..
    log.info("5===> ${e.toJson()} ${e.rev}");

    e.rev0 = "aa"; // runtime error!!

}
}

----------------------
The following is the execution logs.
when 'e.rev0 = "aa";' is called, it causes runtime error.
this error message "type 'String' is not a subtype of type 'void' of 'function result'." is also not easy to understand, but the reason is this rev0 is expecting void argument type!

dirt_server.log [INFO]: 1===>  {"id":null,"_rev":"NNNNN"} NNNNN
dirt_server.log [INFO]: 2===>  {"id":"ooo","_rev":"ooo"} ooo
dirt_server.log [INFO]: 3===>  {"id":"ooo"} null
dirt_server.log [INFO]: 4===>  {"id":"ooo"} null
dirt_server.log [INFO]: 5===>  {"id":"ooo", "_rev":"aa"} aa

Unhandled exception:
type 'String' is not a subtype of type 'void' of 'function result'.
#0      A.rev0= (file:///opt/A.dart:28:28)
#1      main (file:///opt/A.dart:138:34)

-----------------


No comments:

Post a Comment