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

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

Issue 1583773003: Support JS$ prefix for dart and fix bug where _operator_getter and the [] operator were used incons… (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 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 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 import 'dart:html' as html; 94 import 'dart:html' as html;
95 import 'dart:html_common' as html_common; 95 import 'dart:html_common' as html_common;
96 import 'dart:indexed_db' as indexed_db; 96 import 'dart:indexed_db' as indexed_db;
97 import 'dart:typed_data'; 97 import 'dart:typed_data';
98 98
99 // Pretend we are always in checked mode as we aren't interested in users 99 // Pretend we are always in checked mode as we aren't interested in users
100 // running Dartium code outside of checked mode. 100 // running Dartium code outside of checked mode.
101 @Deprecated("Internal Use Only") 101 @Deprecated("Internal Use Only")
102 final bool CHECK_JS_INVOCATIONS = true; 102 final bool CHECK_JS_INVOCATIONS = true;
103 103
104 final String _DART_RESERVED_NAME_PREFIX = r'JS$';
105
106 String _stripReservedNamePrefix(String name) =>
107 name.startsWith(_DART_RESERVED_NAME_PREFIX)
108 ? name.substring(_DART_RESERVED_NAME_PREFIX.length)
109 : name;
110
111 _buildArgs(Invocation invocation) {
112 if (invocation.namedArguments.isEmpty) {
113 return invocation.positionalArguments;
114 } else {
115 var varArgs = new Map<String, Object>();
116 invocation.namedArguments.forEach((symbol, val) {
117 varArgs[mirrors.MirrorSystem.getName(symbol)] = val;
118 });
119 return invocation.positionalArguments.toList()
120 ..add(maybeWrapTypedInterop(new JsObject.jsify(varArgs)));
121 }
122 }
123
104 final _allowedMethods = new Map<Symbol, _DeclarationSet>(); 124 final _allowedMethods = new Map<Symbol, _DeclarationSet>();
105 final _allowedGetters = new Map<Symbol, _DeclarationSet>(); 125 final _allowedGetters = new Map<Symbol, _DeclarationSet>();
106 final _allowedSetters = new Map<Symbol, _DeclarationSet>(); 126 final _allowedSetters = new Map<Symbol, _DeclarationSet>();
107 127
108 final _jsInterfaceTypes = new Set<mirrors.ClassMirror>(); 128 final _jsInterfaceTypes = new Set<mirrors.ClassMirror>();
109 @Deprecated("Internal Use Only") 129 @Deprecated("Internal Use Only")
110 Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes; 130 Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes;
111 131
112 /// A collection of methods where all methods have the same name. 132 /// A collection of methods where all methods have the same name.
113 /// This class is intended to optimize whether a specific invocation is 133 /// This class is intended to optimize whether a specific invocation is
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 if (uri.scheme == 'package' && uri.path == 'js/js.dart') { 305 if (uri.scheme == 'package' && uri.path == 'js/js.dart') {
286 return true; 306 return true;
287 } 307 }
288 } 308 }
289 } 309 }
290 return false; 310 return false;
291 } 311 }
292 312
293 bool _hasJsName(mirrors.DeclarationMirror mirror) => _getJsName(mirror) != null; 313 bool _hasJsName(mirrors.DeclarationMirror mirror) => _getJsName(mirror) != null;
294 314
315 bool hasDomName(mirrors.DeclarationMirror mirror) {
316 var location = mirror.location;
317 if (location == null || location.sourceUri.scheme != 'dart') return false;
318 for (var annotation in mirror.metadata) {
319 if (mirrors.MirrorSystem.getName(annotation.type.simpleName) == "DomName") {
320 // We can't make sure the annotation is in dart: as Dartium believes it
321 // is file://dart/sdk/lib/html/html_common/metadata.dart
322 // instead of a proper dart: location.
323 return true;
324 }
325 }
326 return false;
327 }
328
295 _getJsMemberName(mirrors.DeclarationMirror mirror) { 329 _getJsMemberName(mirrors.DeclarationMirror mirror) {
296 var name = _getJsName(mirror); 330 var name = _getJsName(mirror);
297 return name == null || name.isEmpty ? _getDeclarationName(mirror) : name; 331 return name == null || name.isEmpty ? _getDeclarationName(mirror) : name;
298 } 332 }
299 333
300 // TODO(jacobr): handle setters correctyl. 334 // TODO(jacobr): handle setters correctyl.
301 String _getDeclarationName(mirrors.DeclarationMirror declaration) { 335 String _getDeclarationName(mirrors.DeclarationMirror declaration) {
302 var name = mirrors.MirrorSystem.getName(declaration.simpleName); 336 var name = mirrors.MirrorSystem.getName(declaration.simpleName);
303 if (declaration is mirrors.MethodMirror && declaration.isSetter) { 337 if (declaration is mirrors.MethodMirror && declaration.isSetter) {
304 assert(name.endsWith("=")); 338 assert(name.endsWith("="));
305 name = name.substring(0, name.length - 1); 339 name = name.substring(0, name.length - 1);
306 } 340 }
307 return name; 341 return _stripReservedNamePrefix(name);
308 } 342 }
309 343
310 final _JS_LIBRARY_PREFIX = "js_library"; 344 final _JS_LIBRARY_PREFIX = "js_library";
311 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; 345 final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST";
312 346
313 String _accessJsPath(String path) => _accessJsPathHelper(path.split(".")); 347 String _accessJsPath(String path) => _accessJsPathHelper(path.split("."));
314 348
315 String _accessJsPathHelper(Iterable<String> parts) { 349 String _accessJsPathHelper(Iterable<String> parts) {
316 var sb = new StringBuffer(); 350 var sb = new StringBuffer();
317 sb 351 sb
(...skipping 22 matching lines...) Expand all
340 var name = memberName != null ? memberName : _getDeclarationName(declaration); 374 var name = memberName != null ? memberName : _getDeclarationName(declaration);
341 if (declaration.isConstructor) { 375 if (declaration.isConstructor) {
342 sb.write("factory"); 376 sb.write("factory");
343 } else if (isStatic) { 377 } else if (isStatic) {
344 sb.write("static"); 378 sb.write("static");
345 } else { 379 } else {
346 sb.write("patch"); 380 sb.write("patch");
347 } 381 }
348 sb.write(" "); 382 sb.write(" ");
349 if (declaration.isGetter) { 383 if (declaration.isGetter) {
350 sb.write("get $name => ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_access JsPath(path)});"); 384 sb.write(
385 "get $name => ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_accessJsPat h(path)});");
351 } else if (declaration.isSetter) { 386 } else if (declaration.isSetter) {
352 sb.write("set $name(v) => ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_acc essJsPathSetter(path)});"); 387 sb.write("set $name(v) {\n"
388 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop(v);\n"
389 " return ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(${_accessJsPathSet ter(path)});\n"
390 "}\n");
353 } else { 391 } else {
354 sb.write("$name("); 392 sb.write("$name(");
355 bool hasOptional = false; 393 bool hasOptional = false;
356 int i = 0; 394 int i = 0;
357 var args = <String>[]; 395 var args = <String>[];
358 for (var p in declaration.parameters) { 396 for (var p in declaration.parameters) {
359 assert(!p.isNamed); // XXX throw 397 assert(!p.isNamed); // TODO(jacobr): throw.
360 assert(!p.hasDefaultValue); 398 assert(!p.hasDefaultValue);
361 if (i > 0) { 399 if (i > 0) {
362 sb.write(", "); 400 sb.write(", ");
363 } 401 }
364 if (p.isOptional && !hasOptional) { 402 if (p.isOptional && !hasOptional) {
365 sb.write("["); 403 sb.write("[");
366 hasOptional = true; 404 hasOptional = true;
367 } 405 }
368 var arg = "p$i"; 406 var arg = "p$i";
369 args.add(arg); 407 args.add(arg);
370 sb.write(arg); 408 sb.write(arg);
371 if (p.isOptional) { 409 if (p.isOptional) {
372 sb.write("=${_UNDEFINED_VAR}"); 410 sb.write("=${_UNDEFINED_VAR}");
373 } 411 }
374 i++; 412 i++;
375 } 413 }
376 if (hasOptional) { 414 if (hasOptional) {
377 sb.write("]"); 415 sb.write("]");
378 } 416 }
379 // TODO(jacobr): 417 // TODO(jacobr):
380 sb.write(") => "); 418 sb.write(") {\n");
381 sb.write('${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop('); 419 for (var arg in args) {
420 sb.write(" ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($arg);\n");
421 }
422 sb.write(" return ${_JS_LIBRARY_PREFIX}.maybeWrapTypedInterop(");
382 if (declaration.isConstructor) { 423 if (declaration.isConstructor) {
383 sb.write("new ${_JS_LIBRARY_PREFIX}.JsObject("); 424 sb.write("new ${_JS_LIBRARY_PREFIX}.JsObject(");
384 } 425 }
385 sb 426 sb
386 ..write(_accessJsPath(path)) 427 ..write(_accessJsPath(path))
387 ..write(declaration.isConstructor ? "," : ".apply(") 428 ..write(declaration.isConstructor ? "," : ".apply(")
388 ..write("[${args.join(",")}]"); 429 ..write("[${args.join(",")}]");
389 430
390 if (hasOptional) { 431 if (hasOptional) {
391 sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()"); 432 sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()");
392 } 433 }
393 sb.write("));"); 434 sb.write("));");
435 sb.write("}\n");
394 } 436 }
395 sb.write("\n"); 437 sb.write("\n");
396 } 438 }
397 439
398 bool _isExternal(mirrors.MethodMirror mirror) { 440 bool _isExternal(mirrors.MethodMirror mirror) {
399 // This try-catch block is a workaround for BUG:24834. 441 // This try-catch block is a workaround for BUG:24834.
400 try { 442 try {
401 return mirror.isExternal; 443 return mirror.isExternal;
402 } catch (e) { } 444 } catch (e) {}
403 return false; 445 return false;
404 } 446 }
405 447
406 List<String> _generateExternalMethods() { 448 List<String> _generateExternalMethods() {
407 var staticCodegen = <String>[]; 449 var staticCodegen = <String>[];
408 mirrors.currentMirrorSystem().libraries.forEach((uri, library) { 450 mirrors.currentMirrorSystem().libraries.forEach((uri, library) {
409 var sb = new StringBuffer(); 451 var sb = new StringBuffer();
410 String jsLibraryName = _getJsName(library); 452 String jsLibraryName = _getJsName(library);
411 library.declarations.forEach((name, declaration) { 453 library.declarations.forEach((name, declaration) {
412 if (declaration is mirrors.MethodMirror) { 454 if (declaration is mirrors.MethodMirror) {
413 if ((_hasJsName(declaration) || jsLibraryName != null) && 455 if ((_hasJsName(declaration) || jsLibraryName != null) &&
414 _isExternal(declaration)) { 456 _isExternal(declaration)) {
415 addMemberHelper(declaration, jsLibraryName, sb); 457 addMemberHelper(declaration, jsLibraryName, sb);
416 } 458 }
417 } else if (declaration is mirrors.ClassMirror) { 459 } else if (declaration is mirrors.ClassMirror) {
418 mirrors.ClassMirror clazz = declaration; 460 mirrors.ClassMirror clazz = declaration;
419 if (_hasJsName(clazz)) { 461 var isDom = hasDomName(clazz);
462 var isJsInterop = _hasJsName(clazz);
463 if (isDom || isJsInterop) {
420 // TODO(jacobr): verify class implements JavaScriptObject. 464 // TODO(jacobr): verify class implements JavaScriptObject.
421 String jsClassName = _getJsMemberName(clazz);
422 var className = mirrors.MirrorSystem.getName(clazz.simpleName); 465 var className = mirrors.MirrorSystem.getName(clazz.simpleName);
466 var classNameImpl = '${className}Impl';
423 var sbPatch = new StringBuffer(); 467 var sbPatch = new StringBuffer();
424 jsInterfaceTypes.add(clazz); 468 if (isJsInterop) {
425 clazz.declarations.forEach((name, declaration) { 469 String jsClassName = _getJsMemberName(clazz);
426 if (declaration is! mirrors.MethodMirror || 470
427 !_isExternal(declaration)) return; 471 jsInterfaceTypes.add(clazz);
428 if (declaration.isFactoryConstructor && _isAnonymousClass(clazz)) { 472 clazz.declarations.forEach((name, declaration) {
429 sbPatch.write(" factory ${className}("); 473 if (declaration is! mirrors.MethodMirror ||
430 int i = 0; 474 !_isExternal(declaration)) return;
431 var args = <String>[]; 475 if (declaration.isFactoryConstructor &&
432 for (var p in declaration.parameters) { 476 _isAnonymousClass(clazz)) {
433 args.add(mirrors.MirrorSystem.getName(p.simpleName)); 477 sbPatch.write(" factory ${className}(");
434 i++; 478 int i = 0;
479 var args = <String>[];
480 for (var p in declaration.parameters) {
481 args.add(mirrors.MirrorSystem.getName(p.simpleName));
482 i++;
483 }
484 if (args.isNotEmpty) {
485 sbPatch
486 ..write('{')
487 ..write(args
488 .map((name) => '$name:${_UNDEFINED_VAR}')
489 .join(", "))
490 ..write('}');
491 }
492 sbPatch.write(") {\n"
493 " var ret = new ${_JS_LIBRARY_PREFIX}.JsObject.jsify({}); \n");
494 i = 0;
495 for (var p in declaration.parameters) {
496 assert(p.isNamed); // TODO(jacobr): throw.
497 var name = args[i];
498 var jsName = _stripReservedNamePrefix(
499 mirrors.MirrorSystem.getName(p.simpleName));
500 sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n"
501 " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n "
502 " ret['$jsName'] = $name;\n"
503 " }\n");
504 i++;
505 }
506
507 sbPatch.write(
508 " return new ${_JS_LIBRARY_PREFIX}.JSObject.create(ret);\ n"
509 " }\n");
510 } else if (declaration.isConstructor ||
511 declaration.isFactoryConstructor) {
512 sbPatch.write(" ");
513 addMemberHelper(
514 declaration,
515 (jsLibraryName != null && jsLibraryName.isNotEmpty)
516 ? "${jsLibraryName}.${jsClassName}"
517 : jsClassName,
518 sbPatch,
519 isStatic: true,
520 memberName: className);
435 } 521 }
436 if (args.isNotEmpty) { 522 });
437 sbPatch 523
438 ..write('{') 524 clazz.staticMembers.forEach((memberName, member) {
439 ..write( 525 if (_isExternal(member)) {
440 args.map((name) => '$name:${_UNDEFINED_VAR}').join(", ")) 526 sbPatch.write(" ");
441 ..write('}'); 527 addMemberHelper(
528 member,
529 (jsLibraryName != null && jsLibraryName.isNotEmpty)
530 ? "${jsLibraryName}.${jsClassName}"
531 : jsClassName,
532 sbPatch,
533 isStatic: true);
442 } 534 }
443 sbPatch.write(") {\n" 535 });
444 " var ret = new ${_JS_LIBRARY_PREFIX}.JsObject.jsify({}); \n"); 536 }
445 i = 0; 537 if (isDom) {
446 for (var p in declaration.parameters) { 538 sbPatch.write(" factory ${className}._internalWrap() => "
447 assert(p.isNamed); // XXX throw 539 "new ${classNameImpl}.internal_();\n");
448 var name = args[i];
449 var jsName = mirrors.MirrorSystem.getName(p.simpleName);
450 // XXX apply name conversion rules.
451 sbPatch.write(
452 " if($name != ${_UNDEFINED_VAR}) ret['$jsName'] = $name;\ n");
453 i++;
454 }
455
456 sbPatch.write(" return ret;\n"
457 " }\n");
458 } else if (declaration.isConstructor ||
459 declaration.isFactoryConstructor) {
460 sbPatch.write(" ");
461 addMemberHelper(
462 declaration,
463 (jsLibraryName != null && jsLibraryName.isNotEmpty)
464 ? "${jsLibraryName}.${jsClassName}"
465 : jsClassName,
466 sbPatch,
467 isStatic: true,
468 memberName: className);
469 }
470 });
471
472 clazz.staticMembers.forEach((memberName, member) {
473 if (_isExternal(member)) {
474 sbPatch.write(" ");
475 addMemberHelper(
476 member,
477 (jsLibraryName != null && jsLibraryName.isNotEmpty)
478 ? "${jsLibraryName}.${jsClassName}"
479 : jsClassName,
480 sbPatch,
481 isStatic: true);
482 }
483 });
484 var typeVariablesClause = '';
485 if (!clazz.typeVariables.isEmpty) {
486 typeVariablesClause =
487 '<${clazz.typeVariables.map((m) => mirrors.MirrorSystem.getName( m.simpleName)).join(',')}>';
488 } 540 }
489 if (sbPatch.isNotEmpty) { 541 if (sbPatch.isNotEmpty) {
542 var typeVariablesClause = '';
543 if (!clazz.typeVariables.isEmpty) {
544 typeVariablesClause =
545 '<${clazz.typeVariables.map((m) => mirrors.MirrorSystem.getNam e(m.simpleName)).join(',')}>';
546 }
490 sb.write(""" 547 sb.write("""
491 patch class $className$typeVariablesClause { 548 patch class $className$typeVariablesClause {
492 $sbPatch 549 $sbPatch
493 } 550 }
494 """); 551 """);
552 if (isDom) {
553 sb.write("""
554 class $classNameImpl$typeVariablesClause extends $className implements ${_JS_LIB RARY_PREFIX}.JSObjectInterfacesDom {
555 ${classNameImpl}.internal_() : super.internal_();
556 get runtimeType => $className;
557 toString() => super.toString();
558 }
559 """);
560 }
495 } 561 }
496 } 562 }
497 } 563 }
498 });
499 if (sb.isNotEmpty) {
500 staticCodegen
501 ..add(uri.toString())
502 ..add("${uri}_js_interop_patch.dart")
503 ..add("""
504 import 'dart:js' as ${_JS_LIBRARY_PREFIX};
505
506 /**
507 * Placeholder object for cases where we need to determine exactly how many
508 * args were passed to a function.
509 */
510 const ${_UNDEFINED_VAR} = const Object();
511
512 ${sb}
513 """);
514 }
515 });
516
517 return staticCodegen;
518 }
519
520 List<String> _generateExternalMethods2() {
521 var staticCodegen = <String>[];
522 mirrors.currentMirrorSystem().libraries.forEach((uri, library) {
523 var sb = new StringBuffer();
524 String jsLibraryName = _getJsName(library);
525 library.declarations.forEach((name, declaration) {
526 var isExternal = _isExternal(declaration);
527 if (declaration is mirrors.MethodMirror) {
528 if (isExternal && (_hasJsName(declaration) || jsLibraryName != null)) {
529 addMemberHelper(declaration, jsLibraryName, sb);
530 }
531 } else if (declaration is mirrors.ClassMirror) {
532 mirrors.ClassMirror clazz = declaration;
533 if (_hasJsName(clazz)) {
534 // TODO(jacobr): verify class implements JavaScriptObject.
535 String jsClassName = _getJsMemberName(clazz);
536 var className = mirrors.MirrorSystem.getName(clazz.simpleName);
537 var sbPatch = new StringBuffer();
538 jsInterfaceTypes.add(clazz);
539 clazz.declarations.forEach((name, declaration) {
540 if (declaration is! mirrors.MethodMirror ||
541 !declaration.isAbstract ||
542 !isExternal) return;
543 if (_hasLiteralAnnotation(declaration) &&
544 declaration.isFactoryConstructor) {
545 sbPatch.write(" factory ${className}({");
546 int i = 0;
547 var args = <String>[];
548 for (var p in declaration.parameters) {
549 assert(p.isNamed); // XXX throw
550 args.add(mirrors.MirrorSystem.getName(p.simpleName));
551 i++;
552 }
553 sbPatch
554 ..write(
555 args.map((name) => '$name:${_UNDEFINED_VAR}').join(", "))
556 ..write("}) {\n"
557 " var ret = new ${_JS_LIBRARY_PREFIX}.JsObject.jsify({}); \n");
558 i = 0;
559 for (var p in declaration.parameters) {
560 assert(p.isNamed); // XXX throw
561 var name = args[i];
562 var jsName = mirrors.MirrorSystem.getName(p.simpleName);
563 // XXX apply name conversion rules.
564 sbPatch.write(
565 " if($name != ${_UNDEFINED_VAR}) ret['$jsName'] = $name;\ n");
566 i++;
567 }
568
569 sbPatch.write(" return ret;\n"
570 " }\n");
571 } else if (declaration.isConstructor ||
572 declaration.isFactoryConstructor) {
573 sbPatch.write(" ");
574 addMemberHelper(
575 declaration,
576 (jsLibraryName != null && jsLibraryName.isNotEmpty)
577 ? "${jsLibraryName}.${jsClassName}"
578 : jsClassName,
579 sbPatch,
580 isStatic: true,
581 memberName: className);
582 }
583 });
584
585 clazz.staticMembers.forEach((memberName, member) {
586 if (_isExternal(member)) {
587 sbPatch.write(" ");
588 addMemberHelper(
589 member,
590 (jsLibraryName != null && jsLibraryName.isNotEmpty)
591 ? "${jsLibraryName}.${jsClassName}"
592 : jsClassName,
593 sbPatch,
594 isStatic: true);
595 }
596 });
597 var typeVariablesClause = '';
598 if (!clazz.typeVariables.isEmpty) {
599 typeVariablesClause =
600 '<${clazz.typeVariables.map((m) => mirrors.MirrorSystem.getName( m.simpleName)).join(',')}>';
601 }
602 if (sbPatch.isNotEmpty) {
603 sb.write("""
604 patch class $className$typeVariablesClause {
605 $sbPatch
606 }
607 """);
608 }
609 }
610 }
611 }); 564 });
612 if (sb.isNotEmpty) { 565 if (sb.isNotEmpty) {
613 staticCodegen 566 staticCodegen
614 ..add(uri.toString()) 567 ..add(uri.toString())
615 ..add("${uri}_js_interop_patch.dart") 568 ..add("${uri}_js_interop_patch.dart")
616 ..add(""" 569 ..add("""
617 import 'dart:js' as ${_JS_LIBRARY_PREFIX}; 570 import 'dart:js' as ${_JS_LIBRARY_PREFIX};
618 571
619 /** 572 /**
620 * Placeholder object for cases where we need to determine exactly how many 573 * Placeholder object for cases where we need to determine exactly how many
621 * args were passed to a function. 574 * args were passed to a function.
622 */ 575 */
623 const ${_UNDEFINED_VAR} = const Object(); 576 const ${_UNDEFINED_VAR} = const Object();
624 577
625 ${sb} 578 ${sb}
626 """); 579 """);
627 } 580 }
628 }); 581 });
629 582
630 return staticCodegen; 583 return staticCodegen;
631 } 584 }
632 585
633 /** 586 /**
634 * Generates a part file defining source code for JsObjectImpl and related 587 * Generates part files defining source code for JSObjectImpl, all DOM classes
635 * classes. This calass is needed so that type checks for all registered JavaScr ipt 588 * classes. This codegen is needed so that type checks for all registered
636 * interop classes pass. 589 * JavaScript interop classes pass.
637 */ 590 */
638 List<String> _generateInteropPatchFiles() { 591 List<String> _generateInteropPatchFiles() {
639 var ret = _generateExternalMethods(); 592 var ret = _generateExternalMethods();
640 var libraryPrefixes = new Map<mirrors.LibraryMirror, String>(); 593 var libraryPrefixes = new Map<mirrors.LibraryMirror, String>();
641 var prefixNames = new Set<String>(); 594 var prefixNames = new Set<String>();
642 var sb = new StringBuffer(); 595 var sb = new StringBuffer();
643 596
644 var implements = <String>[]; 597 var implements = <String>[];
645 var implementsArray = <String>[]; 598 var implementsArray = <String>[];
599 var implementsDom = <String>[];
646 var listMirror = mirrors.reflectType(List); 600 var listMirror = mirrors.reflectType(List);
601 var functionMirror = mirrors.reflectType(Function);
602 var jsObjectMirror = mirrors.reflectType(JSObject);
647 603
648 for (var typeMirror in jsInterfaceTypes) { 604 for (var typeMirror in jsInterfaceTypes) {
649 mirrors.LibraryMirror libraryMirror = typeMirror.owner; 605 mirrors.LibraryMirror libraryMirror = typeMirror.owner;
650 var prefixName; 606 var prefixName;
651 if (libraryPrefixes.containsKey(libraryMirror)) { 607 if (libraryPrefixes.containsKey(libraryMirror)) {
652 prefixName = libraryPrefixes[libraryMirror]; 608 prefixName = libraryPrefixes[libraryMirror];
653 } else { 609 } else {
654 var basePrefixName = 610 var basePrefixName =
655 mirrors.MirrorSystem.getName(libraryMirror.simpleName); 611 mirrors.MirrorSystem.getName(libraryMirror.simpleName);
656 basePrefixName = basePrefixName.replaceAll('.', '_'); 612 basePrefixName = basePrefixName.replaceAll('.', '_');
657 if (basePrefixName.isEmpty) basePrefixName = "lib"; 613 if (basePrefixName.isEmpty) basePrefixName = "lib";
658 prefixName = basePrefixName; 614 prefixName = basePrefixName;
659 var i = 1; 615 var i = 1;
660 while (prefixNames.contains(prefixName)) { 616 while (prefixNames.contains(prefixName)) {
661 prefixName = '$basePrefixName$i'; 617 prefixName = '$basePrefixName$i';
662 i++; 618 i++;
663 } 619 }
664 prefixNames.add(prefixName); 620 prefixNames.add(prefixName);
665 libraryPrefixes[libraryMirror] = prefixName; 621 libraryPrefixes[libraryMirror] = prefixName;
666 } 622 }
667 var isArray = typeMirror.isSubtypeOf(listMirror); 623 var isArray = typeMirror.isSubtypeOf(listMirror);
668 (isArray ? implementsArray : implements).add( 624 var isFunction = typeMirror.isSubtypeOf(functionMirror);
669 '${prefixName}.${mirrors.MirrorSystem.getName(typeMirror.simpleName)}'); 625 var isJSObject = typeMirror.isSubtypeOf(jsObjectMirror);
626 var fullName =
627 '${prefixName}.${mirrors.MirrorSystem.getName(typeMirror.simpleName)}';
628 (isArray ? implementsArray : implements).add(fullName);
629 if (!isArray && !isFunction && !isJSObject) {
630 // For DOM classes we need to be a bit more conservative at tagging them
631 // as implementing JS inteorp classes risks strange unintended
632 // consequences as unrleated code may have instanceof checks. Checking
633 // for isJSObject ensures we do not accidentally pull in existing
634 // dart:html classes as they all have JSObject as a base class.
635 // Note that methods from these classes can still be called on a
636 // dart:html instance but checked mode type checks will fail. This is
637 // not ideal but is better than causing strange breaks in existing
638 // code that uses dart:html.
639 // TODO(jacobr): consider throwing compile time errors if @JS classes
640 // extend JSObject as that case cannot be safely handled in Dartium.
641 implementsDom.add(fullName);
642 }
670 } 643 }
671 libraryPrefixes.forEach((libraryMirror, prefix) { 644 libraryPrefixes.forEach((libraryMirror, prefix) {
672 sb.writeln('import "${libraryMirror.uri}" as $prefix;'); 645 sb.writeln('import "${libraryMirror.uri}" as $prefix;');
673 }); 646 });
674 buildImplementsClause(classes) => 647 buildImplementsClause(classes) =>
675 classes.isEmpty ? "" : "implements ${classes.join(', ')}"; 648 classes.isEmpty ? "" : "implements ${classes.join(', ')}";
676 var implementsClause = buildImplementsClause(implements); 649 var implementsClause = buildImplementsClause(implements);
650 var implementsClauseDom = buildImplementsClause(implementsDom);
677 // TODO(jacobr): only certain classes need to be implemented by 651 // TODO(jacobr): only certain classes need to be implemented by
678 // JsFunctionImpl. 652 // JsFunctionImpl.
679 var allTypes = []..addAll(implements)..addAll(implementsArray); 653 var allTypes = []..addAll(implements)..addAll(implementsArray);
680 sb.write(''' 654 sb.write('''
681 class JsObjectImpl extends JsObject $implementsClause { 655 class JSObjectImpl extends JSObject $implementsClause {
682 JsObjectImpl.internal() : super.internal(); 656 JSObjectImpl.internal() : super.internal();
683 } 657 }
684 658
685 class JsFunctionImpl extends JsFunction $implementsClause { 659 class JSFunctionImpl extends JSFunction $implementsClause {
686 JsFunctionImpl.internal() : super.internal(); 660 JSFunctionImpl.internal() : super.internal();
687 } 661 }
688 662
689 class JsArrayImpl<E> extends JsArray<E> ${buildImplementsClause(implementsArray) } { 663 class JSArrayImpl extends JSArray ${buildImplementsClause(implementsArray)} {
690 JsArrayImpl.internal() : super.internal(); 664 JSArrayImpl.internal() : super.internal();
665 }
666
667 // Interfaces that are safe to slam on all DOM classes.
668 // Adding implementsClause would be risky as it could contain Function which
669 // is likely to break a lot of instanceof checks.
670 abstract class JSObjectInterfacesDom $implementsClauseDom {
671 }
672
673 patch class JSObject {
674 factory JSObject.create(JsObject jsObject) {
675 return new JSObjectImpl.internal()..blink_jsObject = jsObject;
676 }
677 }
678
679 patch class JSFunction {
680 factory JSFunction.create(JsObject jsObject) {
681 return new JSFunctionImpl.internal()..blink_jsObject = jsObject;
682 }
683 }
684
685 patch class JSArray {
686 factory JSArray.create(JsObject jsObject) {
687 return new JSArrayImpl.internal()..blink_jsObject = jsObject;
688 }
691 } 689 }
692 690
693 _registerAllJsInterfaces() { 691 _registerAllJsInterfaces() {
694 _registerJsInterfaces([${allTypes.join(", ")}]); 692 _registerJsInterfaces([${allTypes.join(", ")}]);
695 } 693 }
696 694
697 '''); 695 ''');
698 ret..addAll(["dart:js", "JsInteropImpl.dart", sb.toString()]); 696 ret..addAll(["dart:js", "JSInteropImpl.dart", sb.toString()]);
699 return ret; 697 return ret;
700 } 698 }
701 699
702 // Start of block of helper methods facilitating emulating JavaScript Array 700 // Start of block of helper methods facilitating emulating JavaScript Array
703 // methods on Dart List objects passed to JavaScript via JS interop. 701 // methods on Dart List objects passed to JavaScript via JS interop.
704 // TODO(jacobr): match JS more closely. 702 // TODO(jacobr): match JS more closely.
705 String _toStringJs(obj) => '$obj'; 703 String _toStringJs(obj) => '$obj';
706 704
707 // TODO(jacobr): this might not exactly match JS semantics but should be 705 // TODO(jacobr): this might not exactly match JS semantics but should be
708 // adequate for now. 706 // adequate for now.
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
863 bool get _finalized native "Js_interfacesFinalized_Callback"; 861 bool get _finalized native "Js_interfacesFinalized_Callback";
864 862
865 JsObject get context { 863 JsObject get context {
866 if (_cachedContext == null) { 864 if (_cachedContext == null) {
867 _cachedContext = _context; 865 _cachedContext = _context;
868 } 866 }
869 return _cachedContext; 867 return _cachedContext;
870 } 868 }
871 869
872 @Deprecated("Internal Use Only") 870 @Deprecated("Internal Use Only")
873 maybeWrapTypedInterop(o) => 871 maybeWrapTypedInterop(o) => html_common.wrap_jso_no_SerializedScriptvalue(o);
874 html_common.wrap_jso_no_SerializedScriptvalue(o);
875 872
876 _maybeWrap(o) { 873 _maybeWrap(o) {
877 var wrapped = html_common.wrap_jso_no_SerializedScriptvalue(o); 874 var wrapped = html_common.wrap_jso_no_SerializedScriptvalue(o);
878 if (identical(wrapped, o)) return o; 875 if (identical(wrapped, o)) return o;
879 return (wrapped is html.Blob || 876 return (wrapped is html.Blob ||
880 wrapped is html.Event || 877 wrapped is html.Event ||
881 wrapped is indexed_db.KeyRange || 878 wrapped is indexed_db.KeyRange ||
882 wrapped is html.ImageData || 879 wrapped is html.ImageData ||
883 wrapped is html.Node || 880 wrapped is html.Node ||
884 wrapped is TypedData || 881 wrapped is TypedData ||
(...skipping 18 matching lines...) Expand all
903 object._dartHtmlWrapper = wrapper; 900 object._dartHtmlWrapper = wrapper;
904 } 901 }
905 902
906 /** 903 /**
907 * Used by callMethod to get the JS object for each argument passed if the 904 * Used by callMethod to get the JS object for each argument passed if the
908 * argument is a Dart class instance that delegates to a DOM object. See 905 * argument is a Dart class instance that delegates to a DOM object. See
909 * wrap_jso defined in dart:html. 906 * wrap_jso defined in dart:html.
910 */ 907 */
911 @Deprecated("Internal Use Only") 908 @Deprecated("Internal Use Only")
912 unwrap_jso(dartClass_instance) { 909 unwrap_jso(dartClass_instance) {
913 if (dartClass_instance is html.DartHtmlDomObject && 910 if (dartClass_instance is JSObject &&
Alan Knight 2016/01/14 00:22:09 I suppose the ship has sailed that we have them as
914 dartClass_instance is! JsObject) return dartClass_instance.blink_jsObject; 911 dartClass_instance is! JsObject) return dartClass_instance.blink_jsObject;
915 else return dartClass_instance; 912 else return dartClass_instance;
916 } 913 }
917 914
918 /** 915 /**
919 * Proxies a JavaScript object to Dart. 916 * Proxies a JavaScript object to Dart.
920 * 917 *
921 * The properties of the JavaScript object are accessible via the `[]` and 918 * The properties of the JavaScript object are accessible via the `[]` and
922 * `[]=` operators. Methods are callable via [callMethod]. 919 * `[]=` operators. Methods are callable via [callMethod].
923 */ 920 */
(...skipping 15 matching lines...) Expand all
939 return html_common.unwrap_jso(_create(constructor, arguments)); 936 return html_common.unwrap_jso(_create(constructor, arguments));
940 } catch (e) { 937 } catch (e) {
941 // Re-throw any errors (returned as a string) as a DomException. 938 // Re-throw any errors (returned as a string) as a DomException.
942 throw new html.DomException.jsInterop(e); 939 throw new html.DomException.jsInterop(e);
943 } 940 }
944 } 941 }
945 942
946 static JsObject _create(JsFunction constructor, arguments) 943 static JsObject _create(JsFunction constructor, arguments)
947 native "JsObject_constructorCallback"; 944 native "JsObject_constructorCallback";
948 945
949 _buildArgs(Invocation invocation) {
950 if (invocation.namedArguments.isEmpty) {
951 return invocation.positionalArguments;
952 } else {
953 var varArgs = new Map<String, Object>();
954 invocation.namedArguments.forEach((symbol, val) {
955 varArgs[mirrors.MirrorSystem.getName(symbol)] = val;
956 });
957 return invocation.positionalArguments.toList()
958 ..add(new JsObject.jsify(varArgs));
959 }
960 }
961
962 /** 946 /**
963 * Constructs a [JsObject] that proxies a native Dart object; _for expert use 947 * Constructs a [JsObject] that proxies a native Dart object; _for expert use
964 * only_. 948 * only_.
965 * 949 *
966 * Use this constructor only if you wish to get access to JavaScript 950 * Use this constructor only if you wish to get access to JavaScript
967 * properties attached to a browser host object, such as a Node or Blob, that 951 * properties attached to a browser host object, such as a Node or Blob, that
968 * is normally automatically converted into a native Dart object. 952 * is normally automatically converted into a native Dart object.
969 * 953 *
970 * An exception will be thrown if [object] either is `null` or has the type 954 * An exception will be thrown if [object] either is `null` or has the type
971 * `bool`, `num`, or `String`. 955 * `bool`, `num`, or `String`.
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
1092 } catch (e) { 1076 } catch (e) {
1093 if (hasProperty(method)) { 1077 if (hasProperty(method)) {
1094 // Return a DomException if DOM call returned an error. 1078 // Return a DomException if DOM call returned an error.
1095 throw new html.DomException.jsInterop(e); 1079 throw new html.DomException.jsInterop(e);
1096 } else { 1080 } else {
1097 throw new NoSuchMethodError(this, new Symbol(method), args, null); 1081 throw new NoSuchMethodError(this, new Symbol(method), args, null);
1098 } 1082 }
1099 } 1083 }
1100 } 1084 }
1101 1085
1086 _callMethod(String name, List args) native "JsObject_callMethod";
1087 }
1088
1089 /// Base class for all JS objects used through dart:html and typed JS interop.
1090 @Deprecated("Internal Use Only")
1091 class JSObject {
1092 JSObject.internal() {}
1093 external factory JSObject.create(JsObject jsObject);
1094
1095 @Deprecated("Internal Use Only")
1096 JsObject blink_jsObject;
1097
1098 String toString() => blink_jsObject.toString();
1099
1102 noSuchMethod(Invocation invocation) { 1100 noSuchMethod(Invocation invocation) {
1103 throwError() { 1101 throwError() {
1104 throw new NoSuchMethodError(this, invocation.memberName, 1102 super.noSuchMethod(invocation);
1105 invocation.positionalArguments, invocation.namedArguments);
1106 } 1103 }
1107 1104
1108 String name = mirrors.MirrorSystem.getName(invocation.memberName); 1105 String name = _stripReservedNamePrefix(
1106 mirrors.MirrorSystem.getName(invocation.memberName));
1107 argsSafeForTypedInterop(invocation.positionalArguments);
1109 if (invocation.isGetter) { 1108 if (invocation.isGetter) {
1110 if (CHECK_JS_INVOCATIONS) { 1109 if (CHECK_JS_INVOCATIONS) {
1111 var matches = _allowedGetters[invocation.memberName]; 1110 var matches = _allowedGetters[invocation.memberName];
1112 if (matches == null && 1111 if (matches == null &&
1113 !_allowedMethods.containsKey(invocation.memberName)) { 1112 !_allowedMethods.containsKey(invocation.memberName)) {
1114 throwError(); 1113 throwError();
1115 } 1114 }
1116 var ret = this[name]; 1115 var ret = maybeWrapTypedInterop(blink_jsObject._operator_getter(name));
1117 if (matches != null && matches._checkReturnType(ret)) return ret; 1116 if (matches != null) return ret;
1118 if (ret is Function || 1117 if (ret is Function ||
1119 (ret is JsFunction /* shouldn't be needed in the future*/) && 1118 (ret is JsFunction /* shouldn't be needed in the future*/) &&
1120 _allowedMethods.containsKey( 1119 _allowedMethods.containsKey(
1121 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. 1120 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.
1122 throwError(); 1121 throwError();
1123 } else { 1122 } else {
1124 // TODO(jacobr): should we throw if the JavaScript object doesn't have t he property? 1123 // TODO(jacobr): should we throw if the JavaScript object doesn't have t he property?
1125 return maybeWrapTypedInterop(this._operator_getter(name)); 1124 return maybeWrapTypedInterop(blink_jsObject._operator_getter(name));
1126 } 1125 }
1127 } else if (invocation.isSetter) { 1126 } else if (invocation.isSetter) {
1128 if (CHECK_JS_INVOCATIONS) { 1127 if (CHECK_JS_INVOCATIONS) {
1129 var matches = _allowedSetters[invocation.memberName]; 1128 var matches = _allowedSetters[invocation.memberName];
1130 if (matches == null || 1129 if (matches == null ||
1131 !matches.checkInvocation(invocation)) throwError(); 1130 !matches.checkInvocation(invocation)) throwError();
1132 } 1131 }
1133 assert(name.endsWith("=")); 1132 assert(name.endsWith("="));
1134 name = name.substring(0, name.length - 1); 1133 name = name.substring(0, name.length - 1);
1135 return maybeWrapTypedInterop(_operator_setter( 1134 return maybeWrapTypedInterop(blink_jsObject._operator_setter(
1136 name, invocation.positionalArguments.first)); 1135 name, invocation.positionalArguments.first));
1137 } else { 1136 } else {
1138 // TODO(jacobr): also allow calling getters that look like functions. 1137 // TODO(jacobr): also allow calling getters that look like functions.
1139 var matches; 1138 var matches;
1140 if (CHECK_JS_INVOCATIONS) { 1139 if (CHECK_JS_INVOCATIONS) {
1141 matches = _allowedMethods[invocation.memberName]; 1140 matches = _allowedMethods[invocation.memberName];
1142 if (matches == null || 1141 if (matches == null ||
1143 !matches.checkInvocation(invocation)) throwError(); 1142 !matches.checkInvocation(invocation)) throwError();
1144 } 1143 }
1145 var ret = maybeWrapTypedInterop(this._callMethod(name, _buildArgs(invocati on))); 1144 var ret = maybeWrapTypedInterop(
1145 blink_jsObject._callMethod(name, _buildArgs(invocation)));
1146 if (CHECK_JS_INVOCATIONS) { 1146 if (CHECK_JS_INVOCATIONS) {
1147 if (!matches._checkReturnType(ret)) throwError(); 1147 if (!matches._checkReturnType(ret)) {
1148 html.window.console.error("Return value for method: ${name} is "
1149 "${ret.runtimeType} which is inconsistent with all typed "
1150 "JS interop definitions for method ${name}.");
1151 }
1148 } 1152 }
1149 return ret; 1153 return ret;
1150 } 1154 }
1151 } 1155 }
1156 }
1152 1157
1153 _callMethod(String name, List args) native "JsObject_callMethod"; 1158 @Deprecated("Internal Use Only")
1159 class JSArray extends JSObject with ListMixin {
1160 JSArray.internal() : super.internal();
1161 external factory JSArray.create(JsObject jsObject);
1162 operator [](int index) =>
1163 maybeWrapTypedInterop(JsNative.getArrayIndex(blink_jsObject, index));
1164
1165 operator []=(int index, value) => blink_jsObject[index] = value;
1166
1167 int get length => blink_jsObject.length;
1168 int set length(int newLength) => blink_jsObject.length = newLength;
1169 }
1170
1171 @Deprecated("Internal Use Only")
1172 class JSFunction extends JSObject implements Function {
1173 JSFunction.internal() : super.internal();
1174
1175 external factory JSFunction.create(JsObject jsObject);
1176
1177 call(
1178 [a1 = _UNDEFINED,
1179 a2 = _UNDEFINED,
1180 a3 = _UNDEFINED,
1181 a4 = _UNDEFINED,
1182 a5 = _UNDEFINED,
1183 a6 = _UNDEFINED,
1184 a7 = _UNDEFINED,
1185 a8 = _UNDEFINED,
1186 a9 = _UNDEFINED,
1187 a10 = _UNDEFINED]) {
1188 return maybeWrapTypedInterop(blink_jsObject
1189 .apply(_stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])));
1190 }
1191
1192 noSuchMethod(Invocation invocation) {
1193 if (invocation.isMethod && invocation.memberName == #call) {
1194 return maybeWrapTypedInterop(
1195 blink_jsObject.apply(_buildArgs(invocation)));
1196 }
1197 return super.noSuchMethod(invocation);
1198 }
1154 } 1199 }
1155 1200
1156 // JavaScript interop methods that do not automatically wrap to dart:html types. 1201 // JavaScript interop methods that do not automatically wrap to dart:html types.
1157 // Warning: this API is not exposed to dart:js. 1202 // Warning: this API is not exposed to dart:js.
1158 @Deprecated("Internal Use Only") 1203 @Deprecated("Internal Use Only")
1159 class JsNative { 1204 class JsNative {
1160 static getProperty(o, name) { 1205 static getProperty(o, name) {
1161 o = unwrap_jso(o); 1206 o = unwrap_jso(o);
1162 return o != null ? o._operator_getter(name) : null; 1207 return o != null ? o._operator_getter(name) : null;
1163 } 1208 }
(...skipping 14 matching lines...) Expand all
1178 /** 1223 /**
1179 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n ot 1224 * Same behavior as new JsFunction.withThis except that JavaScript "this" is n ot
1180 * wrapped. 1225 * wrapped.
1181 */ 1226 */
1182 static JsFunction withThis(Function f) native "JsFunction_withThisNoWrap"; 1227 static JsFunction withThis(Function f) native "JsFunction_withThisNoWrap";
1183 } 1228 }
1184 1229
1185 /** 1230 /**
1186 * Proxies a JavaScript Function object. 1231 * Proxies a JavaScript Function object.
1187 */ 1232 */
1188 class JsFunction extends JsObject implements Function { 1233 class JsFunction extends JsObject {
1189 JsFunction.internal() : super.internal(); 1234 JsFunction.internal() : super.internal();
1190 1235
1191 /** 1236 /**
1192 * Returns a [JsFunction] that captures its 'this' binding and calls [f] 1237 * Returns a [JsFunction] that captures its 'this' binding and calls [f]
1193 * with the value of this passed as the first argument. 1238 * with the value of this passed as the first argument.
1194 */ 1239 */
1195 factory JsFunction.withThis(Function f) => _withThis(f); 1240 factory JsFunction.withThis(Function f) => _withThis(f);
1196 1241
1197 /** 1242 /**
1198 * Invokes the JavaScript function with arguments [args]. If [thisArg] is 1243 * Invokes the JavaScript function with arguments [args]. If [thisArg] is
1199 * supplied it is the value of `this` for the invocation. 1244 * supplied it is the value of `this` for the invocation.
1200 */ 1245 */
1201 dynamic apply(List args, {thisArg}) => 1246 dynamic apply(List args, {thisArg}) =>
1202 _maybeWrap(_apply(args, thisArg: thisArg)); 1247 _maybeWrap(_apply(args, thisArg: thisArg));
1203 1248
1204 dynamic _apply(List args, {thisArg}) native "JsFunction_apply"; 1249 dynamic _apply(List args, {thisArg}) native "JsFunction_apply";
1205 1250
1206 call([a1 = _UNDEFINED,
1207 a2 = _UNDEFINED,
1208 a3 = _UNDEFINED,
1209 a4 = _UNDEFINED,
1210 a5 = _UNDEFINED,
1211 a6 = _UNDEFINED,
1212 a7 = _UNDEFINED,
1213 a8 = _UNDEFINED,
1214 a9 = _UNDEFINED,
1215 a10 = _UNDEFINED]) {
1216 return apply(
1217 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
1218 }
1219
1220 noSuchMethod(Invocation invocation) {
1221 if (invocation.isMethod && invocation.memberName == #call) {
1222 return apply(_buildArgs(invocation));
1223 }
1224 return super.noSuchMethod(invocation);
1225 }
1226
1227 /** 1251 /**
1228 * Internal only version of apply which uses debugger proxies of Dart objects 1252 * Internal only version of apply which uses debugger proxies of Dart objects
1229 * rather than opaque handles. This method is private because it cannot be 1253 * rather than opaque handles. This method is private because it cannot be
1230 * efficiently implemented in Dart2Js so should only be used by internal 1254 * efficiently implemented in Dart2Js so should only be used by internal
1231 * tools. 1255 * tools.
1232 */ 1256 */
1233 _applyDebuggerOnly(List args, {thisArg}) 1257 _applyDebuggerOnly(List args, {thisArg})
1234 native "JsFunction_applyDebuggerOnly"; 1258 native "JsFunction_applyDebuggerOnly";
1235 1259
1236 static JsFunction _withThis(Function f) native "JsFunction_withThis"; 1260 static JsFunction _withThis(Function f) native "JsFunction_withThis";
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
1342 * args were passed to a function. 1366 * args were passed to a function.
1343 */ 1367 */
1344 const _UNDEFINED = const Object(); 1368 const _UNDEFINED = const Object();
1345 1369
1346 // TODO(jacobr): this method is a hack to work around the lack of proper dart 1370 // TODO(jacobr): this method is a hack to work around the lack of proper dart
1347 // support for varargs methods. 1371 // support for varargs methods.
1348 List _stripUndefinedArgs(List args) => 1372 List _stripUndefinedArgs(List args) =>
1349 args.takeWhile((i) => i != _UNDEFINED).toList(); 1373 args.takeWhile((i) => i != _UNDEFINED).toList();
1350 1374
1351 /** 1375 /**
1376 * Check that that if [arg] is a [Function] it is safe to pass to JavaScript.
1377 * To make a function safe, call [allowInterop] or [allowInteropCaptureThis].
1378 */
1379 @Deprecated("Internal Use Only")
1380 safeForTypedInterop(arg) {
1381 if (CHECK_JS_INVOCATIONS && arg is Function && arg is! JSFunction) {
1382 throw new ArgumentError(
1383 "Attempt to pass Function '$arg' to JavaScript via without calling allow Interop or allowInteropCaptureThis");
1384 }
1385 }
1386
1387 /**
1388 * Check that that if any elements of [args] are [Function] it is safe to pass
1389 * to JavaScript. To make a function safe, call [allowInterop] or
1390 * [allowInteropCaptureThis].
1391 */
1392 @Deprecated("Internal Use Only")
1393 void argsSafeForTypedInterop(Iterable args) {
1394 for (var arg in args) {
1395 safeForTypedInterop(arg);
1396 }
1397 }
1398
1399 List _stripAndWrapArgs(Iterable args) {
1400 var ret = [];
1401 for (var arg in args) {
1402 if (arg == _UNDEFINED) break;
1403 ret.add(maybeWrapTypedInterop(arg));
1404 }
1405 return ret;
1406 }
1407
1408 /**
1352 * Returns a method that can be called with an arbitrary number (for n less 1409 * Returns a method that can be called with an arbitrary number (for n less
1353 * than 11) of arguments without violating Dart type checks. 1410 * than 11) of arguments without violating Dart type checks.
1354 */ 1411 */
1355 Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => ( 1412 Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => (
1356 [a1 = _UNDEFINED, 1413 [a1 = _UNDEFINED,
1357 a2 = _UNDEFINED, 1414 a2 = _UNDEFINED,
1358 a3 = _UNDEFINED, 1415 a3 = _UNDEFINED,
1359 a4 = _UNDEFINED, 1416 a4 = _UNDEFINED,
1360 a5 = _UNDEFINED, 1417 a5 = _UNDEFINED,
1361 a6 = _UNDEFINED, 1418 a6 = _UNDEFINED,
1362 a7 = _UNDEFINED, 1419 a7 = _UNDEFINED,
1363 a8 = _UNDEFINED, 1420 a8 = _UNDEFINED,
1364 a9 = _UNDEFINED, 1421 a9 = _UNDEFINED,
1365 a10 = _UNDEFINED]) => 1422 a10 = _UNDEFINED]) =>
1366 jsFunction._applyDebuggerOnly( 1423 jsFunction._applyDebuggerOnly(
1367 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); 1424 _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
1368 1425
1369 // The allowInterop method is a no-op in Dartium. 1426 /// This helper is purely a hack so we can reuse JsFunction.withThis even when
1370 // TODO(jacobr): tag methods so we can throw if a Dart method is passed to 1427 /// we don't care about passing JS "this". In an ideal world we would implement
1371 // JavaScript using the new interop without calling allowInterop. 1428 /// helpers in C++ that directly implement allowInterop and
1429 /// allowInteropCaptureThis.
1430 class _CreateDartFunctionForInteropIgnoreThis implements Function {
1431 Function _fn;
1432
1433 _CreateDartFunctionForInteropIgnoreThis(this._fn);
1434
1435 call(
1436 [ignoredThis = _UNDEFINED,
1437 a1 = _UNDEFINED,
1438 a2 = _UNDEFINED,
1439 a3 = _UNDEFINED,
1440 a4 = _UNDEFINED,
1441 a5 = _UNDEFINED,
1442 a6 = _UNDEFINED,
1443 a7 = _UNDEFINED,
1444 a8 = _UNDEFINED,
1445 a9 = _UNDEFINED,
1446 a10 = _UNDEFINED]) {
1447 var ret = Function.apply(
1448 _fn, _stripAndWrapArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
1449 safeForTypedInterop(ret);
1450 return ret;
1451 }
1452
1453 noSuchMethod(Invocation invocation) {
1454 if (invocation.isMethod && invocation.memberName == #call) {
1455 // Named arguments not yet supported.
1456 if (invocation.namedArguments.isNotEmpty) return;
1457 var ret = Function.apply(
1458 _fn, _stripAndWrapArgs(invocation.positionalArguments.skip(1)));
1459 // TODO(jacobr): it would be nice to check that the return value is safe
1460 // for interop but we don't want to break existing addEventListener users.
1461 // safeForTypedInterop(ret);
1462 safeForTypedInterop(ret);
1463 return ret;
1464 }
1465 return super.noSuchMethod(invocation);
1466 }
1467 }
1468
1469 /// See comment for [_CreateDartFunctionForInteropIgnoreThis].
1470 /// This Function exists purely because JsObject doesn't have the DOM type
1471 /// conversion semantics we want for JS typed interop.
1472 class _CreateDartFunctionForInterop implements Function {
1473 Function _fn;
1474
1475 _CreateDartFunctionForInterop(this._fn);
1476
1477 call(
1478 [a1 = _UNDEFINED,
1479 a2 = _UNDEFINED,
1480 a3 = _UNDEFINED,
1481 a4 = _UNDEFINED,
1482 a5 = _UNDEFINED,
1483 a6 = _UNDEFINED,
1484 a7 = _UNDEFINED,
1485 a8 = _UNDEFINED,
1486 a9 = _UNDEFINED,
1487 a10 = _UNDEFINED]) {
1488 var ret = Function.apply(
1489 _fn, _stripAndWrapArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]));
1490 safeForTypedInterop(ret);
1491 return ret;
1492 }
1493
1494 noSuchMethod(Invocation invocation) {
1495 if (invocation.isMethod && invocation.memberName == #call) {
1496 // Named arguments not yet supported.
1497 if (invocation.namedArguments.isNotEmpty) return;
1498 var ret = Function.apply(
1499 _fn, _stripAndWrapArgs(invocation.positionalArguments));
1500 safeForTypedInterop(ret);
1501 return ret;
1502 }
1503 return super.noSuchMethod(invocation);
1504 }
1505 }
1506
1507 /// Cached JSFunction associated with the Dart Function.
1508 Expando<JSFunction> _interopExpando = new Expando<JSFunction>();
1372 1509
1373 /// Returns a wrapper around function [f] that can be called from JavaScript 1510 /// Returns a wrapper around function [f] that can be called from JavaScript
1374 /// using the package:js Dart-JavaScript interop. 1511 /// using the package:js Dart-JavaScript interop.
1375 /// 1512 ///
1376 /// For performance reasons in Dart2Js, by default Dart functions cannot be 1513 /// For performance reasons in Dart2Js, by default Dart functions cannot be
1377 /// passed directly to JavaScript unless this method is called to create 1514 /// passed directly to JavaScript unless this method is called to create
1378 /// a Function compatible with both Dart and JavaScript. 1515 /// a Function compatible with both Dart and JavaScript.
1379 /// Calling this method repeatedly on a function will return the same function. 1516 /// Calling this method repeatedly on a function will return the same function.
1380 /// The [Function] returned by this method can be used from both Dart and 1517 /// The [Function] returned by this method can be used from both Dart and
1381 /// JavaScript. We may remove the need to call this method completely in the 1518 /// JavaScript. We may remove the need to call this method completely in the
1382 /// future if Dart2Js is refactored so that its function calling conventions 1519 /// future if Dart2Js is refactored so that its function calling conventions
1383 /// are more compatible with JavaScript. 1520 /// are more compatible with JavaScript.
1384 Function allowInterop(Function f) => f; 1521 JSFunction allowInterop(Function f) {
1522 if (f is JSFunction) {
1523 // The function is already a JSFunction... no need to do anything.
1524 return f;
1525 } else {
1526 var ret = _interopExpando[f];
1527 if (ret == null) {
1528 // TODO(jacobr): we could optimize this.
1529 ret = new JSFunction.create(new JsFunction.withThis(
1530 new _CreateDartFunctionForInteropIgnoreThis(f)));
1531 _interopExpando[f] = ret;
1532 }
1533 return ret;
1534 }
1535 }
1385 1536
1386 Expando<JsFunction> _interopCaptureThisExpando = new Expando<JsFunction>(); 1537 /// Cached JSFunction associated with the Dart function when "this" is
1538 /// captured.
1539 Expando<JSFunction> _interopCaptureThisExpando = new Expando<JSFunction>();
1387 1540
1388 /// Returns a [Function] that when called from JavaScript captures its 'this' 1541 /// Returns a [Function] that when called from JavaScript captures its 'this'
1389 /// binding and calls [f] with the value of this passed as the first argument. 1542 /// binding and calls [f] with the value of this passed as the first argument.
1390 /// When called from Dart, [null] will be passed as the first argument. 1543 /// When called from Dart, [null] will be passed as the first argument.
1391 /// 1544 ///
1392 /// See the documention for [allowInterop]. This method should only be used with 1545 /// See the documention for [allowInterop]. This method should only be used with
1393 /// package:js Dart-JavaScript interop. 1546 /// package:js Dart-JavaScript interop.
1394 Function allowInteropCaptureThis(Function f) { 1547 JSFunction allowInteropCaptureThis(Function f) {
1395 if (f is JsFunction) { 1548 if (f is JSFunction) {
1396 // Behavior when the function is already a JS function is unspecified. 1549 // Behavior when the function is already a JS function is unspecified.
1397 throw new ArgumentError( 1550 throw new ArgumentError(
1398 "Function is already a JS function so cannot capture this."); 1551 "Function is already a JS function so cannot capture this.");
1399 return f; 1552 return f;
1400 } else { 1553 } else {
1401 var ret = _interopCaptureThisExpando[f]; 1554 var ret = _interopCaptureThisExpando[f];
1402 if (ret == null) { 1555 if (ret == null) {
1403 ret = new JsFunction.withThis(f); 1556 // TODO(jacobr): we could optimize this.
1557 ret = new JSFunction.create(
1558 new JsFunction.withThis(new _CreateDartFunctionForInterop(f)));
1404 _interopCaptureThisExpando[f] = ret; 1559 _interopCaptureThisExpando[f] = ret;
1405 } 1560 }
1406 return ret; 1561 return ret;
1407 } 1562 }
1408 } 1563 }
OLDNEW
« no previous file with comments | « sdk/lib/indexed_db/dartium/indexed_db_dartium.dart ('k') | sdk/lib/svg/dartium/svg_dartium.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698