Chromium Code Reviews| 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 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 131 } | 131 } |
| 132 | 132 |
| 133 final _allowedMethods = new Map<Symbol, _DeclarationSet>(); | 133 final _allowedMethods = new Map<Symbol, _DeclarationSet>(); |
| 134 final _allowedGetters = new Map<Symbol, _DeclarationSet>(); | 134 final _allowedGetters = new Map<Symbol, _DeclarationSet>(); |
| 135 final _allowedSetters = new Map<Symbol, _DeclarationSet>(); | 135 final _allowedSetters = new Map<Symbol, _DeclarationSet>(); |
| 136 | 136 |
| 137 final _jsInterfaceTypes = new Set<mirrors.ClassMirror>(); | 137 final _jsInterfaceTypes = new Set<mirrors.ClassMirror>(); |
| 138 @Deprecated("Internal Use Only") | 138 @Deprecated("Internal Use Only") |
| 139 Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes; | 139 Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes; |
| 140 | 140 |
| 141 class _StringLiteralEscape { | |
| 142 // Character code constants. | |
| 143 static const int BACKSPACE = 0x08; | |
| 144 static const int TAB = 0x09; | |
| 145 static const int NEWLINE = 0x0a; | |
| 146 static const int CARRIAGE_RETURN = 0x0d; | |
| 147 static const int FORM_FEED = 0x0c; | |
| 148 static const int QUOTE = 0x22; | |
| 149 static const int CHAR_$ = 0x24; | |
| 150 static const int CHAR_0 = 0x30; | |
| 151 static const int BACKSLASH = 0x5c; | |
| 152 static const int CHAR_b = 0x62; | |
| 153 static const int CHAR_f = 0x66; | |
| 154 static const int CHAR_n = 0x6e; | |
| 155 static const int CHAR_r = 0x72; | |
| 156 static const int CHAR_t = 0x74; | |
| 157 static const int CHAR_u = 0x75; | |
| 158 | |
| 159 final StringSink _sink; | |
| 160 | |
| 161 _StringLiteralEscape(this._sink); | |
| 162 | |
| 163 void writeString(String string) { | |
| 164 _sink.write(string); | |
| 165 } | |
| 166 | |
| 167 void writeStringSlice(String string, int start, int end) { | |
| 168 _sink.write(string.substring(start, end)); | |
| 169 } | |
| 170 | |
| 171 void writeCharCode(int charCode) { | |
| 172 _sink.writeCharCode(charCode); | |
| 173 } | |
| 174 | |
| 175 /// ('0' + x) or ('a' + x - 10) | |
| 176 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; | |
| 177 | |
| 178 /// Write, and suitably escape, a string's content as a JSON string literal. | |
| 179 void writeStringContent(String s) { | |
| 180 // Identical to JSON string lieral escaping except that we also escape $. | |
|
sra1
2016/07/22 20:16:32
lieral?
Jacob
2016/07/25 16:31:58
Done.
| |
| 181 int offset = 0; | |
| 182 final int length = s.length; | |
| 183 for (int i = 0; i < length; i++) { | |
| 184 int charCode = s.codeUnitAt(i); | |
| 185 if (charCode > BACKSLASH) continue; | |
| 186 if (charCode < 32) { | |
| 187 if (i > offset) writeStringSlice(s, offset, i); | |
| 188 offset = i + 1; | |
| 189 writeCharCode(BACKSLASH); | |
| 190 switch (charCode) { | |
| 191 case BACKSPACE: | |
| 192 writeCharCode(CHAR_b); | |
| 193 break; | |
| 194 case TAB: | |
| 195 writeCharCode(CHAR_t); | |
| 196 break; | |
| 197 case NEWLINE: | |
| 198 writeCharCode(CHAR_n); | |
| 199 break; | |
| 200 case FORM_FEED: | |
| 201 writeCharCode(CHAR_f); | |
| 202 break; | |
| 203 case CARRIAGE_RETURN: | |
| 204 writeCharCode(CHAR_r); | |
| 205 break; | |
| 206 default: | |
| 207 writeCharCode(CHAR_u); | |
| 208 writeCharCode(CHAR_0); | |
| 209 writeCharCode(CHAR_0); | |
| 210 writeCharCode(hexDigit((charCode >> 4) & 0xf)); | |
| 211 writeCharCode(hexDigit(charCode & 0xf)); | |
| 212 break; | |
| 213 } | |
| 214 } else if (charCode == QUOTE || charCode == BACKSLASH || charCode == CHAR_ $) { | |
|
sra1
2016/07/22 20:16:32
line length
Jacob
2016/07/25 16:31:58
done. ran dartfmt
| |
| 215 if (i > offset) writeStringSlice(s, offset, i); | |
| 216 offset = i + 1; | |
| 217 writeCharCode(BACKSLASH); | |
| 218 writeCharCode(charCode); | |
| 219 } | |
| 220 } | |
| 221 if (offset == 0) { | |
| 222 writeString(s); | |
| 223 } else if (offset < length) { | |
| 224 writeStringSlice(s, offset, length); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 /** | |
| 229 * Serialize a [num], [String], [bool], [Null], [List] or [Map] value. | |
| 230 * | |
| 231 * Returns true if the value is one of these types, and false if not. | |
| 232 * If a value is both a [List] and a [Map], it's serialized as a [List]. | |
|
sra1
2016/07/22 20:16:32
This comment is wrong.
| |
| 233 */ | |
| 234 bool writeStringLiteral(String str) { | |
|
sra1
2016/07/22 20:16:32
Doesn't return anything.
Jacob
2016/07/25 16:31:58
Done.
| |
| 235 writeString('"'); | |
| 236 writeStringContent(str); | |
| 237 writeString('"'); | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 String _escapeString(String str) { | |
| 242 StringBuffer output = new StringBuffer(); | |
| 243 new _StringLiteralEscape(output)..writeStringLiteral(str); | |
| 244 return output.toString(); | |
|
sra1
2016/07/22 20:16:32
Could you use the JSON codec rather than copy & pa
Jacob
2016/07/25 16:31:58
Can't easily because of $
JSON doesn't need to esc
| |
| 245 } | |
| 246 | |
| 141 /// A collection of methods where all methods have the same name. | 247 /// A collection of methods where all methods have the same name. |
| 142 /// This class is intended to optimize whether a specific invocation is | 248 /// This class is intended to optimize whether a specific invocation is |
| 143 /// appropritate for at least some of the methods in the collection. | 249 /// appropritate for at least some of the methods in the collection. |
| 144 class _DeclarationSet { | 250 class _DeclarationSet { |
| 145 _DeclarationSet() : _members = <mirrors.DeclarationMirror>[]; | 251 _DeclarationSet() : _members = <mirrors.DeclarationMirror>[]; |
| 146 | 252 |
| 147 static bool _checkType(obj, mirrors.TypeMirror type) { | 253 static bool _checkType(obj, mirrors.TypeMirror type) { |
| 148 if (obj == null) return true; | 254 if (obj == null) return true; |
| 149 return mirrors.reflectType(obj.runtimeType).isSubtypeOf(type); | 255 return mirrors.reflectType(obj.runtimeType).isSubtypeOf(type); |
| 150 } | 256 } |
| (...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 362 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; | 468 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; |
| 363 | 469 |
| 364 String _accessJsPath(String path) => _accessJsPathHelper(path.split(".")); | 470 String _accessJsPath(String path) => _accessJsPathHelper(path.split(".")); |
| 365 | 471 |
| 366 String _accessJsPathHelper(Iterable<String> parts) { | 472 String _accessJsPathHelper(Iterable<String> parts) { |
| 367 var sb = new StringBuffer(); | 473 var sb = new StringBuffer(); |
| 368 sb | 474 sb |
| 369 ..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length) | 475 ..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length) |
| 370 ..write("${_JS_LIBRARY_PREFIX}.context"); | 476 ..write("${_JS_LIBRARY_PREFIX}.context"); |
| 371 for (var p in parts) { | 477 for (var p in parts) { |
| 372 sb.write(", '$p')"); | 478 sb.write(", ${_escapeString(p)})"); |
| 373 } | 479 } |
| 374 return sb.toString(); | 480 return sb.toString(); |
| 375 } | 481 } |
| 376 | 482 |
| 377 // TODO(jacobr): remove these helpers and add JsNative.setPropertyDotted, | 483 // TODO(jacobr): remove these helpers and add JsNative.setPropertyDotted, |
| 378 // getPropertyDotted, and callMethodDotted helpers that would be simpler | 484 // getPropertyDotted, and callMethodDotted helpers that would be simpler |
| 379 // and more efficient. | 485 // and more efficient. |
| 380 String _accessJsPathSetter(String path) { | 486 String _accessJsPathSetter(String path) { |
| 381 var parts = path.split("."); | 487 var parts = path.split("."); |
| 382 return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPathHelper(parts .getRange(0, parts.length - 1)) | 488 return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPathHelper(parts .getRange(0, parts.length - 1)) |
| 383 }, '${parts.last}', v)"; | 489 }, ${_escapeString(parts.last)}, v)"; |
| 384 } | 490 } |
| 385 | 491 |
| 386 String _accessJsPathCallMethodHelper(String path) { | 492 String _accessJsPathCallMethodHelper(String path) { |
| 387 var parts = path.split("."); | 493 var parts = path.split("."); |
| 388 return "${_JS_LIBRARY_PREFIX}.JsNative.callMethod(${_accessJsPathHelper(parts. getRange(0, parts.length - 1)) | 494 return "${_JS_LIBRARY_PREFIX}.JsNative.callMethod(${_accessJsPathHelper(parts. getRange(0, parts.length - 1)) |
| 389 }, '${parts.last}',"; | 495 }, ${_escapeString(parts.last)},"; |
| 390 } | 496 } |
| 391 | 497 |
| 392 @Deprecated("Internal Use Only") | 498 @Deprecated("Internal Use Only") |
| 393 void addMemberHelper( | 499 void addMemberHelper( |
| 394 mirrors.MethodMirror declaration, String path, StringBuffer sb, | 500 mirrors.MethodMirror declaration, String path, StringBuffer sb, |
| 395 {bool isStatic: false, String memberName}) { | 501 {bool isStatic: false, String memberName}) { |
| 396 if (!declaration.isConstructor) { | 502 if (!declaration.isConstructor) { |
| 397 var jsName = _getJsMemberName(declaration); | 503 var jsName = _getJsMemberName(declaration); |
| 398 path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName; | 504 path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName; |
| 399 } | 505 } |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 559 sbPatch.write(") {\n" | 665 sbPatch.write(") {\n" |
| 560 " var ret = ${_JS_LIBRARY_PREFIX}.JsNative.newObject();\n "); | 666 " var ret = ${_JS_LIBRARY_PREFIX}.JsNative.newObject();\n "); |
| 561 i = 0; | 667 i = 0; |
| 562 for (var p in declaration.parameters) { | 668 for (var p in declaration.parameters) { |
| 563 assert(p.isNamed); // TODO(jacobr): throw. | 669 assert(p.isNamed); // TODO(jacobr): throw. |
| 564 var name = args[i]; | 670 var name = args[i]; |
| 565 var jsName = _stripReservedNamePrefix( | 671 var jsName = _stripReservedNamePrefix( |
| 566 mirrors.MirrorSystem.getName(p.simpleName)); | 672 mirrors.MirrorSystem.getName(p.simpleName)); |
| 567 sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n" | 673 sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n" |
| 568 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n " | 674 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n " |
| 569 " ${_JS_LIBRARY_PREFIX}.JsNative.setProperty(ret, '$j sName', $name);\n" | 675 " ${_JS_LIBRARY_PREFIX}.JsNative.setProperty(ret, ${_ escapeString(jsName)}, $name);\n" |
| 570 " }\n"); | 676 " }\n"); |
| 571 i++; | 677 i++; |
| 572 } | 678 } |
| 573 | 679 |
| 574 sbPatch.write( | 680 sbPatch.write( |
| 575 " return ret;" | 681 " return ret;" |
| 576 "}\n"); | 682 "}\n"); |
| 577 } else if (declaration.isConstructor || | 683 } else if (declaration.isConstructor || |
| 578 declaration.isFactoryConstructor) { | 684 declaration.isFactoryConstructor) { |
| 579 sbPatch.write(" "); | 685 sbPatch.write(" "); |
| (...skipping 757 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1337 | 1443 |
| 1338 static toTypedObject(JsObject o) native "JSNative_toTypedObject"; | 1444 static toTypedObject(JsObject o) native "JSNative_toTypedObject"; |
| 1339 | 1445 |
| 1340 /** | 1446 /** |
| 1341 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n ot | 1447 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n ot |
| 1342 * wrapped. | 1448 * wrapped. |
| 1343 */ | 1449 */ |
| 1344 static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap"; | 1450 static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap"; |
| 1345 } | 1451 } |
| 1346 | 1452 |
| 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 } | |
| 1382 | |
| 1383 /** | 1453 /** |
| 1384 * Proxies a JavaScript Function object. | 1454 * Proxies a JavaScript Function object. |
| 1385 */ | 1455 */ |
| 1386 class JsFunction extends JsObject { | 1456 class JsFunction extends JsObject { |
| 1387 JsFunction.internal() : super.internal(); | 1457 JsFunction.internal() : super.internal(); |
| 1388 | 1458 |
| 1389 /** | 1459 /** |
| 1390 * Returns a [JsFunction] that captures its 'this' binding and calls [f] | 1460 * Returns a [JsFunction] that captures its 'this' binding and calls [f] |
| 1391 * with the value of this passed as the first argument. | 1461 * with the value of this passed as the first argument. |
| 1392 */ | 1462 */ |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1606 } else { | 1676 } else { |
| 1607 var ret = _interopCaptureThisExpando[f]; | 1677 var ret = _interopCaptureThisExpando[f]; |
| 1608 if (ret == null) { | 1678 if (ret == null) { |
| 1609 // TODO(jacobr): we could optimize this. | 1679 // TODO(jacobr): we could optimize this. |
| 1610 ret = JSFunction._createWithThis(f); | 1680 ret = JSFunction._createWithThis(f); |
| 1611 _interopCaptureThisExpando[f] = ret; | 1681 _interopCaptureThisExpando[f] = ret; |
| 1612 } | 1682 } |
| 1613 return ret; | 1683 return ret; |
| 1614 } | 1684 } |
| 1615 } | 1685 } |
| OLD | NEW |