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

Side by Side Diff: sdk/lib/js/dartium/js_dartium.dart

Issue 1832713002: Optimize dartium dart:html bindings so real world application performance is acceptable. Improves d… (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * Support for interoperating with JavaScript. 6 * Support for interoperating with JavaScript.
7 * 7 *
8 * This library provides access to JavaScript objects from Dart, allowing 8 * This library provides access to JavaScript objects from Dart, allowing
9 * Dart code to get and set properties, and call methods of JavaScript objects 9 * Dart code to get and set properties, and call methods of JavaScript objects
10 * and invoke JavaScript functions. The library takes care of converting 10 * and invoke JavaScript functions. The library takes care of converting
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 * 85 *
86 * var jsArray = new JsObject.jsify([1, 2, 3]); 86 * var jsArray = new JsObject.jsify([1, 2, 3]);
87 */ 87 */
88 library dart.js; 88 library dart.js;
89 89
90 import 'dart: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
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
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
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
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
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
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
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(_) {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698