OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #include "include/dart_api.h" | |
6 #include "include/dart_tools_api.h" | |
7 #include "platform/assert.h" | |
8 #include "vm/globals.h" | |
9 #include "vm/isolate.h" | |
10 #include "vm/lockers.h" | |
11 #include "vm/thread_barrier.h" | |
12 #include "vm/thread_pool.h" | |
13 #include "vm/unit_test.h" | |
14 | |
15 namespace dart { | |
16 | |
17 #ifndef PRODUCT | |
18 | |
19 int64_t SimpleInvoke(Dart_Handle lib, const char* method) { | |
20 Dart_Handle result = Dart_Invoke(lib, NewString(method), 0, NULL); | |
21 EXPECT_VALID(result); | |
22 EXPECT(Dart_IsInteger(result)); | |
23 int64_t integer_result = 0; | |
24 result = Dart_IntegerToInt64(result, &integer_result); | |
25 EXPECT_VALID(result); | |
26 return integer_result; | |
27 } | |
28 | |
29 | |
30 const char* SimpleInvokeStr(Dart_Handle lib, const char* method) { | |
31 Dart_Handle result = Dart_Invoke(lib, NewString(method), 0, NULL); | |
32 const char* result_str = NULL; | |
33 EXPECT(Dart_IsString(result)); | |
34 EXPECT_VALID(Dart_StringToCString(result, &result_str)); | |
35 return result_str; | |
36 } | |
37 | |
38 | |
39 Dart_Handle SimpleInvokeError(Dart_Handle lib, const char* method) { | |
40 Dart_Handle result = Dart_Invoke(lib, NewString(method), 0, NULL); | |
41 EXPECT(Dart_IsError(result)); | |
42 return result; | |
43 } | |
44 | |
45 | |
46 TEST_CASE(IsolateReload_FunctionReplacement) { | |
47 const char* kScript = | |
48 "main() {\n" | |
49 " return 4;\n" | |
50 "}\n"; | |
51 | |
52 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
53 EXPECT_VALID(lib); | |
54 | |
55 EXPECT_EQ(4, SimpleInvoke(lib, "main")); | |
56 | |
57 const char* kReloadScript = | |
58 "var _unused;" | |
59 "main() {\n" | |
60 " return 10;\n" | |
61 "}\n"; | |
62 | |
63 lib = TestCase::ReloadTestScript(kReloadScript); | |
64 EXPECT_VALID(lib); | |
65 | |
66 EXPECT_EQ(10, SimpleInvoke(lib, "main")); | |
67 } | |
68 | |
69 | |
70 TEST_CASE(IsolateReload_BadClass) { | |
71 const char* kScript = | |
72 "class Foo {\n" | |
73 " final a;\n" | |
74 " Foo(this.a);\n" | |
75 "}\n" | |
76 "main() {\n" | |
77 " new Foo(5);\n" | |
78 " return 4;\n" | |
79 "}\n"; | |
80 | |
81 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
82 EXPECT_VALID(lib); | |
83 | |
84 EXPECT_EQ(4, SimpleInvoke(lib, "main")); | |
85 | |
86 const char* kReloadScript = | |
87 "var _unused;" | |
88 "class Foo {\n" | |
89 " final a kjsdf ksjdf ;\n" | |
90 " Foo(this.a);\n" | |
91 "}\n" | |
92 "main() {\n" | |
93 " new Foo(5);\n" | |
94 " return 10;\n" | |
95 "}\n"; | |
96 | |
97 Dart_Handle result = TestCase::ReloadTestScript(kReloadScript); | |
98 EXPECT_ERROR(result, "unexpected token"); | |
99 | |
100 EXPECT_EQ(4, SimpleInvoke(lib, "main")); | |
101 } | |
102 | |
103 | |
104 TEST_CASE(IsolateReload_StaticValuePreserved) { | |
105 const char* kScript = | |
106 "init() => 'old value';\n" | |
107 "var value = init();\n" | |
108 "main() {\n" | |
109 " return 'init()=${init()},value=${value}';\n" | |
110 "}\n"; | |
111 | |
112 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
113 EXPECT_VALID(lib); | |
114 | |
115 EXPECT_STREQ("init()=old value,value=old value", | |
116 SimpleInvokeStr(lib, "main")); | |
117 | |
118 const char* kReloadScript = | |
119 "var _unused;" | |
120 "init() => 'new value';\n" | |
121 "var value = init();\n" | |
122 "main() {\n" | |
123 " return 'init()=${init()},value=${value}';\n" | |
124 "}\n"; | |
125 | |
126 lib = TestCase::ReloadTestScript(kReloadScript); | |
127 EXPECT_VALID(lib); | |
128 | |
129 EXPECT_STREQ("init()=new value,value=old value", | |
130 SimpleInvokeStr(lib, "main")); | |
131 } | |
132 | |
133 | |
134 TEST_CASE(IsolateReload_SavedClosure) { | |
135 // Create a closure in main which only exists in the original source. | |
136 const char* kScript = | |
137 "magic() {\n" | |
138 " var x = 'ante';\n" | |
139 " return x + 'diluvian';\n" | |
140 "}\n" | |
141 "var closure;\n" | |
142 "main() {\n" | |
143 " closure = () { return magic().toString() + '!'; };\n" | |
144 " return closure();\n" | |
145 "}\n"; | |
146 | |
147 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
148 EXPECT_VALID(lib); | |
149 | |
150 EXPECT_STREQ("antediluvian!", SimpleInvokeStr(lib, "main")); | |
151 | |
152 // Remove the original closure from the source code. The closure is | |
153 // able to be recompiled because its source is preserved in a | |
154 // special patch class. | |
155 const char* kReloadScript = | |
156 "magic() {\n" | |
157 " return 'postapocalyptic';\n" | |
158 "}\n" | |
159 "var closure;\n" | |
160 "main() {\n" | |
161 " return closure();\n" | |
162 "}\n"; | |
163 | |
164 lib = TestCase::ReloadTestScript(kReloadScript); | |
165 EXPECT_VALID(lib); | |
166 | |
167 EXPECT_STREQ("postapocalyptic!", SimpleInvokeStr(lib, "main")); | |
168 } | |
169 | |
170 | |
171 TEST_CASE(IsolateReload_TopLevelFieldAdded) { | |
172 const char* kScript = | |
173 "var value1 = 10;\n" | |
174 "main() {\n" | |
175 " return 'value1=${value1}';\n" | |
176 "}\n"; | |
177 | |
178 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
179 EXPECT_VALID(lib); | |
180 | |
181 EXPECT_STREQ("value1=10", SimpleInvokeStr(lib, "main")); | |
182 | |
183 const char* kReloadScript = | |
184 "var value1 = 10;\n" | |
185 "var value2 = 20;\n" | |
186 "main() {\n" | |
187 " return 'value1=${value1},value2=${value2}';\n" | |
188 "}\n"; | |
189 | |
190 lib = TestCase::ReloadTestScript(kReloadScript); | |
191 EXPECT_VALID(lib); | |
192 | |
193 EXPECT_STREQ("value1=10,value2=20", | |
194 SimpleInvokeStr(lib, "main")); | |
195 } | |
196 | |
197 | |
198 TEST_CASE(IsolateReload_ClassAdded) { | |
199 const char* kScript = | |
200 "main() {\n" | |
201 " return 'hello';\n" | |
202 "}\n"; | |
203 | |
204 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
205 EXPECT_VALID(lib); | |
206 | |
207 EXPECT_STREQ("hello", SimpleInvokeStr(lib, "main")); | |
208 | |
209 const char* kReloadScript = | |
210 "var _unused;" | |
211 "class A {\n" | |
212 " toString() => 'hello from A';\n" | |
213 "}\n" | |
214 "main() {\n" | |
215 " return new A().toString();\n" | |
216 "}\n"; | |
217 | |
218 lib = TestCase::ReloadTestScript(kReloadScript); | |
219 EXPECT_VALID(lib); | |
220 | |
221 EXPECT_STREQ("hello from A", SimpleInvokeStr(lib, "main")); | |
222 } | |
223 | |
224 | |
225 TEST_CASE(IsolateReload_LibraryImportAdded) { | |
226 const char* kScript = | |
227 "main() {\n" | |
228 " return max(3, 4);\n" | |
229 "}\n"; | |
230 | |
231 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
232 EXPECT_VALID(lib); | |
233 | |
234 EXPECT_ERROR(SimpleInvokeError(lib, "main"), "max");; | |
235 | |
236 const char* kReloadScript = | |
237 "import 'dart:math';\n" | |
238 "main() {\n" | |
239 " return max(3, 4);\n" | |
240 "}\n"; | |
241 | |
242 lib = TestCase::ReloadTestScript(kReloadScript); | |
243 EXPECT_VALID(lib); | |
244 | |
245 EXPECT_EQ(4, SimpleInvoke(lib, "main")); | |
246 } | |
247 | |
248 | |
249 TEST_CASE(IsolateReload_LibraryImportRemoved) { | |
250 const char* kScript = | |
251 "import 'dart:math';\n" | |
252 "main() {\n" | |
253 " return max(3, 4);\n" | |
254 "}\n"; | |
255 | |
256 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
257 EXPECT_VALID(lib); | |
258 | |
259 EXPECT_EQ(4, SimpleInvoke(lib, "main")); | |
260 | |
261 const char* kReloadScript = | |
262 "main() {\n" | |
263 " return max(3, 4);\n" | |
264 "}\n"; | |
265 | |
266 lib = TestCase::ReloadTestScript(kReloadScript); | |
267 EXPECT_VALID(lib); | |
268 | |
269 EXPECT_ERROR(SimpleInvokeError(lib, "main"), "max");; | |
270 } | |
271 | |
272 | |
273 TEST_CASE(IsolateReload_LibraryDebuggable) { | |
274 const char* kScript = | |
275 "main() {\n" | |
276 " return 1;\n" | |
277 "}\n"; | |
278 | |
279 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
280 EXPECT_VALID(lib); | |
281 | |
282 // The library is by default debuggable. Make it not debuggable. | |
283 intptr_t lib_id = -1; | |
284 bool debuggable = false; | |
285 EXPECT_VALID(Dart_LibraryId(lib, &lib_id)); | |
286 EXPECT_VALID(Dart_GetLibraryDebuggable(lib_id, &debuggable)); | |
287 EXPECT_EQ(true, debuggable); | |
288 EXPECT_VALID(Dart_SetLibraryDebuggable(lib_id, false)); | |
289 EXPECT_VALID(Dart_GetLibraryDebuggable(lib_id, &debuggable)); | |
290 EXPECT_EQ(false, debuggable); | |
291 | |
292 EXPECT_EQ(1, SimpleInvoke(lib, "main")); | |
293 | |
294 const char* kReloadScript = | |
295 "main() {\n" | |
296 " return 2;\n" | |
297 "}\n"; | |
298 | |
299 lib = TestCase::ReloadTestScript(kReloadScript); | |
300 EXPECT_VALID(lib); | |
301 | |
302 EXPECT_EQ(2, SimpleInvoke(lib, "main")); | |
303 | |
304 // Library debuggability is preserved. | |
305 intptr_t new_lib_id = -1; | |
306 EXPECT_VALID(Dart_LibraryId(lib, &new_lib_id)); | |
307 EXPECT_VALID(Dart_GetLibraryDebuggable(new_lib_id, &debuggable)); | |
308 EXPECT_EQ(false, debuggable); | |
309 } | |
310 | |
311 | |
312 TEST_CASE(IsolateReload_ImplicitConstructorChanged) { | |
313 // Note that we are checking that the value 20 gets cleared from the | |
314 // compile-time constants cache. To make this test work, "20" and | |
315 // "10" need to be at the same token position. | |
316 const char* kScript = | |
317 "class A {\n" | |
318 " int field = 20;\n" | |
319 "}\n" | |
320 "var savedA = new A();\n" | |
321 "main() {\n" | |
322 " var newA = new A();\n" | |
323 " return 'saved:${savedA.field} new:${newA.field}';\n" | |
324 "}\n"; | |
325 | |
326 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
327 EXPECT_VALID(lib); | |
328 | |
329 EXPECT_STREQ("saved:20 new:20", SimpleInvokeStr(lib, "main")); | |
330 | |
331 const char* kReloadScript = | |
332 "class A {\n" | |
333 " int field = 10;\n" | |
334 "}\n" | |
335 "var savedA = new A();\n" | |
336 "main() {\n" | |
337 " var newA = new A();\n" | |
338 " return 'saved:${savedA.field} new:${newA.field}';\n" | |
339 "}\n"; | |
340 | |
341 lib = TestCase::ReloadTestScript(kReloadScript); | |
342 EXPECT_VALID(lib); | |
343 | |
344 EXPECT_STREQ("saved:20 new:10", SimpleInvokeStr(lib, "main")); | |
345 } | |
346 | |
347 | |
348 TEST_CASE(IsolateReload_ConstructorChanged) { | |
349 const char* kScript = | |
350 "class A {\n" | |
351 " int field;\n" | |
352 " A() { field = 20; }\n" | |
353 "}\n" | |
354 "var savedA = new A();\n" | |
355 "main() {\n" | |
356 " var newA = new A();\n" | |
357 " return 'saved:${savedA.field} new:${newA.field}';\n" | |
358 "}\n"; | |
359 | |
360 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
361 EXPECT_VALID(lib); | |
362 | |
363 EXPECT_STREQ("saved:20 new:20", SimpleInvokeStr(lib, "main")); | |
364 | |
365 const char* kReloadScript = | |
366 "var _unused;" | |
367 "class A {\n" | |
368 " int field;\n" | |
369 " A() { field = 10; }\n" | |
370 "}\n" | |
371 "var savedA = new A();\n" | |
372 "main() {\n" | |
373 " var newA = new A();\n" | |
374 " return 'saved:${savedA.field} new:${newA.field}';\n" | |
375 "}\n"; | |
376 | |
377 lib = TestCase::ReloadTestScript(kReloadScript); | |
378 EXPECT_VALID(lib); | |
379 | |
380 EXPECT_STREQ("saved:20 new:10", SimpleInvokeStr(lib, "main")); | |
381 } | |
382 | |
383 | |
384 TEST_CASE(IsolateReload_SuperClassChanged) { | |
385 const char* kScript = | |
386 "class A {\n" | |
387 "}\n" | |
388 "class B extends A {\n" | |
389 "}\n" | |
390 "var list = [ new A(), new B() ];\n" | |
391 "main() {\n" | |
392 " return (list.map((x) => '${x is A}/${x is B}')).toString();\n" | |
393 "}\n"; | |
394 | |
395 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
396 EXPECT_VALID(lib); | |
397 | |
398 EXPECT_STREQ("(true/false, true/true)", SimpleInvokeStr(lib, "main")); | |
399 | |
400 const char* kReloadScript = | |
401 "var _unused;" | |
402 "class B{\n" | |
403 "}\n" | |
404 "class A extends B {\n" | |
405 "}\n" | |
406 "var list = [ new A(), new B() ];\n" | |
407 "main() {\n" | |
408 " return (list.map((x) => '${x is A}/${x is B}')).toString();\n" | |
409 "}\n"; | |
410 | |
411 lib = TestCase::ReloadTestScript(kReloadScript); | |
412 EXPECT_VALID(lib); | |
413 | |
414 EXPECT_STREQ("(true/true, false/true)", SimpleInvokeStr(lib, "main")); | |
415 } | |
416 | |
417 | |
418 TEST_CASE(IsolateReload_Generics) { | |
419 // Reload a program with generics without changing the source. We | |
420 // do this to produce duplication TypeArguments and make sure that | |
421 // the system doesn't die. | |
422 const char* kScript = | |
423 "class A {\n" | |
424 "}\n" | |
425 "class B<T extends A> {\n" | |
426 "}\n" | |
427 "main() {\n" | |
428 " return new B<A>().toString();\n" | |
429 "}\n"; | |
430 | |
431 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
432 EXPECT_VALID(lib); | |
433 | |
434 EXPECT_STREQ("Instance of 'B<A>'", SimpleInvokeStr(lib, "main")); | |
435 | |
436 const char* kReloadScript = | |
437 "class A {\n" | |
438 "}\n" | |
439 "class B<T extends A> {\n" | |
440 "}\n" | |
441 "main() {\n" | |
442 " return new B<A>().toString();\n" | |
443 "}\n"; | |
444 | |
445 lib = TestCase::ReloadTestScript(kReloadScript); | |
446 EXPECT_VALID(lib); | |
447 | |
448 EXPECT_STREQ("Instance of 'B<A>'", SimpleInvokeStr(lib, "main")); | |
449 } | |
450 | |
451 | |
452 TEST_CASE(IsolateReload_MixinChanged) { | |
453 const char* kScript = | |
454 "class Mixin1 {\n" | |
455 " var field = 'mixin1';\n" | |
456 " func() => 'mixin1';\n" | |
457 "}\n" | |
458 "class B extends Object with Mixin1 {\n" | |
459 "}\n" | |
460 "var saved = new B();\n" | |
461 "main() {\n" | |
462 " return 'saved:field=${saved.field},func=${saved.func()}';\n" | |
463 "}\n"; | |
464 | |
465 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
466 EXPECT_VALID(lib); | |
467 | |
468 EXPECT_STREQ("saved:field=mixin1,func=mixin1", | |
469 SimpleInvokeStr(lib, "main")); | |
470 | |
471 const char* kReloadScript = | |
472 "class Mixin2 {\n" | |
473 " var field = 'mixin2';\n" | |
474 " func() => 'mixin2';\n" | |
475 "}\n" | |
476 "class B extends Object with Mixin2 {\n" | |
477 "}\n" | |
478 "var saved = new B();\n" | |
479 "main() {\n" | |
480 " var newer = new B();\n" | |
481 " return 'saved:field=${saved.field},func=${saved.func()} '\n" | |
482 " 'newer:field=${newer.field},func=${newer.func()}';\n" | |
483 "}\n"; | |
484 | |
485 lib = TestCase::ReloadTestScript(kReloadScript); | |
486 EXPECT_VALID(lib); | |
487 | |
488 // The saved instance of B retains its old field value from mixin1, | |
489 // but it gets the new implementation of func from mixin2. | |
490 EXPECT_STREQ("saved:field=mixin1,func=mixin2 " | |
491 "newer:field=mixin2,func=mixin2", | |
492 SimpleInvokeStr(lib, "main")); | |
493 } | |
494 | |
495 | |
496 TEST_CASE(IsolateReload_ComplexInheritanceChange) { | |
497 const char* kScript = | |
498 "class A {\n" | |
499 " String name;\n" | |
500 " A(this.name);\n" | |
501 "}\n" | |
502 "class B extends A {\n" | |
503 " B(name) : super(name);\n" | |
504 "}\n" | |
505 "class C extends B {\n" | |
506 " C(name) : super(name);\n" | |
507 "}\n" | |
508 "var list = [ new A('a'), new B('b'), new C('c') ];\n" | |
509 "main() {\n" | |
510 " return (list.map((x) {\n" | |
511 " return '${x.name} is A(${x is A})/ B(${x is B})/ C(${x is C})';\n" | |
512 " })).toString();\n" | |
513 "}\n"; | |
514 | |
515 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
516 EXPECT_VALID(lib); | |
517 | |
518 EXPECT_STREQ("(a is A(true)/ B(false)/ C(false)," | |
519 " b is A(true)/ B(true)/ C(false)," | |
520 " c is A(true)/ B(true)/ C(true))", | |
521 SimpleInvokeStr(lib, "main")); | |
522 | |
523 const char* kReloadScript = | |
524 "class C {\n" | |
525 " String name;\n" | |
526 " C(this.name);\n" | |
527 "}\n" | |
528 "class X extends C {\n" | |
529 " X(name) : super(name);\n" | |
530 "}\n" | |
531 "class A extends X {\n" | |
532 " A(name) : super(name);\n" | |
533 "}\n" | |
534 "var list;\n" | |
535 "main() {\n" | |
536 " list.add(new X('x'));\n" | |
537 " return (list.map((x) {\n" | |
538 " return '${x.name} is A(${x is A})/ C(${x is C})/ X(${x is X})';\n" | |
539 " })).toString();\n" | |
540 "}\n"; | |
541 | |
542 lib = TestCase::ReloadTestScript(kReloadScript); | |
543 EXPECT_VALID(lib); | |
544 | |
545 EXPECT_STREQ("(a is A(true)/ C(true)/ X(true)," | |
546 " b is A(true)/ C(true)/ X(true)," // still extends A... | |
547 " c is A(false)/ C(true)/ X(false)," | |
548 " x is A(false)/ C(true)/ X(true))", | |
549 SimpleInvokeStr(lib, "main")); | |
550 | |
551 // Revive the class B and make sure all allocated instances take | |
552 // their place in the inheritance hierarchy. | |
553 const char* kReloadScript2 = | |
554 "class X {\n" | |
555 " String name;\n" | |
556 " X(this.name);\n" | |
557 "}\n" | |
558 "class A extends X{\n" | |
559 " A(name) : super(name);\n" | |
560 "}\n" | |
561 "class B extends X {\n" | |
562 " B(name) : super(name);\n" | |
563 "}\n" | |
564 "class C extends A {\n" | |
565 " C(name) : super(name);\n" | |
566 "}\n" | |
567 "var list;\n" | |
568 "main() {\n" | |
569 " return (list.map((x) {\n" | |
570 " return '${x.name} is '\n" | |
571 " 'A(${x is A})/ B(${x is B})/ C(${x is C})/ X(${x is X})';\n" | |
572 " })).toString();\n" | |
573 "}\n"; | |
574 | |
575 lib = TestCase::ReloadTestScript(kReloadScript2); | |
576 EXPECT_VALID(lib); | |
577 | |
578 EXPECT_STREQ("(a is A(true)/ B(false)/ C(false)/ X(true)," | |
579 " b is A(false)/ B(true)/ C(false)/ X(true)," | |
580 " c is A(true)/ B(false)/ C(true)/ X(true)," | |
581 " x is A(false)/ B(false)/ C(false)/ X(true))", | |
582 SimpleInvokeStr(lib, "main")); | |
583 } | |
584 | |
585 | |
586 TEST_CASE(IsolateReload_LiveStack) { | |
587 const char* kScript = | |
588 "import 'isolate_reload_test_helper';\n" | |
589 "helper() => 7;\n" | |
590 "alpha() { var x = helper(); reloadTest(); return x + helper(); }\n" | |
591 "foo() => alpha();\n" | |
592 "bar() => foo();\n" | |
593 "main() {\n" | |
594 " return bar();\n" | |
595 "}\n"; | |
596 | |
597 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
598 EXPECT_VALID(lib); | |
599 | |
600 const char* kReloadScript = | |
601 "import 'isolate_reload_test_helper';\n" | |
602 "helper() => 100;\n" | |
603 "alpha() => 5 + helper();\n" | |
604 "foo() => alpha();\n" | |
605 "bar() => foo();\n" | |
606 "main() {\n" | |
607 " return bar();\n" | |
608 "}\n"; | |
609 | |
610 TestCase::SetReloadTestScript(kReloadScript); | |
611 | |
612 EXPECT_EQ(107, SimpleInvoke(lib, "main")); | |
613 | |
614 lib = TestCase::GetReloadErrorOrRootLibrary(); | |
615 EXPECT_VALID(lib); | |
616 | |
617 EXPECT_EQ(105, SimpleInvoke(lib, "main")); | |
618 } | |
619 | |
620 | |
621 TEST_CASE(IsolateReload_LibraryLookup) { | |
622 const char* kScript = | |
623 "main() {\n" | |
624 " return importedFunc();\n" | |
625 "}\n"; | |
626 | |
627 Dart_Handle result; | |
628 | |
629 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
630 EXPECT_VALID(lib); | |
631 | |
632 EXPECT_ERROR(SimpleInvokeError(lib, "main"), "importedFunc"); | |
633 | |
634 // Fail to find 'importable_test_lib' in the isolate. | |
635 result = Dart_LookupLibrary(NewString("importable_test_lib")); | |
636 EXPECT(Dart_IsError(result)); | |
637 | |
638 const char* kReloadScript = | |
639 "import 'importable_test_lib';\n" | |
640 "main() {\n" | |
641 " return importedFunc();\n" | |
642 "}\n"; | |
643 | |
644 // Reload and add 'importable_test_lib' to isolate. | |
645 lib = TestCase::ReloadTestScript(kReloadScript); | |
646 EXPECT_VALID(lib); | |
647 | |
648 EXPECT_STREQ("a", SimpleInvokeStr(lib, "main")); | |
649 | |
650 // Find 'importable_test_lib' in the isolate. | |
651 result = Dart_LookupLibrary(NewString("importable_test_lib")); | |
652 EXPECT(Dart_IsLibrary(result)); | |
653 | |
654 // Reload and remove 'dart:math' from isolate. | |
655 lib = TestCase::ReloadTestScript(kScript); | |
656 EXPECT_VALID(lib); | |
657 | |
658 // Fail to find 'importable_test_lib' in the isolate. | |
659 result = Dart_LookupLibrary(NewString("importable_test_lib")); | |
660 EXPECT(Dart_IsError(result)); | |
661 } | |
662 | |
663 | |
664 TEST_CASE(IsolateReload_LibraryHide) { | |
665 // Import 'importable_test_lib' with importedFunc hidden. Will result in an | |
666 // error. | |
667 const char* kScript = | |
668 "import 'importable_test_lib' hide importedFunc;\n" | |
669 "main() {\n" | |
670 " return importedFunc();\n" | |
671 "}\n"; | |
672 | |
673 // Dart_Handle result; | |
674 | |
675 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
676 EXPECT_VALID(lib); | |
677 | |
678 EXPECT_ERROR(SimpleInvokeError(lib, "main"), "importedFunc"); | |
679 | |
680 // Import 'importable_test_lib'. | |
681 const char* kReloadScript = | |
682 "import 'importable_test_lib';\n" | |
683 "main() {\n" | |
684 " return importedFunc();\n" | |
685 "}\n"; | |
686 | |
687 lib = TestCase::ReloadTestScript(kReloadScript); | |
688 EXPECT_VALID(lib); | |
689 | |
690 EXPECT_STREQ("a", SimpleInvokeStr(lib, "main")); | |
691 } | |
692 | |
693 | |
694 TEST_CASE(IsolateReload_LibraryShow) { | |
695 // Import 'importable_test_lib' with importedIntFunc visible. Will result in | |
696 // an error when 'main' is invoked. | |
697 const char* kScript = | |
698 "import 'importable_test_lib' show importedIntFunc;\n" | |
699 "main() {\n" | |
700 " return importedFunc();\n" | |
701 "}\n" | |
702 "mainInt() {\n" | |
703 " return importedIntFunc();\n" | |
704 "}\n"; | |
705 | |
706 | |
707 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
708 EXPECT_VALID(lib); | |
709 | |
710 // Works. | |
711 EXPECT_EQ(4, SimpleInvoke(lib, "mainInt")); | |
712 // Results in an error. | |
713 EXPECT_ERROR(SimpleInvokeError(lib, "main"), "importedFunc"); | |
714 | |
715 // Import 'importable_test_lib' with importedFunc visible. Will result in | |
716 // an error when 'mainInt' is invoked. | |
717 const char* kReloadScript = | |
718 "import 'importable_test_lib' show importedFunc;\n" | |
719 "main() {\n" | |
720 " return importedFunc();\n" | |
721 "}\n" | |
722 "mainInt() {\n" | |
723 " return importedIntFunc();\n" | |
724 "}\n"; | |
725 | |
726 lib = TestCase::ReloadTestScript(kReloadScript); | |
727 EXPECT_VALID(lib); | |
728 | |
729 // Works. | |
730 EXPECT_STREQ("a", SimpleInvokeStr(lib, "main")); | |
731 // Results in an error. | |
732 EXPECT_ERROR(SimpleInvokeError(lib, "mainInt"), "importedIntFunc"); | |
733 } | |
734 | |
735 | |
736 // Verifies that we clear the ICs for the functions live on the stack in a way | |
737 // that is compatible with the fast path smi stubs. | |
738 TEST_CASE(IsolateReload_SmiFastPathStubs) { | |
739 const char* kScript = | |
740 "import 'isolate_reload_test_helper';\n" | |
741 "import 'importable_test_lib' show importedIntFunc;\n" | |
742 "main() {\n" | |
743 " var x = importedIntFunc();\n" | |
744 " var y = importedIntFunc();\n" | |
745 " reloadTest();\n" | |
746 " return x + y;\n" | |
747 "}\n"; | |
748 | |
749 | |
750 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
751 EXPECT_VALID(lib); | |
752 | |
753 // Identity reload. | |
754 TestCase::SetReloadTestScript(kScript); | |
755 | |
756 EXPECT_EQ(8, SimpleInvoke(lib, "main")); | |
757 } | |
758 | |
759 | |
760 // Verifies that we assign the correct patch classes for imported | |
761 // mixins when we reload. | |
762 TEST_CASE(IsolateReload_ImportedMixinFunction) { | |
763 const char* kScript = | |
764 "import 'importable_test_lib' show ImportedMixin;\n" | |
765 "class A extends Object with ImportedMixin {\n" | |
766 "}" | |
767 "var func = new A().mixinFunc;\n" | |
768 "main() {\n" | |
769 " return func();\n" | |
770 "}\n"; | |
771 | |
772 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
773 EXPECT_VALID(lib); | |
774 | |
775 EXPECT_STREQ("mixin", SimpleInvokeStr(lib, "main")); | |
776 | |
777 const char* kReloadScript = | |
778 "import 'importable_test_lib' show ImportedMixin;\n" | |
779 "class A extends Object with ImportedMixin {\n" | |
780 "}" | |
781 "var func;\n" | |
782 "main() {\n" | |
783 " return func();\n" | |
784 "}\n"; | |
785 | |
786 lib = TestCase::ReloadTestScript(kReloadScript); | |
787 EXPECT_VALID(lib); | |
788 | |
789 EXPECT_STREQ("mixin", SimpleInvokeStr(lib, "main")); | |
790 } | |
791 | |
792 | |
793 TEST_CASE(IsolateReload_TopLevelParseError) { | |
794 const char* kScript = | |
795 "main() {\n" | |
796 " return 4;\n" | |
797 "}\n"; | |
798 | |
799 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
800 EXPECT_VALID(lib); | |
801 | |
802 EXPECT_EQ(4, SimpleInvoke(lib, "main")); | |
803 | |
804 const char* kReloadScript = | |
805 "kjsadkfjaksldfjklsadf;\n" | |
806 "main() {\n" | |
807 " return 4;\n" | |
808 "}\n"; | |
809 | |
810 lib = TestCase::ReloadTestScript(kReloadScript); | |
811 EXPECT_ERROR(lib, "unexpected token"); | |
812 } | |
813 | |
814 | |
815 TEST_CASE(IsolateReload_PendingUnqualifiedCall_StaticToInstance) { | |
816 const char* kScript = | |
817 "import 'isolate_reload_test_helper';\n" | |
818 "class C {\n" | |
819 " static foo() => 'static';\n" | |
820 " test() {\n" | |
821 " reloadTest();\n" | |
822 " return foo();\n" | |
823 " }\n" | |
824 "}\n" | |
825 "main() {\n" | |
826 " return new C().test();\n" | |
827 "}\n"; | |
828 | |
829 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
830 EXPECT_VALID(lib); | |
831 | |
832 const char* kReloadScript = | |
833 "import 'isolate_reload_test_helper';\n" | |
834 "class C {\n" | |
835 " foo() => 'instance';\n" | |
836 " test() {\n" | |
837 " reloadTest();\n" | |
838 " return foo();\n" | |
839 " }\n" | |
840 "}\n" | |
841 "main() {\n" | |
842 " return new C().test();\n" | |
843 "}\n"; | |
844 | |
845 TestCase::SetReloadTestScript(kReloadScript); | |
846 | |
847 EXPECT_EQ("instance", SimpleInvokeStr(lib, "main")); | |
848 | |
849 lib = TestCase::GetReloadErrorOrRootLibrary(); | |
850 EXPECT_VALID(lib); | |
851 | |
852 EXPECT_EQ("instance", SimpleInvokeStr(lib, "main")); | |
853 } | |
854 | |
855 | |
856 TEST_CASE(IsolateReload_PendingUnqualifiedCall_InstanceToStatic) { | |
857 const char* kScript = | |
858 "import 'isolate_reload_test_helper';\n" | |
859 "class C {\n" | |
860 " foo() => 'instance';\n" | |
861 " test() {\n" | |
862 " reloadTest();\n" | |
863 " return foo();\n" | |
864 " }\n" | |
865 "}\n" | |
866 "main() {\n" | |
867 " return new C().test();\n" | |
868 "}\n"; | |
869 | |
870 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
871 EXPECT_VALID(lib); | |
872 | |
873 const char* kReloadScript = | |
874 "import 'isolate_reload_test_helper';\n" | |
875 "class C {\n" | |
876 " static foo() => 'static';\n" | |
877 " test() {\n" | |
878 " reloadTest();\n" | |
879 " return foo();\n" | |
880 " }\n" | |
881 "}\n" | |
882 "main() {\n" | |
883 " return new C().test();\n" | |
884 "}\n"; | |
885 | |
886 TestCase::SetReloadTestScript(kReloadScript); | |
887 | |
888 EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); | |
889 | |
890 lib = TestCase::GetReloadErrorOrRootLibrary(); | |
891 EXPECT_VALID(lib); | |
892 | |
893 EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); | |
894 } | |
895 | |
896 | |
897 TEST_CASE(IsolateReload_PendingConstructorCall_AbstractToConcrete) { | |
898 const char* kScript = | |
899 "import 'isolate_reload_test_helper';\n" | |
900 "abstract class Foo {}\n" | |
901 "class C {\n" | |
902 " test() {\n" | |
903 " reloadTest();\n" | |
904 " return new Foo();\n" | |
905 " }\n" | |
906 "}\n" | |
907 "main() {\n" | |
908 " try {\n" | |
909 " new C().test();\n" | |
910 " return 'okay';\n" | |
911 " } catch (e) {\n" | |
912 " return 'exception';\n" | |
913 " }\n" | |
914 "}\n"; | |
915 | |
916 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
917 EXPECT_VALID(lib); | |
918 | |
919 const char* kReloadScript = | |
920 "import 'isolate_reload_test_helper';\n" | |
921 "class Foo {}\n" | |
922 "class C {\n" | |
923 " test() {\n" | |
924 " reloadTest();\n" | |
925 " return new Foo();\n" | |
926 " }\n" | |
927 "}\n" | |
928 "main() {\n" | |
929 " try {\n" | |
930 " new C().test();\n" | |
931 " return 'okay';\n" | |
932 " } catch (e) {\n" | |
933 " return 'exception';\n" | |
934 " }\n" | |
935 "}\n"; | |
936 | |
937 TestCase::SetReloadTestScript(kReloadScript); | |
938 | |
939 EXPECT_EQ("okay", SimpleInvokeStr(lib, "main")); | |
940 | |
941 lib = TestCase::GetReloadErrorOrRootLibrary(); | |
942 EXPECT_VALID(lib); | |
943 | |
944 EXPECT_EQ("okay", SimpleInvokeStr(lib, "main")); | |
945 } | |
946 | |
947 | |
948 TEST_CASE(IsolateReload_PendingConstructorCall_ConcreteToAbstract) { | |
949 const char* kScript = | |
950 "import 'isolate_reload_test_helper';\n" | |
951 "class Foo {}\n" | |
952 "class C {\n" | |
953 " test() {\n" | |
954 " reloadTest();\n" | |
955 " return new Foo();\n" | |
956 " }\n" | |
957 "}\n" | |
958 "main() {\n" | |
959 " try {\n" | |
960 " new C().test();\n" | |
961 " return 'okay';\n" | |
962 " } catch (e) {\n" | |
963 " return 'exception';\n" | |
964 " }\n" | |
965 "}\n"; | |
966 | |
967 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
968 EXPECT_VALID(lib); | |
969 | |
970 const char* kReloadScript = | |
971 "import 'isolate_reload_test_helper';\n" | |
972 "abstract class Foo {}\n" | |
973 "class C {\n" | |
974 " test() {\n" | |
975 " reloadTest();\n" | |
976 " return new Foo();\n" | |
977 " }\n" | |
978 "}\n" | |
979 "main() {\n" | |
980 " try {\n" | |
981 " new C().test();\n" | |
982 " return 'okay';\n" | |
983 " } catch (e) {\n" | |
984 " return 'exception';\n" | |
985 " }\n" | |
986 "}\n"; | |
987 | |
988 TestCase::SetReloadTestScript(kReloadScript); | |
989 | |
990 EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); | |
991 | |
992 lib = TestCase::GetReloadErrorOrRootLibrary(); | |
993 EXPECT_VALID(lib); | |
994 | |
995 EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); | |
996 } | |
997 | |
998 | |
999 TEST_CASE(IsolateReload_PendingStaticCall_DefinedToNSM) { | |
1000 const char* kScript = | |
1001 "import 'isolate_reload_test_helper';\n" | |
1002 "class C {\n" | |
1003 " static foo() => 'static'\n" | |
1004 " test() {\n" | |
1005 " reloadTest();\n" | |
1006 " return C.foo();\n" | |
1007 " }\n" | |
1008 "}\n" | |
1009 "main() {\n" | |
1010 " try {\n" | |
1011 " return new C().test();\n" | |
1012 " } catch (e) {\n" | |
1013 " return 'exception';\n" | |
1014 " }\n" | |
1015 "}\n"; | |
1016 | |
1017 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1018 EXPECT_VALID(lib); | |
1019 | |
1020 const char* kReloadScript = | |
1021 "import 'isolate_reload_test_helper';\n" | |
1022 "class C {\n" | |
1023 " test() {\n" | |
1024 " reloadTest();\n" | |
1025 " return C.foo();\n" | |
1026 " }\n" | |
1027 "}\n" | |
1028 "main() {\n" | |
1029 " try {\n" | |
1030 " return new C().test();\n" | |
1031 " } catch (e) {\n" | |
1032 " return 'exception';\n" | |
1033 " }\n" | |
1034 "}\n"; | |
1035 | |
1036 TestCase::SetReloadTestScript(kReloadScript); | |
1037 | |
1038 EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); | |
1039 | |
1040 lib = TestCase::GetReloadErrorOrRootLibrary(); | |
1041 EXPECT_VALID(lib); | |
1042 | |
1043 EXPECT_EQ("exception", SimpleInvokeStr(lib, "main")); | |
1044 } | |
1045 | |
1046 | |
1047 TEST_CASE(IsolateReload_PendingStaticCall_NSMToDefined) { | |
1048 const char* kScript = | |
1049 "import 'isolate_reload_test_helper';\n" | |
1050 "class C {\n" | |
1051 " test() {\n" | |
1052 " reloadTest();\n" | |
1053 " return C.foo();\n" | |
1054 " }\n" | |
1055 "}\n" | |
1056 "main() {\n" | |
1057 " try {\n" | |
1058 " return new C().test();\n" | |
1059 " } catch (e) {\n" | |
1060 " return 'exception';\n" | |
1061 " }\n" | |
1062 "}\n"; | |
1063 | |
1064 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1065 EXPECT_VALID(lib); | |
1066 | |
1067 const char* kReloadScript = | |
1068 "import 'isolate_reload_test_helper';\n" | |
1069 "class C {\n" | |
1070 " static foo() => 'static'\n" | |
1071 " test() {\n" | |
1072 " reloadTest();\n" | |
1073 " return C.foo();\n" | |
1074 " }\n" | |
1075 "}\n" | |
1076 "main() {\n" | |
1077 " try {\n" | |
1078 " return new C().test();\n" | |
1079 " } catch (e) {\n" | |
1080 " return 'exception';\n" | |
1081 " }\n" | |
1082 "}\n"; | |
1083 | |
1084 TestCase::SetReloadTestScript(kReloadScript); | |
1085 | |
1086 EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); | |
1087 | |
1088 lib = TestCase::GetReloadErrorOrRootLibrary(); | |
1089 EXPECT_VALID(lib); | |
1090 | |
1091 EXPECT_EQ("static", SimpleInvokeStr(lib, "main")); | |
1092 } | |
1093 | |
1094 | |
1095 TEST_CASE(IsolateReload_EnumEquality) { | |
1096 const char* kScript = | |
1097 "enum Fruit {\n" | |
1098 " Apple,\n" | |
1099 " Banana,\n" | |
1100 "}\n" | |
1101 "var x;\n" | |
1102 "main() {\n" | |
1103 " x = Fruit.Banana;\n" | |
1104 " return Fruit.Apple.toString();\n" | |
1105 "}\n"; | |
1106 | |
1107 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1108 EXPECT_VALID(lib); | |
1109 | |
1110 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1111 | |
1112 const char* kReloadScript = | |
1113 "enum Fruit {\n" | |
1114 " Apple,\n" | |
1115 " Banana,\n" | |
1116 "}\n" | |
1117 "var x;\n" | |
1118 "main() {\n" | |
1119 " if (x == Fruit.Banana) {\n" | |
1120 " return 'yes';\n" | |
1121 " } else {\n" | |
1122 " return 'no';\n" | |
1123 " }\n" | |
1124 "}\n"; | |
1125 | |
1126 lib = TestCase::ReloadTestScript(kReloadScript); | |
1127 EXPECT_VALID(lib); | |
1128 | |
1129 EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); | |
1130 } | |
1131 | |
1132 | |
1133 TEST_CASE(IsolateReload_EnumIdentical) { | |
1134 const char* kScript = | |
1135 "enum Fruit {\n" | |
1136 " Apple,\n" | |
1137 " Banana,\n" | |
1138 "}\n" | |
1139 "var x;\n" | |
1140 "main() {\n" | |
1141 " x = Fruit.Banana;\n" | |
1142 " return Fruit.Apple.toString();\n" | |
1143 "}\n"; | |
1144 | |
1145 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1146 EXPECT_VALID(lib); | |
1147 | |
1148 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1149 | |
1150 const char* kReloadScript = | |
1151 "enum Fruit {\n" | |
1152 " Apple,\n" | |
1153 " Banana,\n" | |
1154 "}\n" | |
1155 "var x;\n" | |
1156 "main() {\n" | |
1157 " if (identical(x, Fruit.Banana)) {\n" | |
1158 " return 'yes';\n" | |
1159 " } else {\n" | |
1160 " return 'no';\n" | |
1161 " }\n" | |
1162 "}\n"; | |
1163 | |
1164 lib = TestCase::ReloadTestScript(kReloadScript); | |
1165 EXPECT_VALID(lib); | |
1166 | |
1167 EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); | |
1168 } | |
1169 | |
1170 | |
1171 TEST_CASE(IsolateReload_EnumReorderIdentical) { | |
1172 const char* kScript = | |
1173 "enum Fruit {\n" | |
1174 " Apple,\n" | |
1175 " Banana,\n" | |
1176 "}\n" | |
1177 "var x;\n" | |
1178 "main() {\n" | |
1179 " x = Fruit.Banana;\n" | |
1180 " return Fruit.Apple.toString();\n" | |
1181 "}\n"; | |
1182 | |
1183 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1184 EXPECT_VALID(lib); | |
1185 | |
1186 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1187 | |
1188 const char* kReloadScript = | |
1189 "enum Fruit {\n" | |
1190 " Banana,\n" | |
1191 " Apple,\n" | |
1192 "}\n" | |
1193 "var x;\n" | |
1194 "main() {\n" | |
1195 " if (identical(x, Fruit.Banana)) {\n" | |
1196 " return 'yes';\n" | |
1197 " } else {\n" | |
1198 " return 'no';\n" | |
1199 " }\n" | |
1200 "}\n"; | |
1201 | |
1202 lib = TestCase::ReloadTestScript(kReloadScript); | |
1203 EXPECT_VALID(lib); | |
1204 | |
1205 EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); | |
1206 } | |
1207 | |
1208 | |
1209 TEST_CASE(IsolateReload_EnumAddition) { | |
1210 const char* kScript = | |
1211 "enum Fruit {\n" | |
1212 " Apple,\n" | |
1213 " Banana,\n" | |
1214 "}\n" | |
1215 "var x;\n" | |
1216 "main() {\n" | |
1217 " return Fruit.Apple.toString();\n" | |
1218 "}\n"; | |
1219 | |
1220 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1221 EXPECT_VALID(lib); | |
1222 | |
1223 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1224 | |
1225 const char* kReloadScript = | |
1226 "enum Fruit {\n" | |
1227 " Apple,\n" | |
1228 " Cantalope,\n" | |
1229 " Banana,\n" | |
1230 "}\n" | |
1231 "var x;\n" | |
1232 "main() {\n" | |
1233 " String r = '${Fruit.Apple.index}/${Fruit.Apple} ';\n" | |
1234 " r += '${Fruit.Cantalope.index}/${Fruit.Cantalope} ';\n" | |
1235 " r += '${Fruit.Banana.index}/${Fruit.Banana}';\n" | |
1236 " return r;\n" | |
1237 "}\n"; | |
1238 | |
1239 lib = TestCase::ReloadTestScript(kReloadScript); | |
1240 EXPECT_VALID(lib); | |
1241 | |
1242 EXPECT_STREQ("0/Fruit.Apple 1/Fruit.Cantalope 2/Fruit.Banana", | |
1243 SimpleInvokeStr(lib, "main")); | |
1244 } | |
1245 | |
1246 | |
1247 TEST_CASE(IsolateReload_EnumToNotEnum) { | |
1248 const char* kScript = | |
1249 "enum Fruit {\n" | |
1250 " Apple\n" | |
1251 "}\n" | |
1252 "main() {\n" | |
1253 " return Fruit.Apple.toString();\n" | |
1254 "}\n"; | |
1255 | |
1256 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1257 EXPECT_VALID(lib); | |
1258 | |
1259 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1260 | |
1261 const char* kReloadScript = | |
1262 "class Fruit {\n" | |
1263 " final int zero = 0;\n" | |
1264 "}\n" | |
1265 "main() {\n" | |
1266 "}\n"; | |
1267 | |
1268 Dart_Handle result = TestCase::ReloadTestScript(kReloadScript); | |
1269 EXPECT_ERROR(result, "Enum class cannot be redefined to be a non-enum class"); | |
1270 } | |
1271 | |
1272 | |
1273 TEST_CASE(IsolateReload_NotEnumToEnum) { | |
1274 const char* kScript = | |
1275 "class Fruit {\n" | |
1276 " final int zero = 0;\n" | |
1277 "}\n" | |
1278 "main() {\n" | |
1279 " return 'yes';\n" | |
1280 "}\n"; | |
1281 | |
1282 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1283 EXPECT_VALID(lib); | |
1284 | |
1285 EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); | |
1286 | |
1287 const char* kReloadScript = | |
1288 "enum Fruit {\n" | |
1289 " Apple\n" | |
1290 "}\n" | |
1291 "main() {\n" | |
1292 " return Fruit.Apple.toString();\n" | |
1293 "}\n"; | |
1294 | |
1295 Dart_Handle result = TestCase::ReloadTestScript(kReloadScript); | |
1296 EXPECT_ERROR(result, "Class cannot be redefined to be a enum class"); | |
1297 } | |
1298 | |
1299 | |
1300 TEST_CASE(IsolateReload_EnumDelete) { | |
1301 const char* kScript = | |
1302 "enum Fruit {\n" | |
1303 " Apple,\n" | |
1304 " Banana,\n" | |
1305 " Cantalope,\n" | |
1306 "}\n" | |
1307 "var x;\n" | |
1308 "main() {\n" | |
1309 " return Fruit.Apple.toString();\n" | |
1310 "}\n"; | |
1311 | |
1312 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1313 EXPECT_VALID(lib); | |
1314 | |
1315 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1316 | |
1317 // Delete 'Cantalope'. | |
1318 | |
1319 const char* kReloadScript = | |
1320 "enum Fruit {\n" | |
1321 " Apple,\n" | |
1322 " Banana,\n" | |
1323 "}\n" | |
1324 "var x;\n" | |
1325 "main() {\n" | |
1326 " String r = '${Fruit.Apple.index}/${Fruit.Apple} ';\n" | |
1327 " r += '${Fruit.Banana.index}/${Fruit.Banana} ';\n" | |
1328 " r += '${Fruit.Cantalope.index}/${Fruit.Cantalope}';\n" | |
1329 " return r;\n" | |
1330 "}\n"; | |
1331 | |
1332 lib = TestCase::ReloadTestScript(kReloadScript); | |
1333 EXPECT_VALID(lib); | |
1334 | |
1335 EXPECT_STREQ("0/Fruit.Apple 1/Fruit.Banana 2/Fruit.Cantalope", | |
1336 SimpleInvokeStr(lib, "main")); | |
1337 } | |
1338 | |
1339 | |
1340 TEST_CASE(IsolateReload_EnumComplex) { | |
1341 const char* kScript = | |
1342 "enum Fruit {\n" | |
1343 " Apple,\n" | |
1344 " Banana,\n" | |
1345 " Cantalope,\n" | |
1346 "}\n" | |
1347 "var x;\n" | |
1348 "var y;\n" | |
1349 "var z;\n" | |
1350 "main() {\n" | |
1351 " x = Fruit.Apple;\n" | |
1352 " y = Fruit.Banana;\n" | |
1353 " z = Fruit.Cantalope;\n" | |
1354 " return Fruit.Apple.toString();\n" | |
1355 "}\n"; | |
1356 | |
1357 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1358 EXPECT_VALID(lib); | |
1359 | |
1360 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1361 | |
1362 // Delete 'Cantalope'. Add 'Dragon'. Move 'Apple' and 'Banana'. | |
1363 | |
1364 const char* kReloadScript = | |
1365 "enum Fruit {\n" | |
1366 " Dragon,\n" | |
1367 " Apple,\n" | |
1368 " Banana,\n" | |
1369 "}\n" | |
1370 "var x;\n" | |
1371 "var y;\n" | |
1372 "var z;\n" | |
1373 "main() {\n" | |
1374 " String r = '';\n" | |
1375 " r += '${identical(x, Fruit.Apple)}';\n" | |
1376 " r += ' ${identical(y, Fruit.Banana)}';\n" | |
1377 " r += ' ${identical(z, Fruit.Cantalope)}';\n" | |
1378 " r += ' ${Fruit.Dragon}';\n" | |
1379 " return r;\n" | |
1380 "}\n"; | |
1381 | |
1382 lib = TestCase::ReloadTestScript(kReloadScript); | |
1383 EXPECT_VALID(lib); | |
1384 | |
1385 EXPECT_STREQ("true true true Fruit.Dragon", SimpleInvokeStr(lib, "main")); | |
1386 } | |
1387 | |
1388 | |
1389 TEST_CASE(IsolateReload_EnumValuesArray) { | |
1390 const char* kScript = | |
1391 "enum Fruit {\n" | |
1392 " Cantalope,\n" | |
1393 " Apple,\n" | |
1394 " Banana,\n" | |
1395 "}\n" | |
1396 "var x;\n" | |
1397 "main() {\n" | |
1398 " x = Fruit.Cantalope;\n" | |
1399 " return Fruit.Apple.toString();\n" | |
1400 "}\n"; | |
1401 | |
1402 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1403 EXPECT_VALID(lib); | |
1404 | |
1405 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1406 | |
1407 // Delete 'Cantalope'. | |
1408 | |
1409 const char* kReloadScript = | |
1410 "enum Fruit {\n" | |
1411 " Banana,\n" | |
1412 " Apple\n" | |
1413 "}\n" | |
1414 "var x;\n" | |
1415 "bool identityCheck(Fruit f) {\n" | |
1416 " return identical(Fruit.values[f.index], f);\n" | |
1417 "}\n" | |
1418 "main() {\n" | |
1419 " if ((x is Fruit) && identical(x, Fruit.Cantalope)) {\n" | |
1420 " String r = '${identityCheck(Fruit.Apple)}';\n" | |
1421 " r += ' ${identityCheck(Fruit.Banana)}';\n" | |
1422 " r += ' ${identityCheck(Fruit.Cantalope)}';\n" | |
1423 " r += ' ${identityCheck(x)}';\n" | |
1424 " return r;\n" | |
1425 " }\n" | |
1426 "}\n"; | |
1427 | |
1428 lib = TestCase::ReloadTestScript(kReloadScript); | |
1429 EXPECT_VALID(lib); | |
1430 | |
1431 EXPECT_STREQ("true true true true", | |
1432 SimpleInvokeStr(lib, "main")); | |
1433 } | |
1434 | |
1435 | |
1436 TEST_CASE(IsolateReload_EnumIdentityReload) { | |
1437 const char* kScript = | |
1438 "enum Fruit {\n" | |
1439 " Apple,\n" | |
1440 " Banana,\n" | |
1441 " Cantalope,\n" | |
1442 "}\n" | |
1443 "var x;\n" | |
1444 "var y;\n" | |
1445 "var z;\n" | |
1446 "var w;\n" | |
1447 "main() {\n" | |
1448 " x = { Fruit.Apple: Fruit.Apple.index,\n" | |
1449 " Fruit.Banana: Fruit.Banana.index,\n" | |
1450 " Fruit.Cantalope: Fruit.Cantalope.index};\n" | |
1451 " y = Fruit.Apple;\n" | |
1452 " z = Fruit.Banana;\n" | |
1453 " w = Fruit.Cantalope;\n" | |
1454 " return Fruit.Apple.toString();\n" | |
1455 "}\n"; | |
1456 | |
1457 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1458 EXPECT_VALID(lib); | |
1459 | |
1460 EXPECT_STREQ("Fruit.Apple", SimpleInvokeStr(lib, "main")); | |
1461 | |
1462 const char* kReloadScript = | |
1463 "enum Fruit {\n" | |
1464 " Apple,\n" | |
1465 " Banana,\n" | |
1466 " Cantalope,\n" | |
1467 "}\n" | |
1468 "var x;\n" | |
1469 "var y;\n" | |
1470 "var z;\n" | |
1471 "var w;\n" | |
1472 "bool identityCheck(Fruit f, int index) {\n" | |
1473 " return identical(Fruit.values[index], f);\n" | |
1474 "}\n" | |
1475 "main() {\n" | |
1476 " String r = '';\n" | |
1477 " x.forEach((key, value) {\n" | |
1478 " r += '${identityCheck(key, value)} ';\n" | |
1479 " });\n" | |
1480 " r += '${x[Fruit.Apple] == Fruit.Apple.index} ';\n" | |
1481 " r += '${x[Fruit.Banana] == Fruit.Banana.index} ';\n" | |
1482 " r += '${x[Fruit.Cantalope] == Fruit.Cantalope.index} ';\n" | |
1483 " r += '${identical(y, Fruit.values[x[Fruit.Apple]])} ';\n" | |
1484 " r += '${identical(z, Fruit.values[x[Fruit.Banana]])} ';\n" | |
1485 " r += '${identical(w, Fruit.values[x[Fruit.Cantalope]])} ';\n" | |
1486 " return r;\n" | |
1487 "}\n"; | |
1488 | |
1489 lib = TestCase::ReloadTestScript(kReloadScript); | |
1490 EXPECT_VALID(lib); | |
1491 | |
1492 EXPECT_STREQ("true true true true true true true true true ", | |
1493 SimpleInvokeStr(lib, "main")); | |
1494 } | |
1495 | |
1496 | |
1497 TEST_CASE(IsolateReload_ConstantIdentical) { | |
1498 const char* kScript = | |
1499 "class Fruit {\n" | |
1500 " final String name;\n" | |
1501 " const Fruit(this.name);\n" | |
1502 " String toString() => name;\n" | |
1503 "}\n" | |
1504 "var x;\n" | |
1505 "main() {\n" | |
1506 " x = const Fruit('Pear');\n" | |
1507 " return x.toString();\n" | |
1508 "}\n"; | |
1509 | |
1510 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1511 EXPECT_VALID(lib); | |
1512 | |
1513 EXPECT_STREQ("Pear", SimpleInvokeStr(lib, "main")); | |
1514 | |
1515 const char* kReloadScript = | |
1516 "class Fruit {\n" | |
1517 " final String name;\n" | |
1518 " const Fruit(this.name);\n" | |
1519 " String toString() => name;\n" | |
1520 "}\n" | |
1521 "var x;\n" | |
1522 "main() {\n" | |
1523 " if (identical(x, const Fruit('Pear'))) {\n" | |
1524 " return 'yes';\n" | |
1525 " } else {\n" | |
1526 " return 'no';\n" | |
1527 " }\n" | |
1528 "}\n"; | |
1529 | |
1530 lib = TestCase::ReloadTestScript(kReloadScript); | |
1531 EXPECT_VALID(lib); | |
1532 | |
1533 EXPECT_STREQ("yes", SimpleInvokeStr(lib, "main")); | |
1534 } | |
1535 | |
1536 | |
1537 TEST_CASE(IsolateReload_EnumValuesToString) { | |
1538 const char* kScript = | |
1539 "enum Fruit {\n" | |
1540 " Apple,\n" | |
1541 " Banana,\n" | |
1542 "}\n" | |
1543 "var x;\n" | |
1544 "main() {\n" | |
1545 " String r = '';\n" | |
1546 " r += Fruit.Apple.toString();\n" | |
1547 " r += ' ';\n" | |
1548 " r += Fruit.Banana.toString();\n" | |
1549 " return r;\n" | |
1550 "}\n"; | |
1551 | |
1552 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1553 EXPECT_VALID(lib); | |
1554 | |
1555 EXPECT_STREQ("Fruit.Apple Fruit.Banana", SimpleInvokeStr(lib, "main")); | |
1556 | |
1557 // Insert 'Cantalope'. | |
1558 | |
1559 const char* kReloadScript = | |
1560 "enum Fruit {\n" | |
1561 " Apple,\n" | |
1562 " Cantalope,\n" | |
1563 " Banana\n" | |
1564 "}\n" | |
1565 "var x;\n" | |
1566 "main() {\n" | |
1567 " String r = '';\n" | |
1568 " r += Fruit.Apple.toString();\n" | |
1569 " r += ' ';\n" | |
1570 " r += Fruit.Cantalope.toString();\n" | |
1571 " r += ' ';\n" | |
1572 " r += Fruit.Banana.toString();\n" | |
1573 " return r;\n" | |
1574 "}\n"; | |
1575 | |
1576 lib = TestCase::ReloadTestScript(kReloadScript); | |
1577 EXPECT_VALID(lib); | |
1578 | |
1579 EXPECT_STREQ("Fruit.Apple Fruit.Cantalope Fruit.Banana", | |
1580 SimpleInvokeStr(lib, "main")); | |
1581 } | |
1582 | |
1583 | |
1584 TEST_CASE(IsolateReload_DirectSubclasses_Success) { | |
1585 Object& new_subclass = Object::Handle(); | |
1586 String& name = String::Handle(); | |
1587 | |
1588 // Lookup the Iterator class by name from the dart core library. | |
1589 ObjectStore* object_store = Isolate::Current()->object_store(); | |
1590 const Library& core_lib = Library::Handle(object_store->core_library()); | |
1591 name = String::New("Iterator"); | |
1592 const Class& iterator_cls = Class::Handle(core_lib.LookupClass(name)); | |
1593 | |
1594 // Keep track of how many subclasses an Iterator has. | |
1595 const GrowableObjectArray& subclasses = | |
1596 GrowableObjectArray::Handle(iterator_cls.direct_subclasses()); | |
1597 intptr_t saved_subclass_count = subclasses.Length(); | |
1598 | |
1599 const char* kScript = | |
1600 "class AIterator extends Iterator {\n" | |
1601 "}\n" | |
1602 "main() {\n" | |
1603 " return 1;\n" | |
1604 "}\n"; | |
1605 | |
1606 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1607 EXPECT_VALID(lib); | |
1608 EXPECT_EQ(1, SimpleInvoke(lib, "main")); | |
1609 | |
1610 // Iterator has one non-core subclass. | |
1611 EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); | |
1612 | |
1613 // The new subclass is named AIterator. | |
1614 new_subclass = subclasses.At(subclasses.Length() - 1); | |
1615 name = Class::Cast(new_subclass).Name(); | |
1616 EXPECT_STREQ("AIterator", name.ToCString()); | |
1617 | |
1618 const char* kReloadScript = | |
1619 "class AIterator {\n" | |
1620 "}\n" | |
1621 "class BIterator extends Iterator {\n" | |
1622 "}\n" | |
1623 "main() {\n" | |
1624 " return 2;\n" | |
1625 "}\n"; | |
1626 | |
1627 lib = TestCase::ReloadTestScript(kReloadScript); | |
1628 EXPECT_VALID(lib); | |
1629 EXPECT_EQ(2, SimpleInvoke(lib, "main")); | |
1630 | |
1631 // Iterator still has only one non-core subclass (AIterator is gone). | |
1632 EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); | |
1633 | |
1634 // The new subclass is named BIterator. | |
1635 new_subclass = subclasses.At(subclasses.Length() - 1); | |
1636 name = Class::Cast(new_subclass).Name(); | |
1637 EXPECT_STREQ("BIterator", name.ToCString()); | |
1638 } | |
1639 | |
1640 | |
1641 TEST_CASE(IsolateReload_DirectSubclasses_GhostSubclass) { | |
1642 Object& new_subclass = Object::Handle(); | |
1643 String& name = String::Handle(); | |
1644 | |
1645 // Lookup the Iterator class by name from the dart core library. | |
1646 ObjectStore* object_store = Isolate::Current()->object_store(); | |
1647 const Library& core_lib = Library::Handle(object_store->core_library()); | |
1648 name = String::New("Iterator"); | |
1649 const Class& iterator_cls = Class::Handle(core_lib.LookupClass(name)); | |
1650 | |
1651 // Keep track of how many subclasses an Iterator has. | |
1652 const GrowableObjectArray& subclasses = | |
1653 GrowableObjectArray::Handle(iterator_cls.direct_subclasses()); | |
1654 intptr_t saved_subclass_count = subclasses.Length(); | |
1655 | |
1656 const char* kScript = | |
1657 "class AIterator extends Iterator {\n" | |
1658 "}\n" | |
1659 "main() {\n" | |
1660 " return 1;\n" | |
1661 "}\n"; | |
1662 | |
1663 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1664 EXPECT_VALID(lib); | |
1665 EXPECT_EQ(1, SimpleInvoke(lib, "main")); | |
1666 | |
1667 // Iterator has one new subclass. | |
1668 EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); | |
1669 | |
1670 // The new subclass is named AIterator. | |
1671 new_subclass = subclasses.At(subclasses.Length() - 1); | |
1672 name = Class::Cast(new_subclass).Name(); | |
1673 EXPECT_STREQ("AIterator", name.ToCString()); | |
1674 | |
1675 const char* kReloadScript = | |
1676 "class BIterator extends Iterator {\n" | |
1677 "}\n" | |
1678 "main() {\n" | |
1679 " return 2;\n" | |
1680 "}\n"; | |
1681 | |
1682 lib = TestCase::ReloadTestScript(kReloadScript); | |
1683 EXPECT_VALID(lib); | |
1684 EXPECT_EQ(2, SimpleInvoke(lib, "main")); | |
1685 | |
1686 // Iterator has two non-core subclasses. | |
1687 EXPECT_EQ(saved_subclass_count + 2, subclasses.Length()); | |
1688 | |
1689 // The non-core subclasses are AIterator and BIterator. | |
1690 new_subclass = subclasses.At(subclasses.Length() - 2); | |
1691 name = Class::Cast(new_subclass).Name(); | |
1692 EXPECT_STREQ("AIterator", name.ToCString()); | |
1693 | |
1694 new_subclass = subclasses.At(subclasses.Length() - 1); | |
1695 name = Class::Cast(new_subclass).Name(); | |
1696 EXPECT_STREQ("BIterator", name.ToCString()); | |
1697 } | |
1698 | |
1699 | |
1700 // Make sure that we restore the direct subclass info when we revert. | |
1701 TEST_CASE(IsolateReload_DirectSubclasses_Failure) { | |
1702 Object& new_subclass = Object::Handle(); | |
1703 String& name = String::Handle(); | |
1704 | |
1705 // Lookup the Iterator class by name from the dart core library. | |
1706 ObjectStore* object_store = Isolate::Current()->object_store(); | |
1707 const Library& core_lib = Library::Handle(object_store->core_library()); | |
1708 name = String::New("Iterator"); | |
1709 const Class& iterator_cls = Class::Handle(core_lib.LookupClass(name)); | |
1710 | |
1711 // Keep track of how many subclasses an Iterator has. | |
1712 const GrowableObjectArray& subclasses = | |
1713 GrowableObjectArray::Handle(iterator_cls.direct_subclasses()); | |
1714 intptr_t saved_subclass_count = subclasses.Length(); | |
1715 | |
1716 const char* kScript = | |
1717 "class AIterator extends Iterator {\n" | |
1718 "}\n" | |
1719 "class Foo {\n" | |
1720 " final a;\n" | |
1721 " Foo(this.a);\n" | |
1722 "}\n" | |
1723 "main() {\n" | |
1724 " new Foo(5);\n" // Force Foo to be finalized. | |
1725 " return 1;\n" | |
1726 "}\n"; | |
1727 | |
1728 Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); | |
1729 EXPECT_VALID(lib); | |
1730 EXPECT_EQ(1, SimpleInvoke(lib, "main")); | |
1731 | |
1732 // Iterator has one non-core subclass... | |
1733 EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); | |
1734 | |
1735 // ... and the non-core subclass is named AIterator. | |
1736 new_subclass = subclasses.At(subclasses.Length() - 1); | |
1737 name = Class::Cast(new_subclass).Name(); | |
1738 EXPECT_STREQ("AIterator", name.ToCString()); | |
1739 | |
1740 // Attempt to reload with a bogus script. | |
1741 const char* kReloadScript = | |
1742 "class BIterator extends Iterator {\n" | |
1743 "}\n" | |
1744 "class Foo {\n" | |
1745 " final a kjsdf ksjdf ;\n" // When we refinalize, we get an error. | |
1746 " Foo(this.a);\n" | |
1747 "}\n" | |
1748 "main() {\n" | |
1749 " new Foo(5);\n" | |
1750 " return 2;\n" | |
1751 "}\n"; | |
1752 | |
1753 lib = TestCase::ReloadTestScript(kReloadScript); | |
1754 EXPECT_ERROR(lib, "unexpected token"); | |
1755 | |
1756 // If we don't clean up the subclasses, we would find BIterator in | |
1757 // the list of subclasses, which would be bad. Make sure that | |
1758 // Iterator still has only one non-core subclass... | |
1759 EXPECT_EQ(saved_subclass_count + 1, subclasses.Length()); | |
1760 | |
1761 // ...and the non-core subclass is still named AIterator. | |
1762 new_subclass = subclasses.At(subclasses.Length() - 1); | |
1763 name = Class::Cast(new_subclass).Name(); | |
1764 EXPECT_STREQ("AIterator", name.ToCString()); | |
1765 } | |
1766 | |
1767 #endif // !PRODUCT | |
rmacnak
2016/05/11 19:56:18
Missing cases with schema changes (add/remove fiel
Cutch
2016/05/12 15:50:14
Added a test cases for field addition and deletion
| |
1768 | |
1769 } // namespace dart | |
OLD | NEW |