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 |