OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library _js_helper; | 5 library _js_helper; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, | 8 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS, |
9 JS, | 9 JS, |
10 JS_CALL_IN_ISOLATE, | 10 JS_CALL_IN_ISOLATE, |
(...skipping 13 matching lines...) Expand all Loading... |
24 JS_OBJECT_CLASS_NAME, | 24 JS_OBJECT_CLASS_NAME, |
25 JS_NULL_CLASS_NAME, | 25 JS_NULL_CLASS_NAME, |
26 JS_OPERATOR_AS_PREFIX, | 26 JS_OPERATOR_AS_PREFIX, |
27 JS_OPERATOR_IS_PREFIX, | 27 JS_OPERATOR_IS_PREFIX, |
28 JS_SIGNATURE_NAME, | 28 JS_SIGNATURE_NAME, |
29 RAW_DART_FUNCTION_REF; | 29 RAW_DART_FUNCTION_REF; |
30 import 'dart:_interceptors'; | 30 import 'dart:_interceptors'; |
31 import 'dart:_collection-dev' as _symbol_dev; | 31 import 'dart:_collection-dev' as _symbol_dev; |
32 | 32 |
33 import 'dart:_js_names' show | 33 import 'dart:_js_names' show |
| 34 extractKeys, |
34 mangledNames, | 35 mangledNames, |
35 unmangleGlobalNameIfPreservedAnyways; | 36 unmangleGlobalNameIfPreservedAnyways; |
36 | 37 |
37 part 'constant_map.dart'; | 38 part 'constant_map.dart'; |
38 part 'native_helper.dart'; | 39 part 'native_helper.dart'; |
39 part 'regexp_helper.dart'; | 40 part 'regexp_helper.dart'; |
40 part 'string_helper.dart'; | 41 part 'string_helper.dart'; |
41 part 'js_rti.dart'; | 42 part 'js_rti.dart'; |
42 | 43 |
43 bool isJsIndexable(var object, var record) { | 44 bool isJsIndexable(var object, var record) { |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
274 int hash = JS('int|Null', r'#.$identityHash', object); | 275 int hash = JS('int|Null', r'#.$identityHash', object); |
275 if (hash == null) { | 276 if (hash == null) { |
276 hash = JS('int', '(Math.random() * 0x3fffffff) | 0'); | 277 hash = JS('int', '(Math.random() * 0x3fffffff) | 0'); |
277 JS('void', r'#.$identityHash = #', object, hash); | 278 JS('void', r'#.$identityHash = #', object, hash); |
278 } | 279 } |
279 return JS('int', '#', hash); | 280 return JS('int', '#', hash); |
280 } | 281 } |
281 | 282 |
282 static computeGlobalThis() => JS('', 'function() { return this; }()'); | 283 static computeGlobalThis() => JS('', 'function() { return this; }()'); |
283 | 284 |
| 285 /** |
| 286 * This is the low-level method that is used to implement |
| 287 * [print]. It is possible to override this function from JavaScript |
| 288 * by defining a function in JavaScript called "dartPrint". |
| 289 */ |
| 290 static void printString(String string) { |
| 291 if (JS('bool', r'typeof dartPrint == "function"')) { |
| 292 // Support overriding print from JavaScript. |
| 293 JS('void', r'dartPrint(#)', string); |
| 294 return; |
| 295 } |
| 296 |
| 297 // Inside browser or nodejs. |
| 298 if (JS('bool', r'typeof console == "object"') && |
| 299 JS('bool', r'typeof console.log == "function"')) { |
| 300 JS('void', r'console.log(#)', string); |
| 301 return; |
| 302 } |
| 303 |
| 304 // Don't throw inside IE, the console is only defined if dev tools is open. |
| 305 if (JS('bool', r'typeof window == "object"')) { |
| 306 return; |
| 307 } |
| 308 |
| 309 // Running in d8, the V8 developer shell, or in Firefox' js-shell. |
| 310 if (JS('bool', r'typeof print == "function"')) { |
| 311 JS('void', r'print(#)', string); |
| 312 return; |
| 313 } |
| 314 |
| 315 // This is somewhat nasty, but we don't want to drag in a bunch of |
| 316 // dependencies to handle a situation that cannot happen. So we |
| 317 // avoid using Dart [:throw:] and Dart [toString]. |
| 318 JS('void', 'throw "Unable to print message: " + String(#)', string); |
| 319 } |
| 320 |
284 static _throwFormatException(String string) { | 321 static _throwFormatException(String string) { |
285 throw new FormatException(string); | 322 throw new FormatException(string); |
286 } | 323 } |
287 | 324 |
288 static int parseInt(String source, | 325 static int parseInt(String source, |
289 int radix, | 326 int radix, |
290 int handleError(String source)) { | 327 int handleError(String source)) { |
291 if (handleError == null) handleError = _throwFormatException; | 328 if (handleError == null) handleError = _throwFormatException; |
292 | 329 |
293 checkString(source); | 330 checkString(source); |
(...skipping 1705 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1999 } | 2036 } |
2000 | 2037 |
2001 /** | 2038 /** |
2002 * Error thrown when a runtime error occurs. | 2039 * Error thrown when a runtime error occurs. |
2003 */ | 2040 */ |
2004 class RuntimeError extends Error { | 2041 class RuntimeError extends Error { |
2005 final message; | 2042 final message; |
2006 RuntimeError(this.message); | 2043 RuntimeError(this.message); |
2007 String toString() => "RuntimeError: $message"; | 2044 String toString() => "RuntimeError: $message"; |
2008 } | 2045 } |
| 2046 |
| 2047 abstract class RuntimeType { |
| 2048 const RuntimeType(); |
| 2049 |
| 2050 bool isTest(expression); |
| 2051 |
| 2052 toRti(); |
| 2053 } |
| 2054 |
| 2055 // TODO(ahe): Remove this class. |
| 2056 class DummyRuntimeType extends RuntimeType { |
| 2057 final String impl; |
| 2058 |
| 2059 DummyRuntimeType(this.impl); |
| 2060 |
| 2061 bool isTest(expression) { |
| 2062 throw "<<is-test with '$impl' not implemented>>"; |
| 2063 } |
| 2064 |
| 2065 String toString() => 'DummyRuntimeType($impl)'; |
| 2066 } |
| 2067 |
| 2068 // TODO(ahe): Delete this method. |
| 2069 DummyRuntimeType buildDummyType(String impl) => new DummyRuntimeType(impl); |
| 2070 |
| 2071 class RuntimeFunctionType extends RuntimeType { |
| 2072 final RuntimeType returnType; |
| 2073 final List<RuntimeType> parameterTypes; |
| 2074 final List<RuntimeType> optionalParameterTypes; |
| 2075 final namedParameters; |
| 2076 |
| 2077 static var /* bool */ inAssert = false; |
| 2078 |
| 2079 RuntimeFunctionType(this.returnType, |
| 2080 this.parameterTypes, |
| 2081 this.optionalParameterTypes, |
| 2082 this.namedParameters); |
| 2083 |
| 2084 // TODO(ahe): Implement this. |
| 2085 bool get isVoid => returnType is VoidRuntimeType; |
| 2086 |
| 2087 /// Called from generated code. [expression] is a Dart object and this method |
| 2088 /// returns true if [this] is a supertype of [expression]. |
| 2089 bool _isTest(expression) { |
| 2090 // Type inferrer don't think this is called with dynamic arguments. |
| 2091 expression = JS('', '#', expression); |
| 2092 |
| 2093 var interceptor = getInterceptor(expression); |
| 2094 if (!JS('bool', '# in #', JS_SIGNATURE_NAME(), interceptor)) return false; |
| 2095 var functionTypeObject = JS('', '#[#]()', interceptor, JS_SIGNATURE_NAME()); |
| 2096 bool result = isFunctionSubtype(functionTypeObject, toRti()); |
| 2097 |
| 2098 // DEBUG REMOVE |
| 2099 var pretty = new FunctionTypeInfoDecoderRing(functionTypeObject).toString(); |
| 2100 var self = new FunctionTypeInfoDecoderRing(toRti()).toString(); |
| 2101 Primitives.printString("<<$pretty is $this (${self}) [$result]>>"); |
| 2102 |
| 2103 return result; |
| 2104 } |
| 2105 |
| 2106 _asCheck(expression) { |
| 2107 // Type inferrer don't think this is called with dynamic arguments. |
| 2108 return _check(JS('', '#', expression), true); |
| 2109 } |
| 2110 |
| 2111 _assertCheck(expression) { |
| 2112 if (inAssert) return; |
| 2113 inAssert = true; // Don't try to check this library itself. |
| 2114 try { |
| 2115 // Type inferrer don't think this is called with dynamic arguments. |
| 2116 return _check(JS('', '#', expression), false); |
| 2117 } finally { |
| 2118 inAssert = false; |
| 2119 } |
| 2120 } |
| 2121 |
| 2122 _check(expression, bool isCast) { |
| 2123 if (expression == null) return null; |
| 2124 if (_isTest(expression)) return expression; |
| 2125 |
| 2126 var self = new FunctionTypeInfoDecoderRing(toRti()).toString(); |
| 2127 if (isCast) { |
| 2128 var interceptor = getInterceptor(expression); |
| 2129 var pretty; |
| 2130 if (JS('bool', '# in #', JS_SIGNATURE_NAME(), interceptor)) { |
| 2131 var functionTypeObject = |
| 2132 JS('', '#[#]()', interceptor, JS_SIGNATURE_NAME()); |
| 2133 pretty = new FunctionTypeInfoDecoderRing(functionTypeObject).toString(); |
| 2134 } else { |
| 2135 pretty = Primitives.objectTypeName(expression); |
| 2136 } |
| 2137 throw new CastErrorImplementation(pretty, self); |
| 2138 } else { |
| 2139 // TODO(ahe): Pass "pretty" function-type to TypeErrorImplementation? |
| 2140 throw new TypeErrorImplementation(expression, self); |
| 2141 } |
| 2142 } |
| 2143 |
| 2144 toRti() { |
| 2145 var result = JS('=Object', '{ #: "dynafunc" }', JS_FUNCTION_TYPE_TAG()); |
| 2146 if (isVoid) { |
| 2147 JS('', '#[#] = true', result, JS_FUNCTION_TYPE_VOID_RETURN_TAG()); |
| 2148 } else { |
| 2149 if (returnType is! DynamicRuntimeType) { |
| 2150 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_RETURN_TYPE_TAG(), |
| 2151 returnType.toRti()); |
| 2152 } |
| 2153 } |
| 2154 if (parameterTypes.length > 0) { |
| 2155 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG(), |
| 2156 listToRti(parameterTypes)); |
| 2157 } |
| 2158 |
| 2159 if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) { |
| 2160 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG(), |
| 2161 listToRti(optionalParameterTypes)); |
| 2162 } |
| 2163 |
| 2164 if (namedParameters != null) { |
| 2165 var namedRti = JS('=Object', '{}'); |
| 2166 for (var name in extractKeys(namedParameters)) { |
| 2167 var rti = JS('', '#[#]', namedParameters, name).toRti(); |
| 2168 JS('', '#[#] = #', namedRti, name, rti); |
| 2169 } |
| 2170 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG(), |
| 2171 namedRti); |
| 2172 } |
| 2173 |
| 2174 return result; |
| 2175 } |
| 2176 |
| 2177 static listToRti(list) { |
| 2178 list = JS('JSFixedArray', '#', list); |
| 2179 var result = JS('JSExtendableArray', '[]'); |
| 2180 for (var i = 0; i < list.length; i++) { |
| 2181 JS('', '#.push(#)', result, list[i].toRti()); |
| 2182 } |
| 2183 return result; |
| 2184 } |
| 2185 |
| 2186 String toString() { |
| 2187 String result = '('; |
| 2188 bool needsComma = false; |
| 2189 for (RuntimeType type in parameterTypes) { |
| 2190 if (needsComma) result += ', '; |
| 2191 result += '$type'; |
| 2192 needsComma = true; |
| 2193 } |
| 2194 if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) { |
| 2195 if (needsComma) result += ', '; |
| 2196 needsComma = false; |
| 2197 result += '['; |
| 2198 for (RuntimeType type in optionalParameterTypes) { |
| 2199 if (needsComma) result += ', '; |
| 2200 result += '$type'; |
| 2201 needsComma = true; |
| 2202 } |
| 2203 result += ']'; |
| 2204 } |
| 2205 result += ') -> $returnType'; |
| 2206 return result; |
| 2207 } |
| 2208 } |
| 2209 |
| 2210 RuntimeFunctionType buildFunctionType(RuntimeType returnType, |
| 2211 List parameterTypes, |
| 2212 List optionalParameterTypes) { |
| 2213 return new RuntimeFunctionType( |
| 2214 returnType, parameterTypes, optionalParameterTypes, null); |
| 2215 } |
| 2216 |
| 2217 RuntimeFunctionType buildNamedFunctionType(RuntimeType returnType, |
| 2218 List parameterTypes, |
| 2219 namedParameters) { |
| 2220 return new RuntimeFunctionType( |
| 2221 returnType, parameterTypes, null, namedParameters); |
| 2222 } |
| 2223 |
| 2224 RuntimeType buildInterfaceType(rti, List typeArguments) { |
| 2225 String name = JS('String|Null', r'#.name', rti); |
| 2226 typeArguments = JS('JSFixedArray', '#', typeArguments); |
| 2227 if (typeArguments.isEmpty) return new RuntimeTypePlain(name); |
| 2228 return new RuntimeTypeGeneric(name, typeArguments, null); |
| 2229 } |
| 2230 |
| 2231 class DynamicRuntimeType extends RuntimeType { |
| 2232 const DynamicRuntimeType(); |
| 2233 |
| 2234 String toString() => 'dynamic'; |
| 2235 |
| 2236 toRti() => null; |
| 2237 } |
| 2238 |
| 2239 RuntimeType getDynamicRuntimeType() => const DynamicRuntimeType(); |
| 2240 |
| 2241 class VoidRuntimeType extends RuntimeType { |
| 2242 const VoidRuntimeType(); |
| 2243 |
| 2244 String toString() => 'void'; |
| 2245 |
| 2246 toRti() => throw 'internal error'; |
| 2247 } |
| 2248 |
| 2249 RuntimeType getVoidRuntimeType() => const VoidRuntimeType(); |
| 2250 |
| 2251 RuntimeType convertRtiToRuntimeType(rti) { |
| 2252 if (rti == null) { |
| 2253 return getDynamicRuntimeType(); |
| 2254 } else if (JS('bool', 'typeof # == "function"', rti)) { |
| 2255 return new RuntimeTypePlain(JS('String', r'rti.name')); |
| 2256 } else if (JS('bool', '#.constructor == Array', rti)) { |
| 2257 List list = JS('JSFixedArray', '#', rti); |
| 2258 String name = JS('String', r'#.name', list[0]); |
| 2259 List arguments = []; |
| 2260 for (int i = 1; i < list.length; i++) { |
| 2261 arguments.add(convertRtiToRuntimeType(list[i])); |
| 2262 } |
| 2263 return new RuntimeTypeGeneric(name, arguments, rti); |
| 2264 } else { |
| 2265 return new DummyRuntimeType("${JS('String', 'JSON.stringify(#)', rti)}"); |
| 2266 } |
| 2267 } |
| 2268 |
| 2269 class RuntimeTypePlain extends RuntimeType { |
| 2270 final String name; |
| 2271 |
| 2272 RuntimeTypePlain(this.name); |
| 2273 |
| 2274 toRti() { |
| 2275 var rti = JS('', 'init.allClasses[#]', name); |
| 2276 if (rti == null) throw "no type for '$name'"; |
| 2277 return rti; |
| 2278 } |
| 2279 |
| 2280 String toString() => name; |
| 2281 } |
| 2282 |
| 2283 class RuntimeTypeGeneric extends RuntimeType { |
| 2284 final String name; |
| 2285 final List<RuntimeType> arguments; |
| 2286 var rti; |
| 2287 |
| 2288 RuntimeTypeGeneric(this.name, this.arguments, this.rti); |
| 2289 |
| 2290 toRti() { |
| 2291 if (rti != null) return rti; |
| 2292 var result = JS('JSExtendableArray', '[init.allClasses[#]]', name); |
| 2293 if (result[0] == null) { |
| 2294 throw "no type for '$name<...>'"; |
| 2295 } |
| 2296 for (RuntimeType argument in arguments) { |
| 2297 JS('', '#.push(#)', result, argument.toRti()); |
| 2298 } |
| 2299 return rti = result; |
| 2300 } |
| 2301 |
| 2302 String toString() => '$name<${arguments.join(", ")}>'; |
| 2303 } |
| 2304 |
| 2305 class FunctionTypeInfoDecoderRing { |
| 2306 final _typeData; |
| 2307 String _cachedToString; |
| 2308 |
| 2309 FunctionTypeInfoDecoderRing(this._typeData); |
| 2310 |
| 2311 bool get _hasReturnType => JS('bool', '"ret" in #', _typeData); |
| 2312 get _returnType => JS('', '#.ret', _typeData); |
| 2313 |
| 2314 bool get _isVoid => JS('bool', '!!#.void', _typeData); |
| 2315 |
| 2316 bool get _hasArguments => JS('bool', '"args" in #', _typeData); |
| 2317 List get _arguments => JS('JSExtendableArray', '#.args', _typeData); |
| 2318 |
| 2319 bool get _hasOptionalArguments => JS('bool', '"opt" in #', _typeData); |
| 2320 List get _optionalArguments => JS('JSExtendableArray', '#.opt', _typeData); |
| 2321 |
| 2322 bool get _hasNamedArguments => JS('bool', '"named" in #', _typeData); |
| 2323 get _namedArguments => JS('=Object', '#.named', _typeData); |
| 2324 |
| 2325 String _convert(type) { |
| 2326 String result = runtimeTypeToString(type); |
| 2327 if (result != null) return result; |
| 2328 if (JS('bool', '"func" in #', type)) { |
| 2329 return new FunctionTypeInfoDecoderRing(type).toString(); |
| 2330 } else { |
| 2331 throw 'bad type'; |
| 2332 } |
| 2333 } |
| 2334 |
| 2335 String toString() { |
| 2336 if (_cachedToString != null) return _cachedToString; |
| 2337 var s = "("; |
| 2338 var sep = ''; |
| 2339 if (_hasArguments) { |
| 2340 for (var argument in _arguments) { |
| 2341 s += sep; |
| 2342 s += _convert(argument); |
| 2343 sep = ', '; |
| 2344 } |
| 2345 } |
| 2346 if (_hasOptionalArguments) { |
| 2347 s += '$sep['; |
| 2348 sep = ''; |
| 2349 for (var argument in _optionalArguments) { |
| 2350 s += sep; |
| 2351 s += _convert(argument); |
| 2352 sep = ', '; |
| 2353 } |
| 2354 s += ']'; |
| 2355 } |
| 2356 if (_hasNamedArguments) { |
| 2357 s += '$sep{'; |
| 2358 sep = ''; |
| 2359 for (var name in extractKeys(_namedArguments)) { |
| 2360 s += sep; |
| 2361 s += '$name: '; |
| 2362 s += _convert(JS('', '#[#]', _namedArguments, name)); |
| 2363 sep = ', '; |
| 2364 } |
| 2365 s += '}'; |
| 2366 } |
| 2367 s += ') -> '; |
| 2368 if (_isVoid) { |
| 2369 s += 'void'; |
| 2370 } else if (_hasReturnType) { |
| 2371 s += _convert(_returnType); |
| 2372 } else { |
| 2373 s += 'dynamic'; |
| 2374 } |
| 2375 return _cachedToString = "$s"; |
| 2376 } |
| 2377 } |
OLD | NEW |