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:_foreign_helper' show JS, DART_CLOSURE_TO_JS; | 7 import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS; |
8 import 'dart:_js_helper' show Primitives, convertDartClosureToJS; | 8 import 'dart:_js_helper' show Primitives, convertDartClosureToJS; |
9 | 9 |
10 final JsObject context = new JsObject._fromJs(Primitives.computeGlobalThis()); | 10 final JsObject context = new JsObject._fromJs(Primitives.computeGlobalThis()); |
11 | 11 |
12 JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data); | 12 JsObject jsify(dynamic data) => data == null ? null : new JsObject._json(data); |
13 | 13 |
14 class Callback implements Serializable<JsFunction> { | 14 class Callback implements Serializable<JsFunction> { |
15 final Function _f; // here to allow capture in closure | 15 final Function _f; // here to allow capture in closure |
16 final bool _withThis; // here to allow capture in closure | 16 final bool _withThis; // here to allow capture in closure |
17 dynamic _jsFunction; | 17 dynamic _jsFunction; |
18 | 18 |
19 Callback._(this._f, this._withThis) { | 19 Callback._(this._f, this._withThis) { |
20 _jsFunction = JS('=Object', r''' | 20 _jsFunction = JS('', r''' |
21 (function(){ | 21 (function(){ |
22 var f = #; | 22 var f = #; |
23 return function(){ | 23 return function(){ |
24 return f(this, Array.prototype.slice.apply(arguments)); | 24 return f(this, Array.prototype.slice.apply(arguments)); |
25 }; | 25 }; |
26 }).apply(this)''', convertDartClosureToJS(_call, 2)); | 26 }).apply(this)''', convertDartClosureToJS(_call, 2)); |
27 } | 27 } |
28 | 28 |
29 factory Callback(Function f) => new Callback._(f, false); | 29 factory Callback(Function f) => new Callback._(f, false); |
30 factory Callback.withThis(Function f) => new Callback._(f, true); | 30 factory Callback.withThis(Function f) => new Callback._(f, true); |
(...skipping 30 matching lines...) Expand all Loading... | |
61 _callDartFunction(callback, bool captureThis, self, List arguments) { | 61 _callDartFunction(callback, bool captureThis, self, List arguments) { |
62 if (captureThis) { | 62 if (captureThis) { |
63 arguments = [self]..addAll(arguments); | 63 arguments = [self]..addAll(arguments); |
64 } | 64 } |
65 var dartArgs = arguments.map(_convertToDart).toList(); | 65 var dartArgs = arguments.map(_convertToDart).toList(); |
66 return _convertToJS(Function.apply(callback, dartArgs)); | 66 return _convertToJS(Function.apply(callback, dartArgs)); |
67 } | 67 } |
68 | 68 |
69 | 69 |
70 class JsObject implements Serializable<JsObject> { | 70 class JsObject implements Serializable<JsObject> { |
71 // The wrapped JS object. | |
71 final dynamic _jsObject; | 72 final dynamic _jsObject; |
72 | 73 |
73 JsObject._fromJs(this._jsObject) { | 74 JsObject._fromJs(this._jsObject) { |
74 // remember this proxy for the JS object | 75 // Remember this proxy for the JS object |
75 _getDartProxy(_jsObject, _DART_OBJECT_PROPERTY_NAME, (o) => this); | 76 _getDartProxy(_jsObject, _DART_OBJECT_PROPERTY_NAME, (o) => this); |
76 } | 77 } |
77 | 78 |
78 // TODO(vsm): Type constructor as Serializable<JsFunction> when | 79 // TODO(vsm): Type constructor as Serializable<JsFunction> when |
79 // dartbug.com/11854 is fixed. | 80 // dartbug.com/11854 is fixed. |
80 factory JsObject(var constructor, [List arguments]) { | 81 factory JsObject(constructor, [List arguments]) { |
81 final constr = _convertToJS(constructor); | 82 var constr = _convertToJS(constructor); |
82 if (arguments == null) { | 83 if (arguments == null) { |
83 return new JsObject._fromJs(JS('=Object', 'new #()', constr)); | 84 return new JsObject._fromJs(JS('', 'new #()', constr)); |
84 } | 85 } |
85 final args = arguments.map(_convertToJS).toList(); | 86 // The following code solves the problem of invoking a JavaScript |
86 switch (args.length) { | 87 // constructor with an unknown number arguments. |
87 case 0: | 88 // First bind the constructor to the argument list using bind.apply(). |
88 return new JsObject._fromJs(JS('=Object', 'new #()', constr)); | 89 // The first argument to bind() is the binding of 'this', so add 'null' to |
89 case 1: | 90 // the arguments list passed to apply(). |
90 return new JsObject._fromJs(JS('=Object', 'new #(#)', constr, args[0])); | 91 // After that, use the JavaScript 'new' operator which overrides any binding |
91 case 2: | 92 // of 'this' with the new instance. |
92 return new JsObject._fromJs(JS('=Object', 'new #(#,#)', constr, args[0], | 93 var args = [null]..addAll(arguments.map(_convertToJS)); |
93 args[1])); | 94 var factoryFunction = JS('', '#.bind.apply(#, #)', constr, constr, args); |
94 case 3: | 95 // Without this line, calling factoryFunction as a constructor throws |
95 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#)', constr, | 96 JS('String', 'String(#)', factoryFunction); |
96 args[0], args[1], args[2])); | 97 return new JsObject._fromJs(JS('', 'new #()', factoryFunction)); |
97 case 4: | |
98 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#)', constr, | |
99 args[0], args[1], args[2], args[3])); | |
100 case 5: | |
101 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#)', constr, | |
102 args[0], args[1], args[2], args[3], args[4])); | |
103 case 6: | |
104 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#)', constr, | |
105 args[0], args[1], args[2], args[3], args[4], args[5])); | |
106 case 7: | |
107 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#)', | |
108 constr, args[0], args[1], args[2], args[3], args[4], args[5], | |
109 args[6])); | |
110 case 8: | |
111 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#,#)', | |
112 constr, args[0], args[1], args[2], args[3], args[4], args[5], | |
113 args[6], args[7])); | |
114 case 9: | |
115 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#,#,#)', | |
116 constr, args[0], args[1], args[2], args[3], args[4], args[5], | |
117 args[6], args[7], args[8])); | |
118 case 10: | |
119 return new JsObject._fromJs(JS('=Object', 'new #(#,#,#,#,#,#,#,#,#,#)', | |
120 constr, args[0], args[1], args[2], args[3], args[4], args[5], | |
121 args[6], args[7], args[8], args[9])); | |
122 } | |
123 return new JsObject._fromJs(JS('=Object', r'''(function(){ | |
124 var Type = function(){}; | |
125 Type.prototype = #.prototype; | |
126 var instance = new Type(); | |
127 ret = #.apply(instance, #); | |
128 ret = Object(ret) === ret ? ret : instance; | |
129 return ret; | |
130 })()''', constr, constr, args)); | |
131 } | 98 } |
132 | 99 |
133 factory JsObject._json(data) => new JsObject._fromJs(_convertDataTree(data)); | 100 factory JsObject._json(data) => new JsObject._fromJs(_convertDataTree(data)); |
134 | 101 |
135 static _convertDataTree(data) { | 102 static _convertDataTree(data) { |
136 if (data is Map) { | 103 if (data is Map) { |
137 final convertedData = JS('=Object', '{}'); | 104 final convertedData = JS('=Object', '{}'); |
138 for (var key in data.keys) { | 105 for (var key in data.keys) { |
139 JS('=Object', '#[#]=#', convertedData, key, | 106 JS('=Object', '#[#]=#', convertedData, key, |
140 _convertDataTree(data[key])); | 107 _convertDataTree(data[key])); |
141 } | 108 } |
142 return convertedData; | 109 return convertedData; |
143 } else if (data is Iterable) { | 110 } else if (data is Iterable) { |
144 return data.map(_convertDataTree).toList(); | 111 return data.map(_convertDataTree).toList(); |
145 } else { | 112 } else { |
146 return _convertToJS(data); | 113 return _convertToJS(data); |
147 } | 114 } |
148 } | 115 } |
149 | 116 |
150 JsObject toJs() => this; | 117 JsObject toJs() => this; |
151 | 118 |
152 operator[](key) => | 119 /** |
153 _convertToDart(JS('=Object', '#[#]', _convertToJS(this), key)); | 120 * Returns the value associated with [key] from the proxied JavaScript |
154 | 121 * object. |
155 operator[]=(key, value) => JS('void', '#[#]=#', _convertToJS(this), key, | 122 * |
156 _convertToJS(value)); | 123 * [key] must either be a [String] or [int]. |
124 */ | |
125 // TODO(justinfagnani): rename key/name to property | |
126 Object operator[](key) { | |
alexandre.ardhuin
2013/10/13 18:49:41
Returning Object will generate warnings in existin
justinfagnani
2013/10/15 22:18:08
I prefer dynamic too, forgot about the warnings. I
| |
127 if (key is! String && key is! int) { | |
128 throw new ArgumentError("key is not a String or int"); | |
129 } | |
130 return _convertToDart(JS('', '#[#]', _jsObject, key)); | |
131 } | |
132 | |
133 /** | |
134 * Sets the value associated with [key] from the proxied JavaScript | |
135 * object. | |
136 * | |
137 * [key] must either be a [String] or [int]. | |
138 */ | |
139 operator[]=(key, value) { | |
140 if (key is! String && key is! int) { | |
141 throw new ArgumentError("key is not a String or int"); | |
142 } | |
143 JS('', '#[#]=#', _jsObject, key, _convertToJS(value)); | |
144 } | |
157 | 145 |
158 int get hashCode => 0; | 146 int get hashCode => 0; |
159 | 147 |
160 operator==(other) => other is JsObject && | 148 bool operator==(other) => other is JsObject && |
161 JS('bool', '# === #', _convertToJS(this), _convertToJS(other)); | 149 JS('bool', '# === #', _jsObject, other._jsObject); |
162 | 150 |
163 bool hasProperty(String property) => JS('bool', '# in #', property, | 151 bool hasProperty(name) { |
164 _convertToJS(this)); | 152 if (name is! String && name is! int) { |
153 throw new ArgumentError("name is not a String or int"); | |
154 } | |
155 return JS('bool', '# in #', name, _jsObject); | |
156 } | |
165 | 157 |
166 void deleteProperty(String name) { | 158 void deleteProperty(name) { |
167 JS('void', 'delete #[#]', _convertToJS(this), name); | 159 if (name is! String && name is! int) { |
160 throw new ArgumentError("name is not a String or int"); | |
161 } | |
162 JS('bool', 'delete #[#]', _jsObject, name); | |
168 } | 163 } |
169 | 164 |
170 // TODO(vsm): Type type as Serializable<JsFunction> when | 165 // TODO(vsm): Type type as Serializable<JsFunction> when |
171 // dartbug.com/11854 is fixed. | 166 // dartbug.com/11854 is fixed. |
172 bool instanceof(var type) => | 167 bool instanceof(type) { |
173 JS('bool', '# instanceof #', _convertToJS(this), _convertToJS(type)); | 168 return JS('bool', '# instanceof #', _jsObject, _convertToJS(type)); |
169 } | |
174 | 170 |
175 String toString() { | 171 String toString() { |
176 try { | 172 try { |
177 return JS('String', '#.toString()', _convertToJS(this)); | 173 return JS('String', 'String(#)', _jsObject); |
178 } catch(e) { | 174 } catch(e) { |
179 return super.toString(); | 175 return super.toString(); |
180 } | 176 } |
181 } | 177 } |
182 | 178 |
183 callMethod(String name, [List args]) => | 179 Object callMethod(String name, [List args]) { |
alexandre.ardhuin
2013/10/13 18:49:41
Same as operator[](key) about returning Object.
justinfagnani
2013/10/15 22:18:08
Done.
| |
184 _convertToDart(JS('=Object', '#[#].apply(#, #)', _convertToJS(this), name, | 180 if (name is! String && name is! int) { |
185 _convertToJS(this), | 181 throw new ArgumentError("name is not a String or int"); |
186 args == null ? null : args.map(_convertToJS).toList())); | 182 } |
183 return _convertToDart(JS('', '#[#].apply(#, #)', _jsObject, name, | |
184 _jsObject, | |
185 args == null ? null : args.map(_convertToJS).toList())); | |
186 } | |
187 } | 187 } |
188 | 188 |
189 class JsFunction extends JsObject implements Serializable<JsFunction> { | 189 class JsFunction extends JsObject implements Serializable<JsFunction> { |
190 | |
190 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); | 191 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); |
192 | |
191 apply(thisArg, [List args]) => | 193 apply(thisArg, [List args]) => |
192 _convertToDart(JS('=Object', '#.apply(#, #)', _convertToJS(this), | 194 _convertToDart(JS('', '#.apply(#, #)', _jsObject, |
193 _convertToJS(thisArg), | 195 _convertToJS(thisArg), |
194 args == null ? null : args.map(_convertToJS).toList())); | 196 args == null ? null : args.map(_convertToJS).toList())); |
195 } | 197 } |
196 | 198 |
197 abstract class Serializable<T> { | 199 abstract class Serializable<T> { |
198 T toJs(); | 200 T toJs(); |
199 } | 201 } |
200 | 202 |
201 // property added to a Dart object referencing its JS-side DartObject proxy | 203 // property added to a Dart object referencing its JS-side DartObject proxy |
202 const _DART_OBJECT_PROPERTY_NAME = r'_$dart_dartObject'; | 204 const _DART_OBJECT_PROPERTY_NAME = r'_$dart_dartObject'; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
234 // set a property on the JS closure referencing the Dart closure | 236 // set a property on the JS closure referencing the Dart closure |
235 _defineProperty(jsFunction, _DART_CLOSURE_PROPERTY_NAME, o); | 237 _defineProperty(jsFunction, _DART_CLOSURE_PROPERTY_NAME, o); |
236 return jsFunction; | 238 return jsFunction; |
237 }); | 239 }); |
238 } else { | 240 } else { |
239 return _getJsProxy(o, _JS_OBJECT_PROPERTY_NAME, | 241 return _getJsProxy(o, _JS_OBJECT_PROPERTY_NAME, |
240 (o) => JS('', 'new DartObject(#)', o)); | 242 (o) => JS('', 'new DartObject(#)', o)); |
241 } | 243 } |
242 } | 244 } |
243 | 245 |
244 dynamic _getJsProxy(o, String propertyName, createProxy(o)) { | 246 Object _getJsProxy(o, String propertyName, createProxy(o)) { |
245 var jsProxy = JS('', '#[#]', o, propertyName); | 247 var jsProxy = JS('', '#[#]', o, propertyName); |
246 if (jsProxy == null) { | 248 if (jsProxy == null) { |
247 jsProxy = createProxy(o); | 249 jsProxy = createProxy(o); |
248 _defineProperty(o, propertyName, jsProxy); | 250 _defineProperty(o, propertyName, jsProxy); |
249 } | 251 } |
250 return jsProxy; | 252 return jsProxy; |
251 } | 253 } |
252 | 254 |
253 // converts a Dart object to a reference to a native JS object | 255 // converts a Dart object to a reference to a native JS object |
254 // which might be a DartObject JS->Dart proxy | 256 // which might be a DartObject JS->Dart proxy |
255 dynamic _convertToDart(dynamic o) { | 257 Object _convertToDart(o) { |
256 if (JS('bool', '# == null', o)) { | 258 if (JS('bool', '# == null', o) || |
257 return null; | 259 JS('bool', 'typeof # == "string"', o) || |
258 } else if (JS('bool', 'typeof # == "string" || # instanceof String', o, o) || | 260 JS('bool', 'typeof # == "number"', o) || |
259 JS('bool', 'typeof # == "number" || # instanceof Number', o, o) || | 261 JS('bool', 'typeof # == "boolean"', o)) { |
260 JS('bool', 'typeof # == "boolean" || # instanceof Boolean', o, o)) { | |
261 return o; | 262 return o; |
262 } else if (JS('bool', '# instanceof Function', o)) { | 263 } else if (JS('bool', 'typeof # == "function"', o)) { |
263 return _getDartProxy(o, _DART_CLOSURE_PROPERTY_NAME, | 264 return _getDartProxy(o, _DART_CLOSURE_PROPERTY_NAME, |
264 (o) => new JsFunction._fromJs(o)); | 265 (o) => new JsFunction._fromJs(o)); |
265 } else if (JS('bool', '# instanceof DartObject', o)) { | 266 } else if (JS('bool', '#.constructor === DartObject', o)) { |
266 return JS('var', '#.o', o); | 267 return JS('', '#.o', o); |
267 } else { | 268 } else { |
268 return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME, | 269 return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME, |
269 (o) => new JsObject._fromJs(o)); | 270 (o) => new JsObject._fromJs(o)); |
270 } | 271 } |
271 } | 272 } |
272 | 273 |
273 dynamic _getDartProxy(o, String propertyName, createProxy(o)) { | 274 Object _getDartProxy(o, String propertyName, createProxy(o)) { |
274 var dartProxy = JS('', '#[#]', o, propertyName); | 275 var dartProxy = JS('', '#[#]', o, propertyName); |
275 if (dartProxy == null) { | 276 if (dartProxy == null) { |
276 dartProxy = createProxy(o); | 277 dartProxy = createProxy(o); |
277 _defineProperty(o, propertyName, dartProxy); | 278 _defineProperty(o, propertyName, dartProxy); |
278 } | 279 } |
279 return dartProxy; | 280 return dartProxy; |
280 } | 281 } |
OLD | NEW |