Index: sdk/lib/js/dartium/js_dartium.dart |
diff --git a/sdk/lib/js/dartium/js_dartium.dart b/sdk/lib/js/dartium/js_dartium.dart |
deleted file mode 100644 |
index 6c3ed0d68059b401aa6e57a6cb685f100dbf5cb5..0000000000000000000000000000000000000000 |
--- a/sdk/lib/js/dartium/js_dartium.dart |
+++ /dev/null |
@@ -1,1706 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-/** |
- * Support for interoperating with JavaScript. |
- * |
- * This library provides access to JavaScript objects from Dart, allowing |
- * Dart code to get and set properties, and call methods of JavaScript objects |
- * and invoke JavaScript functions. The library takes care of converting |
- * between Dart and JavaScript objects where possible, or providing proxies if |
- * conversion isn't possible. |
- * |
- * This library does not yet make Dart objects usable from JavaScript, their |
- * methods and proeprties are not accessible, though it does allow Dart |
- * functions to be passed into and called from JavaScript. |
- * |
- * [JsObject] is the core type and represents a proxy of a JavaScript object. |
- * JsObject gives access to the underlying JavaScript objects properties and |
- * methods. `JsObject`s can be acquired by calls to JavaScript, or they can be |
- * created from proxies to JavaScript constructors. |
- * |
- * The top-level getter [context] provides a [JsObject] that represents the |
- * global object in JavaScript, usually `window`. |
- * |
- * The following example shows an alert dialog via a JavaScript call to the |
- * global function `alert()`: |
- * |
- * import 'dart:js'; |
- * |
- * main() => context.callMethod('alert', ['Hello from Dart!']); |
- * |
- * This example shows how to create a [JsObject] from a JavaScript constructor |
- * and access its properties: |
- * |
- * import 'dart:js'; |
- * |
- * main() { |
- * var object = new JsObject(context['Object']); |
- * object['greeting'] = 'Hello'; |
- * object['greet'] = (name) => "${object['greeting']} $name"; |
- * var message = object.callMethod('greet', ['JavaScript']); |
- * context['console'].callMethod('log', [message]); |
- * } |
- * |
- * ## Proxying and automatic conversion |
- * |
- * When setting properties on a JsObject or passing arguments to a Javascript |
- * method or function, Dart objects are automatically converted or proxied to |
- * JavaScript objects. When accessing JavaScript properties, or when a Dart |
- * closure is invoked from JavaScript, the JavaScript objects are also |
- * converted to Dart. |
- * |
- * Functions and closures are proxied in such a way that they are callable. A |
- * Dart closure assigned to a JavaScript property is proxied by a function in |
- * JavaScript. A JavaScript function accessed from Dart is proxied by a |
- * [JsFunction], which has a [apply] method to invoke it. |
- * |
- * The following types are transferred directly and not proxied: |
- * |
- * * "Basic" types: `null`, `bool`, `num`, `String`, `DateTime` |
- * * `Blob` |
- * * `Event` |
- * * `HtmlCollection` |
- * * `ImageData` |
- * * `KeyRange` |
- * * `Node` |
- * * `NodeList` |
- * * `TypedData`, including its subclasses like `Int32List`, but _not_ |
- * `ByteBuffer` |
- * * `Window` |
- * |
- * ## Converting collections with JsObject.jsify() |
- * |
- * To create a JavaScript collection from a Dart collection use the |
- * [JsObject.jsify] constructor, which converts Dart [Map]s and [Iterable]s |
- * into JavaScript Objects and Arrays. |
- * |
- * The following expression creates a new JavaScript object with the properties |
- * `a` and `b` defined: |
- * |
- * var jsMap = new JsObject.jsify({'a': 1, 'b': 2}); |
- * |
- * This expression creates a JavaScript array: |
- * |
- * var jsArray = new JsObject.jsify([1, 2, 3]); |
- */ |
-library dart.js; |
- |
-import 'dart:collection' show ListMixin; |
-import 'dart:nativewrappers'; |
-import 'dart:math' as math; |
-import 'dart:mirrors' as mirrors; |
-import 'dart:html' as html; |
-import 'dart:_blink' as _blink; |
-import 'dart:html_common' as html_common; |
-import 'dart:indexed_db' as indexed_db; |
-import 'dart:typed_data'; |
-import 'dart:core'; |
- |
-import 'cached_patches.dart'; |
- |
-// Pretend we are always in checked mode as we aren't interested in users |
-// running Dartium code outside of checked mode. |
-@Deprecated("Internal Use Only") |
-final bool CHECK_JS_INVOCATIONS = true; |
- |
-final String _DART_RESERVED_NAME_PREFIX = r'JS$'; |
-// If a private class is defined to use @JS we need to inject a non-private |
-// class with a name that will not cause collisions in the library so we can |
-// make JSObject implement that interface even though it is in a different |
-// library. |
-final String escapePrivateClassPrefix = r'$JSImplClass23402893498'; |
- |
-// Exposed to return ArrayBufferView from a TypedArray passed to readPixels. |
-toArrayBufferView(TypedData data) native "Dart_TypedArray_ArrayBufferView"; |
- |
-String _stripReservedNamePrefix(String name) => |
- name.startsWith(_DART_RESERVED_NAME_PREFIX) |
- ? name.substring(_DART_RESERVED_NAME_PREFIX.length) |
- : name; |
- |
-_buildArgs(Invocation invocation) { |
- if (invocation.namedArguments.isEmpty) { |
- return invocation.positionalArguments; |
- } else { |
- var varArgs = new Map<String, Object>(); |
- invocation.namedArguments.forEach((symbol, val) { |
- varArgs[mirrors.MirrorSystem.getName(symbol)] = val; |
- }); |
- return invocation.positionalArguments.toList() |
- ..add(JsNative.jsify(varArgs)); |
- } |
-} |
- |
-final _allowedMethods = new Map<Symbol, _DeclarationSet>(); |
-final _allowedGetters = new Map<Symbol, _DeclarationSet>(); |
-final _allowedSetters = new Map<Symbol, _DeclarationSet>(); |
- |
-final _jsInterfaceTypes = new Set<mirrors.ClassMirror>(); |
-@Deprecated("Internal Use Only") |
-Iterable<mirrors.ClassMirror> get jsInterfaceTypes => _jsInterfaceTypes; |
- |
-class _StringLiteralEscape { |
- // Character code constants. |
- static const int BACKSPACE = 0x08; |
- static const int TAB = 0x09; |
- static const int NEWLINE = 0x0a; |
- static const int CARRIAGE_RETURN = 0x0d; |
- static const int FORM_FEED = 0x0c; |
- static const int QUOTE = 0x22; |
- static const int CHAR_$ = 0x24; |
- static const int CHAR_0 = 0x30; |
- static const int BACKSLASH = 0x5c; |
- static const int CHAR_b = 0x62; |
- static const int CHAR_f = 0x66; |
- static const int CHAR_n = 0x6e; |
- static const int CHAR_r = 0x72; |
- static const int CHAR_t = 0x74; |
- static const int CHAR_u = 0x75; |
- |
- final StringSink _sink; |
- |
- _StringLiteralEscape(this._sink); |
- |
- void writeString(String string) { |
- _sink.write(string); |
- } |
- |
- void writeStringSlice(String string, int start, int end) { |
- _sink.write(string.substring(start, end)); |
- } |
- |
- void writeCharCode(int charCode) { |
- _sink.writeCharCode(charCode); |
- } |
- |
- /// ('0' + x) or ('a' + x - 10) |
- static int hexDigit(int x) => x < 10 ? 48 + x : 87 + x; |
- |
- /// Write, and suitably escape, a string's content as a JSON string literal. |
- void writeStringContent(String s) { |
- // Identical to JSON string literal escaping except that we also escape $. |
- int offset = 0; |
- final int length = s.length; |
- for (int i = 0; i < length; i++) { |
- int charCode = s.codeUnitAt(i); |
- if (charCode > BACKSLASH) continue; |
- if (charCode < 32) { |
- if (i > offset) writeStringSlice(s, offset, i); |
- offset = i + 1; |
- writeCharCode(BACKSLASH); |
- switch (charCode) { |
- case BACKSPACE: |
- writeCharCode(CHAR_b); |
- break; |
- case TAB: |
- writeCharCode(CHAR_t); |
- break; |
- case NEWLINE: |
- writeCharCode(CHAR_n); |
- break; |
- case FORM_FEED: |
- writeCharCode(CHAR_f); |
- break; |
- case CARRIAGE_RETURN: |
- writeCharCode(CHAR_r); |
- break; |
- default: |
- writeCharCode(CHAR_u); |
- writeCharCode(CHAR_0); |
- writeCharCode(CHAR_0); |
- writeCharCode(hexDigit((charCode >> 4) & 0xf)); |
- writeCharCode(hexDigit(charCode & 0xf)); |
- break; |
- } |
- } else if (charCode == QUOTE || |
- charCode == BACKSLASH || |
- charCode == CHAR_$) { |
- if (i > offset) writeStringSlice(s, offset, i); |
- offset = i + 1; |
- writeCharCode(BACKSLASH); |
- writeCharCode(charCode); |
- } |
- } |
- if (offset == 0) { |
- writeString(s); |
- } else if (offset < length) { |
- writeStringSlice(s, offset, length); |
- } |
- } |
- |
- /** |
- * Serialize a [num], [String], [bool], [Null], [List] or [Map] value. |
- * |
- * Returns true if the value is one of these types, and false if not. |
- * If a value is both a [List] and a [Map], it's serialized as a [List]. |
- */ |
- bool writeStringLiteral(String str) { |
- writeString('"'); |
- writeStringContent(str); |
- writeString('"'); |
- } |
-} |
- |
-String _escapeString(String str) { |
- StringBuffer output = new StringBuffer(); |
- new _StringLiteralEscape(output)..writeStringLiteral(str); |
- return output.toString(); |
-} |
- |
-/// A collection of methods where all methods have the same name. |
-/// This class is intended to optimize whether a specific invocation is |
-/// appropriate for at least some of the methods in the collection. |
-class _DeclarationSet { |
- _DeclarationSet() : _members = <mirrors.DeclarationMirror>[]; |
- |
- static bool _checkType(obj, mirrors.TypeMirror type) { |
- if (obj == null) return true; |
- return mirrors.reflectType(obj.runtimeType).isSubtypeOf(type); |
- } |
- |
- /// Returns whether the return [value] has a type is consistent with the |
- /// return type from at least one of the members matching the DeclarationSet. |
- bool _checkReturnType(value) { |
- if (value == null) return true; |
- var valueMirror = mirrors.reflectType(value.runtimeType); |
- for (var member in _members) { |
- if (member is mirrors.VariableMirror || member.isGetter) { |
- // TODO(jacobr): actually check return types for getters that return |
- // function types. |
- return true; |
- } else { |
- if (valueMirror.isSubtypeOf(member.returnType)) return true; |
- } |
- } |
- return false; |
- } |
- |
- /** |
- * Check whether the [invocation] is consistent with the [member] mirror. |
- */ |
- bool _checkDeclaration( |
- Invocation invocation, mirrors.DeclarationMirror member) { |
- if (member is mirrors.VariableMirror || (member as dynamic).isGetter) { |
- // TODO(jacobr): actually check method types against the function type |
- // returned by the getter or field. |
- return true; |
- } |
- var parameters = (member as dynamic).parameters; |
- var positionalArguments = invocation.positionalArguments; |
- // Too many arguments |
- if (parameters.length < positionalArguments.length) return false; |
- // Too few required arguments. |
- if (parameters.length > positionalArguments.length && |
- !parameters[positionalArguments.length].isOptional) return false; |
- for (var i = 0; i < positionalArguments.length; i++) { |
- if (parameters[i].isNamed) { |
- // Not enough positional arguments. |
- return false; |
- } |
- if (!_checkType(invocation.positionalArguments[i], parameters[i].type)) |
- return false; |
- } |
- if (invocation.namedArguments.isNotEmpty) { |
- var startNamed; |
- for (startNamed = parameters.length - 1; startNamed >= 0; startNamed--) { |
- if (!parameters[startNamed].isNamed) break; |
- } |
- startNamed++; |
- |
- // TODO(jacobr): we are unnecessarily using an O(n^2) algorithm here. |
- // If we have JS APIs with a large number of named parameters we should |
- // optimize this. Either use a HashSet or invert this, walking over |
- // parameters, querying invocation, and making sure we match |
- //invocation.namedArguments.size keys. |
- for (var name in invocation.namedArguments.keys) { |
- bool match = false; |
- for (var j = startNamed; j < parameters.length; j++) { |
- var p = parameters[j]; |
- if (p.simpleName == name) { |
- if (!_checkType( |
- invocation.namedArguments[name], parameters[j].type)) |
- return false; |
- match = true; |
- break; |
- } |
- } |
- if (match == false) return false; |
- } |
- } |
- return true; |
- } |
- |
- bool checkInvocation(Invocation invocation) { |
- for (var member in _members) { |
- if (_checkDeclaration(invocation, member)) return true; |
- } |
- return false; |
- } |
- |
- void add(mirrors.DeclarationMirror mirror) { |
- _members.add(mirror); |
- } |
- |
- final List<mirrors.DeclarationMirror> _members; |
-} |
- |
-/** |
- * Temporary method that we hope to remove at some point. This method should |
- * generally only be called by machine generated code. |
- */ |
-@Deprecated("Internal Use Only") |
-void registerJsInterfaces([List<Type> classes]) { |
- // This method is now obsolete in Dartium. |
-} |
- |
-void _registerJsInterfaces(List<Type> classes) { |
- for (Type type in classes) { |
- mirrors.ClassMirror typeMirror = mirrors.reflectType(type); |
- typeMirror.declarations.forEach((symbol, declaration) { |
- if (declaration is mirrors.MethodMirror || |
- declaration is mirrors.VariableMirror && !declaration.isStatic) { |
- bool treatAsGetter = false; |
- bool treatAsSetter = false; |
- if (declaration is mirrors.VariableMirror) { |
- treatAsGetter = true; |
- if (!declaration.isConst && !declaration.isFinal) { |
- treatAsSetter = true; |
- } |
- } else { |
- if (declaration.isGetter) { |
- treatAsGetter = true; |
- } else if (declaration.isSetter) { |
- treatAsSetter = true; |
- } else if (!declaration.isConstructor) { |
- _allowedMethods |
- .putIfAbsent(symbol, () => new _DeclarationSet()) |
- .add(declaration); |
- } |
- } |
- if (treatAsGetter) { |
- _allowedGetters |
- .putIfAbsent(symbol, () => new _DeclarationSet()) |
- .add(declaration); |
- _allowedMethods |
- .putIfAbsent(symbol, () => new _DeclarationSet()) |
- .add(declaration); |
- } |
- if (treatAsSetter) { |
- _allowedSetters |
- .putIfAbsent(symbol, () => new _DeclarationSet()) |
- .add(declaration); |
- } |
- } |
- }); |
- } |
-} |
- |
-_finalizeJsInterfaces() native "Js_finalizeJsInterfaces"; |
- |
-String _getJsName(mirrors.DeclarationMirror mirror) { |
- if (_atJsType != null) { |
- for (var annotation in mirror.metadata) { |
- if (annotation.type.reflectedType == _atJsType) { |
- try { |
- var name = annotation.reflectee.name; |
- return name != null ? name : ""; |
- } catch (e) {} |
- } |
- } |
- } |
- return null; |
-} |
- |
-bool _isAnonymousClass(mirrors.ClassMirror mirror) { |
- for (var annotation in mirror.metadata) { |
- if (mirrors.MirrorSystem.getName(annotation.type.simpleName) == |
- "_Anonymous") { |
- mirrors.LibraryMirror library = annotation.type.owner; |
- var uri = library.uri; |
- // make sure the annotation is from package://js |
- if (uri.scheme == 'package' && uri.path == 'js/js.dart') { |
- return true; |
- } |
- } |
- } |
- return false; |
-} |
- |
-bool _hasJsName(mirrors.DeclarationMirror mirror) { |
- if (_atJsType != null) { |
- for (var annotation in mirror.metadata) { |
- if (annotation.type.reflectedType == _atJsType) { |
- return true; |
- } |
- } |
- } |
- return false; |
-} |
- |
-var _domNameType; |
- |
-bool hasDomName(mirrors.DeclarationMirror mirror) { |
- var location = mirror.location; |
- if (location == null || location.sourceUri.scheme != 'dart') return false; |
- for (var annotation in mirror.metadata) { |
- if (mirrors.MirrorSystem.getName(annotation.type.simpleName) == "DomName") { |
- // We can't make sure the annotation is in dart: as Dartium believes it |
- // is file://dart/sdk/lib/html/html_common/metadata.dart |
- // instead of a proper dart: location. |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-_getJsMemberName(mirrors.DeclarationMirror mirror) { |
- var name = _getJsName(mirror); |
- return name == null || name.isEmpty |
- ? _stripReservedNamePrefix(_getDeclarationName(mirror)) |
- : name; |
-} |
- |
-// TODO(jacobr): handle setters correctyl. |
-String _getDeclarationName(mirrors.DeclarationMirror declaration) { |
- var name = mirrors.MirrorSystem.getName(declaration.simpleName); |
- if (declaration is mirrors.MethodMirror && declaration.isSetter) { |
- assert(name.endsWith("=")); |
- name = name.substring(0, name.length - 1); |
- } |
- return name; |
-} |
- |
-final _JS_LIBRARY_PREFIX = "js_library"; |
-final _UNDEFINED_VAR = "_UNDEFINED_JS_CONST"; |
- |
-String _accessJsPath(String path) => _accessJsPathHelper(path.split(".")); |
- |
-String _accessJsPathHelper(Iterable<String> parts) { |
- var sb = new StringBuffer(); |
- sb |
- ..write('${_JS_LIBRARY_PREFIX}.JsNative.getProperty(' * parts.length) |
- ..write("${_JS_LIBRARY_PREFIX}.context"); |
- for (var p in parts) { |
- sb.write(", ${_escapeString(p)})"); |
- } |
- return sb.toString(); |
-} |
- |
-// TODO(jacobr): remove these helpers and add JsNative.setPropertyDotted, |
-// getPropertyDotted, and callMethodDotted helpers that would be simpler |
-// and more efficient. |
-String _accessJsPathSetter(String path) { |
- var parts = path.split("."); |
- return "${_JS_LIBRARY_PREFIX}.JsNative.setProperty(${_accessJsPathHelper(parts.getRange(0, parts.length - 1)) |
- }, ${_escapeString(parts.last)}, v)"; |
-} |
- |
-String _accessJsPathCallMethodHelper(String path) { |
- var parts = path.split("."); |
- return "${_JS_LIBRARY_PREFIX}.JsNative.callMethod(${_accessJsPathHelper(parts.getRange(0, parts.length - 1)) |
- }, ${_escapeString(parts.last)},"; |
-} |
- |
-@Deprecated("Internal Use Only") |
-void addMemberHelper( |
- mirrors.MethodMirror declaration, String path, StringBuffer sb, |
- {bool isStatic: false, String memberName}) { |
- if (!declaration.isConstructor) { |
- var jsName = _getJsMemberName(declaration); |
- path = (path != null && path.isNotEmpty) ? "${path}.${jsName}" : jsName; |
- } |
- var name = memberName != null ? memberName : _getDeclarationName(declaration); |
- if (declaration.isConstructor) { |
- sb.write("factory"); |
- } else if (isStatic) { |
- sb.write("static"); |
- } else { |
- sb.write("@patch"); |
- } |
- sb.write(" "); |
- if (declaration.isGetter) { |
- sb.write("get $name => ${_accessJsPath(path)};"); |
- } else if (declaration.isSetter) { |
- sb.write("set $name(v) {\n" |
- " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop(v);\n" |
- " return ${_accessJsPathSetter(path)};\n" |
- "}\n"); |
- } else { |
- sb.write("$name("); |
- bool hasOptional = false; |
- int i = 0; |
- var args = <String>[]; |
- for (var p in declaration.parameters) { |
- assert(!p.isNamed); // TODO(jacobr): throw. |
- assert(!p.hasDefaultValue); |
- if (i > 0) { |
- sb.write(", "); |
- } |
- if (p.isOptional && !hasOptional) { |
- sb.write("["); |
- hasOptional = true; |
- } |
- var arg = "p$i"; |
- args.add(arg); |
- sb.write(arg); |
- if (p.isOptional) { |
- sb.write("=${_UNDEFINED_VAR}"); |
- } |
- i++; |
- } |
- if (hasOptional) { |
- sb.write("]"); |
- } |
- // TODO(jacobr): |
- sb.write(") {\n"); |
- for (var arg in args) { |
- sb.write(" ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($arg);\n"); |
- } |
- sb.write(" return "); |
- if (declaration.isConstructor) { |
- sb.write("${_JS_LIBRARY_PREFIX}.JsNative.callConstructor("); |
- sb..write(_accessJsPath(path))..write(","); |
- } else { |
- sb.write(_accessJsPathCallMethodHelper(path)); |
- } |
- sb.write("[${args.join(",")}]"); |
- |
- if (hasOptional) { |
- sb.write(".takeWhile((i) => i != ${_UNDEFINED_VAR}).toList()"); |
- } |
- sb.write(");"); |
- sb.write("}\n"); |
- } |
- sb.write("\n"); |
-} |
- |
-bool _isExternal(mirrors.MethodMirror mirror) { |
- // This try-catch block is a workaround for BUG:24834. |
- try { |
- return mirror.isExternal; |
- } catch (e) {} |
- return false; |
-} |
- |
-List<String> _generateExternalMethods( |
- List<String> libraryPaths, bool useCachedPatches) { |
- var staticCodegen = <String>[]; |
- |
- if (libraryPaths.length == 0) { |
- mirrors.currentMirrorSystem().libraries.forEach((uri, library) { |
- var library_name = "${uri.scheme}:${uri.path}"; |
- if (useCachedPatches && cached_patches.containsKey(library_name)) { |
- // Use the pre-generated patch files for DOM dart:nnnn libraries. |
- var patch = cached_patches[library_name]; |
- staticCodegen.addAll(patch); |
- } else if (_hasJsName(library)) { |
- // Library marked with @JS |
- _generateLibraryCodegen(uri, library, staticCodegen); |
- } else if (!useCachedPatches) { |
- // Can't use the cached patches file, instead this is a signal to generate |
- // the patches for this file. |
- _generateLibraryCodegen(uri, library, staticCodegen); |
- } |
- }); // End of library foreach |
- } else { |
- // Used to generate cached_patches.dart file for all IDL generated dart: |
- // files to the WebKit DOM. |
- for (var library_name in libraryPaths) { |
- var parts = library_name.split(':'); |
- var uri = new Uri(scheme: parts[0], path: parts[1]); |
- var library = mirrors.currentMirrorSystem().libraries[uri]; |
- _generateLibraryCodegen(uri, library, staticCodegen); |
- } |
- } |
- |
- return staticCodegen; |
-} |
- |
-_generateLibraryCodegen(uri, library, staticCodegen) { |
- // Is it a dart generated library? |
- var dartLibrary = uri.scheme == 'dart'; |
- |
- var sb = new StringBuffer(); |
- String jsLibraryName = _getJsName(library); |
- |
- // Sort by patch file by its declaration name. |
- var sortedDeclKeys = library.declarations.keys.toList(); |
- sortedDeclKeys.sort((a, b) => mirrors.MirrorSystem |
- .getName(a) |
- .compareTo(mirrors.MirrorSystem.getName(b))); |
- |
- sortedDeclKeys.forEach((name) { |
- var declaration = library.declarations[name]; |
- if (declaration is mirrors.MethodMirror) { |
- if ((_hasJsName(declaration) || jsLibraryName != null) && |
- _isExternal(declaration)) { |
- addMemberHelper(declaration, jsLibraryName, sb); |
- } |
- } else if (declaration is mirrors.ClassMirror) { |
- mirrors.ClassMirror clazz = declaration; |
- var isDom = dartLibrary ? hasDomName(clazz) : false; |
- var isJsInterop = _hasJsName(clazz); |
- if (isDom || isJsInterop) { |
- // TODO(jacobr): verify class implements JavaScriptObject. |
- var className = mirrors.MirrorSystem.getName(clazz.simpleName); |
- bool isPrivateUserDefinedClass = |
- className.startsWith('_') && !dartLibrary; |
- var classNameImpl = '${className}Impl'; |
- var sbPatch = new StringBuffer(); |
- if (isJsInterop) { |
- String jsClassName = _getJsMemberName(clazz); |
- |
- jsInterfaceTypes.add(clazz); |
- clazz.declarations.forEach((name, declaration) { |
- if (declaration is! mirrors.MethodMirror || |
- !_isExternal(declaration)) return; |
- if (declaration.isFactoryConstructor && _isAnonymousClass(clazz)) { |
- sbPatch.write(" factory ${className}("); |
- int i = 0; |
- var args = <String>[]; |
- for (var p in declaration.parameters) { |
- args.add(mirrors.MirrorSystem.getName(p.simpleName)); |
- i++; |
- } |
- if (args.isNotEmpty) { |
- sbPatch |
- ..write('{') |
- ..write( |
- args.map((name) => '$name:${_UNDEFINED_VAR}').join(", ")) |
- ..write('}'); |
- } |
- sbPatch.write(") {\n" |
- " var ret = ${_JS_LIBRARY_PREFIX}.JsNative.newObject();\n"); |
- i = 0; |
- for (var p in declaration.parameters) { |
- assert(p.isNamed); // TODO(jacobr): throw. |
- var name = args[i]; |
- var jsName = _stripReservedNamePrefix( |
- mirrors.MirrorSystem.getName(p.simpleName)); |
- sbPatch.write(" if($name != ${_UNDEFINED_VAR}) {\n" |
- " ${_JS_LIBRARY_PREFIX}.safeForTypedInterop($name);\n" |
- " ${_JS_LIBRARY_PREFIX}.JsNative.setProperty(ret, ${_escapeString(jsName)}, $name);\n" |
- " }\n"); |
- i++; |
- } |
- |
- sbPatch.write(" return ret;" |
- "}\n"); |
- } else if (declaration.isConstructor || |
- declaration.isFactoryConstructor) { |
- sbPatch.write(" "); |
- addMemberHelper( |
- declaration, |
- (jsLibraryName != null && jsLibraryName.isNotEmpty) |
- ? "${jsLibraryName}.${jsClassName}" |
- : jsClassName, |
- sbPatch, |
- isStatic: true, |
- memberName: className); |
- } |
- }); // End of clazz.declarations.forEach |
- |
- clazz.staticMembers.forEach((memberName, member) { |
- if (_isExternal(member)) { |
- sbPatch.write(" "); |
- addMemberHelper( |
- member, |
- (jsLibraryName != null && jsLibraryName.isNotEmpty) |
- ? "${jsLibraryName}.${jsClassName}" |
- : jsClassName, |
- sbPatch, |
- isStatic: true); |
- } |
- }); |
- } |
- if (isDom) { |
- sbPatch.write( |
- " static Type get instanceRuntimeType => ${classNameImpl};\n"); |
- } |
- if (isPrivateUserDefinedClass) { |
- sb.write(""" |
-class ${escapePrivateClassPrefix}${className} implements $className {} |
-"""); |
- } |
- |
- if (sbPatch.isNotEmpty) { |
- var typeVariablesClause = ''; |
- if (!clazz.typeVariables.isEmpty) { |
- typeVariablesClause = |
- '<${clazz.typeVariables.map((m) => mirrors.MirrorSystem.getName(m.simpleName)).join(',')}>'; |
- } |
- sb.write(""" |
-@patch class $className$typeVariablesClause { |
-$sbPatch |
-} |
-"""); |
- if (isDom) { |
- sb.write(""" |
-class $classNameImpl$typeVariablesClause extends $className implements ${_JS_LIBRARY_PREFIX}.JSObjectInterfacesDom { |
- ${classNameImpl}.internal_() : super.internal_(); |
- get runtimeType => $className; |
- toString() => super.toString(); |
-} |
-"""); |
- } |
- } |
- } |
- } |
- }); |
- if (sb.isNotEmpty) { |
- staticCodegen |
- ..add(uri.toString()) |
- ..add("${uri}_js_interop_patch.dart") |
- ..add(""" |
-import 'dart:js' as ${_JS_LIBRARY_PREFIX}; |
- |
-/** |
- * Placeholder object for cases where we need to determine exactly how many |
- * args were passed to a function. |
- */ |
-const ${_UNDEFINED_VAR} = const Object(); |
- |
-${sb} |
-"""); |
- } |
-} |
- |
-// Remember the @JS type to compare annotation type. |
-var _atJsType = -1; |
- |
-void setupJsTypeCache() { |
- // Cache the @JS Type. |
- if (_atJsType == -1) { |
- var uri = new Uri(scheme: "package", path: "js/js.dart"); |
- var jsLibrary = mirrors.currentMirrorSystem().libraries[uri]; |
- if (jsLibrary != null) { |
- // @ JS used somewhere. |
- var jsDeclaration = jsLibrary.declarations[new Symbol("JS")]; |
- _atJsType = jsDeclaration.reflectedType; |
- } else { |
- // @ JS not used in any library. |
- _atJsType = null; |
- } |
- } |
-} |
- |
-/** |
- * Generates part files defining source code for JSObjectImpl, all DOM classes |
- * classes. This codegen is needed so that type checks for all registered |
- * JavaScript interop classes pass. |
- * If genCachedPatches is true then the patch files don't exist this is a special |
- * signal to generate and emit the patches to stdout to be captured and put into |
- * the file sdk/lib/js/dartium/cached_patches.dart |
- */ |
-List<String> _generateInteropPatchFiles( |
- List<String> libraryPaths, genCachedPatches) { |
- // Cache the @JS Type. |
- if (_atJsType == -1) setupJsTypeCache(); |
- |
- var ret = |
- _generateExternalMethods(libraryPaths, genCachedPatches ? false : true); |
- var libraryPrefixes = new Map<mirrors.LibraryMirror, String>(); |
- var prefixNames = new Set<String>(); |
- var sb = new StringBuffer(); |
- |
- var implements = <String>[]; |
- var implementsArray = <String>[]; |
- var implementsDom = <String>[]; |
- var listMirror = mirrors.reflectType(List); |
- var functionMirror = mirrors.reflectType(Function); |
- var jsObjectMirror = mirrors.reflectType(JSObject); |
- |
- for (var typeMirror in jsInterfaceTypes) { |
- mirrors.LibraryMirror libraryMirror = typeMirror.owner; |
- var location = libraryMirror.location; |
- var dartLibrary = location != null && location.sourceUri.scheme == 'dart'; |
- |
- var prefixName; |
- if (libraryPrefixes.containsKey(libraryMirror)) { |
- prefixName = libraryPrefixes[libraryMirror]; |
- } else { |
- var basePrefixName = |
- mirrors.MirrorSystem.getName(libraryMirror.simpleName); |
- basePrefixName = basePrefixName.replaceAll('.', '_'); |
- if (basePrefixName.isEmpty) basePrefixName = "lib"; |
- prefixName = basePrefixName; |
- var i = 1; |
- while (prefixNames.contains(prefixName)) { |
- prefixName = '$basePrefixName$i'; |
- i++; |
- } |
- prefixNames.add(prefixName); |
- libraryPrefixes[libraryMirror] = prefixName; |
- } |
- var isArray = typeMirror.isSubtypeOf(listMirror); |
- var isFunction = typeMirror.isSubtypeOf(functionMirror); |
- var isJSObject = typeMirror.isSubtypeOf(jsObjectMirror); |
- var className = mirrors.MirrorSystem.getName(typeMirror.simpleName); |
- var isPrivateUserDefinedClass = className.startsWith('_') && !dartLibrary; |
- if (isPrivateUserDefinedClass) |
- className = '${escapePrivateClassPrefix}${className}'; |
- var fullName = '${prefixName}.${className}'; |
- (isArray ? implementsArray : implements).add(fullName); |
- if (!isArray && !isFunction && !isJSObject) { |
- // For DOM classes we need to be a bit more conservative at tagging them |
- // as implementing JS interop classes risks strange unintended |
- // consequences as unrleated code may have instanceof checks. Checking |
- // for isJSObject ensures we do not accidentally pull in existing |
- // dart:html classes as they all have JSObject as a base class. |
- // Note that methods from these classes can still be called on a |
- // dart:html instance but checked mode type checks will fail. This is |
- // not ideal but is better than causing strange breaks in existing |
- // code that uses dart:html. |
- // TODO(jacobr): consider throwing compile time errors if @JS classes |
- // extend JSObject as that case cannot be safely handled in Dartium. |
- implementsDom.add(fullName); |
- } |
- } |
- libraryPrefixes.forEach((libraryMirror, prefix) { |
- sb.writeln('import "${libraryMirror.uri}" as $prefix;'); |
- }); |
- buildImplementsClause(classes) => |
- classes.isEmpty ? "" : "implements ${classes.join(', ')}"; |
- var implementsClause = buildImplementsClause(implements); |
- var implementsClauseDom = buildImplementsClause(implementsDom); |
- // TODO(jacobr): only certain classes need to be implemented by |
- // JsFunctionImpl. |
- var allTypes = []..addAll(implements)..addAll(implementsArray); |
- sb.write(''' |
-class JSObjectImpl extends JSObject $implementsClause { |
- JSObjectImpl.internal() : super.internal(); |
-} |
- |
-class JSFunctionImpl extends JSFunction $implementsClause { |
- JSFunctionImpl.internal() : super.internal(); |
-} |
- |
-class JSArrayImpl extends JSArray ${buildImplementsClause(implementsArray)} { |
- JSArrayImpl.internal() : super.internal(); |
-} |
- |
-// Interfaces that are safe to slam on all DOM classes. |
-// Adding implementsClause would be risky as it could contain Function which |
-// is likely to break a lot of instanceof checks. |
-abstract class JSObjectInterfacesDom $implementsClauseDom { |
-} |
- |
-@patch class JSObject { |
- static Type get instanceRuntimeType => JSObjectImpl; |
-} |
- |
-@patch class JSFunction { |
- static Type get instanceRuntimeType => JSFunctionImpl; |
-} |
- |
-@patch class JSArray { |
- static Type get instanceRuntimeType => JSArrayImpl; |
-} |
- |
-_registerAllJsInterfaces() { |
- _registerJsInterfaces([${allTypes.join(", ")}]); |
-} |
- |
-'''); |
- ret..addAll(["dart:js", "JSInteropImpl.dart", sb.toString()]); |
- return ret; |
-} |
- |
-// Start of block of helper methods facilitating emulating JavaScript Array |
-// methods on Dart List objects passed to JavaScript via JS interop. |
-// TODO(jacobr): match JS more closely. |
-String _toStringJs(obj) => '$obj'; |
- |
-// TODO(jacobr): this might not exactly match JS semantics but should be |
-// adequate for now. |
-int _toIntJs(obj) { |
- if (obj is int) return obj; |
- if (obj is num) return obj.toInt(); |
- return num.parse('$obj'.trim(), (_) => 0).toInt(); |
-} |
- |
-// TODO(jacobr): this might not exactly match JS semantics but should be |
-// adequate for now. |
-num _toNumJs(obj) { |
- return obj is num ? obj : num.parse('$obj'.trim(), (_) => 0); |
-} |
- |
-/// Match the behavior of setting List length in JavaScript with the exception |
-/// that Dart does not distinguish undefined and null. |
-_setListLength(List list, rawlen) { |
- num len = _toNumJs(rawlen); |
- if (len is! int || len < 0) { |
- throw new RangeError("Invalid array length"); |
- } |
- if (len > list.length) { |
- _arrayExtend(list, len); |
- } else if (len < list.length) { |
- list.removeRange(len, list.length); |
- } |
- return rawlen; |
-} |
- |
-// TODO(jacobr): should we really bother with this method instead of just |
-// shallow copying to a JS array and calling the JavaScript join method? |
-String _arrayJoin(List list, sep) { |
- if (sep == null) { |
- sep = ","; |
- } |
- return list.map((e) => e == null ? "" : e.toString()).join(sep.toString()); |
-} |
- |
-// TODO(jacobr): should we really bother with this method instead of just |
-// shallow copying to a JS array and using the toString method? |
-String _arrayToString(List list) => _arrayJoin(list, ","); |
- |
-int _arrayPush(List list, List args) { |
- for (var e in args) { |
- list.add(e); |
- } |
- return list.length; |
-} |
- |
-_arrayPop(List list) { |
- if (list.length > 0) return list.removeLast(); |
-} |
- |
-// TODO(jacobr): would it be better to just copy input to a JS List |
-// and call Array.concat? |
-List _arrayConcat(List input, List args) { |
- var ret = new List.from(input); |
- for (var e in args) { |
- // TODO(jacobr): technically in ES6 we should use |
- // Symbol.isConcatSpreadable to determine whether call addAll. Once v8 |
- // supports it, we can make all Dart classes implementing Iterable |
- // specify isConcatSpreadable and tweak this behavior to allow Iterable. |
- if (e is List) { |
- ret.addAll(e); |
- } else { |
- ret.add(e); |
- } |
- } |
- return ret; |
-} |
- |
-List _arraySplice(List input, List args) { |
- int start = 0; |
- if (args.length > 0) { |
- var rawStart = _toIntJs(args[0]); |
- if (rawStart < 0) { |
- start = math.max(0, input.length - rawStart); |
- } else { |
- start = math.min(input.length, rawStart); |
- } |
- } |
- var end = start; |
- if (args.length > 1) { |
- var rawDeleteCount = _toIntJs(args[1]); |
- if (rawDeleteCount < 0) rawDeleteCount = 0; |
- end = math.min(input.length, start + rawDeleteCount); |
- } |
- var replacement = []; |
- var removedElements = input.getRange(start, end).toList(); |
- if (args.length > 2) { |
- replacement = args.getRange(2, args.length); |
- } |
- input.replaceRange(start, end, replacement); |
- return removedElements; |
-} |
- |
-List _arrayReverse(List l) { |
- for (var i = 0, j = l.length - 1; i < j; i++, j--) { |
- var tmp = l[i]; |
- l[i] = l[j]; |
- l[j] = tmp; |
- } |
- return l; |
-} |
- |
-_arrayShift(List l) { |
- if (l.isEmpty) return null; // Technically we should return undefined. |
- return l.removeAt(0); |
-} |
- |
-int _arrayUnshift(List l, List args) { |
- l.insertAll(0, args); |
- return l.length; |
-} |
- |
-_arrayExtend(List l, int newLength) { |
- for (var i = l.length; i < newLength; i++) { |
- // TODO(jacobr): we'd really like to add undefined to better match |
- // JavaScript semantics. |
- l.add(null); |
- } |
-} |
- |
-List _arraySort(List l, rawCompare) { |
- // TODO(jacobr): alternately we could just copy the Array to JavaScript, |
- // invoke the JS sort method and then copy the result back to Dart. |
- Comparator compare; |
- if (rawCompare == null) { |
- compare = (a, b) => _toStringJs(a).compareTo(_toStringJs(b)); |
- } else if (rawCompare is JsFunction) { |
- compare = (a, b) => rawCompare.apply([a, b]); |
- } else { |
- compare = rawCompare; |
- } |
- l.sort(compare); |
- return l; |
-} |
-// End of block of helper methods to emulate JavaScript Array methods on Dart List. |
- |
-/** |
- * Can be called to provide a predictable point where no more JS interfaces can |
- * be added. Creating an instance of JsObject will also automatically trigger |
- * all JsObjects to be finalized. |
- */ |
-@Deprecated("Internal Use Only") |
-void finalizeJsInterfaces() { |
- if (_finalized == true) { |
- throw 'JSInterop class registration already finalized'; |
- } |
- _finalizeJsInterfaces(); |
-} |
- |
-JsObject _cachedContext; |
- |
-JsObject get _context native "Js_context_Callback"; |
- |
-bool get _finalized native "Js_interfacesFinalized_Callback"; |
- |
-JsObject get context { |
- if (_cachedContext == null) { |
- _cachedContext = _context; |
- } |
- return _cachedContext; |
-} |
- |
-_lookupType(o, bool isCrossFrame, bool isElement) { |
- try { |
- var type = html_common.lookupType(o, isElement); |
- var typeMirror = mirrors.reflectType(type); |
- var legacyInteropConvertToNative = |
- typeMirror.isSubtypeOf(mirrors.reflectType(html.Blob)) || |
- typeMirror.isSubtypeOf(mirrors.reflectType(html.Event)) || |
- typeMirror.isSubtypeOf(mirrors.reflectType(indexed_db.KeyRange)) || |
- typeMirror.isSubtypeOf(mirrors.reflectType(html.ImageData)) || |
- typeMirror.isSubtypeOf(mirrors.reflectType(html.Node)) || |
-// TypedData is removed from this list as it is converted directly |
-// rather than flowing through the interceptor code path. |
-// typeMirror.isSubtypeOf(mirrors.reflectType(typed_data.TypedData)) || |
- typeMirror.isSubtypeOf(mirrors.reflectType(html.Window)); |
- if (isCrossFrame && |
- !typeMirror.isSubtypeOf(mirrors.reflectType(html.Window))) { |
- // TODO(jacobr): evaluate using the true cross frame Window class, etc. |
- // as well as triggering that legacy JS Interop returns raw JsObject |
- // instances. |
- legacyInteropConvertToNative = false; |
- } |
- return [type, legacyInteropConvertToNative]; |
- } catch (e) {} |
- return [JSObject.instanceRuntimeType, false]; |
-} |
- |
-/** |
- * Base class for both the legacy JsObject class and the modern JSObject class. |
- * This allows the JsNative utility class tobehave identically whether it is |
- * called on a JsObject or a JSObject. |
- */ |
-class _JSObjectBase extends NativeFieldWrapperClass2 { |
- String _toString() native "JSObject_toString"; |
- _callMethod(String name, List args) native "JSObject_callMethod"; |
- _operator_getter(String property) native "JSObject_[]"; |
- _operator_setter(String property, value) native "JSObject_[]="; |
- bool _hasProperty(String property) native "JsObject_hasProperty"; |
- bool _instanceof(/*JsFunction|JSFunction*/ type) native "JsObject_instanceof"; |
- |
- int get hashCode native "JSObject_hashCode"; |
-} |
- |
-/** |
- * Proxies a JavaScript object to Dart. |
- * |
- * The properties of the JavaScript object are accessible via the `[]` and |
- * `[]=` operators. Methods are callable via [callMethod]. |
- */ |
-class JsObject extends _JSObjectBase { |
- JsObject.internal(); |
- |
- /** |
- * Constructs a new JavaScript object from [constructor] and returns a proxy |
- * to it. |
- */ |
- factory JsObject(JsFunction constructor, [List arguments]) { |
- try { |
- return _create(constructor, arguments); |
- } catch (e) { |
- // Re-throw any errors (returned as a string) as a DomException. |
- throw new html.DomException.jsInterop(e); |
- } |
- } |
- |
- static JsObject _create(JsFunction constructor, arguments) |
- native "JsObject_constructorCallback"; |
- |
- /** |
- * Constructs a [JsObject] that proxies a native Dart object; _for expert use |
- * only_. |
- * |
- * Use this constructor only if you wish to get access to JavaScript |
- * properties attached to a browser host object, such as a Node or Blob, that |
- * is normally automatically converted into a native Dart object. |
- * |
- * An exception will be thrown if [object] either is `null` or has the type |
- * `bool`, `num`, or `String`. |
- */ |
- factory JsObject.fromBrowserObject(object) { |
- if (object is num || object is String || object is bool || object == null) { |
- throw new ArgumentError("object cannot be a num, string, bool, or null"); |
- } |
- if (object is JsObject) return object; |
- return _fromBrowserObject(object); |
- } |
- |
- /** |
- * Recursively converts a JSON-like collection of Dart objects to a |
- * collection of JavaScript objects and returns a [JsObject] proxy to it. |
- * |
- * [object] must be a [Map] or [Iterable], the contents of which are also |
- * converted. Maps and Iterables are copied to a new JavaScript object. |
- * Primitives and other transferrable values are directly converted to their |
- * JavaScript type, and all other objects are proxied. |
- */ |
- factory JsObject.jsify(object) { |
- if ((object is! Map) && (object is! Iterable)) { |
- throw new ArgumentError("object must be a Map or Iterable"); |
- } |
- return _jsify(object); |
- } |
- |
- static JsObject _jsify(object) native "JsObject_jsify"; |
- |
- static JsObject _fromBrowserObject(object) |
- native "JsObject_fromBrowserObject"; |
- |
- /** |
- * Returns the value associated with [property] from the proxied JavaScript |
- * object. |
- * |
- * The type of [property] must be either [String] or [num]. |
- */ |
- operator [](property) { |
- try { |
- return _operator_getterLegacy(property); |
- } catch (e) { |
- // Re-throw any errors (returned as a string) as a DomException. |
- throw new html.DomException.jsInterop(e); |
- } |
- } |
- |
- _operator_getterLegacy(property) native "JsObject_[]Legacy"; |
- |
- /** |
- * Sets the value associated with [property] on the proxied JavaScript |
- * object. |
- * |
- * The type of [property] must be either [String] or [num]. |
- */ |
- operator []=(property, value) { |
- try { |
- _operator_setterLegacy(property, value); |
- } catch (e) { |
- // Re-throw any errors (returned as a string) as a DomException. |
- throw new html.DomException.jsInterop(e); |
- } |
- } |
- |
- _operator_setterLegacy(property, value) native "JsObject_[]=Legacy"; |
- |
- int get hashCode native "JsObject_hashCode"; |
- |
- operator ==(other) { |
- if (other is! JsObject && other is! JSObject) return false; |
- return _identityEquality(this, other); |
- } |
- |
- static bool _identityEquality(a, b) native "JsObject_identityEquality"; |
- |
- /** |
- * Returns `true` if the JavaScript object contains the specified property |
- * either directly or though its prototype chain. |
- * |
- * This is the equivalent of the `in` operator in JavaScript. |
- */ |
- bool hasProperty(String property) => _hasProperty(property); |
- |
- /** |
- * Removes [property] from the JavaScript object. |
- * |
- * This is the equivalent of the `delete` operator in JavaScript. |
- */ |
- void deleteProperty(String property) native "JsObject_deleteProperty"; |
- |
- /** |
- * Returns `true` if the JavaScript object has [type] in its prototype chain. |
- * |
- * This is the equivalent of the `instanceof` operator in JavaScript. |
- */ |
- bool instanceof(JsFunction type) => _instanceof(type); |
- |
- /** |
- * Returns the result of the JavaScript objects `toString` method. |
- */ |
- String toString() { |
- try { |
- return _toString(); |
- } catch (e) { |
- return super.toString(); |
- } |
- } |
- |
- String _toString() native "JsObject_toString"; |
- |
- /** |
- * Calls [method] on the JavaScript object with the arguments [args] and |
- * returns the result. |
- * |
- * The type of [method] must be either [String] or [num]. |
- */ |
- callMethod(String method, [List args]) { |
- try { |
- return _callMethodLegacy(method, args); |
- } catch (e) { |
- if (hasProperty(method)) { |
- // Return a DomException if DOM call returned an error. |
- throw new html.DomException.jsInterop(e); |
- } else { |
- throw new NoSuchMethodError(this, new Symbol(method), args, null); |
- } |
- } |
- } |
- |
- _callMethodLegacy(String name, List args) native "JsObject_callMethodLegacy"; |
-} |
- |
-/// Base class for all JS objects used through dart:html and typed JS interop. |
-@Deprecated("Internal Use Only") |
-class JSObject extends _JSObjectBase { |
- JSObject.internal() {} |
- external static Type get instanceRuntimeType; |
- |
- /** |
- * Returns the result of the JavaScript objects `toString` method. |
- */ |
- String toString() { |
- try { |
- return _toString(); |
- } catch (e) { |
- return super.toString(); |
- } |
- } |
- |
- noSuchMethod(Invocation invocation) { |
- throwError() { |
- super.noSuchMethod(invocation); |
- } |
- |
- String name = _stripReservedNamePrefix( |
- mirrors.MirrorSystem.getName(invocation.memberName)); |
- argsSafeForTypedInterop(invocation.positionalArguments); |
- if (invocation.isGetter) { |
- if (CHECK_JS_INVOCATIONS) { |
- var matches = _allowedGetters[invocation.memberName]; |
- if (matches == null && |
- !_allowedMethods.containsKey(invocation.memberName)) { |
- throwError(); |
- } |
- var ret = _operator_getter(name); |
- if (matches != null) return ret; |
- if (ret is Function || |
- (ret is JsFunction /* shouldn't be needed in the future*/) && |
- _allowedMethods.containsKey(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. |
- throwError(); |
- } else { |
- // TODO(jacobr): should we throw if the JavaScript object doesn't have the property? |
- return _operator_getter(name); |
- } |
- } else if (invocation.isSetter) { |
- if (CHECK_JS_INVOCATIONS) { |
- var matches = _allowedSetters[invocation.memberName]; |
- if (matches == null || !matches.checkInvocation(invocation)) |
- throwError(); |
- } |
- assert(name.endsWith("=")); |
- name = name.substring(0, name.length - 1); |
- return _operator_setter(name, invocation.positionalArguments.first); |
- } else { |
- // TODO(jacobr): also allow calling getters that look like functions. |
- var matches; |
- if (CHECK_JS_INVOCATIONS) { |
- matches = _allowedMethods[invocation.memberName]; |
- if (matches == null || !matches.checkInvocation(invocation)) |
- throwError(); |
- } |
- var ret = _callMethod(name, _buildArgs(invocation)); |
- if (CHECK_JS_INVOCATIONS) { |
- if (!matches._checkReturnType(ret)) { |
- html.window.console.error("Return value for method: ${name} is " |
- "${ret.runtimeType} which is inconsistent with all typed " |
- "JS interop definitions for method ${name}."); |
- } |
- } |
- return ret; |
- } |
- } |
-} |
- |
-@Deprecated("Internal Use Only") |
-class JSArray extends JSObject with ListMixin { |
- JSArray.internal() : super.internal(); |
- external static Type get instanceRuntimeType; |
- |
- // Reuse JsArray_length as length behavior is unchanged. |
- int get length native "JsArray_length"; |
- |
- set length(int length) { |
- _operator_setter('length', length); |
- } |
- |
- _checkIndex(int index, {bool insert: false}) { |
- int length = insert ? this.length + 1 : this.length; |
- if (index is int && (index < 0 || index >= length)) { |
- throw new RangeError.range(index, 0, length); |
- } |
- } |
- |
- _checkRange(int start, int end) { |
- int cachedLength = this.length; |
- if (start < 0 || start > cachedLength) { |
- throw new RangeError.range(start, 0, cachedLength); |
- } |
- if (end < start || end > cachedLength) { |
- throw new RangeError.range(end, start, cachedLength); |
- } |
- } |
- |
- _indexed_getter(int index) native "JSArray_indexed_getter"; |
- _indexed_setter(int index, o) native "JSArray_indexed_setter"; |
- |
- // Methods required by ListMixin |
- |
- operator [](index) { |
- if (index is int) { |
- _checkIndex(index); |
- } |
- |
- return _indexed_getter(index); |
- } |
- |
- void operator []=(int index, value) { |
- _checkIndex(index); |
- _indexed_setter(index, value); |
- } |
-} |
- |
-@Deprecated("Internal Use Only") |
-class JSFunction extends JSObject implements Function { |
- JSFunction.internal() : super.internal(); |
- |
- external static Type get instanceRuntimeType; |
- |
- call( |
- [a1 = _UNDEFINED, |
- a2 = _UNDEFINED, |
- a3 = _UNDEFINED, |
- a4 = _UNDEFINED, |
- a5 = _UNDEFINED, |
- a6 = _UNDEFINED, |
- a7 = _UNDEFINED, |
- a8 = _UNDEFINED, |
- a9 = _UNDEFINED, |
- a10 = _UNDEFINED]) { |
- return _apply( |
- _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); |
- } |
- |
- noSuchMethod(Invocation invocation) { |
- if (invocation.isMethod && invocation.memberName == #call) { |
- return _apply(_buildArgs(invocation)); |
- } |
- return super.noSuchMethod(invocation); |
- } |
- |
- dynamic _apply(List args, {thisArg}) native "JSFunction_apply"; |
- |
- static JSFunction _createWithThis(Function f) |
- native "JSFunction_createWithThis"; |
- static JSFunction _create(Function f) native "JSFunction_create"; |
-} |
- |
-// JavaScript interop methods that do not automatically wrap to dart:html types. |
-// Warning: this API is not exposed to dart:js. |
-// TODO(jacobr): rename to JSNative and make at least part of this API public. |
-@Deprecated("Internal Use Only") |
-class JsNative { |
- static JSObject jsify(object) native "JSObject_jsify"; |
- static JSObject newObject() native "JSObject_newObject"; |
- static JSArray newArray() native "JSObject_newArray"; |
- |
- static hasProperty(_JSObjectBase o, name) => o._hasProperty(name); |
- static getProperty(_JSObjectBase o, name) => o._operator_getter(name); |
- static setProperty(_JSObjectBase o, name, value) => |
- o._operator_setter(name, value); |
- static callMethod(_JSObjectBase o, String method, List args) => |
- o._callMethod(method, args); |
- static instanceof(_JSObjectBase o, /*JsFunction|JSFunction*/ type) => |
- o._instanceof(type); |
- static callConstructor0(_JSObjectBase constructor) |
- native "JSNative_callConstructor0"; |
- static callConstructor(_JSObjectBase constructor, List args) |
- native "JSNative_callConstructor"; |
- |
- static toTypedObject(JsObject o) native "JSNative_toTypedObject"; |
- |
- /** |
- * Same behavior as new JsFunction.withThis except that JavaScript "this" is not |
- * wrapped. |
- */ |
- static JSFunction withThis(Function f) native "JsFunction_withThisNoWrap"; |
-} |
- |
-/** |
- * Proxies a JavaScript Function object. |
- */ |
-class JsFunction extends JsObject { |
- JsFunction.internal() : super.internal(); |
- |
- /** |
- * Returns a [JsFunction] that captures its 'this' binding and calls [f] |
- * with the value of this passed as the first argument. |
- */ |
- factory JsFunction.withThis(Function f) => _withThis(f); |
- |
- /** |
- * Invokes the JavaScript function with arguments [args]. If [thisArg] is |
- * supplied it is the value of `this` for the invocation. |
- */ |
- dynamic apply(List args, {thisArg}) => _apply(args, thisArg: thisArg); |
- |
- dynamic _apply(List args, {thisArg}) native "JsFunction_apply"; |
- |
- /** |
- * Internal only version of apply which uses debugger proxies of Dart objects |
- * rather than opaque handles. This method is private because it cannot be |
- * efficiently implemented in Dart2Js so should only be used by internal |
- * tools. |
- */ |
- _applyDebuggerOnly(List args, {thisArg}) |
- native "JsFunction_applyDebuggerOnly"; |
- |
- static JsFunction _withThis(Function f) native "JsFunction_withThis"; |
-} |
- |
-/** |
- * A [List] proxying a JavaScript Array. |
- */ |
-class JsArray<E> extends JsObject with ListMixin<E> { |
- JsArray.internal() : super.internal(); |
- |
- factory JsArray() => _newJsArray(); |
- |
- static JsArray _newJsArray() native "JsArray_newJsArray"; |
- |
- factory JsArray.from(Iterable<E> other) => |
- _newJsArrayFromSafeList(new List.from(other)); |
- |
- static JsArray _newJsArrayFromSafeList(List list) |
- native "JsArray_newJsArrayFromSafeList"; |
- |
- _checkIndex(int index, {bool insert: false}) { |
- int length = insert ? this.length + 1 : this.length; |
- if (index is int && (index < 0 || index >= length)) { |
- throw new RangeError.range(index, 0, length); |
- } |
- } |
- |
- _checkRange(int start, int end) { |
- int cachedLength = this.length; |
- if (start < 0 || start > cachedLength) { |
- throw new RangeError.range(start, 0, cachedLength); |
- } |
- if (end < start || end > cachedLength) { |
- throw new RangeError.range(end, start, cachedLength); |
- } |
- } |
- |
- // Methods required by ListMixin |
- |
- E operator [](index) { |
- if (index is int) { |
- _checkIndex(index); |
- } |
- |
- return super[index]; |
- } |
- |
- void operator []=(index, E value) { |
- if (index is int) { |
- _checkIndex(index); |
- } |
- super[index] = value; |
- } |
- |
- int get length native "JsArray_length"; |
- |
- set length(int length) { |
- super['length'] = length; |
- } |
- |
- // Methods overridden for better performance |
- |
- void add(E value) { |
- callMethod('push', [value]); |
- } |
- |
- void addAll(Iterable<E> iterable) { |
- // TODO(jacobr): this can be optimized slightly. |
- callMethod('push', new List.from(iterable)); |
- } |
- |
- void insert(int index, E element) { |
- _checkIndex(index, insert: true); |
- callMethod('splice', [index, 0, element]); |
- } |
- |
- E removeAt(int index) { |
- _checkIndex(index); |
- return callMethod('splice', [index, 1])[0]; |
- } |
- |
- E removeLast() { |
- if (length == 0) throw new RangeError(-1); |
- return callMethod('pop'); |
- } |
- |
- void removeRange(int start, int end) { |
- _checkRange(start, end); |
- callMethod('splice', [start, end - start]); |
- } |
- |
- void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { |
- _checkRange(start, end); |
- int length = end - start; |
- if (length == 0) return; |
- if (skipCount < 0) throw new ArgumentError(skipCount); |
- var args = [start, length]..addAll(iterable.skip(skipCount).take(length)); |
- callMethod('splice', args); |
- } |
- |
- void sort([int compare(E a, E b)]) { |
- callMethod('sort', [compare]); |
- } |
-} |
- |
-/** |
- * Placeholder object for cases where we need to determine exactly how many |
- * args were passed to a function. |
- */ |
-const _UNDEFINED = const Object(); |
- |
-// TODO(jacobr): this method is a hack to work around the lack of proper dart |
-// support for varargs methods. |
-List _stripUndefinedArgs(List args) => |
- args.takeWhile((i) => i != _UNDEFINED).toList(); |
- |
-/** |
- * Check that that if [arg] is a [Function] it is safe to pass to JavaScript. |
- * To make a function safe, call [allowInterop] or [allowInteropCaptureThis]. |
- */ |
-@Deprecated("Internal Use Only") |
-safeForTypedInterop(arg) { |
- if (CHECK_JS_INVOCATIONS && arg is Function && arg is! JSFunction) { |
- throw new ArgumentError( |
- "Attempt to pass Function '$arg' to JavaScript via without calling allowInterop or allowInteropCaptureThis"); |
- } |
-} |
- |
-/** |
- * Check that that if any elements of [args] are [Function] it is safe to pass |
- * to JavaScript. To make a function safe, call [allowInterop] or |
- * [allowInteropCaptureThis]. |
- */ |
-@Deprecated("Internal Use Only") |
-void argsSafeForTypedInterop(Iterable args) { |
- for (var arg in args) { |
- safeForTypedInterop(arg); |
- } |
-} |
- |
-/** |
- * Returns a method that can be called with an arbitrary number (for n less |
- * than 11) of arguments without violating Dart type checks. |
- */ |
-Function _wrapAsDebuggerVarArgsFunction(JsFunction jsFunction) => ( |
- [a1 = _UNDEFINED, |
- a2 = _UNDEFINED, |
- a3 = _UNDEFINED, |
- a4 = _UNDEFINED, |
- a5 = _UNDEFINED, |
- a6 = _UNDEFINED, |
- a7 = _UNDEFINED, |
- a8 = _UNDEFINED, |
- a9 = _UNDEFINED, |
- a10 = _UNDEFINED]) => |
- jsFunction._applyDebuggerOnly( |
- _stripUndefinedArgs([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])); |
- |
-/// Returns a wrapper around function [f] that can be called from JavaScript |
-/// using the package:js Dart-JavaScript interop. |
-/// |
-/// For performance reasons in Dart2Js, by default Dart functions cannot be |
-/// passed directly to JavaScript unless this method is called to create |
-/// a Function compatible with both Dart and JavaScript. |
-/// Calling this method repeatedly on a function will return the same function. |
-/// The [Function] returned by this method can be used from both Dart and |
-/// JavaScript. We may remove the need to call this method completely in the |
-/// future if Dart2Js is refactored so that its function calling conventions |
-/// are more compatible with JavaScript. |
-Function/*=F*/ allowInterop/*<F extends Function>*/(Function/*=F*/ f) { |
- if (f is JSFunction) { |
- // The function is already a JSFunction... no need to do anything. |
- return f; |
- } else { |
- return JSFunction._create(f); |
- } |
-} |
- |
-/// Cached JSFunction associated with the Dart function when "this" is |
-/// captured. |
-Expando<JSFunction> _interopCaptureThisExpando = new Expando<JSFunction>(); |
- |
-/// Returns a [Function] that when called from JavaScript captures its 'this' |
-/// binding and calls [f] with the value of this passed as the first argument. |
-/// When called from Dart, [null] will be passed as the first argument. |
-/// |
-/// See the documentation for [allowInterop]. This method should only be used |
-/// with package:js Dart-JavaScript interop. |
-JSFunction allowInteropCaptureThis(Function f) { |
- if (f is JSFunction) { |
- // Behavior when the function is already a JS function is unspecified. |
- throw new ArgumentError( |
- "Function is already a JS function so cannot capture this."); |
- return f; |
- } else { |
- var ret = _interopCaptureThisExpando[f]; |
- if (ret == null) { |
- // TODO(jacobr): we could optimize this. |
- ret = JSFunction._createWithThis(f); |
- _interopCaptureThisExpando[f] = ret; |
- } |
- return ret; |
- } |
-} |