| 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 |