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

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

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