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 |