Chromium Code Reviews| 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) { | |
|
ahe
2012/03/26 14:01:23
Generally, I would prefer if these methods followe
ngeoffray
2012/03/26 14:13:44
Done.
| |
| 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 = constructorNameFallback(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 String name = constructorNameFallback(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'; | |
|
ahe
2012/03/26 14:01:23
I doubt that this is really a bool: JS('bool', '#.
ngeoffray
2012/03/26 14:13:44
Done.
| |
| 25 if (name == 'Document') return 'HTMLDocument'; | |
| 26 return name; | |
| 27 } | |
| 28 | |
| 29 String constructorNameFallback(obj) { | |
| 30 var constructor = JS('var', "#.constructor", obj); | |
| 31 if (JS('bool', "typeof(#) == 'function'", constructor)) { | |
|
ahe
2012/03/26 14:01:23
I think we have generally used this pattern, but i
ngeoffray
2012/03/26 14:13:44
Yeah, that could be better.
ahe
2012/03/27 09:42:08
I don't know if it is a problem to use === on stri
| |
| 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) | |
|
ahe
2012/03/26 14:57:28
This is not a boolean and I think it is unnecessar
ngeoffray
2012/03/27 09:17:35
It's necessary because it's checking if the string
| |
| 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); | |
|
ahe
2012/03/26 14:57:28
I'm concerned about this. We should share this wit
ngeoffray
2012/03/27 09:17:35
Probably, but I'd wait another CL to make sure it'
| |
| 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('Firefox')) { | |
| 62 return firefoxTypeNameOf; | |
| 63 } else if (userAgent.contains('MSIE')) { | |
| 64 return ieTypeNameOf; | |
| 65 } else { | |
| 66 return constructorNameFallback; | |
| 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(); | |
|
ahe
2012/03/26 14:57:28
===
ngeoffray
2012/03/27 09:17:35
Done.
| |
| 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. | |
|
ahe
2012/03/26 14:57:28
This comment is convoluted and it doesn't match wh
ngeoffray
2012/03/27 09:17:35
Comment updated. Yes [methods] is a JS object, see
| |
| 108 */ | |
| 109 dynamicBind(var obj, | |
| 110 String name, | |
|
ahe
2012/03/26 14:57:28
What is the name for?
ngeoffray
2012/03/27 09:17:35
That's the name of the method. Used to patch the p
| |
| 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) { | |
|
ahe
2012/03/26 14:57:28
What is _dynamicMetadata and how does it relate to
ngeoffray
2012/03/27 09:17:35
I updated the comment.
| |
| 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));' | |
|
ahe
2012/03/26 14:57:28
Would this work: new List.from(arguments)
We could
ngeoffray
2012/03/27 09:17:35
I'm not sure I want to mix regular code that calls
ahe
2012/03/27 09:42:08
I would find this code easier to understand if it
ngeoffray
2012/03/27 10:37:33
I could change it to:
'#(#, #, #(arguments));', D
| |
| 134 '}', | |
| 135 DART_CLOSURE_TO_JS(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)) { | |
|
ahe
2012/03/26 14:57:28
I doubt that #.methods is a boolean.
ngeoffray
2012/03/27 09:17:35
Done.
| |
| 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 DART_CLOSURE_TO_JS(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 { | |
|
ahe
2012/03/26 14:57:28
Exactly what information is this information about
ngeoffray
2012/03/27 09:17:35
Added a comment.
| |
| 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); | |
|
ahe
2012/03/26 14:57:28
Do you really need to return from here?
ngeoffray
2012/03/27 09:17:35
No, return removed.
| |
| 208 } | |
| 209 | |
| 210 /** | |
| 211 * Builds the metadata used for encoding the class hierarchy of native | |
| 212 * classes. | |
|
ahe
2012/03/26 14:57:28
Add example?
ngeoffray
2012/03/27 09:17:35
Done.
| |
| 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; | |
|
ahe
2012/03/26 14:57:28
What is this for and who initializes it?
ngeoffray
2012/03/27 09:17:35
It's a Js object initialized by the compiler. It w
ahe
2012/03/27 09:42:08
Add TODO?
| |
| 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 |