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 library dart.js; | 5 library dart.js; |
| 6 | 6 |
| 7 import 'dart:collection' show HashMap; | |
| 7 import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS; | 8 import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS; |
| 9 import 'dart:_interceptors' show JavaScriptObject, UnknownJavaScriptObject; | |
| 8 import 'dart:_js_helper' show Primitives, convertDartClosureToJS; | 10 import 'dart:_js_helper' show Primitives, convertDartClosureToJS; |
| 9 | 11 |
| 10 final JsObject context = new JsObject._fromJs(Primitives.computeGlobalThis()); | 12 final JsObject context = new JsObject._fromJs(Primitives.computeGlobalThis()); |
| 11 | 13 |
| 12 JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data); | |
| 13 | |
| 14 class Callback implements Serializable<JsFunction> { | |
| 15 final Function _f; // here to allow capture in closure | |
| 16 final bool _withThis; // here to allow capture in closure | |
| 17 dynamic _jsFunction; | |
| 18 | |
| 19 Callback._(this._f, this._withThis) { | |
| 20 _jsFunction = JS('', r''' | |
| 21 (function(){ | |
| 22 var f = #; | |
| 23 return function(){ | |
| 24 return f(this, Array.prototype.slice.apply(arguments)); | |
| 25 }; | |
| 26 }).apply(this)''', convertDartClosureToJS(_call, 2)); | |
| 27 } | |
| 28 | |
| 29 factory Callback(Function f) => new Callback._(f, false); | |
| 30 factory Callback.withThis(Function f) => new Callback._(f, true); | |
| 31 | |
| 32 _call(thisArg, List args) { | |
| 33 final arguments = new List.from(args); | |
| 34 if (_withThis) arguments.insert(0, thisArg); | |
| 35 final dartArgs = arguments.map(_convertToDart).toList(); | |
| 36 return _convertToJS(Function.apply(_f, dartArgs)); | |
| 37 } | |
| 38 | |
| 39 JsFunction toJs() => new JsFunction._fromJs(_jsFunction); | |
| 40 } | |
| 41 | |
| 42 /* | |
| 43 * TODO(justinfagnani): add tests and make public when we remove Callback. | |
| 44 * | |
| 45 * Returns a [JsFunction] that captures its 'this' binding and calls [f] | |
| 46 * with the value of this passed as the first argument. | |
| 47 */ | |
| 48 JsFunction _captureThis(Function f) => | |
| 49 new JsFunction._fromJs(_convertDartFunction(f, captureThis: true)); | |
| 50 | |
| 51 _convertDartFunction(Function f, {bool captureThis: false}) { | 14 _convertDartFunction(Function f, {bool captureThis: false}) { |
| 52 return JS('', | 15 return JS('', |
| 53 'function(_call, f, captureThis) {' | 16 'function(_call, f, captureThis) {' |
| 54 'return function() {' | 17 'return function() {' |
| 55 'return _call(f, captureThis, this, ' | 18 'return _call(f, captureThis, this, ' |
| 56 'Array.prototype.slice.apply(arguments));' | 19 'Array.prototype.slice.apply(arguments));' |
| 57 '}' | 20 '}' |
| 58 '}(#, #, #)', DART_CLOSURE_TO_JS(_callDartFunction), f, captureThis); | 21 '}(#, #, #)', DART_CLOSURE_TO_JS(_callDartFunction), f, captureThis); |
| 59 } | 22 } |
| 60 | 23 |
| 61 _callDartFunction(callback, bool captureThis, self, List arguments) { | 24 _callDartFunction(callback, bool captureThis, self, List arguments) { |
| 62 if (captureThis) { | 25 if (captureThis) { |
| 63 arguments = [self]..addAll(arguments); | 26 arguments = [self]..addAll(arguments); |
| 64 } | 27 } |
| 65 var dartArgs = arguments.map(_convertToDart).toList(); | 28 var dartArgs = arguments.map(_convertToDart).toList(); |
| 66 return _convertToJS(Function.apply(callback, dartArgs)); | 29 return _convertToJS(Function.apply(callback, dartArgs)); |
| 67 } | 30 } |
| 68 | 31 |
| 69 | 32 |
| 70 class JsObject implements Serializable<JsObject> { | 33 class JsObject implements Serializable<JsObject> { |
| 71 // The wrapped JS object. | 34 // The wrapped JS object. |
| 72 final dynamic _jsObject; | 35 final dynamic _jsObject; |
| 73 | 36 |
| 74 JsObject._fromJs(this._jsObject) { | 37 JsObject._fromJs(this._jsObject) { |
| 38 assert(_jsObject != null); | |
| 75 // Remember this proxy for the JS object | 39 // Remember this proxy for the JS object |
| 76 _getDartProxy(_jsObject, _DART_OBJECT_PROPERTY_NAME, (o) => this); | 40 _getDartProxy(_jsObject, _DART_OBJECT_PROPERTY_NAME, (o) => this); |
| 77 } | 41 } |
| 78 | 42 |
| 43 /** | |
| 44 * Expert users only: | |
| 45 * Use this constructor only if you wish to get access to JS expandos | |
| 46 * attached to a WebKit native object such as a Node. | |
| 47 * An exception will be thrown if a primitive type is passed in passing one | |
| 48 * of these types to this method indicates an error. | |
| 49 */ | |
| 50 factory JsObject.fromDartObject(Object object) { | |
|
alexandre.ardhuin
2013/10/16 19:32:11
Rename to `fromBrowserObject`.
justinfagnani
2013/10/18 03:19:08
Done.
| |
| 51 if (object is num || object is String || object is bool || object == null) { | |
| 52 throw new IllegalArgumentException( | |
| 53 "object cannot be a num, string, bool, or null"); | |
| 54 } | |
| 55 return new JsObject._fromJs(_convertToJS(object)); | |
| 56 } | |
| 57 | |
| 58 /** | |
| 59 * Converts a json-like [data] to a JavaScript map or array and return a | |
| 60 * [JsObject] to it. | |
| 61 */ | |
| 62 factory JsObject.jsify(Object object) { | |
| 63 if ((object is! Map) && (object is! Iterable)) { | |
| 64 throw new IllegalArgumentException("object must be a Map or Iterable"); | |
| 65 } | |
| 66 return new JsObject._fromJs(_convertDataTree(object)); | |
| 67 } | |
| 68 | |
| 79 // TODO(vsm): Type constructor as Serializable<JsFunction> when | 69 // TODO(vsm): Type constructor as Serializable<JsFunction> when |
| 80 // dartbug.com/11854 is fixed. | 70 // dartbug.com/11854 is fixed. |
| 81 factory JsObject(constructor, [List arguments]) { | 71 factory JsObject(constructor, [List arguments]) { |
| 82 var constr = _convertToJS(constructor); | 72 var constr = _convertToJS(constructor); |
| 83 if (arguments == null) { | 73 if (arguments == null) { |
| 84 return new JsObject._fromJs(JS('', 'new #()', constr)); | 74 return new JsObject._fromJs(JS('', 'new #()', constr)); |
| 85 } | 75 } |
| 86 // The following code solves the problem of invoking a JavaScript | 76 // The following code solves the problem of invoking a JavaScript |
| 87 // constructor with an unknown number arguments. | 77 // constructor with an unknown number arguments. |
| 88 // First bind the constructor to the argument list using bind.apply(). | 78 // First bind the constructor to the argument list using bind.apply(). |
| 89 // The first argument to bind() is the binding of 'this', so add 'null' to | 79 // The first argument to bind() is the binding of 'this', so add 'null' to |
| 90 // the arguments list passed to apply(). | 80 // the arguments list passed to apply(). |
| 91 // After that, use the JavaScript 'new' operator which overrides any binding | 81 // After that, use the JavaScript 'new' operator which overrides any binding |
| 92 // of 'this' with the new instance. | 82 // of 'this' with the new instance. |
| 93 var args = [null]..addAll(arguments.map(_convertToJS)); | 83 var args = [null]..addAll(arguments.map(_convertToJS)); |
| 94 var factoryFunction = JS('', '#.bind.apply(#, #)', constr, constr, args); | 84 var factoryFunction = JS('', '#.bind.apply(#, #)', constr, constr, args); |
| 95 // Without this line, calling factoryFunction as a constructor throws | 85 // Without this line, calling factoryFunction as a constructor throws |
| 96 JS('String', 'String(#)', factoryFunction); | 86 JS('String', 'String(#)', factoryFunction); |
| 97 return new JsObject._fromJs(JS('', 'new #()', factoryFunction)); | 87 // This could return an UnknownJavaScriptObject, or a native |
| 88 // object for which there is an interceptor | |
| 89 var jsObj = JS('JavaScriptObject', 'new #()', factoryFunction); | |
| 90 // print("UnknownJavaScriptObject: ${jsObj is UnknownJavaScriptObject}"); | |
|
alexandre.ardhuin
2013/10/16 19:32:11
Remove these comments ?
justinfagnani
2013/10/18 03:19:08
Done.
| |
| 91 // print("JavaScriptObject: ${jsObj is JavaScriptObject}"); | |
| 92 // JS('void', 'console.log("jsObj: " + #)', jsObj); | |
| 93 return new JsObject._fromJs(jsObj); | |
| 98 } | 94 } |
| 99 | 95 |
| 100 factory JsObject._json(data) => new JsObject._fromJs(_convertDataTree(data)); | 96 // TODO: handle cycles |
| 97 static _convertDataTree(data) { | |
| 98 var _convertedObjects = new HashMap.identity(); | |
| 101 | 99 |
| 102 static _convertDataTree(data) { | 100 _convert(o) { |
| 103 if (data is Map) { | 101 if (_convertedObjects.containsKey(o)) { |
| 104 final convertedData = JS('=Object', '{}'); | 102 return _convertedObjects[o]; |
| 105 for (var key in data.keys) { | |
| 106 JS('=Object', '#[#]=#', convertedData, key, | |
| 107 _convertDataTree(data[key])); | |
| 108 } | 103 } |
| 109 return convertedData; | 104 if (o is Map) { |
| 110 } else if (data is Iterable) { | 105 final convertedMap = JS('=Object', '{}'); |
| 111 return data.map(_convertDataTree).toList(); | 106 _convertedObjects[o] = convertedMap; |
| 112 } else { | 107 for (var key in o.keys) { |
| 113 return _convertToJS(data); | 108 JS('=Object', '#[#]=#', convertedMap, key, _convert(o[key])); |
| 109 } | |
| 110 return convertedMap; | |
| 111 } else if (o is Iterable) { | |
| 112 var convertedList = []; | |
| 113 _convertedObjects[o] = convertedList; | |
| 114 convertedList.addAll(o.map(_convert)); | |
| 115 return convertedList; | |
| 116 } else { | |
| 117 return _convertToJS(o); | |
| 118 } | |
| 114 } | 119 } |
| 120 | |
| 121 return _convert(data); | |
| 115 } | 122 } |
| 116 | 123 |
| 117 JsObject toJs() => this; | 124 JsObject toJs() => this; |
| 118 | 125 |
| 119 /** | 126 /** |
| 120 * Returns the value associated with [key] from the proxied JavaScript | 127 * Returns the value associated with [key] from the proxied JavaScript |
| 121 * object. | 128 * object. |
| 122 * | 129 * |
| 123 * [key] must either be a [String] or [int]. | 130 * [key] must either be a [String] or [int]. |
| 124 */ | 131 */ |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 181 throw new ArgumentError("name is not a String or int"); | 188 throw new ArgumentError("name is not a String or int"); |
| 182 } | 189 } |
| 183 return _convertToDart(JS('', '#[#].apply(#, #)', _jsObject, name, | 190 return _convertToDart(JS('', '#[#].apply(#, #)', _jsObject, name, |
| 184 _jsObject, | 191 _jsObject, |
| 185 args == null ? null : args.map(_convertToJS).toList())); | 192 args == null ? null : args.map(_convertToJS).toList())); |
| 186 } | 193 } |
| 187 } | 194 } |
| 188 | 195 |
| 189 class JsFunction extends JsObject implements Serializable<JsFunction> { | 196 class JsFunction extends JsObject implements Serializable<JsFunction> { |
| 190 | 197 |
| 198 /* | |
| 199 * Returns a [JsFunction] that captures its 'this' binding and calls [f] | |
| 200 * with the value of this passed as the first argument. | |
| 201 */ | |
| 202 factory JsFunction.withThis(Function f) { | |
| 203 var jsFunc = _convertDartFunction(f, captureThis: true); | |
| 204 return new JsFunction._fromJs(jsFunc); | |
| 205 } | |
| 206 | |
| 191 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); | 207 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); |
| 192 | 208 |
| 193 dynamic apply(thisArg, [List args]) => | 209 dynamic apply(thisArg, [List args]) => |
| 194 _convertToDart(JS('', '#.apply(#, #)', _jsObject, | 210 _convertToDart(JS('', '#.apply(#, #)', _jsObject, |
| 195 _convertToJS(thisArg), | 211 _convertToJS(thisArg), |
| 196 args == null ? null : args.map(_convertToJS).toList())); | 212 args == null ? null : args.map(_convertToJS).toList())); |
| 197 } | 213 } |
| 198 | 214 |
| 199 abstract class Serializable<T> { | 215 abstract class Serializable<T> { |
| 200 T toJs(); | 216 T toJs(); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 272 } | 288 } |
| 273 | 289 |
| 274 Object _getDartProxy(o, String propertyName, createProxy(o)) { | 290 Object _getDartProxy(o, String propertyName, createProxy(o)) { |
| 275 var dartProxy = JS('', '#[#]', o, propertyName); | 291 var dartProxy = JS('', '#[#]', o, propertyName); |
| 276 if (dartProxy == null) { | 292 if (dartProxy == null) { |
| 277 dartProxy = createProxy(o); | 293 dartProxy = createProxy(o); |
| 278 _defineProperty(o, propertyName, dartProxy); | 294 _defineProperty(o, propertyName, dartProxy); |
| 279 } | 295 } |
| 280 return dartProxy; | 296 return dartProxy; |
| 281 } | 297 } |
| OLD | NEW |