Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: tool/input_sdk/lib/js/dart2js/js_dart2js.dart

Issue 1200233004: fixes #168, dart:js implementation with a test (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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
11 * between Dart and JavaScript objects where possible, or providing proxies if 11 * between Dart and JavaScript objects where possible, or providing proxies if
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 * `a` and `b` defined: 80 * `a` and `b` defined:
81 * 81 *
82 * var jsMap = new JsObject.jsify({'a': 1, 'b': 2}); 82 * var jsMap = new JsObject.jsify({'a': 1, 'b': 2});
83 * 83 *
84 * This expression creates a JavaScript array: 84 * This expression creates a JavaScript array:
85 * 85 *
86 * var jsArray = new JsObject.jsify([1, 2, 3]); 86 * var jsArray = new JsObject.jsify([1, 2, 3]);
87 */ 87 */
88 library dart.js; 88 library dart.js;
89 89
90 import 'dart:html' show Blob, Event, ImageData, Node, Window;
91 import 'dart:collection' show HashMap, ListMixin; 90 import 'dart:collection' show HashMap, ListMixin;
92 import 'dart:indexed_db' show KeyRange;
93 import 'dart:typed_data' show TypedData;
94 91
95 import 'dart:_foreign_helper' show JS, DART_CLOSURE_TO_JS; 92 import 'dart:_interceptors' as _interceptors show JSArray;
96 import 'dart:_interceptors' show JavaScriptObject, UnknownJavaScriptObject; 93 import 'dart:_js_helper' show JsName, Primitives;
97 import 'dart:_js_helper' show Primitives, convertDartClosureToJS, 94 import 'dart:_foreign_helper' show JS;
98 getIsolateAffinityTag;
99 95
100 final JsObject context = _wrapToDart(JS('', 'self')); 96 final JsObject context = _wrapToDart(JS('', 'dart.global'));
101
102 _convertDartFunction(Function f, {bool captureThis: false}) {
103 return JS('',
104 'function(_call, f, captureThis) {'
105 'return function() {'
106 'return _call(f, captureThis, this, '
107 'Array.prototype.slice.apply(arguments));'
108 '}'
109 '}(#, #, #)', DART_CLOSURE_TO_JS(_callDartFunction), f, captureThis);
110 }
111
112 _callDartFunction(callback, bool captureThis, self, List arguments) {
113 if (captureThis) {
114 arguments = [self]..addAll(arguments);
115 }
116 var dartArgs = new List.from(arguments.map(_convertToDart));
117 return _convertToJS(Function.apply(callback, dartArgs));
118 }
119 97
120 /** 98 /**
121 * Proxies a JavaScript object to Dart. 99 * Proxies a JavaScript object to Dart.
122 * 100 *
123 * The properties of the JavaScript object are accessible via the `[]` and 101 * The properties of the JavaScript object are accessible via the `[]` and
124 * `[]=` operators. Methods are callable via [callMethod]. 102 * `[]=` operators. Methods are callable via [callMethod].
125 */ 103 */
126 class JsObject { 104 class JsObject {
127 // The wrapped JS object. 105 // The wrapped JS object.
128 final dynamic _jsObject; 106 final dynamic _jsObject;
129 107
130 // This shoud only be called from _wrapToDart 108 // This should only be called from _wrapToDart
131 JsObject._fromJs(this._jsObject) { 109 JsObject._fromJs(this._jsObject) {
132 assert(_jsObject != null); 110 assert(_jsObject != null);
133 } 111 }
134 112
135 /** 113 /**
136 * Constructs a new JavaScript object from [constructor] and returns a proxy 114 * Constructs a new JavaScript object from [constructor] and returns a proxy
137 * to it. 115 * to it.
138 */ 116 */
139 factory JsObject(JsFunction constructor, [List arguments]) { 117 factory JsObject(JsFunction constructor, [List arguments]) {
140 var constr = _convertToJS(constructor); 118 var ctor = constructor._jsObject;
141 if (arguments == null) { 119 if (arguments == null) {
142 return _wrapToDart(JS('', 'new #()', constr)); 120 return _wrapToDart(JS('', 'new #()', ctor));
143 } 121 }
144 // The following code solves the problem of invoking a JavaScript 122 return _wrapToDart(JS('', 'new #(...#)', ctor, arguments));
vsm 2015/06/24 23:16:15 Well, that's a lot simpler... :-)
145 // constructor with an unknown number arguments.
146 // First bind the constructor to the argument list using bind.apply().
147 // The first argument to bind() is the binding of 'this', so add 'null' to
148 // the arguments list passed to apply().
149 // After that, use the JavaScript 'new' operator which overrides any binding
150 // of 'this' with the new instance.
151 var args = [null]..addAll(arguments.map(_convertToJS));
152 var factoryFunction = JS('', '#.bind.apply(#, #)', constr, constr, args);
153 // Without this line, calling factoryFunction as a constructor throws
154 JS('String', 'String(#)', factoryFunction);
155 // This could return an UnknownJavaScriptObject, or a native
156 // object for which there is an interceptor
157 var jsObj = JS('JavaScriptObject', 'new #()', factoryFunction);
158
159 return _wrapToDart(jsObj);
160 } 123 }
161 124
162 /** 125 /**
163 * Constructs a [JsObject] that proxies a native Dart object; _for expert use 126 * Constructs a [JsObject] that proxies a native Dart object; _for expert use
164 * only_. 127 * only_.
165 * 128 *
166 * Use this constructor only if you wish to get access to JavaScript 129 * Use this constructor only if you wish to get access to JavaScript
167 * properties attached to a browser host object, such as a Node or Blob, that 130 * properties attached to a browser host object, such as a Node or Blob, that
168 * is normally automatically converted into a native Dart object. 131 * is normally automatically converted into a native Dart object.
169 * 132 *
170 * An exception will be thrown if [object] either is `null` or has the type 133 * An exception will be thrown if [object] either is `null` or has the type
171 * `bool`, `num`, or `String`. 134 * `bool`, `num`, or `String`.
172 */ 135 */
173 factory JsObject.fromBrowserObject(object) { 136 factory JsObject.fromBrowserObject(object) {
174 if (object is num || object is String || object is bool || object == null) { 137 if (object is num || object is String || object is bool || object == null) {
175 throw new ArgumentError( 138 throw new ArgumentError("object cannot be a num, string, bool, or null");
176 "object cannot be a num, string, bool, or null");
177 } 139 }
178 return _wrapToDart(_convertToJS(object)); 140 return _wrapToDart(_convertToJS(object));
179 } 141 }
180 142
181 /** 143 /**
182 * Recursively converts a JSON-like collection of Dart objects to a 144 * Recursively converts a JSON-like collection of Dart objects to a
183 * collection of JavaScript objects and returns a [JsObject] proxy to it. 145 * collection of JavaScript objects and returns a [JsObject] proxy to it.
184 * 146 *
185 * [object] must be a [Map] or [Iterable], the contents of which are also 147 * [object] must be a [Map] or [Iterable], the contents of which are also
186 * converted. Maps and Iterables are copied to a new JavaScript object. 148 * converted. Maps and Iterables are copied to a new JavaScript object.
187 * Primitives and other transferrable values are directly converted to their 149 * Primitives and other transferable values are directly converted to their
188 * JavaScript type, and all other objects are proxied. 150 * JavaScript type, and all other objects are proxied.
189 */ 151 */
190 factory JsObject.jsify(object) { 152 factory JsObject.jsify(object) {
191 if ((object is! Map) && (object is! Iterable)) { 153 if ((object is! Map) && (object is! Iterable)) {
192 throw new ArgumentError("object must be a Map or Iterable"); 154 throw new ArgumentError("object must be a Map or Iterable");
193 } 155 }
194 return _wrapToDart(_convertDataTree(object)); 156 return _wrapToDart(_convertDataTree(object));
195 } 157 }
196 158
197 static _convertDataTree(data) { 159 static _convertDataTree(data) {
198 var _convertedObjects = new HashMap.identity(); 160 var _convertedObjects = new HashMap.identity();
199 161
200 _convert(o) { 162 _convert(o) {
201 if (_convertedObjects.containsKey(o)) { 163 if (_convertedObjects.containsKey(o)) {
202 return _convertedObjects[o]; 164 return _convertedObjects[o];
203 } 165 }
204 if (o is Map) { 166 if (o is Map) {
205 final convertedMap = JS('=Object', '{}'); 167 final convertedMap = JS('', '{}');
206 _convertedObjects[o] = convertedMap; 168 _convertedObjects[o] = convertedMap;
207 for (var key in o.keys) { 169 for (var key in o.keys) {
208 JS('=Object', '#[#]=#', convertedMap, key, _convert(o[key])); 170 JS('', '#[#] = #', convertedMap, key, _convert(o[key]));
vsm 2015/06/24 23:16:15 Should we preserve the '=Object'? We won't use it
Jennifer Messerly 2015/06/25 20:11:27 hmm, I hope not :) Not sure it ever makes sense t
209 } 171 }
210 return convertedMap; 172 return convertedMap;
211 } else if (o is Iterable) { 173 } else if (o is Iterable) {
212 var convertedList = []; 174 var convertedList = [];
213 _convertedObjects[o] = convertedList; 175 _convertedObjects[o] = convertedList;
214 convertedList.addAll(o.map(_convert)); 176 convertedList.addAll(o.map(_convert));
215 return convertedList; 177 return convertedList;
216 } else { 178 } else {
217 return _convertToJS(o); 179 return _convertToJS(o);
218 } 180 }
219 } 181 }
220 182
221 return _convert(data); 183 return _convert(data);
222 } 184 }
223 185
224 /** 186 /**
225 * Returns the value associated with [property] from the proxied JavaScript 187 * Returns the value associated with [property] from the proxied JavaScript
226 * object. 188 * object.
227 * 189 *
228 * The type of [property] must be either [String] or [num]. 190 * The type of [property] must be either [String] or [num].
229 */ 191 */
230 dynamic operator[](property) { 192 dynamic operator [](property) {
231 if (property is! String && property is! num) { 193 if (property is! String && property is! num) {
232 throw new ArgumentError("property is not a String or num"); 194 throw new ArgumentError("property is not a String or num");
233 } 195 }
234 return _convertToDart(JS('', '#[#]', _jsObject, property)); 196 return _convertToDart(JS('', '#[#]', _jsObject, property));
235 } 197 }
236 198
237 /** 199 /**
238 * Sets the value associated with [property] on the proxied JavaScript 200 * Sets the value associated with [property] on the proxied JavaScript
239 * object. 201 * object.
240 * 202 *
241 * The type of [property] must be either [String] or [num]. 203 * The type of [property] must be either [String] or [num].
242 */ 204 */
243 operator[]=(property, value) { 205 operator []=(property, value) {
244 if (property is! String && property is! num) { 206 if (property is! String && property is! num) {
245 throw new ArgumentError("property is not a String or num"); 207 throw new ArgumentError("property is not a String or num");
246 } 208 }
247 JS('', '#[#]=#', _jsObject, property, _convertToJS(value)); 209 JS('', '#[#] = #', _jsObject, property, _convertToJS(value));
248 } 210 }
249 211
250 int get hashCode => 0; 212 int get hashCode => 0;
251 213
252 bool operator==(other) => other is JsObject && 214 bool operator ==(other) =>
253 JS('bool', '# === #', _jsObject, other._jsObject); 215 other is JsObject && JS('bool', '# === #', _jsObject, other._jsObject);
254 216
255 /** 217 /**
256 * Returns `true` if the JavaScript object contains the specified property 218 * Returns `true` if the JavaScript object contains the specified property
257 * either directly or though its prototype chain. 219 * either directly or though its prototype chain.
258 * 220 *
259 * This is the equivalent of the `in` operator in JavaScript. 221 * This is the equivalent of the `in` operator in JavaScript.
260 */ 222 */
261 bool hasProperty(property) { 223 bool hasProperty(property) {
262 if (property is! String && property is! num) { 224 if (property is! String && property is! num) {
263 throw new ArgumentError("property is not a String or num"); 225 throw new ArgumentError("property is not a String or num");
(...skipping 21 matching lines...) Expand all
285 bool instanceof(JsFunction type) { 247 bool instanceof(JsFunction type) {
286 return JS('bool', '# instanceof #', _jsObject, _convertToJS(type)); 248 return JS('bool', '# instanceof #', _jsObject, _convertToJS(type));
287 } 249 }
288 250
289 /** 251 /**
290 * Returns the result of the JavaScript objects `toString` method. 252 * Returns the result of the JavaScript objects `toString` method.
291 */ 253 */
292 String toString() { 254 String toString() {
293 try { 255 try {
294 return JS('String', 'String(#)', _jsObject); 256 return JS('String', 'String(#)', _jsObject);
295 } catch(e) { 257 } catch (e) {
296 return super.toString(); 258 return super.toString();
297 } 259 }
298 } 260 }
299 261
300 /** 262 /**
301 * Calls [method] on the JavaScript object with the arguments [args] and 263 * Calls [method] on the JavaScript object with the arguments [args] and
302 * returns the result. 264 * returns the result.
303 * 265 *
304 * The type of [method] must be either [String] or [num]. 266 * The type of [method] must be either [String] or [num].
305 */ 267 */
306 dynamic callMethod(method, [List args]) { 268 dynamic callMethod(method, [List args]) {
307 if (method is! String && method is! num) { 269 if (method is! String && method is! num) {
308 throw new ArgumentError("method is not a String or num"); 270 throw new ArgumentError("method is not a String or num");
309 } 271 }
310 return _convertToDart(JS('', '#[#].apply(#, #)', _jsObject, method, 272 if (args != null) args = new List.from(args.map(_convertToJS));
311 _jsObject, 273 var fn = JS('', '#[#]', _jsObject, method);
312 args == null ? null : new List.from(args.map(_convertToJS)))); 274 if (!JS('bool', '# instanceof Function', fn)) {
275 throw new NoSuchMethodError(_jsObject, new Symbol(method), args, {});
276 }
277 return _convertToDart(JS('', '#.apply(#, #)', fn, _jsObject, args));
313 } 278 }
314 } 279 }
315 280
316 /** 281 /**
317 * Proxies a JavaScript Function object. 282 * Proxies a JavaScript Function object.
318 */ 283 */
319 class JsFunction extends JsObject { 284 class JsFunction extends JsObject {
320 285
321 /** 286 /**
322 * Returns a [JsFunction] that captures its 'this' binding and calls [f] 287 * Returns a [JsFunction] that captures its 'this' binding and calls [f]
323 * with the value of this passed as the first argument. 288 * with the value of this passed as the first argument.
324 */ 289 */
325 factory JsFunction.withThis(Function f) { 290 factory JsFunction.withThis(Function f) {
326 var jsFunc = _convertDartFunction(f, captureThis: true); 291 return new JsFunction._fromJs(JS('', 'function(/*...arguments*/) {'
327 return new JsFunction._fromJs(jsFunc); 292 ' let args = [#(this)];'
293 ' for (let arg of arguments) {'
294 ' args.push(#(arg));'
295 ' }'
296 ' return #(#(...args));'
297 '}', _convertToDart, _convertToDart, _convertToJS, f));
328 } 298 }
329 299
330 JsFunction._fromJs(jsObject) : super._fromJs(jsObject); 300 JsFunction._fromJs(jsObject) : super._fromJs(jsObject);
331 301
332 /** 302 /**
333 * Invokes the JavaScript function with arguments [args]. If [thisArg] is 303 * Invokes the JavaScript function with arguments [args]. If [thisArg] is
334 * supplied it is the value of `this` for the invocation. 304 * supplied it is the value of `this` for the invocation.
335 */ 305 */
336 dynamic apply(List args, { thisArg }) => 306 dynamic apply(List args, {thisArg}) => _convertToDart(JS('', '#.apply(#, #)',
337 _convertToDart(JS('', '#.apply(#, #)', _jsObject, 307 _jsObject, _convertToJS(thisArg),
338 _convertToJS(thisArg), 308 args == null ? null : new List.from(args.map(_convertToJS))));
339 args == null ? null : new List.from(args.map(_convertToJS))));
340 } 309 }
341 310
342 /** 311 // TODO(jmesserly): this is totally unnecessary in dev_compiler.
343 * A [List] that proxies a JavaScript array. 312 /** A [List] that proxies a JavaScript array. */
344 */
345 class JsArray<E> extends JsObject with ListMixin<E> { 313 class JsArray<E> extends JsObject with ListMixin<E> {
346 314
347 /** 315 /**
348 * Creates a new JavaScript array. 316 * Creates a new JavaScript array.
349 */ 317 */
350 JsArray() : super._fromJs([]); 318 JsArray() : super._fromJs([]);
351 319
352 /** 320 /**
353 * Creates a new JavaScript array and initializes it to the contents of 321 * Creates a new JavaScript array and initializes it to the contents of
354 * [other]. 322 * [other].
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 int get length { 370 int get length {
403 // Check the length honours the List contract. 371 // Check the length honours the List contract.
404 var len = JS('', '#.length', _jsObject); 372 var len = JS('', '#.length', _jsObject);
405 // JavaScript arrays have lengths which are unsigned 32-bit integers. 373 // JavaScript arrays have lengths which are unsigned 32-bit integers.
406 if (JS('bool', 'typeof # === "number" && (# >>> 0) === #', len, len, len)) { 374 if (JS('bool', 'typeof # === "number" && (# >>> 0) === #', len, len, len)) {
407 return JS('int', '#', len); 375 return JS('int', '#', len);
408 } 376 }
409 throw new StateError('Bad JsArray length'); 377 throw new StateError('Bad JsArray length');
410 } 378 }
411 379
412 void set length(int length) { super['length'] = length; } 380 void set length(int length) {
413 381 super['length'] = length;
382 }
414 383
415 // Methods overriden for better performance 384 // Methods overriden for better performance
416 385
417 void add(E value) { 386 void add(E value) {
418 callMethod('push', [value]); 387 callMethod('push', [value]);
419 } 388 }
420 389
421 void addAll(Iterable<E> iterable) { 390 void addAll(Iterable<E> iterable) {
422 var list = (JS('bool', '# instanceof Array', iterable)) 391 var list = (JS('bool', '# instanceof Array', iterable))
423 ? iterable 392 ? iterable
(...skipping 15 matching lines...) Expand all
439 if (length == 0) throw new RangeError(-1); 408 if (length == 0) throw new RangeError(-1);
440 return callMethod('pop'); 409 return callMethod('pop');
441 } 410 }
442 411
443 void removeRange(int start, int end) { 412 void removeRange(int start, int end) {
444 _checkRange(start, end, length); 413 _checkRange(start, end, length);
445 callMethod('splice', [start, end - start]); 414 callMethod('splice', [start, end - start]);
446 } 415 }
447 416
448 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { 417 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
449 _checkRange(start, end, length); 418 _checkRange(start, end, this.length);
450 int length = end - start; 419 int length = end - start;
451 if (length == 0) return; 420 if (length == 0) return;
452 if (skipCount < 0) throw new ArgumentError(skipCount); 421 if (skipCount < 0) throw new ArgumentError(skipCount);
453 var args = [start, length]..addAll(iterable.skip(skipCount).take(length)); 422 var args = [start, length]..addAll(iterable.skip(skipCount).take(length));
454 callMethod('splice', args); 423 callMethod('splice', args);
455 } 424 }
456 425
457 void sort([int compare(E a, E b)]) { 426 void sort([int compare(E a, E b)]) {
458 // Note: arr.sort(null) is a type error in FF 427 // Note: arr.sort(null) is a type error in FF
459 callMethod('sort', compare == null ? [] : [compare]); 428 callMethod('sort', compare == null ? [] : [compare]);
460 } 429 }
461 } 430 }
462 431
463 // property added to a Dart object referencing its JS-side DartObject proxy 432 bool _isBrowserType(o) => JS('bool',
464 final String _DART_OBJECT_PROPERTY_NAME = 433 '# instanceof Blob || '
465 getIsolateAffinityTag(r'_$dart_dartObject'); 434 '# instanceof Event || '
466 final String _DART_CLOSURE_PROPERTY_NAME = 435 '(window.KeyRange && # instanceof KeyRange) || '
467 getIsolateAffinityTag(r'_$dart_dartClosure'); 436 '# instanceof ImageData || '
437 '# instanceof Node || '
438 '(window.TypedData && # instanceof TypedData) || '
439 '# instanceof Window', o, o, o, o, o, o, o);
468 440
469 // property added to a JS object referencing its Dart-side JsObject proxy 441 class _DartObject {
470 const _JS_OBJECT_PROPERTY_NAME = r'_$dart_jsObject'; 442 final _dartObj;
471 const _JS_FUNCTION_PROPERTY_NAME = r'$dart_jsFunction'; 443 _DartObject(this._dartObj);
472
473 bool _defineProperty(o, String name, value) {
474 if (_isExtensible(o) &&
475 // TODO(ahe): Calling _hasOwnProperty to work around
476 // https://code.google.com/p/dart/issues/detail?id=21331.
477 !_hasOwnProperty(o, name)) {
478 try {
479 JS('void', 'Object.defineProperty(#, #, { value: #})', o, name, value);
480 return true;
481 } catch (e) {
482 // object is native and lies about being extensible
483 // see https://bugzilla.mozilla.org/show_bug.cgi?id=775185
484 }
485 }
486 return false;
487 } 444 }
488 445
489 bool _hasOwnProperty(o, String name) {
490 return JS('bool', 'Object.prototype.hasOwnProperty.call(#, #)', o, name);
491 }
492
493 bool _isExtensible(o) => JS('bool', 'Object.isExtensible(#)', o);
494
495 Object _getOwnProperty(o, String name) {
496 if (_hasOwnProperty(o, name)) {
497 return JS('', '#[#]', o, name);
498 }
499 return null;
500 }
501
502 bool _isLocalObject(o) => JS('bool', '# instanceof Object', o);
503
504 // The shared constructor function for proxies to Dart objects in JavaScript.
505 final _dartProxyCtor = JS('', 'function DartObject(o) { this.o = o; }');
506
507 dynamic _convertToJS(dynamic o) { 446 dynamic _convertToJS(dynamic o) {
508 // Note: we don't write `if (o == null) return null;` to make sure dart2js 447 if (o == null ||
509 // doesn't convert `return null;` into `return;` (which would make `null` be 448 o is String ||
510 // `undefined` in Javascprit). See dartbug.com/20305 for details. 449 o is num ||
511 if (o == null || o is String || o is num || o is bool) { 450 o is bool ||
512 return o; 451 _isBrowserType(o)) {
513 } else if (o is Blob || o is Event || o is KeyRange || o is ImageData
514 || o is Node || o is TypedData || o is Window) {
515 return o; 452 return o;
516 } else if (o is DateTime) { 453 } else if (o is DateTime) {
517 return Primitives.lazyAsJsDate(o); 454 return Primitives.lazyAsJsDate(o);
518 } else if (o is JsObject) { 455 } else if (o is JsObject) {
519 return o._jsObject; 456 return o._jsObject;
520 } else if (o is Function) { 457 } else if (o is Function) {
521 return _getJsProxy(o, _JS_FUNCTION_PROPERTY_NAME, (o) { 458 return _putIfAbsent(_jsProxies, o, _wrapDartFunction);
522 var jsFunction = _convertDartFunction(o);
523 // set a property on the JS closure referencing the Dart closure
524 _defineProperty(jsFunction, _DART_CLOSURE_PROPERTY_NAME, o);
525 return jsFunction;
526 });
527 } else { 459 } else {
528 var ctor = _dartProxyCtor; 460 // TODO(jmesserly): for now, we wrap other objects, to keep compatibility
529 return _getJsProxy(o, _JS_OBJECT_PROPERTY_NAME, 461 // with the original dart:js behavior.
530 (o) => JS('', 'new #(#)', ctor, o)); 462 return _putIfAbsent(_jsProxies, o, (o) => new _DartObject(o));
531 } 463 }
532 } 464 }
533 465
534 Object _getJsProxy(o, String propertyName, createProxy(o)) { 466 dynamic _wrapDartFunction(f) {
535 var jsProxy = _getOwnProperty(o, propertyName); 467 var wrapper = JS('', 'function(/*...arguments*/) {'
536 if (jsProxy == null) { 468 ' let args = Array.prototype.map.call(arguments, #);'
537 jsProxy = createProxy(o); 469 ' return #(#(...args));'
538 _defineProperty(o, propertyName, jsProxy); 470 '}', _convertToDart, _convertToJS, f);
539 } 471 _dartProxies[wrapper] = f;
540 return jsProxy; 472 return wrapper;
541 } 473 }
542 474
543 // converts a Dart object to a reference to a native JS object 475 // converts a Dart object to a reference to a native JS object
544 // which might be a DartObject JS->Dart proxy 476 // which might be a DartObject JS->Dart proxy
545 Object _convertToDart(o) { 477 Object _convertToDart(o) {
546 if (JS('bool', '# == null', o) || 478 if (JS('bool', '# == null', o) ||
547 JS('bool', 'typeof # == "string"', o) || 479 JS('bool', 'typeof # == "string"', o) ||
548 JS('bool', 'typeof # == "number"', o) || 480 JS('bool', 'typeof # == "number"', o) ||
549 JS('bool', 'typeof # == "boolean"', o)) { 481 JS('bool', 'typeof # == "boolean"', o) ||
482 _isBrowserType(o)) {
550 return o; 483 return o;
551 } else if (_isLocalObject(o)
552 && (o is Blob || o is Event || o is KeyRange || o is ImageData
553 || o is Node || o is TypedData || o is Window)) {
554 // long line: dart2js doesn't allow string concatenation in the JS() form
555 return JS('Blob|Event|KeyRange|ImageData|Node|TypedData|Window', '#', o);
556 } else if (JS('bool', '# instanceof Date', o)) { 484 } else if (JS('bool', '# instanceof Date', o)) {
557 var ms = JS('num', '#.getTime()', o); 485 var ms = JS('num', '#.getTime()', o);
558 return new DateTime.fromMillisecondsSinceEpoch(ms); 486 return new DateTime.fromMillisecondsSinceEpoch(ms);
559 } else if (JS('bool', '#.constructor === #', o, _dartProxyCtor)) { 487 } else if (o is _DartObject) {
560 return JS('', '#.o', o); 488 return o._dartObj;
561 } else { 489 } else {
562 return _wrapToDart(o); 490 return _putIfAbsent(_dartProxies, o, _wrapToDart);
563 } 491 }
564 } 492 }
565 493
566 JsObject _wrapToDart(o) { 494 _wrapToDart(o) {
567 if (JS('bool', 'typeof # == "function"', o)) { 495 if (JS('bool', 'typeof # == "function"', o)) {
568 return _getDartProxy(o, _DART_CLOSURE_PROPERTY_NAME, 496 return new JsFunction._fromJs(o);
569 (o) => new JsFunction._fromJs(o));
570 } else if (JS('bool', '# instanceof Array', o)) {
571 return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME,
572 (o) => new JsArray._fromJs(o));
573 } else {
574 return _getDartProxy(o, _DART_OBJECT_PROPERTY_NAME,
575 (o) => new JsObject._fromJs(o));
576 } 497 }
498 if (JS('bool', '# instanceof Array', o)) {
499 return new JsArray._fromJs(o);
500 }
501 return new JsObject._fromJs(o);
577 } 502 }
578 503
579 Object _getDartProxy(o, String propertyName, createProxy(o)) { 504 final _dartProxies = JS('', 'new WeakMap()');
580 var dartProxy = _getOwnProperty(o, propertyName); 505 final _jsProxies = JS('', 'new WeakMap()');
581 // Temporary fix for dartbug.com/15193 506
582 // In some cases it's possible to see a JavaScript object that 507 Object _putIfAbsent(weakMap, o, getValue(o)) {
583 // came from a different context and was previously proxied to 508 var value = JS('', '#.get(#)', weakMap, o);
584 // Dart in that context. The JS object will have a cached proxy 509 if (value == null) {
585 // but it won't be a valid Dart object in this context. 510 value = getValue(o);
586 // For now we throw away the cached proxy, but we should be able 511 JS('', '#.set(#, #)', weakMap, o, value);
587 // to cache proxies from multiple JS contexts and Dart isolates.
588 if (dartProxy == null || !_isLocalObject(o)) {
589 dartProxy = createProxy(o);
590 _defineProperty(o, propertyName, dartProxy);
591 } 512 }
592 return dartProxy; 513 return value;
593 } 514 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698