| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 277 throw new NoSuchMethodError(_jsObject, new Symbol(method), args, {}); | 277 throw new NoSuchMethodError(_jsObject, new Symbol(method), args, {}); |
| 278 } | 278 } |
| 279 return _convertToDart(JS('', '#.apply(#, #)', fn, _jsObject, args)); | 279 return _convertToDart(JS('', '#.apply(#, #)', fn, _jsObject, args)); |
| 280 } | 280 } |
| 281 } | 281 } |
| 282 | 282 |
| 283 /** | 283 /** |
| 284 * Proxies a JavaScript Function object. | 284 * Proxies a JavaScript Function object. |
| 285 */ | 285 */ |
| 286 class JsFunction extends JsObject { | 286 class JsFunction extends JsObject { |
| 287 | |
| 288 /** | 287 /** |
| 289 * Returns a [JsFunction] that captures its 'this' binding and calls [f] | 288 * Returns a [JsFunction] that captures its 'this' binding and calls [f] |
| 290 * with the value of this passed as the first argument. | 289 * with the value of this passed as the first argument. |
| 291 */ | 290 */ |
| 292 factory JsFunction.withThis(Function f) { | 291 factory JsFunction.withThis(Function f) { |
| 293 return new JsFunction._fromJs(JS('', 'function(/*...arguments*/) {' | 292 return new JsFunction._fromJs(JS( |
| 293 '', |
| 294 'function(/*...arguments*/) {' |
| 294 ' let args = [#(this)];' | 295 ' let args = [#(this)];' |
| 295 ' for (let arg of arguments) {' | 296 ' for (let arg of arguments) {' |
| 296 ' args.push(#(arg));' | 297 ' args.push(#(arg));' |
| 297 ' }' | 298 ' }' |
| 298 ' return #(#(...args));' | 299 ' return #(#(...args));' |
| 299 '}', _convertToDart, _convertToDart, _convertToJS, f)); | 300 '}', |
| 301 _convertToDart, |
| 302 _convertToDart, |
| 303 _convertToJS, |
| 304 f)); |
| 300 } | 305 } |
| 301 | 306 |
| 302 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); | 307 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); |
| 303 | 308 |
| 304 /** | 309 /** |
| 305 * Invokes the JavaScript function with arguments [args]. If [thisArg] is | 310 * Invokes the JavaScript function with arguments [args]. If [thisArg] is |
| 306 * supplied it is the value of `this` for the invocation. | 311 * supplied it is the value of `this` for the invocation. |
| 307 */ | 312 */ |
| 308 dynamic apply(List args, {thisArg}) => _convertToDart(JS('', '#.apply(#, #)', | 313 dynamic apply(List args, {thisArg}) => _convertToDart(JS( |
| 309 _jsObject, _convertToJS(thisArg), | 314 '', |
| 315 '#.apply(#, #)', |
| 316 _jsObject, |
| 317 _convertToJS(thisArg), |
| 310 args == null ? null : new List.from(args.map(_convertToJS)))); | 318 args == null ? null : new List.from(args.map(_convertToJS)))); |
| 311 } | 319 } |
| 312 | 320 |
| 313 // TODO(jmesserly): this is totally unnecessary in dev_compiler. | 321 // TODO(jmesserly): this is totally unnecessary in dev_compiler. |
| 314 /** A [List] that proxies a JavaScript array. */ | 322 /** A [List] that proxies a JavaScript array. */ |
| 315 class JsArray<E> extends JsObject with ListMixin<E> { | 323 class JsArray<E> extends JsObject with ListMixin<E> { |
| 316 | |
| 317 /** | 324 /** |
| 318 * Creates a new JavaScript array. | 325 * Creates a new JavaScript array. |
| 319 */ | 326 */ |
| 320 JsArray() : super._fromJs([]); | 327 JsArray() : super._fromJs([]); |
| 321 | 328 |
| 322 /** | 329 /** |
| 323 * Creates a new JavaScript array and initializes it to the contents of | 330 * Creates a new JavaScript array and initializes it to the contents of |
| 324 * [other]. | 331 * [other]. |
| 325 */ | 332 */ |
| 326 JsArray.from(Iterable<E> other) | 333 JsArray.from(Iterable<E> other) |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 414 void removeRange(int start, int end) { | 421 void removeRange(int start, int end) { |
| 415 _checkRange(start, end, length); | 422 _checkRange(start, end, length); |
| 416 callMethod('splice', [start, end - start]); | 423 callMethod('splice', [start, end - start]); |
| 417 } | 424 } |
| 418 | 425 |
| 419 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { | 426 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { |
| 420 _checkRange(start, end, this.length); | 427 _checkRange(start, end, this.length); |
| 421 int length = end - start; | 428 int length = end - start; |
| 422 if (length == 0) return; | 429 if (length == 0) return; |
| 423 if (skipCount < 0) throw new ArgumentError(skipCount); | 430 if (skipCount < 0) throw new ArgumentError(skipCount); |
| 424 var args = <Object>[start, length]..addAll(iterable.skip(skipCount).take(len
gth)); | 431 var args = <Object>[start, length] |
| 432 ..addAll(iterable.skip(skipCount).take(length)); |
| 425 callMethod('splice', args); | 433 callMethod('splice', args); |
| 426 } | 434 } |
| 427 | 435 |
| 428 void sort([int compare(E a, E b)]) { | 436 void sort([int compare(E a, E b)]) { |
| 429 // Note: arr.sort(null) is a type error in FF | 437 // Note: arr.sort(null) is a type error in FF |
| 430 callMethod('sort', compare == null ? [] : [compare]); | 438 callMethod('sort', compare == null ? [] : [compare]); |
| 431 } | 439 } |
| 432 } | 440 } |
| 433 | 441 |
| 434 // Cross frame objects should not be considered browser types. | 442 // Cross frame objects should not be considered browser types. |
| 435 // We include the the instanceof Object test to filter out cross frame objects | 443 // We include the the instanceof Object test to filter out cross frame objects |
| 436 // on FireFox. Surprisingly on FireFox the instanceof Window test succeeds for | 444 // on FireFox. Surprisingly on FireFox the instanceof Window test succeeds for |
| 437 // cross frame windows while the instanceof Object test fails. | 445 // cross frame windows while the instanceof Object test fails. |
| 438 bool _isBrowserType(o) => JS('bool', | 446 bool _isBrowserType(o) => JS( |
| 447 'bool', |
| 439 '# instanceof Object && (' | 448 '# instanceof Object && (' |
| 440 '# instanceof Blob || ' | 449 '# instanceof Blob || ' |
| 441 '# instanceof Event || ' | 450 '# instanceof Event || ' |
| 442 '(window.KeyRange && # instanceof KeyRange) || ' | 451 '(window.KeyRange && # instanceof KeyRange) || ' |
| 443 '(window.IDBKeyRange && # instanceof IDBKeyRange) || ' | 452 '(window.IDBKeyRange && # instanceof IDBKeyRange) || ' |
| 444 '# instanceof ImageData || ' | 453 '# instanceof ImageData || ' |
| 445 '# instanceof Node || ' | 454 '# instanceof Node || ' |
| 446 // Int8Array.__proto__ is TypedArray. | 455 // Int8Array.__proto__ is TypedArray. |
| 447 '(window.Int8Array && # instanceof Int8Array.__proto__) || ' | 456 '(window.Int8Array && # instanceof Int8Array.__proto__) || ' |
| 448 '# instanceof Window)', o, o, o, o, o, o, o, o, o); | 457 '# instanceof Window)', |
| 458 o, |
| 459 o, |
| 460 o, |
| 461 o, |
| 462 o, |
| 463 o, |
| 464 o, |
| 465 o, |
| 466 o); |
| 449 | 467 |
| 450 class _DartObject { | 468 class _DartObject { |
| 451 final _dartObj; | 469 final _dartObj; |
| 452 _DartObject(this._dartObj); | 470 _DartObject(this._dartObj); |
| 453 } | 471 } |
| 454 | 472 |
| 455 dynamic _convertToJS(dynamic o) { | 473 dynamic _convertToJS(dynamic o) { |
| 456 if (o == null || | 474 if (o == null || o is String || o is num || o is bool || _isBrowserType(o)) { |
| 457 o is String || | |
| 458 o is num || | |
| 459 o is bool || | |
| 460 _isBrowserType(o)) { | |
| 461 return o; | 475 return o; |
| 462 } else if (o is DateTime) { | 476 } else if (o is DateTime) { |
| 463 return Primitives.lazyAsJsDate(o); | 477 return Primitives.lazyAsJsDate(o); |
| 464 } else if (o is JsObject) { | 478 } else if (o is JsObject) { |
| 465 return o._jsObject; | 479 return o._jsObject; |
| 466 } else if (o is Function) { | 480 } else if (o is Function) { |
| 467 return _putIfAbsent(_jsProxies, o, _wrapDartFunction); | 481 return _putIfAbsent(_jsProxies, o, _wrapDartFunction); |
| 468 } else { | 482 } else { |
| 469 // TODO(jmesserly): for now, we wrap other objects, to keep compatibility | 483 // TODO(jmesserly): for now, we wrap other objects, to keep compatibility |
| 470 // with the original dart:js behavior. | 484 // with the original dart:js behavior. |
| 471 return _putIfAbsent(_jsProxies, o, (o) => new _DartObject(o)); | 485 return _putIfAbsent(_jsProxies, o, (o) => new _DartObject(o)); |
| 472 } | 486 } |
| 473 } | 487 } |
| 474 | 488 |
| 475 dynamic _wrapDartFunction(f) { | 489 dynamic _wrapDartFunction(f) { |
| 476 var wrapper = JS('', 'function(/*...arguments*/) {' | 490 var wrapper = JS( |
| 491 '', |
| 492 'function(/*...arguments*/) {' |
| 477 ' let args = Array.prototype.map.call(arguments, #);' | 493 ' let args = Array.prototype.map.call(arguments, #);' |
| 478 ' return #(#(...args));' | 494 ' return #(#(...args));' |
| 479 '}', _convertToDart, _convertToJS, f); | 495 '}', |
| 496 _convertToDart, |
| 497 _convertToJS, |
| 498 f); |
| 480 JS('', '#.set(#, #)', _dartProxies, wrapper, f); | 499 JS('', '#.set(#, #)', _dartProxies, wrapper, f); |
| 481 | 500 |
| 482 return wrapper; | 501 return wrapper; |
| 483 } | 502 } |
| 484 | 503 |
| 485 // converts a Dart object to a reference to a native JS object | 504 // converts a Dart object to a reference to a native JS object |
| 486 // which might be a DartObject JS->Dart proxy | 505 // which might be a DartObject JS->Dart proxy |
| 487 Object _convertToDart(o) { | 506 Object _convertToDart(o) { |
| 488 if (JS('bool', '# == null', o) || | 507 if (JS('bool', '# == null', o) || |
| 489 JS('bool', 'typeof # == "string"', o) || | 508 JS('bool', 'typeof # == "string"', o) || |
| 490 JS('bool', 'typeof # == "number"', o) || | 509 JS('bool', 'typeof # == "number"', o) || |
| 491 JS('bool', 'typeof # == "boolean"', o) || | 510 JS('bool', 'typeof # == "boolean"', o) || |
| 492 _isBrowserType(o)) { | 511 _isBrowserType(o)) { |
| 493 return o; | 512 return o; |
| 494 } else if (JS('bool', '# instanceof Date', o)) { | 513 } else if (JS('bool', '# instanceof Date', o)) { |
| 495 var ms = JS('num', '#.getTime()', o); | 514 var ms = JS('num', '#.getTime()', o); |
| 496 return new DateTime.fromMillisecondsSinceEpoch(ms); | 515 return new DateTime.fromMillisecondsSinceEpoch(ms); |
| 497 } else if (o is _DartObject && | 516 } else if (o is _DartObject && |
| 498 JS('bool', 'dart.jsobject != dart.getReifiedType(#)', o)) { | 517 JS('bool', 'dart.jsobject != dart.getReifiedType(#)', o)) { |
| 499 return o._dartObj; | 518 return o._dartObj; |
| 500 } else { | 519 } else { |
| 501 return _wrapToDart(o); | 520 return _wrapToDart(o); |
| 502 } | 521 } |
| 503 } | 522 } |
| 504 | 523 |
| 505 Object _wrapToDart(o) => _putIfAbsent(_dartProxies, o, _wrapToDartHelper); | 524 Object _wrapToDart(o) => _putIfAbsent(_dartProxies, o, _wrapToDartHelper); |
| 506 | 525 |
| 507 Object _wrapToDartHelper(o) { | 526 Object _wrapToDartHelper(o) { |
| 508 if (JS('bool', 'typeof # == "function"', o)) { | 527 if (JS('bool', 'typeof # == "function"', o)) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 534 /// using the package:js Dart-JavaScript interop. | 553 /// using the package:js Dart-JavaScript interop. |
| 535 /// | 554 /// |
| 536 /// For performance reasons in Dart2Js, by default Dart functions cannot be | 555 /// For performance reasons in Dart2Js, by default Dart functions cannot be |
| 537 /// passed directly to JavaScript unless this method is called to create | 556 /// passed directly to JavaScript unless this method is called to create |
| 538 /// a Function compatible with both Dart and JavaScript. | 557 /// a Function compatible with both Dart and JavaScript. |
| 539 /// Calling this method repeatedly on a function will return the same function. | 558 /// Calling this method repeatedly on a function will return the same function. |
| 540 /// The [Function] returned by this method can be used from both Dart and | 559 /// The [Function] returned by this method can be used from both Dart and |
| 541 /// JavaScript. We may remove the need to call this method completely in the | 560 /// JavaScript. We may remove the need to call this method completely in the |
| 542 /// future if Dart2Js is refactored so that its function calling conventions | 561 /// future if Dart2Js is refactored so that its function calling conventions |
| 543 /// are more compatible with JavaScript. | 562 /// are more compatible with JavaScript. |
| 544 Function /*=F*/ allowInterop/*<F extends Function>*/(Function /*=F*/ f) => f; | 563 Function/*=F*/ allowInterop/*<F extends Function>*/(Function/*=F*/ f) => f; |
| 545 | 564 |
| 546 Expando<Function> _interopCaptureThisExpando = new Expando<Function>(); | 565 Expando<Function> _interopCaptureThisExpando = new Expando<Function>(); |
| 547 | 566 |
| 548 /// Returns a [Function] that when called from JavaScript captures its 'this' | 567 /// Returns a [Function] that when called from JavaScript captures its 'this' |
| 549 /// binding and calls [f] with the value of this passed as the first argument. | 568 /// binding and calls [f] with the value of this passed as the first argument. |
| 550 /// When called from Dart, [null] will be passed as the first argument. | 569 /// When called from Dart, [null] will be passed as the first argument. |
| 551 /// | 570 /// |
| 552 /// See the documention for [allowInterop]. This method should only be used with | 571 /// See the documention for [allowInterop]. This method should only be used with |
| 553 /// package:js Dart-JavaScript interop. | 572 /// package:js Dart-JavaScript interop. |
| 554 Function allowInteropCaptureThis(Function f) { | 573 Function allowInteropCaptureThis(Function f) { |
| 555 var ret = _interopCaptureThisExpando[f]; | 574 var ret = _interopCaptureThisExpando[f]; |
| 556 if (ret == null) { | 575 if (ret == null) { |
| 557 ret = JS('', | 576 ret = JS( |
| 577 '', |
| 558 'function(/*...arguments*/) {' | 578 'function(/*...arguments*/) {' |
| 559 ' let args = [this];' | 579 ' let args = [this];' |
| 560 ' for (let arg of arguments) {' | 580 ' for (let arg of arguments) {' |
| 561 ' args.push(arg);' | 581 ' args.push(arg);' |
| 562 ' }' | 582 ' }' |
| 563 ' return #(...args);' | 583 ' return #(...args);' |
| 564 '}', | 584 '}', |
| 565 f); | 585 f); |
| 566 _interopCaptureThisExpando[f] = ret; | 586 _interopCaptureThisExpando[f] = ret; |
| 567 } | 587 } |
| 568 return ret; | 588 return ret; |
| 569 } | 589 } |
| OLD | NEW |