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 |