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

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

Issue 3008563002: Removed Dartium SDK libs (Closed)
Patch Set: Update to point to dart2js Created 3 years, 3 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/js/dartium/cached_patches.dart ('k') | sdk/lib/js_util/dartium/js_util_dartium.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Support for interoperating with JavaScript.
7 *
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
10 * and invoke JavaScript functions. The library takes care of converting
11 * between Dart and JavaScript objects where possible, or providing proxies if
12 * conversion isn't possible.
13 *
14 * This library does not yet make Dart objects usable from JavaScript, their
15 * methods and proeprties are not accessible, though it does allow Dart
16 * functions to be passed into and called from JavaScript.
17 *
18 * [JsObject] is the core type and represents a proxy of a JavaScript object.
19 * JsObject gives access to the underlying JavaScript objects properties and
20 * methods. `JsObject`s can be acquired by calls to JavaScript, or they can be
21 * created from proxies to JavaScript constructors.
22 *
23 * The top-level getter [context] provides a [JsObject] that represents the
24 * global object in JavaScript, usually `window`.
25 *
26 * The following example shows an alert dialog via a JavaScript call to the
27 * global function `alert()`:
28 *
29 * import 'dart:js';
30 *
31 * main() => context.callMethod('alert', ['Hello from Dart!']);
32 *
33 * This example shows how to create a [JsObject] from a JavaScript constructor
34 * and access its properties:
35 *
36 * import 'dart:js';
37 *
38 * main() {
39 * var object = new JsObject(context['Object']);
40 * object['greeting'] = 'Hello';
41 * object['greet'] = (name) => "${object['greeting']} $name";
42 * var message = object.callMethod('greet', ['JavaScript']);
43 * context['console'].callMethod('log', [message]);
44 * }
45 *
46 * ## Proxying and automatic conversion
47 *
48 * When setting properties on a JsObject or passing arguments to a Javascript
49 * method or function, Dart objects are automatically converted or proxied to
50 * JavaScript objects. When accessing JavaScript properties, or when a Dart
51 * closure is invoked from JavaScript, the JavaScript objects are also
52 * converted to Dart.
53 *
54 * Functions and closures are proxied in such a way that they are callable. A
55 * Dart closure assigned to a JavaScript property is proxied by a function in
56 * JavaScript. A JavaScript function accessed from Dart is proxied by a
57 * [JsFunction], which has a [apply] method to invoke it.
58 *
59 * The following types are transferred directly and not proxied:
60 *
61 * * "Basic" types: `null`, `bool`, `num`, `String`, `DateTime`
62 * * `Blob`
63 * * `Event`
64 * * `HtmlCollection`
65 * * `ImageData`
66 * * `KeyRange`
67 * * `Node`
68 * * `NodeList`
69 * * `TypedData`, including its subclasses like `Int32List`, but _not_
70 * `ByteBuffer`
71 * * `Window`
72 *
73 * ## Converting collections with JsObject.jsify()
74 *
75 * To create a JavaScript collection from a Dart collection use the
76 * [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s
77 * into JavaScript Objects and Arrays.
78 *
79 * The following expression creates a new JavaScript object with the properties
80 * `a` and `b` defined:
81 *
82 * var jsMap = new JsObject.jsify({'a': 1, 'b': 2});
83 *
84 * This expression creates a JavaScript array:
85 *
86 * var jsArray = new JsObject.jsify([1, 2, 3]);
87 */
88 library dart.js;
89
90 import 'dart:collection' show ListMixin;
91 import 'dart:nativewrappers';
92 import 'dart:math' as math;
93 import 'dart:mirrors' as mirrors;
94 import 'dart:html' as html;
95 import 'dart:_blink' as _blink;
96 import 'dart:html_common' as html_common;
97 import 'dart:indexed_db' as indexed_db;
98 import 'dart:typed_data';
99 import 'dart:core';
100
101 import 'cached_patches.dart';
102
103 // Pretend we are always in checked mode as we aren't interested in users
104 // running Dartium code outside of checked mode.
105 @Deprecated("Internal Use Only")
106 final bool CHECK_JS_INVOCATIONS = true;
107
108 final String _DART_RESERVED_NAME_PREFIX = r'JS$';
109 // If a private class is defined to use @JS we need to inject a non-private
110 // class with a name that will not cause collisions in the library so we can
111 // make JSObject implement that interface even though it is in a different
112 // library.
113 final String escapePrivateClassPrefix = r'$JSImplClass23402893498';
114
115 // Exposed to return ArrayBufferView from a TypedArray passed to readPixels.
116 toArrayBufferView(TypedData data) native "Dart_TypedArray_ArrayBufferView";
117
118 String _stripReservedNamePrefix(String name) =>
119 name.startsWith(_DART_RESERVED_NAME_PREFIX)
120 ? name.substring(_DART_RESERVED_NAME_PREFIX.length)
121 : name;
122
123 _buildArgs(Invocation invocation) {
124 if (invocation.namedArguments.isEmpty) {
125 return invocation.positionalArguments;
126 } else {
127 var varArgs = new Map<String, Object>();
128 invocation.namedArguments.forEach((symbol, val) {
129 varArgs[mirrors.MirrorSystem.getName(symbol)] = val;
130 });
131 return invocation.positionalArguments.toList()
132 ..add(JsNative.jsify(varArgs));
133 }
134 }
135
136 final _allowedMethods = new Map<Symbol, _DeclarationSet>();
137 final _allowedGetters = new Map<Symbol, _DeclarationSet>();
138 final _allowedSetters = new Map<Symbol, _DeclarationSet>();
139
140 final _jsInterfaceTypes = new Set<mirrors.ClassMirror>();
141 @Deprecated("Internal Use Only")
142 Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes;
143
144 class _StringLiteralEscape {
145 // Character code constants.
146 static const int BACKSPACE = 0x08;
147 static const int TAB = 0x09;
148 static const int NEWLINE = 0x0a;
149 static const int CARRIAGE_RETURN = 0x0d;
150 static const int FORM_FEED = 0x0c;
151 static const int QUOTE = 0x22;
152 static const int CHAR_$ = 0x24;
153 static const int CHAR_0 = 0x30;
154 static const int BACKSLASH = 0x5c;
155 static const int CHAR_b = 0x62;
156 static const int CHAR_f = 0x66;
157 static const int CHAR_n = 0x6e;
158 static const int CHAR_r = 0x72;
159 static const int CHAR_t = 0x74;
160 static const int CHAR_u = 0x75;
161
162 final StringSink _sink;
163
164 _StringLiteralEscape(this._sink);
165
166 void writeString(String string) {
167 _sink.write(string);
168 }
169
170 void writeStringSlice(String string, int start, int end) {
171 _sink.write(string.substring(start, end));
172 }
173
174 void writeCharCode(int charCode) {
175 _sink.writeCharCode(charCode);
176 }
177
178 /// ('0' + x) or ('a' + x - 10)
179 static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x;
180
181 /// Write, and suitably escape, a string's content as a JSON string literal.
182 void writeStringContent(String s) {
183 // Identical to JSON string literal escaping except that we also escape $.
184 int offset = 0;
185 final int length = s.length;
186 for (int i = 0; i < length; i++) {
187 int charCode = s.codeUnitAt(i);
188 if (charCode > BACKSLASH) continue;
189 if (charCode < 32) {
190 if (i > offset) writeStringSlice(s, offset, i);
191 offset = i + 1;
192 writeCharCode(BACKSLASH);
193 switch (charCode) {
194 case BACKSPACE:
195 writeCharCode(CHAR_b);
196 break;
197 case TAB:
198 writeCharCode(CHAR_t);
199 break;
200 case NEWLINE:
201 writeCharCode(CHAR_n);
202 break;
203 case FORM_FEED:
204 writeCharCode(CHAR_f);
205 break;
206 case CARRIAGE_RETURN:
207 writeCharCode(CHAR_r);
208 break;
209 default:
210 writeCharCode(CHAR_u);
211 writeCharCode(CHAR_0);
212 writeCharCode(CHAR_0);
213 writeCharCode(hexDigit((charCode >> 4) & 0xf));
214 writeCharCode(hexDigit(charCode & 0xf));
215 break;
216 }
217 } else if (charCode == QUOTE ||
218 charCode == BACKSLASH ||
219 charCode == CHAR_$) {
220 if (i > offset) writeStringSlice(s, offset, i);
221 offset = i + 1;
222 writeCharCode(BACKSLASH);
223 writeCharCode(charCode);
224 }
225 }
226 if (offset == 0) {
227 writeString(s);
228 } else if (offset < length) {
229 writeStringSlice(s, offset, length);
230 }
231 }
232
233 /**
234 * Serialize a [num], [String], [bool], [Null], [List] or [Map] value.
235 *
236 * Returns true if the value is one of these types, and false if not.
237 * If a value is both a [List] and a [Map], it's serialized as a [List].
238 */
239 bool writeStringLiteral(String str) {
240 writeString('"');
241 writeStringContent(str);
242 writeString('"');
243 }
244 }
245
246 String _escapeString(String str) {
247 StringBuffer output = new StringBuffer();
248 new _StringLiteralEscape(output)..writeStringLiteral(str);
249 return output.toString();
250 }
251
252 /// A collection of methods where all methods have the same name.
253 /// This class is intended to optimize whether a specific invocation is
254 /// appropriate for at least some of the methods in the collection.
255 class _DeclarationSet {
256 _DeclarationSet() : _members = <mirrors.DeclarationMirror>[];
257
258 static bool _checkType(obj, mirrors.TypeMirror type) {
259 if (obj == null) return true;
260 return mirrors.reflectType(obj.runtimeType).isSubtypeOf(type);
261 }
262
263 /// Returns whether the return [value] has a type is consistent with the
264 /// return type from at least one of the members matching the DeclarationSet.
265 bool _checkReturnType(value) {
266 if (value == null) return true;
267 var valueMirror = mirrors.reflectType(value.runtimeType);
268 for (var member in _members) {
269 if (member is mirrors.VariableMirror || member.isGetter) {
270 // TODO(jacobr): actually check return types for getters that return
271 // function types.
272 return true;
273 } else {
274 if (valueMirror.isSubtypeOf(member.returnType)) return true;
275 }
276 }
277 return false;
278 }
279
280 /**
281 * Check whether the [invocation] is consistent with the [member] mirror.
282 */
283 bool _checkDeclaration(
284 Invocation invocation, mirrors.DeclarationMirror member) {
285 if (member is mirrors.VariableMirror || (member as dynamic).isGetter) {
286 // TODO(jacobr): actually check method types against the function type
287 // returned by the getter or field.
288 return true;
289 }
290 var parameters = (member as dynamic).parameters;
291 var positionalArguments = invocation.positionalArguments;
292 // Too many arguments
293 if (parameters.length < positionalArguments.length) return false;
294 // Too few required arguments.
295 if (parameters.length > positionalArguments.length &&
296 !parameters[positionalArguments.length].isOptional) return false;
297 for (var i = 0; i < positionalArguments.length; i++) {
298 if (parameters[i].isNamed) {
299 // Not enough positional arguments.
300 return false;
301 }
302 if (!_checkType(invocation.positionalArguments[i], parameters[i].type))
303 return false;
304 }
305 if (invocation.namedArguments.isNotEmpty) {
306 var startNamed;
307 for (startNamed = parameters.length - 1; startNamed >= 0; startNamed--) {
308 if (!parameters[startNamed].isNamed) break;
309 }
310 startNamed++;
311
312 // TODO(jacobr): we are unnecessarily using an O(n^2) algorithm here.
313 // If we have JS APIs with a large number of named parameters we should
314 // optimize this. Either use a HashSet or invert this, walking over
315 // parameters, querying invocation, and making sure we match
316 //invocation.namedArguments.size keys.
317 for (var name in invocation.namedArguments.keys) {
318 bool match = false;
319 for (var j = startNamed; j < parameters.length; j++) {
320 var p = parameters[j];
321 if (p.simpleName == name) {
322 if (!_checkType(
323 invocation.namedArguments[name], parameters[j].type))
324 return false;
325 match = true;
326 break;
327 }
328 }
329 if (match == false) return false;
330 }
331 }
332 return true;
333 }
334
335 bool checkInvocation(Invocation invocation) {
336 for (var member in _members) {
337 if (_checkDeclaration(invocation, member)) return true;
338 }
339 return false;
340 }
341
342 void add(mirrors.DeclarationMirror mirror) {
343 _members.add(mirror);
344 }
345
346 final List<mirrors.DeclarationMirror> _members;
347 }
348
349 /**
350 * Temporary method that we hope to remove at some point. This method should
351 * generally only be called by machine generated code.
352 */
353 @Deprecated("Internal Use Only")
354 void registerJsInterfaces([List<Type> classes]) {
355 // This method is now obsolete in Dartium.
356 }
357
358 void _registerJsInterfaces(List<Type> classes) {
359 for (Type type in classes) {
360 mirrors.ClassMirror typeMirror = mirrors.reflectType(type);
361 typeMirror.declarations.forEach((symbol, declaration) {
362 if (declaration is mirrors.MethodMirror ||
363 declaration is mirrors.VariableMirror && !declaration.isStatic) {
364 bool treatAsGetter = false;
365 bool treatAsSetter = false;
366 if (declaration is mirrors.VariableMirror) {
367 treatAsGetter = true;
368 if (!declaration.isConst && !declaration.isFinal) {
369 treatAsSetter = true;
370 }
371 } else {
372 if (declaration.isGetter) {
373 treatAsGetter = true;
374 } else if (declaration.isSetter) {
375 treatAsSetter = true;
376 } else if (!declaration.isConstructor) {
377 _allowedMethods
378 .putIfAbsent(symbol, () => new _DeclarationSet())
379 .add(declaration);
380 }
381 }
382 if (treatAsGetter) {
383 _allowedGetters
384 .putIfAbsent(symbol, () => new _DeclarationSet())
385 .add(declaration);
386 _allowedMethods
387 .putIfAbsent(symbol, () => new _DeclarationSet())
388 .add(declaration);
389 }
390 if (treatAsSetter) {
391 _allowedSetters
392 .putIfAbsent(symbol, () => new _DeclarationSet())
393 .add(declaration);
394 }
395 }
396 });
397 }
398 }
399
400 _finalizeJsInterfaces() native "Js_finalizeJsInterfaces";
401
402 String _getJsName(mirrors.DeclarationMirror mirror) {
403 if (_atJsType != null) {
404 for (var annotation in mirror.metadata) {
405 if (annotation.type.reflectedType == _atJsType) {
406 try {
407 var name = annotation.reflectee.name;
408 return name != null ? name : "";
409 } catch (e) {}
410 }
411 }
412 }
413 return null;
414 }
415
416 bool _isAnonymousClass(mirrors.ClassMirror mirror) {
417 for (var annotation in mirror.metadata) {
418 if (mirrors.MirrorSystem.getName(annotation.type.simpleName) ==
419 "_Anonymous") {
420 mirrors.LibraryMirror library = annotation.type.owner;
421 var uri = library.uri;
422 // make sure the annotation is from package://js
423 if (uri.scheme == 'package' && uri.path == 'js/js.dart') {
424 return true;
425 }
426 }
427 }
428 return false;
429 }
430
431 bool _hasJsName(mirrors.DeclarationMirror mirror) {
432 if (_atJsType != null) {
433 for (var annotation in mirror.metadata) {
434 if (annotation.type.reflectedType == _atJsType) {
435 return true;
436 }
437 }
438 }
439 return false;
440 }
441
442 var _domNameType;
443
444 bool hasDomName(mirrors.DeclarationMirror mirror) {
445 var location = mirror.location;
446 if (location == null || location.sourceUri.scheme != 'dart') return false;
447 for (var annotation in mirror.metadata) {
448 if (mirrors.MirrorSystem.getName(annotation.type.simpleName) == "DomName") {
449 // We can't make sure the annotation is in dart: as Dartium believes it
450 // is file://dart/sdk/lib/html/html_common/metadata.dart
451 // instead of a proper dart: location.
452 return true;
453 }
454 }
455 return false;
456 }
457
458 _getJsMemberName(mirrors.DeclarationMirror mirror) {
459 var name = _getJsName(mirror);
460 return name == null || name.isEmpty
461 ? _stripReservedNamePrefix(_getDeclarationName(mirror))
462 : name;
463 }
464
465 // TODO(jacobr): handle setters correctyl.
466 String _getDeclarationName(mirrors.DeclarationMirror declaration) {
467 var name = mirrors.MirrorSystem.getName(declaration.simpleName);
468 if (declaration is mirrors.MethodMirror && declaration.isSetter) {
469 assert(name.endsWith("="));
470 name = name.substring(0, name.length - 1);
471 }
472 return name;
473 }
474
475 final _JS_LIBRARY_PREFIX = "js_library";
476 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST";
477
478 String _accessJsPath(String path) => _accessJsPathHelper(path.split("."));
479
480 String _accessJsPathHelper(Iterable<String> parts) {
481 var sb = new StringBuffer();
482 sb
483 ..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length)
484 ..write("${_JS_LIBRARY_PREFIX}.context");
485 for (var p in parts) {
486 sb.write(", ${_escapeString(p)})");
487 }
488 return sb.toString();
489 }
490
491 // TODO(jacobr): remove these helpers and add JsNative.setPropertyDotted,
492 // getPropertyDotted, and callMethodDotted helpers that would be simpler
493 // and more efficient.
494 String _accessJsPathSetter(String path) {
495 var parts = path.split(".");
496 return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPathHelper(parts .getRange(0, parts.length - 1))
497 }, ${_escapeString(parts.last)}, v)";
498 }
499
500 String _accessJsPathCallMethodHelper(String path) {
501 var parts = path.split(".");
502 return "${_JS_LIBRARY_PREFIX}.JsNative.callMethod(${_accessJsPathHelper(parts. getRange(0, parts.length - 1))
503 }, ${_escapeString(parts.last)},";
504 }
505
506 @Deprecated("Internal Use Only")
507 void addMemberHelper(
508 mirrors.MethodMirror declaration, String path, StringBuffer sb,
509 {bool isStatic: false, String memberName}) {
510 if (!declaration.isConstructor) {
511 var jsName = _getJsMemberName(declaration);
512 path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName;
513 }
514 var name = memberName != null ? memberName : _getDeclarationName(declaration);
515 if (declaration.isConstructor) {
516 sb.write("factory");
517 } else if (isStatic) {
518 sb.write("static");
519 } else {
520 sb.write("@patch");
521 }
522 sb.write(" ");
523 if (declaration.isGetter) {
524 sb.write("get $name => ${_accessJsPath(path)};");
525 } else if (declaration.isSetter) {
526 sb.write("set $name(v) {\n"
527 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop(v);\n"
528 " return ${_accessJsPathSetter(path)};\n"
529 "}\n");
530 } else {
531 sb.write("$name(");
532 bool hasOptional = false;
533 int i = 0;
534 var args = <String>[];
535 for (var p in declaration.parameters) {
536 assert(!p.isNamed); // TODO(jacobr): throw.
537 assert(!p.hasDefaultValue);
538 if (i > 0) {
539 sb.write(", ");
540 }
541 if (p.isOptional && !hasOptional) {
542 sb.write("[");
543 hasOptional = true;
544 }
545 var arg = "p$i";
546 args.add(arg);
547 sb.write(arg);
548 if (p.isOptional) {
549 sb.write("=${_UNDEFINED_VAR}");
550 }
551 i++;
552 }
553 if (hasOptional) {
554 sb.write("]");
555 }
556 // TODO(jacobr):
557 sb.write(") {\n");
558 for (var arg in args) {
559 sb.write(" ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($arg);\n");
560 }
561 sb.write(" return ");
562 if (declaration.isConstructor) {
563 sb.write("${_JS_LIBRARY_PREFIX}.JsNative.callConstructor(");
564 sb..write(_accessJsPath(path))..write(",");
565 } else {
566 sb.write(_accessJsPathCallMethodHelper(path));
567 }
568 sb.write("[${args.join(",")}]");
569
570 if (hasOptional) {
571 sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()");
572 }
573 sb.write(");");
574 sb.write("}\n");
575 }
576 sb.write("\n");
577 }
578
579 bool _isExternal(mirrors.MethodMirror mirror) {
580 // This try-catch block is a workaround for BUG:24834.
581 try {
582 return mirror.isExternal;
583 } catch (e) {}
584 return false;
585 }
586
587 List<String> _generateExternalMethods(
588 List<String> libraryPaths, bool useCachedPatches) {
589 var staticCodegen = <String>[];
590
591 if (libraryPaths.length == 0) {
592 mirrors.currentMirrorSystem().libraries.forEach((uri, library) {
593 var library_name = "${uri.scheme}:${uri.path}";
594 if (useCachedPatches && cached_patches.containsKey(library_name)) {
595 // Use the pre-generated patch files for DOM dart:nnnn libraries.
596 var patch = cached_patches[library_name];
597 staticCodegen.addAll(patch);
598 } else if (_hasJsName(library)) {
599 // Library marked with @JS
600 _generateLibraryCodegen(uri, library, staticCodegen);
601 } else if (!useCachedPatches) {
602 // Can't use the cached patches file, instead this is a signal to genera te
603 // the patches for this file.
604 _generateLibraryCodegen(uri, library, staticCodegen);
605 }
606 }); // End of library foreach
607 } else {
608 // Used to generate cached_patches.dart file for all IDL generated dart:
609 // files to the WebKit DOM.
610 for (var library_name in libraryPaths) {
611 var parts = library_name.split(':');
612 var uri = new Uri(scheme: parts[0], path: parts[1]);
613 var library = mirrors.currentMirrorSystem().libraries[uri];
614 _generateLibraryCodegen(uri, library, staticCodegen);
615 }
616 }
617
618 return staticCodegen;
619 }
620
621 _generateLibraryCodegen(uri, library, staticCodegen) {
622 // Is it a dart generated library?
623 var dartLibrary = uri.scheme == 'dart';
624
625 var sb = new StringBuffer();
626 String jsLibraryName = _getJsName(library);
627
628 // Sort by patch file by its declaration name.
629 var sortedDeclKeys = library.declarations.keys.toList();
630 sortedDeclKeys.sort((a, b) => mirrors.MirrorSystem
631 .getName(a)
632 .compareTo(mirrors.MirrorSystem.getName(b)));
633
634 sortedDeclKeys.forEach((name) {
635 var declaration = library.declarations[name];
636 if (declaration is mirrors.MethodMirror) {
637 if ((_hasJsName(declaration) || jsLibraryName != null) &&
638 _isExternal(declaration)) {
639 addMemberHelper(declaration, jsLibraryName, sb);
640 }
641 } else if (declaration is mirrors.ClassMirror) {
642 mirrors.ClassMirror clazz = declaration;
643 var isDom = dartLibrary ? hasDomName(clazz) : false;
644 var isJsInterop = _hasJsName(clazz);
645 if (isDom || isJsInterop) {
646 // TODO(jacobr): verify class implements JavaScriptObject.
647 var className = mirrors.MirrorSystem.getName(clazz.simpleName);
648 bool isPrivateUserDefinedClass =
649 className.startsWith('_') && !dartLibrary;
650 var classNameImpl = '${className}Impl';
651 var sbPatch = new StringBuffer();
652 if (isJsInterop) {
653 String jsClassName = _getJsMemberName(clazz);
654
655 jsInterfaceTypes.add(clazz);
656 clazz.declarations.forEach((name, declaration) {
657 if (declaration is! mirrors.MethodMirror ||
658 !_isExternal(declaration)) return;
659 if (declaration.isFactoryConstructor && _isAnonymousClass(clazz)) {
660 sbPatch.write(" factory ${className}(");
661 int i = 0;
662 var args = <String>[];
663 for (var p in declaration.parameters) {
664 args.add(mirrors.MirrorSystem.getName(p.simpleName));
665 i++;
666 }
667 if (args.isNotEmpty) {
668 sbPatch
669 ..write('{')
670 ..write(
671 args.map((name) => '$name:${_UNDEFINED_VAR}').join(", "))
672 ..write('}');
673 }
674 sbPatch.write(") {\n"
675 " var ret = ${_JS_LIBRARY_PREFIX}.JsNative.newObject();\n") ;
676 i = 0;
677 for (var p in declaration.parameters) {
678 assert(p.isNamed); // TODO(jacobr): throw.
679 var name = args[i];
680 var jsName = _stripReservedNamePrefix(
681 mirrors.MirrorSystem.getName(p.simpleName));
682 sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n"
683 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n"
684 " ${_JS_LIBRARY_PREFIX}.JsNative.setProperty(ret, ${_es capeString(jsName)}, $name);\n"
685 " }\n");
686 i++;
687 }
688
689 sbPatch.write(" return ret;"
690 "}\n");
691 } else if (declaration.isConstructor ||
692 declaration.isFactoryConstructor) {
693 sbPatch.write(" ");
694 addMemberHelper(
695 declaration,
696 (jsLibraryName != null && jsLibraryName.isNotEmpty)
697 ? "${jsLibraryName}.${jsClassName}"
698 : jsClassName,
699 sbPatch,
700 isStatic: true,
701 memberName: className);
702 }
703 }); // End of clazz.declarations.forEach
704
705 clazz.staticMembers.forEach((memberName, member) {
706 if (_isExternal(member)) {
707 sbPatch.write(" ");
708 addMemberHelper(
709 member,
710 (jsLibraryName != null && jsLibraryName.isNotEmpty)
711 ? "${jsLibraryName}.${jsClassName}"
712 : jsClassName,
713 sbPatch,
714 isStatic: true);
715 }
716 });
717 }
718 if (isDom) {
719 sbPatch.write(
720 " static Type get instanceRuntimeType => ${classNameImpl};\n");
721 }
722 if (isPrivateUserDefinedClass) {
723 sb.write("""
724 class ${escapePrivateClassPrefix}${className} implements $className {}
725 """);
726 }
727
728 if (sbPatch.isNotEmpty) {
729 var typeVariablesClause = '';
730 if (!clazz.typeVariables.isEmpty) {
731 typeVariablesClause =
732 '<${clazz.typeVariables.map((m) => mirrors.MirrorSystem.getName( m.simpleName)).join(',')}>';
733 }
734 sb.write("""
735 @patch class $className$typeVariablesClause {
736 $sbPatch
737 }
738 """);
739 if (isDom) {
740 sb.write("""
741 class $classNameImpl$typeVariablesClause extends $className implements ${_JS_LIB RARY_PREFIX}.JSObjectInterfacesDom {
742 ${classNameImpl}.internal_() : super.internal_();
743 get runtimeType => $className;
744 toString() => super.toString();
745 }
746 """);
747 }
748 }
749 }
750 }
751 });
752 if (sb.isNotEmpty) {
753 staticCodegen
754 ..add(uri.toString())
755 ..add("${uri}_js_interop_patch.dart")
756 ..add("""
757 import 'dart:js' as ${_JS_LIBRARY_PREFIX};
758
759 /**
760 * Placeholder object for cases where we need to determine exactly how many
761 * args were passed to a function.
762 */
763 const ${_UNDEFINED_VAR} = const Object();
764
765 ${sb}
766 """);
767 }
768 }
769
770 // Remember the @JS type to compare annotation type.
771 var _atJsType = -1;
772
773 void setupJsTypeCache() {
774 // Cache the @JS Type.
775 if (_atJsType == -1) {
776 var uri = new Uri(scheme: "package", path: "js/js.dart");
777 var jsLibrary = mirrors.currentMirrorSystem().libraries[uri];
778 if (jsLibrary != null) {
779 // @ JS used somewhere.
780 var jsDeclaration = jsLibrary.declarations[new Symbol("JS")];
781 _atJsType = jsDeclaration.reflectedType;
782 } else {
783 // @ JS not used in any library.
784 _atJsType = null;
785 }
786 }
787 }
788
789 /**
790 * Generates part files defining source code for JSObjectImpl, all DOM classes
791 * classes. This codegen is needed so that type checks for all registered
792 * JavaScript interop classes pass.
793 * If genCachedPatches is true then the patch files don't exist this is a specia l
794 * signal to generate and emit the patches to stdout to be captured and put into
795 * the file sdk/lib/js/dartium/cached_patches.dart
796 */
797 List<String> _generateInteropPatchFiles(
798 List<String> libraryPaths, genCachedPatches) {
799 // Cache the @JS Type.
800 if (_atJsType == -1) setupJsTypeCache();
801
802 var ret =
803 _generateExternalMethods(libraryPaths, genCachedPatches ? false : true);
804 var libraryPrefixes = new Map<mirrors.LibraryMirror, String>();
805 var prefixNames = new Set<String>();
806 var sb = new StringBuffer();
807
808 var implements = <String>[];
809 var implementsArray = <String>[];
810 var implementsDom = <String>[];
811 var listMirror = mirrors.reflectType(List);
812 var functionMirror = mirrors.reflectType(Function);
813 var jsObjectMirror = mirrors.reflectType(JSObject);
814
815 for (var typeMirror in jsInterfaceTypes) {
816 mirrors.LibraryMirror libraryMirror = typeMirror.owner;
817 var location = libraryMirror.location;
818 var dartLibrary = location != null && location.sourceUri.scheme == 'dart';
819
820 var prefixName;
821 if (libraryPrefixes.containsKey(libraryMirror)) {
822 prefixName = libraryPrefixes[libraryMirror];
823 } else {
824 var basePrefixName =
825 mirrors.MirrorSystem.getName(libraryMirror.simpleName);
826 basePrefixName = basePrefixName.replaceAll('.', '_');
827 if (basePrefixName.isEmpty) basePrefixName = "lib";
828 prefixName = basePrefixName;
829 var i = 1;
830 while (prefixNames.contains(prefixName)) {
831 prefixName = '$basePrefixName$i';
832 i++;
833 }
834 prefixNames.add(prefixName);
835 libraryPrefixes[libraryMirror] = prefixName;
836 }
837 var isArray = typeMirror.isSubtypeOf(listMirror);
838 var isFunction = typeMirror.isSubtypeOf(functionMirror);
839 var isJSObject = typeMirror.isSubtypeOf(jsObjectMirror);
840 var className = mirrors.MirrorSystem.getName(typeMirror.simpleName);
841 var isPrivateUserDefinedClass = className.startsWith('_') && !dartLibrary;
842 if (isPrivateUserDefinedClass)
843 className = '${escapePrivateClassPrefix}${className}';
844 var fullName = '${prefixName}.${className}';
845 (isArray ? implementsArray : implements).add(fullName);
846 if (!isArray && !isFunction && !isJSObject) {
847 // For DOM classes we need to be a bit more conservative at tagging them
848 // as implementing JS interop classes risks strange unintended
849 // consequences as unrleated code may have instanceof checks. Checking
850 // for isJSObject ensures we do not accidentally pull in existing
851 // dart:html classes as they all have JSObject as a base class.
852 // Note that methods from these classes can still be called on a
853 // dart:html instance but checked mode type checks will fail. This is
854 // not ideal but is better than causing strange breaks in existing
855 // code that uses dart:html.
856 // TODO(jacobr): consider throwing compile time errors if @JS classes
857 // extend JSObject as that case cannot be safely handled in Dartium.
858 implementsDom.add(fullName);
859 }
860 }
861 libraryPrefixes.forEach((libraryMirror, prefix) {
862 sb.writeln('import "${libraryMirror.uri}" as $prefix;');
863 });
864 buildImplementsClause(classes) =>
865 classes.isEmpty ? "" : "implements ${classes.join(', ')}";
866 var implementsClause = buildImplementsClause(implements);
867 var implementsClauseDom = buildImplementsClause(implementsDom);
868 // TODO(jacobr): only certain classes need to be implemented by
869 // JsFunctionImpl.
870 var allTypes = []..addAll(implements)..addAll(implementsArray);
871 sb.write('''
872 class JSObjectImpl extends JSObject $implementsClause {
873 JSObjectImpl.internal() : super.internal();
874 }
875
876 class JSFunctionImpl extends JSFunction $implementsClause {
877 JSFunctionImpl.internal() : super.internal();
878 }
879
880 class JSArrayImpl extends JSArray ${buildImplementsClause(implementsArray)} {
881 JSArrayImpl.internal() : super.internal();
882 }
883
884 // Interfaces that are safe to slam on all DOM classes.
885 // Adding implementsClause would be risky as it could contain Function which
886 // is likely to break a lot of instanceof checks.
887 abstract class JSObjectInterfacesDom $implementsClauseDom {
888 }
889
890 @patch class JSObject {
891 static Type get instanceRuntimeType => JSObjectImpl;
892 }
893
894 @patch class JSFunction {
895 static Type get instanceRuntimeType => JSFunctionImpl;
896 }
897
898 @patch class JSArray {
899 static Type get instanceRuntimeType => JSArrayImpl;
900 }
901
902 _registerAllJsInterfaces() {
903 _registerJsInterfaces([${allTypes.join(", ")}]);
904 }
905
906 ''');
907 ret..addAll(["dart:js", "JSInteropImpl.dart", sb.toString()]);
908 return ret;
909 }
910
911 // Start of block of helper methods facilitating emulating JavaScript Array
912 // methods on Dart List objects passed to JavaScript via JS interop.
913 // TODO(jacobr): match JS more closely.
914 String _toStringJs(obj) => '$obj';
915
916 // TODO(jacobr): this might not exactly match JS semantics but should be
917 // adequate for now.
918 int _toIntJs(obj) {
919 if (obj is int) return obj;
920 if (obj is num) return obj.toInt();
921 return num.parse('$obj'.trim(), (_) => 0).toInt();
922 }
923
924 // TODO(jacobr): this might not exactly match JS semantics but should be
925 // adequate for now.
926 num _toNumJs(obj) {
927 return obj is num ? obj : num.parse('$obj'.trim(), (_) => 0);
928 }
929
930 /// Match the behavior of setting List length in JavaScript with the exception
931 /// that Dart does not distinguish undefined and null.
932 _setListLength(List list, rawlen) {
933 num len = _toNumJs(rawlen);
934 if (len is! int || len < 0) {
935 throw new RangeError("Invalid array length");
936 }
937 if (len > list.length) {
938 _arrayExtend(list, len);
939 } else if (len < list.length) {
940 list.removeRange(len, list.length);
941 }
942 return rawlen;
943 }
944
945 // TODO(jacobr): should we really bother with this method instead of just
946 // shallow copying to a JS array and calling the JavaScript join method?
947 String _arrayJoin(List list, sep) {
948 if (sep == null) {
949 sep = ",";
950 }
951 return list.map((e) => e == null ? "" : e.toString()).join(sep.toString());
952 }
953
954 // TODO(jacobr): should we really bother with this method instead of just
955 // shallow copying to a JS array and using the toString method?
956 String _arrayToString(List list) => _arrayJoin(list, ",");
957
958 int _arrayPush(List list, List args) {
959 for (var e in args) {
960 list.add(e);
961 }
962 return list.length;
963 }
964
965 _arrayPop(List list) {
966 if (list.length > 0) return list.removeLast();
967 }
968
969 // TODO(jacobr): would it be better to just copy input to a JS List
970 // and call Array.concat?
971 List _arrayConcat(List input, List args) {
972 var ret = new List.from(input);
973 for (var e in args) {
974 // TODO(jacobr): technically in ES6 we should use
975 // Symbol.isConcatSpreadable to determine whether call addAll. Once v8
976 // supports it, we can make all Dart classes implementing Iterable
977 // specify isConcatSpreadable and tweak this behavior to allow Iterable.
978 if (e is List) {
979 ret.addAll(e);
980 } else {
981 ret.add(e);
982 }
983 }
984 return ret;
985 }
986
987 List _arraySplice(List input, List args) {
988 int start = 0;
989 if (args.length > 0) {
990 var rawStart = _toIntJs(args[0]);
991 if (rawStart < 0) {
992 start = math.max(0, input.length - rawStart);
993 } else {
994 start = math.min(input.length, rawStart);
995 }
996 }
997 var end = start;
998 if (args.length > 1) {
999 var rawDeleteCount = _toIntJs(args[1]);
1000 if (rawDeleteCount < 0) rawDeleteCount = 0;
1001 end = math.min(input.length, start + rawDeleteCount);
1002 }
1003 var replacement = [];
1004 var removedElements = input.getRange(start, end).toList();
1005 if (args.length > 2) {
1006 replacement = args.getRange(2, args.length);
1007 }
1008 input.replaceRange(start, end, replacement);
1009 return removedElements;
1010 }
1011
1012 List _arrayReverse(List l) {
1013 for (var i = 0, j = l.length - 1; i < j; i++, j--) {
1014 var tmp = l[i];
1015 l[i] = l[j];
1016 l[j] = tmp;
1017 }
1018 return l;
1019 }
1020
1021 _arrayShift(List l) {
1022 if (l.isEmpty) return null; // Technically we should return undefined.
1023 return l.removeAt(0);
1024 }
1025
1026 int _arrayUnshift(List l, List args) {
1027 l.insertAll(0, args);
1028 return l.length;
1029 }
1030
1031 _arrayExtend(List l, int newLength) {
1032 for (var i = l.length; i < newLength; i++) {
1033 // TODO(jacobr): we'd really like to add undefined to better match
1034 // JavaScript semantics.
1035 l.add(null);
1036 }
1037 }
1038
1039 List _arraySort(List l, rawCompare) {
1040 // TODO(jacobr): alternately we could just copy the Array to JavaScript,
1041 // invoke the JS sort method and then copy the result back to Dart.
1042 Comparator compare;
1043 if (rawCompare == null) {
1044 compare = (a, b) => _toStringJs(a).compareTo(_toStringJs(b));
1045 } else if (rawCompare is JsFunction) {
1046 compare = (a, b) => rawCompare.apply([a, b]);
1047 } else {
1048 compare = rawCompare;
1049 }
1050 l.sort(compare);
1051 return l;
1052 }
1053 // End of block of helper methods to emulate JavaScript Array methods on Dart Li st.
1054
1055 /**
1056 * Can be called to provide a predictable point where no more JS interfaces can
1057 * be added. Creating an instance of JsObject will also automatically trigger
1058 * all JsObjects to be finalized.
1059 */
1060 @Deprecated("Internal Use Only")
1061 void finalizeJsInterfaces() {
1062 if (_finalized == true) {
1063 throw 'JSInterop class registration already finalized';
1064 }
1065 _finalizeJsInterfaces();
1066 }
1067
1068 JsObject _cachedContext;
1069
1070 JsObject get _context native "Js_context_Callback";
1071
1072 bool get _finalized native "Js_interfacesFinalized_Callback";
1073
1074 JsObject get context {
1075 if (_cachedContext == null) {
1076 _cachedContext = _context;
1077 }
1078 return _cachedContext;
1079 }
1080
1081 _lookupType(o, bool isCrossFrame, bool isElement) {
1082 try {
1083 var type = html_common.lookupType(o, isElement);
1084 var typeMirror = mirrors.reflectType(type);
1085 var legacyInteropConvertToNative =
1086 typeMirror.isSubtypeOf(mirrors.reflectType(html.Blob)) ||
1087 typeMirror.isSubtypeOf(mirrors.reflectType(html.Event)) ||
1088 typeMirror.isSubtypeOf(mirrors.reflectType(indexed_db.KeyRange)) ||
1089 typeMirror.isSubtypeOf(mirrors.reflectType(html.ImageData)) ||
1090 typeMirror.isSubtypeOf(mirrors.reflectType(html.Node)) ||
1091 // TypedData is removed from this list as it is converted directly
1092 // rather than flowing through the interceptor code path.
1093 // typeMirror.isSubtypeOf(mirrors.reflectType(typed_data.TypedData)) ||
1094 typeMirror.isSubtypeOf(mirrors.reflectType(html.Window));
1095 if (isCrossFrame &&
1096 !typeMirror.isSubtypeOf(mirrors.reflectType(html.Window))) {
1097 // TODO(jacobr): evaluate using the true cross frame Window class, etc.
1098 // as well as triggering that legacy JS Interop returns raw JsObject
1099 // instances.
1100 legacyInteropConvertToNative = false;
1101 }
1102 return [type, legacyInteropConvertToNative];
1103 } catch (e) {}
1104 return [JSObject.instanceRuntimeType, false];
1105 }
1106
1107 /**
1108 * Base class for both the legacy JsObject class and the modern JSObject class.
1109 * This allows the JsNative utility class tobehave identically whether it is
1110 * called on a JsObject or a JSObject.
1111 */
1112 class _JSObjectBase extends NativeFieldWrapperClass2 {
1113 String _toString() native "JSObject_toString";
1114 _callMethod(String name, List args) native "JSObject_callMethod";
1115 _operator_getter(String property) native "JSObject_[]";
1116 _operator_setter(String property, value) native "JSObject_[]=";
1117 bool _hasProperty(String property) native "JsObject_hasProperty";
1118 bool _instanceof(/*JsFunction|JSFunction*/ type) native "JsObject_instanceof";
1119
1120 int get hashCode native "JSObject_hashCode";
1121 }
1122
1123 /**
1124 * Proxies a JavaScript object to Dart.
1125 *
1126 * The properties of the JavaScript object are accessible via the `[]` and
1127 * `[]=` operators. Methods are callable via [callMethod].
1128 */
1129 class JsObject extends _JSObjectBase {
1130 JsObject.internal();
1131
1132 /**
1133 * Constructs a new JavaScript object from [constructor] and returns a proxy
1134 * to it.
1135 */
1136 factory JsObject(JsFunction constructor, [List arguments]) {
1137 try {
1138 return _create(constructor, arguments);
1139 } catch (e) {
1140 // Re-throw any errors (returned as a string) as a DomException.
1141 throw new html.DomException.jsInterop(e);
1142 }
1143 }
1144
1145 static JsObject _create(JsFunction constructor, arguments)
1146 native "JsObject_constructorCallback";
1147
1148 /**
1149 * Constructs a [JsObject] that proxies a native Dart object; _for expert use
1150 * only_.
1151 *
1152 * Use this constructor only if you wish to get access to JavaScript
1153 * properties attached to a browser host object, such as a Node or Blob, that
1154 * is normally automatically converted into a native Dart object.
1155 *
1156 * An exception will be thrown if [object] either is `null` or has the type
1157 * `bool`, `num`, or `String`.
1158 */
1159 factory JsObject.fromBrowserObject(object) {
1160 if (object is num || object is String || object is bool || object == null) {
1161 throw new ArgumentError("object cannot be a num, string, bool, or null");
1162 }
1163 if (object is JsObject) return object;
1164 return _fromBrowserObject(object);
1165 }
1166
1167 /**
1168 * Recursively converts a JSON-like collection of Dart objects to a
1169 * collection of JavaScript objects and returns a [JsObject] proxy to it.
1170 *
1171 * [object] must be a [Map] or [Iterable], the contents of which are also
1172 * converted. Maps and Iterables are copied to a new JavaScript object.
1173 * Primitives and other transferrable values are directly converted to their
1174 * JavaScript type, and all other objects are proxied.
1175 */
1176 factory JsObject.jsify(object) {
1177 if ((object is! Map) && (object is! Iterable)) {
1178 throw new ArgumentError("object must be a Map or Iterable");
1179 }
1180 return _jsify(object);
1181 }
1182
1183 static JsObject _jsify(object) native "JsObject_jsify";
1184
1185 static JsObject _fromBrowserObject(object)
1186 native "JsObject_fromBrowserObject";
1187
1188 /**
1189 * Returns the value associated with [property] from the proxied JavaScript
1190 * object.
1191 *
1192 * The type of [property] must be either [String] or [num].
1193 */
1194 operator [](property) {
1195 try {
1196 return _operator_getterLegacy(property);
1197 } catch (e) {
1198 // Re-throw any errors (returned as a string) as a DomException.
1199 throw new html.DomException.jsInterop(e);
1200 }
1201 }
1202
1203 _operator_getterLegacy(property) native "JsObject_[]Legacy";
1204
1205 /**
1206 * Sets the value associated with [property] on the proxied JavaScript
1207 * object.
1208 *
1209 * The type of [property] must be either [String] or [num].
1210 */
1211 operator []=(property, value) {
1212 try {
1213 _operator_setterLegacy(property, value);
1214 } catch (e) {
1215 // Re-throw any errors (returned as a string) as a DomException.
1216 throw new html.DomException.jsInterop(e);
1217 }
1218 }
1219
1220 _operator_setterLegacy(property, value) native "JsObject_[]=Legacy";
1221
1222 int get hashCode native "JsObject_hashCode";
1223
1224 operator ==(other) {
1225 if (other is! JsObject && other is! JSObject) return false;
1226 return _identityEquality(this, other);
1227 }
1228
1229 static bool _identityEquality(a, b) native "JsObject_identityEquality";
1230
1231 /**
1232 * Returns `true` if the JavaScript object contains the specified property
1233 * either directly or though its prototype chain.
1234 *
1235 * This is the equivalent of the `in` operator in JavaScript.
1236 */
1237 bool hasProperty(String property) => _hasProperty(property);
1238
1239 /**
1240 * Removes [property] from the JavaScript object.
1241 *
1242 * This is the equivalent of the `delete` operator in JavaScript.
1243 */
1244 void deleteProperty(String property) native "JsObject_deleteProperty";
1245
1246 /**
1247 * Returns `true` if the JavaScript object has [type] in its prototype chain.
1248 *
1249 * This is the equivalent of the `instanceof` operator in JavaScript.
1250 */
1251 bool instanceof(JsFunction type) => _instanceof(type);
1252
1253 /**
1254 * Returns the result of the JavaScript objects `toString` method.
1255 */
1256 String toString() {
1257 try {
1258 return _toString();
1259 } catch (e) {
1260 return super.toString();
1261 }
1262 }
1263
1264 String _toString() native "JsObject_toString";
1265
1266 /**
1267 * Calls [method] on the JavaScript object with the arguments [args] and
1268 * returns the result.
1269 *
1270 * The type of [method] must be either [String] or [num].
1271 */
1272 callMethod(String method, [List args]) {
1273 try {
1274 return _callMethodLegacy(method, args);
1275 } catch (e) {
1276 if (hasProperty(method)) {
1277 // Return a DomException if DOM call returned an error.
1278 throw new html.DomException.jsInterop(e);
1279 } else {
1280 throw new NoSuchMethodError(this, new Symbol(method), args, null);
1281 }
1282 }
1283 }
1284
1285 _callMethodLegacy(String name, List args) native "JsObject_callMethodLegacy";
1286 }
1287
1288 /// Base class for all JS objects used through dart:html and typed JS interop.
1289 @Deprecated("Internal Use Only")
1290 class JSObject extends _JSObjectBase {
1291 JSObject.internal() {}
1292 external static Type get instanceRuntimeType;
1293
1294 /**
1295 * Returns the result of the JavaScript objects `toString` method.
1296 */
1297 String toString() {
1298 try {
1299 return _toString();
1300 } catch (e) {
1301 return super.toString();
1302 }
1303 }
1304
1305 noSuchMethod(Invocation invocation) {
1306 throwError() {
1307 super.noSuchMethod(invocation);
1308 }
1309
1310 String name = _stripReservedNamePrefix(
1311 mirrors.MirrorSystem.getName(invocation.memberName));
1312 argsSafeForTypedInterop(invocation.positionalArguments);
1313 if (invocation.isGetter) {
1314 if (CHECK_JS_INVOCATIONS) {
1315 var matches = _allowedGetters[invocation.memberName];
1316 if (matches == null &&
1317 !_allowedMethods.containsKey(invocation.memberName)) {
1318 throwError();
1319 }
1320 var ret = _operator_getter(name);
1321 if (matches != null) return ret;
1322 if (ret is Function ||
1323 (ret is JsFunction /* shouldn't be needed in the future*/) &&
1324 _allowedMethods.containsKey(invocation.memberName))
1325 return ret; // Warning: we have not bound "this"... we could type chec k on the Function but that is of little value in Dart.
1326 throwError();
1327 } else {
1328 // TODO(jacobr): should we throw if the JavaScript object doesn't have t he property?
1329 return _operator_getter(name);
1330 }
1331 } else if (invocation.isSetter) {
1332 if (CHECK_JS_INVOCATIONS) {
1333 var matches = _allowedSetters[invocation.memberName];
1334 if (matches == null || !matches.checkInvocation(invocation))
1335 throwError();
1336 }
1337 assert(name.endsWith("="));
1338 name = name.substring(0, name.length - 1);
1339 return _operator_setter(name, invocation.positionalArguments.first);
1340 } else {
1341 // TODO(jacobr): also allow calling getters that look like functions.
1342 var matches;
1343 if (CHECK_JS_INVOCATIONS) {
1344 matches = _allowedMethods[invocation.memberName];
1345 if (matches == null || !matches.checkInvocation(invocation))
1346 throwError();
1347 }
1348 var ret = _callMethod(name, _buildArgs(invocation));
1349 if (CHECK_JS_INVOCATIONS) {
1350 if (!matches._checkReturnType(ret)) {
1351 html.window.console.error("Return value for method: ${name} is "
1352 "${ret.runtimeType} which is inconsistent with all typed "
1353 "JS interop definitions for method ${name}.");
1354 }
1355 }
1356 return ret;
1357 }
1358 }
1359 }
1360
1361 @Deprecated("Internal Use Only")
1362 class JSArray extends JSObject with ListMixin {
1363 JSArray.internal() : super.internal();
1364 external static Type get instanceRuntimeType;
1365
1366 // Reuse JsArray_length as length behavior is unchanged.
1367 int get length native "JsArray_length";
1368
1369 set length(int length) {
1370 _operator_setter('length', length);
1371 }
1372
1373 _checkIndex(int index, {bool insert: false}) {
1374 int length = insert ? this.length + 1 : this.length;
1375 if (index is int && (index < 0 || index >= length)) {
1376 throw new RangeError.range(index, 0, length);
1377 }
1378 }
1379
1380 _checkRange(int start, int end) {
1381 int cachedLength = this.length;
1382 if (start < 0 || start > cachedLength) {
1383 throw new RangeError.range(start, 0, cachedLength);
1384 }
1385 if (end < start || end > cachedLength) {
1386 throw new RangeError.range(end, start, cachedLength);
1387 }
1388 }
1389
1390 _indexed_getter(int index) native "JSArray_indexed_getter";
1391 _indexed_setter(int index, o) native "JSArray_indexed_setter";
1392
1393 // Methods required by ListMixin
1394
1395 operator [](index) {
1396 if (index is int) {
1397 _checkIndex(index);
1398 }
1399
1400 return _indexed_getter(index);
1401 }
1402
1403 void operator []=(int index, value) {
1404 _checkIndex(index);
1405 _indexed_setter(index, value);
1406 }
1407 }
1408
1409 @Deprecated("Internal Use Only")
1410 class JSFunction extends JSObject implements Function {
1411 JSFunction.internal() : super.internal();
1412
1413 external static Type get instanceRuntimeType;
1414
1415 call(
1416 [a1 = _UNDEFINED,
1417 a2 = _UNDEFINED,
1418 a3 = _UNDEFINED,
1419 a4 = _UNDEFINED,
1420 a5 = _UNDEFINED,
1421 a6 = _UNDEFINED,
1422 a7 = _UNDEFINED,
1423 a8 = _UNDEFINED,
1424 a9 = _UNDEFINED,
1425 a10 = _UNDEFINED]) {
1426 return _apply(
1427 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
1428 }
1429
1430 noSuchMethod(Invocation invocation) {
1431 if (invocation.isMethod && invocation.memberName == #call) {
1432 return _apply(_buildArgs(invocation));
1433 }
1434 return super.noSuchMethod(invocation);
1435 }
1436
1437 dynamic _apply(List args, {thisArg}) native "JSFunction_apply";
1438
1439 static JSFunction _createWithThis(Function f)
1440 native "JSFunction_createWithThis";
1441 static JSFunction _create(Function f) native "JSFunction_create";
1442 }
1443
1444 // JavaScript interop methods that do not automatically wrap to dart:html types.
1445 // Warning: this API is not exposed to dart:js.
1446 // TODO(jacobr): rename to JSNative and make at least part of this API public.
1447 @Deprecated("Internal Use Only")
1448 class JsNative {
1449 static JSObject jsify(object) native "JSObject_jsify";
1450 static JSObject newObject() native "JSObject_newObject";
1451 static JSArray newArray() native "JSObject_newArray";
1452
1453 static hasProperty(_JSObjectBase o, name) => o._hasProperty(name);
1454 static getProperty(_JSObjectBase o, name) => o._operator_getter(name);
1455 static setProperty(_JSObjectBase o, name, value) =>
1456 o._operator_setter(name, value);
1457 static callMethod(_JSObjectBase o, String method, List args) =>
1458 o._callMethod(method, args);
1459 static instanceof(_JSObjectBase o, /*JsFunction|JSFunction*/ type) =>
1460 o._instanceof(type);
1461 static callConstructor0(_JSObjectBase constructor)
1462 native "JSNative_callConstructor0";
1463 static callConstructor(_JSObjectBase constructor, List args)
1464 native "JSNative_callConstructor";
1465
1466 static toTypedObject(JsObject o) native "JSNative_toTypedObject";
1467
1468 /**
1469 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n ot
1470 * wrapped.
1471 */
1472 static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap";
1473 }
1474
1475 /**
1476 * Proxies a JavaScript Function object.
1477 */
1478 class JsFunction extends JsObject {
1479 JsFunction.internal() : super.internal();
1480
1481 /**
1482 * Returns a [JsFunction] that captures its 'this' binding and calls [f]
1483 * with the value of this passed as the first argument.
1484 */
1485 factory JsFunction.withThis(Function f) => _withThis(f);
1486
1487 /**
1488 * Invokes the JavaScript function with arguments [args]. If [thisArg] is
1489 * supplied it is the value of `this` for the invocation.
1490 */
1491 dynamic apply(List args, {thisArg}) => _apply(args, thisArg: thisArg);
1492
1493 dynamic _apply(List args, {thisArg}) native "JsFunction_apply";
1494
1495 /**
1496 * Internal only version of apply which uses debugger proxies of Dart objects
1497 * rather than opaque handles. This method is private because it cannot be
1498 * efficiently implemented in Dart2Js so should only be used by internal
1499 * tools.
1500 */
1501 _applyDebuggerOnly(List args, {thisArg})
1502 native "JsFunction_applyDebuggerOnly";
1503
1504 static JsFunction _withThis(Function f) native "JsFunction_withThis";
1505 }
1506
1507 /**
1508 * A [List] proxying a JavaScript Array.
1509 */
1510 class JsArray<E> extends JsObject with ListMixin<E> {
1511 JsArray.internal() : super.internal();
1512
1513 factory JsArray() => _newJsArray();
1514
1515 static JsArray _newJsArray() native "JsArray_newJsArray";
1516
1517 factory JsArray.from(Iterable<E> other) =>
1518 _newJsArrayFromSafeList(new List.from(other));
1519
1520 static JsArray _newJsArrayFromSafeList(List list)
1521 native "JsArray_newJsArrayFromSafeList";
1522
1523 _checkIndex(int index, {bool insert: false}) {
1524 int length = insert ? this.length + 1 : this.length;
1525 if (index is int && (index < 0 || index >= length)) {
1526 throw new RangeError.range(index, 0, length);
1527 }
1528 }
1529
1530 _checkRange(int start, int end) {
1531 int cachedLength = this.length;
1532 if (start < 0 || start > cachedLength) {
1533 throw new RangeError.range(start, 0, cachedLength);
1534 }
1535 if (end < start || end > cachedLength) {
1536 throw new RangeError.range(end, start, cachedLength);
1537 }
1538 }
1539
1540 // Methods required by ListMixin
1541
1542 E operator [](index) {
1543 if (index is int) {
1544 _checkIndex(index);
1545 }
1546
1547 return super[index];
1548 }
1549
1550 void operator []=(index, E value) {
1551 if (index is int) {
1552 _checkIndex(index);
1553 }
1554 super[index] = value;
1555 }
1556
1557 int get length native "JsArray_length";
1558
1559 set length(int length) {
1560 super['length'] = length;
1561 }
1562
1563 // Methods overridden for better performance
1564
1565 void add(E value) {
1566 callMethod('push', [value]);
1567 }
1568
1569 void addAll(Iterable<E> iterable) {
1570 // TODO(jacobr): this can be optimized slightly.
1571 callMethod('push', new List.from(iterable));
1572 }
1573
1574 void insert(int index, E element) {
1575 _checkIndex(index, insert: true);
1576 callMethod('splice', [index, 0, element]);
1577 }
1578
1579 E removeAt(int index) {
1580 _checkIndex(index);
1581 return callMethod('splice', [index, 1])[0];
1582 }
1583
1584 E removeLast() {
1585 if (length == 0) throw new RangeError(-1);
1586 return callMethod('pop');
1587 }
1588
1589 void removeRange(int start, int end) {
1590 _checkRange(start, end);
1591 callMethod('splice', [start, end - start]);
1592 }
1593
1594 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
1595 _checkRange(start, end);
1596 int length = end - start;
1597 if (length == 0) return;
1598 if (skipCount < 0) throw new ArgumentError(skipCount);
1599 var args = [start, length]..addAll(iterable.skip(skipCount).take(length));
1600 callMethod('splice', args);
1601 }
1602
1603 void sort([int compare(E a, E b)]) {
1604 callMethod('sort', [compare]);
1605 }
1606 }
1607
1608 /**
1609 * Placeholder object for cases where we need to determine exactly how many
1610 * args were passed to a function.
1611 */
1612 const _UNDEFINED = const Object();
1613
1614 // TODO(jacobr): this method is a hack to work around the lack of proper dart
1615 // support for varargs methods.
1616 List _stripUndefinedArgs(List args) =>
1617 args.takeWhile((i) => i != _UNDEFINED).toList();
1618
1619 /**
1620 * Check that that if [arg] is a [Function] it is safe to pass to JavaScript.
1621 * To make a function safe, call [allowInterop] or [allowInteropCaptureThis].
1622 */
1623 @Deprecated("Internal Use Only")
1624 safeForTypedInterop(arg) {
1625 if (CHECK_JS_INVOCATIONS && arg is Function && arg is! JSFunction) {
1626 throw new ArgumentError(
1627 "Attempt to pass Function '$arg' to JavaScript via without calling allow Interop or allowInteropCaptureThis");
1628 }
1629 }
1630
1631 /**
1632 * Check that that if any elements of [args] are [Function] it is safe to pass
1633 * to JavaScript. To make a function safe, call [allowInterop] or
1634 * [allowInteropCaptureThis].
1635 */
1636 @Deprecated("Internal Use Only")
1637 void argsSafeForTypedInterop(Iterable args) {
1638 for (var arg in args) {
1639 safeForTypedInterop(arg);
1640 }
1641 }
1642
1643 /**
1644 * Returns a method that can be called with an arbitrary number (for n less
1645 * than 11) of arguments without violating Dart type checks.
1646 */
1647 Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => (
1648 [a1 = _UNDEFINED,
1649 a2 = _UNDEFINED,
1650 a3 = _UNDEFINED,
1651 a4 = _UNDEFINED,
1652 a5 = _UNDEFINED,
1653 a6 = _UNDEFINED,
1654 a7 = _UNDEFINED,
1655 a8 = _UNDEFINED,
1656 a9 = _UNDEFINED,
1657 a10 = _UNDEFINED]) =>
1658 jsFunction._applyDebuggerOnly(
1659 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
1660
1661 /// Returns a wrapper around function [f] that can be called from JavaScript
1662 /// using the package:js Dart-JavaScript interop.
1663 ///
1664 /// For performance reasons in Dart2Js, by default Dart functions cannot be
1665 /// passed directly to JavaScript unless this method is called to create
1666 /// a Function compatible with both Dart and JavaScript.
1667 /// Calling this method repeatedly on a function will return the same function.
1668 /// The [Function] returned by this method can be used from both Dart and
1669 /// JavaScript. We may remove the need to call this method completely in the
1670 /// future if Dart2Js is refactored so that its function calling conventions
1671 /// are more compatible with JavaScript.
1672 Function/*=F*/ allowInterop/*<F extends Function>*/(Function/*=F*/ f) {
1673 if (f is JSFunction) {
1674 // The function is already a JSFunction... no need to do anything.
1675 return f;
1676 } else {
1677 return JSFunction._create(f);
1678 }
1679 }
1680
1681 /// Cached JSFunction associated with the Dart function when "this" is
1682 /// captured.
1683 Expando<JSFunction> _interopCaptureThisExpando = new Expando<JSFunction>();
1684
1685 /// Returns a [Function] that when called from JavaScript captures its 'this'
1686 /// binding and calls [f] with the value of this passed as the first argument.
1687 /// When called from Dart, [null] will be passed as the first argument.
1688 ///
1689 /// See the documentation for [allowInterop]. This method should only be used
1690 /// with package:js Dart-JavaScript interop.
1691 JSFunction allowInteropCaptureThis(Function f) {
1692 if (f is JSFunction) {
1693 // Behavior when the function is already a JS function is unspecified.
1694 throw new ArgumentError(
1695 "Function is already a JS function so cannot capture this.");
1696 return f;
1697 } else {
1698 var ret = _interopCaptureThisExpando[f];
1699 if (ret == null) {
1700 // TODO(jacobr): we could optimize this.
1701 ret = JSFunction._createWithThis(f);
1702 _interopCaptureThisExpando[f] = ret;
1703 }
1704 return ret;
1705 }
1706 }
OLDNEW
« no previous file with comments | « sdk/lib/js/dartium/cached_patches.dart ('k') | sdk/lib/js_util/dartium/js_util_dartium.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698