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

Side by Side Diff: sdk/lib/js/dartium/js_dartium.dart

Issue 1213873004: Enhance dart:js interop in a backwards compatible manner. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: about to land Created 5 years, 5 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
« no previous file with comments | « sdk/lib/html/dartium/html_dartium.dart ('k') | tests/html/html.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
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:collection' show ListMixin; 90 import 'dart:collection' show ListMixin;
91 import 'dart:nativewrappers'; 91 import 'dart:nativewrappers';
92 import 'dart:math' as math;
93 import 'dart:mirrors' as mirrors;
94
95 // Pretend we are always in checked mode as we aren't interested in users
96 // running Dartium code outside of checked mode.
97 final bool CHECK_JS_INVOCATIONS = true;
98
99 final _allowedMethods = new Map<Symbol, _DeclarationSet>();
100 final _allowedGetters = new Map<Symbol, _DeclarationSet>();
101 final _allowedSetters = new Map<Symbol, _DeclarationSet>();
102
103 final _jsInterfaceTypes = new Set<Type>();
104 Iterable<Type> get jsInterfaceTypes => _jsInterfaceTypes;
105
106 /// A collection of methods where all methods have the same name.
107 /// This class is intended to optimize whether a specific invocation is
108 /// appropritate for at least some of the methods in the collection.
109 class _DeclarationSet {
110 _DeclarationSet() : _members = <mirrors.DeclarationMirror>[];
111
112 static bool _checkType(obj, mirrors.TypeMirror type) {
113 if (obj == null) return true;
114 return mirrors.reflectType(obj.runtimeType).isSubtypeOf(type);
115 }
116
117 /// Returns whether the return [value] has a type is consistent with the
118 /// return type from at least one of the members matching the DeclarationSet.
119 bool _checkReturnType(value) {
120 if (value == null) return true;
121 var valueMirror = mirrors.reflectType(value.runtimeType);
122 for (var member in _members) {
123 if (member is mirrors.VariableMirror || member.isGetter) {
124 // TODO(jacobr): actually check return types for getters that return
125 // function types.
126 return true;
127 } else {
128 if (valueMirror.isSubtypeOf(member.returnType)) return true;
129 }
130 }
131 return false;
132 }
133
134 /**
135 * Check whether the [invocation] is consistent with the [member] mirror.
136 */
137 bool _checkDeclaration(
138 Invocation invocation, mirrors.DeclarationMirror member) {
139 if (member is mirrors.VariableMirror || (member as dynamic).isGetter) {
140 // TODO(jacobr): actually check method types against the function type
141 // returned by the getter or field.
142 return true;
143 }
144 var parameters = (member as dynamic).parameters;
145 var positionalArguments = invocation.positionalArguments;
146 // Too many arguments
147 if (parameters.length < positionalArguments.length) return false;
148 // Too few required arguments.
149 if (parameters.length > positionalArguments.length &&
150 !parameters[positionalArguments.length].isOptional) return false;
151 for (var i = 0; i < positionalArguments.length; i++) {
152 if (parameters[i].isNamed) {
153 // Not enough positional arguments.
154 return false;
155 }
156 if (!_checkType(
157 invocation.positionalArguments[i], parameters[i].type)) return false;
158 }
159 if (invocation.namedArguments.isNotEmpty) {
160 var startNamed;
161 for (startNamed = parameters.length - 1; startNamed >= 0; startNamed--) {
162 if (!parameters[startNamed].isNamed) break;
163 }
164 startNamed++;
165
166 // TODO(jacobr): we are unneccessarily using an O(n^2) algorithm here.
167 // If we have JS APIs with a lange number of named parameters we should
168 // optimize this. Either use a HashSet or invert this, walking over
169 // parameters, querying invocation, and making sure we match
170 //invocation.namedArguments.size keys.
171 for (var name in invocation.namedArguments.keys) {
172 bool match = false;
173 for (var j = startNamed; j < parameters.length; j++) {
174 var p = parameters[j];
175 if (p.simpleName == name) {
176 if (!_checkType(invocation.namedArguments[name],
177 parameters[j].type)) return false;
178 match = true;
179 break;
180 }
181 }
182 if (match == false) return false;
183 }
184 }
185 return true;
186 }
187
188 bool checkInvocation(Invocation invocation) {
189 for (var member in _members) {
190 if (_checkDeclaration(invocation, member)) return true;
191 }
192 return false;
193 }
194
195 void add(mirrors.DeclarationMirror mirror) {
196 _members.add(mirror);
197 }
198
199 final List<mirrors.DeclarationMirror> _members;
200 }
201
202 /**
203 * Temporary method that we hope to remove at some point. This method should
204 * generally only be called by machine generated code.
205 */
206 void registerJsInterfaces(List<Type> classes) {
207 if (_finalized == true) {
208 throw 'JSInterop class registration already finalized';
209 }
210 for (Type type in classes) {
211 if (!_jsInterfaceTypes.add(type)) continue; // Already registered.
212 mirrors.ClassMirror typeMirror = mirrors.reflectType(type);
213 typeMirror.declarations.forEach((symbol, declaration) {
214 if (declaration is mirrors.MethodMirror ||
215 declaration is mirrors.VariableMirror && !declaration.isStatic) {
216 bool treatAsGetter = false;
217 bool treatAsSetter = false;
218 if (declaration is mirrors.VariableMirror) {
219 treatAsGetter = true;
220 if (!declaration.isConst && !declaration.isFinal) {
221 treatAsSetter = true;
222 }
223 } else {
224 if (declaration.isGetter) {
225 treatAsGetter = true;
226 } else if (declaration.isSetter) {
227 treatAsSetter = true;
228 } else if (!declaration.isConstructor) {
229 _allowedMethods
230 .putIfAbsent(symbol, () => new _DeclarationSet())
231 .add(declaration);
232 }
233 }
234 if (treatAsGetter) {
235 _allowedGetters
236 .putIfAbsent(symbol, () => new _DeclarationSet())
237 .add(declaration);
238 _allowedMethods
239 .putIfAbsent(symbol, () => new _DeclarationSet())
240 .add(declaration);
241 }
242 if (treatAsSetter) {
243 _allowedSetters
244 .putIfAbsent(symbol, () => new _DeclarationSet())
245 .add(declaration);
246 }
247 }
248 });
249 }
250 }
251
252 _finalizeJsInterfaces() native "Js_finalizeJsInterfaces";
253
254 /**
255 * Generates a part file defining source code for JsObjectImpl and related
256 * classes. This calass is needed so that type checks for all registered JavaScr ipt
257 * interop classes pass.
258 */
259 String _generateJsObjectImplPart() {
260 Iterable<Type> types = jsInterfaceTypes;
261 var libraryPrefixes = new Map<mirrors.LibraryMirror, String>();
262 var prefixNames = new Set<String>();
263 var sb = new StringBuffer();
264
265 var implements = <String>[];
266 for (var type in types) {
267 mirrors.ClassMirror typeMirror = mirrors.reflectType(type);
268 mirrors.LibraryMirror libraryMirror = typeMirror.owner;
269 var prefixName;
270 if (libraryPrefixes.containsKey(libraryMirror)) {
271 prefixName = libraryPrefixes[libraryMirror];
272 } else {
273 var basePrefixName =
274 mirrors.MirrorSystem.getName(libraryMirror.simpleName);
275 basePrefixName = basePrefixName.replaceAll('.', '_');
276 if (basePrefixName.isEmpty) basePrefixName = "lib";
277 prefixName = basePrefixName;
278 var i = 1;
279 while (prefixNames.contains(prefixName)) {
280 prefixName = '$basePrefixName$i';
281 i++;
282 }
283 prefixNames.add(prefixName);
284 libraryPrefixes[libraryMirror] = prefixName;
285 }
286 implements.add(
287 '${prefixName}.${mirrors.MirrorSystem.getName(typeMirror.simpleName)}');
288 }
289 libraryPrefixes.forEach((libraryMirror, prefix) {
290 sb.writeln('import "${libraryMirror.uri}" as $prefix;');
291 });
292 var implementsClause =
293 implements.isEmpty ? "" : "implements ${implements.join(', ')}";
294 // TODO(jacobr): only certain classes need to be implemented by
295 // Function and Array.
296 sb.write('''
297 class JsObjectImpl extends JsObject $implementsClause {
298 JsObjectImpl.internal() : super.internal();
299 }
300
301 class JsFunctionImpl extends JsFunction $implementsClause {
302 JsFunctionImpl.internal() : super.internal();
303 }
304
305 class JsArrayImpl<E> extends JsArray<E> $implementsClause {
306 JsArrayImpl.internal() : super.internal();
307 }
308 ''');
309 return sb.toString();
310 }
311
312 // Start of block of helper methods facilitating emulating JavaScript Array
313 // methods on Dart List objects passed to JavaScript via JS interop.
314 // TODO(jacobr): match JS more closely.
315 String _toStringJs(obj) => '$obj';
316
317 // TODO(jacobr): this might not exactly match JS semantics but should be
318 // adequate for now.
319 int _toIntJs(obj) {
320 if (obj is int) return obj;
321 if (obj is num) return obj.toInt();
322 return num.parse('$obj'.trim(), (_) => 0).toInt();
323 }
324
325 // TODO(jacobr): this might not exactly match JS semantics but should be
326 // adequate for now.
327 num _toNumJs(obj) {
328 return obj is num ? obj : num.parse('$obj'.trim(), (_) => 0);
329 }
330
331 /// Match the behavior of setting List length in JavaScript with the exception
332 /// that Dart does not distinguish undefined and null.
333 _setListLength(List list, rawlen) {
334 num len = _toNumJs(rawlen);
335 if (len is! int || len < 0) {
336 throw new RangeError("Invalid array length");
337 }
338 if (len > list.length) {
339 _arrayExtend(list, len);
340 } else if (len < list.length) {
341 list.removeRange(len, list.length);
342 }
343 return rawlen;
344 }
345
346 // TODO(jacobr): should we really bother with this method instead of just
347 // shallow copying to a JS array and calling the JavaScript join method?
348 String _arrayJoin(List list, sep) {
349 if (sep == null) {
350 sep = ",";
351 }
352 return list.map((e) => e == null ? "" : e.toString()).join(sep.toString());
353 }
354
355 // TODO(jacobr): should we really bother with this method instead of just
356 // shallow copying to a JS array and using the toString method?
357 String _arrayToString(List list) => _arrayJoin(list, ",");
358
359 int _arrayPush(List list, List args) {
360 for(var e in args) {
361 list.add(e);
362 }
363 return list.length;
364 }
365
366 _arrayPop(List list) {
367 if (list.length > 0) return list.removeLast();
368 }
369
370 // TODO(jacobr): would it be better to just copy input to a JS List
371 // and call Array.concat?
372 List _arrayConcat(List input, List args) {
373 var ret = new List.from(input);
374 for (var e in args) {
375 // TODO(jacobr): technically in ES6 we should use
376 // Symbol.isConcatSpreadable to determine whether call addAll. Once v8
377 // supports it, we can make all Dart classes implementing Iterable
378 // specify isConcatSpreadable and tweak this behavior to allow Iterable.
379 if (e is List) {
380 ret.addAll(e);
381 } else {
382 ret.add(e);
383 }
384 }
385 return ret;
386 }
387
388 List _arraySplice(List input, List args) {
389 int start = 0;
390 if (args.length > 0) {
391 var rawStart = _toIntJs(args[0]);
392 if (rawStart < 0) {
393 start = math.max(0, input.length - rawStart);
394 } else {
395 start = math.min(input.length, rawStart);
396 }
397 }
398 var end = start;
399 if (args.length > 1) {
400 var rawDeleteCount = _toIntJs(args[1]);
401 if (rawDeleteCount < 0) rawDeleteCount = 0;
402 end = math.min(input.length, start + rawDeleteCount);
403 }
404 var replacement = [];
405 var removedElements = input.getRange(start, end).toList();
406 if (args.length > 2) {
407 replacement = args.getRange(2, args.length);
408 }
409 input.replaceRange(start, end, replacement);
410 return removedElements;
411 }
412
413 List _arrayReverse(List l) {
414 for (var i = 0, j = l.length - 1; i < j; i++, j--) {
415 var tmp = l[i];
416 l[i] = l[j];
417 l[j] = tmp;
418 }
419 return l;
420 }
421
422 _arrayShift(List l) {
423 if (l.isEmpty) return null; // Technically we should return undefined.
424 return l.removeAt(0);
425 }
426
427 int _arrayUnshift(List l, List args) {
428 l.insertAll(0, args);
429 return l.length;
430 }
431
432 _arrayExtend(List l, int newLength) {
433 for (var i = l.length; i < newLength; i++) {
434 // TODO(jacobr): we'd really like to add undefined to better match
435 // JavaScript semantics.
436 l.add(null);
437 }
438 }
439
440 List _arraySort(List l, rawCompare) {
441 // TODO(jacobr): alternately we could just copy the Array to JavaScript,
442 // invoke the JS sort method and then copy the result back to Dart.
443 Comparator compare;
444 if (rawCompare == null) {
445 compare = (a, b) => _toStringJs(a).compareTo(_toStringJs(b));
446 } else if (rawCompare is JsFunction) {
447 compare = (a, b) => rawCompare.apply([a, b]);
448 } else {
449 compare = rawCompare;
450 }
451 l.sort(compare);
452 return l;
453 }
454 // End of block of helper methods to emulate JavaScript Array methods on Dart Li st.
455
456 /**
457 * Can be called to provide a predictable point where no more JS interfaces can
458 * be added. Creating an instance of JsObject will also automatically trigger
459 * all JsObjects to be finalized.
460 */
461 void finalizeJsInterfaces() {
462 if (_finalized == true) {
463 throw 'JSInterop class registration already finalized';
464 }
465 _finalizeJsInterfaces();
466 }
92 467
93 JsObject _cachedContext; 468 JsObject _cachedContext;
94 469
95 JsObject get _context native "Js_context_Callback"; 470 JsObject get _context native "Js_context_Callback";
96 471
472 bool get _finalized native "Js_interfacesFinalized_Callback";
473
97 JsObject get context { 474 JsObject get context {
98 if (_cachedContext == null) { 475 if (_cachedContext == null) {
99 _cachedContext = _context; 476 _cachedContext = _context;
100 } 477 }
101 return _cachedContext; 478 return _cachedContext;
102 } 479 }
103 480
104 /** 481 /**
105 * Proxies a JavaScript object to Dart. 482 * Proxies a JavaScript object to Dart.
106 * 483 *
107 * The properties of the JavaScript object are accessible via the `[]` and 484 * The properties of the JavaScript object are accessible via the `[]` and
108 * `[]=` operators. Methods are callable via [callMethod]. 485 * `[]=` operators. Methods are callable via [callMethod].
109 */ 486 */
110 class JsObject extends NativeFieldWrapperClass2 { 487 class JsObject extends NativeFieldWrapperClass2 {
111 JsObject.internal(); 488 JsObject.internal();
112 489
113 /** 490 /**
114 * Constructs a new JavaScript object from [constructor] and returns a proxy 491 * Constructs a new JavaScript object from [constructor] and returns a proxy
115 * to it. 492 * to it.
116 */ 493 */
117 factory JsObject(JsFunction constructor, [List arguments]) => _create(construc tor, arguments); 494 factory JsObject(JsFunction constructor, [List arguments]) =>
495 _create(constructor, arguments);
118 496
119 static JsObject _create(JsFunction constructor, arguments) native "JsObject_co nstructorCallback"; 497 static JsObject _create(
498 JsFunction constructor, arguments) native "JsObject_constructorCallback";
499
500 _buildArgs(Invocation invocation) {
501 if (invocation.namedArguments.isEmpty) {
502 return invocation.positionalArguments;
503 } else {
504 var varArgs = new Map<String, Object>();
505 invocation.namedArguments.forEach((symbol, val) {
506 varArgs[mirrors.MirrorSystem.getName(symbol)] = val;
507 });
508 return invocation.positionalArguments.toList()
509 ..add(new JsObject.jsify(varArgs));
510 }
511 }
120 512
121 /** 513 /**
122 * Constructs a [JsObject] that proxies a native Dart object; _for expert use 514 * Constructs a [JsObject] that proxies a native Dart object; _for expert use
123 * only_. 515 * only_.
124 * 516 *
125 * Use this constructor only if you wish to get access to JavaScript 517 * Use this constructor only if you wish to get access to JavaScript
126 * properties attached to a browser host object, such as a Node or Blob, that 518 * properties attached to a browser host object, such as a Node or Blob, that
127 * is normally automatically converted into a native Dart object. 519 * is normally automatically converted into a native Dart object.
128 * 520 *
129 * An exception will be thrown if [object] either is `null` or has the type 521 * An exception will be thrown if [object] either is `null` or has the type
130 * `bool`, `num`, or `String`. 522 * `bool`, `num`, or `String`.
131 */ 523 */
132 factory JsObject.fromBrowserObject(object) { 524 factory JsObject.fromBrowserObject(object) {
133 if (object is num || object is String || object is bool || object == null) { 525 if (object is num || object is String || object is bool || object == null) {
134 throw new ArgumentError( 526 throw new ArgumentError("object cannot be a num, string, bool, or null");
135 "object cannot be a num, string, bool, or null");
136 } 527 }
137 return _fromBrowserObject(object); 528 return _fromBrowserObject(object);
138 } 529 }
139 530
140 /** 531 /**
141 * Recursively converts a JSON-like collection of Dart objects to a 532 * Recursively converts a JSON-like collection of Dart objects to a
142 * collection of JavaScript objects and returns a [JsObject] proxy to it. 533 * collection of JavaScript objects and returns a [JsObject] proxy to it.
143 * 534 *
144 * [object] must be a [Map] or [Iterable], the contents of which are also 535 * [object] must be a [Map] or [Iterable], the contents of which are also
145 * converted. Maps and Iterables are copied to a new JavaScript object. 536 * converted. Maps and Iterables are copied to a new JavaScript object.
146 * Primitives and other transferrable values are directly converted to their 537 * Primitives and other transferrable values are directly converted to their
147 * JavaScript type, and all other objects are proxied. 538 * JavaScript type, and all other objects are proxied.
148 */ 539 */
149 factory JsObject.jsify(object) { 540 factory JsObject.jsify(object) {
150 if ((object is! Map) && (object is! Iterable)) { 541 if ((object is! Map) && (object is! Iterable)) {
151 throw new ArgumentError("object must be a Map or Iterable"); 542 throw new ArgumentError("object must be a Map or Iterable");
152 } 543 }
153 return _jsify(object); 544 return _jsify(object);
154 } 545 }
155 546
156 static JsObject _jsify(object) native "JsObject_jsify"; 547 static JsObject _jsify(object) native "JsObject_jsify";
157 548
158 static JsObject _fromBrowserObject(object) native "JsObject_fromBrowserObject" ; 549 static JsObject _fromBrowserObject(
550 object) native "JsObject_fromBrowserObject";
159 551
160 /** 552 /**
161 * Returns the value associated with [property] from the proxied JavaScript 553 * Returns the value associated with [property] from the proxied JavaScript
162 * object. 554 * object.
163 * 555 *
164 * The type of [property] must be either [String] or [num]. 556 * The type of [property] must be either [String] or [num].
165 */ 557 */
166 operator[](property) native "JsObject_[]"; 558 operator [](property) native "JsObject_[]";
167 559
168 /** 560 /**
169 * Sets the value associated with [property] on the proxied JavaScript 561 * Sets the value associated with [property] on the proxied JavaScript
170 * object. 562 * object.
171 * 563 *
172 * The type of [property] must be either [String] or [num]. 564 * The type of [property] must be either [String] or [num].
173 */ 565 */
174 operator[]=(property, value) native "JsObject_[]="; 566 operator []=(property, value) native "JsObject_[]=";
175 567
176 int get hashCode native "JsObject_hashCode"; 568 int get hashCode native "JsObject_hashCode";
177 569
178 operator==(other) => other is JsObject && _identityEquality(this, other); 570 operator ==(other) => other is JsObject && _identityEquality(this, other);
179 571
180 static bool _identityEquality(JsObject a, JsObject b) native "JsObject_identit yEquality"; 572 static bool _identityEquality(
573 JsObject a, JsObject b) native "JsObject_identityEquality";
181 574
182 /** 575 /**
183 * Returns `true` if the JavaScript object contains the specified property 576 * Returns `true` if the JavaScript object contains the specified property
184 * either directly or though its prototype chain. 577 * either directly or though its prototype chain.
185 * 578 *
186 * This is the equivalent of the `in` operator in JavaScript. 579 * This is the equivalent of the `in` operator in JavaScript.
187 */ 580 */
188 bool hasProperty(String property) native "JsObject_hasProperty"; 581 bool hasProperty(String property) native "JsObject_hasProperty";
189 582
190 /** 583 /**
191 * Removes [property] from the JavaScript object. 584 * Removes [property] from the JavaScript object.
192 * 585 *
193 * This is the equivalent of the `delete` operator in JavaScript. 586 * This is the equivalent of the `delete` operator in JavaScript.
194 */ 587 */
195 void deleteProperty(String property) native "JsObject_deleteProperty"; 588 void deleteProperty(String property) native "JsObject_deleteProperty";
196 589
197 /** 590 /**
198 * Returns `true` if the JavaScript object has [type] in its prototype chain. 591 * Returns `true` if the JavaScript object has [type] in its prototype chain.
199 * 592 *
200 * This is the equivalent of the `instanceof` operator in JavaScript. 593 * This is the equivalent of the `instanceof` operator in JavaScript.
201 */ 594 */
202 bool instanceof(JsFunction type) native "JsObject_instanceof"; 595 bool instanceof(JsFunction type) native "JsObject_instanceof";
203 596
204 /** 597 /**
205 * Returns the result of the JavaScript objects `toString` method. 598 * Returns the result of the JavaScript objects `toString` method.
206 */ 599 */
207 String toString() { 600 String toString() {
208 try { 601 try {
209 return _toString(); 602 return _toString();
210 } catch(e) { 603 } catch (e) {
211 return super.toString(); 604 return super.toString();
212 } 605 }
213 } 606 }
214 607
215 String _toString() native "JsObject_toString"; 608 String _toString() native "JsObject_toString";
216 609
217 /** 610 /**
218 * Calls [method] on the JavaScript object with the arguments [args] and 611 * Calls [method] on the JavaScript object with the arguments [args] and
219 * returns the result. 612 * returns the result.
220 * 613 *
221 * The type of [method] must be either [String] or [num]. 614 * The type of [method] must be either [String] or [num].
222 */ 615 */
223 callMethod(String method, [List args]) { 616 callMethod(String method, [List args]) {
224 try { 617 try {
225 return _callMethod(method, args); 618 return _callMethod(method, args);
226 } catch(e) { 619 } catch (e) {
227 if (hasProperty(method)) { 620 if (hasProperty(method)) {
228 rethrow; 621 rethrow;
229 } else { 622 } else {
230 throw new NoSuchMethodError(this, new Symbol(method), args, null); 623 throw new NoSuchMethodError(this, new Symbol(method), args, null);
231 } 624 }
232 } 625 }
233 } 626 }
234 627
628 noSuchMethod(Invocation invocation) {
629 throwError() {
630 throw new NoSuchMethodError(this, invocation.memberName,
631 invocation.positionalArguments, invocation.namedArguments);
632 }
633
634 String name = mirrors.MirrorSystem.getName(invocation.memberName);
635 if (invocation.isGetter) {
636 if (CHECK_JS_INVOCATIONS) {
637 var matches = _allowedGetters[invocation.memberName];
638 if (matches == null &&
639 !_allowedMethods.containsKey(invocation.memberName)) {
640 throwError();
641 }
642 var ret = this[name];
643 if (matches != null && matches._checkReturnType(ret)) return ret;
644 if (ret is Function ||
645 (ret is JsFunction /* shouldn't be needed in the future*/) &&
646 _allowedMethods.containsKey(
647 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.
648 throwError();
649 } else {
650 // TODO(jacobr): should we throw if the JavaScript object doesn't have t he property?
651 return this[name];
652 }
653 } else if (invocation.isSetter) {
654 if (CHECK_JS_INVOCATIONS) {
655 var matches = _allowedSetters[invocation.memberName];
656 if (matches == null ||
657 !matches.checkInvocation(invocation)) throwError();
658 }
659 assert(name.endsWith("="));
660 name = name.substring(0, name.length - 1);
661 return this[name] = invocation.positionalArguments.first;
662 } else {
663 // TODO(jacobr): also allow calling getters that look like functions.
664 var matches;
665 if (CHECK_JS_INVOCATIONS) {
666 matches = _allowedMethods[invocation.memberName];
667 if (matches == null ||
668 !matches.checkInvocation(invocation)) throwError();
669 }
670 var ret = this.callMethod(name, _buildArgs(invocation));
671 if (CHECK_JS_INVOCATIONS) {
672 if (!matches._checkReturnType(ret)) throwError();
673 }
674 return ret;
675 }
676 }
677
235 _callMethod(String name, List args) native "JsObject_callMethod"; 678 _callMethod(String name, List args) native "JsObject_callMethod";
236 } 679 }
237 680
238 /** 681 /**
239 * Proxies a JavaScript Function object. 682 * Proxies a JavaScript Function object.
240 */ 683 */
241 class JsFunction extends JsObject { 684 class JsFunction extends JsObject implements Function {
242 JsFunction.internal() : super.internal(); 685 JsFunction.internal() : super.internal();
243 686
244 /** 687 /**
245 * Returns a [JsFunction] that captures its 'this' binding and calls [f] 688 * Returns a [JsFunction] that captures its 'this' binding and calls [f]
246 * with the value of this passed as the first argument. 689 * with the value of this passed as the first argument.
247 */ 690 */
248 factory JsFunction.withThis(Function f) => _withThis(f); 691 factory JsFunction.withThis(Function f) => _withThis(f);
249 692
250 /** 693 /**
251 * Invokes the JavaScript function with arguments [args]. If [thisArg] is 694 * Invokes the JavaScript function with arguments [args]. If [thisArg] is
252 * supplied it is the value of `this` for the invocation. 695 * supplied it is the value of `this` for the invocation.
253 */ 696 */
254 dynamic apply(List args, {thisArg}) native "JsFunction_apply"; 697 dynamic apply(List args, {thisArg}) native "JsFunction_apply";
255 698
699 noSuchMethod(Invocation invocation) {
700 if (invocation.isMethod && invocation.memberName == #call) {
701 return apply(_buildArgs(invocation));
702 }
703 return super.noSuchMethod(invocation);
704 }
705
256 /** 706 /**
257 * Internal only version of apply which uses debugger proxies of Dart objects 707 * Internal only version of apply which uses debugger proxies of Dart objects
258 * rather than opaque handles. This method is private because it cannot be 708 * rather than opaque handles. This method is private because it cannot be
259 * efficiently implemented in Dart2Js so should only be used by internal 709 * efficiently implemented in Dart2Js so should only be used by internal
260 * tools. 710 * tools.
261 */ 711 */
262 _applyDebuggerOnly(List args, {thisArg}) native "JsFunction_applyDebuggerOnly" ; 712 _applyDebuggerOnly(List args,
713 {thisArg}) native "JsFunction_applyDebuggerOnly";
263 714
264 static JsFunction _withThis(Function f) native "JsFunction_withThis"; 715 static JsFunction _withThis(Function f) native "JsFunction_withThis";
265 } 716 }
266 717
267 /** 718 /**
268 * A [List] proxying a JavaScript Array. 719 * A [List] proxying a JavaScript Array.
269 */ 720 */
270 class JsArray<E> extends JsObject with ListMixin<E> { 721 class JsArray<E> extends JsObject with ListMixin<E> {
722 JsArray.internal() : super.internal();
271 723
272 factory JsArray() => _newJsArray(); 724 factory JsArray() => _newJsArray();
273 725
274 static JsArray _newJsArray() native "JsArray_newJsArray"; 726 static JsArray _newJsArray() native "JsArray_newJsArray";
275 727
276 factory JsArray.from(Iterable<E> other) => _newJsArrayFromSafeList(new List.fr om(other)); 728 factory JsArray.from(Iterable<E> other) =>
729 _newJsArrayFromSafeList(new List.from(other));
277 730
278 static JsArray _newJsArrayFromSafeList(List list) native "JsArray_newJsArrayFr omSafeList"; 731 static JsArray _newJsArrayFromSafeList(
732 List list) native "JsArray_newJsArrayFromSafeList";
279 733
280 _checkIndex(int index, {bool insert: false}) { 734 _checkIndex(int index, {bool insert: false}) {
281 int length = insert ? this.length + 1 : this.length; 735 int length = insert ? this.length + 1 : this.length;
282 if (index is int && (index < 0 || index >= length)) { 736 if (index is int && (index < 0 || index >= length)) {
283 throw new RangeError.range(index, 0, length); 737 throw new RangeError.range(index, 0, length);
284 } 738 }
285 } 739 }
286 740
287 _checkRange(int start, int end) { 741 _checkRange(int start, int end) {
288 int cachedLength = this.length; 742 int cachedLength = this.length;
289 if (start < 0 || start > cachedLength) { 743 if (start < 0 || start > cachedLength) {
290 throw new RangeError.range(start, 0, cachedLength); 744 throw new RangeError.range(start, 0, cachedLength);
291 } 745 }
292 if (end < start || end > cachedLength) { 746 if (end < start || end > cachedLength) {
293 throw new RangeError.range(end, start, cachedLength); 747 throw new RangeError.range(end, start, cachedLength);
294 } 748 }
295 } 749 }
296 750
297 // Methods required by ListMixin 751 // Methods required by ListMixin
298 752
299 E operator [](index) { 753 E operator [](index) {
300 if (index is int) { 754 if (index is int) {
301 _checkIndex(index); 755 _checkIndex(index);
302 } 756 }
303 return super[index]; 757 return super[index];
304 } 758 }
305 759
306 void operator []=(index, E value) { 760 void operator []=(index, E value) {
307 if(index is int) { 761 if (index is int) {
308 _checkIndex(index); 762 _checkIndex(index);
309 } 763 }
310 super[index] = value; 764 super[index] = value;
311 } 765 }
312 766
313 int get length native "JsArray_length"; 767 int get length native "JsArray_length";
314 768
315 void set length(int length) { super['length'] = length; } 769 void set length(int length) {
770 super['length'] = length;
771 }
316 772
317 // Methods overriden for better performance 773 // Methods overriden for better performance
318 774
319 void add(E value) { 775 void add(E value) {
320 callMethod('push', [value]); 776 callMethod('push', [value]);
321 } 777 }
322 778
323 void addAll(Iterable<E> iterable) { 779 void addAll(Iterable<E> iterable) {
324 // TODO(jacobr): this can be optimized slightly. 780 // TODO(jacobr): this can be optimized slightly.
325 callMethod('push', new List.from(iterable)); 781 callMethod('push', new List.from(iterable));
326 } 782 }
327 783
328 void insert(int index, E element) { 784 void insert(int index, E element) {
329 _checkIndex(index, insert:true); 785 _checkIndex(index, insert: true);
330 callMethod('splice', [index, 0, element]); 786 callMethod('splice', [index, 0, element]);
331 } 787 }
332 788
333 E removeAt(int index) { 789 E removeAt(int index) {
334 _checkIndex(index); 790 _checkIndex(index);
335 return callMethod('splice', [index, 1])[0]; 791 return callMethod('splice', [index, 1])[0];
336 } 792 }
337 793
338 E removeLast() { 794 E removeLast() {
339 if (length == 0) throw new RangeError(-1); 795 if (length == 0) throw new RangeError(-1);
(...skipping 18 matching lines...) Expand all
358 callMethod('sort', [compare]); 814 callMethod('sort', [compare]);
359 } 815 }
360 } 816 }
361 817
362 /** 818 /**
363 * Placeholder object for cases where we need to determine exactly how many 819 * Placeholder object for cases where we need to determine exactly how many
364 * args were passed to a function. 820 * args were passed to a function.
365 */ 821 */
366 const _UNDEFINED = const Object(); 822 const _UNDEFINED = const Object();
367 823
368 // FIXME(jacobr): this method is a hack to work around the lack of proper dart 824 // TODO(jacobr): this method is a hack to work around the lack of proper dart
369 // support for varargs methods. 825 // support for varargs methods.
370 List _stripUndefinedArgs(List args) => 826 List _stripUndefinedArgs(List args) =>
371 args.takeWhile((i) => i != _UNDEFINED).toList(); 827 args.takeWhile((i) => i != _UNDEFINED).toList();
372 828
373 /** 829 /**
374 * Returns a method that can be called with an arbitrary number (for n less 830 * Returns a method that can be called with an arbitrary number (for n less
375 * than 11) of arguments without violating Dart type checks. 831 * than 11) of arguments without violating Dart type checks.
376 */ 832 */
377 Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => 833 Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) =>
378 ([a1=_UNDEFINED, a2=_UNDEFINED, a3=_UNDEFINED, a4=_UNDEFINED, 834 ([a1 = _UNDEFINED, a2 = _UNDEFINED, a3 = _UNDEFINED, a4 = _UNDEFINED,
379 a5=_UNDEFINED, a6=_UNDEFINED, a7=_UNDEFINED, a8=_UNDEFINED, 835 a5 = _UNDEFINED, a6 = _UNDEFINED, a7 = _UNDEFINED, a8 = _UNDEFINED,
380 a9=_UNDEFINED, a10=_UNDEFINED]) => 836 a9 = _UNDEFINED, a10 = _UNDEFINED]) => jsFunction._applyDebuggerOnly(
381 jsFunction._applyDebuggerOnly(_stripUndefinedArgs( 837 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
382 [a1,a2,a3,a4,a5,a6,a7,a8,a9,a10]));
OLDNEW
« no previous file with comments | « sdk/lib/html/dartium/html_dartium.dart ('k') | tests/html/html.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698