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 |