OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import "dart:math"; | 5 import "dart:math"; |
6 import "dart:typed_data"; | 6 import "dart:typed_data"; |
7 | 7 |
8 // Equivalent of calling FATAL from C++ code. | 8 // Equivalent of calling FATAL from C++ code. |
9 _fatal(msg) native "DartCore_fatal"; | 9 _fatal(msg) native "DartCore_fatal"; |
10 | 10 |
11 // We need to pass the exception and stack trace objects as second and third | |
12 // parameter to the continuation. See vm/ast_transformer.cc for usage. | |
13 void _asyncCatchHelper(catchFunction, continuation) { | |
14 catchFunction((e, s) => continuation(null, e, s)); | |
15 } | |
16 | |
17 // The members of this class are cloned and added to each class that | 11 // The members of this class are cloned and added to each class that |
18 // represents an enum type. | 12 // represents an enum type. |
19 class _EnumHelper { | 13 class _EnumHelper { |
20 // Declare the list of enum value names private. When this field is | 14 // Declare the list of enum value names private. When this field is |
21 // cloned into a user-defined enum class, the field will be inaccessible | 15 // cloned into a user-defined enum class, the field will be inaccessible |
22 // because of the library-specific name suffix. The toString() function | 16 // because of the library-specific name suffix. The toString() function |
23 // below can access it because it uses the same name suffix. | 17 // below can access it because it uses the same name suffix. |
24 static const List<String> _enum_names = null; | 18 static const List<String> _enum_names = null; |
25 String toString() => _enum_names[index]; | 19 String toString() => _enum_names[index]; |
26 int get hashCode => _enum_names[index].hashCode; | 20 int get hashCode => _enum_names[index].hashCode; |
27 } | 21 } |
28 | 22 |
29 | |
30 // _AsyncStarStreamController is used by the compiler to implement | |
31 // async* generator functions. | |
32 class _AsyncStarStreamController { | |
33 StreamController controller; | |
34 Function asyncStarBody; | |
35 bool isAdding = false; | |
36 bool onListenReceived = false; | |
37 bool isScheduled = false; | |
38 bool isSuspendedAtYield = false; | |
39 Completer cancellationCompleter = null; | |
40 | |
41 Stream get stream => controller.stream; | |
42 | |
43 void runBody() { | |
44 isScheduled = false; | |
45 isSuspendedAtYield = false; | |
46 asyncStarBody(); | |
47 } | |
48 | |
49 void scheduleGenerator() { | |
50 if (isScheduled || controller.isPaused || isAdding) { | |
51 return; | |
52 } | |
53 isScheduled = true; | |
54 scheduleMicrotask(runBody); | |
55 } | |
56 | |
57 // Adds element to steam, returns true if the caller should terminate | |
58 // execution of the generator. | |
59 // | |
60 // TODO(hausner): Per spec, the generator should be suspended before | |
61 // exiting when the stream is closed. We could add a getter like this: | |
62 // get isCancelled => controller.hasListener; | |
63 // The generator would translate a 'yield e' statement to | |
64 // controller.add(e); | |
65 // suspend; | |
66 // if (controller.isCancelled) return; | |
67 bool add(event) { | |
68 if (!onListenReceived) _fatal("yield before stream is listened to!"); | |
69 if (isSuspendedAtYield) _fatal("unexpected yield"); | |
70 // If stream is cancelled, tell caller to exit the async generator. | |
71 if (!controller.hasListener) { | |
72 return true; | |
73 } | |
74 controller.add(event); | |
75 scheduleGenerator(); | |
76 isSuspendedAtYield = true; | |
77 return false; | |
78 } | |
79 | |
80 // Adds the elements of stream into this controller's stream. | |
81 // The generator will be scheduled again when all of the | |
82 // elements of the added stream have been consumed. | |
83 // Returns true if the caller should terminate | |
84 // execution of the generator. | |
85 bool addStream(Stream stream) { | |
86 if (!onListenReceived) _fatal("yield before stream is listened to!"); | |
87 // If stream is cancelled, tell caller to exit the async generator. | |
88 if (!controller.hasListener) return true; | |
89 isAdding = true; | |
90 var whenDoneAdding = | |
91 controller.addStream(stream as Stream, cancelOnError: false); | |
92 whenDoneAdding.then((_) { | |
93 isAdding = false; | |
94 scheduleGenerator(); | |
95 }); | |
96 return false; | |
97 } | |
98 | |
99 void addError(error, stackTrace) { | |
100 if ((cancellationCompleter != null) && !cancellationCompleter.isCompleted) { | |
101 // If the stream has been cancelled, complete the cancellation future | |
102 // with the error. | |
103 cancellationCompleter.completeError(error, stackTrace); | |
104 return; | |
105 } | |
106 // If stream is cancelled, tell caller to exit the async generator. | |
107 if (!controller.hasListener) return; | |
108 controller.addError(error, stackTrace); | |
109 // No need to schedule the generator body here. This code is only | |
110 // called from the catch clause of the implicit try-catch-finally | |
111 // around the generator body. That is, we are on the error path out | |
112 // of the generator and do not need to run the generator again. | |
113 } | |
114 | |
115 close() { | |
116 if ((cancellationCompleter != null) && !cancellationCompleter.isCompleted) { | |
117 // If the stream has been cancelled, complete the cancellation future | |
118 // with the error. | |
119 cancellationCompleter.complete(); | |
120 } | |
121 controller.close(); | |
122 } | |
123 | |
124 _AsyncStarStreamController(this.asyncStarBody) { | |
125 controller = new StreamController(onListen: this.onListen, | |
126 onResume: this.onResume, | |
127 onCancel: this.onCancel); | |
128 } | |
129 | |
130 onListen() { | |
131 assert(!onListenReceived); | |
132 onListenReceived = true; | |
133 scheduleGenerator(); | |
134 } | |
135 | |
136 onResume() { | |
137 if (isSuspendedAtYield) { | |
138 scheduleGenerator(); | |
139 } | |
140 } | |
141 | |
142 onCancel() { | |
143 if (controller.isClosed) { | |
144 return null; | |
145 } | |
146 if (cancellationCompleter == null) { | |
147 cancellationCompleter = new Completer(); | |
148 scheduleGenerator(); | |
149 } | |
150 return cancellationCompleter.future; | |
151 } | |
152 } | |
153 | |
154 | |
155 // _SyncIterable and _syncIterator are used by the compiler to | 23 // _SyncIterable and _syncIterator are used by the compiler to |
156 // implement sync* generator functions. A sync* generator allocates | 24 // implement sync* generator functions. A sync* generator allocates |
157 // and returns a new _SyncIterable object. | 25 // and returns a new _SyncIterable object. |
158 | 26 |
159 typedef bool SyncGeneratorCallback(Iterator iterator); | 27 typedef bool SyncGeneratorCallback(Iterator iterator); |
160 | 28 |
161 class _SyncIterable extends IterableBase { | 29 class _SyncIterable extends IterableBase { |
162 // moveNextFn is the closurized body of the generator function. | 30 // moveNextFn is the closurized body of the generator function. |
163 final SyncGeneratorCallback moveNextFn; | 31 final SyncGeneratorCallback moveNextFn; |
164 | 32 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 if (isYieldEach) { | 66 if (isYieldEach) { |
199 // Spec mandates: it is a dynamic error if the class of [the object | 67 // Spec mandates: it is a dynamic error if the class of [the object |
200 // returned by yield*] does not implement Iterable. | 68 // returned by yield*] does not implement Iterable. |
201 yieldEachIterator = (current as Iterable).iterator; | 69 yieldEachIterator = (current as Iterable).iterator; |
202 continue; | 70 continue; |
203 } | 71 } |
204 return true; | 72 return true; |
205 } | 73 } |
206 } | 74 } |
207 } | 75 } |
OLD | NEW |