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 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
99 import 'dart:core'; | 99 import 'dart:core'; |
100 | 100 |
101 import 'cached_patches.dart'; | 101 import 'cached_patches.dart'; |
102 | 102 |
103 // Pretend we are always in checked mode as we aren't interested in users | 103 // Pretend we are always in checked mode as we aren't interested in users |
104 // running Dartium code outside of checked mode. | 104 // running Dartium code outside of checked mode. |
105 @Deprecated("Internal Use Only") | 105 @Deprecated("Internal Use Only") |
106 final bool CHECK_JS_INVOCATIONS = true; | 106 final bool CHECK_JS_INVOCATIONS = true; |
107 | 107 |
108 final String _DART_RESERVED_NAME_PREFIX = r'JS$'; | 108 final String _DART_RESERVED_NAME_PREFIX = r'JS$'; |
109 // If a private class is defined to use @JS we need to inject a non-private | |
110 // class with a name that will not cause collisions in the library so we can | |
111 // make JSObject implement that interface even though it is in a different | |
112 // library. | |
113 final String escapePrivateClassPrefix = r'$JSImplClass23402893498'; | |
Alan Knight
2016/07/19 16:56:16
Is this a problem if there are two different priva
Jacob
2016/07/19 17:36:40
No because all libraries are imported into the dar
| |
109 | 114 |
110 String _stripReservedNamePrefix(String name) => | 115 String _stripReservedNamePrefix(String name) => |
111 name.startsWith(_DART_RESERVED_NAME_PREFIX) | 116 name.startsWith(_DART_RESERVED_NAME_PREFIX) |
112 ? name.substring(_DART_RESERVED_NAME_PREFIX.length) | 117 ? name.substring(_DART_RESERVED_NAME_PREFIX.length) |
113 : name; | 118 : name; |
114 | 119 |
115 _buildArgs(Invocation invocation) { | 120 _buildArgs(Invocation invocation) { |
116 if (invocation.namedArguments.isEmpty) { | 121 if (invocation.namedArguments.isEmpty) { |
117 return invocation.positionalArguments; | 122 return invocation.positionalArguments; |
118 } else { | 123 } else { |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 // is file://dart/sdk/lib/html/html_common/metadata.dart | 338 // is file://dart/sdk/lib/html/html_common/metadata.dart |
334 // instead of a proper dart: location. | 339 // instead of a proper dart: location. |
335 return true; | 340 return true; |
336 } | 341 } |
337 } | 342 } |
338 return false; | 343 return false; |
339 } | 344 } |
340 | 345 |
341 _getJsMemberName(mirrors.DeclarationMirror mirror) { | 346 _getJsMemberName(mirrors.DeclarationMirror mirror) { |
342 var name = _getJsName(mirror); | 347 var name = _getJsName(mirror); |
343 return name == null || name.isEmpty ? _getDeclarationName(mirror) : name; | 348 return name == null || name.isEmpty ? _stripReservedNamePrefix(_getDeclaration Name(mirror)) : name; |
344 } | 349 } |
345 | 350 |
346 // TODO(jacobr): handle setters correctyl. | 351 // TODO(jacobr): handle setters correctyl. |
347 String _getDeclarationName(mirrors.DeclarationMirror declaration) { | 352 String _getDeclarationName(mirrors.DeclarationMirror declaration) { |
348 var name = mirrors.MirrorSystem.getName(declaration.simpleName); | 353 var name = mirrors.MirrorSystem.getName(declaration.simpleName); |
349 if (declaration is mirrors.MethodMirror && declaration.isSetter) { | 354 if (declaration is mirrors.MethodMirror && declaration.isSetter) { |
350 assert(name.endsWith("=")); | 355 assert(name.endsWith("=")); |
351 name = name.substring(0, name.length - 1); | 356 name = name.substring(0, name.length - 1); |
352 } | 357 } |
353 return _stripReservedNamePrefix(name); | 358 return name; |
354 } | 359 } |
355 | 360 |
356 final _JS_LIBRARY_PREFIX = "js_library"; | 361 final _JS_LIBRARY_PREFIX = "js_library"; |
357 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; | 362 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; |
358 | 363 |
359 String _accessJsPath(String path) => _accessJsPathHelper(path.split(".")); | 364 String _accessJsPath(String path) => _accessJsPathHelper(path.split(".")); |
360 | 365 |
361 String _accessJsPathHelper(Iterable<String> parts) { | 366 String _accessJsPathHelper(Iterable<String> parts) { |
362 var sb = new StringBuffer(); | 367 var sb = new StringBuffer(); |
363 sb | 368 sb |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
517 _isExternal(declaration)) { | 522 _isExternal(declaration)) { |
518 addMemberHelper(declaration, jsLibraryName, sb); | 523 addMemberHelper(declaration, jsLibraryName, sb); |
519 } | 524 } |
520 } else if (declaration is mirrors.ClassMirror) { | 525 } else if (declaration is mirrors.ClassMirror) { |
521 mirrors.ClassMirror clazz = declaration; | 526 mirrors.ClassMirror clazz = declaration; |
522 var isDom = dartLibrary ? hasDomName(clazz) : false; | 527 var isDom = dartLibrary ? hasDomName(clazz) : false; |
523 var isJsInterop = _hasJsName(clazz); | 528 var isJsInterop = _hasJsName(clazz); |
524 if (isDom || isJsInterop) { | 529 if (isDom || isJsInterop) { |
525 // TODO(jacobr): verify class implements JavaScriptObject. | 530 // TODO(jacobr): verify class implements JavaScriptObject. |
526 var className = mirrors.MirrorSystem.getName(clazz.simpleName); | 531 var className = mirrors.MirrorSystem.getName(clazz.simpleName); |
532 bool isPrivate = className.startsWith('_'); | |
527 var classNameImpl = '${className}Impl'; | 533 var classNameImpl = '${className}Impl'; |
528 var sbPatch = new StringBuffer(); | 534 var sbPatch = new StringBuffer(); |
529 if (isJsInterop) { | 535 if (isJsInterop) { |
530 String jsClassName = _getJsMemberName(clazz); | 536 String jsClassName = _getJsMemberName(clazz); |
531 | 537 |
532 jsInterfaceTypes.add(clazz); | 538 jsInterfaceTypes.add(clazz); |
533 clazz.declarations.forEach((name, declaration) { | 539 clazz.declarations.forEach((name, declaration) { |
534 if (declaration is! mirrors.MethodMirror || | 540 if (declaration is! mirrors.MethodMirror || |
535 !_isExternal(declaration)) return; | 541 !_isExternal(declaration)) return; |
536 if (declaration.isFactoryConstructor && | 542 if (declaration.isFactoryConstructor && |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
591 ? "${jsLibraryName}.${jsClassName}" | 597 ? "${jsLibraryName}.${jsClassName}" |
592 : jsClassName, | 598 : jsClassName, |
593 sbPatch, | 599 sbPatch, |
594 isStatic: true); | 600 isStatic: true); |
595 } | 601 } |
596 }); | 602 }); |
597 } | 603 } |
598 if (isDom) { | 604 if (isDom) { |
599 sbPatch.write(" static Type get instanceRuntimeType => ${classNameI mpl};\n"); | 605 sbPatch.write(" static Type get instanceRuntimeType => ${classNameI mpl};\n"); |
600 } | 606 } |
607 if (isPrivate) { | |
608 sb.write(""" | |
609 class ${escapePrivateClassPrefix}${className} implements $className {} | |
610 """); | |
611 } | |
612 | |
601 if (sbPatch.isNotEmpty) { | 613 if (sbPatch.isNotEmpty) { |
602 var typeVariablesClause = ''; | 614 var typeVariablesClause = ''; |
603 if (!clazz.typeVariables.isEmpty) { | 615 if (!clazz.typeVariables.isEmpty) { |
604 typeVariablesClause = | 616 typeVariablesClause = |
605 '<${clazz.typeVariables.map((m) => mirrors.MirrorSystem.getNam e(m.simpleName)).join(',')}>'; | 617 '<${clazz.typeVariables.map((m) => mirrors.MirrorSystem.getNam e(m.simpleName)).join(',')}>'; |
606 } | 618 } |
607 sb.write(""" | 619 sb.write(""" |
608 patch class $className$typeVariablesClause { | 620 patch class $className$typeVariablesClause { |
609 $sbPatch | 621 $sbPatch |
610 } | 622 } |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
698 while (prefixNames.contains(prefixName)) { | 710 while (prefixNames.contains(prefixName)) { |
699 prefixName = '$basePrefixName$i'; | 711 prefixName = '$basePrefixName$i'; |
700 i++; | 712 i++; |
701 } | 713 } |
702 prefixNames.add(prefixName); | 714 prefixNames.add(prefixName); |
703 libraryPrefixes[libraryMirror] = prefixName; | 715 libraryPrefixes[libraryMirror] = prefixName; |
704 } | 716 } |
705 var isArray = typeMirror.isSubtypeOf(listMirror); | 717 var isArray = typeMirror.isSubtypeOf(listMirror); |
706 var isFunction = typeMirror.isSubtypeOf(functionMirror); | 718 var isFunction = typeMirror.isSubtypeOf(functionMirror); |
707 var isJSObject = typeMirror.isSubtypeOf(jsObjectMirror); | 719 var isJSObject = typeMirror.isSubtypeOf(jsObjectMirror); |
720 var className = mirrors.MirrorSystem.getName(typeMirror.simpleName); | |
721 var isPrivate = className.startsWith('_'); | |
722 if (isPrivate) className = '${escapePrivateClassPrefix}${className}'; | |
708 var fullName = | 723 var fullName = |
709 '${prefixName}.${mirrors.MirrorSystem.getName(typeMirror.simpleName)}'; | 724 '${prefixName}.${className}'; |
710 (isArray ? implementsArray : implements).add(fullName); | 725 (isArray ? implementsArray : implements).add(fullName); |
711 if (!isArray && !isFunction && !isJSObject) { | 726 if (!isArray && !isFunction && !isJSObject) { |
712 // For DOM classes we need to be a bit more conservative at tagging them | 727 // For DOM classes we need to be a bit more conservative at tagging them |
713 // as implementing JS inteorp classes risks strange unintended | 728 // as implementing JS inteorp classes risks strange unintended |
714 // consequences as unrleated code may have instanceof checks. Checking | 729 // consequences as unrleated code may have instanceof checks. Checking |
715 // for isJSObject ensures we do not accidentally pull in existing | 730 // for isJSObject ensures we do not accidentally pull in existing |
716 // dart:html classes as they all have JSObject as a base class. | 731 // dart:html classes as they all have JSObject as a base class. |
717 // Note that methods from these classes can still be called on a | 732 // Note that methods from these classes can still be called on a |
718 // dart:html instance but checked mode type checks will fail. This is | 733 // dart:html instance but checked mode type checks will fail. This is |
719 // not ideal but is better than causing strange breaks in existing | 734 // not ideal but is better than causing strange breaks in existing |
(...skipping 602 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1322 | 1337 |
1323 static toTypedObject(JsObject o) native "JSNative_toTypedObject"; | 1338 static toTypedObject(JsObject o) native "JSNative_toTypedObject"; |
1324 | 1339 |
1325 /** | 1340 /** |
1326 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n ot | 1341 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n ot |
1327 * wrapped. | 1342 * wrapped. |
1328 */ | 1343 */ |
1329 static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap"; | 1344 static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap"; |
1330 } | 1345 } |
1331 | 1346 |
1347 /// Utility methods to efficiently manipulate typed JSInterop objects in cases | |
1348 /// where the name to call is not known at runtime. You should only use these | |
1349 /// methods when the same effect cannot be achieved with @JS annotations. | |
1350 /// These methods would be extension methods on JSObject if Dart supported | |
1351 /// extension methods. | |
1352 class JSNative { | |
1353 /** | |
1354 * WARNING: performance of this method is much worse than other methods | |
1355 * in JSNative. Only use this method as a last resort. | |
1356 * | |
1357 * Recursively converts a JSON-like collection of Dart objects to a | |
1358 * collection of JavaScript objects and returns a [JsObject] proxy to it. | |
1359 * | |
1360 * [object] must be a [Map] or [Iterable], the contents of which are also | |
1361 * converted. Maps and Iterables are copied to a new JavaScript object. | |
1362 * Primitives and other transferrable values are directly converted to their | |
1363 * JavaScript type, and all other objects are proxied. | |
1364 */ | |
1365 static jsify(object) { | |
1366 if ((object is! Map) && (object is! Iterable)) { | |
1367 throw new ArgumentError("object must be a Map or Iterable"); | |
1368 } | |
1369 return _jsify(object); | |
1370 } | |
1371 | |
1372 static _jsify(object) native "JSObject_jsify"; | |
1373 static JSObject newObject() native "JSObject_newObject"; | |
1374 | |
1375 static hasProperty(JSObject o, name) => o._hasProperty(name); | |
1376 static getProperty(JSObject o, name) => o._operator_getter(name); | |
1377 static setProperty(JSObject o, name, value) => o._operator_setter(name, value) ; | |
1378 static callMethod(JSObject o, String method, List args) => o._callMethod(metho d, args); | |
1379 static instanceof(JSObject o, Function type) => o._instanceof(type); | |
1380 static callConstructor(JSObject constructor, List args) native "JSNative_callC onstructor"; | |
1381 } | |
1332 | 1382 |
1333 /** | 1383 /** |
1334 * Proxies a JavaScript Function object. | 1384 * Proxies a JavaScript Function object. |
1335 */ | 1385 */ |
1336 class JsFunction extends JsObject { | 1386 class JsFunction extends JsObject { |
1337 JsFunction.internal() : super.internal(); | 1387 JsFunction.internal() : super.internal(); |
1338 | 1388 |
1339 /** | 1389 /** |
1340 * Returns a [JsFunction] that captures its 'this' binding and calls [f] | 1390 * Returns a [JsFunction] that captures its 'this' binding and calls [f] |
1341 * with the value of this passed as the first argument. | 1391 * with the value of this passed as the first argument. |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1556 } else { | 1606 } else { |
1557 var ret = _interopCaptureThisExpando[f]; | 1607 var ret = _interopCaptureThisExpando[f]; |
1558 if (ret == null) { | 1608 if (ret == null) { |
1559 // TODO(jacobr): we could optimize this. | 1609 // TODO(jacobr): we could optimize this. |
1560 ret = JSFunction._createWithThis(f); | 1610 ret = JSFunction._createWithThis(f); |
1561 _interopCaptureThisExpando[f] = ret; | 1611 _interopCaptureThisExpando[f] = ret; |
1562 } | 1612 } |
1563 return ret; | 1613 return ret; |
1564 } | 1614 } |
1565 } | 1615 } |
1566 | |
1567 debugPrint(_) {} | |
OLD | NEW |