| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library trydart.compilation; | |
| 6 | |
| 7 import 'dart:html' show | |
| 8 Blob, | |
| 9 Element, | |
| 10 ErrorEvent, | |
| 11 IFrameElement, | |
| 12 MessageEvent, | |
| 13 Url, | |
| 14 Worker, | |
| 15 window; | |
| 16 | |
| 17 import 'dart:isolate' show | |
| 18 ReceivePort, | |
| 19 SendPort; | |
| 20 | |
| 21 import 'editor.dart' show | |
| 22 addDiagnostic, | |
| 23 isMalformedInput; | |
| 24 | |
| 25 import 'run.dart' show | |
| 26 makeOutputFrame; | |
| 27 | |
| 28 import 'ui.dart' show | |
| 29 buildButton, | |
| 30 interaction, | |
| 31 outputDiv, | |
| 32 outputFrame; | |
| 33 | |
| 34 import 'settings.dart' show | |
| 35 alwaysRunInWorker, | |
| 36 alwaysRunInIframe, | |
| 37 communicateViaBlobs, | |
| 38 incrementalCompilation, | |
| 39 minified, | |
| 40 onlyAnalyze, | |
| 41 verboseCompiler; | |
| 42 | |
| 43 import 'iframe_error_handler.dart' show | |
| 44 errorStream; | |
| 45 | |
| 46 /** | |
| 47 * Scheme for recognizing files stored in memory. | |
| 48 * | |
| 49 * From http://tools.ietf.org/html/bcp35#section-2.8: | |
| 50 * | |
| 51 * Organizations that desire a private name space for URI scheme names | |
| 52 * are encouraged to use a prefix based on their domain name, expressed | |
| 53 * in reverse order. For example, a URI scheme name of com-example-info | |
| 54 * might be registered by the vendor that owns the example.com domain | |
| 55 * name. | |
| 56 */ | |
| 57 const String PRIVATE_SCHEME = 'org-trydart'; | |
| 58 | |
| 59 SendPort compilerPort; | |
| 60 | |
| 61 // TODO(ahe): Remove this. | |
| 62 String get currentSource => window.localStorage['currentSource']; | |
| 63 | |
| 64 void set currentSource(String text) { | |
| 65 window.localStorage['currentSource'] = text; | |
| 66 } | |
| 67 | |
| 68 bool startCompilation() { | |
| 69 if (!CompilationProcess.shouldStartCompilation()) return false; | |
| 70 new CompilationProcess(currentSource, outputDiv).start(); | |
| 71 return true; | |
| 72 } | |
| 73 | |
| 74 class CompilationProcess { | |
| 75 final String source; | |
| 76 final Element console; | |
| 77 final ReceivePort receivePort = new ReceivePort(); | |
| 78 final Set<String> seenMessages = new Set<String>(); | |
| 79 bool isDone = false; | |
| 80 bool usesDartHtml = false; | |
| 81 Worker worker; | |
| 82 List<String> objectUrls = <String>[]; | |
| 83 String firstError; | |
| 84 | |
| 85 static CompilationProcess current; | |
| 86 | |
| 87 CompilationProcess(this.source, this.console); | |
| 88 | |
| 89 static bool shouldStartCompilation() { | |
| 90 if (compilerPort == null) return false; | |
| 91 if (isMalformedInput) return false; | |
| 92 if (current != null) return current.isDone; | |
| 93 return true; | |
| 94 } | |
| 95 | |
| 96 void start() { | |
| 97 if (!shouldStartCompilation()) { | |
| 98 receivePort.close(); | |
| 99 return; | |
| 100 } | |
| 101 if (current != null) current.dispose(); | |
| 102 current = this; | |
| 103 var options = [ | |
| 104 '--analyze-main', | |
| 105 '--no-source-maps', | |
| 106 ]; | |
| 107 if (verboseCompiler) options.add('--verbose'); | |
| 108 if (minified) options.add('--minify'); | |
| 109 if (onlyAnalyze) options.add('--analyze-only'); | |
| 110 if (incrementalCompilation.value) { | |
| 111 options.addAll(['--incremental-support', '--disable-type-inference']); | |
| 112 } | |
| 113 interaction.compilationStarting(); | |
| 114 compilerPort.send([['options', options], receivePort.sendPort]); | |
| 115 compilerPort.send([['communicateViaBlobs', communicateViaBlobs.value], | |
| 116 receivePort.sendPort]); | |
| 117 receivePort.listen(onMessage); | |
| 118 compilerPort.send([source, receivePort.sendPort]); | |
| 119 } | |
| 120 | |
| 121 void dispose() { | |
| 122 if (worker != null) worker.terminate(); | |
| 123 objectUrls.forEach(Url.revokeObjectUrl); | |
| 124 } | |
| 125 | |
| 126 onMessage(message) { | |
| 127 String kind = message is String ? message : message[0]; | |
| 128 var data = (message is List && message.length == 2) ? message[1] : null; | |
| 129 switch (kind) { | |
| 130 case 'done': return onDone(data); | |
| 131 case 'url': return onUrl(data); | |
| 132 case 'code': return onCode(data); | |
| 133 case 'diagnostic': return onDiagnostic(data); | |
| 134 case 'crash': return onCrash(data); | |
| 135 case 'failed': return onFail(data); | |
| 136 case 'dart:html': return onDartHtml(data); | |
| 137 default: | |
| 138 throw ['Unknown message kind', message]; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 onDartHtml(_) { | |
| 143 usesDartHtml = true; | |
| 144 } | |
| 145 | |
| 146 onFail(_) { | |
| 147 interaction.onCompilationFailed(firstError); | |
| 148 } | |
| 149 | |
| 150 onDone(_) { | |
| 151 interaction.onCompilationDone(); | |
| 152 isDone = true; | |
| 153 receivePort.close(); | |
| 154 } | |
| 155 | |
| 156 // This is called in browsers that support creating Object URLs in a | |
| 157 // web worker. For example, Chrome and Firefox 21. | |
| 158 onUrl(String url) { | |
| 159 objectUrls.add(url); | |
| 160 String wrapper = ''' | |
| 161 // Fool isolate_helper.dart so it does not think this is an isolate. | |
| 162 var window = self; | |
| 163 function dartPrint(msg) { | |
| 164 self.postMessage(msg); | |
| 165 }; | |
| 166 self.importScripts("$url"); | |
| 167 '''; | |
| 168 var wrapperUrl = | |
| 169 Url.createObjectUrl(new Blob([wrapper], 'application/javascript')); | |
| 170 objectUrls.add(wrapperUrl); | |
| 171 | |
| 172 run(wrapperUrl, () => makeOutputFrame(url)); | |
| 173 } | |
| 174 | |
| 175 // This is called in browsers that do not support creating Object | |
| 176 // URLs in a web worker. For example, Safari and Firefox < 21. | |
| 177 onCode(String code) { | |
| 178 IFrameElement makeIframe() { | |
| 179 // The obvious thing would be to call [makeOutputFrame], but | |
| 180 // Safari doesn't support access to Object URLs in an iframe. | |
| 181 | |
| 182 IFrameElement frame = new IFrameElement() | |
| 183 ..src = 'iframe.html' | |
| 184 ..style.width = '100%' | |
| 185 ..style.height = '0px'; | |
| 186 frame.onLoad.listen((_) { | |
| 187 frame.contentWindow.postMessage(['source', code], '*'); | |
| 188 }); | |
| 189 return frame; | |
| 190 } | |
| 191 | |
| 192 String codeWithPrint = | |
| 193 '$code\n' | |
| 194 'function dartPrint(msg) { postMessage(msg); }\n'; | |
| 195 var url = | |
| 196 Url.createObjectUrl( | |
| 197 new Blob([codeWithPrint], 'application/javascript')); | |
| 198 objectUrls.add(url); | |
| 199 | |
| 200 run(url, makeIframe); | |
| 201 } | |
| 202 | |
| 203 void run(String url, IFrameElement makeIframe()) { | |
| 204 void retryInIframe() { | |
| 205 interaction.aboutToRun(); | |
| 206 var frame = makeIframe(); | |
| 207 frame.style | |
| 208 ..visibility = 'hidden' | |
| 209 ..position = 'absolute'; | |
| 210 outputFrame.parent.insertBefore(frame, outputFrame); | |
| 211 outputFrame = frame; | |
| 212 errorStream(frame).listen(interaction.onIframeError); | |
| 213 } | |
| 214 void onError(String errorMessage) { | |
| 215 interaction.consolePrintLine(errorMessage); | |
| 216 console | |
| 217 ..append(buildButton('Try in iframe', (_) => retryInIframe())) | |
| 218 ..appendText('\n'); | |
| 219 } | |
| 220 interaction.aboutToRun(); | |
| 221 if (alwaysRunInIframe.value || | |
| 222 usesDartHtml && !alwaysRunInWorker) { | |
| 223 retryInIframe(); | |
| 224 } else { | |
| 225 runInWorker(url, onError); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 void runInWorker(String url, void onError(String errorMessage)) { | |
| 230 worker = new Worker(url) | |
| 231 ..onMessage.listen((MessageEvent event) { | |
| 232 interaction.consolePrintLine(event.data); | |
| 233 }) | |
| 234 ..onError.listen((ErrorEvent event) { | |
| 235 worker.terminate(); | |
| 236 worker = null; | |
| 237 onError(event.message); | |
| 238 }); | |
| 239 } | |
| 240 | |
| 241 onDiagnostic(Map<String, dynamic> diagnostic) { | |
| 242 if (currentSource != source) return; | |
| 243 String kind = diagnostic['kind']; | |
| 244 String message = diagnostic['message']; | |
| 245 if (kind == 'verbose info') { | |
| 246 interaction.verboseCompilerMessage(message); | |
| 247 return; | |
| 248 } | |
| 249 if (kind == 'error' && firstError == null) { | |
| 250 firstError = message; | |
| 251 } | |
| 252 String uri = diagnostic['uri']; | |
| 253 if (uri != '${PRIVATE_SCHEME}:/main.dart') { | |
| 254 interaction.consolePrintLine('$uri: [$kind] $message'); | |
| 255 return; | |
| 256 } | |
| 257 int begin = diagnostic['begin']; | |
| 258 int end = diagnostic['end']; | |
| 259 if (begin == null) return; | |
| 260 if (seenMessages.add('$begin:$end: [$kind] $message')) { | |
| 261 // Guard against duplicated messages. | |
| 262 addDiagnostic(kind, message, begin, end); | |
| 263 } | |
| 264 } | |
| 265 | |
| 266 onCrash(data) { | |
| 267 interaction.onCompilerCrash(data); | |
| 268 } | |
| 269 } | |
| OLD | NEW |