| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2012, 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 String chromeTypeNameOf(obj) { |
| 6 String name = JS('String', "#.constructor.name", obj); |
| 7 if (name == 'Window') return 'DOMWindow'; |
| 8 return name; |
| 9 } |
| 10 |
| 11 String firefoxTypeNameOf(obj) { |
| 12 String name = constructorNameWithFallback(obj); |
| 13 if (name == 'Window') return 'DOMWindow'; |
| 14 if (name == 'Document') return 'HTMLDocument'; |
| 15 if (name == 'XMLDocument') return 'Document'; |
| 16 return name; |
| 17 } |
| 18 |
| 19 String ieTypeNameOf(obj) { |
| 20 var name = constructorNameWithFallback(obj); |
| 21 if (name == 'Window') return 'DOMWindow'; |
| 22 // IE calls both HTML and XML documents 'Document', so we check for the |
| 23 // xmlVersion property, which is the empty string on HTML documents. |
| 24 if (name == 'Document' && JS('bool', '#.xmlVersion', obj)) return 'Document'; |
| 25 if (name == 'Document') return 'HTMLDocument'; |
| 26 return name; |
| 27 } |
| 28 |
| 29 String constructorNameWithFallback(obj) { |
| 30 var constructor = JS('var', "#.constructor", obj); |
| 31 if (JS('bool', "typeof(#) == 'function'", constructor)) { |
| 32 // The constructor isn't null or undefined at this point. Try |
| 33 // to grab hold of its name. |
| 34 var name = JS('var', '#.name', constructor); |
| 35 // If the name is a non-empty string, we use that as the type |
| 36 // name of this object. On Firefox, we often get 'Object' as |
| 37 // the constructor name even for more specialized objects so |
| 38 // we have to fall through to the toString() based implementation |
| 39 // below in that case. |
| 40 if (JS('bool', "typeof(#) == 'string'", name) |
| 41 && JS('bool', '#', name) |
| 42 && name != 'Object') { |
| 43 return name; |
| 44 } |
| 45 } |
| 46 String string = JS('String', 'Object.prototype.toString.call(#)', obj); |
| 47 return string.substring(8, string.length - 1); |
| 48 } |
| 49 |
| 50 |
| 51 /** |
| 52 * Returns the function to use to get the type name of an object. |
| 53 */ |
| 54 Function getTypeNameOfFunction() { |
| 55 // If we're not in the browser, we're almost certainly running on v8. |
| 56 if (JS("bool", "typeof(navigator) != 'object'")) return chromeTypeNameOf; |
| 57 |
| 58 String userAgent = JS('String', "navigator.userAgent"); |
| 59 if (userAgent.contains(const RegExp('Chrome|DumpRenderTree'))) { |
| 60 return chromeTypeNameOf; |
| 61 } else if (userAgent.contains(const RegExp('Firefox'))) { |
| 62 return firefoxTypeNameOf; |
| 63 } else if (userAgent.contains('MSIE')) { |
| 64 return ieTypeNameOf; |
| 65 } else { |
| 66 return constructorNameWithFallback; |
| 67 } |
| 68 } |
| 69 |
| 70 |
| 71 /** |
| 72 * Cached value for the function to use to get the type name of an |
| 73 * object. |
| 74 */ |
| 75 Function _getTypeNameOf; |
| 76 |
| 77 /** |
| 78 * Returns the type name of [obj]. |
| 79 */ |
| 80 String getTypeNameOf(var obj) { |
| 81 if (_getTypeNameOf == null) _getTypeNameOf = getTypeNameOfFunction(); |
| 82 return _getTypeNameOf(obj); |
| 83 } |
| 84 |
| 85 /** |
| 86 * Sets a JavaScript property on an object. |
| 87 */ |
| 88 void defineProperty(var obj, String property, var value) { |
| 89 JS('void', """Object.defineProperty(#, #, |
| 90 {value: #, enumerable: false, writable: false, configurable: true});""", |
| 91 obj, |
| 92 property, |
| 93 value); |
| 94 } |
| 95 |
| 96 /** |
| 97 * Helper method to throw a [NoSuchMethodException] for a invalid call |
| 98 * on a native object. |
| 99 */ |
| 100 void throwNoSuchMethod(obj, name, arguments) { |
| 101 throw new NoSuchMethodException(obj, name, arguments); |
| 102 } |
| 103 |
| 104 /** |
| 105 * Returns a method that queries [methods] with the prototype of [obj], |
| 106 * patches the prototype of [obj] with the found JS code, |
| 107 * and invokes the JS code. |
| 108 */ |
| 109 dynamicBind(var obj, |
| 110 String name, |
| 111 var methods, |
| 112 List arguments) { |
| 113 String tag = getTypeNameOf(obj); |
| 114 var method = JS('var', '#[#]', methods, tag); |
| 115 |
| 116 if (method === null && _dynamicMetadata !== null) { |
| 117 for (int i = 0; i < _dynamicMetadata.length; i++) { |
| 118 MetaInfo entry = _dynamicMetadata[i]; |
| 119 if (entry.set.contains(tag)) { |
| 120 method = JS('var', '#[#]', methods, entry.tag); |
| 121 if (method !== null) break; |
| 122 } |
| 123 } |
| 124 } |
| 125 |
| 126 if (method === null) { |
| 127 method = JS('var', "#['Object']", methods); |
| 128 } |
| 129 |
| 130 if (method === null) { |
| 131 method = JS('var', |
| 132 'function () {' |
| 133 '#(#, #, Array.prototype.slice.call(arguments));' |
| 134 '}', |
| 135 JS_TO_CLOSURE(throwNoSuchMethod), obj, name); |
| 136 } |
| 137 |
| 138 var nullCheckMethod = JS('var', |
| 139 'function() {' |
| 140 'var res = #.apply(this, Array.prototype.slice.call(arguments));' |
| 141 'return res === null ? (void 0) : res;' |
| 142 '}', |
| 143 method); |
| 144 |
| 145 var proto = JS('var', 'Object.getPrototypeOf(#)', obj); |
| 146 if (JS('bool', '!#.hasOwnProperty(#)', proto, name)) { |
| 147 defineProperty(proto, name, nullCheckMethod); |
| 148 } |
| 149 |
| 150 return JS('var', '#.apply(#, #)', nullCheckMethod, obj, arguments); |
| 151 } |
| 152 |
| 153 /** |
| 154 * Code for doing the dynamic dispatch on JavaScript prototypes that are not |
| 155 * available at compile-time. Each property of a native Dart class |
| 156 * is registered through this function, which is called with the |
| 157 * following pattern: |
| 158 * |
| 159 * dynamicFunction('propertyName').prototypeName = // JS code |
| 160 * |
| 161 * What this function does is: |
| 162 * - Creates a map of { prototypeName: JS code }. |
| 163 * - Attaches 'propertyName' to the JS Object prototype that will |
| 164 * intercept at runtime all calls to propertyName. |
| 165 * - Sets the value of 'propertyName' to the returned method from |
| 166 * [dynamicBind]. |
| 167 * |
| 168 */ |
| 169 dynamicFunction(name) { |
| 170 var f = JS('var', 'Object.prototype[#]', name); |
| 171 if (f !== null && JS('bool', '#.methods', f)) { |
| 172 return JS('var', '#.methods', f); |
| 173 } |
| 174 |
| 175 // TODO(ngeoffray): We could make this a map if the code we |
| 176 // generate plays well with a Dart map. |
| 177 var methods = JS('var', '{}'); |
| 178 // If there is a method attached to the Dart Object class, use it as |
| 179 // the method to call in case no method is registered for that type. |
| 180 var dartMethod = JS('var', 'Object.getPrototypeOf(#)[#]', new Object(), name); |
| 181 if (dartMethod !== null) JS('void', "#['Object'] = #", methods, dartMethod); |
| 182 |
| 183 var bind = JS('var', |
| 184 'function() {' |
| 185 'return #(this, #, #, Array.prototype.slice.call(arguments));' |
| 186 '}', |
| 187 JS_TO_CLOSURE(dynamicBind), name, methods); |
| 188 |
| 189 JS('void', '#.methods = #', bind, methods); |
| 190 defineProperty(JS('var', 'Object.prototype'), name, bind); |
| 191 return methods; |
| 192 } |
| 193 |
| 194 |
| 195 class MetaInfo { |
| 196 String tag; |
| 197 String tags; |
| 198 Set<String> set; |
| 199 MetaInfo(this.tag, this.tags, this.set); |
| 200 } |
| 201 |
| 202 List<MetaInfo> get _dynamicMetadata() { |
| 203 return JS('var', '\$dynamicMetadata'); |
| 204 } |
| 205 |
| 206 void set _dynamicMetadata(List<String> table) { |
| 207 return JS('void', '\$dynamicMetadata = #', table); |
| 208 } |
| 209 |
| 210 /** |
| 211 * Builds the metadata used for encoding the class hierarchy of native |
| 212 * classes. |
| 213 */ |
| 214 void dynamicSetMetadata(List<List<String>> inputTable) { |
| 215 _dynamicMetadata = <MetaInfo>[]; |
| 216 for (int i = 0; i < inputTable.length; i++) { |
| 217 String tag = inputTable[i][0]; |
| 218 String tags = inputTable[i][1]; |
| 219 Set<String> set = new Set<String>(); |
| 220 List<String> tagNames = tags.split('|'); |
| 221 for (int j = 0; j < tagNames.length; j++) { |
| 222 set.add(tagNames[j]); |
| 223 } |
| 224 _dynamicMetadata.add(new MetaInfo(tag, tags, set)); |
| 225 } |
| 226 } |
| 227 |
| 228 var isChecksHelper; |
| 229 |
| 230 // This method will be called for 'is' checks on native types. |
| 231 // It takes the object on which the 'is' check is being done, and the |
| 232 // property name for the type check. The method patches the real |
| 233 // prototype of the object with the value from the Dart object |
| 234 // (see [generateNativeClass]). |
| 235 bool dynamicIsCheck(obj, String typeName) { |
| 236 if (isJsArray(obj)) return false; |
| 237 // Check if the Dart object corresponding to this class has the property. |
| 238 var res = JS('bool', '!!#[#][#]', isChecksHelper, getTypeNameOf(obj), |
| 239 typeName); |
| 240 var proto = JS('var', 'Object.getPrototypeOf(#)', obj); |
| 241 defineProperty(proto, typeName, res); |
| 242 return res; |
| 243 } |
| OLD | NEW |