|  | // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "vm/dart_api_impl.h" | 
|  | #include "vm/dart_api_state.h" | 
|  | #include "vm/object.h" | 
|  | #include "vm/symbols.h" | 
|  | #include "vm/unit_test.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | FieldPtr LookupField(Dart_Handle library, | 
|  | const char* class_name, | 
|  | const char* field_name) { | 
|  | LibraryPtr raw_library = Library::RawCast(Api::UnwrapHandle(library)); | 
|  | Library& lib = Library::ZoneHandle(raw_library); | 
|  | const String& classname = | 
|  | String::Handle(Symbols::New(Thread::Current(), class_name)); | 
|  | Class& cls = Class::Handle(lib.LookupClass(classname)); | 
|  | EXPECT(!cls.IsNull());  // No ambiguity error expected. | 
|  |  | 
|  | String& fieldname = String::Handle(String::New(field_name)); | 
|  | Field& field = | 
|  | Field::ZoneHandle(cls.LookupInstanceFieldAllowPrivate(fieldname)); | 
|  | EXPECT(!field.IsNull()); | 
|  | return field.ptr(); | 
|  | } | 
|  |  | 
|  | TEST_CASE(GuardFieldSimpleTest) { | 
|  | const char* script_chars = | 
|  | "class A {\n" | 
|  | "  var f1 = 3.0;\n" | 
|  | "  dynamic f2 = 3;\n" | 
|  | "  var f3 = List<dynamic>.filled(4, null);\n" | 
|  | "  foo() {\n" | 
|  | "    f1 = f1 + f1;\n" | 
|  | "  }\n" | 
|  | "  bar() {\n" | 
|  | "    f2 = null;\n" | 
|  | "    f2 = 3.0;\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runFoo() {\n" | 
|  | "  var a = A();\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    a.foo();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runBar() {\n" | 
|  | "  var a = A();\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    a.bar();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "main() {\n" | 
|  | "  for (int i = 0; i < 100; i++) {\n" | 
|  | "    runFoo();\n" | 
|  | "    runBar();\n" | 
|  | "  }\n" | 
|  | "}\n"; | 
|  | Dart_Handle lib = TestCase::LoadTestScript(script_chars, nullptr); | 
|  | Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr); | 
|  | EXPECT_VALID(result); | 
|  | TransitionNativeToVM transition(thread); | 
|  | Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1")); | 
|  | Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2")); | 
|  | Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3")); | 
|  | const intptr_t no_length = Field::kNoFixedLength; | 
|  | EXPECT_EQ(no_length, f1.guarded_list_length()); | 
|  | EXPECT_EQ(kDoubleCid, f1.guarded_cid()); | 
|  | EXPECT_EQ(false, f1.is_nullable()); | 
|  | EXPECT_EQ(no_length, f2.guarded_list_length()); | 
|  | EXPECT_EQ(kDynamicCid, f2.guarded_cid()); | 
|  | EXPECT_EQ(true, f2.is_nullable()); | 
|  | EXPECT_EQ(no_length, f3.guarded_list_length()); | 
|  | } | 
|  |  | 
|  | TEST_CASE(GuardFieldFinalListTest) { | 
|  | const char* script_chars = | 
|  | "class A {\n" | 
|  | "  var f1 = 3.0;\n" | 
|  | "  dynamic f2 = 3;\n" | 
|  | "  final f3 = List<dynamic>.filled(4, null);\n" | 
|  | "  foo() {\n" | 
|  | "    f1 = f1 + f1;\n" | 
|  | "  }\n" | 
|  | "  bar() {\n" | 
|  | "    f2 = null;\n" | 
|  | "    f2 = 3.0;\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runFoo() {\n" | 
|  | "  var a = A();\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    a.foo();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runBar() {\n" | 
|  | "  var a = A();\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    a.bar();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "main() {\n" | 
|  | "  for (int i = 0; i < 100; i++) {\n" | 
|  | "    runFoo();\n" | 
|  | "    runBar();\n" | 
|  | "  }\n" | 
|  | "}\n"; | 
|  | Dart_Handle lib = TestCase::LoadTestScript(script_chars, nullptr); | 
|  | Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr); | 
|  | EXPECT_VALID(result); | 
|  | TransitionNativeToVM transition(thread); | 
|  | Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1")); | 
|  | Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2")); | 
|  | Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3")); | 
|  | const intptr_t no_length = Field::kNoFixedLength; | 
|  | EXPECT_EQ(no_length, f1.guarded_list_length()); | 
|  | EXPECT_EQ(kDoubleCid, f1.guarded_cid()); | 
|  | EXPECT_EQ(false, f1.is_nullable()); | 
|  | EXPECT_EQ(no_length, f2.guarded_list_length()); | 
|  | EXPECT_EQ(kDynamicCid, f2.guarded_cid()); | 
|  | EXPECT_EQ(true, f2.is_nullable()); | 
|  | EXPECT_EQ(4, f3.guarded_list_length()); | 
|  | EXPECT_EQ(kArrayCid, f3.guarded_cid()); | 
|  | EXPECT_EQ(false, f3.is_nullable()); | 
|  | } | 
|  |  | 
|  | TEST_CASE(GuardFieldFinalVariableLengthListTest) { | 
|  | const char* script_chars = | 
|  | "class A {\n" | 
|  | "  var f1 = 3.0;\n" | 
|  | "  dynamic f2 = 3;\n" | 
|  | "  final f3 = List.empty(growable: true);\n" | 
|  | "  foo() {\n" | 
|  | "    f1 = f1 + f1;\n" | 
|  | "  }\n" | 
|  | "  bar() {\n" | 
|  | "    f2 = null;\n" | 
|  | "    f2 = 3.0;\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runFoo() {\n" | 
|  | "  var a = A();\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    a.foo();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runBar() {\n" | 
|  | "  var a = A();\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    a.bar();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "main() {\n" | 
|  | "  for (int i = 0; i < 100; i++) {\n" | 
|  | "    runFoo();\n" | 
|  | "    runBar();\n" | 
|  | "  }\n" | 
|  | "}\n"; | 
|  | Dart_Handle lib = TestCase::LoadTestScript(script_chars, nullptr); | 
|  | Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr); | 
|  | EXPECT_VALID(result); | 
|  | TransitionNativeToVM transition(thread); | 
|  | Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1")); | 
|  | Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2")); | 
|  | Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3")); | 
|  | const intptr_t no_length = Field::kNoFixedLength; | 
|  | EXPECT_EQ(no_length, f1.guarded_list_length()); | 
|  | EXPECT_EQ(kDoubleCid, f1.guarded_cid()); | 
|  | EXPECT_EQ(false, f1.is_nullable()); | 
|  | EXPECT_EQ(no_length, f2.guarded_list_length()); | 
|  | EXPECT_EQ(kDynamicCid, f2.guarded_cid()); | 
|  | EXPECT_EQ(true, f2.is_nullable()); | 
|  | EXPECT_EQ(no_length, f3.guarded_list_length()); | 
|  | EXPECT_EQ(kGrowableObjectArrayCid, f3.guarded_cid()); | 
|  | EXPECT_EQ(false, f3.is_nullable()); | 
|  | } | 
|  |  | 
|  | TEST_CASE(GuardFieldConstructorTest) { | 
|  | const char* script_chars = | 
|  | "import 'dart:typed_data';\n" | 
|  | "class A {\n" | 
|  | "  var f1 = 3.0;\n" | 
|  | "  dynamic f2 = 3;\n" | 
|  | "  final f3;\n" | 
|  | "  A(x) : f3 = x;\n" | 
|  | "  foo() {\n" | 
|  | "    f1 = f1 + f1;\n" | 
|  | "  }\n" | 
|  | "  bar() {\n" | 
|  | "    f2 = null;\n" | 
|  | "    f2 = 3.0;\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runFoo() {\n" | 
|  | "  var l = new Float32List(5);\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    var a = new A(l);\n" | 
|  | "    a.foo();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runBar() {\n" | 
|  | "  var l = new Float32List(5);\n" | 
|  | "  var a = new A(l);\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    a.bar();\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "main() {\n" | 
|  | "  for (int i = 0; i < 100; i++) {\n" | 
|  | "    runFoo();\n" | 
|  | "    runBar();\n" | 
|  | "  }\n" | 
|  | "}\n"; | 
|  | Dart_Handle lib = TestCase::LoadTestScript(script_chars, nullptr); | 
|  | Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr); | 
|  | EXPECT_VALID(result); | 
|  | TransitionNativeToVM transition(thread); | 
|  | Field& f1 = Field::ZoneHandle(LookupField(lib, "A", "f1")); | 
|  | Field& f2 = Field::ZoneHandle(LookupField(lib, "A", "f2")); | 
|  | Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3")); | 
|  | const intptr_t no_length = Field::kNoFixedLength; | 
|  | EXPECT_EQ(no_length, f1.guarded_list_length()); | 
|  | EXPECT_EQ(kDoubleCid, f1.guarded_cid()); | 
|  | EXPECT_EQ(false, f1.is_nullable()); | 
|  | EXPECT_EQ(no_length, f2.guarded_list_length()); | 
|  | EXPECT_EQ(kDynamicCid, f2.guarded_cid()); | 
|  | EXPECT_EQ(true, f2.is_nullable()); | 
|  | const intptr_t length = 5; | 
|  | EXPECT_EQ(length, f3.guarded_list_length()); | 
|  | EXPECT_EQ(kTypedDataFloat32ArrayCid, f3.guarded_cid()); | 
|  | EXPECT_EQ(false, f3.is_nullable()); | 
|  | } | 
|  |  | 
|  | TEST_CASE(GuardFieldConstructor2Test) { | 
|  | const char* script_chars = | 
|  | "import 'dart:typed_data';\n" | 
|  | "class A {\n" | 
|  | "  final f3;\n" | 
|  | "  A(x) : f3 = x;\n" | 
|  | "  foo() {\n" | 
|  | "  }\n" | 
|  | "  bar() {\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runFoo() {\n" | 
|  | "  var l = new Float32List(5);\n" | 
|  | "  for (int i = 0; i < 2000; i++) {\n" | 
|  | "    var a = new A(l);\n" | 
|  | "  }\n" | 
|  | "}\n" | 
|  | "\n" | 
|  | "runBar() {\n" | 
|  | "  var l = new Float32List(99);\n" | 
|  | "  var a = new A(l);\n" | 
|  | "}\n" | 
|  | "main() {\n" | 
|  | "  for (int i = 0; i < 100; i++) {\n" | 
|  | "    runFoo();\n" | 
|  | "    runBar();\n" | 
|  | "  }\n" | 
|  | "}\n"; | 
|  | Dart_Handle lib = TestCase::LoadTestScript(script_chars, nullptr); | 
|  | Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr); | 
|  | EXPECT_VALID(result); | 
|  | TransitionNativeToVM transition(thread); | 
|  | Field& f3 = Field::ZoneHandle(LookupField(lib, "A", "f3")); | 
|  | const intptr_t no_length = Field::kNoFixedLength; | 
|  | EXPECT_EQ(no_length, f3.guarded_list_length()); | 
|  | EXPECT_EQ(kTypedDataFloat32ArrayCid, f3.guarded_cid()); | 
|  | EXPECT_EQ(false, f3.is_nullable()); | 
|  | } | 
|  |  | 
|  | }  // namespace dart |