OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 /** | |
6 * Generates JS helpers for dart:core. This used to be in a file "core.js". | |
7 * Having them in Dart code means we can easily control which are generated. | |
8 */ | |
9 // TODO(jmesserly): one idea to make this cleaner: put these as private "native" | |
10 // methods somewhere in a library that we import. This would be rather elegant | |
11 // because they'd get the right name collision behavior, conversions, | |
12 // include-if-used, etc for free. Not sure if it's worth doing that. | |
13 class CoreJs { | |
14 // These values track if the helper is actually used. If it is we generate it. | |
15 bool useThrow = false; | |
16 bool useNotNullBool = false; | |
17 bool useIndex = false; | |
18 bool useSetIndex = false; | |
19 | |
20 bool useWrap0 = false; | |
21 bool useWrap1 = false; | |
22 bool useWrap2 = false; | |
23 bool useIsolates = false; | |
24 | |
25 // These helpers had to switch to a new pattern, because they can be generated | |
26 // after everything else. | |
27 bool _generatedTypeNameOf = false; | |
28 bool _generatedDynamicProto = false; | |
29 bool _generatedDynamicSetMetadata = false; | |
30 bool _generatedInherits = false; | |
31 bool _generatedDefProp = false; | |
32 bool _generatedBind = false; | |
33 | |
34 | |
35 Map<String, String> _usedOperators; | |
36 | |
37 CodeWriter writer; | |
38 | |
39 CoreJs(): _usedOperators = {}, writer = new CodeWriter(); | |
40 | |
41 void markCorelibTypeUsed(String typeName) { | |
42 world.gen.markTypeUsed(world.corelib.types[typeName]); | |
43 } | |
44 | |
45 _emit(String code) => writer.writeln(code); | |
46 | |
47 /** | |
48 * Generates the special operator method, e.g. $add. | |
49 * We want to do $add(x, y) instead of x.$add(y) so it doesn't box. | |
50 * Same idea for the other methods. | |
51 */ | |
52 void useOperator(String name) { | |
53 if (_usedOperators[name] != null) return; | |
54 | |
55 if (name != ':ne' && name != ':eq') { | |
56 // TODO(jimhug): Only do this once! | |
57 markCorelibTypeUsed('NoSuchMethodException'); | |
58 } | |
59 if (name != ':bit_not' && name != ':negate') { | |
60 // TODO(jimhug): Only do this once! | |
61 markCorelibTypeUsed('IllegalArgumentException'); | |
62 } | |
63 | |
64 var code; | |
65 switch (name) { | |
66 case ':ne': | |
67 code = _NE_FUNCTION; | |
68 break; | |
69 | |
70 case ':eq': | |
71 ensureDefProp(); | |
72 code = _EQ_FUNCTION; | |
73 break; | |
74 | |
75 case ':bit_not': | |
76 code = _BIT_NOT_FUNCTION; | |
77 break; | |
78 | |
79 case ':negate': | |
80 code = _NEGATE_FUNCTION; | |
81 break; | |
82 | |
83 case ':add': | |
84 code = _ADD_FUNCTION; | |
85 break; | |
86 | |
87 case ':truncdiv': | |
88 useThrow = true; | |
89 // TODO(jimhug): Only do this once! | |
90 markCorelibTypeUsed('IntegerDivisionByZeroException'); | |
91 code = _TRUNCDIV_FUNCTION; | |
92 break; | |
93 | |
94 case ':mod': | |
95 code = _MOD_FUNCTION; | |
96 break; | |
97 | |
98 default: | |
99 // All of the other helpers are generated the same way | |
100 var op = TokenKind.rawOperatorFromMethod(name); | |
101 var jsname = world.toJsIdentifier(name); | |
102 code = _otherOperator(jsname, op); | |
103 break; | |
104 } | |
105 | |
106 _usedOperators[name] = code; | |
107 } | |
108 | |
109 // NOTE: some helpers can't be generated when we generate corelib, | |
110 // because we don't discover that we need them until later. | |
111 // Generate on-demand instead | |
112 void ensureDynamicProto() { | |
113 if (_generatedDynamicProto) return; | |
114 _generatedDynamicProto = true; | |
115 ensureTypeNameOf(); | |
116 ensureDefProp(); | |
117 _emit(_DYNAMIC_FUNCTION); | |
118 } | |
119 | |
120 void ensureDynamicSetMetadata() { | |
121 if (_generatedDynamicSetMetadata) return; | |
122 _generatedDynamicSetMetadata = true; | |
123 _emit(_DYNAMIC_SET_METADATA_FUNCTION); | |
124 } | |
125 | |
126 void ensureTypeNameOf() { | |
127 if (_generatedTypeNameOf) return; | |
128 _generatedTypeNameOf = true; | |
129 ensureDefProp(); | |
130 _emit(_TYPE_NAME_OF_FUNCTION); | |
131 } | |
132 | |
133 /** Generates the $inherits function when it's first used. */ | |
134 void ensureInheritsHelper() { | |
135 if (_generatedInherits) return; | |
136 _generatedInherits = true; | |
137 _emit(_INHERITS_FUNCTION); | |
138 } | |
139 | |
140 /** Generates the $defProp function when it's first used. */ | |
141 void ensureDefProp() { | |
142 if (_generatedDefProp) return; | |
143 _generatedDefProp = true; | |
144 _emit(_DEF_PROP_FUNCTION); | |
145 } | |
146 | |
147 void ensureBind() { | |
148 if (_generatedBind) return; | |
149 _generatedBind = true; | |
150 _emit(_BIND_CODE); | |
151 } | |
152 | |
153 void generate(CodeWriter w) { | |
154 // Write any stuff we had queued up, then replace our writer with a | |
155 // subwriter into the one in WorldGenerator so anything we discover that we | |
156 // need later on will be generated on-demand. | |
157 w.write(writer.text); | |
158 writer = w.subWriter(); | |
159 | |
160 if (useNotNullBool) { | |
161 useThrow = true; | |
162 _emit(_NOTNULL_BOOL_FUNCTION); | |
163 } | |
164 | |
165 if (useThrow) { | |
166 _emit(_THROW_FUNCTION); | |
167 } | |
168 | |
169 if (useIndex) { | |
170 markCorelibTypeUsed('NoSuchMethodException'); | |
171 ensureDefProp(); | |
172 _emit(options.disableBoundsChecks ? | |
173 _INDEX_OPERATORS : _CHECKED_INDEX_OPERATORS); | |
174 } | |
175 | |
176 if (useSetIndex) { | |
177 markCorelibTypeUsed('NoSuchMethodException'); | |
178 ensureDefProp(); | |
179 _emit(options.disableBoundsChecks ? | |
180 _SETINDEX_OPERATORS : _CHECKED_SETINDEX_OPERATORS); | |
181 } | |
182 | |
183 if (!useIsolates) { | |
184 if (useWrap0) _emit(_EMPTY_WRAP_CALL0_FUNCTION); | |
185 if (useWrap1) _emit(_EMPTY_WRAP_CALL1_FUNCTION); | |
186 if (useWrap2) _emit(_EMPTY_WRAP_CALL2_FUNCTION); | |
187 } | |
188 | |
189 // Write operator helpers | |
190 for (var opImpl in orderValuesByKeys(_usedOperators)) { | |
191 _emit(opImpl); | |
192 } | |
193 | |
194 if (world.dom != null || world.html != null) { | |
195 ensureTypeNameOf(); | |
196 ensureDefProp(); | |
197 // TODO(jmesserly): we need to find a way to avoid conflicts with other | |
198 // generated "typeName" fields. Ideally we wouldn't be patching 'Object' | |
199 // here. | |
200 _emit('\$defProp(Object.prototype, "get\$typeName", ' | |
201 'Object.prototype.\$typeNameOf);'); | |
202 } | |
203 } | |
204 } | |
205 | |
206 | |
207 /** Snippet for `$ne`. */ | |
208 final String _NE_FUNCTION = @""" | |
209 function $ne$(x, y) { | |
210 if (x == null) return y != null; | |
211 return (typeof(x) != 'object') ? x !== y : !x.$eq(y); | |
212 } | |
213 """; | |
214 | |
215 /** Snippet for `$eq`. */ | |
216 final String _EQ_FUNCTION = @""" | |
217 function $eq$(x, y) { | |
218 if (x == null) return y == null; | |
219 return (typeof(x) != 'object') ? x === y : x.$eq(y); | |
220 } | |
221 // TODO(jimhug): Should this or should it not match equals? | |
222 $defProp(Object.prototype, '$eq', function(other) { | |
223 return this === other; | |
224 }); | |
225 """; | |
226 | |
227 /** Snippet for `$bit_not`. */ | |
228 final String _BIT_NOT_FUNCTION = @""" | |
229 function $bit_not$(x) { | |
230 if (typeof(x) == 'number') return ~x; | |
231 if (typeof(x) == 'object') return x.$bit_not(); | |
232 $throw(new NoSuchMethodException(x, "operator ~", [])); | |
233 } | |
234 """; | |
235 | |
236 /** Snippet for `$negate`. */ | |
237 final String _NEGATE_FUNCTION = @""" | |
238 function $negate$(x) { | |
239 if (typeof(x) == 'number') return -x; | |
240 if (typeof(x) == 'object') return x.$negate(); | |
241 $throw(new NoSuchMethodException(x, "operator negate", [])); | |
242 } | |
243 """; | |
244 | |
245 /** Snippet for `$add`. This relies on JS's string "+" to match Dart's. */ | |
246 final String _ADD_FUNCTION = @""" | |
247 function $add$complex$(x, y) { | |
248 if (typeof(x) == 'number') { | |
249 $throw(new IllegalArgumentException(y)); | |
250 } else if (typeof(x) == 'string') { | |
251 var str = (y == null) ? 'null' : y.toString(); | |
252 if (typeof(str) != 'string') { | |
253 throw new Error("calling toString() on right hand operand of operator " + | |
254 "+ did not return a String"); | |
255 } | |
256 return x + str; | |
257 } else if (typeof(x) == 'object') { | |
258 return x.$add(y); | |
259 } else { | |
260 $throw(new NoSuchMethodException(x, "operator +", [y])); | |
261 } | |
262 } | |
263 | |
264 function $add$(x, y) { | |
265 if (typeof(x) == 'number' && typeof(y) == 'number') return x + y; | |
266 return $add$complex$(x, y); | |
267 } | |
268 """; | |
269 | |
270 /** Snippet for `$truncdiv`. This uses `$throw`. */ | |
271 final String _TRUNCDIV_FUNCTION = @""" | |
272 function $truncdiv$(x, y) { | |
273 if (typeof(x) == 'number') { | |
274 if (typeof(y) == 'number') { | |
275 if (y == 0) $throw(new IntegerDivisionByZeroException()); | |
276 var tmp = x / y; | |
277 return (tmp < 0) ? Math.ceil(tmp) : Math.floor(tmp); | |
278 } else { | |
279 $throw(new IllegalArgumentException(y)); | |
280 } | |
281 } else if (typeof(x) == 'object') { | |
282 return x.$truncdiv(y); | |
283 } else { | |
284 $throw(new NoSuchMethodException(x, "operator ~/", [y])); | |
285 } | |
286 } | |
287 """; | |
288 | |
289 /** Snippet for `$mod`. */ | |
290 final String _MOD_FUNCTION = @""" | |
291 function $mod$(x, y) { | |
292 if (typeof(x) == 'number') { | |
293 if (typeof(y) == 'number') { | |
294 var result = x % y; | |
295 if (result == 0) { | |
296 return 0; // Make sure we don't return -0.0. | |
297 } else if (result < 0) { | |
298 if (y < 0) { | |
299 return result - y; | |
300 } else { | |
301 return result + y; | |
302 } | |
303 } | |
304 return result; | |
305 } else { | |
306 $throw(new IllegalArgumentException(y)); | |
307 } | |
308 } else if (typeof(x) == 'object') { | |
309 return x.$mod(y); | |
310 } else { | |
311 $throw(new NoSuchMethodException(x, "operator %", [y])); | |
312 } | |
313 } | |
314 """; | |
315 | |
316 /** Code snippet for all other operators. */ | |
317 String _otherOperator(String jsname, String op) { | |
318 return """ | |
319 function $jsname\$complex\$(x, y) { | |
320 if (typeof(x) == 'number') { | |
321 \$throw(new IllegalArgumentException(y)); | |
322 } else if (typeof(x) == 'object') { | |
323 return x.$jsname(y); | |
324 } else { | |
325 \$throw(new NoSuchMethodException(x, "operator $op", [y])); | |
326 } | |
327 } | |
328 function $jsname\$(x, y) { | |
329 if (typeof(x) == 'number' && typeof(y) == 'number') return x $op y; | |
330 return $jsname\$complex\$(x, y); | |
331 } | |
332 """; | |
333 } | |
334 | |
335 /** | |
336 * Snippet for `$dynamic`. Usage: | |
337 * $dynamic(name).SomeTypeName = ... method ...; | |
338 * $dynamic(name).Object = ... noSuchMethod ...; | |
339 */ | |
340 final String _DYNAMIC_FUNCTION = @""" | |
341 function $dynamic(name) { | |
342 var f = Object.prototype[name]; | |
343 if (f && f.methods) return f.methods; | |
344 | |
345 var methods = {}; | |
346 if (f) methods.Object = f; | |
347 function $dynamicBind() { | |
348 // Find the target method | |
349 var obj = this; | |
350 var tag = obj.$typeNameOf(); | |
351 var method = methods[tag]; | |
352 if (!method) { | |
353 var table = $dynamicMetadata; | |
354 for (var i = 0; i < table.length; i++) { | |
355 var entry = table[i]; | |
356 if (entry.map.hasOwnProperty(tag)) { | |
357 method = methods[entry.tag]; | |
358 if (method) break; | |
359 } | |
360 } | |
361 } | |
362 method = method || methods.Object; | |
363 | |
364 var proto = Object.getPrototypeOf(obj); | |
365 | |
366 if (method == null) { | |
367 // Trampoline to throw NoSuchMethodException (TODO: call noSuchMethod). | |
368 method = function(){ | |
369 // Exact type check to prevent this code shadowing the dispatcher from a | |
370 // subclass. | |
371 if (Object.getPrototypeOf(this) === proto) { | |
372 // TODO(sra): 'name' is the jsname, should be the Dart name. | |
373 $throw(new NoSuchMethodException( | |
374 obj, name, Array.prototype.slice.call(arguments))); | |
375 } | |
376 return Object.prototype[name].apply(this, arguments); | |
377 }; | |
378 } | |
379 | |
380 if (!proto.hasOwnProperty(name)) { | |
381 $defProp(proto, name, method); | |
382 } | |
383 | |
384 return method.apply(this, Array.prototype.slice.call(arguments)); | |
385 }; | |
386 $dynamicBind.methods = methods; | |
387 $defProp(Object.prototype, name, $dynamicBind); | |
388 return methods; | |
389 } | |
390 if (typeof $dynamicMetadata == 'undefined') $dynamicMetadata = []; | |
391 """; | |
392 | |
393 /** | |
394 * Snippet for `$dynamicSetMetadata`. | |
395 */ | |
396 final String _DYNAMIC_SET_METADATA_FUNCTION = @""" | |
397 function $dynamicSetMetadata(inputTable) { | |
398 // TODO: Deal with light isolates. | |
399 var table = []; | |
400 for (var i = 0; i < inputTable.length; i++) { | |
401 var tag = inputTable[i][0]; | |
402 var tags = inputTable[i][1]; | |
403 var map = {}; | |
404 var tagNames = tags.split('|'); | |
405 for (var j = 0; j < tagNames.length; j++) { | |
406 map[tagNames[j]] = true; | |
407 } | |
408 table.push({tag: tag, tags: tags, map: map}); | |
409 } | |
410 $dynamicMetadata = table; | |
411 } | |
412 """; | |
413 | |
414 /** Snippet for `$typeNameOf`. */ | |
415 final String _TYPE_NAME_OF_FUNCTION = @""" | |
416 $defProp(Object.prototype, '$typeNameOf', (function() { | |
417 function constructorNameWithFallback(obj) { | |
418 var constructor = obj.constructor; | |
419 if (typeof(constructor) == 'function') { | |
420 // The constructor isn't null or undefined at this point. Try | |
421 // to grab hold of its name. | |
422 var name = constructor.name; | |
423 // If the name is a non-empty string, we use that as the type | |
424 // name of this object. On Firefox, we often get 'Object' as | |
425 // the constructor name even for more specialized objects so | |
426 // we have to fall through to the toString() based implementation | |
427 // below in that case. | |
428 if (typeof(name) == 'string' && name && name != 'Object') return name; | |
429 } | |
430 var string = Object.prototype.toString.call(obj); | |
431 return string.substring(8, string.length - 1); | |
432 } | |
433 | |
434 function chrome$typeNameOf() { | |
435 var name = this.constructor.name; | |
436 if (name == 'Window') return 'DOMWindow'; | |
437 if (name == 'CanvasPixelArray') return 'Uint8ClampedArray'; | |
438 return name; | |
439 } | |
440 | |
441 function firefox$typeNameOf() { | |
442 var name = constructorNameWithFallback(this); | |
443 if (name == 'Window') return 'DOMWindow'; | |
444 if (name == 'Document') return 'HTMLDocument'; | |
445 if (name == 'XMLDocument') return 'Document'; | |
446 if (name == 'WorkerMessageEvent') return 'MessageEvent'; | |
447 return name; | |
448 } | |
449 | |
450 function ie$typeNameOf() { | |
451 var name = constructorNameWithFallback(this); | |
452 if (name == 'Window') return 'DOMWindow'; | |
453 // IE calls both HTML and XML documents 'Document', so we check for the | |
454 // xmlVersion property, which is the empty string on HTML documents. | |
455 if (name == 'Document' && this.xmlVersion) return 'Document'; | |
456 if (name == 'Document') return 'HTMLDocument'; | |
457 if (name == 'HTMLTableDataCellElement') return 'HTMLTableCellElement'; | |
458 if (name == 'HTMLTableHeaderCellElement') return 'HTMLTableCellElement'; | |
459 if (name == 'MSStyleCSSProperties') return 'CSSStyleDeclaration'; | |
460 if (name == 'CanvasPixelArray') return 'Uint8ClampedArray'; | |
461 if (name == 'HTMLPhraseElement') return 'HTMLElement'; | |
462 if (name == 'MouseWheelEvent') return 'WheelEvent'; | |
463 return name; | |
464 } | |
465 | |
466 // If we're not in the browser, we're almost certainly running on v8. | |
467 if (typeof(navigator) != 'object') return chrome$typeNameOf; | |
468 | |
469 var userAgent = navigator.userAgent; | |
470 if (/Chrome|DumpRenderTree/.test(userAgent)) return chrome$typeNameOf; | |
471 if (/Firefox/.test(userAgent)) return firefox$typeNameOf; | |
472 if (/MSIE/.test(userAgent)) return ie$typeNameOf; | |
473 return function() { return constructorNameWithFallback(this); }; | |
474 })()); | |
475 """; | |
476 | |
477 /** Snippet for `$inherits`. */ | |
478 final String _INHERITS_FUNCTION = @""" | |
479 /** Implements extends for Dart classes on JavaScript prototypes. */ | |
480 function $inherits(child, parent) { | |
481 if (child.prototype.__proto__) { | |
482 child.prototype.__proto__ = parent.prototype; | |
483 } else { | |
484 function tmp() {}; | |
485 tmp.prototype = parent.prototype; | |
486 child.prototype = new tmp(); | |
487 child.prototype.constructor = child; | |
488 } | |
489 } | |
490 """; | |
491 | |
492 /** Snippet for `$defProp`. */ | |
493 final String _DEF_PROP_FUNCTION = @""" | |
494 function $defProp(obj, prop, value) { | |
495 Object.defineProperty(obj, prop, | |
496 {value: value, enumerable: false, writable: true, configurable: true}); | |
497 } | |
498 """; | |
499 | |
500 /** Snippet for `$stackTraceOf`. */ | |
501 final String _STACKTRACEOF_FUNCTION = @""" | |
502 function $stackTraceOf(e) { | |
503 // TODO(jmesserly): we shouldn't be relying on the e.stack property. | |
504 // Need to mangle it. | |
505 return (e && e.stack) ? e.stack : null; | |
506 } | |
507 """; | |
508 | |
509 /** | |
510 * Snippet for `$notnull_bool`. This pattern chosen because IE9 does really | |
511 * badly with typeof, and it's still decent on other browsers. | |
512 */ | |
513 final String _NOTNULL_BOOL_FUNCTION = @""" | |
514 function $notnull_bool(test) { | |
515 if (test === true || test === false) return test; | |
516 $throw(new TypeError(test, 'bool')); | |
517 } | |
518 """; | |
519 | |
520 /** Snippet for `$throw`. */ | |
521 final String _THROW_FUNCTION = @""" | |
522 function $throw(e) { | |
523 // If e is not a value, we can use V8's captureStackTrace utility method. | |
524 // TODO(jmesserly): capture the stack trace on other JS engines. | |
525 if (e && (typeof e == 'object') && Error.captureStackTrace) { | |
526 // TODO(jmesserly): this will clobber the e.stack property | |
527 Error.captureStackTrace(e, $throw); | |
528 } | |
529 throw e; | |
530 } | |
531 """; | |
532 | |
533 /** | |
534 * Snippet for `$index` in Object, Array, and String. If not overridden, | |
535 * `$index` and `$setindex` fall back to JS [] and []= accessors. | |
536 */ | |
537 // TODO(jimhug): This fallback could be very confusing in a few cases - | |
538 // because of the bizare default [] rules in JS. We need to revisit this | |
539 // to get the right errors - at least in checked mode (once we have that). | |
540 // TODO(jmesserly): do perf analysis, figure out if this is worth it and | |
541 // what the cost of $index $setindex is on all browsers | |
542 | |
543 // Performance of Object.prototype methods can go down because there are | |
544 // so many of them. Instead, first time we hit it, put it on the derived | |
545 // prototype. TODO(jmesserly): make this go away by handling index more | |
546 // like a normal method. | |
547 final String _INDEX_OPERATORS = @""" | |
548 $defProp(Object.prototype, '$index', function(i) { | |
549 $throw(new NoSuchMethodException(this, "operator []", [i])); | |
550 }); | |
551 $defProp(Array.prototype, '$index', function(i) { | |
552 return this[i]; | |
553 }); | |
554 $defProp(String.prototype, '$index', function(i) { | |
555 return this[i]; | |
556 }); | |
557 """; | |
558 | |
559 final String _CHECKED_INDEX_OPERATORS = @""" | |
560 $defProp(Object.prototype, '$index', function(i) { | |
561 $throw(new NoSuchMethodException(this, "operator []", [i])); | |
562 }); | |
563 $defProp(Array.prototype, '$index', function(index) { | |
564 var i = index | 0; | |
565 if (i !== index) { | |
566 throw new IllegalArgumentException('index is not int'); | |
567 } else if (i < 0 || i >= this.length) { | |
568 throw new IndexOutOfRangeException(index); | |
569 } | |
570 return this[i]; | |
571 }); | |
572 $defProp(String.prototype, '$index', function(i) { | |
573 return this[i]; | |
574 }); | |
575 """; | |
576 | |
577 | |
578 | |
579 /** Snippet for `$setindex` in Object, Array, and String. */ | |
580 final String _SETINDEX_OPERATORS = @""" | |
581 $defProp(Object.prototype, '$setindex', function(i, value) { | |
582 $throw(new NoSuchMethodException(this, "operator []=", [i, value])); | |
583 }); | |
584 $defProp(Array.prototype, '$setindex', | |
585 function(i, value) { return this[i] = value; });"""; | |
586 | |
587 final String _CHECKED_SETINDEX_OPERATORS = @""" | |
588 $defProp(Object.prototype, '$setindex', function(i, value) { | |
589 $throw(new NoSuchMethodException(this, "operator []=", [i, value])); | |
590 }); | |
591 $defProp(Array.prototype, '$setindex', function(index, value) { | |
592 var i = index | 0; | |
593 if (i !== index) { | |
594 throw new IllegalArgumentException('index is not int'); | |
595 } else if (i < 0 || i >= this.length) { | |
596 throw new IndexOutOfRangeException(index); | |
597 } | |
598 return this[i] = value; | |
599 }); | |
600 """; | |
601 | |
602 /** Snippet for `$wrap_call$0`, in case it was not necessary. */ | |
603 final String _EMPTY_WRAP_CALL0_FUNCTION = @""" | |
604 function $wrap_call$0(fn) { return fn; } | |
605 """; | |
606 | |
607 /** Snippet for `$wrap_call$1`, in case it was not necessary. */ | |
608 final String _EMPTY_WRAP_CALL1_FUNCTION = @""" | |
609 function $wrap_call$1(fn) { return fn; }; | |
610 """; | |
611 | |
612 /** Snippet for `$wrap_call$2`, in case it was not necessary. */ | |
613 final String _EMPTY_WRAP_CALL2_FUNCTION = @""" | |
614 function $wrap_call$2(fn) { return fn; }; | |
615 """; | |
616 | |
617 /** Snippet that initializes Function.prototype.bind. */ | |
618 final String _BIND_CODE = @""" | |
619 Function.prototype.bind = Function.prototype.bind || | |
620 function(thisObj) { | |
621 var func = this; | |
622 var funcLength = func.$length || func.length; | |
623 var argsLength = arguments.length; | |
624 if (argsLength > 1) { | |
625 var boundArgs = Array.prototype.slice.call(arguments, 1); | |
626 var bound = function() { | |
627 // Prepend the bound arguments to the current arguments. | |
628 var newArgs = Array.prototype.slice.call(arguments); | |
629 Array.prototype.unshift.apply(newArgs, boundArgs); | |
630 return func.apply(thisObj, newArgs); | |
631 }; | |
632 bound.$length = Math.max(0, funcLength - (argsLength - 1)); | |
633 return bound; | |
634 } else { | |
635 var bound = function() { | |
636 return func.apply(thisObj, arguments); | |
637 }; | |
638 bound.$length = funcLength; | |
639 return bound; | |
640 } | |
641 }; | |
642 """; | |
OLD | NEW |