| OLD | NEW |
| 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 Loading... |
| 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:collection' show ListMixin; | 90 import 'dart:collection' show ListMixin; |
| 91 import 'dart:nativewrappers'; | 91 import 'dart:nativewrappers'; |
| 92 import 'dart:math' as math; | 92 import 'dart:math' as math; |
| 93 import 'dart:mirrors' as mirrors; | 93 import 'dart:mirrors' as mirrors; |
| 94 import 'dart:html' as html; | 94 import 'dart:html' as html; |
| 95 import 'dart:_blink' as _blink; |
| 95 import 'dart:html_common' as html_common; | 96 import 'dart:html_common' as html_common; |
| 96 import 'dart:indexed_db' as indexed_db; | 97 import 'dart:indexed_db' as indexed_db; |
| 97 import 'dart:typed_data'; | |
| 98 | 98 |
| 99 // Pretend we are always in checked mode as we aren't interested in users | 99 // Pretend we are always in checked mode as we aren't interested in users |
| 100 // running Dartium code outside of checked mode. | 100 // running Dartium code outside of checked mode. |
| 101 @Deprecated("Internal Use Only") | 101 @Deprecated("Internal Use Only") |
| 102 final bool CHECK_JS_INVOCATIONS = true; | 102 final bool CHECK_JS_INVOCATIONS = true; |
| 103 | 103 |
| 104 final String _DART_RESERVED_NAME_PREFIX = r'JS$'; | 104 final String _DART_RESERVED_NAME_PREFIX = r'JS$'; |
| 105 | 105 |
| 106 String _stripReservedNamePrefix(String name) => | 106 String _stripReservedNamePrefix(String name) => |
| 107 name.startsWith(_DART_RESERVED_NAME_PREFIX) | 107 name.startsWith(_DART_RESERVED_NAME_PREFIX) |
| 108 ? name.substring(_DART_RESERVED_NAME_PREFIX.length) | 108 ? name.substring(_DART_RESERVED_NAME_PREFIX.length) |
| 109 : name; | 109 : name; |
| 110 | 110 |
| 111 _buildArgs(Invocation invocation) { | 111 _buildArgs(Invocation invocation) { |
| 112 if (invocation.namedArguments.isEmpty) { | 112 if (invocation.namedArguments.isEmpty) { |
| 113 return invocation.positionalArguments; | 113 return invocation.positionalArguments; |
| 114 } else { | 114 } else { |
| 115 var varArgs = new Map<String, Object>(); | 115 var varArgs = new Map<String, Object>(); |
| 116 invocation.namedArguments.forEach((symbol, val) { | 116 invocation.namedArguments.forEach((symbol, val) { |
| 117 varArgs[mirrors.MirrorSystem.getName(symbol)] = val; | 117 varArgs[mirrors.MirrorSystem.getName(symbol)] = val; |
| 118 }); | 118 }); |
| 119 return invocation.positionalArguments.toList() | 119 return invocation.positionalArguments.toList() |
| 120 ..add(maybeWrapTypedInterop(new JsObject.jsify(varArgs))); | 120 ..add(JsNative.jsify(varArgs)); |
| 121 } | 121 } |
| 122 } | 122 } |
| 123 | 123 |
| 124 final _allowedMethods = new Map<Symbol, _DeclarationSet>(); | 124 final _allowedMethods = new Map<Symbol, _DeclarationSet>(); |
| 125 final _allowedGetters = new Map<Symbol, _DeclarationSet>(); | 125 final _allowedGetters = new Map<Symbol, _DeclarationSet>(); |
| 126 final _allowedSetters = new Map<Symbol, _DeclarationSet>(); | 126 final _allowedSetters = new Map<Symbol, _DeclarationSet>(); |
| 127 | 127 |
| 128 final _jsInterfaceTypes = new Set<mirrors.ClassMirror>(); | 128 final _jsInterfaceTypes = new Set<mirrors.ClassMirror>(); |
| 129 @Deprecated("Internal Use Only") | 129 @Deprecated("Internal Use Only") |
| 130 Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes; | 130 Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes; |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 350 var sb = new StringBuffer(); | 350 var sb = new StringBuffer(); |
| 351 sb | 351 sb |
| 352 ..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length) | 352 ..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length) |
| 353 ..write("${_JS_LIBRARY_PREFIX}.context"); | 353 ..write("${_JS_LIBRARY_PREFIX}.context"); |
| 354 for (var p in parts) { | 354 for (var p in parts) { |
| 355 sb.write(", '$p')"); | 355 sb.write(", '$p')"); |
| 356 } | 356 } |
| 357 return sb.toString(); | 357 return sb.toString(); |
| 358 } | 358 } |
| 359 | 359 |
| 360 // TODO(jacobr): remove these helpers and add JsNative.setPropertyDotted, |
| 361 // getPropertyDotted, and callMethodDotted helpers that would be simpler |
| 362 // and more efficient. |
| 360 String _accessJsPathSetter(String path) { | 363 String _accessJsPathSetter(String path) { |
| 361 var parts = path.split("."); | 364 var parts = path.split("."); |
| 362 return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPathHelper(parts
.getRange(0, parts.length - 1)) | 365 return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPathHelper(parts
.getRange(0, parts.length - 1)) |
| 363 }, '${parts.last}', v)"; | 366 }, '${parts.last}', v)"; |
| 364 } | 367 } |
| 365 | 368 |
| 369 String _accessJsPathCallMethodHelper(String path) { |
| 370 var parts = path.split("."); |
| 371 return "${_JS_LIBRARY_PREFIX}.JsNative.callMethod(${_accessJsPathHelper(parts.
getRange(0, parts.length - 1)) |
| 372 }, '${parts.last}',"; |
| 373 } |
| 374 |
| 366 @Deprecated("Internal Use Only") | 375 @Deprecated("Internal Use Only") |
| 367 void addMemberHelper( | 376 void addMemberHelper( |
| 368 mirrors.MethodMirror declaration, String path, StringBuffer sb, | 377 mirrors.MethodMirror declaration, String path, StringBuffer sb, |
| 369 {bool isStatic: false, String memberName}) { | 378 {bool isStatic: false, String memberName}) { |
| 370 if (!declaration.isConstructor) { | 379 if (!declaration.isConstructor) { |
| 371 var jsName = _getJsMemberName(declaration); | 380 var jsName = _getJsMemberName(declaration); |
| 372 path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName; | 381 path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName; |
| 373 } | 382 } |
| 374 var name = memberName != null ? memberName : _getDeclarationName(declaration); | 383 var name = memberName != null ? memberName : _getDeclarationName(declaration); |
| 375 if (declaration.isConstructor) { | 384 if (declaration.isConstructor) { |
| 376 sb.write("factory"); | 385 sb.write("factory"); |
| 377 } else if (isStatic) { | 386 } else if (isStatic) { |
| 378 sb.write("static"); | 387 sb.write("static"); |
| 379 } else { | 388 } else { |
| 380 sb.write("patch"); | 389 sb.write("patch"); |
| 381 } | 390 } |
| 382 sb.write(" "); | 391 sb.write(" "); |
| 383 if (declaration.isGetter) { | 392 if (declaration.isGetter) { |
| 384 sb.write( | 393 sb.write( |
| 385 "get $name => ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_accessJsPat
h(path)});"); | 394 "get $name => ${_accessJsPath(path)};"); |
| 386 } else if (declaration.isSetter) { | 395 } else if (declaration.isSetter) { |
| 387 sb.write("set $name(v) {\n" | 396 sb.write("set $name(v) {\n" |
| 388 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop(v);\n" | 397 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop(v);\n" |
| 389 " return ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_accessJsPathSet
ter(path)});\n" | 398 " return ${_accessJsPathSetter(path)};\n" |
| 390 "}\n"); | 399 "}\n"); |
| 391 } else { | 400 } else { |
| 392 sb.write("$name("); | 401 sb.write("$name("); |
| 393 bool hasOptional = false; | 402 bool hasOptional = false; |
| 394 int i = 0; | 403 int i = 0; |
| 395 var args = <String>[]; | 404 var args = <String>[]; |
| 396 for (var p in declaration.parameters) { | 405 for (var p in declaration.parameters) { |
| 397 assert(!p.isNamed); // TODO(jacobr): throw. | 406 assert(!p.isNamed); // TODO(jacobr): throw. |
| 398 assert(!p.hasDefaultValue); | 407 assert(!p.hasDefaultValue); |
| 399 if (i > 0) { | 408 if (i > 0) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 412 i++; | 421 i++; |
| 413 } | 422 } |
| 414 if (hasOptional) { | 423 if (hasOptional) { |
| 415 sb.write("]"); | 424 sb.write("]"); |
| 416 } | 425 } |
| 417 // TODO(jacobr): | 426 // TODO(jacobr): |
| 418 sb.write(") {\n"); | 427 sb.write(") {\n"); |
| 419 for (var arg in args) { | 428 for (var arg in args) { |
| 420 sb.write(" ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($arg);\n"); | 429 sb.write(" ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($arg);\n"); |
| 421 } | 430 } |
| 422 sb.write(" return ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop("); | 431 sb.write(" return "); |
| 423 if (declaration.isConstructor) { | 432 if (declaration.isConstructor) { |
| 424 sb.write("new ${_JS_LIBRARY_PREFIX}.JsObject("); | 433 sb.write("${_JS_LIBRARY_PREFIX}.JsNative.callConstructor("); |
| 434 sb..write(_accessJsPath(path))..write(","); |
| 435 } else { |
| 436 sb.write(_accessJsPathCallMethodHelper(path)); |
| 425 } | 437 } |
| 426 sb | 438 sb.write("[${args.join(",")}]"); |
| 427 ..write(_accessJsPath(path)) | |
| 428 ..write(declaration.isConstructor ? "," : ".apply(") | |
| 429 ..write("[${args.join(",")}]"); | |
| 430 | 439 |
| 431 if (hasOptional) { | 440 if (hasOptional) { |
| 432 sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()"); | 441 sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()"); |
| 433 } | 442 } |
| 434 sb.write("));"); | 443 sb.write(");"); |
| 435 sb.write("}\n"); | 444 sb.write("}\n"); |
| 436 } | 445 } |
| 437 sb.write("\n"); | 446 sb.write("\n"); |
| 438 } | 447 } |
| 439 | 448 |
| 440 bool _isExternal(mirrors.MethodMirror mirror) { | 449 bool _isExternal(mirrors.MethodMirror mirror) { |
| 441 // This try-catch block is a workaround for BUG:24834. | 450 // This try-catch block is a workaround for BUG:24834. |
| 442 try { | 451 try { |
| 443 return mirror.isExternal; | 452 return mirror.isExternal; |
| 444 } catch (e) {} | 453 } catch (e) {} |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 483 } | 492 } |
| 484 if (args.isNotEmpty) { | 493 if (args.isNotEmpty) { |
| 485 sbPatch | 494 sbPatch |
| 486 ..write('{') | 495 ..write('{') |
| 487 ..write(args | 496 ..write(args |
| 488 .map((name) => '$name:${_UNDEFINED_VAR}') | 497 .map((name) => '$name:${_UNDEFINED_VAR}') |
| 489 .join(", ")) | 498 .join(", ")) |
| 490 ..write('}'); | 499 ..write('}'); |
| 491 } | 500 } |
| 492 sbPatch.write(") {\n" | 501 sbPatch.write(") {\n" |
| 493 " var ret = new ${_JS_LIBRARY_PREFIX}.JsObject.jsify({});
\n"); | 502 " var ret = ${_JS_LIBRARY_PREFIX}.JsNative.newObject();\n
"); |
| 494 i = 0; | 503 i = 0; |
| 495 for (var p in declaration.parameters) { | 504 for (var p in declaration.parameters) { |
| 496 assert(p.isNamed); // TODO(jacobr): throw. | 505 assert(p.isNamed); // TODO(jacobr): throw. |
| 497 var name = args[i]; | 506 var name = args[i]; |
| 498 var jsName = _stripReservedNamePrefix( | 507 var jsName = _stripReservedNamePrefix( |
| 499 mirrors.MirrorSystem.getName(p.simpleName)); | 508 mirrors.MirrorSystem.getName(p.simpleName)); |
| 500 sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n" | 509 sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n" |
| 501 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n
" | 510 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n
" |
| 502 " ret['$jsName'] = $name;\n" | 511 " ${_JS_LIBRARY_PREFIX}.JsNative.setProperty(ret, '$j
sName', $name);\n" |
| 503 " }\n"); | 512 " }\n"); |
| 504 i++; | 513 i++; |
| 505 } | 514 } |
| 506 | 515 |
| 507 sbPatch.write( | 516 sbPatch.write( |
| 508 " return new ${_JS_LIBRARY_PREFIX}.JSObject.create(ret);\
n" | 517 " return ret;" |
| 509 " }\n"); | 518 "}\n"); |
| 510 } else if (declaration.isConstructor || | 519 } else if (declaration.isConstructor || |
| 511 declaration.isFactoryConstructor) { | 520 declaration.isFactoryConstructor) { |
| 512 sbPatch.write(" "); | 521 sbPatch.write(" "); |
| 513 addMemberHelper( | 522 addMemberHelper( |
| 514 declaration, | 523 declaration, |
| 515 (jsLibraryName != null && jsLibraryName.isNotEmpty) | 524 (jsLibraryName != null && jsLibraryName.isNotEmpty) |
| 516 ? "${jsLibraryName}.${jsClassName}" | 525 ? "${jsLibraryName}.${jsClassName}" |
| 517 : jsClassName, | 526 : jsClassName, |
| 518 sbPatch, | 527 sbPatch, |
| 519 isStatic: true, | 528 isStatic: true, |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 664 JSArrayImpl.internal() : super.internal(); | 673 JSArrayImpl.internal() : super.internal(); |
| 665 } | 674 } |
| 666 | 675 |
| 667 // Interfaces that are safe to slam on all DOM classes. | 676 // Interfaces that are safe to slam on all DOM classes. |
| 668 // Adding implementsClause would be risky as it could contain Function which | 677 // Adding implementsClause would be risky as it could contain Function which |
| 669 // is likely to break a lot of instanceof checks. | 678 // is likely to break a lot of instanceof checks. |
| 670 abstract class JSObjectInterfacesDom $implementsClauseDom { | 679 abstract class JSObjectInterfacesDom $implementsClauseDom { |
| 671 } | 680 } |
| 672 | 681 |
| 673 patch class JSObject { | 682 patch class JSObject { |
| 674 factory JSObject.create(JsObject jsObject) { | 683 static Type get instanceRuntimeType => JSObjectImpl; |
| 675 var ret = new JSObjectImpl.internal()..blink_jsObject = jsObject; | |
| 676 jsObject._dartHtmlWrapper = ret; | |
| 677 return ret; | |
| 678 } | |
| 679 } | 684 } |
| 680 | 685 |
| 681 patch class JSFunction { | 686 patch class JSFunction { |
| 682 factory JSFunction.create(JsObject jsObject) { | 687 static Type get instanceRuntimeType => JSFunctionImpl; |
| 683 var ret = new JSFunctionImpl.internal()..blink_jsObject = jsObject; | |
| 684 jsObject._dartHtmlWrapper = ret; | |
| 685 return ret; | |
| 686 } | |
| 687 } | 688 } |
| 688 | 689 |
| 689 patch class JSArray { | 690 patch class JSArray { |
| 690 factory JSArray.create(JsObject jsObject) { | 691 static Type get instanceRuntimeType => JSArrayImpl; |
| 691 var ret = new JSArrayImpl.internal()..blink_jsObject = jsObject; | |
| 692 jsObject._dartHtmlWrapper = ret; | |
| 693 return ret; | |
| 694 } | |
| 695 } | 692 } |
| 696 | 693 |
| 697 _registerAllJsInterfaces() { | 694 _registerAllJsInterfaces() { |
| 698 _registerJsInterfaces([${allTypes.join(", ")}]); | 695 _registerJsInterfaces([${allTypes.join(", ")}]); |
| 699 } | 696 } |
| 700 | 697 |
| 701 '''); | 698 '''); |
| 702 ret..addAll(["dart:js", "JSInteropImpl.dart", sb.toString()]); | 699 ret..addAll(["dart:js", "JSInteropImpl.dart", sb.toString()]); |
| 703 return ret; | 700 return ret; |
| 704 } | 701 } |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 866 | 863 |
| 867 bool get _finalized native "Js_interfacesFinalized_Callback"; | 864 bool get _finalized native "Js_interfacesFinalized_Callback"; |
| 868 | 865 |
| 869 JsObject get context { | 866 JsObject get context { |
| 870 if (_cachedContext == null) { | 867 if (_cachedContext == null) { |
| 871 _cachedContext = _context; | 868 _cachedContext = _context; |
| 872 } | 869 } |
| 873 return _cachedContext; | 870 return _cachedContext; |
| 874 } | 871 } |
| 875 | 872 |
| 876 @Deprecated("Internal Use Only") | 873 _lookupType(o, bool isCrossFrame) { |
| 877 maybeWrapTypedInterop(o) => html_common.wrap_jso_no_SerializedScriptvalue(o); | 874 try { |
| 878 | 875 var type = html_common.lookupType(o); |
| 879 _maybeWrap(o) { | 876 var typeMirror = mirrors.reflectType(type); |
| 880 var wrapped = html_common.wrap_jso_no_SerializedScriptvalue(o); | 877 var legacyInteropConvertToNative = typeMirror.isSubtypeOf(mirrors.reflectType
(html.Blob)) || |
| 881 if (identical(wrapped, o)) return o; | 878 typeMirror.isSubtypeOf(mirrors.reflectType(html.Event)) || |
| 882 return (wrapped is html.Blob || | 879 typeMirror.isSubtypeOf(mirrors.reflectType(indexed_db.KeyRange)) || |
| 883 wrapped is html.Event || | 880 typeMirror.isSubtypeOf(mirrors.reflectType(html.ImageData)) || |
| 884 wrapped is indexed_db.KeyRange || | 881 typeMirror.isSubtypeOf(mirrors.reflectType(html.Node)) || |
| 885 wrapped is html.ImageData || | 882 // TypedData is removed from this list as it is converted directly |
| 886 wrapped is html.Node || | 883 // rather than flowing through the interceptor code path. |
| 887 wrapped is TypedData || | 884 // typeMirror.isSubtypeOf(mirrors.reflectType(typed_data.TypedData)) || |
| 888 wrapped is html.Window) ? wrapped : o; | 885 typeMirror.isSubtypeOf(mirrors.reflectType(html.Window)); |
| 886 if (isCrossFrame && !typeMirror.isSubtypeOf(mirrors.reflectType(html.Window)
)) { |
| 887 // TODO(jacobr): evaluate using the true cross frame Window class, etc. |
| 888 // as well as triggering that legacy JS Interop returns raw JsObject |
| 889 // instances. |
| 890 legacyInteropConvertToNative = false; |
| 891 } |
| 892 return [type, legacyInteropConvertToNative]; |
| 893 } catch (e) { } |
| 894 return [JSObject.instanceRuntimeType, false]; |
| 889 } | 895 } |
| 890 | 896 |
| 891 /** | 897 /** |
| 892 * Get the dart wrapper object for object. Top-level so we | 898 * Base class for both the legacy JsObject class and the modern JSObject class. |
| 893 * we can access it from other libraries without it being | 899 * This allows the JsNative utility class tobehave identically whether it is |
| 894 * a public instance field on JsObject. | 900 * called on a JsObject or a JSObject. |
| 895 */ | 901 */ |
| 896 @Deprecated("Internal Use Only") | 902 class _JSObjectBase extends NativeFieldWrapperClass2 { |
| 897 getDartHtmlWrapperFor(JsObject object) => object._dartHtmlWrapper; | 903 String _toString() native "JSObject_toString"; |
| 904 _callMethod(String name, List args) native "JSObject_callMethod"; |
| 905 _operator_getter(String property) native "JSObject_[]"; |
| 906 _operator_setter(String property, value) native "JSObject_[]="; |
| 898 | 907 |
| 899 /** | 908 int get hashCode native "JSObject_hashCode"; |
| 900 * Set the dart wrapper object for object. Top-level so we | |
| 901 * we can access it from other libraries without it being | |
| 902 * a public instance field on JsObject. | |
| 903 */ | |
| 904 @Deprecated("Internal Use Only") | |
| 905 void setDartHtmlWrapperFor(JsObject object, wrapper) { | |
| 906 object._dartHtmlWrapper = wrapper; | |
| 907 } | 909 } |
| 908 | 910 |
| 909 /** | 911 /** |
| 910 * Used by callMethod to get the JS object for each argument passed if the | |
| 911 * argument is a Dart class instance that delegates to a DOM object. See | |
| 912 * wrap_jso defined in dart:html. | |
| 913 */ | |
| 914 @Deprecated("Internal Use Only") | |
| 915 unwrap_jso(dartClass_instance) { | |
| 916 if (dartClass_instance is JSObject && | |
| 917 dartClass_instance is! JsObject) return dartClass_instance.blink_jsObject; | |
| 918 else return dartClass_instance; | |
| 919 } | |
| 920 | |
| 921 /** | |
| 922 * Proxies a JavaScript object to Dart. | 912 * Proxies a JavaScript object to Dart. |
| 923 * | 913 * |
| 924 * The properties of the JavaScript object are accessible via the `[]` and | 914 * The properties of the JavaScript object are accessible via the `[]` and |
| 925 * `[]=` operators. Methods are callable via [callMethod]. | 915 * `[]=` operators. Methods are callable via [callMethod]. |
| 926 */ | 916 */ |
| 927 class JsObject extends NativeFieldWrapperClass2 { | 917 class JsObject extends _JSObjectBase { |
| 928 JsObject.internal(); | 918 JsObject.internal(); |
| 929 | 919 |
| 930 /** | 920 /** |
| 931 * If this JsObject is wrapped, e.g. DOM objects, then we can save the | |
| 932 * wrapper here and preserve its identity. | |
| 933 */ | |
| 934 var _dartHtmlWrapper; | |
| 935 | |
| 936 /** | |
| 937 * Constructs a new JavaScript object from [constructor] and returns a proxy | 921 * Constructs a new JavaScript object from [constructor] and returns a proxy |
| 938 * to it. | 922 * to it. |
| 939 */ | 923 */ |
| 940 factory JsObject(JsFunction constructor, [List arguments]) { | 924 factory JsObject(JsFunction constructor, [List arguments]) { |
| 941 try { | 925 try { |
| 942 return html_common.unwrap_jso(_create(constructor, arguments)); | 926 return _create(constructor, arguments); |
| 943 } catch (e) { | 927 } catch (e) { |
| 944 // Re-throw any errors (returned as a string) as a DomException. | 928 // Re-throw any errors (returned as a string) as a DomException. |
| 945 throw new html.DomException.jsInterop(e); | 929 throw new html.DomException.jsInterop(e); |
| 946 } | 930 } |
| 947 } | 931 } |
| 948 | 932 |
| 949 static JsObject _create(JsFunction constructor, arguments) | 933 static JsObject _create(JsFunction constructor, arguments) |
| 950 native "JsObject_constructorCallback"; | 934 native "JsObject_constructorCallback"; |
| 951 | 935 |
| 952 /** | 936 /** |
| 953 * Constructs a [JsObject] that proxies a native Dart object; _for expert use | 937 * Constructs a [JsObject] that proxies a native Dart object; _for expert use |
| 954 * only_. | 938 * only_. |
| 955 * | 939 * |
| 956 * Use this constructor only if you wish to get access to JavaScript | 940 * Use this constructor only if you wish to get access to JavaScript |
| 957 * properties attached to a browser host object, such as a Node or Blob, that | 941 * properties attached to a browser host object, such as a Node or Blob, that |
| 958 * is normally automatically converted into a native Dart object. | 942 * is normally automatically converted into a native Dart object. |
| 959 * | 943 * |
| 960 * An exception will be thrown if [object] either is `null` or has the type | 944 * An exception will be thrown if [object] either is `null` or has the type |
| 961 * `bool`, `num`, or `String`. | 945 * `bool`, `num`, or `String`. |
| 962 */ | 946 */ |
| 963 factory JsObject.fromBrowserObject(object) { | 947 factory JsObject.fromBrowserObject(object) { |
| 964 if (object is num || object is String || object is bool || object == null) { | 948 if (object is num || object is String || object is bool || object == null) { |
| 965 throw new ArgumentError("object cannot be a num, string, bool, or null"); | 949 throw new ArgumentError("object cannot be a num, string, bool, or null"); |
| 966 } | 950 } |
| 951 if (object is JsObject) return object; |
| 967 return _fromBrowserObject(object); | 952 return _fromBrowserObject(object); |
| 968 } | 953 } |
| 969 | 954 |
| 970 /** | 955 /** |
| 971 * Recursively converts a JSON-like collection of Dart objects to a | 956 * Recursively converts a JSON-like collection of Dart objects to a |
| 972 * collection of JavaScript objects and returns a [JsObject] proxy to it. | 957 * collection of JavaScript objects and returns a [JsObject] proxy to it. |
| 973 * | 958 * |
| 974 * [object] must be a [Map] or [Iterable], the contents of which are also | 959 * [object] must be a [Map] or [Iterable], the contents of which are also |
| 975 * converted. Maps and Iterables are copied to a new JavaScript object. | 960 * converted. Maps and Iterables are copied to a new JavaScript object. |
| 976 * Primitives and other transferrable values are directly converted to their | 961 * Primitives and other transferrable values are directly converted to their |
| 977 * JavaScript type, and all other objects are proxied. | 962 * JavaScript type, and all other objects are proxied. |
| 978 */ | 963 */ |
| 979 factory JsObject.jsify(object) { | 964 factory JsObject.jsify(object) { |
| 980 if ((object is! Map) && (object is! Iterable)) { | 965 if ((object is! Map) && (object is! Iterable)) { |
| 981 throw new ArgumentError("object must be a Map or Iterable"); | 966 throw new ArgumentError("object must be a Map or Iterable"); |
| 982 } | 967 } |
| 983 return _jsify(object); | 968 return _jsify(object); |
| 984 } | 969 } |
| 985 | 970 |
| 986 static JsObject _jsify(object) native "JsObject_jsify"; | 971 static JsObject _jsify(object) native "JsObject_jsify"; |
| 987 | 972 |
| 988 static JsObject _fromBrowserObject(object) => html_common.unwrap_jso(object); | 973 static JsObject _fromBrowserObject(object) native "JsObject_fromBrowserObject"
; |
| 989 | 974 |
| 990 /** | 975 /** |
| 991 * Returns the value associated with [property] from the proxied JavaScript | 976 * Returns the value associated with [property] from the proxied JavaScript |
| 992 * object. | 977 * object. |
| 993 * | 978 * |
| 994 * The type of [property] must be either [String] or [num]. | 979 * The type of [property] must be either [String] or [num]. |
| 995 */ | 980 */ |
| 996 operator [](property) { | 981 operator [](property) { |
| 997 try { | 982 try { |
| 998 return _maybeWrap(_operator_getter(property)); | 983 return _operator_getterLegacy(property); |
| 999 } catch (e) { | 984 } catch (e) { |
| 1000 // Re-throw any errors (returned as a string) as a DomException. | 985 // Re-throw any errors (returned as a string) as a DomException. |
| 1001 throw new html.DomException.jsInterop(e); | 986 throw new html.DomException.jsInterop(e); |
| 1002 } | 987 } |
| 1003 } | 988 } |
| 1004 | 989 |
| 1005 _operator_getter(property) native "JsObject_[]"; | 990 _operator_getterLegacy(property) native "JsObject_[]Legacy"; |
| 1006 | 991 |
| 1007 /** | 992 /** |
| 1008 * Sets the value associated with [property] on the proxied JavaScript | 993 * Sets the value associated with [property] on the proxied JavaScript |
| 1009 * object. | 994 * object. |
| 1010 * | 995 * |
| 1011 * The type of [property] must be either [String] or [num]. | 996 * The type of [property] must be either [String] or [num]. |
| 1012 */ | 997 */ |
| 1013 operator []=(property, value) { | 998 operator []=(property, value) { |
| 1014 try { | 999 try { |
| 1015 _operator_setter(property, value); | 1000 _operator_setterLegacy(property, value); |
| 1016 } catch (e) { | 1001 } catch (e) { |
| 1017 // Re-throw any errors (returned as a string) as a DomException. | 1002 // Re-throw any errors (returned as a string) as a DomException. |
| 1018 throw new html.DomException.jsInterop(e); | 1003 throw new html.DomException.jsInterop(e); |
| 1019 } | 1004 } |
| 1020 } | 1005 } |
| 1021 | 1006 |
| 1022 _operator_setter(property, value) native "JsObject_[]="; | 1007 _operator_setterLegacy(property, value) native "JsObject_[]=Legacy"; |
| 1023 | 1008 |
| 1024 int get hashCode native "JsObject_hashCode"; | 1009 int get hashCode native "JsObject_hashCode"; |
| 1025 | 1010 |
| 1026 operator ==(other) { | 1011 operator ==(other) { |
| 1027 var is_JsObject = other is JsObject; | 1012 if (other is! JsObject && other is! JSObject) return false; |
| 1028 if (!is_JsObject) { | 1013 return _identityEquality(this, other); |
| 1029 other = html_common.unwrap_jso(other); | |
| 1030 is_JsObject = other is JsObject; | |
| 1031 } | |
| 1032 return is_JsObject && _identityEquality(this, other); | |
| 1033 } | 1014 } |
| 1034 | 1015 |
| 1035 static bool _identityEquality(JsObject a, JsObject b) | 1016 static bool _identityEquality(a, b) |
| 1036 native "JsObject_identityEquality"; | 1017 native "JsObject_identityEquality"; |
| 1037 | 1018 |
| 1038 /** | 1019 /** |
| 1039 * Returns `true` if the JavaScript object contains the specified property | 1020 * Returns `true` if the JavaScript object contains the specified property |
| 1040 * either directly or though its prototype chain. | 1021 * either directly or though its prototype chain. |
| 1041 * | 1022 * |
| 1042 * This is the equivalent of the `in` operator in JavaScript. | 1023 * This is the equivalent of the `in` operator in JavaScript. |
| 1043 */ | 1024 */ |
| 1044 bool hasProperty(String property) native "JsObject_hasProperty"; | 1025 bool hasProperty(String property) native "JsObject_hasProperty"; |
| 1045 | 1026 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1071 String _toString() native "JsObject_toString"; | 1052 String _toString() native "JsObject_toString"; |
| 1072 | 1053 |
| 1073 /** | 1054 /** |
| 1074 * Calls [method] on the JavaScript object with the arguments [args] and | 1055 * Calls [method] on the JavaScript object with the arguments [args] and |
| 1075 * returns the result. | 1056 * returns the result. |
| 1076 * | 1057 * |
| 1077 * The type of [method] must be either [String] or [num]. | 1058 * The type of [method] must be either [String] or [num]. |
| 1078 */ | 1059 */ |
| 1079 callMethod(String method, [List args]) { | 1060 callMethod(String method, [List args]) { |
| 1080 try { | 1061 try { |
| 1081 return _maybeWrap(_callMethod(method, args)); | 1062 return _callMethodLegacy(method, args); |
| 1082 } catch (e) { | 1063 } catch (e) { |
| 1083 if (hasProperty(method)) { | 1064 if (hasProperty(method)) { |
| 1084 // Return a DomException if DOM call returned an error. | 1065 // Return a DomException if DOM call returned an error. |
| 1085 throw new html.DomException.jsInterop(e); | 1066 throw new html.DomException.jsInterop(e); |
| 1086 } else { | 1067 } else { |
| 1087 throw new NoSuchMethodError(this, new Symbol(method), args, null); | 1068 throw new NoSuchMethodError(this, new Symbol(method), args, null); |
| 1088 } | 1069 } |
| 1089 } | 1070 } |
| 1090 } | 1071 } |
| 1091 | 1072 |
| 1092 _callMethod(String name, List args) native "JsObject_callMethod"; | 1073 _callMethodLegacy(String name, List args) native "JsObject_callMethodLegacy"; |
| 1093 } | 1074 } |
| 1094 | 1075 |
| 1076 |
| 1095 /// Base class for all JS objects used through dart:html and typed JS interop. | 1077 /// Base class for all JS objects used through dart:html and typed JS interop. |
| 1096 @Deprecated("Internal Use Only") | 1078 @Deprecated("Internal Use Only") |
| 1097 class JSObject { | 1079 class JSObject extends _JSObjectBase { |
| 1098 JSObject.internal() {} | 1080 JSObject.internal() {} |
| 1099 external factory JSObject.create(JsObject jsObject); | 1081 external static Type get instanceRuntimeType; |
| 1100 | 1082 |
| 1101 @Deprecated("Internal Use Only") | 1083 /** |
| 1102 JsObject blink_jsObject; | 1084 * Returns the result of the JavaScript objects `toString` method. |
| 1103 | 1085 */ |
| 1104 String toString() => blink_jsObject.toString(); | 1086 String toString() { |
| 1087 try { |
| 1088 return _toString(); |
| 1089 } catch (e) { |
| 1090 return super.toString(); |
| 1091 } |
| 1092 } |
| 1105 | 1093 |
| 1106 noSuchMethod(Invocation invocation) { | 1094 noSuchMethod(Invocation invocation) { |
| 1107 throwError() { | 1095 throwError() { |
| 1108 super.noSuchMethod(invocation); | 1096 super.noSuchMethod(invocation); |
| 1109 } | 1097 } |
| 1110 | 1098 |
| 1111 String name = _stripReservedNamePrefix( | 1099 String name = _stripReservedNamePrefix( |
| 1112 mirrors.MirrorSystem.getName(invocation.memberName)); | 1100 mirrors.MirrorSystem.getName(invocation.memberName)); |
| 1113 argsSafeForTypedInterop(invocation.positionalArguments); | 1101 argsSafeForTypedInterop(invocation.positionalArguments); |
| 1114 if (invocation.isGetter) { | 1102 if (invocation.isGetter) { |
| 1115 if (CHECK_JS_INVOCATIONS) { | 1103 if (CHECK_JS_INVOCATIONS) { |
| 1116 var matches = _allowedGetters[invocation.memberName]; | 1104 var matches = _allowedGetters[invocation.memberName]; |
| 1117 if (matches == null && | 1105 if (matches == null && |
| 1118 !_allowedMethods.containsKey(invocation.memberName)) { | 1106 !_allowedMethods.containsKey(invocation.memberName)) { |
| 1119 throwError(); | 1107 throwError(); |
| 1120 } | 1108 } |
| 1121 var ret = maybeWrapTypedInterop(blink_jsObject._operator_getter(name)); | 1109 var ret = _operator_getter(name); |
| 1122 if (matches != null) return ret; | 1110 if (matches != null) return ret; |
| 1123 if (ret is Function || | 1111 if (ret is Function || |
| 1124 (ret is JsFunction /* shouldn't be needed in the future*/) && | 1112 (ret is JsFunction /* shouldn't be needed in the future*/) && |
| 1125 _allowedMethods.containsKey( | 1113 _allowedMethods.containsKey( |
| 1126 invocation.memberName)) return ret; // Warning: we have not
bound "this"... we could type check on the Function but that is of little value
in Dart. | 1114 invocation.memberName)) return ret; // Warning: we have not
bound "this"... we could type check on the Function but that is of little value
in Dart. |
| 1127 throwError(); | 1115 throwError(); |
| 1128 } else { | 1116 } else { |
| 1129 // TODO(jacobr): should we throw if the JavaScript object doesn't have t
he property? | 1117 // TODO(jacobr): should we throw if the JavaScript object doesn't have t
he property? |
| 1130 return maybeWrapTypedInterop(blink_jsObject._operator_getter(name)); | 1118 return _operator_getter(name); |
| 1131 } | 1119 } |
| 1132 } else if (invocation.isSetter) { | 1120 } else if (invocation.isSetter) { |
| 1133 if (CHECK_JS_INVOCATIONS) { | 1121 if (CHECK_JS_INVOCATIONS) { |
| 1134 var matches = _allowedSetters[invocation.memberName]; | 1122 var matches = _allowedSetters[invocation.memberName]; |
| 1135 if (matches == null || | 1123 if (matches == null || |
| 1136 !matches.checkInvocation(invocation)) throwError(); | 1124 !matches.checkInvocation(invocation)) throwError(); |
| 1137 } | 1125 } |
| 1138 assert(name.endsWith("=")); | 1126 assert(name.endsWith("=")); |
| 1139 name = name.substring(0, name.length - 1); | 1127 name = name.substring(0, name.length - 1); |
| 1140 return maybeWrapTypedInterop(blink_jsObject._operator_setter( | 1128 return _operator_setter(name, invocation.positionalArguments.first); |
| 1141 name, invocation.positionalArguments.first)); | |
| 1142 } else { | 1129 } else { |
| 1143 // TODO(jacobr): also allow calling getters that look like functions. | 1130 // TODO(jacobr): also allow calling getters that look like functions. |
| 1144 var matches; | 1131 var matches; |
| 1145 if (CHECK_JS_INVOCATIONS) { | 1132 if (CHECK_JS_INVOCATIONS) { |
| 1146 matches = _allowedMethods[invocation.memberName]; | 1133 matches = _allowedMethods[invocation.memberName]; |
| 1147 if (matches == null || | 1134 if (matches == null || |
| 1148 !matches.checkInvocation(invocation)) throwError(); | 1135 !matches.checkInvocation(invocation)) throwError(); |
| 1149 } | 1136 } |
| 1150 var ret = maybeWrapTypedInterop( | 1137 var ret = _callMethod(name, _buildArgs(invocation)); |
| 1151 blink_jsObject._callMethod(name, _buildArgs(invocation))); | |
| 1152 if (CHECK_JS_INVOCATIONS) { | 1138 if (CHECK_JS_INVOCATIONS) { |
| 1153 if (!matches._checkReturnType(ret)) { | 1139 if (!matches._checkReturnType(ret)) { |
| 1154 html.window.console.error("Return value for method: ${name} is " | 1140 html.window.console.error("Return value for method: ${name} is " |
| 1155 "${ret.runtimeType} which is inconsistent with all typed " | 1141 "${ret.runtimeType} which is inconsistent with all typed " |
| 1156 "JS interop definitions for method ${name}."); | 1142 "JS interop definitions for method ${name}."); |
| 1157 } | 1143 } |
| 1158 } | 1144 } |
| 1159 return ret; | 1145 return ret; |
| 1160 } | 1146 } |
| 1161 } | 1147 } |
| 1162 } | 1148 } |
| 1163 | 1149 |
| 1164 @Deprecated("Internal Use Only") | 1150 @Deprecated("Internal Use Only") |
| 1165 class JSArray extends JSObject with ListMixin { | 1151 class JSArray extends JSObject with ListMixin { |
| 1166 JSArray.internal() : super.internal(); | 1152 JSArray.internal() : super.internal(); |
| 1167 external factory JSArray.create(JsObject jsObject); | 1153 external static Type get instanceRuntimeType; |
| 1168 operator [](int index) => | |
| 1169 maybeWrapTypedInterop(JsNative.getArrayIndex(blink_jsObject, index)); | |
| 1170 | 1154 |
| 1171 operator []=(int index, value) => blink_jsObject[index] = value; | 1155 // Reuse JsArray_length as length behavior is unchanged. |
| 1156 int get length native "JsArray_length"; |
| 1172 | 1157 |
| 1173 int get length => blink_jsObject.length; | 1158 set length(int length) { |
| 1174 set length(int newLength) => blink_jsObject.length = newLength; | 1159 _operator_setter('length', length); |
| 1160 } |
| 1161 |
| 1162 _checkIndex(int index, {bool insert: false}) { |
| 1163 int length = insert ? this.length + 1 : this.length; |
| 1164 if (index is int && (index < 0 || index >= length)) { |
| 1165 throw new RangeError.range(index, 0, length); |
| 1166 } |
| 1167 } |
| 1168 |
| 1169 _checkRange(int start, int end) { |
| 1170 int cachedLength = this.length; |
| 1171 if (start < 0 || start > cachedLength) { |
| 1172 throw new RangeError.range(start, 0, cachedLength); |
| 1173 } |
| 1174 if (end < start || end > cachedLength) { |
| 1175 throw new RangeError.range(end, start, cachedLength); |
| 1176 } |
| 1177 } |
| 1178 |
| 1179 _indexed_getter(int index) native "JSArray_indexed_getter"; |
| 1180 _indexed_setter(int index, o) native "JSArray_indexed_setter"; |
| 1181 |
| 1182 // Methods required by ListMixin |
| 1183 |
| 1184 operator [](index) { |
| 1185 if (index is int) { |
| 1186 _checkIndex(index); |
| 1187 } |
| 1188 |
| 1189 return _indexed_getter(index); |
| 1190 } |
| 1191 |
| 1192 void operator []=(int index, E value) { |
| 1193 _checkIndex(index); |
| 1194 _indexed_setter(index, value); |
| 1195 } |
| 1175 } | 1196 } |
| 1176 | 1197 |
| 1177 @Deprecated("Internal Use Only") | 1198 @Deprecated("Internal Use Only") |
| 1178 class JSFunction extends JSObject implements Function { | 1199 class JSFunction extends JSObject implements Function { |
| 1179 JSFunction.internal() : super.internal(); | 1200 JSFunction.internal() : super.internal(); |
| 1180 | 1201 |
| 1181 external factory JSFunction.create(JsObject jsObject); | 1202 external static Type get instanceRuntimeType; |
| 1182 | 1203 |
| 1183 call( | 1204 call( |
| 1184 [a1 = _UNDEFINED, | 1205 [a1 = _UNDEFINED, |
| 1185 a2 = _UNDEFINED, | 1206 a2 = _UNDEFINED, |
| 1186 a3 = _UNDEFINED, | 1207 a3 = _UNDEFINED, |
| 1187 a4 = _UNDEFINED, | 1208 a4 = _UNDEFINED, |
| 1188 a5 = _UNDEFINED, | 1209 a5 = _UNDEFINED, |
| 1189 a6 = _UNDEFINED, | 1210 a6 = _UNDEFINED, |
| 1190 a7 = _UNDEFINED, | 1211 a7 = _UNDEFINED, |
| 1191 a8 = _UNDEFINED, | 1212 a8 = _UNDEFINED, |
| 1192 a9 = _UNDEFINED, | 1213 a9 = _UNDEFINED, |
| 1193 a10 = _UNDEFINED]) { | 1214 a10 = _UNDEFINED]) { |
| 1194 return maybeWrapTypedInterop(blink_jsObject | 1215 return _apply(_stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])
); |
| 1195 .apply(_stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]))); | |
| 1196 } | 1216 } |
| 1197 | 1217 |
| 1198 noSuchMethod(Invocation invocation) { | 1218 noSuchMethod(Invocation invocation) { |
| 1199 if (invocation.isMethod && invocation.memberName == #call) { | 1219 if (invocation.isMethod && invocation.memberName == #call) { |
| 1200 return maybeWrapTypedInterop( | 1220 return _apply(_buildArgs(invocation)); |
| 1201 blink_jsObject.apply(_buildArgs(invocation))); | |
| 1202 } | 1221 } |
| 1203 return super.noSuchMethod(invocation); | 1222 return super.noSuchMethod(invocation); |
| 1204 } | 1223 } |
| 1224 |
| 1225 dynamic _apply(List args, {thisArg}) native "JSFunction_apply"; |
| 1226 |
| 1227 static JSFunction _createWithThis(Function f) native "JSFunction_createWithThi
s"; |
| 1228 static JSFunction _create(Function f) native "JSFunction_create"; |
| 1205 } | 1229 } |
| 1206 | 1230 |
| 1207 // JavaScript interop methods that do not automatically wrap to dart:html types. | 1231 // JavaScript interop methods that do not automatically wrap to dart:html types. |
| 1208 // Warning: this API is not exposed to dart:js. | 1232 // Warning: this API is not exposed to dart:js. |
| 1233 // TODO(jacobr): rename to JSNative and make at least part of this API public. |
| 1209 @Deprecated("Internal Use Only") | 1234 @Deprecated("Internal Use Only") |
| 1210 class JsNative { | 1235 class JsNative { |
| 1211 static getProperty(o, name) { | 1236 static JSObject jsify(object) native "JSObject_jsify"; |
| 1212 o = unwrap_jso(o); | 1237 static JSObject newObject() native "JSObject_newObject"; |
| 1213 return o != null ? o._operator_getter(name) : null; | |
| 1214 } | |
| 1215 | 1238 |
| 1216 static setProperty(o, name, value) { | 1239 static getProperty(_JSObjectBase o, name) => o._operator_getter(name); |
| 1217 return unwrap_jso(o)._operator_setter(name, value); | 1240 static setProperty(_JSObjectBase o, name, value) => o._operator_setter(name, v
alue); |
| 1218 } | 1241 static callMethod(_JSObjectBase o, String method, List args) => o._callMethod(
method, args); |
| 1219 | 1242 |
| 1220 static callMethod(o, String method, List args) { | 1243 static callConstructor0(_JSObjectBase constructor) native "JSNative_callConstr
uctor0"; |
| 1221 return unwrap_jso(o)._callMethod(method, args); | 1244 static callConstructor(_JSObjectBase constructor, List args) native "JSNative_
callConstructor"; |
| 1222 } | |
| 1223 | 1245 |
| 1224 static getArrayIndex(JsArray array, int index) { | 1246 static toTypedObject(JsObject o) native "JSNative_toTypedObject"; |
| 1225 array._checkIndex(index); | 1247 |
| 1226 return getProperty(array, index); | 1248 /// Internal custom element implementation method. |
| 1227 } | 1249 @Deprecated("Internal Use Only") |
| 1250 static $setInstanceInterceptor(_JSObjectBase o, Type type) native "JSNative_se
tInstanceInterceptor"; |
| 1228 | 1251 |
| 1229 /** | 1252 /** |
| 1230 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n
ot | 1253 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n
ot |
| 1231 * wrapped. | 1254 * wrapped. |
| 1232 */ | 1255 */ |
| 1233 static JsFunction withThis(Function f) native "JsFunction_withThisNoWrap"; | 1256 static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap"; |
| 1234 } | 1257 } |
| 1235 | 1258 |
| 1259 |
| 1236 /** | 1260 /** |
| 1237 * Proxies a JavaScript Function object. | 1261 * Proxies a JavaScript Function object. |
| 1238 */ | 1262 */ |
| 1239 class JsFunction extends JsObject { | 1263 class JsFunction extends JsObject { |
| 1240 JsFunction.internal() : super.internal(); | 1264 JsFunction.internal() : super.internal(); |
| 1241 | 1265 |
| 1242 /** | 1266 /** |
| 1243 * Returns a [JsFunction] that captures its 'this' binding and calls [f] | 1267 * Returns a [JsFunction] that captures its 'this' binding and calls [f] |
| 1244 * with the value of this passed as the first argument. | 1268 * with the value of this passed as the first argument. |
| 1245 */ | 1269 */ |
| 1246 factory JsFunction.withThis(Function f) => _withThis(f); | 1270 factory JsFunction.withThis(Function f) => _withThis(f); |
| 1247 | 1271 |
| 1248 /** | 1272 /** |
| 1249 * Invokes the JavaScript function with arguments [args]. If [thisArg] is | 1273 * Invokes the JavaScript function with arguments [args]. If [thisArg] is |
| 1250 * supplied it is the value of `this` for the invocation. | 1274 * supplied it is the value of `this` for the invocation. |
| 1251 */ | 1275 */ |
| 1252 dynamic apply(List args, {thisArg}) => | 1276 dynamic apply(List args, {thisArg}) => |
| 1253 _maybeWrap(_apply(args, thisArg: thisArg)); | 1277 _apply(args, thisArg: thisArg); |
| 1254 | 1278 |
| 1255 dynamic _apply(List args, {thisArg}) native "JsFunction_apply"; | 1279 dynamic _apply(List args, {thisArg}) native "JsFunction_apply"; |
| 1256 | 1280 |
| 1257 /** | 1281 /** |
| 1258 * Internal only version of apply which uses debugger proxies of Dart objects | 1282 * Internal only version of apply which uses debugger proxies of Dart objects |
| 1259 * rather than opaque handles. This method is private because it cannot be | 1283 * rather than opaque handles. This method is private because it cannot be |
| 1260 * efficiently implemented in Dart2Js so should only be used by internal | 1284 * efficiently implemented in Dart2Js so should only be used by internal |
| 1261 * tools. | 1285 * tools. |
| 1262 */ | 1286 */ |
| 1263 _applyDebuggerOnly(List args, {thisArg}) | 1287 _applyDebuggerOnly(List args, {thisArg}) |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1395 * to JavaScript. To make a function safe, call [allowInterop] or | 1419 * to JavaScript. To make a function safe, call [allowInterop] or |
| 1396 * [allowInteropCaptureThis]. | 1420 * [allowInteropCaptureThis]. |
| 1397 */ | 1421 */ |
| 1398 @Deprecated("Internal Use Only") | 1422 @Deprecated("Internal Use Only") |
| 1399 void argsSafeForTypedInterop(Iterable args) { | 1423 void argsSafeForTypedInterop(Iterable args) { |
| 1400 for (var arg in args) { | 1424 for (var arg in args) { |
| 1401 safeForTypedInterop(arg); | 1425 safeForTypedInterop(arg); |
| 1402 } | 1426 } |
| 1403 } | 1427 } |
| 1404 | 1428 |
| 1405 List _stripAndWrapArgs(Iterable args) { | |
| 1406 var ret = []; | |
| 1407 for (var arg in args) { | |
| 1408 if (arg == _UNDEFINED) break; | |
| 1409 ret.add(maybeWrapTypedInterop(arg)); | |
| 1410 } | |
| 1411 return ret; | |
| 1412 } | |
| 1413 | |
| 1414 /** | 1429 /** |
| 1415 * Returns a method that can be called with an arbitrary number (for n less | 1430 * Returns a method that can be called with an arbitrary number (for n less |
| 1416 * than 11) of arguments without violating Dart type checks. | 1431 * than 11) of arguments without violating Dart type checks. |
| 1417 */ | 1432 */ |
| 1418 Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => ( | 1433 Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => ( |
| 1419 [a1 = _UNDEFINED, | 1434 [a1 = _UNDEFINED, |
| 1420 a2 = _UNDEFINED, | 1435 a2 = _UNDEFINED, |
| 1421 a3 = _UNDEFINED, | 1436 a3 = _UNDEFINED, |
| 1422 a4 = _UNDEFINED, | 1437 a4 = _UNDEFINED, |
| 1423 a5 = _UNDEFINED, | 1438 a5 = _UNDEFINED, |
| 1424 a6 = _UNDEFINED, | 1439 a6 = _UNDEFINED, |
| 1425 a7 = _UNDEFINED, | 1440 a7 = _UNDEFINED, |
| 1426 a8 = _UNDEFINED, | 1441 a8 = _UNDEFINED, |
| 1427 a9 = _UNDEFINED, | 1442 a9 = _UNDEFINED, |
| 1428 a10 = _UNDEFINED]) => | 1443 a10 = _UNDEFINED]) => |
| 1429 jsFunction._applyDebuggerOnly( | 1444 jsFunction._applyDebuggerOnly( |
| 1430 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); | 1445 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); |
| 1431 | 1446 |
| 1432 /// This helper is purely a hack so we can reuse JsFunction.withThis even when | |
| 1433 /// we don't care about passing JS "this". In an ideal world we would implement | |
| 1434 /// helpers in C++ that directly implement allowInterop and | |
| 1435 /// allowInteropCaptureThis. | |
| 1436 class _CreateDartFunctionForInteropIgnoreThis implements Function { | |
| 1437 Function _fn; | |
| 1438 | |
| 1439 _CreateDartFunctionForInteropIgnoreThis(this._fn); | |
| 1440 | |
| 1441 call( | |
| 1442 [ignoredThis = _UNDEFINED, | |
| 1443 a1 = _UNDEFINED, | |
| 1444 a2 = _UNDEFINED, | |
| 1445 a3 = _UNDEFINED, | |
| 1446 a4 = _UNDEFINED, | |
| 1447 a5 = _UNDEFINED, | |
| 1448 a6 = _UNDEFINED, | |
| 1449 a7 = _UNDEFINED, | |
| 1450 a8 = _UNDEFINED, | |
| 1451 a9 = _UNDEFINED, | |
| 1452 a10 = _UNDEFINED]) { | |
| 1453 var ret = Function.apply( | |
| 1454 _fn, _stripAndWrapArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); | |
| 1455 safeForTypedInterop(ret); | |
| 1456 return ret; | |
| 1457 } | |
| 1458 | |
| 1459 noSuchMethod(Invocation invocation) { | |
| 1460 if (invocation.isMethod && invocation.memberName == #call) { | |
| 1461 // Named arguments not yet supported. | |
| 1462 if (invocation.namedArguments.isNotEmpty) return; | |
| 1463 var ret = Function.apply( | |
| 1464 _fn, _stripAndWrapArgs(invocation.positionalArguments.skip(1))); | |
| 1465 // TODO(jacobr): it would be nice to check that the return value is safe | |
| 1466 // for interop but we don't want to break existing addEventListener users. | |
| 1467 // safeForTypedInterop(ret); | |
| 1468 safeForTypedInterop(ret); | |
| 1469 return ret; | |
| 1470 } | |
| 1471 return super.noSuchMethod(invocation); | |
| 1472 } | |
| 1473 } | |
| 1474 | |
| 1475 /// See comment for [_CreateDartFunctionForInteropIgnoreThis]. | |
| 1476 /// This Function exists purely because JsObject doesn't have the DOM type | |
| 1477 /// conversion semantics we want for JS typed interop. | |
| 1478 class _CreateDartFunctionForInterop implements Function { | |
| 1479 Function _fn; | |
| 1480 | |
| 1481 _CreateDartFunctionForInterop(this._fn); | |
| 1482 | |
| 1483 call( | |
| 1484 [a1 = _UNDEFINED, | |
| 1485 a2 = _UNDEFINED, | |
| 1486 a3 = _UNDEFINED, | |
| 1487 a4 = _UNDEFINED, | |
| 1488 a5 = _UNDEFINED, | |
| 1489 a6 = _UNDEFINED, | |
| 1490 a7 = _UNDEFINED, | |
| 1491 a8 = _UNDEFINED, | |
| 1492 a9 = _UNDEFINED, | |
| 1493 a10 = _UNDEFINED]) { | |
| 1494 var ret = Function.apply( | |
| 1495 _fn, _stripAndWrapArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); | |
| 1496 safeForTypedInterop(ret); | |
| 1497 return ret; | |
| 1498 } | |
| 1499 | |
| 1500 noSuchMethod(Invocation invocation) { | |
| 1501 if (invocation.isMethod && invocation.memberName == #call) { | |
| 1502 // Named arguments not yet supported. | |
| 1503 if (invocation.namedArguments.isNotEmpty) return; | |
| 1504 var ret = Function.apply( | |
| 1505 _fn, _stripAndWrapArgs(invocation.positionalArguments)); | |
| 1506 safeForTypedInterop(ret); | |
| 1507 return ret; | |
| 1508 } | |
| 1509 return super.noSuchMethod(invocation); | |
| 1510 } | |
| 1511 } | |
| 1512 | |
| 1513 /// Cached JSFunction associated with the Dart Function. | |
| 1514 Expando<JSFunction> _interopExpando = new Expando<JSFunction>(); | |
| 1515 | |
| 1516 /// Returns a wrapper around function [f] that can be called from JavaScript | 1447 /// Returns a wrapper around function [f] that can be called from JavaScript |
| 1517 /// using the package:js Dart-JavaScript interop. | 1448 /// using the package:js Dart-JavaScript interop. |
| 1518 /// | 1449 /// |
| 1519 /// For performance reasons in Dart2Js, by default Dart functions cannot be | 1450 /// For performance reasons in Dart2Js, by default Dart functions cannot be |
| 1520 /// passed directly to JavaScript unless this method is called to create | 1451 /// passed directly to JavaScript unless this method is called to create |
| 1521 /// a Function compatible with both Dart and JavaScript. | 1452 /// a Function compatible with both Dart and JavaScript. |
| 1522 /// Calling this method repeatedly on a function will return the same function. | 1453 /// Calling this method repeatedly on a function will return the same function. |
| 1523 /// The [Function] returned by this method can be used from both Dart and | 1454 /// The [Function] returned by this method can be used from both Dart and |
| 1524 /// JavaScript. We may remove the need to call this method completely in the | 1455 /// JavaScript. We may remove the need to call this method completely in the |
| 1525 /// future if Dart2Js is refactored so that its function calling conventions | 1456 /// future if Dart2Js is refactored so that its function calling conventions |
| 1526 /// are more compatible with JavaScript. | 1457 /// are more compatible with JavaScript. |
| 1527 JSFunction allowInterop(Function f) { | 1458 JSFunction allowInterop(Function f) { |
| 1528 if (f is JSFunction) { | 1459 if (f is JSFunction) { |
| 1529 // The function is already a JSFunction... no need to do anything. | 1460 // The function is already a JSFunction... no need to do anything. |
| 1530 return f; | 1461 return f; |
| 1531 } else { | 1462 } else { |
| 1532 var ret = _interopExpando[f]; | 1463 return JSFunction._create(f); |
| 1533 if (ret == null) { | |
| 1534 // TODO(jacobr): we could optimize this. | |
| 1535 ret = new JSFunction.create(new JsFunction.withThis( | |
| 1536 new _CreateDartFunctionForInteropIgnoreThis(f))); | |
| 1537 _interopExpando[f] = ret; | |
| 1538 } | |
| 1539 return ret; | |
| 1540 } | 1464 } |
| 1541 } | 1465 } |
| 1542 | 1466 |
| 1543 /// Cached JSFunction associated with the Dart function when "this" is | 1467 /// Cached JSFunction associated with the Dart function when "this" is |
| 1544 /// captured. | 1468 /// captured. |
| 1545 Expando<JSFunction> _interopCaptureThisExpando = new Expando<JSFunction>(); | 1469 Expando<JSFunction> _interopCaptureThisExpando = new Expando<JSFunction>(); |
| 1546 | 1470 |
| 1547 /// Returns a [Function] that when called from JavaScript captures its 'this' | 1471 /// Returns a [Function] that when called from JavaScript captures its 'this' |
| 1548 /// binding and calls [f] with the value of this passed as the first argument. | 1472 /// binding and calls [f] with the value of this passed as the first argument. |
| 1549 /// When called from Dart, [null] will be passed as the first argument. | 1473 /// When called from Dart, [null] will be passed as the first argument. |
| 1550 /// | 1474 /// |
| 1551 /// See the documention for [allowInterop]. This method should only be used with | 1475 /// See the documention for [allowInterop]. This method should only be used with |
| 1552 /// package:js Dart-JavaScript interop. | 1476 /// package:js Dart-JavaScript interop. |
| 1553 JSFunction allowInteropCaptureThis(Function f) { | 1477 JSFunction allowInteropCaptureThis(Function f) { |
| 1554 if (f is JSFunction) { | 1478 if (f is JSFunction) { |
| 1555 // Behavior when the function is already a JS function is unspecified. | 1479 // Behavior when the function is already a JS function is unspecified. |
| 1556 throw new ArgumentError( | 1480 throw new ArgumentError( |
| 1557 "Function is already a JS function so cannot capture this."); | 1481 "Function is already a JS function so cannot capture this."); |
| 1558 return f; | 1482 return f; |
| 1559 } else { | 1483 } else { |
| 1560 var ret = _interopCaptureThisExpando[f]; | 1484 var ret = _interopCaptureThisExpando[f]; |
| 1561 if (ret == null) { | 1485 if (ret == null) { |
| 1562 // TODO(jacobr): we could optimize this. | 1486 // TODO(jacobr): we could optimize this. |
| 1563 ret = new JSFunction.create( | 1487 ret = JSFunction._createWithThis(f); |
| 1564 new JsFunction.withThis(new _CreateDartFunctionForInterop(f))); | |
| 1565 _interopCaptureThisExpando[f] = ret; | 1488 _interopCaptureThisExpando[f] = ret; |
| 1566 } | 1489 } |
| 1567 return ret; | 1490 return ret; |
| 1568 } | 1491 } |
| 1569 } | 1492 } |
| 1493 |
| 1494 debugPrint(_) {} |
| OLD | NEW |