OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, 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 // Javascript preamble, that lets the output of dart2js run on V8's d8 shell. | |
6 | |
7 // Node wraps files and provides them with a different `this`. The global | |
8 // `this` can be accessed through `global`. | |
9 | |
10 var self = this; | |
11 if (typeof global != "undefined") self = global; // Node.js. | |
12 | |
13 (function(self) { | |
14 // Using strict mode to avoid accidentally defining global variables. | |
15 "use strict"; // Should be first statement of this function. | |
16 | |
17 // Location (Uri.base) | |
18 | |
19 var workingDirectory; | |
20 // TODO(sgjesse): This does not work on Windows. | |
21 if (typeof os == "object" && "system" in os) { | |
22 // V8. | |
23 workingDirectory = os.system("pwd"); | |
24 var length = workingDirectory.length; | |
25 if (workingDirectory[length - 1] == '\n') { | |
26 workingDirectory = workingDirectory.substring(0, length - 1); | |
27 } | |
28 } else if (typeof process != "undefined" && | |
29 typeof process.cwd == "function") { | |
30 // Node.js. | |
31 workingDirectory = process.cwd(); | |
32 } | |
33 self.location = { href: "file://" + workingDirectory + "/" }; | |
34 | |
35 // Event loop. | |
36 | |
37 // Task queue as cyclic list queue. | |
38 var taskQueue = new Array(8); // Length is power of 2. | |
39 var head = 0; | |
40 var tail = 0; | |
41 var mask = taskQueue.length - 1; | |
42 function addTask(elem) { | |
43 taskQueue[head] = elem; | |
44 head = (head + 1) & mask; | |
45 if (head == tail) _growTaskQueue(); | |
46 } | |
47 function removeTask() { | |
48 if (head == tail) return; | |
49 var result = taskQueue[tail]; | |
50 taskQueue[tail] = undefined; | |
51 tail = (tail + 1) & mask; | |
52 return result; | |
53 } | |
54 function _growTaskQueue() { | |
55 // head == tail. | |
56 var length = taskQueue.length; | |
57 var split = head; | |
58 taskQueue.length = length * 2; | |
59 if (split * 2 < length) { // split < length / 2 | |
60 for (var i = 0; i < split; i++) { | |
61 taskQueue[length + i] = taskQueue[i]; | |
62 taskQueue[i] = undefined; | |
63 } | |
64 head += length; | |
65 } else { | |
66 for (var i = split; i < length; i++) { | |
67 taskQueue[length + i] = taskQueue[i]; | |
68 taskQueue[i] = undefined; | |
69 } | |
70 tail += length; | |
71 } | |
72 mask = taskQueue.length - 1; | |
73 } | |
74 | |
75 // Mapping from timer id to timer function. | |
76 // The timer id is written on the function as .$timerId. | |
77 // That field is cleared when the timer is cancelled, but it is not returned | |
78 // from the queue until its time comes. | |
79 var timerIds = {}; | |
80 var timerIdCounter = 1; // Counter used to assing ids. | |
81 | |
82 // Zero-timer queue as simple array queue using push/shift. | |
83 var zeroTimerQueue = []; | |
84 | |
85 function addTimer(f, ms) { | |
86 var id = timerIdCounter++; | |
87 f.$timerId = id; | |
88 timerIds[id] = f; | |
89 if (ms == 0) { | |
90 zeroTimerQueue.push(f); | |
91 } else { | |
92 addDelayedTimer(f, ms); | |
93 } | |
94 return id; | |
95 } | |
96 | |
97 function nextZeroTimer() { | |
98 while (zeroTimerQueue.length > 0) { | |
99 var action = zeroTimerQueue.shift(); | |
100 if (action.$timerId !== undefined) return action; | |
101 } | |
102 } | |
103 | |
104 function nextEvent() { | |
105 var action = removeTask(); | |
106 if (action) { | |
107 return action; | |
108 } | |
109 do { | |
110 action = nextZeroTimer(); | |
111 if (action) break; | |
112 var nextList = nextDelayedTimerQueue(); | |
113 if (!nextList) { | |
114 return; | |
115 } | |
116 var newTime = nextList.shift(); | |
117 advanceTimeTo(newTime); | |
118 zeroTimerQueue = nextList; | |
119 } while (true) | |
120 var id = action.$timerId; | |
121 clearTimerId(action, id); | |
122 return action; | |
123 } | |
124 | |
125 // Mocking time. | |
126 var timeOffset = 0; | |
127 var now = function() { | |
128 // Install the mock Date object only once. | |
129 // Following calls to "now" will just use the new (mocked) Date.now | |
130 // method directly. | |
131 installMockDate(); | |
132 now = Date.now; | |
133 return Date.now(); | |
134 }; | |
135 var originalDate = Date; | |
136 var originalNow = originalDate.now; | |
137 function advanceTimeTo(time) { | |
138 timeOffset = time - originalNow(); | |
139 } | |
140 function installMockDate() { | |
141 var NewDate = function Date(Y, M, D, h, m, s, ms) { | |
142 if (this instanceof Date) { | |
143 // Assume a construct call. | |
144 switch (arguments.length) { | |
145 case 0: return new originalDate(originalNow() + timeOffset); | |
146 case 1: return new originalDate(Y); | |
147 case 2: return new originalDate(Y, M); | |
148 case 3: return new originalDate(Y, M, D); | |
149 case 4: return new originalDate(Y, M, D, h); | |
150 case 5: return new originalDate(Y, M, D, h, m); | |
151 case 6: return new originalDate(Y, M, D, h, m, s); | |
152 default: return new originalDate(Y, M, D, h, m, s, ms); | |
153 } | |
154 } | |
155 return new originalDate(originalNow() + timeOffset).toString(); | |
156 }; | |
157 NewDate.UTC = originalDate.UTC; | |
158 NewDate.parse = originalDate.parse; | |
159 NewDate.now = function now() { return originalNow() + timeOffset; }; | |
160 NewDate.prototype = originalDate.prototype; | |
161 originalDate.prototype.constructor = NewDate; | |
162 Date = NewDate; | |
163 } | |
164 | |
165 // Heap priority queue with key index. | |
166 // Each entry is list of [timeout, callback1 ... callbackn]. | |
167 var timerHeap = []; | |
168 var timerIndex = {}; | |
169 function addDelayedTimer(f, ms) { | |
170 var timeout = now() + ms; | |
171 var timerList = timerIndex[timeout]; | |
172 if (timerList == null) { | |
173 timerList = [timeout, f]; | |
174 timerIndex[timeout] = timerList; | |
175 var index = timerHeap.length; | |
176 timerHeap.length += 1; | |
177 bubbleUp(index, timeout, timerList); | |
178 } else { | |
179 timerList.push(f); | |
180 } | |
181 } | |
182 | |
183 function nextDelayedTimerQueue() { | |
184 if (timerHeap.length == 0) return null; | |
185 var result = timerHeap[0]; | |
186 var last = timerHeap.pop(); | |
187 if (timerHeap.length > 0) { | |
188 bubbleDown(0, last[0], last); | |
189 } | |
190 return result; | |
191 } | |
192 | |
193 function bubbleUp(index, key, value) { | |
194 while (index != 0) { | |
195 var parentIndex = (index - 1) >> 1; | |
196 var parent = timerHeap[parentIndex]; | |
197 var parentKey = parent[0]; | |
198 if (key > parentKey) break; | |
199 timerHeap[index] = parent; | |
200 index = parentIndex; | |
201 } | |
202 timerHeap[index] = value; | |
203 } | |
204 | |
205 function bubbleDown(index, key, value) { | |
206 while (true) { | |
207 var leftChildIndex = index * 2 + 1; | |
208 if (leftChildIndex >= timerHeap.length) break; | |
209 var minChildIndex = leftChildIndex; | |
210 var minChild = timerHeap[leftChildIndex]; | |
211 var minChildKey = minChild[0]; | |
212 var rightChildIndex = leftChildIndex + 1; | |
213 if (rightChildIndex < timerHeap.length) { | |
214 var rightChild = timerHeap[rightChildIndex]; | |
215 var rightKey = rightChild[0]; | |
216 if (rightKey < minChildKey) { | |
217 minChildIndex = rightChildIndex; | |
218 minChild = rightChild; | |
219 minChildKey = rightKey; | |
220 } | |
221 } | |
222 if (minChildKey > key) break; | |
223 timerHeap[index] = minChild; | |
224 index = minChildIndex; | |
225 } | |
226 timerHeap[index] = value; | |
227 } | |
228 | |
229 function addInterval(f, ms) { | |
230 var id = timerIdCounter++; | |
231 function repeat() { | |
232 // Reactivate with the same id. | |
233 repeat.$timerId = id; | |
234 timerIds[id] = repeat; | |
235 addDelayedTimer(repeat, ms); | |
236 f(); | |
237 } | |
238 repeat.$timerId = id; | |
239 timerIds[id] = repeat; | |
240 addDelayedTimer(repeat, ms); | |
241 return id; | |
242 } | |
243 | |
244 function cancelTimer(id) { | |
245 var f = timerIds[id]; | |
246 if (f == null) return; | |
247 clearTimerId(f, id); | |
248 } | |
249 | |
250 function clearTimerId(f, id) { | |
251 f.$timerId = undefined; | |
252 delete timerIds[id]; | |
253 } | |
254 | |
255 function eventLoop(action) { | |
256 while (action) { | |
257 try { | |
258 action(); | |
259 } catch (e) { | |
260 if (typeof onerror == "function") { | |
261 onerror(e, null, -1); | |
262 } else { | |
263 throw e; | |
264 } | |
265 } | |
266 action = nextEvent(); | |
267 } | |
268 } | |
269 | |
270 // Global properties. "self" refers to the global object, so adding a | |
271 // property to "self" defines a global variable. | |
272 self.self = self | |
273 self.dartMainRunner = function(main, args) { | |
274 // Initialize. | |
275 var action = function() { main(args); } | |
276 eventLoop(action); | |
277 }; | |
278 self.setTimeout = addTimer; | |
279 self.clearTimeout = cancelTimer; | |
280 self.setInterval = addInterval; | |
281 self.clearInterval = cancelTimer; | |
282 self.scheduleImmediate = addTask; | |
283 | |
284 function computeCurrentScript() { | |
285 try { | |
286 throw new Error(); | |
287 } catch(e) { | |
288 var stack = e.stack; | |
289 // The V8 stack looks like: | |
290 // at computeCurrentScript (preambles/d8.js:286:13) | |
291 // at Object.currentScript (preambles/d8.js:308:31) | |
292 // at init.currentScript (/tmp/foo.js:308:19) | |
293 // at /tmp/foo.js:320:7 | |
294 // at /tmp/foo.js:331:4 | |
295 var re = new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "mg"); | |
296 var lastMatch = null; | |
297 do { | |
298 var match = re.exec(stack); | |
299 if (match != null) lastMatch = match; | |
300 } while (match != null); | |
301 return lastMatch[1]; | |
302 } | |
303 } | |
304 | |
305 // Adding a 'document' is dangerous since it invalidates the 'typeof document' | |
306 // test to see if we are running in the browser. It means that the runtime | |
307 // needs to do more precise checks. | |
308 // Note that we can't run "currentScript" right away, since that would give | |
309 // us the location of the preamble file. Instead we wait for the first access | |
310 // which should happen just before invoking main. At this point we are in | |
311 // the main file and setting the currentScript property is correct. | |
312 var cachedCurrentScript = null; | |
313 self.document = { get currentScript() { | |
314 if (cachedCurrentScript == null) { | |
315 cachedCurrentScript = {src: computeCurrentScript()}; | |
316 } | |
317 return cachedCurrentScript; | |
318 } | |
319 }; | |
320 | |
321 // Support for deferred loading. | |
322 self.dartDeferredLibraryLoader = function(uri, successCallback, errorCallback)
{ | |
323 try { | |
324 load(uri); | |
325 successCallback(); | |
326 } catch (error) { | |
327 errorCallback(error); | |
328 } | |
329 }; | |
330 })(self); | |
OLD | NEW |