Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 part of _js_helper; | 5 part of _js_helper; |
| 6 | 6 |
| 7 String typeNameInChrome(obj) { | 7 String typeNameInChrome(obj) { |
| 8 String name = JS('String', "#.constructor.name", obj); | 8 String name = JS('String', "#.constructor.name", obj); |
| 9 if (name == 'Window') return 'DOMWindow'; | 9 return typeNameInWebKitCommon(name); |
| 10 if (name == 'CanvasPixelArray') return 'Uint8ClampedArray'; | |
| 11 if (name == 'WebKitMutationObserver') return 'MutationObserver'; | |
| 12 if (name == 'AudioChannelMerger') return 'ChannelMergerNode'; | |
| 13 if (name == 'AudioChannelSplitter') return 'ChannelSplitterNode'; | |
| 14 if (name == 'AudioGainNode') return 'GainNode'; | |
| 15 if (name == 'AudioPannerNode') return 'PannerNode'; | |
| 16 if (name == 'JavaScriptAudioNode') return 'ScriptProcessorNode'; | |
| 17 if (name == 'Oscillator') return 'OscillatorNode'; | |
| 18 if (name == 'RealtimeAnalyserNode') return 'AnalyserNode'; | |
| 19 return name; | |
| 20 } | 10 } |
| 21 | 11 |
| 22 String typeNameInSafari(obj) { | 12 String typeNameInSafari(obj) { |
| 23 String name = JS('String', '#', constructorNameFallback(obj)); | 13 String name = JS('String', '#', constructorNameFallback(obj)); |
| 24 // Safari is very similar to Chrome. | 14 // Safari is very similar to Chrome. |
| 15 return typeNameInWebKitCommon(name); | |
| 16 } | |
| 17 | |
| 18 String typeNameInWebKitCommon(tag) { | |
| 19 String name = JS('String', '#', tag); | |
| 25 if (name == 'Window') return 'DOMWindow'; | 20 if (name == 'Window') return 'DOMWindow'; |
| 26 if (name == 'CanvasPixelArray') return 'Uint8ClampedArray'; | 21 if (name == 'CanvasPixelArray') return 'Uint8ClampedArray'; |
| 27 if (name == 'WebKitMutationObserver') return 'MutationObserver'; | 22 if (name == 'WebKitMutationObserver') return 'MutationObserver'; |
| 28 if (name == 'AudioChannelMerger') return 'ChannelMergerNode'; | 23 if (name == 'AudioChannelMerger') return 'ChannelMergerNode'; |
| 29 if (name == 'AudioChannelSplitter') return 'ChannelSplitterNode'; | 24 if (name == 'AudioChannelSplitter') return 'ChannelSplitterNode'; |
| 30 if (name == 'AudioGainNode') return 'GainNode'; | 25 if (name == 'AudioGainNode') return 'GainNode'; |
| 31 if (name == 'AudioPannerNode') return 'PannerNode'; | 26 if (name == 'AudioPannerNode') return 'PannerNode'; |
| 32 if (name == 'JavaScriptAudioNode') return 'ScriptProcessorNode'; | 27 if (name == 'JavaScriptAudioNode') return 'ScriptProcessorNode'; |
| 33 if (name == 'Oscillator') return 'OscillatorNode'; | 28 if (name == 'Oscillator') return 'OscillatorNode'; |
| 34 if (name == 'RealtimeAnalyserNode') return 'AnalyserNode'; | 29 if (name == 'RealtimeAnalyserNode') return 'AnalyserNode'; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 95 && !identical(name, '') | 90 && !identical(name, '') |
| 96 && !identical(name, 'Object') | 91 && !identical(name, 'Object') |
| 97 && !identical(name, 'Function.prototype')) { // Can happen in Opera. | 92 && !identical(name, 'Function.prototype')) { // Can happen in Opera. |
| 98 return name; | 93 return name; |
| 99 } | 94 } |
| 100 } | 95 } |
| 101 String string = JS('String', 'Object.prototype.toString.call(#)', object); | 96 String string = JS('String', 'Object.prototype.toString.call(#)', object); |
| 102 return JS('String', '#.substring(8, # - 1)', string, string.length); | 97 return JS('String', '#.substring(8, # - 1)', string, string.length); |
| 103 } | 98 } |
| 104 | 99 |
| 100 /** | |
| 101 * If a lookup on an object [object] that has [tag] fails, this function is | |
| 102 * called to provide an alternate tag. This allows us to fail gracefully if we | |
| 103 * can make a good guess, for example, when browsers add novel kinds of | |
| 104 * HTMLElement that we have never heard of. | |
| 105 */ | |
| 106 String alternateTag(object, String tag) { | |
| 107 // Does it smell like some kind of HTML element? | |
| 108 if (JS('bool', r'!!/^HTML[A-Z].*Element$/.test(#)', tag)) { | |
| 109 // Check that it is not a simple JavaScript object. | |
| 110 String string = JS('String', 'Object.prototype.toString.call(#)', object); | |
| 111 if (string == '[object Object]') return null; | |
| 112 return 'HTMLElement'; | |
| 113 } | |
| 114 return null; | |
| 115 } | |
| 116 | |
| 105 // TODO(ngeoffray): stop using this method once our optimizers can | 117 // TODO(ngeoffray): stop using this method once our optimizers can |
| 106 // change str1.contains(str2) into str1.indexOf(str2) != -1. | 118 // change str1.contains(str2) into str1.indexOf(str2) != -1. |
| 107 bool contains(String userAgent, String name) { | 119 bool contains(String userAgent, String name) { |
| 108 return JS('int', '#.indexOf(#)', userAgent, name) != -1; | 120 return JS('int', '#.indexOf(#)', userAgent, name) != -1; |
| 109 } | 121 } |
| 110 | 122 |
| 111 int arrayLength(List array) { | 123 int arrayLength(List array) { |
| 112 return JS('int', '#.length', array); | 124 return JS('int', '#.length', array); |
| 113 } | 125 } |
| 114 | 126 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 String name = JS('String', '#', getTypeNameOf(obj)); | 198 String name = JS('String', '#', getTypeNameOf(obj)); |
| 187 return 'Instance of $name'; | 199 return 'Instance of $name'; |
| 188 } | 200 } |
| 189 | 201 |
| 190 int hashCodeForNativeObject(object) => Primitives.objectHashCode(object); | 202 int hashCodeForNativeObject(object) => Primitives.objectHashCode(object); |
| 191 | 203 |
| 192 /** | 204 /** |
| 193 * Sets a JavaScript property on an object. | 205 * Sets a JavaScript property on an object. |
| 194 */ | 206 */ |
| 195 void defineProperty(var obj, String property, var value) { | 207 void defineProperty(var obj, String property, var value) { |
| 196 JS('void', """Object.defineProperty(#, #, | 208 JS('void', |
| 197 {value: #, enumerable: false, writable: true, configurable: true})""", | 209 'Object.defineProperty(#, #, ' |
| 210 '{value: #, enumerable: false, writable: true, configurable: true})', | |
| 198 obj, | 211 obj, |
| 199 property, | 212 property, |
| 200 value); | 213 value); |
| 201 } | 214 } |
| 202 | 215 |
| 203 /** | 216 /** |
| 204 * This method looks up the type name of [obj] in [methods]. [methods] | 217 * This method looks up the type name of [obj] in [methods]. [methods] |
| 205 * is a Javascript object. If it cannot find it, it looks into the | 218 * is a Javascript object. If it cannot find it, it looks into the |
| 206 * [_dynamicMetadata] array. If the method can still not be found, it | 219 * [_dynamicMetadata] array. If the method can still not be found, it |
| 207 * creates a method that will throw a [NoSuchMethodError]. | 220 * creates a method that will throw a [NoSuchMethodError]. |
| 208 * | 221 * |
| 209 * Once it has a method, the prototype of [obj] is patched with that | 222 * Once it has a method, the prototype of [obj] is patched with that |
| 210 * method, on the property [name]. The method is then invoked. | 223 * method, on the property [name]. The method is then invoked. |
| 211 * | 224 * |
| 212 * This method returns the result of invoking the found method. | 225 * This method returns the result of invoking the found method. |
| 213 */ | 226 */ |
| 214 dynamicBind(var obj, | 227 dynamicBind(var obj, |
| 215 String name, | 228 String name, |
| 216 var methods, | 229 var methods, |
| 217 List arguments) { | 230 List arguments) { |
| 218 // The tag is related to the class name. E.g. the dart:html class | 231 // The tag is related to the class name. E.g. the dart:html class |
| 219 // '_ButtonElement' has the tag 'HTMLButtonElement'. TODO(erikcorry): rename | 232 // '_ButtonElement' has the tag 'HTMLButtonElement'. TODO(erikcorry): rename |
| 220 // getTypeNameOf to getTypeTag. | 233 // getTypeNameOf to getTypeTag. |
| 221 String tag = getTypeNameOf(obj); | 234 String tag = getTypeNameOf(obj); |
| 222 var hasOwnPropertyFunction = JS('var', 'Object.prototype.hasOwnProperty'); | 235 var hasOwnPropertyFunction = JS('var', 'Object.prototype.hasOwnProperty'); |
| 223 var method = lookupDynamicClass(hasOwnPropertyFunction, methods, tag); | |
| 224 | 236 |
| 225 if (method == null && _dynamicMetadata != null) { | 237 var method = dynamicBindLookup(hasOwnPropertyFunction, tag, methods); |
| 226 // Look at the inheritance data, getting the class tags and using them | 238 if (method == null) { |
| 227 // to check the methods table for this method name. | 239 String altTag = alternateTag(obj, tag); |
| 228 for (int i = 0; i < arrayLength(_dynamicMetadata); i++) { | 240 if (altTag != null) { |
|
kasperl
2012/12/12 06:43:50
Nit: I'd try to avoid the abbreviation of alt.
| |
| 229 MetaInfo entry = arrayGet(_dynamicMetadata, i); | 241 method = dynamicBindLookup(hasOwnPropertyFunction, altTag, methods); |
| 230 if (callHasOwnProperty(hasOwnPropertyFunction, entry._set, tag)) { | |
| 231 method = | |
| 232 lookupDynamicClass(hasOwnPropertyFunction, methods, entry._tag); | |
| 233 // Stop if we found it in the methods array. | |
| 234 if (method != null) break; | |
| 235 } | |
| 236 } | 242 } |
| 237 } | 243 } |
| 238 | 244 |
| 239 // If we didn't find the method then look up in the Dart Object class, using | 245 // If we didn't find the method then look up in the Dart Object class, using |
| 240 // getTypeNameOf in case the minifier has renamed Object. | 246 // getTypeNameOf in case the minifier has renamed Object. |
| 241 if (method == null) { | 247 if (method == null) { |
| 242 String nameOfObjectClass = getTypeNameOf(const Object()); | 248 String nameOfObjectClass = getTypeNameOf(const Object()); |
| 243 method = | 249 method = |
| 244 lookupDynamicClass(hasOwnPropertyFunction, methods, nameOfObjectClass); | 250 lookupDynamicClass(hasOwnPropertyFunction, methods, nameOfObjectClass); |
| 245 } | 251 } |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 261 proto, name, name); | 267 proto, name, name); |
| 262 } | 268 } |
| 263 | 269 |
| 264 if (!callHasOwnProperty(hasOwnPropertyFunction, proto, name)) { | 270 if (!callHasOwnProperty(hasOwnPropertyFunction, proto, name)) { |
| 265 defineProperty(proto, name, method); | 271 defineProperty(proto, name, method); |
| 266 } | 272 } |
| 267 | 273 |
| 268 return JS('var', '#.apply(#, #)', method, obj, arguments); | 274 return JS('var', '#.apply(#, #)', method, obj, arguments); |
| 269 } | 275 } |
| 270 | 276 |
| 277 dynamicBindLookup(var hasOwnPropertyFunction, String tag, var methods) { | |
| 278 var method = lookupDynamicClass(hasOwnPropertyFunction, methods, tag); | |
| 279 // Look at the inheritance data, getting the class tags and using them | |
| 280 // to check the methods table for this method name. | |
| 281 if (method == null && _dynamicMetadata != null) { | |
| 282 for (int i = 0; i < arrayLength(_dynamicMetadata); i++) { | |
| 283 MetaInfo entry = arrayGet(_dynamicMetadata, i); | |
| 284 if (callHasOwnProperty(hasOwnPropertyFunction, entry._set, tag)) { | |
| 285 method = | |
| 286 lookupDynamicClass(hasOwnPropertyFunction, methods, entry._tag); | |
| 287 // Stop if we found it in the methods array. | |
| 288 if (method != null) break; | |
| 289 } | |
| 290 } | |
| 291 } | |
| 292 return method; | |
| 293 } | |
| 294 | |
| 271 // For each method name and class inheritance subtree, we use an ordinary JS | 295 // For each method name and class inheritance subtree, we use an ordinary JS |
| 272 // object as a hash map to store the method for each class. Entries are added | 296 // object as a hash map to store the method for each class. Entries are added |
| 273 // in native_emitter.dart (see dynamicName). In order to avoid the class names | 297 // in native_emitter.dart (see dynamicName). In order to avoid the class names |
| 274 // clashing with the method names on Object.prototype (needed for native | 298 // clashing with the method names on Object.prototype (needed for native |
| 275 // objects) we must always use hasOwnProperty. | 299 // objects) we must always use hasOwnProperty. |
| 276 var lookupDynamicClass(var hasOwnPropertyFunction, | 300 var lookupDynamicClass(var hasOwnPropertyFunction, |
| 277 var methods, | 301 var methods, |
| 278 String className) { | 302 String className) { |
| 279 return callHasOwnProperty(hasOwnPropertyFunction, methods, className) ? | 303 return callHasOwnProperty(hasOwnPropertyFunction, methods, className) ? |
| 280 propertyGet(methods, className) : | 304 propertyGet(methods, className) : |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 389 } | 413 } |
| 390 return result; | 414 return result; |
| 391 } | 415 } |
| 392 | 416 |
| 393 /** | 417 /** |
| 394 * Called by the compiler to setup [_dynamicMetadata]. | 418 * Called by the compiler to setup [_dynamicMetadata]. |
| 395 */ | 419 */ |
| 396 void dynamicSetMetadata(List<List<String>> inputTable) { | 420 void dynamicSetMetadata(List<List<String>> inputTable) { |
| 397 _dynamicMetadata = buildDynamicMetadata(inputTable); | 421 _dynamicMetadata = buildDynamicMetadata(inputTable); |
| 398 } | 422 } |
| OLD | NEW |