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 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 if (declaration is mirrors.MethodMirror && declaration.isSetter) { | 303 if (declaration is mirrors.MethodMirror && declaration.isSetter) { |
304 assert(name.endsWith("=")); | 304 assert(name.endsWith("=")); |
305 name = name.substring(0, name.length - 1); | 305 name = name.substring(0, name.length - 1); |
306 } | 306 } |
307 return name; | 307 return name; |
308 } | 308 } |
309 | 309 |
310 final _JS_LIBRARY_PREFIX = "js_library"; | 310 final _JS_LIBRARY_PREFIX = "js_library"; |
311 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; | 311 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; |
312 | 312 |
313 String _accessJsPath(String path) => | 313 String _accessJsPath(String path) { |
314 "${_JS_LIBRARY_PREFIX}.context${path.split(".").map((p) => "['$p']").join(''
)}"; | 314 var parts = path.split("."); |
| 315 var sb = new StringBuffer(); |
| 316 sb |
| 317 ..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length) |
| 318 ..write("${_JS_LIBRARY_PREFIX}.context"); |
| 319 for (var p in parts) { |
| 320 sb.write(", '$p')"); |
| 321 } |
| 322 return sb.toString(); |
| 323 } |
| 324 |
| 325 |
| 326 String _accessJsPathSetter(String path) { |
| 327 var parts = path.split("."); |
| 328 return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPath(parts.getRa
nge(0, parts.length - 1).join('.')) |
| 329 }, '{parts.end}', v)"; |
| 330 } |
315 | 331 |
316 @Deprecated("Internal Use Only") | 332 @Deprecated("Internal Use Only") |
317 void addMemberHelper( | 333 void addMemberHelper( |
318 mirrors.MethodMirror declaration, String path, StringBuffer sb, | 334 mirrors.MethodMirror declaration, String path, StringBuffer sb, |
319 {bool isStatic: false, String memberName}) { | 335 {bool isStatic: false, String memberName}) { |
320 if (!declaration.isConstructor) { | 336 if (!declaration.isConstructor) { |
321 var jsName = _getJsMemberName(declaration); | 337 var jsName = _getJsMemberName(declaration); |
322 path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName; | 338 path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName; |
323 } | 339 } |
324 var name = memberName != null ? memberName : _getDeclarationName(declaration); | 340 var name = memberName != null ? memberName : _getDeclarationName(declaration); |
325 if (declaration.isConstructor) { | 341 if (declaration.isConstructor) { |
326 sb.write("factory"); | 342 sb.write("factory"); |
327 } else if (isStatic) { | 343 } else if (isStatic) { |
328 sb.write("static"); | 344 sb.write("static"); |
329 } else { | 345 } else { |
330 sb.write("patch"); | 346 sb.write("patch"); |
331 } | 347 } |
332 sb.write(" "); | 348 sb.write(" "); |
333 if (declaration.isGetter) { | 349 if (declaration.isGetter) { |
334 sb.write("get $name => ${_accessJsPath(path)};"); | 350 sb.write("get $name => ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_access
JsPath(path)});"); |
335 } else if (declaration.isSetter) { | 351 } else if (declaration.isSetter) { |
336 sb.write("set $name(v) => ${_accessJsPath(path)} = v;"); | 352 sb.write("set $name(v) => ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_acc
essJsPathSetter(path)});"); |
337 } else { | 353 } else { |
338 sb.write("$name("); | 354 sb.write("$name("); |
339 bool hasOptional = false; | 355 bool hasOptional = false; |
340 int i = 0; | 356 int i = 0; |
341 var args = <String>[]; | 357 var args = <String>[]; |
342 for (var p in declaration.parameters) { | 358 for (var p in declaration.parameters) { |
343 assert(!p.isNamed); // XXX throw | 359 assert(!p.isNamed); // XXX throw |
344 assert(!p.hasDefaultValue); | 360 assert(!p.hasDefaultValue); |
345 if (i > 0) { | 361 if (i > 0) { |
346 sb.write(", "); | 362 sb.write(", "); |
347 } | 363 } |
348 if (p.isOptional && !hasOptional) { | 364 if (p.isOptional && !hasOptional) { |
349 sb.write("["); | 365 sb.write("["); |
350 hasOptional = true; | 366 hasOptional = true; |
351 } | 367 } |
352 var arg = "p$i"; | 368 var arg = "p$i"; |
353 args.add(arg); | 369 args.add(arg); |
354 sb.write(arg); | 370 sb.write(arg); |
355 if (p.isOptional) { | 371 if (p.isOptional) { |
356 sb.write("=${_UNDEFINED_VAR}"); | 372 sb.write("=${_UNDEFINED_VAR}"); |
357 } | 373 } |
358 i++; | 374 i++; |
359 } | 375 } |
360 if (hasOptional) { | 376 if (hasOptional) { |
361 sb.write("]"); | 377 sb.write("]"); |
362 } | 378 } |
363 // TODO(jacobr): | 379 // TODO(jacobr): |
364 sb.write(") => "); | 380 sb.write(") => "); |
| 381 sb.write('${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop('); |
365 if (declaration.isConstructor) { | 382 if (declaration.isConstructor) { |
366 sb.write("new ${_JS_LIBRARY_PREFIX}.JsObject("); | 383 sb.write("new ${_JS_LIBRARY_PREFIX}.JsObject("); |
367 } | 384 } |
368 sb | 385 sb |
369 ..write(_accessJsPath(path)) | 386 ..write(_accessJsPath(path)) |
370 ..write(declaration.isConstructor ? "," : ".apply(") | 387 ..write(declaration.isConstructor ? "," : ".apply(") |
371 ..write("[${args.join(",")}]"); | 388 ..write("[${args.join(",")}]"); |
372 | 389 |
373 if (hasOptional) { | 390 if (hasOptional) { |
374 sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()"); | 391 sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()"); |
375 } | 392 } |
376 sb.write(");"); | 393 sb.write("));"); |
377 } | 394 } |
378 sb.write("\n"); | 395 sb.write("\n"); |
379 } | 396 } |
380 | 397 |
381 // TODO(jacobr): make this check more robust. | 398 // TODO(jacobr): make this check more robust. |
382 bool _isExternal(mirrors.Mirror mirror) { | 399 bool _isExternal(mirrors.Mirror mirror) { |
383 /* | 400 /* |
384 var source = mirror.source; | 401 var source = mirror.source; |
385 return source != null && source.startsWith("external "); | 402 return source != null && source.startsWith("external "); |
386 */ | 403 */ |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
846 | 863 |
847 bool get _finalized native "Js_interfacesFinalized_Callback"; | 864 bool get _finalized native "Js_interfacesFinalized_Callback"; |
848 | 865 |
849 JsObject get context { | 866 JsObject get context { |
850 if (_cachedContext == null) { | 867 if (_cachedContext == null) { |
851 _cachedContext = _context; | 868 _cachedContext = _context; |
852 } | 869 } |
853 return _cachedContext; | 870 return _cachedContext; |
854 } | 871 } |
855 | 872 |
| 873 @Deprecated("Internal Use Only") |
| 874 maybeWrapTypedInterop(o) => |
| 875 html_common.wrap_jso_no_SerializedScriptvalue(o); |
| 876 |
856 _maybeWrap(o) { | 877 _maybeWrap(o) { |
857 var wrapped = html_common.wrap_jso_no_SerializedScriptvalue(o); | 878 var wrapped = html_common.wrap_jso_no_SerializedScriptvalue(o); |
858 if (identical(wrapped, o)) return o; | 879 if (identical(wrapped, o)) return o; |
859 return (wrapped is html.Blob || | 880 return (wrapped is html.Blob || |
860 wrapped is html.Event || | 881 wrapped is html.Event || |
861 wrapped is indexed_db.KeyRange || | 882 wrapped is indexed_db.KeyRange || |
862 wrapped is html.ImageData || | 883 wrapped is html.ImageData || |
863 wrapped is html.Node || | 884 wrapped is html.Node || |
864 wrapped is TypedData || | 885 wrapped is TypedData || |
865 wrapped is html.Window) ? wrapped : o; | 886 wrapped is html.Window) ? wrapped : o; |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1095 } | 1116 } |
1096 var ret = this[name]; | 1117 var ret = this[name]; |
1097 if (matches != null && matches._checkReturnType(ret)) return ret; | 1118 if (matches != null && matches._checkReturnType(ret)) return ret; |
1098 if (ret is Function || | 1119 if (ret is Function || |
1099 (ret is JsFunction /* shouldn't be needed in the future*/) && | 1120 (ret is JsFunction /* shouldn't be needed in the future*/) && |
1100 _allowedMethods.containsKey( | 1121 _allowedMethods.containsKey( |
1101 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. | 1122 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. |
1102 throwError(); | 1123 throwError(); |
1103 } else { | 1124 } else { |
1104 // TODO(jacobr): should we throw if the JavaScript object doesn't have t
he property? | 1125 // TODO(jacobr): should we throw if the JavaScript object doesn't have t
he property? |
1105 return this[name]; | 1126 return maybeWrapTypedInterop(this._operator_getter(name)); |
1106 } | 1127 } |
1107 } else if (invocation.isSetter) { | 1128 } else if (invocation.isSetter) { |
1108 if (CHECK_JS_INVOCATIONS) { | 1129 if (CHECK_JS_INVOCATIONS) { |
1109 var matches = _allowedSetters[invocation.memberName]; | 1130 var matches = _allowedSetters[invocation.memberName]; |
1110 if (matches == null || | 1131 if (matches == null || |
1111 !matches.checkInvocation(invocation)) throwError(); | 1132 !matches.checkInvocation(invocation)) throwError(); |
1112 } | 1133 } |
1113 assert(name.endsWith("=")); | 1134 assert(name.endsWith("=")); |
1114 name = name.substring(0, name.length - 1); | 1135 name = name.substring(0, name.length - 1); |
1115 return this[name] = invocation.positionalArguments.first; | 1136 return maybeWrapTypedInterop(_operator_setter( |
| 1137 name, invocation.positionalArguments.first)); |
1116 } else { | 1138 } else { |
1117 // TODO(jacobr): also allow calling getters that look like functions. | 1139 // TODO(jacobr): also allow calling getters that look like functions. |
1118 var matches; | 1140 var matches; |
1119 if (CHECK_JS_INVOCATIONS) { | 1141 if (CHECK_JS_INVOCATIONS) { |
1120 matches = _allowedMethods[invocation.memberName]; | 1142 matches = _allowedMethods[invocation.memberName]; |
1121 if (matches == null || | 1143 if (matches == null || |
1122 !matches.checkInvocation(invocation)) throwError(); | 1144 !matches.checkInvocation(invocation)) throwError(); |
1123 } | 1145 } |
1124 var ret = this.callMethod(name, _buildArgs(invocation)); | 1146 var ret = maybeWrapTypedInterop(this._callMethod(name, _buildArgs(invocati
on))); |
1125 if (CHECK_JS_INVOCATIONS) { | 1147 if (CHECK_JS_INVOCATIONS) { |
1126 if (!matches._checkReturnType(ret)) throwError(); | 1148 if (!matches._checkReturnType(ret)) throwError(); |
1127 } | 1149 } |
1128 return ret; | 1150 return ret; |
1129 } | 1151 } |
1130 } | 1152 } |
1131 | 1153 |
1132 _callMethod(String name, List args) native "JsObject_callMethod"; | 1154 _callMethod(String name, List args) native "JsObject_callMethod"; |
1133 } | 1155 } |
1134 | 1156 |
1135 // JavaScript interop methods that do not automatically wrap to dart:html types. | 1157 // JavaScript interop methods that do not automatically wrap to dart:html types. |
1136 // Warning: this API is not exposed to dart:js. | 1158 // Warning: this API is not exposed to dart:js. |
1137 @Deprecated("Internal Use Only") | 1159 @Deprecated("Internal Use Only") |
1138 class JsNative { | 1160 class JsNative { |
1139 static getProperty(JsObject o, name) { | 1161 static getProperty(o, name) { |
1140 return o._operator_getter(name); | 1162 o = unwrap_jso(o); |
| 1163 return o != null ? o._operator_getter(name) : null; |
1141 } | 1164 } |
1142 | 1165 |
1143 static callMethod(JsObject o, String method, List args) { | 1166 static setProperty(o, name, value) { |
1144 return o._callMethod(method, args); | 1167 return unwrap_jso(o)._operator_setter(name, value); |
| 1168 } |
| 1169 |
| 1170 static callMethod(o, String method, List args) { |
| 1171 return unwrap_jso(o)._callMethod(method, args); |
1145 } | 1172 } |
1146 | 1173 |
1147 static getArrayIndex(JsArray array, int index) { | 1174 static getArrayIndex(JsArray array, int index) { |
1148 array._checkIndex(index); | 1175 array._checkIndex(index); |
1149 return getProperty(array, index); | 1176 return getProperty(array, index); |
1150 } | 1177 } |
1151 | 1178 |
1152 /** | 1179 /** |
1153 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n
ot | 1180 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n
ot |
1154 * wrapped. | 1181 * wrapped. |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1373 return f; | 1400 return f; |
1374 } else { | 1401 } else { |
1375 var ret = _interopCaptureThisExpando[f]; | 1402 var ret = _interopCaptureThisExpando[f]; |
1376 if (ret == null) { | 1403 if (ret == null) { |
1377 ret = new JsFunction.withThis(f); | 1404 ret = new JsFunction.withThis(f); |
1378 _interopCaptureThisExpando[f] = ret; | 1405 _interopCaptureThisExpando[f] = ret; |
1379 } | 1406 } |
1380 return ret; | 1407 return ret; |
1381 } | 1408 } |
1382 } | 1409 } |
OLD | NEW |