OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino 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.md file. | |
4 | |
5 library fletchc.verbs.implementation; | |
6 | |
7 import 'dart:async' show | |
8 Future, | |
9 StreamIterator; | |
10 | |
11 export 'dart:async' show | |
12 Future, | |
13 StreamIterator; | |
14 | |
15 // Don't export this. | |
16 import 'package:compiler/src/filenames.dart' show | |
17 appendSlash; | |
18 | |
19 // Don't export most of these. | |
20 import '../hub/sentence_parser.dart' show | |
21 ErrorTarget, | |
22 NamedTarget, | |
23 Preposition, | |
24 PrepositionKind, | |
25 Sentence, | |
26 Target, | |
27 TargetKind, | |
28 Verb; | |
29 | |
30 export '../hub/sentence_parser.dart' show | |
31 TargetKind; | |
32 | |
33 import '../diagnostic.dart' show | |
34 DiagnosticKind, | |
35 throwFatalError; | |
36 | |
37 export '../diagnostic.dart' show | |
38 DiagnosticKind, | |
39 throwFatalError; | |
40 | |
41 import '../hub/client_commands.dart' show | |
42 CommandSender, | |
43 ClientCommand; | |
44 | |
45 export '../hub/client_commands.dart' show | |
46 CommandSender, | |
47 ClientCommand; | |
48 | |
49 import '../hub/session_manager.dart' show | |
50 FletchCompiler, | |
51 FletchDelta, | |
52 IncrementalCompiler, | |
53 WorkerConnection, | |
54 Session, | |
55 SessionState, | |
56 UserSession, | |
57 currentSession; | |
58 | |
59 export '../hub/session_manager.dart' show | |
60 FletchCompiler, | |
61 FletchDelta, | |
62 IncrementalCompiler, | |
63 WorkerConnection, | |
64 Session, | |
65 SessionState, | |
66 UserSession, | |
67 currentSession; | |
68 | |
69 export '../diagnostic.dart' show | |
70 DiagnosticKind, | |
71 throwFatalError; | |
72 | |
73 import '../hub/session_manager.dart' show | |
74 SessionState, | |
75 UserSession, | |
76 createSession; | |
77 | |
78 export '../hub/session_manager.dart' show | |
79 SessionState, | |
80 UserSession, | |
81 createSession; | |
82 | |
83 import '../hub/hub_main.dart' show | |
84 ClientConnection, | |
85 IsolatePool; | |
86 | |
87 export '../hub/hub_main.dart' show | |
88 ClientConnection, | |
89 IsolatePool; | |
90 | |
91 import '../hub/session_manager.dart' show | |
92 lookupSession; // Don't export this. | |
93 | |
94 import 'actions.dart' show | |
95 Action; | |
96 | |
97 export 'actions.dart' show | |
98 Action; | |
99 | |
100 import 'options.dart' show | |
101 Options; | |
102 | |
103 export 'options.dart' show | |
104 Options; | |
105 | |
106 import 'documentation.dart' show | |
107 helpDocumentation; | |
108 | |
109 import '../guess_configuration.dart' show | |
110 fletchVersion; | |
111 | |
112 void reportErroneousTarget(ErrorTarget target) { | |
113 throwFatalError(target.errorKind, userInput: target.userInput); | |
114 } | |
115 | |
116 AnalyzedSentence helpSentence(String message) { | |
117 Future<int> printHelp(_,__) async { | |
118 print(message); | |
119 return 0; | |
120 } | |
121 Action contextHelp = new Action(printHelp, null); | |
122 return new AnalyzedSentence( | |
123 new Verb("?", contextHelp), null, null, null, null, null, null, | |
124 null, null, null, null); | |
125 } | |
126 | |
127 AnalyzedSentence analyzeSentence(Sentence sentence, Options options) { | |
128 // Check the sentence's version matches the persistent process' version. | |
129 if (sentence.version != null && sentence.version != fletchVersion) { | |
130 throwFatalError( | |
131 DiagnosticKind.compilerVersionMismatch, | |
132 userInput: fletchVersion, | |
133 additionalUserInput: sentence.version); | |
134 } | |
135 if (options != null && options.version) { | |
136 return helpSentence(fletchVersion); | |
137 } | |
138 if (sentence.verb.isErroneous) { | |
139 sentence.verb.action.perform(null, null); | |
140 } | |
141 | |
142 sentence.targets.where((Target t) => t.isErroneous) | |
143 .forEach(reportErroneousTarget); | |
144 sentence.prepositions.map((p) => p.target).where((Target t) => t.isErroneous) | |
145 .forEach(reportErroneousTarget); | |
146 | |
147 Uri base = Uri.base; | |
148 if (sentence.currentDirectory != null) { | |
149 base = fileUri(appendSlash(sentence.currentDirectory), base); | |
150 } | |
151 Verb verb = sentence.verb; | |
152 Action action = verb.action; | |
153 List<String> trailing = sentence.trailing; | |
154 | |
155 for (Target target in sentence.targets) { | |
156 if (target.kind == TargetKind.HELP) { | |
157 return helpSentence(action.documentation); | |
158 } | |
159 } | |
160 | |
161 NamedTarget inSession; | |
162 Uri toUri; | |
163 Uri withUri; | |
164 | |
165 /// Validates a preposition of kind `in`. For now, the only possible legal | |
166 /// target is of kind `session`. Store such as session in [inSession]. | |
167 void checkInTarget(Preposition preposition) { | |
168 assert(preposition.kind == PrepositionKind.IN); | |
169 if (preposition.target.kind == TargetKind.SESSION) { | |
170 if (inSession != null) { | |
171 throwFatalError( | |
172 DiagnosticKind.duplicatedIn, preposition: preposition); | |
173 } | |
174 inSession = preposition.target; | |
175 if (!action.requiresSession) { | |
176 throwFatalError( | |
177 DiagnosticKind.verbRequiresNoSession, | |
178 verb: verb, sessionName: inSession.name); | |
179 } | |
180 } else { | |
181 throwFatalError( | |
182 DiagnosticKind.cantPerformVerbIn, | |
183 verb: verb, target: preposition.target); | |
184 } | |
185 } | |
186 | |
187 /// Validates a preposition of kind `to`. For now, the only possible legal | |
188 /// target is of kind `file`. Store such a file in [toUri]. | |
189 void checkToTarget(Preposition preposition) { | |
190 assert(preposition.kind == PrepositionKind.TO); | |
191 if (preposition.target.kind == TargetKind.FILE) { | |
192 if (toUri != null) { | |
193 throwFatalError( | |
194 DiagnosticKind.duplicatedTo, preposition: preposition); | |
195 } | |
196 NamedTarget target = preposition.target; | |
197 toUri = fileUri(target.name, base); | |
198 if (!action.requiresToUri) { | |
199 throwFatalError( | |
200 DiagnosticKind.verbRequiresNoToFile, | |
201 verb: verb, userInput: target.name); | |
202 } | |
203 } else { | |
204 throwFatalError( | |
205 DiagnosticKind.cantPerformVerbTo, | |
206 verb: verb, target: preposition.target); | |
207 } | |
208 } | |
209 | |
210 /// Validates a preposition of kind `with`. For now, the only possible legal | |
211 /// target is of kind `file`. Store such a file in [withUri]. | |
212 void checkWithTarget(Preposition preposition) { | |
213 assert(preposition.kind == PrepositionKind.WITH); | |
214 if (preposition.target.kind == TargetKind.FILE) { | |
215 if (withUri != null) { | |
216 throwFatalError( | |
217 DiagnosticKind.duplicatedWith, preposition: preposition); | |
218 } | |
219 NamedTarget target = preposition.target; | |
220 withUri = fileUri(target.name, base); | |
221 if (!action.supportsWithUri) { | |
222 throwFatalError( | |
223 DiagnosticKind.verbRequiresNoWithFile, | |
224 verb: verb, userInput: target.name); | |
225 } | |
226 } else { | |
227 throwFatalError( | |
228 DiagnosticKind.cantPerformVerbWith, | |
229 verb: verb, target: preposition.target); | |
230 } | |
231 } | |
232 | |
233 Target target; | |
234 Target secondaryTarget; | |
235 Iterator<Target> targets = sentence.targets.iterator; | |
236 if (targets.moveNext()) { | |
237 target = targets.current; | |
238 } | |
239 if (targets.moveNext()) { | |
240 secondaryTarget = targets.current; | |
241 } | |
242 while (targets.moveNext()) { | |
243 throwFatalError( | |
244 DiagnosticKind.verbDoesNotSupportTarget, verb: verb, target: target); | |
245 } | |
246 if (secondaryTarget != null) { | |
247 if (secondaryTarget.kind == TargetKind.FILE) { | |
248 NamedTarget target = secondaryTarget; | |
249 if (action.requiresToUri) { | |
250 toUri = fileUri(target.name, base); | |
251 } else { | |
252 throwFatalError( | |
253 DiagnosticKind.verbRequiresNoToFile, | |
254 verb: verb, userInput: target.name); | |
255 } | |
256 } else { | |
257 throwFatalError( | |
258 DiagnosticKind.cantPerformVerbTo, | |
259 verb: verb, target: secondaryTarget); | |
260 } | |
261 } | |
262 | |
263 for (Preposition preposition in sentence.prepositions) { | |
264 switch (preposition.kind) { | |
265 case PrepositionKind.IN: | |
266 checkInTarget(preposition); | |
267 break; | |
268 | |
269 case PrepositionKind.TO: | |
270 checkToTarget(preposition); | |
271 break; | |
272 | |
273 case PrepositionKind.WITH: | |
274 checkWithTarget(preposition); | |
275 break; | |
276 } | |
277 } | |
278 | |
279 if (action.requiresToUri && toUri == null) { | |
280 throwFatalError(DiagnosticKind.missingToFile); | |
281 } | |
282 | |
283 if (!action.allowsTrailing) { | |
284 if (trailing != null) { | |
285 throwFatalError( | |
286 DiagnosticKind.extraArguments, userInput: trailing.join(' ')); | |
287 } | |
288 } | |
289 | |
290 TargetKind requiredTarget = action.requiredTarget; | |
291 | |
292 if (target != null && | |
293 requiredTarget == null && | |
294 action.supportedTargets == null) { | |
295 throwFatalError( | |
296 DiagnosticKind.verbDoesntSupportTarget, verb: verb, target: target); | |
297 } | |
298 | |
299 if (action.requiresTarget) { | |
300 if (target == null) { | |
301 switch (requiredTarget) { | |
302 case TargetKind.TCP_SOCKET: | |
303 throwFatalError(DiagnosticKind.noTcpSocketTarget); | |
304 break; | |
305 | |
306 case TargetKind.FILE: | |
307 throwFatalError(DiagnosticKind.noFileTarget); | |
308 break; | |
309 | |
310 default: | |
311 if (action.requiresTargetSession) { | |
312 throwFatalError( | |
313 DiagnosticKind.verbRequiresSessionTarget, verb: verb); | |
314 } else if (requiredTarget != null) { | |
315 throwFatalError( | |
316 DiagnosticKind.verbRequiresSpecificTarget, verb: verb, | |
317 requiredTarget: requiredTarget); | |
318 } else { | |
319 throwFatalError( | |
320 DiagnosticKind.verbRequiresTarget, verb: verb); | |
321 } | |
322 break; | |
323 } | |
324 } else if (requiredTarget != null && target.kind != requiredTarget) { | |
325 switch (requiredTarget) { | |
326 case TargetKind.TCP_SOCKET: | |
327 throwFatalError( | |
328 DiagnosticKind.verbRequiresSocketTarget, | |
329 verb: verb, target: target); | |
330 break; | |
331 | |
332 case TargetKind.FILE: | |
333 throwFatalError( | |
334 DiagnosticKind.verbRequiresFileTarget, | |
335 verb: verb, target: target); | |
336 break; | |
337 | |
338 default: | |
339 throwFatalError( | |
340 DiagnosticKind.verbRequiresSpecificTargetButGot, | |
341 verb: verb, target: target, | |
342 requiredTarget: requiredTarget); | |
343 } | |
344 } | |
345 } | |
346 | |
347 if (action.supportedTargets != null && target != null) { | |
348 if (!action.supportedTargets.contains(target.kind)) { | |
349 throwFatalError( | |
350 DiagnosticKind.verbDoesNotSupportTarget, verb: verb, target: target); | |
351 } | |
352 } | |
353 | |
354 String sessionName; | |
355 if (inSession != null) { | |
356 sessionName = inSession.name; | |
357 if (sessionName == null) { | |
358 throwFatalError(DiagnosticKind.missingSessionName); | |
359 } | |
360 } else if (action.requiresSession) { | |
361 sessionName = currentSession; | |
362 } else if (action.requiresTargetSession && | |
363 target is NamedTarget && | |
364 target.name == null) { | |
365 throwFatalError(DiagnosticKind.missingSessionName); | |
366 } | |
367 | |
368 String targetName; | |
369 Uri targetUri; | |
370 if (target is NamedTarget) { | |
371 targetName = target.name; | |
372 if (target.kind == TargetKind.FILE) { | |
373 targetUri = fileUri(targetName, base); | |
374 } | |
375 } | |
376 | |
377 Uri programName = | |
378 sentence.programName == null ? null : fileUri(sentence.programName, base); | |
379 return new AnalyzedSentence( | |
380 verb, target, targetName, trailing, sessionName, base, programName, | |
381 targetUri, toUri, withUri, options); | |
382 } | |
383 | |
384 Uri fileUri(String path, Uri base) => base.resolveUri(new Uri.file(path)); | |
385 | |
386 abstract class VerbContext { | |
387 final ClientConnection clientConnection; | |
388 | |
389 final IsolatePool pool; | |
390 | |
391 final UserSession session; | |
392 | |
393 VerbContext(this.clientConnection, this.pool, this.session); | |
394 | |
395 Future<int> performTaskInWorker(SharedTask task); | |
396 | |
397 VerbContext copyWithSession(UserSession session); | |
398 } | |
399 | |
400 /// Represents a task that is shared between the hub (main isolate) and a worker | |
401 /// isolate. Since instances of this class are sent from the hub (main isolate) | |
402 /// to a worker isolate, they should be kept simple: | |
403 /// | |
404 /// * Pay attention to the transitive closure of its fields. The closure | |
405 /// should be kept as small as possible to avoid too much copying. | |
406 /// | |
407 /// * Avoid enums and other compile-time constants in the transitive closure, | |
408 /// as they aren't canonicalized by the Dart VM, see issue 23244. | |
409 abstract class SharedTask { | |
410 const SharedTask(); | |
411 | |
412 Future<int> call( | |
413 CommandSender commandSender, | |
414 StreamIterator<ClientCommand> commandIterator); | |
415 } | |
416 | |
417 class AnalyzedSentence { | |
418 final Verb verb; | |
419 | |
420 final Target target; | |
421 | |
422 final String targetName; | |
423 | |
424 final List<String> trailing; | |
425 | |
426 final String sessionName; | |
427 | |
428 /// The current working directory of the C++ client. | |
429 final Uri base; | |
430 | |
431 final Uri programName; | |
432 | |
433 /// Value of 'file NAME' converted to a Uri (main target, no preposition). | |
434 final Uri targetUri; | |
435 | |
436 /// Value of 'to file NAME' converted to a Uri. | |
437 final Uri toTargetUri; | |
438 | |
439 /// Value of 'with <URI>' converted to a Uri. | |
440 final Uri withUri; | |
441 | |
442 final Options options; | |
443 | |
444 AnalyzedSentence( | |
445 this.verb, | |
446 this.target, | |
447 this.targetName, | |
448 this.trailing, | |
449 this.sessionName, | |
450 this.base, | |
451 this.programName, | |
452 this.targetUri, | |
453 this.toTargetUri, | |
454 this.withUri, | |
455 this.options); | |
456 | |
457 Future<int> performVerb(VerbContext context) { | |
458 return verb.action.perform(this, context); | |
459 } | |
460 } | |
OLD | NEW |