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

Side by Side Diff: sdk/lib/js/dart2js/js_dart2js.dart

Issue 1318043005: Support user generated custom native JS classes. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: ptal Created 5 years, 2 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
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Support for interoperating with JavaScript. 6 * Support for interoperating with JavaScript.
7 * 7 *
8 * This library provides access to JavaScript objects from Dart, allowing 8 * This library provides access to JavaScript objects from Dart, allowing
9 * Dart code to get and set properties, and call methods of JavaScript objects 9 * Dart code to get and set properties, and call methods of JavaScript objects
10 * and invoke JavaScript functions. The library takes care of converting 10 * and invoke JavaScript functions. The library takes care of converting
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 * 85 *
86 * var jsArray = new JsObject.jsify([1, 2, 3]); 86 * var jsArray = new JsObject.jsify([1, 2, 3]);
87 */ 87 */
88 library dart.js; 88 library dart.js;
89 89
90 import 'dart:html' show Blob, Event, ImageData, Node, Window; 90 import 'dart:html' show Blob, Event, ImageData, Node, Window;
91 import 'dart:collection' show HashMap, ListMixin; 91 import 'dart:collection' show HashMap, ListMixin;
92 import 'dart:indexed_db' show KeyRange; 92 import 'dart:indexed_db' show KeyRange;
93 import 'dart:typed_data' show TypedData; 93 import 'dart:typed_data' show TypedData;
94 94
95 import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS; 95 import 'dart:_foreign_helper' show JS, JS_CONST, DART_CLOSURE_TO_JS;
96 import 'dart:_interceptors' show JavaScriptObject, UnknownJavaScriptObject; 96 import 'dart:_interceptors'
97 import 'dart:_js_helper' show Primitives, convertDartClosureToJS, 97 show JavaScriptObject, UnknownJavaScriptObject, DART_CLOSURE_PROPERTY_NAME;
98 getIsolateAffinityTag; 98 import 'dart:_js_helper'
99 show Primitives, convertDartClosureToJS, getIsolateAffinityTag;
100
101 export 'dart:_interceptors' show JavaScriptObject;
99 102
100 final JsObject context = _wrapToDart(JS('', 'self')); 103 final JsObject context = _wrapToDart(JS('', 'self'));
101 104
102 _convertDartFunction(Function f, {bool captureThis: false}) { 105 _convertDartFunction(Function f, {bool captureThis: false}) {
103 return JS('', 106 return JS(
104 'function(_call, f, captureThis) {' 107 '',
105 'return function() {' 108 '''
106 'return _call(f, captureThis, this, ' 109 function(_call, f, captureThis) {
107 'Array.prototype.slice.apply(arguments));' 110 return function() {
108 '}' 111 return _call(f, captureThis, this,
109 '}(#, #, #)', DART_CLOSURE_TO_JS(_callDartFunction), f, captureThis); 112 Array.prototype.slice.apply(arguments));
113 }
114 }(#, #, #)
115 ''',
116 DART_CLOSURE_TO_JS(_callDartFunction),
117 f,
118 captureThis);
110 } 119 }
111 120
112 _callDartFunction(callback, bool captureThis, self, List arguments) { 121 _callDartFunction(callback, bool captureThis, self, List arguments) {
113 if (captureThis) { 122 if (captureThis) {
114 arguments = [self]..addAll(arguments); 123 arguments = [self]..addAll(arguments);
115 } 124 }
116 var dartArgs = new List.from(arguments.map(_convertToDart)); 125 var dartArgs = new List.from(arguments.map(_convertToDart));
117 return _convertToJS(Function.apply(callback, dartArgs)); 126 return _convertToJS(Function.apply(callback, dartArgs));
118 } 127 }
119 128
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 * 214 *
206 * Use this constructor only if you wish to get access to JavaScript 215 * Use this constructor only if you wish to get access to JavaScript
207 * properties attached to a browser host object, such as a Node or Blob, that 216 * properties attached to a browser host object, such as a Node or Blob, that
208 * is normally automatically converted into a native Dart object. 217 * is normally automatically converted into a native Dart object.
209 * 218 *
210 * An exception will be thrown if [object] either is `null` or has the type 219 * An exception will be thrown if [object] either is `null` or has the type
211 * `bool`, `num`, or `String`. 220 * `bool`, `num`, or `String`.
212 */ 221 */
213 factory JsObject.fromBrowserObject(object) { 222 factory JsObject.fromBrowserObject(object) {
214 if (object is num || object is String || object is bool || object == null) { 223 if (object is num || object is String || object is bool || object == null) {
215 throw new ArgumentError( 224 throw new ArgumentError("object cannot be a num, string, bool, or null");
216 "object cannot be a num, string, bool, or null");
217 } 225 }
218 return _wrapToDart(_convertToJS(object)); 226 return _wrapToDart(_convertToJS(object));
219 } 227 }
220 228
221 /** 229 /**
222 * Recursively converts a JSON-like collection of Dart objects to a 230 * Recursively converts a JSON-like collection of Dart objects to a
223 * collection of JavaScript objects and returns a [JsObject] proxy to it. 231 * collection of JavaScript objects and returns a [JsObject] proxy to it.
224 * 232 *
225 * [object] must be a [Map] or [Iterable], the contents of which are also 233 * [object] must be a [Map] or [Iterable], the contents of which are also
226 * converted. Maps and Iterables are copied to a new JavaScript object. 234 * converted. Maps and Iterables are copied to a new JavaScript object.
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
260 268
261 return _convert(data); 269 return _convert(data);
262 } 270 }
263 271
264 /** 272 /**
265 * Returns the value associated with [property] from the proxied JavaScript 273 * Returns the value associated with [property] from the proxied JavaScript
266 * object. 274 * object.
267 * 275 *
268 * The type of [property] must be either [String] or [num]. 276 * The type of [property] must be either [String] or [num].
269 */ 277 */
270 dynamic operator[](property) { 278 dynamic operator [](property) {
271 if (property is! String && property is! num) { 279 if (property is! String && property is! num) {
272 throw new ArgumentError("property is not a String or num"); 280 throw new ArgumentError("property is not a String or num");
273 } 281 }
274 return _convertToDart(JS('', '#[#]', _jsObject, property)); 282 return _convertToDart(JS('', '#[#]', _jsObject, property));
275 } 283 }
276 284
277 /** 285 /**
278 * Sets the value associated with [property] on the proxied JavaScript 286 * Sets the value associated with [property] on the proxied JavaScript
279 * object. 287 * object.
280 * 288 *
281 * The type of [property] must be either [String] or [num]. 289 * The type of [property] must be either [String] or [num].
282 */ 290 */
283 operator[]=(property, value) { 291 operator []=(property, value) {
284 if (property is! String && property is! num) { 292 if (property is! String && property is! num) {
285 throw new ArgumentError("property is not a String or num"); 293 throw new ArgumentError("property is not a String or num");
286 } 294 }
287 JS('', '#[#]=#', _jsObject, property, _convertToJS(value)); 295 JS('', '#[#]=#', _jsObject, property, _convertToJS(value));
288 } 296 }
289 297
290 int get hashCode => 0; 298 int get hashCode => 0;
291 299
292 bool operator==(other) => other is JsObject && 300 bool operator ==(other) =>
293 JS('bool', '# === #', _jsObject, other._jsObject); 301 other is JsObject && JS('bool', '# === #', _jsObject, other._jsObject);
294 302
295 /** 303 /**
296 * Returns `true` if the JavaScript object contains the specified property 304 * Returns `true` if the JavaScript object contains the specified property
297 * either directly or though its prototype chain. 305 * either directly or though its prototype chain.
298 * 306 *
299 * This is the equivalent of the `in` operator in JavaScript. 307 * This is the equivalent of the `in` operator in JavaScript.
300 */ 308 */
301 bool hasProperty(property) { 309 bool hasProperty(property) {
302 if (property is! String && property is! num) { 310 if (property is! String && property is! num) {
303 throw new ArgumentError("property is not a String or num"); 311 throw new ArgumentError("property is not a String or num");
(...skipping 21 matching lines...) Expand all
325 bool instanceof(JsFunction type) { 333 bool instanceof(JsFunction type) {
326 return JS('bool', '# instanceof #', _jsObject, _convertToJS(type)); 334 return JS('bool', '# instanceof #', _jsObject, _convertToJS(type));
327 } 335 }
328 336
329 /** 337 /**
330 * Returns the result of the JavaScript objects `toString` method. 338 * Returns the result of the JavaScript objects `toString` method.
331 */ 339 */
332 String toString() { 340 String toString() {
333 try { 341 try {
334 return JS('String', 'String(#)', _jsObject); 342 return JS('String', 'String(#)', _jsObject);
335 } catch(e) { 343 } catch (e) {
336 return super.toString(); 344 return super.toString();
337 } 345 }
338 } 346 }
339 347
340 /** 348 /**
341 * Calls [method] on the JavaScript object with the arguments [args] and 349 * Calls [method] on the JavaScript object with the arguments [args] and
342 * returns the result. 350 * returns the result.
343 * 351 *
344 * The type of [method] must be either [String] or [num]. 352 * The type of [method] must be either [String] or [num].
345 */ 353 */
346 dynamic callMethod(method, [List args]) { 354 dynamic callMethod(method, [List args]) {
347 if (method is! String && method is! num) { 355 if (method is! String && method is! num) {
348 throw new ArgumentError("method is not a String or num"); 356 throw new ArgumentError("method is not a String or num");
349 } 357 }
350 return _convertToDart(JS('', '#[#].apply(#, #)', _jsObject, method, 358 return _convertToDart(JS(
359 '',
360 '#[#].apply(#, #)',
361 _jsObject,
362 method,
351 _jsObject, 363 _jsObject,
352 args == null ? null : new List.from(args.map(_convertToJS)))); 364 args == null ? null : new List.from(args.map(_convertToJS))));
353 } 365 }
354 } 366 }
355 367
356 /** 368 /**
357 * Proxies a JavaScript Function object. 369 * Proxies a JavaScript Function object.
358 */ 370 */
359 class JsFunction extends JsObject { 371 class JsFunction extends JsObject {
360
361 /** 372 /**
362 * Returns a [JsFunction] that captures its 'this' binding and calls [f] 373 * Returns a [JsFunction] that captures its 'this' binding and calls [f]
363 * with the value of this passed as the first argument. 374 * with the value of this passed as the first argument.
364 */ 375 */
365 factory JsFunction.withThis(Function f) { 376 factory JsFunction.withThis(Function f) {
366 var jsFunc = _convertDartFunction(f, captureThis: true); 377 var jsFunc = _convertDartFunction(f, captureThis: true);
367 return new JsFunction._fromJs(jsFunc); 378 return new JsFunction._fromJs(jsFunc);
368 } 379 }
369 380
370 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); 381 JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
371 382
372 /** 383 /**
373 * Invokes the JavaScript function with arguments [args]. If [thisArg] is 384 * Invokes the JavaScript function with arguments [args]. If [thisArg] is
374 * supplied it is the value of `this` for the invocation. 385 * supplied it is the value of `this` for the invocation.
375 */ 386 */
376 dynamic apply(List args, { thisArg }) => 387 dynamic apply(List args, {thisArg}) => _convertToDart(JS(
377 _convertToDart(JS('', '#.apply(#, #)', _jsObject, 388 '',
378 _convertToJS(thisArg), 389 '#.apply(#, #)',
379 args == null ? null : new List.from(args.map(_convertToJS)))); 390 _jsObject,
391 _convertToJS(thisArg),
392 args == null ? null : new List.from(args.map(_convertToJS))));
380 } 393 }
381 394
382 /** 395 /**
383 * A [List] that proxies a JavaScript array. 396 * A [List] that proxies a JavaScript array.
384 */ 397 */
385 class JsArray<E> extends JsObject with ListMixin<E> { 398 class JsArray<E> extends JsObject with ListMixin<E> {
386
387 /** 399 /**
388 * Creates a new JavaScript array. 400 * Creates a new JavaScript array.
389 */ 401 */
390 JsArray() : super._fromJs([]); 402 JsArray() : super._fromJs([]);
391 403
392 /** 404 /**
393 * Creates a new JavaScript array and initializes it to the contents of 405 * Creates a new JavaScript array and initializes it to the contents of
394 * [other]. 406 * [other].
395 */ 407 */
396 JsArray.from(Iterable<E> other) 408 JsArray.from(Iterable<E> other)
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 int get length { 454 int get length {
443 // Check the length honours the List contract. 455 // Check the length honours the List contract.
444 var len = JS('', '#.length', _jsObject); 456 var len = JS('', '#.length', _jsObject);
445 // JavaScript arrays have lengths which are unsigned 32-bit integers. 457 // JavaScript arrays have lengths which are unsigned 32-bit integers.
446 if (JS('bool', 'typeof # === "number" && (# >>> 0) === #', len, len, len)) { 458 if (JS('bool', 'typeof # === "number" && (# >>> 0) === #', len, len, len)) {
447 return JS('int', '#', len); 459 return JS('int', '#', len);
448 } 460 }
449 throw new StateError('Bad JsArray length'); 461 throw new StateError('Bad JsArray length');
450 } 462 }
451 463
452 set length(int length) { super['length'] = length; } 464 void set length(int length) {
453 465 super['length'] = length;
466 }
454 467
455 // Methods overriden for better performance 468 // Methods overriden for better performance
456 469
457 void add(E value) { 470 void add(E value) {
458 callMethod('push', [value]); 471 callMethod('push', [value]);
459 } 472 }
460 473
461 void addAll(Iterable<E> iterable) { 474 void addAll(Iterable<E> iterable) {
462 var list = (JS('bool', '# instanceof Array', iterable)) 475 var list = (JS('bool', '# instanceof Array', iterable))
463 ? iterable 476 ? iterable
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 509
497 void sort([int compare(E a, E b)]) { 510 void sort([int compare(E a, E b)]) {
498 // Note: arr.sort(null) is a type error in FF 511 // Note: arr.sort(null) is a type error in FF
499 callMethod('sort', compare == null ? [] : [compare]); 512 callMethod('sort', compare == null ? [] : [compare]);
500 } 513 }
501 } 514 }
502 515
503 // property added to a Dart object referencing its JS-side DartObject proxy 516 // property added to a Dart object referencing its JS-side DartObject proxy
504 final String _DART_OBJECT_PROPERTY_NAME = 517 final String _DART_OBJECT_PROPERTY_NAME =
505 getIsolateAffinityTag(r'_$dart_dartObject'); 518 getIsolateAffinityTag(r'_$dart_dartObject');
506 final String _DART_CLOSURE_PROPERTY_NAME =
507 getIsolateAffinityTag(r'_$dart_dartClosure');
508 519
509 // property added to a JS object referencing its Dart-side JsObject proxy 520 // property added to a JS object referencing its Dart-side JsObject proxy
510 const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject'; 521 const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject';
511 const _JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction'; 522 const _JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction';
523 const _JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS = r'_$dart_jsFunctionCaptureThis';
512 524
513 bool _defineProperty(o, String name, value) { 525 bool _defineProperty(o, String name, value) {
514 try { 526 try {
515 if (_isExtensible(o) && 527 if (_isExtensible(o) &&
516 // TODO(ahe): Calling _hasOwnProperty to work around 528 // TODO(ahe): Calling _hasOwnProperty to work around
517 // https://code.google.com/p/dart/issues/detail?id=21331. 529 // https://code.google.com/p/dart/issues/detail?id=21331.
518 !_hasOwnProperty(o, name)) { 530 !_hasOwnProperty(o, name)) {
519 JS('void', 'Object.defineProperty(#, #, { value: #})', o, name, value); 531 JS('void', 'Object.defineProperty(#, #, { value: #})', o, name, value);
520 return true; 532 return true;
521 } 533 }
(...skipping 26 matching lines...) Expand all
548 dynamic _convertToJS(dynamic o) { 560 dynamic _convertToJS(dynamic o) {
549 // Note: we don't write `if (o == null) return null;` to make sure dart2js 561 // Note: we don't write `if (o == null) return null;` to make sure dart2js
550 // doesn't convert `return null;` into `return;` (which would make `null` be 562 // doesn't convert `return null;` into `return;` (which would make `null` be
551 // `undefined` in Javascprit). See dartbug.com/20305 for details. 563 // `undefined` in Javascprit). See dartbug.com/20305 for details.
552 if (o == null || o is String || o is num || o is bool) { 564 if (o == null || o is String || o is num || o is bool) {
553 return o; 565 return o;
554 } 566 }
555 if (o is JsObject) { 567 if (o is JsObject) {
556 return o._jsObject; 568 return o._jsObject;
557 } 569 }
558 if (o is Blob || o is Event || o is KeyRange || o is ImageData || o is Node || 570 if (o is Blob ||
559 o is TypedData || o is Window) { 571 o is Event ||
572 o is KeyRange ||
573 o is ImageData ||
574 o is Node ||
575 o is TypedData ||
576 o is Window) {
560 return o; 577 return o;
561 } 578 }
562 if (o is DateTime) { 579 if (o is DateTime) {
563 return Primitives.lazyAsJsDate(o); 580 return Primitives.lazyAsJsDate(o);
564 } 581 }
565 if (o is Function) { 582 if (o is Function) {
566 return _getJsProxy(o, _JS_FUNCTION_PROPERTY_NAME, (o) { 583 return _getJsProxy(o, _JS_FUNCTION_PROPERTY_NAME, (o) {
567 var jsFunction = _convertDartFunction(o); 584 var jsFunction = _convertDartFunction(o);
568 // set a property on the JS closure referencing the Dart closure 585 // set a property on the JS closure referencing the Dart closure
569 _defineProperty(jsFunction, _DART_CLOSURE_PROPERTY_NAME, o); 586 _defineProperty(jsFunction, DART_CLOSURE_PROPERTY_NAME, o);
570 return jsFunction; 587 return jsFunction;
571 }); 588 });
572 } 589 }
573 var ctor = _dartProxyCtor; 590 var ctor = _dartProxyCtor;
574 return _getJsProxy(o, _JS_OBJECT_PROPERTY_NAME, 591 return _getJsProxy(
575 (o) => JS('', 'new #(#)', ctor, o)); 592 o, _JS_OBJECT_PROPERTY_NAME, (o) => JS('', 'new #(#)', ctor, o));
576 } 593 }
577 594
578 Object _getJsProxy(o, String propertyName, createProxy(o)) { 595 Object _getJsProxy(o, String propertyName, createProxy(o)) {
579 var jsProxy = _getOwnProperty(o, propertyName); 596 var jsProxy = _getOwnProperty(o, propertyName);
580 if (jsProxy == null) { 597 if (jsProxy == null) {
581 jsProxy = createProxy(o); 598 jsProxy = createProxy(o);
582 _defineProperty(o, propertyName, jsProxy); 599 _defineProperty(o, propertyName, jsProxy);
583 } 600 }
584 return jsProxy; 601 return jsProxy;
585 } 602 }
586 603
587 // converts a Dart object to a reference to a native JS object 604 // converts a Dart object to a reference to a native JS object
588 // which might be a DartObject JS->Dart proxy 605 // which might be a DartObject JS->Dart proxy
589 Object _convertToDart(o) { 606 Object _convertToDart(o) {
590 if (JS('bool', '# == null', o) || 607 if (JS('bool', '# == null', o) ||
591 JS('bool', 'typeof # == "string"', o) || 608 JS('bool', 'typeof # == "string"', o) ||
592 JS('bool', 'typeof # == "number"', o) || 609 JS('bool', 'typeof # == "number"', o) ||
593 JS('bool', 'typeof # == "boolean"', o)) { 610 JS('bool', 'typeof # == "boolean"', o)) {
594 return o; 611 return o;
595 } else if (_isLocalObject(o) 612 } else if (_isLocalObject(o) &&
596 && (o is Blob || o is Event || o is KeyRange || o is ImageData 613 (o is Blob ||
597 || o is Node || o is TypedData || o is Window)) { 614 o is Event ||
615 o is KeyRange ||
616 o is ImageData ||
617 o is Node ||
618 o is TypedData ||
619 o is Window)) {
598 // long line: dart2js doesn't allow string concatenation in the JS() form 620 // long line: dart2js doesn't allow string concatenation in the JS() form
599 return JS('Blob|Event|KeyRange|ImageData|Node|TypedData|Window', '#', o); 621 return JS('Blob|Event|KeyRange|ImageData|Node|TypedData|Window', '#', o);
600 } else if (JS('bool', '# instanceof Date', o)) { 622 } else if (JS('bool', '# instanceof Date', o)) {
601 var ms = JS('num', '#.getTime()', o); 623 var ms = JS('num', '#.getTime()', o);
602 return new DateTime.fromMillisecondsSinceEpoch(ms); 624 return new DateTime.fromMillisecondsSinceEpoch(ms);
603 } else if (JS('bool', '#.constructor === #', o, _dartProxyCtor)) { 625 } else if (JS('bool', '#.constructor === #', o, _dartProxyCtor)) {
604 return JS('', '#.o', o); 626 return JS('', '#.o', o);
605 } else { 627 } else {
606 return _wrapToDart(o); 628 return _wrapToDart(o);
607 } 629 }
608 } 630 }
609 631
610 JsObject _wrapToDart(o) { 632 JsObject _wrapToDart(o) {
611 if (JS('bool', 'typeof # == "function"', o)) { 633 if (JS('bool', 'typeof # == "function"', o)) {
612 return _getDartProxy(o, _DART_CLOSURE_PROPERTY_NAME, 634 return _getDartProxy(
613 (o) => new JsFunction._fromJs(o)); 635 o, DART_CLOSURE_PROPERTY_NAME, (o) => new JsFunction._fromJs(o));
614 } 636 }
615 if (JS('bool', '# instanceof Array', o)) { 637 if (JS('bool', '# instanceof Array', o)) {
616 return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME, 638 return _getDartProxy(
617 (o) => new JsArray._fromJs(o)); 639 o, _DART_OBJECT_PROPERTY_NAME, (o) => new JsArray._fromJs(o));
618 } 640 }
619 return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME, 641 return _getDartProxy(
620 (o) => new JsObject._fromJs(o)); 642 o, _DART_OBJECT_PROPERTY_NAME, (o) => new JsObject._fromJs(o));
621 } 643 }
622 644
623 Object _getDartProxy(o, String propertyName, createProxy(o)) { 645 Object _getDartProxy(o, String propertyName, createProxy(o)) {
624 var dartProxy = _getOwnProperty(o, propertyName); 646 var dartProxy = _getOwnProperty(o, propertyName);
625 // Temporary fix for dartbug.com/15193 647 // Temporary fix for dartbug.com/15193
626 // In some cases it's possible to see a JavaScript object that 648 // In some cases it's possible to see a JavaScript object that
627 // came from a different context and was previously proxied to 649 // came from a different context and was previously proxied to
628 // Dart in that context. The JS object will have a cached proxy 650 // Dart in that context. The JS object will have a cached proxy
629 // but it won't be a valid Dart object in this context. 651 // but it won't be a valid Dart object in this context.
630 // For now we throw away the cached proxy, but we should be able 652 // For now we throw away the cached proxy, but we should be able
631 // to cache proxies from multiple JS contexts and Dart isolates. 653 // to cache proxies from multiple JS contexts and Dart isolates.
632 if (dartProxy == null || !_isLocalObject(o)) { 654 if (dartProxy == null || !_isLocalObject(o)) {
633 dartProxy = createProxy(o); 655 dartProxy = createProxy(o);
634 _defineProperty(o, propertyName, dartProxy); 656 _defineProperty(o, propertyName, dartProxy);
635 } 657 }
636 return dartProxy; 658 return dartProxy;
637 } 659 }
660
661 // Start of methods for new style Dart-JS interop.
Siggi Cherem (dart-lang) 2015/10/06 22:38:02 nit: maybe add a // ------------------------------
Jacob 2015/10/13 01:19:24 Done.
662
663 const _UNDEFINED = const JS_CONST('void 0');
664
665 // TODO(jacobr): this method is a hack to work around the lack of proper dart
666 // support for varargs methods.
667 List _stripUndefinedArgs(List args) =>
668 args.takeWhile((i) => JS('bool', '# !== #', i, _UNDEFINED)).toList();
sra1 2015/10/06 21:42:17 could also use JS('bool', 'typeof # != "undefined"
Jacob 2015/10/13 01:19:24 done. is that faster?
669
670 class _JavaScriptFunctionHack implements Function {
671 call(
672 [a = _UNDEFINED,
673 b = _UNDEFINED,
674 c = _UNDEFINED,
675 d = _UNDEFINED,
676 e = _UNDEFINED,
677 f = _UNDEFINED,
678 g = _UNDEFINED,
679 h = _UNDEFINED,
680 i = _UNDEFINED,
681 j = _UNDEFINED]) {
682 // Exceedingly slow default implementation.
683 return JS('', '#.apply(null, #)', this,
684 _stripUndefinedArgs([a, b, c, d, e, f, g, h, i, j]));
685 }
686 }
687
688 void _copyOwnProperties(src, dest) {
689 JS(
690 '',
Siggi Cherem (dart-lang) 2015/10/06 22:38:02 eeek - maybe file a dartfmt bug, this format seems
Jacob 2015/10/13 01:19:24 i think the existing is easier to read although le
691 r'''
692 (function(src, dest) {
693 var properties = Object.getOwnPropertyNames(src);
694 for (var i = 0, len = properties.length; i < len; i++) {
695 var name = properties[i];
696 dest[name] = src[name];
697 }
698 })(#, #)
699 ''',
700 src,
701 dest);
702 }
703
704 // TODO(jacobr): remove this method. So far it appears that specifying the list
705 // of registered types in Dart2Js has significant negative code size
706 // implications so it is better to specify usage purely based on which
707 // libraries are imported. Remove after Dartium is modified to function without
708 // requiring this method.
709 void registerJsInterfaces([List<Type> types]) {
Siggi Cherem (dart-lang) 2015/10/06 22:38:02 I thought you said this was no longer used?
Jacob 2015/10/13 01:19:24 I need a home for the fnProto munging. We also nee
Siggi Cherem (dart-lang) 2015/10/13 02:08:52 sounds good - btw, this code disappeared, did it m
Jacob 2015/10/13 03:10:42 Yep. I now generate the .call$N methods through th
710 // No need to actually register in Dart2JS.
711 var fnHackProto = JS('', '#.__proto__', new _JavaScriptFunctionHack());
712 var fnProto = JS('', 'Function.prototype');
713 _copyOwnProperties(fnHackProto, fnProto);
714 // Add optimized call methods for small numbers of arguments.
715 if (JS('bool', r'#.hasOwnProperty("call$0") ', fnHackProto)) {
716 JS('', r'#.call$0 = function() { return this(); }', fnProto);
717 JS('', r'#.call$1 = function(a) { return this(a); }', fnProto);
718 JS('', r'#.call$2 = function(a, b) { return this(a, b); }', fnProto);
719 JS('', r'#.call$3 = function(a, b, c) { return this(a, b, c); }', fnProto);
720 JS('', r'#.call$4 = function(a, b, c, d) { return this(a, b, c, d); }',
721 fnProto);
722 } else {
723 if (!JS('bool', r'#.hasOwnProperty("$0") ', fnHackProto)) {
724 throw 'Internal error. Unexpected minified output';
725 }
726 JS('', r'#.$0 = function() { return this(); }', fnProto);
727 JS('', r'#.$1 = function(a) { return this(a); }', fnProto);
728 JS('', r'#.$2 = function(a, b) { return this(a, b); }', fnProto);
729 JS('', r'#.$3 = function(a, b, c) { return this(a, b, c); }', fnProto);
730 JS('', r'#.$4 = function(a, b, c, d) { return this(a, b, c, d); }',
731 fnProto);
732 }
733 }
734
735 _convertDartFunctionFast(Function f) {
736 var existing = JS('', '#.#', f, _JS_FUNCTION_PROPERTY_NAME);
737 if (existing != null) return existing;
738 var ret = JS(
739 '',
740 '''
741 function(_call, f) {
742 return function() {
743 return _call(f, Array.prototype.slice.apply(arguments));
sra1 2015/10/06 21:42:17 .apply -> .call ? Add small targeted test for thi
Jacob 2015/10/13 01:19:24 I have tests for this. Array.prototype.slice.apply
744 }
745 }(#, #)
746 ''',
747 DART_CLOSURE_TO_JS(_callDartFunctionFast),
748 f);
749 JS('', '#.# = #', ret, DART_CLOSURE_PROPERTY_NAME, f);
750 JS('', '#.# = #', f, _JS_FUNCTION_PROPERTY_NAME, ret);
751 return ret;
752 }
753
754 _convertDartFunctionFastCaptureThis(Function f) {
755 var existing = JS('', '#.#', f, _JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS);
756 if (existing != null) return existing;
757 var ret = JS(
758 '',
759 '''
760 function(_call, f) {
761 return function() {
762 return _call(f, this,Array.prototype.slice.apply(arguments));
sra1 2015/10/06 21:42:17 'this,' -> 'this, ' .apply -> .call ? Add test f
Jacob 2015/10/13 01:19:24 see comment for previous case.
763 }
764 }(#, #)
765 ''',
766 DART_CLOSURE_TO_JS(_callDartFunctionFastCaptureThis),
767 f);
768 JS('', '#.# = #', ret, DART_CLOSURE_PROPERTY_NAME, f);
769 JS('', '#.# = #', f, _JS_FUNCTION_PROPERTY_NAME_CAPTURE_THIS, ret);
770 return ret;
771 }
772
773 _callDartFunctionFast(callback, List arguments) {
774 return Function.apply(callback, arguments);
775 }
776
777 _callDartFunctionFastCaptureThis(callback, self, List arguments) {
778 return _convertToJS(Function.apply(callback, [self]..addAll(arguments)));
779 }
780
781 Function allowInterop(Function f) {
782 if (JS('bool', 'typeof(#) == "function"', f)) {
783 // Already supports interop, just use the existing function.
784 return f;
785 } else {
786 return _convertDartFunctionFast(f);
787 }
788 }
789
790 Function allowInteropCaptureThis(Function f) {
791 if (JS('bool', 'typeof(#) == "function"', f)) {
792 // Behavior when the function is already a JS function is unspecified.
793 throw new ArgumentError(
794 "Function is already a JS function so cannot capture this.");
795 return f;
796 } else {
797 return _convertDartFunctionFastCaptureThis(f);
798 }
799 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698