Index: runtime/vm/isolate_reload_test.cc |
diff --git a/runtime/vm/isolate_reload_test.cc b/runtime/vm/isolate_reload_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b6d138228589e1d72d085a6dfa8d5edad704eb53 |
--- /dev/null |
+++ b/runtime/vm/isolate_reload_test.cc |
@@ -0,0 +1,1832 @@ |
+// Copyright (c) 2016, 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 "include/dart_api.h" |
+#include "include/dart_tools_api.h" |
+#include "platform/assert.h" |
+#include "vm/globals.h" |
+#include "vm/isolate.h" |
+#include "vm/lockers.h" |
+#include "vm/thread_barrier.h" |
+#include "vm/thread_pool.h" |
+#include "vm/unit_test.h" |
+ |
+namespace dart { |
+ |
+#ifndef PRODUCT |
+ |
+// TODO(johnmccutchan): |
+// - Tests involving generics. |
+ |
+int64_t SimpleInvoke(Dart_Handle lib, const char* method) { |
+ Dart_Handle result = Dart_Invoke(lib, NewString(method), 0, NULL); |
+ EXPECT_VALID(result); |
+ EXPECT(Dart_IsInteger(result)); |
+ int64_t integer_result = 0; |
+ result = Dart_IntegerToInt64(result, &integer_result); |
+ EXPECT_VALID(result); |
+ return integer_result; |
+} |
+ |
+ |
+const char* SimpleInvokeStr(Dart_Handle lib, const char* method) { |
+ Dart_Handle result = Dart_Invoke(lib, NewString(method), 0, NULL); |
+ const char* result_str = NULL; |
+ EXPECT(Dart_IsString(result)); |
+ EXPECT_VALID(Dart_StringToCString(result, &result_str)); |
+ return result_str; |
+} |
+ |
+ |
+Dart_Handle SimpleInvokeError(Dart_Handle lib, const char* method) { |
+ Dart_Handle result = Dart_Invoke(lib, NewString(method), 0, NULL); |
+ EXPECT(Dart_IsError(result)); |
+ return result; |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_FunctionReplacement) { |
+ const char* kScript = |
+ "main() {\n" |
+ " return 4;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(4, SimpleInvoke(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "var _unused;" |
+ "main() {\n" |
+ " return 10;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(10, SimpleInvoke(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_BadClass) { |
+ const char* kScript = |
+ "class Foo {\n" |
+ " final a;\n" |
+ " Foo(this.a);\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo(5);\n" |
+ " return 4;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(4, SimpleInvoke(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "var _unused;" |
+ "class Foo {\n" |
+ " final a kjsdf ksjdf ;\n" |
+ " Foo(this.a);\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo(5);\n" |
+ " return 10;\n" |
+ "}\n"; |
+ |
+ Dart_Handle result = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_ERROR(result, "unexpected token"); |
+ |
+ EXPECT_EQ(4, SimpleInvoke(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_StaticValuePreserved) { |
+ const char* kScript = |
+ "init() => 'old value';\n" |
+ "var value = init();\n" |
+ "main() {\n" |
+ " return 'init()=${init()},value=${value}';\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("init()=old value,value=old value", |
+ SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "var _unused;" |
+ "init() => 'new value';\n" |
+ "var value = init();\n" |
+ "main() {\n" |
+ " return 'init()=${init()},value=${value}';\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("init()=new value,value=old value", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_SavedClosure) { |
+ // Create a closure in main which only exists in the original source. |
+ const char* kScript = |
+ "magic() {\n" |
+ " var x = 'ante';\n" |
+ " return x + 'diluvian';\n" |
+ "}\n" |
+ "var closure;\n" |
+ "main() {\n" |
+ " closure = () { return magic().toString() + '!'; };\n" |
+ " return closure();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("antediluvian!", SimpleInvokeStr(lib, "main")); |
+ |
+ // Remove the original closure from the source code. The closure is |
+ // able to be recompiled because its source is preserved in a |
+ // special patch class. |
+ const char* kReloadScript = |
+ "magic() {\n" |
+ " return 'postapocalyptic';\n" |
+ "}\n" |
+ "var closure;\n" |
+ "main() {\n" |
+ " return closure();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("postapocalyptic!", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_TopLevelFieldAdded) { |
+ const char* kScript = |
+ "var value1 = 10;\n" |
+ "main() {\n" |
+ " return 'value1=${value1}';\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("value1=10", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "var value1 = 10;\n" |
+ "var value2 = 20;\n" |
+ "main() {\n" |
+ " return 'value1=${value1},value2=${value2}';\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("value1=10,value2=20", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_ClassFieldAdded) { |
+ const char* kScript = |
+ "class Foo {\n" |
+ " var x;\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo();\n" |
+ " return 44;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(44, SimpleInvoke(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class Foo {\n" |
+ " var x;\n" |
+ " var y;\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo();\n" |
+ " return 44;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_ERROR(lib, "Number of instance fields changed"); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_ClassFieldRemoved) { |
+ const char* kScript = |
+ "class Foo {\n" |
+ " var x;\n" |
+ " var y;\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo();\n" |
+ " return 44;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(44, SimpleInvoke(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class Foo {\n" |
+ " var x;\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo();\n" |
+ " return 44;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_ERROR(lib, "Number of instance fields changed"); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_ClassAdded) { |
+ const char* kScript = |
+ "main() {\n" |
+ " return 'hello';\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("hello", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "var _unused;" |
+ "class A {\n" |
+ " toString() => 'hello from A';\n" |
+ "}\n" |
+ "main() {\n" |
+ " return new A().toString();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("hello from A", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_LibraryImportAdded) { |
+ const char* kScript = |
+ "main() {\n" |
+ " return max(3, 4);\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_ERROR(SimpleInvokeError(lib, "main"), "max");; |
+ |
+ const char* kReloadScript = |
+ "import 'dart:math';\n" |
+ "main() {\n" |
+ " return max(3, 4);\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(4, SimpleInvoke(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_LibraryImportRemoved) { |
+ const char* kScript = |
+ "import 'dart:math';\n" |
+ "main() {\n" |
+ " return max(3, 4);\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(4, SimpleInvoke(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "main() {\n" |
+ " return max(3, 4);\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_ERROR(SimpleInvokeError(lib, "main"), "max");; |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_LibraryDebuggable) { |
+ const char* kScript = |
+ "main() {\n" |
+ " return 1;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ // The library is by default debuggable. Make it not debuggable. |
+ intptr_t lib_id = -1; |
+ bool debuggable = false; |
+ EXPECT_VALID(Dart_LibraryId(lib, &lib_id)); |
+ EXPECT_VALID(Dart_GetLibraryDebuggable(lib_id, &debuggable)); |
+ EXPECT_EQ(true, debuggable); |
+ EXPECT_VALID(Dart_SetLibraryDebuggable(lib_id, false)); |
+ EXPECT_VALID(Dart_GetLibraryDebuggable(lib_id, &debuggable)); |
+ EXPECT_EQ(false, debuggable); |
+ |
+ EXPECT_EQ(1, SimpleInvoke(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "main() {\n" |
+ " return 2;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(2, SimpleInvoke(lib, "main")); |
+ |
+ // Library debuggability is preserved. |
+ intptr_t new_lib_id = -1; |
+ EXPECT_VALID(Dart_LibraryId(lib, &new_lib_id)); |
+ EXPECT_VALID(Dart_GetLibraryDebuggable(new_lib_id, &debuggable)); |
+ EXPECT_EQ(false, debuggable); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_ImplicitConstructorChanged) { |
+ // Note that we are checking that the value 20 gets cleared from the |
+ // compile-time constants cache. To make this test work, "20" and |
+ // "10" need to be at the same token position. |
+ const char* kScript = |
+ "class A {\n" |
+ " int field = 20;\n" |
+ "}\n" |
+ "var savedA = new A();\n" |
+ "main() {\n" |
+ " var newA = new A();\n" |
+ " return 'saved:${savedA.field} new:${newA.field}';\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("saved:20 new:20", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class A {\n" |
+ " int field = 10;\n" |
+ "}\n" |
+ "var savedA = new A();\n" |
+ "main() {\n" |
+ " var newA = new A();\n" |
+ " return 'saved:${savedA.field} new:${newA.field}';\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("saved:20 new:10", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_ConstructorChanged) { |
+ const char* kScript = |
+ "class A {\n" |
+ " int field;\n" |
+ " A() { field = 20; }\n" |
+ "}\n" |
+ "var savedA = new A();\n" |
+ "main() {\n" |
+ " var newA = new A();\n" |
+ " return 'saved:${savedA.field} new:${newA.field}';\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("saved:20 new:20", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "var _unused;" |
+ "class A {\n" |
+ " int field;\n" |
+ " A() { field = 10; }\n" |
+ "}\n" |
+ "var savedA = new A();\n" |
+ "main() {\n" |
+ " var newA = new A();\n" |
+ " return 'saved:${savedA.field} new:${newA.field}';\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("saved:20 new:10", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_SuperClassChanged) { |
+ const char* kScript = |
+ "class A {\n" |
+ "}\n" |
+ "class B extends A {\n" |
+ "}\n" |
+ "var list = [ new A(), new B() ];\n" |
+ "main() {\n" |
+ " return (list.map((x) => '${x is A}/${x is B}')).toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("(true/false, true/true)", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "var _unused;" |
+ "class B{\n" |
+ "}\n" |
+ "class A extends B {\n" |
+ "}\n" |
+ "var list = [ new A(), new B() ];\n" |
+ "main() {\n" |
+ " return (list.map((x) => '${x is A}/${x is B}')).toString();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("(true/true, false/true)", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_Generics) { |
+ // Reload a program with generics without changing the source. We |
+ // do this to produce duplication TypeArguments and make sure that |
+ // the system doesn't die. |
+ const char* kScript = |
+ "class A {\n" |
+ "}\n" |
+ "class B<T extends A> {\n" |
+ "}\n" |
+ "main() {\n" |
+ " return new B<A>().toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Instance of 'B<A>'", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class A {\n" |
+ "}\n" |
+ "class B<T extends A> {\n" |
+ "}\n" |
+ "main() {\n" |
+ " return new B<A>().toString();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Instance of 'B<A>'", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_MixinChanged) { |
+ const char* kScript = |
+ "class Mixin1 {\n" |
+ " var field = 'mixin1';\n" |
+ " func() => 'mixin1';\n" |
+ "}\n" |
+ "class B extends Object with Mixin1 {\n" |
+ "}\n" |
+ "var saved = new B();\n" |
+ "main() {\n" |
+ " return 'saved:field=${saved.field},func=${saved.func()}';\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("saved:field=mixin1,func=mixin1", |
+ SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class Mixin2 {\n" |
+ " var field = 'mixin2';\n" |
+ " func() => 'mixin2';\n" |
+ "}\n" |
+ "class B extends Object with Mixin2 {\n" |
+ "}\n" |
+ "var saved = new B();\n" |
+ "main() {\n" |
+ " var newer = new B();\n" |
+ " return 'saved:field=${saved.field},func=${saved.func()} '\n" |
+ " 'newer:field=${newer.field},func=${newer.func()}';\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ // The saved instance of B retains its old field value from mixin1, |
+ // but it gets the new implementation of func from mixin2. |
+ EXPECT_STREQ("saved:field=mixin1,func=mixin2 " |
+ "newer:field=mixin2,func=mixin2", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_ComplexInheritanceChange) { |
+ const char* kScript = |
+ "class A {\n" |
+ " String name;\n" |
+ " A(this.name);\n" |
+ "}\n" |
+ "class B extends A {\n" |
+ " B(name) : super(name);\n" |
+ "}\n" |
+ "class C extends B {\n" |
+ " C(name) : super(name);\n" |
+ "}\n" |
+ "var list = [ new A('a'), new B('b'), new C('c') ];\n" |
+ "main() {\n" |
+ " return (list.map((x) {\n" |
+ " return '${x.name} is A(${x is A})/ B(${x is B})/ C(${x is C})';\n" |
+ " })).toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("(a is A(true)/ B(false)/ C(false)," |
+ " b is A(true)/ B(true)/ C(false)," |
+ " c is A(true)/ B(true)/ C(true))", |
+ SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class C {\n" |
+ " String name;\n" |
+ " C(this.name);\n" |
+ "}\n" |
+ "class X extends C {\n" |
+ " X(name) : super(name);\n" |
+ "}\n" |
+ "class A extends X {\n" |
+ " A(name) : super(name);\n" |
+ "}\n" |
+ "var list;\n" |
+ "main() {\n" |
+ " list.add(new X('x'));\n" |
+ " return (list.map((x) {\n" |
+ " return '${x.name} is A(${x is A})/ C(${x is C})/ X(${x is X})';\n" |
+ " })).toString();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("(a is A(true)/ C(true)/ X(true)," |
+ " b is A(true)/ C(true)/ X(true)," // still extends A... |
+ " c is A(false)/ C(true)/ X(false)," |
+ " x is A(false)/ C(true)/ X(true))", |
+ SimpleInvokeStr(lib, "main")); |
+ |
+ // Revive the class B and make sure all allocated instances take |
+ // their place in the inheritance hierarchy. |
+ const char* kReloadScript2 = |
+ "class X {\n" |
+ " String name;\n" |
+ " X(this.name);\n" |
+ "}\n" |
+ "class A extends X{\n" |
+ " A(name) : super(name);\n" |
+ "}\n" |
+ "class B extends X {\n" |
+ " B(name) : super(name);\n" |
+ "}\n" |
+ "class C extends A {\n" |
+ " C(name) : super(name);\n" |
+ "}\n" |
+ "var list;\n" |
+ "main() {\n" |
+ " return (list.map((x) {\n" |
+ " return '${x.name} is '\n" |
+ " 'A(${x is A})/ B(${x is B})/ C(${x is C})/ X(${x is X})';\n" |
+ " })).toString();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript2); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("(a is A(true)/ B(false)/ C(false)/ X(true)," |
+ " b is A(false)/ B(true)/ C(false)/ X(true)," |
+ " c is A(true)/ B(false)/ C(true)/ X(true)," |
+ " x is A(false)/ B(false)/ C(false)/ X(true))", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_LiveStack) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "helper() => 7;\n" |
+ "alpha() { var x = helper(); reloadTest(); return x + helper(); }\n" |
+ "foo() => alpha();\n" |
+ "bar() => foo();\n" |
+ "main() {\n" |
+ " return bar();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ const char* kReloadScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "helper() => 100;\n" |
+ "alpha() => 5 + helper();\n" |
+ "foo() => alpha();\n" |
+ "bar() => foo();\n" |
+ "main() {\n" |
+ " return bar();\n" |
+ "}\n"; |
+ |
+ TestCase::SetReloadTestScript(kReloadScript); |
+ |
+ EXPECT_EQ(107, SimpleInvoke(lib, "main")); |
+ |
+ lib = TestCase::GetReloadErrorOrRootLibrary(); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(105, SimpleInvoke(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_LibraryLookup) { |
+ const char* kScript = |
+ "main() {\n" |
+ " return importedFunc();\n" |
+ "}\n"; |
+ |
+ Dart_Handle result; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_ERROR(SimpleInvokeError(lib, "main"), "importedFunc"); |
+ |
+ // Fail to find 'importable_test_lib' in the isolate. |
+ result = Dart_LookupLibrary(NewString("importable_test_lib")); |
+ EXPECT(Dart_IsError(result)); |
+ |
+ const char* kReloadScript = |
+ "import 'importable_test_lib';\n" |
+ "main() {\n" |
+ " return importedFunc();\n" |
+ "}\n"; |
+ |
+ // Reload and add 'importable_test_lib' to isolate. |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("a", SimpleInvokeStr(lib, "main")); |
+ |
+ // Find 'importable_test_lib' in the isolate. |
+ result = Dart_LookupLibrary(NewString("importable_test_lib")); |
+ EXPECT(Dart_IsLibrary(result)); |
+ |
+ // Reload and remove 'dart:math' from isolate. |
+ lib = TestCase::ReloadTestScript(kScript); |
+ EXPECT_VALID(lib); |
+ |
+ // Fail to find 'importable_test_lib' in the isolate. |
+ result = Dart_LookupLibrary(NewString("importable_test_lib")); |
+ EXPECT(Dart_IsError(result)); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_LibraryHide) { |
+ // Import 'importable_test_lib' with importedFunc hidden. Will result in an |
+ // error. |
+ const char* kScript = |
+ "import 'importable_test_lib' hide importedFunc;\n" |
+ "main() {\n" |
+ " return importedFunc();\n" |
+ "}\n"; |
+ |
+ // Dart_Handle result; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_ERROR(SimpleInvokeError(lib, "main"), "importedFunc"); |
+ |
+ // Import 'importable_test_lib'. |
+ const char* kReloadScript = |
+ "import 'importable_test_lib';\n" |
+ "main() {\n" |
+ " return importedFunc();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("a", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_LibraryShow) { |
+ // Import 'importable_test_lib' with importedIntFunc visible. Will result in |
+ // an error when 'main' is invoked. |
+ const char* kScript = |
+ "import 'importable_test_lib' show importedIntFunc;\n" |
+ "main() {\n" |
+ " return importedFunc();\n" |
+ "}\n" |
+ "mainInt() {\n" |
+ " return importedIntFunc();\n" |
+ "}\n"; |
+ |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ // Works. |
+ EXPECT_EQ(4, SimpleInvoke(lib, "mainInt")); |
+ // Results in an error. |
+ EXPECT_ERROR(SimpleInvokeError(lib, "main"), "importedFunc"); |
+ |
+ // Import 'importable_test_lib' with importedFunc visible. Will result in |
+ // an error when 'mainInt' is invoked. |
+ const char* kReloadScript = |
+ "import 'importable_test_lib' show importedFunc;\n" |
+ "main() {\n" |
+ " return importedFunc();\n" |
+ "}\n" |
+ "mainInt() {\n" |
+ " return importedIntFunc();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ // Works. |
+ EXPECT_STREQ("a", SimpleInvokeStr(lib, "main")); |
+ // Results in an error. |
+ EXPECT_ERROR(SimpleInvokeError(lib, "mainInt"), "importedIntFunc"); |
+} |
+ |
+ |
+// Verifies that we clear the ICs for the functions live on the stack in a way |
+// that is compatible with the fast path smi stubs. |
+TEST_CASE(IsolateReload_SmiFastPathStubs) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "import 'importable_test_lib' show importedIntFunc;\n" |
+ "main() {\n" |
+ " var x = importedIntFunc();\n" |
+ " var y = importedIntFunc();\n" |
+ " reloadTest();\n" |
+ " return x + y;\n" |
+ "}\n"; |
+ |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ // Identity reload. |
+ TestCase::SetReloadTestScript(kScript); |
+ |
+ EXPECT_EQ(8, SimpleInvoke(lib, "main")); |
+} |
+ |
+ |
+// Verifies that we assign the correct patch classes for imported |
+// mixins when we reload. |
+TEST_CASE(IsolateReload_ImportedMixinFunction) { |
+ const char* kScript = |
+ "import 'importable_test_lib' show ImportedMixin;\n" |
+ "class A extends Object with ImportedMixin {\n" |
+ "}" |
+ "var func = new A().mixinFunc;\n" |
+ "main() {\n" |
+ " return func();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("mixin", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "import 'importable_test_lib' show ImportedMixin;\n" |
+ "class A extends Object with ImportedMixin {\n" |
+ "}" |
+ "var func;\n" |
+ "main() {\n" |
+ " return func();\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("mixin", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_TopLevelParseError) { |
+ const char* kScript = |
+ "main() {\n" |
+ " return 4;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ(4, SimpleInvoke(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "kjsadkfjaksldfjklsadf;\n" |
+ "main() {\n" |
+ " return 4;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_ERROR(lib, "unexpected token"); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_PendingUnqualifiedCall_StaticToInstance) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " static foo() => 'static';\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " return new C().test();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ const char* kReloadScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " foo() => 'instance';\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " return new C().test();\n" |
+ "}\n"; |
+ |
+ TestCase::SetReloadTestScript(kReloadScript); |
+ |
+ EXPECT_EQ("instance", SimpleInvokeStr(lib, "main")); |
+ |
+ lib = TestCase::GetReloadErrorOrRootLibrary(); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ("instance", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_PendingUnqualifiedCall_InstanceToStatic) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " foo() => 'instance';\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " return new C().test();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ const char* kReloadScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " static foo() => 'static';\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " return new C().test();\n" |
+ "}\n"; |
+ |
+ TestCase::SetReloadTestScript(kReloadScript); |
+ |
+ EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); |
+ |
+ lib = TestCase::GetReloadErrorOrRootLibrary(); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_PendingConstructorCall_AbstractToConcrete) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "abstract class Foo {}\n" |
+ "class C {\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return new Foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " new C().test();\n" |
+ " return 'okay';\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ const char* kReloadScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class Foo {}\n" |
+ "class C {\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return new Foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " new C().test();\n" |
+ " return 'okay';\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ TestCase::SetReloadTestScript(kReloadScript); |
+ |
+ EXPECT_EQ("okay", SimpleInvokeStr(lib, "main")); |
+ |
+ lib = TestCase::GetReloadErrorOrRootLibrary(); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ("okay", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_PendingConstructorCall_ConcreteToAbstract) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class Foo {}\n" |
+ "class C {\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return new Foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " new C().test();\n" |
+ " return 'okay';\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ const char* kReloadScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "abstract class Foo {}\n" |
+ "class C {\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return new Foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " new C().test();\n" |
+ " return 'okay';\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ TestCase::SetReloadTestScript(kReloadScript); |
+ |
+ EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); |
+ |
+ lib = TestCase::GetReloadErrorOrRootLibrary(); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_PendingStaticCall_DefinedToNSM) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " static foo() => 'static'\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return C.foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " return new C().test();\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ const char* kReloadScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return C.foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " return new C().test();\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ TestCase::SetReloadTestScript(kReloadScript); |
+ |
+ EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); |
+ |
+ lib = TestCase::GetReloadErrorOrRootLibrary(); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_PendingStaticCall_NSMToDefined) { |
+ const char* kScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return C.foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " return new C().test();\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ const char* kReloadScript = |
+ "import 'isolate_reload_test_helper';\n" |
+ "class C {\n" |
+ " static foo() => 'static'\n" |
+ " test() {\n" |
+ " reloadTest();\n" |
+ " return C.foo();\n" |
+ " }\n" |
+ "}\n" |
+ "main() {\n" |
+ " try {\n" |
+ " return new C().test();\n" |
+ " } catch (e) {\n" |
+ " return 'exception';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ TestCase::SetReloadTestScript(kReloadScript); |
+ |
+ EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); |
+ |
+ lib = TestCase::GetReloadErrorOrRootLibrary(); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumEquality) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " x = Fruit.Banana;\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " if (x == Fruit.Banana) {\n" |
+ " return 'yes';\n" |
+ " } else {\n" |
+ " return 'no';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumIdentical) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " x = Fruit.Banana;\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " if (identical(x, Fruit.Banana)) {\n" |
+ " return 'yes';\n" |
+ " } else {\n" |
+ " return 'no';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumReorderIdentical) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " x = Fruit.Banana;\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Banana,\n" |
+ " Apple,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " if (identical(x, Fruit.Banana)) {\n" |
+ " return 'yes';\n" |
+ " } else {\n" |
+ " return 'no';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumAddition) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Cantalope,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " String r = '${Fruit.Apple.index}/${Fruit.Apple} ';\n" |
+ " r += '${Fruit.Cantalope.index}/${Fruit.Cantalope} ';\n" |
+ " r += '${Fruit.Banana.index}/${Fruit.Banana}';\n" |
+ " return r;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("0/Fruit.Apple 1/Fruit.Cantalope 2/Fruit.Banana", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumToNotEnum) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple\n" |
+ "}\n" |
+ "main() {\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class Fruit {\n" |
+ " final int zero = 0;\n" |
+ "}\n" |
+ "main() {\n" |
+ "}\n"; |
+ |
+ Dart_Handle result = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_ERROR(result, "Enum class cannot be redefined to be a non-enum class"); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_NotEnumToEnum) { |
+ const char* kScript = |
+ "class Fruit {\n" |
+ " final int zero = 0;\n" |
+ "}\n" |
+ "main() {\n" |
+ " return 'yes';\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Apple\n" |
+ "}\n" |
+ "main() {\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle result = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_ERROR(result, "Class cannot be redefined to be a enum class"); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumDelete) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ " Cantalope,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ // Delete 'Cantalope'. |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " String r = '${Fruit.Apple.index}/${Fruit.Apple} ';\n" |
+ " r += '${Fruit.Banana.index}/${Fruit.Banana} ';\n" |
+ " r += '${Fruit.Cantalope.index}/${Fruit.Cantalope}';\n" |
+ " return r;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("0/Fruit.Apple 1/Fruit.Banana 2/Fruit.Cantalope", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumComplex) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ " Cantalope,\n" |
+ "}\n" |
+ "var x;\n" |
+ "var y;\n" |
+ "var z;\n" |
+ "main() {\n" |
+ " x = Fruit.Apple;\n" |
+ " y = Fruit.Banana;\n" |
+ " z = Fruit.Cantalope;\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ // Delete 'Cantalope'. Add 'Dragon'. Move 'Apple' and 'Banana'. |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Dragon,\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "var y;\n" |
+ "var z;\n" |
+ "main() {\n" |
+ " String r = '';\n" |
+ " r += '${identical(x, Fruit.Apple)}';\n" |
+ " r += ' ${identical(y, Fruit.Banana)}';\n" |
+ " r += ' ${identical(z, Fruit.Cantalope)}';\n" |
+ " r += ' ${Fruit.Dragon}';\n" |
+ " return r;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("true true true Fruit.Dragon", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumValuesArray) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Cantalope,\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " x = Fruit.Cantalope;\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ // Delete 'Cantalope'. |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Banana,\n" |
+ " Apple\n" |
+ "}\n" |
+ "var x;\n" |
+ "bool identityCheck(Fruit f) {\n" |
+ " return identical(Fruit.values[f.index], f);\n" |
+ "}\n" |
+ "main() {\n" |
+ " if ((x is Fruit) && identical(x, Fruit.Cantalope)) {\n" |
+ " String r = '${identityCheck(Fruit.Apple)}';\n" |
+ " r += ' ${identityCheck(Fruit.Banana)}';\n" |
+ " r += ' ${identityCheck(Fruit.Cantalope)}';\n" |
+ " r += ' ${identityCheck(x)}';\n" |
+ " return r;\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("true true true true", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumIdentityReload) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ " Cantalope,\n" |
+ "}\n" |
+ "var x;\n" |
+ "var y;\n" |
+ "var z;\n" |
+ "var w;\n" |
+ "main() {\n" |
+ " x = { Fruit.Apple: Fruit.Apple.index,\n" |
+ " Fruit.Banana: Fruit.Banana.index,\n" |
+ " Fruit.Cantalope: Fruit.Cantalope.index};\n" |
+ " y = Fruit.Apple;\n" |
+ " z = Fruit.Banana;\n" |
+ " w = Fruit.Cantalope;\n" |
+ " return Fruit.Apple.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ " Cantalope,\n" |
+ "}\n" |
+ "var x;\n" |
+ "var y;\n" |
+ "var z;\n" |
+ "var w;\n" |
+ "bool identityCheck(Fruit f, int index) {\n" |
+ " return identical(Fruit.values[index], f);\n" |
+ "}\n" |
+ "main() {\n" |
+ " String r = '';\n" |
+ " x.forEach((key, value) {\n" |
+ " r += '${identityCheck(key, value)} ';\n" |
+ " });\n" |
+ " r += '${x[Fruit.Apple] == Fruit.Apple.index} ';\n" |
+ " r += '${x[Fruit.Banana] == Fruit.Banana.index} ';\n" |
+ " r += '${x[Fruit.Cantalope] == Fruit.Cantalope.index} ';\n" |
+ " r += '${identical(y, Fruit.values[x[Fruit.Apple]])} ';\n" |
+ " r += '${identical(z, Fruit.values[x[Fruit.Banana]])} ';\n" |
+ " r += '${identical(w, Fruit.values[x[Fruit.Cantalope]])} ';\n" |
+ " return r;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("true true true true true true true true true ", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_ConstantIdentical) { |
+ const char* kScript = |
+ "class Fruit {\n" |
+ " final String name;\n" |
+ " const Fruit(this.name);\n" |
+ " String toString() => name;\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " x = const Fruit('Pear');\n" |
+ " return x.toString();\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Pear", SimpleInvokeStr(lib, "main")); |
+ |
+ const char* kReloadScript = |
+ "class Fruit {\n" |
+ " final String name;\n" |
+ " const Fruit(this.name);\n" |
+ " String toString() => name;\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " if (identical(x, const Fruit('Pear'))) {\n" |
+ " return 'yes';\n" |
+ " } else {\n" |
+ " return 'no';\n" |
+ " }\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_EnumValuesToString) { |
+ const char* kScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Banana,\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " String r = '';\n" |
+ " r += Fruit.Apple.toString();\n" |
+ " r += ' ';\n" |
+ " r += Fruit.Banana.toString();\n" |
+ " return r;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple Fruit.Banana", SimpleInvokeStr(lib, "main")); |
+ |
+ // Insert 'Cantalope'. |
+ |
+ const char* kReloadScript = |
+ "enum Fruit {\n" |
+ " Apple,\n" |
+ " Cantalope,\n" |
+ " Banana\n" |
+ "}\n" |
+ "var x;\n" |
+ "main() {\n" |
+ " String r = '';\n" |
+ " r += Fruit.Apple.toString();\n" |
+ " r += ' ';\n" |
+ " r += Fruit.Cantalope.toString();\n" |
+ " r += ' ';\n" |
+ " r += Fruit.Banana.toString();\n" |
+ " return r;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ |
+ EXPECT_STREQ("Fruit.Apple Fruit.Cantalope Fruit.Banana", |
+ SimpleInvokeStr(lib, "main")); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_DirectSubclasses_Success) { |
+ Object& new_subclass = Object::Handle(); |
+ String& name = String::Handle(); |
+ |
+ // Lookup the Iterator class by name from the dart core library. |
+ ObjectStore* object_store = Isolate::Current()->object_store(); |
+ const Library& core_lib = Library::Handle(object_store->core_library()); |
+ name = String::New("Iterator"); |
+ const Class& iterator_cls = Class::Handle(core_lib.LookupClass(name)); |
+ |
+ // Keep track of how many subclasses an Iterator has. |
+ const GrowableObjectArray& subclasses = |
+ GrowableObjectArray::Handle(iterator_cls.direct_subclasses()); |
+ intptr_t saved_subclass_count = subclasses.Length(); |
+ |
+ const char* kScript = |
+ "class AIterator extends Iterator {\n" |
+ "}\n" |
+ "main() {\n" |
+ " return 1;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ EXPECT_EQ(1, SimpleInvoke(lib, "main")); |
+ |
+ // Iterator has one non-core subclass. |
+ EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); |
+ |
+ // The new subclass is named AIterator. |
+ new_subclass = subclasses.At(subclasses.Length() - 1); |
+ name = Class::Cast(new_subclass).Name(); |
+ EXPECT_STREQ("AIterator", name.ToCString()); |
+ |
+ const char* kReloadScript = |
+ "class AIterator {\n" |
+ "}\n" |
+ "class BIterator extends Iterator {\n" |
+ "}\n" |
+ "main() {\n" |
+ " return 2;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ EXPECT_EQ(2, SimpleInvoke(lib, "main")); |
+ |
+ // Iterator still has only one non-core subclass (AIterator is gone). |
+ EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); |
+ |
+ // The new subclass is named BIterator. |
+ new_subclass = subclasses.At(subclasses.Length() - 1); |
+ name = Class::Cast(new_subclass).Name(); |
+ EXPECT_STREQ("BIterator", name.ToCString()); |
+} |
+ |
+ |
+TEST_CASE(IsolateReload_DirectSubclasses_GhostSubclass) { |
+ Object& new_subclass = Object::Handle(); |
+ String& name = String::Handle(); |
+ |
+ // Lookup the Iterator class by name from the dart core library. |
+ ObjectStore* object_store = Isolate::Current()->object_store(); |
+ const Library& core_lib = Library::Handle(object_store->core_library()); |
+ name = String::New("Iterator"); |
+ const Class& iterator_cls = Class::Handle(core_lib.LookupClass(name)); |
+ |
+ // Keep track of how many subclasses an Iterator has. |
+ const GrowableObjectArray& subclasses = |
+ GrowableObjectArray::Handle(iterator_cls.direct_subclasses()); |
+ intptr_t saved_subclass_count = subclasses.Length(); |
+ |
+ const char* kScript = |
+ "class AIterator extends Iterator {\n" |
+ "}\n" |
+ "main() {\n" |
+ " return 1;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ EXPECT_EQ(1, SimpleInvoke(lib, "main")); |
+ |
+ // Iterator has one new subclass. |
+ EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); |
+ |
+ // The new subclass is named AIterator. |
+ new_subclass = subclasses.At(subclasses.Length() - 1); |
+ name = Class::Cast(new_subclass).Name(); |
+ EXPECT_STREQ("AIterator", name.ToCString()); |
+ |
+ const char* kReloadScript = |
+ "class BIterator extends Iterator {\n" |
+ "}\n" |
+ "main() {\n" |
+ " return 2;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_VALID(lib); |
+ EXPECT_EQ(2, SimpleInvoke(lib, "main")); |
+ |
+ // Iterator has two non-core subclasses. |
+ EXPECT_EQ(saved_subclass_count + 2, subclasses.Length()); |
+ |
+ // The non-core subclasses are AIterator and BIterator. |
+ new_subclass = subclasses.At(subclasses.Length() - 2); |
+ name = Class::Cast(new_subclass).Name(); |
+ EXPECT_STREQ("AIterator", name.ToCString()); |
+ |
+ new_subclass = subclasses.At(subclasses.Length() - 1); |
+ name = Class::Cast(new_subclass).Name(); |
+ EXPECT_STREQ("BIterator", name.ToCString()); |
+} |
+ |
+ |
+// Make sure that we restore the direct subclass info when we revert. |
+TEST_CASE(IsolateReload_DirectSubclasses_Failure) { |
+ Object& new_subclass = Object::Handle(); |
+ String& name = String::Handle(); |
+ |
+ // Lookup the Iterator class by name from the dart core library. |
+ ObjectStore* object_store = Isolate::Current()->object_store(); |
+ const Library& core_lib = Library::Handle(object_store->core_library()); |
+ name = String::New("Iterator"); |
+ const Class& iterator_cls = Class::Handle(core_lib.LookupClass(name)); |
+ |
+ // Keep track of how many subclasses an Iterator has. |
+ const GrowableObjectArray& subclasses = |
+ GrowableObjectArray::Handle(iterator_cls.direct_subclasses()); |
+ intptr_t saved_subclass_count = subclasses.Length(); |
+ |
+ const char* kScript = |
+ "class AIterator extends Iterator {\n" |
+ "}\n" |
+ "class Foo {\n" |
+ " final a;\n" |
+ " Foo(this.a);\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo(5);\n" // Force Foo to be finalized. |
+ " return 1;\n" |
+ "}\n"; |
+ |
+ Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
+ EXPECT_VALID(lib); |
+ EXPECT_EQ(1, SimpleInvoke(lib, "main")); |
+ |
+ // Iterator has one non-core subclass... |
+ EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); |
+ |
+ // ... and the non-core subclass is named AIterator. |
+ new_subclass = subclasses.At(subclasses.Length() - 1); |
+ name = Class::Cast(new_subclass).Name(); |
+ EXPECT_STREQ("AIterator", name.ToCString()); |
+ |
+ // Attempt to reload with a bogus script. |
+ const char* kReloadScript = |
+ "class BIterator extends Iterator {\n" |
+ "}\n" |
+ "class Foo {\n" |
+ " final a kjsdf ksjdf ;\n" // When we refinalize, we get an error. |
+ " Foo(this.a);\n" |
+ "}\n" |
+ "main() {\n" |
+ " new Foo(5);\n" |
+ " return 2;\n" |
+ "}\n"; |
+ |
+ lib = TestCase::ReloadTestScript(kReloadScript); |
+ EXPECT_ERROR(lib, "unexpected token"); |
+ |
+ // If we don't clean up the subclasses, we would find BIterator in |
+ // the list of subclasses, which would be bad. Make sure that |
+ // Iterator still has only one non-core subclass... |
+ EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); |
+ |
+ // ...and the non-core subclass is still named AIterator. |
+ new_subclass = subclasses.At(subclasses.Length() - 1); |
+ name = Class::Cast(new_subclass).Name(); |
+ EXPECT_STREQ("AIterator", name.ToCString()); |
+} |
+ |
+#endif // !PRODUCT |
+ |
+} // namespace dart |