Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(366)

Side by Side Diff: lib/dartdoc/frog/corejs.dart

Issue 10696191: Frog removed from dartdoc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 """;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698