OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /// Utility methods to efficiently manipulate typed JSInterop objects in cases |
| 6 /// where the name to call is not known at runtime. You should only use these |
| 7 /// methods when the same effect cannot be achieved with @JS annotations. |
| 8 /// These methods would be extension methods on JSObject if Dart supported |
| 9 /// extension methods. |
| 10 library dart.js_util; |
| 11 |
| 12 import 'dart:_foreign_helper' show JS; |
| 13 import 'dart:collection' show HashMap; |
| 14 |
| 15 /// WARNING: performance of this method is much worse than other uitil |
| 16 /// methods in this library. Only use this method as a last resort. |
| 17 /// |
| 18 /// Recursively converts a JSON-like collection of Dart objects to a |
| 19 /// collection of JavaScript objects and returns a [JsObject] proxy to it. |
| 20 /// |
| 21 /// [object] must be a [Map] or [Iterable], the contents of which are also |
| 22 /// converted. Maps and Iterables are copied to a new JavaScript object. |
| 23 /// Primitives and other transferrable values are directly converted to their |
| 24 /// JavaScript type, and all other objects are proxied. |
| 25 jsify(object) { |
| 26 if ((object is! Map) && (object is! Iterable)) { |
| 27 throw new ArgumentError("object must be a Map or Iterable"); |
| 28 } |
| 29 return _convertDataTree(object); |
| 30 } |
| 31 |
| 32 _convertDataTree(data) { |
| 33 var _convertedObjects = new HashMap.identity(); |
| 34 |
| 35 _convert(o) { |
| 36 if (_convertedObjects.containsKey(o)) { |
| 37 return _convertedObjects[o]; |
| 38 } |
| 39 if (o is Map) { |
| 40 final convertedMap = JS('=Object', '{}'); |
| 41 _convertedObjects[o] = convertedMap; |
| 42 for (var key in o.keys) { |
| 43 JS('=Object', '#[#]=#', convertedMap, key, _convert(o[key])); |
| 44 } |
| 45 return convertedMap; |
| 46 } else if (o is Iterable) { |
| 47 var convertedList = []; |
| 48 _convertedObjects[o] = convertedList; |
| 49 convertedList.addAll(o.map(_convert)); |
| 50 return convertedList; |
| 51 } else { |
| 52 return o; |
| 53 } |
| 54 } |
| 55 |
| 56 return _convert(data); |
| 57 } |
| 58 |
| 59 JSObject newObject() => JS('=Object', '{}'); |
| 60 |
| 61 hasProperty(o, name) => JS('bool', '# in #', name, o); |
| 62 getProperty(o, name) => JS('Object', '#[#]', o, name); |
| 63 setProperty(o, name, value) => JS('', '#[#]=#', o, name, value); |
| 64 |
| 65 callMethod(o, String method, List args) => |
| 66 JS('Object', '#[#].apply(#, #)', o, method, o, args); |
| 67 |
| 68 instanceof(o, Function type) => JS('bool', '# instanceof #', o, type); |
| 69 callConstructor(Function constr, List arguments) { |
| 70 if (arguments == null) { |
| 71 return JS('Object', 'new #()', constr); |
| 72 } |
| 73 |
| 74 if (JS('bool', '# instanceof Array', arguments)) { |
| 75 int argumentCount = JS('int', '#.length', arguments); |
| 76 switch (argumentCount) { |
| 77 case 0: |
| 78 return JS('Object', 'new #()', constr); |
| 79 |
| 80 case 1: |
| 81 var arg0 = JS('', '#[0]', arguments); |
| 82 return JS('Object', 'new #(#)', constr, arg0); |
| 83 |
| 84 case 2: |
| 85 var arg0 = JS('', '#[0]', arguments); |
| 86 var arg1 = JS('', '#[1]', arguments); |
| 87 return JS('Object', 'new #(#, #)', constr, arg0, arg1); |
| 88 |
| 89 case 3: |
| 90 var arg0 = JS('', '#[0]', arguments); |
| 91 var arg1 = JS('', '#[1]', arguments); |
| 92 var arg2 = JS('', '#[2]', arguments); |
| 93 return JS('Object', 'new #(#, #, #)', constr, arg0, arg1, arg2); |
| 94 |
| 95 case 4: |
| 96 var arg0 = JS('', '#[0]', arguments); |
| 97 var arg1 = JS('', '#[1]', arguments); |
| 98 var arg2 = JS('', '#[2]', arguments); |
| 99 var arg3 = JS('', '#[3]', arguments); |
| 100 return JS( |
| 101 'Object', 'new #(#, #, #, #)', constr, arg0, arg1, arg2, arg3); |
| 102 } |
| 103 } |
| 104 |
| 105 // The following code solves the problem of invoking a JavaScript |
| 106 // constructor with an unknown number arguments. |
| 107 // First bind the constructor to the argument list using bind.apply(). |
| 108 // The first argument to bind() is the binding of 't', so add 'null' to |
| 109 // the arguments list passed to apply(). |
| 110 // After that, use the JavaScript 'new' operator which overrides any binding |
| 111 // of 'this' with the new instance. |
| 112 var args = [null]..addAll(arguments); |
| 113 var factoryFunction = JS('', '#.bind.apply(#, #)', constr, constr, args); |
| 114 // Without this line, calling factoryFunction as a constructor throws |
| 115 JS('String', 'String(#)', factoryFunction); |
| 116 // This could return an UnknownJavaScriptObject, or a native |
| 117 // object for which there is an interceptor |
| 118 return JS('Object', 'new #()', factoryFunction); |
| 119 |
| 120 // TODO(sra): Investigate: |
| 121 // |
| 122 // var jsObj = JS('', 'Object.create(#.prototype)', constr); |
| 123 // JS('', '#.apply(#, #)', constr, jsObj, |
| 124 // []..addAll(arguments.map(_convertToJS))); |
| 125 // return _wrapToDart(jsObj); |
| 126 } |
OLD | NEW |