Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: tool/input_sdk/private/js_helper.dart

Issue 1152333009: Delete dart2js specific private library code (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Rebase Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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:_js_embedded_names' show
8 ALL_CLASSES,
9 GET_ISOLATE_TAG,
10 INTERCEPTED_NAMES,
11 INTERCEPTORS_BY_TAG,
12 LEAF_TAGS,
13 METADATA,
14 DEFERRED_LIBRARY_URIS,
15 DEFERRED_LIBRARY_HASHES,
16 INITIALIZE_LOADED_HUNK,
17 IS_HUNK_LOADED,
18 IS_HUNK_INITIALIZED,
19 NATIVE_SUPERCLASS_TAG_NAME;
20
21 import 'dart:collection'; 7 import 'dart:collection';
22 import 'dart:_isolate_helper' show
23 IsolateNatives,
24 leaveJsAsync,
25 enterJsAsync,
26 isWorker;
27
28 import 'dart:async' show Future, DeferredLoadException, Completer;
29 8
30 import 'dart:_foreign_helper' show 9 import 'dart:_foreign_helper' show
31 DART_CLOSURE_TO_JS,
32 JS, 10 JS,
33 JS_CALL_IN_ISOLATE, 11 JS_STRING_CONCAT;
34 JS_CONST,
35 JS_CURRENT_ISOLATE,
36 JS_CURRENT_ISOLATE_CONTEXT,
37 JS_DART_OBJECT_CONSTRUCTOR,
38 JS_EFFECT,
39 JS_EMBEDDED_GLOBAL,
40 JS_FUNCTION_CLASS_NAME,
41 JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG,
42 JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG,
43 JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG,
44 JS_FUNCTION_TYPE_RETURN_TYPE_TAG,
45 JS_FUNCTION_TYPE_TAG,
46 JS_FUNCTION_TYPE_VOID_RETURN_TAG,
47 JS_GET_NAME,
48 JS_GET_FLAG,
49 JS_HAS_EQUALS,
50 JS_IS_INDEXABLE_FIELD_NAME,
51 JS_NULL_CLASS_NAME,
52 JS_OBJECT_CLASS_NAME,
53 JS_OPERATOR_AS_PREFIX,
54 JS_OPERATOR_IS_PREFIX,
55 JS_SIGNATURE_NAME,
56 JS_STRING_CONCAT,
57 RAW_DART_FUNCTION_REF;
58 12
59 import 'dart:_interceptors'; 13 import 'dart:_interceptors';
60 import 'dart:_internal' as _symbol_dev;
61 import 'dart:_internal' show MappedIterable;
62
63 import 'dart:_js_names' show
64 extractKeys,
65 mangledNames;
66 14
67 part 'annotations.dart'; 15 part 'annotations.dart';
68 part 'constant_map.dart';
69 part 'native_helper.dart'; 16 part 'native_helper.dart';
70 part 'regexp_helper.dart'; 17 part 'regexp_helper.dart';
71 part 'string_helper.dart'; 18 part 'string_helper.dart';
72 part 'js_rti.dart'; 19 part 'js_rti.dart';
73 20
74 // TODO(jacobr): remove.
75 String unmangleGlobalNameIfPreservedAnyways(String str) => str;
76 // TODO(jacobr): remove.
77 String unmangleAllIdentifiersIfPreservedAnyways(String str) => str;
78
79 class _Patch { 21 class _Patch {
80 const _Patch(); 22 const _Patch();
81 } 23 }
82 24
83 const _Patch patch = const _Patch(); 25 const _Patch patch = const _Patch();
84 26
85 27
86 /// Marks the internal map in dart2js, so that internal libraries can is-check 28 /// Marks the internal map in dart2js, so that internal libraries can is-check
87 // them. 29 // them.
88 abstract class InternalMap { 30 abstract class InternalMap {
89 } 31 }
90 32
91 /// No-op method that is called to inform the compiler that preambles might
92 /// be needed when executing the resulting JS file in a command-line
93 /// JS engine.
94 requiresPreamble() {}
95
96 String S(value) {
97 if (value is String) return value;
98 if (value is num) {
99 if (value != 0) {
100 // ""+x is faster than String(x) for integers on most browsers.
101 return JS('String', r'"" + (#)', value);
102 }
103 } else if (true == value) {
104 return 'true';
105 } else if (false == value) {
106 return 'false';
107 } else if (value == null) {
108 return 'null';
109 }
110 var res = value.toString();
111 if (res is !String) throw new ArgumentError(value);
112 return res;
113 }
114
115 createInvocationMirror(String name, internalName, kind, arguments,
116 argumentNames) {
117 return new JSInvocationMirror(name,
118 internalName,
119 kind,
120 arguments,
121 argumentNames);
122 }
123
124 createUnmangledInvocationMirror(Symbol symbol, internalName, kind, arguments,
125 argumentNames) {
126 return new JSInvocationMirror(symbol,
127 internalName,
128 kind,
129 arguments,
130 argumentNames);
131 }
132
133 void throwInvalidReflectionError(String memberName) {
134 throw new UnsupportedError("Can't use '$memberName' in reflection "
135 "because it is not included in a @MirrorsUsed annotation.");
136 }
137
138 /// Helper to print the given method information to the console the first
139 /// time it is called with it.
140 @NoInline()
141 void traceHelper(String method) {
142 if (JS('bool', '!this.cache')) {
143 JS('', 'this.cache = Object.create(null)');
144 }
145 if (JS('bool', '!this.cache[#]', method)) {
146 JS('', 'console.log(#)', method);
147 JS('', 'this.cache[#] = true', method);
148 }
149 }
150
151 class JSInvocationMirror implements Invocation {
152 static const METHOD = 0;
153 static const GETTER = 1;
154 static const SETTER = 2;
155
156 /// When [_memberName] is a String, it holds the mangled name of this
157 /// invocation. When it is a Symbol, it holds the unmangled name.
158 var /* String or Symbol */ _memberName;
159 final String _internalName;
160 final int _kind;
161 final List _arguments;
162 final List _namedArgumentNames;
163 /** Map from argument name to index in _arguments. */
164 Map<String, dynamic> _namedIndices = null;
165
166 JSInvocationMirror(this._memberName,
167 this._internalName,
168 this._kind,
169 this._arguments,
170 this._namedArgumentNames);
171
172 Symbol get memberName {
173 if (_memberName is Symbol) return _memberName;
174 String name = _memberName;
175 String unmangledName = mangledNames[name];
176 if (unmangledName != null) {
177 name = unmangledName.split(':')[0];
178 } else {
179 if (mangledNames[_internalName] == null) {
180 print("Warning: '$name' is used reflectively but not in MirrorsUsed. "
181 "This will break minified code.");
182 }
183 }
184 _memberName = new _symbol_dev.Symbol.unvalidated(name);
185 return _memberName;
186 }
187
188 bool get isMethod => _kind == METHOD;
189 bool get isGetter => _kind == GETTER;
190 bool get isSetter => _kind == SETTER;
191 bool get isAccessor => _kind != METHOD;
192
193 List get positionalArguments {
194 if (isGetter) return const [];
195 var argumentCount = _arguments.length - _namedArgumentNames.length;
196 if (argumentCount == 0) return const [];
197 var list = [];
198 for (var index = 0 ; index < argumentCount ; index++) {
199 list.add(_arguments[index]);
200 }
201 return makeLiteralListConst(list);
202 }
203
204 Map<Symbol, dynamic> get namedArguments {
205 // TODO: Make maps const (issue 10471)
206 if (isAccessor) return <Symbol, dynamic>{};
207 int namedArgumentCount = _namedArgumentNames.length;
208 int namedArgumentsStartIndex = _arguments.length - namedArgumentCount;
209 if (namedArgumentCount == 0) return <Symbol, dynamic>{};
210 var map = new Map<Symbol, dynamic>();
211 for (int i = 0; i < namedArgumentCount; i++) {
212 map[new _symbol_dev.Symbol.unvalidated(_namedArgumentNames[i])] =
213 _arguments[namedArgumentsStartIndex + i];
214 }
215 return map;
216 }
217
218 _getCachedInvocation(Object object) {
219 var interceptor = getInterceptor(object);
220 var receiver = object;
221 var name = _internalName;
222 var arguments = _arguments;
223 var interceptedNames = JS_EMBEDDED_GLOBAL('', INTERCEPTED_NAMES);
224 bool isIntercepted =
225 JS("bool", 'Object.prototype.hasOwnProperty.call(#, #)',
226 interceptedNames, name);
227 if (isIntercepted) {
228 receiver = interceptor;
229 if (JS('bool', '# === #', object, interceptor)) {
230 interceptor = null;
231 }
232 } else {
233 interceptor = null;
234 }
235 bool isCatchAll = false;
236 var method = JS('var', '#[#]', receiver, name);
237 if (JS('bool', 'typeof # != "function"', method) ) {
238 String baseName = _symbol_dev.Symbol.getName(memberName);
239 method = JS('', '#[# + "*"]', receiver, baseName);
240 if (method == null) {
241 interceptor = getInterceptor(object);
242 method = JS('', '#[# + "*"]', interceptor, baseName);
243 if (method != null) {
244 isIntercepted = true;
245 receiver = interceptor;
246 } else {
247 interceptor = null;
248 }
249 }
250 isCatchAll = true;
251 }
252 if (JS('bool', 'typeof # == "function"', method)) {
253 if (isCatchAll) {
254 return new CachedCatchAllInvocation(
255 name, method, isIntercepted, interceptor);
256 } else {
257 return new CachedInvocation(name, method, isIntercepted, interceptor);
258 }
259 } else {
260 // In this case, receiver doesn't implement name. So we should
261 // invoke noSuchMethod instead (which will often throw a
262 // NoSuchMethodError).
263 return new CachedNoSuchMethodInvocation(interceptor);
264 }
265 }
266
267 /// This method is called by [InstanceMirror.delegate].
268 static invokeFromMirror(JSInvocationMirror invocation, Object victim) {
269 var cached = invocation._getCachedInvocation(victim);
270 if (cached.isNoSuchMethod) {
271 return cached.invokeOn(victim, invocation);
272 } else {
273 return cached.invokeOn(victim, invocation._arguments);
274 }
275 }
276
277 static getCachedInvocation(JSInvocationMirror invocation, Object victim) {
278 return invocation._getCachedInvocation(victim);
279 }
280 }
281
282 class CachedInvocation {
283 // The mangled name of this invocation.
284 String mangledName;
285
286 /// The JS function to call.
287 var jsFunction;
288
289 /// True if this is an intercepted call.
290 bool isIntercepted;
291
292 /// Non-null interceptor if this is an intercepted call through an
293 /// [Interceptor].
294 Interceptor cachedInterceptor;
295
296 CachedInvocation(this.mangledName,
297 this.jsFunction,
298 this.isIntercepted,
299 this.cachedInterceptor);
300
301 bool get isNoSuchMethod => false;
302 bool get isGetterStub => JS("bool", "!!#.\$getterStub", jsFunction);
303
304 /// Applies [jsFunction] to [victim] with [arguments].
305 /// Users of this class must take care to check the arguments first.
306 invokeOn(Object victim, List arguments) {
307 var receiver = victim;
308 if (!isIntercepted) {
309 if (arguments is! JSArray) arguments = new List.from(arguments);
310 } else {
311 arguments = [victim]..addAll(arguments);
312 if (cachedInterceptor != null) receiver = cachedInterceptor;
313 }
314 return JS("var", "#.apply(#, #)", jsFunction, receiver, arguments);
315 }
316 }
317
318 class CachedCatchAllInvocation extends CachedInvocation {
319 final ReflectionInfo info;
320
321 CachedCatchAllInvocation(String name,
322 jsFunction,
323 bool isIntercepted,
324 Interceptor cachedInterceptor)
325 : info = new ReflectionInfo(jsFunction),
326 super(name, jsFunction, isIntercepted, cachedInterceptor);
327
328 bool get isGetterStub => false;
329
330 invokeOn(Object victim, List arguments) {
331 var receiver = victim;
332 int providedArgumentCount;
333 int fullParameterCount =
334 info.requiredParameterCount + info.optionalParameterCount;
335 if (!isIntercepted) {
336 if (arguments is JSArray) {
337 providedArgumentCount = arguments.length;
338 // If we need to add extra arguments before calling, we have
339 // to copy the arguments array.
340 if (providedArgumentCount < fullParameterCount) {
341 arguments = new List.from(arguments);
342 }
343 } else {
344 arguments = new List.from(arguments);
345 providedArgumentCount = arguments.length;
346 }
347 } else {
348 arguments = [victim]..addAll(arguments);
349 if (cachedInterceptor != null) receiver = cachedInterceptor;
350 providedArgumentCount = arguments.length - 1;
351 }
352 if (info.areOptionalParametersNamed &&
353 (providedArgumentCount > info.requiredParameterCount)) {
354 throw new UnimplementedNoSuchMethodError(
355 "Invocation of unstubbed method '${info.reflectionName}'"
356 " with ${arguments.length} arguments.");
357 } else if (providedArgumentCount < info.requiredParameterCount) {
358 throw new UnimplementedNoSuchMethodError(
359 "Invocation of unstubbed method '${info.reflectionName}'"
360 " with $providedArgumentCount arguments (too few).");
361 } else if (providedArgumentCount > fullParameterCount) {
362 throw new UnimplementedNoSuchMethodError(
363 "Invocation of unstubbed method '${info.reflectionName}'"
364 " with $providedArgumentCount arguments (too many).");
365 }
366 for (int i = providedArgumentCount; i < fullParameterCount; i++) {
367 arguments.add(getMetadata(info.defaultValue(i)));
368 }
369 return JS("var", "#.apply(#, #)", jsFunction, receiver, arguments);
370 }
371 }
372
373 class CachedNoSuchMethodInvocation {
374 /// Non-null interceptor if this is an intercepted call through an
375 /// [Interceptor].
376 var interceptor;
377
378 CachedNoSuchMethodInvocation(this.interceptor);
379
380 bool get isNoSuchMethod => true;
381 bool get isGetterStub => false;
382
383 invokeOn(Object victim, Invocation invocation) {
384 var receiver = (interceptor == null) ? victim : interceptor;
385 return receiver.noSuchMethod(invocation);
386 }
387 }
388
389 class ReflectionInfo {
390 static const int REQUIRED_PARAMETERS_INFO = 0;
391 static const int OPTIONAL_PARAMETERS_INFO = 1;
392 static const int FUNCTION_TYPE_INDEX = 2;
393 static const int FIRST_DEFAULT_ARGUMENT = 3;
394
395 /// A JavaScript function object.
396 final jsFunction;
397
398 /// Raw reflection information.
399 final List data;
400
401 /// Is this a getter or a setter.
402 final bool isAccessor;
403
404 /// Number of required parameters.
405 final int requiredParameterCount;
406
407 /// Number of optional parameters.
408 final int optionalParameterCount;
409
410 /// Are optional parameters named.
411 final bool areOptionalParametersNamed;
412
413 /// Either an index to the function type in the embedded `metadata` global or
414 /// a JavaScript function object which can compute such a type (presumably
415 /// due to free type variables).
416 final functionType;
417
418 List cachedSortedIndices;
419
420 ReflectionInfo.internal(this.jsFunction,
421 this.data,
422 this.isAccessor,
423 this.requiredParameterCount,
424 this.optionalParameterCount,
425 this.areOptionalParametersNamed,
426 this.functionType);
427
428 factory ReflectionInfo(jsFunction) {
429 List data = JS('JSExtendableArray|Null', r'#.$reflectionInfo', jsFunction);
430 if (data == null) return null;
431 data = JSArray.markFixedList(data);
432
433 int requiredParametersInfo =
434 JS('int', '#[#]', data, REQUIRED_PARAMETERS_INFO);
435 int requiredParameterCount = JS('int', '# >> 1', requiredParametersInfo);
436 bool isAccessor = (requiredParametersInfo & 1) == 1;
437
438 int optionalParametersInfo =
439 JS('int', '#[#]', data, OPTIONAL_PARAMETERS_INFO);
440 int optionalParameterCount = JS('int', '# >> 1', optionalParametersInfo);
441 bool areOptionalParametersNamed = (optionalParametersInfo & 1) == 1;
442
443 var functionType = JS('', '#[#]', data, FUNCTION_TYPE_INDEX);
444 return new ReflectionInfo.internal(
445 jsFunction, data, isAccessor, requiredParameterCount,
446 optionalParameterCount, areOptionalParametersNamed, functionType);
447 }
448
449 String parameterName(int parameter) {
450 int metadataIndex;
451 if (JS_GET_FLAG('MUST_RETAIN_METADATA')) {
452 metadataIndex = JS('int', '#[2 * # + # + #]', data,
453 parameter, optionalParameterCount, FIRST_DEFAULT_ARGUMENT);
454 } else {
455 metadataIndex = JS('int', '#[# + # + #]', data,
456 parameter, optionalParameterCount, FIRST_DEFAULT_ARGUMENT);
457 }
458 var metadata = JS_EMBEDDED_GLOBAL('', METADATA);
459 return JS('String', '#[#]', metadata, metadataIndex);
460 }
461
462 List<int> parameterMetadataAnnotations(int parameter) {
463 if (!JS_GET_FLAG('MUST_RETAIN_METADATA')) {
464 throw new StateError('metadata has not been preserved');
465 } else {
466 return JS('', '#[2 * # + # + # + 1]', data, parameter,
467 optionalParameterCount, FIRST_DEFAULT_ARGUMENT);
468 }
469 }
470
471 int defaultValue(int parameter) {
472 if (parameter < requiredParameterCount) return null;
473 return JS('int', '#[# + # - #]', data,
474 FIRST_DEFAULT_ARGUMENT, parameter, requiredParameterCount);
475 }
476
477 /// Returns the default value of the [parameter]th entry of the list of
478 /// parameters sorted by name.
479 int defaultValueInOrder(int parameter) {
480 if (parameter < requiredParameterCount) return null;
481
482 if (!areOptionalParametersNamed || optionalParameterCount == 1) {
483 return defaultValue(parameter);
484 }
485
486 int index = sortedIndex(parameter - requiredParameterCount);
487 return defaultValue(index);
488 }
489
490 /// Returns the default value of the [parameter]th entry of the list of
491 /// parameters sorted by name.
492 String parameterNameInOrder(int parameter) {
493 if (parameter < requiredParameterCount) return null;
494
495 if (!areOptionalParametersNamed ||
496 optionalParameterCount == 1) {
497 return parameterName(parameter);
498 }
499
500 int index = sortedIndex(parameter - requiredParameterCount);
501 return parameterName(index);
502 }
503
504 /// Computes the index of the parameter in the list of named parameters sorted
505 /// by their name.
506 int sortedIndex(int unsortedIndex) {
507 if (cachedSortedIndices == null) {
508 // TODO(karlklose): cache this between [ReflectionInfo] instances or cache
509 // [ReflectionInfo] instances by [jsFunction].
510 cachedSortedIndices = new List(optionalParameterCount);
511 Map<String, int> positions = <String, int>{};
512 for (int i = 0; i < optionalParameterCount; i++) {
513 int index = requiredParameterCount + i;
514 positions[parameterName(index)] = index;
515 }
516 int index = 0;
517 (positions.keys.toList()..sort()).forEach((String name) {
518 cachedSortedIndices[index++] = positions[name];
519 });
520 }
521 return cachedSortedIndices[unsortedIndex];
522 }
523
524 @NoInline()
525 computeFunctionRti(jsConstructor) {
526 if (JS('bool', 'typeof # == "number"', functionType)) {
527 return getMetadata(functionType);
528 } else if (JS('bool', 'typeof # == "function"', functionType)) {
529 var fakeInstance = JS('', 'new #()', jsConstructor);
530 setRuntimeTypeInfo(
531 fakeInstance, JS('JSExtendableArray', '#["<>"]', fakeInstance));
532 return JS('=Object|Null', r'#.apply({$receiver:#})',
533 functionType, fakeInstance);
534 } else {
535 throw new RuntimeError('Unexpected function type');
536 }
537 }
538
539 String get reflectionName => JS('String', r'#.$reflectionName', jsFunction);
540 }
541
542 getMetadata(int index) {
543 var metadata = JS_EMBEDDED_GLOBAL('', METADATA);
544 return JS('', '#[#]', metadata, index);
545 }
546
547 class Primitives { 33 class Primitives {
548 /// Isolate-unique ID for caching [JsClosureMirror.function]. 34 /// Isolate-unique ID for caching [JsClosureMirror.function].
549 /// Note the initial value is used by the first isolate (or if there are no 35 /// Note the initial value is used by the first isolate (or if there are no
550 /// isolates), new isolates will update this value to avoid conflicts by 36 /// isolates), new isolates will update this value to avoid conflicts by
551 /// calling [initializeStatics]. 37 /// calling [initializeStatics].
552 static String mirrorFunctionCacheName = '\$cachedFunction'; 38 static String mirrorFunctionCacheName = '\$cachedFunction';
553 39
554 /// Isolate-unique ID for caching [JsInstanceMirror._invoke]. 40 /// Isolate-unique ID for caching [JsInstanceMirror._invoke].
555 static String mirrorInvokeCacheName = '\$cachedInvocation'; 41 static String mirrorInvokeCacheName = '\$cachedInvocation';
556 42
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
673 return result; 159 return result;
674 } 160 }
675 return handleError(source); 161 return handleError(source);
676 } 162 }
677 return result; 163 return result;
678 } 164 }
679 165
680 /** [: r"$".codeUnitAt(0) :] */ 166 /** [: r"$".codeUnitAt(0) :] */
681 static const int DOLLAR_CHAR_VALUE = 36; 167 static const int DOLLAR_CHAR_VALUE = 36;
682 168
683 // TODO(jmesserly): remove this method.
684 static var constructorNameFallback = JS('Function', r'''
685 function getTagFallback(o) {
686 var constructor = o.constructor;
687 if (typeof constructor == "function") {
688 var name = constructor.name;
689 // If the name is a non-empty string, we use that as the type name of this
690 // object. There are various cases where that does not work, so we have t o
691 // detect them and fall through to the toString() based implementation.
692
693 if (typeof name == "string" &&
694
695 // Sometimes the string is empty. This test also catches minified
696 // shadow dom polyfil wrapper for Window on Firefox where the faked
697 // constructor name does not 'stick'. The shortest real DOM object
698 // names have three characters (e.g. URL, CSS).
699 name.length > 2 &&
700
701 // On Firefox we often get "Object" as the constructor name, even for
702 // more specialized DOM objects.
703 name !== "Object" &&
704
705 // This can happen in Opera.
706 name !== "Function.prototype") {
707 return name;
708 }
709 }
710 var s = Object.prototype.toString.call(o);
711 return s.substring(8, s.length - 1);
712 }''');
713
714 /// Creates a string containing the complete type for the class [className]
715 /// with the given type arguments.
716 ///
717 /// In minified mode, uses the unminified names if available.
718 static String formatType(String className, List typeArguments) {
719 return unmangleAllIdentifiersIfPreservedAnyways
720 ('$className${joinArguments(typeArguments, 0)}');
721 }
722
723 /// Returns the type of [object] as a string (including type arguments). 169 /// Returns the type of [object] as a string (including type arguments).
724 /// 170 ///
725 /// In minified mode, uses the unminified names if available. 171 /// In minified mode, uses the unminified names if available.
726 static String objectTypeName(Object object) { 172 static String objectTypeName(Object object) {
727 String name = JS('String', '#(#)', constructorNameFallback, object); 173 return getRuntimeType(object).toString();
728 if (name == 'Object') {
729 // Try to decompile the constructor by turning it into a string and get
730 // the name out of that. If the decompiled name is a string containing an
731 // identifier, we use that instead of the very generic 'Object'.
732 var decompiled =
733 JS('var', r'#.match(/^\s*function\s*(\S*)\s*\(/)[1]',
734 JS('var', r'String(#.constructor)', object));
735 if (decompiled is String)
736 if (JS('bool', r'/^\w+$/.test(#)', decompiled))
737 name = decompiled;
738 }
739 // TODO(kasperl): If the namer gave us a fresh global name, we may
740 // want to remove the numeric suffix that makes it unique too.
741 // TODO(jacobr): commented this out as it seems bogus and it breaks as
742 // codeUnitAt is not yet supported.
743 /*
744 if (name.length > 1 && identical(name.codeUnitAt(0), DOLLAR_CHAR_VALUE)) {
745 name = name.substring(1);
746 }
747 */
748 return formatType(name, getRuntimeTypeInfo(object));
749 } 174 }
750 175
751 /// In minified mode, uses the unminified names if available. 176 /// In minified mode, uses the unminified names if available.
752 static String objectToString(Object object) { 177 static String objectToString(Object object) {
753 // String name = objectTypeName(object); 178 // String name = objectTypeName(object);
754 String name = JS('String', 'dart.typeName(dart.realRuntimeType(#))', object) ; 179 String name = JS('String', 'dart.typeName(dart.realRuntimeType(#))', object) ;
755 return "Instance of '$name'"; 180 return "Instance of '$name'";
756 } 181 }
757 182
758 static num dateNow() => JS('int', r'Date.now()'); 183 static num dateNow() => JS('int', r'Date.now()');
(...skipping 21 matching lines...) Expand all
780 'typeof version == "function"' 205 'typeof version == "function"'
781 ' && typeof os == "object" && "system" in os'); 206 ' && typeof os == "object" && "system" in os');
782 } 207 }
783 208
784 static bool get isJsshell { 209 static bool get isJsshell {
785 return JS('bool', 210 return JS('bool',
786 'typeof version == "function" && typeof system == "function"'); 211 'typeof version == "function" && typeof system == "function"');
787 } 212 }
788 213
789 static String currentUri() { 214 static String currentUri() {
790 requiresPreamble();
791 // In a browser return self.location.href. 215 // In a browser return self.location.href.
792 if (JS('bool', '!!self.location')) { 216 if (JS('bool', '!!self.location')) {
793 return JS('String', 'self.location.href'); 217 return JS('String', 'self.location.href');
794 } 218 }
795 219
796 return null; 220 return null;
797 } 221 }
798 222
799 // This is to avoid stack overflows due to very large argument arrays in 223 // This is to avoid stack overflows due to very large argument arrays in
800 // apply(). It fixes http://dartbug.com/6919 224 // apply(). It fixes http://dartbug.com/6919
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
1019 return JS('var', '#[#]', object, key); 443 return JS('var', '#[#]', object, key);
1020 } 444 }
1021 445
1022 static void setProperty(object, key, value) { 446 static void setProperty(object, key, value) {
1023 if (object == null || object is bool || object is num || object is String) { 447 if (object == null || object is bool || object is num || object is String) {
1024 throw new ArgumentError(object); 448 throw new ArgumentError(object);
1025 } 449 }
1026 JS('void', '#[#] = #', object, key, value); 450 JS('void', '#[#] = #', object, key, value);
1027 } 451 }
1028 452
1029 static functionNoSuchMethod(function,
1030 List positionalArguments,
1031 Map<String, dynamic> namedArguments) {
1032 int argumentCount = 0;
1033 List arguments = [];
1034 List namedArgumentList = [];
1035
1036 if (positionalArguments != null) {
1037 argumentCount += positionalArguments.length;
1038 arguments.addAll(positionalArguments);
1039 }
1040
1041 String names = '';
1042 if (namedArguments != null && !namedArguments.isEmpty) {
1043 namedArguments.forEach((String name, argument) {
1044 names = '$names\$$name';
1045 namedArgumentList.add(name);
1046 arguments.add(argument);
1047 argumentCount++;
1048 });
1049 }
1050
1051 String selectorName =
1052 '${JS_GET_NAME("CALL_PREFIX")}\$$argumentCount$names';
1053
1054 return function.noSuchMethod(
1055 createUnmangledInvocationMirror(
1056 #call,
1057 selectorName,
1058 JSInvocationMirror.METHOD,
1059 arguments,
1060 namedArgumentList));
1061 }
1062
1063 static applyFunction(Function function,
1064 List positionalArguments,
1065 Map<String, dynamic> namedArguments) {
1066 // Dispatch on presence of named arguments to improve tree-shaking.
1067 //
1068 // This dispatch is as simple as possible to help the compiler detect the
1069 // common case of `null` namedArguments, either via inlining or
1070 // specialization.
1071 return namedArguments == null
1072 ? applyFunctionWithPositionalArguments(
1073 function, positionalArguments)
1074 : applyFunctionWithNamedArguments(
1075 function, positionalArguments, namedArguments);
1076 }
1077
1078 static applyFunctionWithPositionalArguments(Function function,
1079 List positionalArguments) {
1080 int argumentCount = 0;
1081 List arguments;
1082
1083 if (positionalArguments != null) {
1084 if (JS('bool', '# instanceof Array', positionalArguments)) {
1085 arguments = positionalArguments;
1086 } else {
1087 arguments = new List.from(positionalArguments);
1088 }
1089 argumentCount = JS('int', '#.length', arguments);
1090 } else {
1091 arguments = [];
1092 }
1093
1094 String selectorName = '${JS_GET_NAME("CALL_PREFIX")}\$$argumentCount';
1095 var jsFunction = JS('var', '#[#]', function, selectorName);
1096 if (jsFunction == null) {
1097
1098 // TODO(ahe): This might occur for optional arguments if there is no call
1099 // selector with that many arguments.
1100
1101 return functionNoSuchMethod(function, positionalArguments, null);
1102 }
1103 // We bound 'this' to [function] because of how we compile
1104 // closures: escaped local variables are stored and accessed through
1105 // [function].
1106 return JS('var', '#.apply(#, #)', jsFunction, function, arguments);
1107 }
1108
1109 static applyFunctionWithNamedArguments(Function function,
1110 List positionalArguments,
1111 Map<String, dynamic> namedArguments) {
1112 if (namedArguments.isEmpty) {
1113 return applyFunctionWithPositionalArguments(
1114 function, positionalArguments);
1115 }
1116 // TODO(ahe): The following code can be shared with
1117 // JsInstanceMirror.invoke.
1118 var interceptor = getInterceptor(function);
1119 var jsFunction = JS('', '#["call*"]', interceptor);
1120
1121 if (jsFunction == null) {
1122 return functionNoSuchMethod(
1123 function, positionalArguments, namedArguments);
1124 }
1125 ReflectionInfo info = new ReflectionInfo(jsFunction);
1126 if (info == null || !info.areOptionalParametersNamed) {
1127 return functionNoSuchMethod(
1128 function, positionalArguments, namedArguments);
1129 }
1130
1131 if (positionalArguments != null) {
1132 positionalArguments = new List.from(positionalArguments);
1133 } else {
1134 positionalArguments = [];
1135 }
1136 // Check the number of positional arguments is valid.
1137 if (info.requiredParameterCount != positionalArguments.length) {
1138 return functionNoSuchMethod(
1139 function, positionalArguments, namedArguments);
1140 }
1141 var defaultArguments = new Map();
1142 for (int i = 0; i < info.optionalParameterCount; i++) {
1143 int index = i + info.requiredParameterCount;
1144 var parameterName = info.parameterNameInOrder(index);
1145 var value = info.defaultValueInOrder(index);
1146 var defaultValue = getMetadata(value);
1147 defaultArguments[parameterName] = defaultValue;
1148 }
1149 bool bad = false;
1150 namedArguments.forEach((String parameter, value) {
1151 if (defaultArguments.containsKey(parameter)) {
1152 defaultArguments[parameter] = value;
1153 } else {
1154 // Extraneous named argument.
1155 bad = true;
1156 }
1157 });
1158 if (bad) {
1159 return functionNoSuchMethod(
1160 function, positionalArguments, namedArguments);
1161 }
1162 positionalArguments.addAll(defaultArguments.values);
1163 return JS('', '#.apply(#, #)', jsFunction, function, positionalArguments);
1164 }
1165
1166 static _mangledNameMatchesType(String mangledName, TypeImpl type) {
1167 return JS('bool', '# == #', mangledName, type._typeName);
1168 }
1169
1170 static bool identicalImplementation(a, b) { 453 static bool identicalImplementation(a, b) {
1171 return JS('bool', '# == null', a) 454 return JS('bool', '# == null', a)
1172 ? JS('bool', '# == null', b) 455 ? JS('bool', '# == null', b)
1173 : JS('bool', '# === #', a, b); 456 : JS('bool', '# === #', a, b);
1174 } 457 }
1175 458
1176 static StackTrace extractStackTrace(Error error) { 459 static StackTrace extractStackTrace(Error error) {
1177 return getTraceFromException(JS('', r'#.$thrownJsError', error)); 460 return getTraceFromException(JS('', r'#.$thrownJsError', error));
1178 } 461 }
1179 } 462 }
1180 463
1181 /// Helper class for allocating and using JS object literals as caches.
1182 class JsCache {
1183 /// Returns a JavaScript object suitable for use as a cache.
1184 static allocate() {
1185 var result = JS('=Object', 'Object.create(null)');
1186 // Deleting a property makes V8 assume that it shouldn't create a hidden
1187 // class for [result] and map transitions. Although these map transitions
1188 // pay off if there are many cache hits for the same keys, it becomes
1189 // really slow when there aren't many repeated hits.
1190 JS('void', '#.x=0', result);
1191 JS('void', 'delete #.x', result);
1192 return result;
1193 }
1194
1195 static fetch(cache, String key) {
1196 return JS('', '#[#]', cache, key);
1197 }
1198
1199 static void update(cache, String key, value) {
1200 JS('void', '#[#] = #', cache, key, value);
1201 }
1202 }
1203
1204 /**
1205 * Called by generated code to throw an illegal-argument exception,
1206 * for example, if a non-integer index is given to an optimized
1207 * indexed access.
1208 */
1209 iae(argument) {
1210 throw new ArgumentError(argument);
1211 }
1212
1213 /**
1214 * Called by generated code to throw an index-out-of-range exception,
1215 * for example, if a bounds check fails in an optimized indexed
1216 * access. This may also be called when the index is not an integer, in
1217 * which case it throws an illegal-argument exception instead, like
1218 * [iae], or when the receiver is null.
1219 */
1220 ioore(receiver, index) {
1221 if (receiver == null) receiver.length; // Force a NoSuchMethodError.
1222 if (index is !int) iae(index);
1223 throw new RangeError.value(index);
1224 }
1225
1226 stringLastIndexOfUnchecked(receiver, element, start) 464 stringLastIndexOfUnchecked(receiver, element, start)
1227 => JS('int', r'#.lastIndexOf(#, #)', receiver, element, start); 465 => JS('int', r'#.lastIndexOf(#, #)', receiver, element, start);
1228 466
1229 467
1230 checkNull(object) { 468 checkNull(object) {
1231 if (object == null) throw new ArgumentError(null); 469 if (object == null) throw new ArgumentError(null);
1232 return object; 470 return object;
1233 } 471 }
1234 472
1235 checkNum(value) { 473 checkNum(value) {
(...skipping 17 matching lines...) Expand all
1253 return value; 491 return value;
1254 } 492 }
1255 493
1256 checkString(value) { 494 checkString(value) {
1257 if (value is !String) { 495 if (value is !String) {
1258 throw new ArgumentError(value); 496 throw new ArgumentError(value);
1259 } 497 }
1260 return value; 498 return value;
1261 } 499 }
1262 500
1263 /**
1264 * Wrap the given Dart object and record a stack trace.
1265 *
1266 * The code in [unwrapException] deals with getting the original Dart
1267 * object out of the wrapper again.
1268 */
1269 @NoInline()
1270 wrapException(ex) {
1271 if (ex == null) ex = new NullThrownError();
1272 var wrapper = JS('', 'new Error()');
1273 // [unwrapException] looks for the property 'dartException'.
1274 JS('void', '#.dartException = #', wrapper, ex);
1275
1276 if (JS('bool', '"defineProperty" in Object')) {
1277 // Define a JavaScript getter for 'message'. This is to work around V8 bug
1278 // (https://code.google.com/p/v8/issues/detail?id=2519). The default
1279 // toString on Error returns the value of 'message' if 'name' is
1280 // empty. Setting toString directly doesn't work, see the bug.
1281 JS('void', 'Object.defineProperty(#, "message", { get: # })',
1282 wrapper, DART_CLOSURE_TO_JS(toStringWrapper));
1283 JS('void', '#.name = ""', wrapper);
1284 } else {
1285 // In the unlikely event the browser doesn't support Object.defineProperty,
1286 // hope that it just calls toString.
1287 JS('void', '#.toString = #', wrapper, DART_CLOSURE_TO_JS(toStringWrapper));
1288 }
1289
1290 return wrapper;
1291 }
1292
1293 /// Do not call directly.
1294 toStringWrapper() {
1295 // This method gets installed as toString on a JavaScript object. Due to the
1296 // weird scope rules of JavaScript, JS 'this' will refer to that object.
1297 return JS('', r'this.dartException').toString();
1298 }
1299
1300 /**
1301 * This wraps the exception and does the throw. It is possible to call this in
1302 * a JS expression context, where the throw statement is not allowed. Helpers
1303 * are never inlined, so we don't risk inlining the throw statement into an
1304 * expression context.
1305 */
1306 throwExpression(ex) {
1307 JS('void', 'throw #', wrapException(ex));
1308 }
1309
1310 makeLiteralListConst(list) {
1311 JS('bool', r'#.immutable$list = #', list, true);
1312 JS('bool', r'#.fixed$length = #', list, true);
1313 return list;
1314 }
1315
1316 throwRuntimeError(message) { 501 throwRuntimeError(message) {
1317 throw new RuntimeError(message); 502 throw new RuntimeError(message);
1318 } 503 }
1319 504
1320 throwAbstractClassInstantiationError(className) { 505 throwAbstractClassInstantiationError(className) {
1321 throw new AbstractClassInstantiationError(className); 506 throw new AbstractClassInstantiationError(className);
1322 } 507 }
1323 508
1324
1325 /**
1326 * Helper class for building patterns recognizing native type errors.
1327 */
1328 class TypeErrorDecoder {
1329 // Field names are private to help tree-shaking.
1330
1331 /// A regular expression which matches is matched against an error message.
1332 final String _pattern;
1333
1334 /// The group index of "arguments" in [_pattern], or -1 if _pattern has no
1335 /// match for "arguments".
1336 final int _arguments;
1337
1338 /// The group index of "argumentsExpr" in [_pattern], or -1 if _pattern has
1339 /// no match for "argumentsExpr".
1340 final int _argumentsExpr;
1341
1342 /// The group index of "expr" in [_pattern], or -1 if _pattern has no match
1343 /// for "expr".
1344 final int _expr;
1345
1346 /// The group index of "method" in [_pattern], or -1 if _pattern has no match
1347 /// for "method".
1348 final int _method;
1349
1350 /// The group index of "receiver" in [_pattern], or -1 if _pattern has no
1351 /// match for "receiver".
1352 final int _receiver;
1353
1354 /// Pattern used to recognize a NoSuchMethodError error (and
1355 /// possibly extract the method name).
1356 static final TypeErrorDecoder noSuchMethodPattern =
1357 extractPattern(provokeCallErrorOn(buildJavaScriptObject()));
1358
1359 /// Pattern used to recognize an "object not a closure" error (and
1360 /// possibly extract the method name).
1361 static final TypeErrorDecoder notClosurePattern =
1362 extractPattern(provokeCallErrorOn(buildJavaScriptObjectWithNonClosure()));
1363
1364 /// Pattern used to recognize a NoSuchMethodError on JavaScript null
1365 /// call.
1366 static final TypeErrorDecoder nullCallPattern =
1367 extractPattern(provokeCallErrorOn(JS('', 'null')));
1368
1369 /// Pattern used to recognize a NoSuchMethodError on JavaScript literal null
1370 /// call.
1371 static final TypeErrorDecoder nullLiteralCallPattern =
1372 extractPattern(provokeCallErrorOnNull());
1373
1374 /// Pattern used to recognize a NoSuchMethodError on JavaScript
1375 /// undefined call.
1376 static final TypeErrorDecoder undefinedCallPattern =
1377 extractPattern(provokeCallErrorOn(JS('', 'void 0')));
1378
1379 /// Pattern used to recognize a NoSuchMethodError on JavaScript literal
1380 /// undefined call.
1381 static final TypeErrorDecoder undefinedLiteralCallPattern =
1382 extractPattern(provokeCallErrorOnUndefined());
1383
1384 /// Pattern used to recognize a NoSuchMethodError on JavaScript null
1385 /// property access.
1386 static final TypeErrorDecoder nullPropertyPattern =
1387 extractPattern(provokePropertyErrorOn(JS('', 'null')));
1388
1389 /// Pattern used to recognize a NoSuchMethodError on JavaScript literal null
1390 /// property access.
1391 static final TypeErrorDecoder nullLiteralPropertyPattern =
1392 extractPattern(provokePropertyErrorOnNull());
1393
1394 /// Pattern used to recognize a NoSuchMethodError on JavaScript
1395 /// undefined property access.
1396 static final TypeErrorDecoder undefinedPropertyPattern =
1397 extractPattern(provokePropertyErrorOn(JS('', 'void 0')));
1398
1399 /// Pattern used to recognize a NoSuchMethodError on JavaScript literal
1400 /// undefined property access.
1401 static final TypeErrorDecoder undefinedLiteralPropertyPattern =
1402 extractPattern(provokePropertyErrorOnUndefined());
1403
1404 TypeErrorDecoder(this._arguments,
1405 this._argumentsExpr,
1406 this._expr,
1407 this._method,
1408 this._receiver,
1409 this._pattern);
1410
1411 /// Returns a JavaScript object literal (map) with at most the
1412 /// following keys:
1413 ///
1414 /// * arguments: The arguments as formatted by the JavaScript
1415 /// engine. No browsers are known to provide this information.
1416 ///
1417 /// * argumentsExpr: The syntax of the arguments (JavaScript source
1418 /// code). No browsers are known to provide this information.
1419 ///
1420 /// * expr: The syntax of the receiver expression (JavaScript source
1421 /// code). Firefox provides this information, for example: "$expr$.$method$
1422 /// is not a function".
1423 ///
1424 /// * method: The name of the called method (mangled name). At least Firefox
1425 /// and Chrome/V8 provides this information, for example, "Object [object
1426 /// Object] has no method '$method$'".
1427 ///
1428 /// * receiver: The string representation of the receiver. Chrome/V8
1429 /// used to provide this information (by calling user-defined
1430 /// JavaScript toString on receiver), but it has degenerated into
1431 /// "[object Object]" in recent versions.
1432 matchTypeError(message) {
1433 var match = JS('JSExtendableArray|Null',
1434 'new RegExp(#).exec(#)', _pattern, message);
1435 if (match == null) return null;
1436 var result = JS('', 'Object.create(null)');
1437 if (_arguments != -1) {
1438 JS('', '#.arguments = #[# + 1]', result, match, _arguments);
1439 }
1440 if (_argumentsExpr != -1) {
1441 JS('', '#.argumentsExpr = #[# + 1]', result, match, _argumentsExpr);
1442 }
1443 if (_expr != -1) {
1444 JS('', '#.expr = #[# + 1]', result, match, _expr);
1445 }
1446 if (_method != -1) {
1447 JS('', '#.method = #[# + 1]', result, match, _method);
1448 }
1449 if (_receiver != -1) {
1450 JS('', '#.receiver = #[# + 1]', result, match, _receiver);
1451 }
1452
1453 return result;
1454 }
1455
1456 /// Builds a JavaScript Object with a toString method saying
1457 /// r"$receiver$".
1458 static buildJavaScriptObject() {
1459 return JS('', r'{ toString: function() { return "$receiver$"; } }');
1460 }
1461
1462 /// Builds a JavaScript Object with a toString method saying
1463 /// r"$receiver$". The property "$method" is defined, but is not a function.
1464 static buildJavaScriptObjectWithNonClosure() {
1465 return JS('', r'{ $method$: null, '
1466 r'toString: function() { return "$receiver$"; } }');
1467 }
1468
1469 /// Extract a pattern from a JavaScript TypeError message.
1470 ///
1471 /// The patterns are extracted by forcing TypeErrors on known
1472 /// objects thus forcing known strings into the error message. The
1473 /// known strings are then replaced with wildcards which in theory
1474 /// makes it possible to recognize the desired information even if
1475 /// the error messages are reworded or translated.
1476 static extractPattern(String message) {
1477 // Some JavaScript implementations (V8 at least) include a
1478 // representation of the receiver in the error message, however,
1479 // this representation is not always [: receiver.toString() :],
1480 // sometimes it is [: Object.prototype.toString(receiver) :], and
1481 // sometimes it is an implementation specific method (but that
1482 // doesn't seem to happen for object literals). So sometimes we
1483 // get the text "[object Object]". The shortest way to get that
1484 // string is using "String({})".
1485 // See: http://code.google.com/p/v8/issues/detail?id=2519.
1486 message = JS('String', r"#.replace(String({}), '$receiver$')", message);
1487
1488 // Since we want to create a new regular expression from an unknown string,
1489 // we must escape all regular expression syntax.
1490 message = JS('String', r"#.replace(new RegExp(#, 'g'), '\\$&')",
1491 message, ESCAPE_REGEXP);
1492
1493 // Look for the special pattern \$camelCase\$ (all the $ symbols
1494 // have been escaped already), as we will soon be inserting
1495 // regular expression syntax that we want interpreted by RegExp.
1496 List<String> match =
1497 JS('JSExtendableArray|Null', r"#.match(/\\\$[a-zA-Z]+\\\$/g)", message);
1498 if (match == null) match = [];
1499
1500 // Find the positions within the substring matches of the error message
1501 // components. This will help us extract information later, such as the
1502 // method name.
1503 int arguments = JS('int', '#.indexOf(#)', match, r'\$arguments\$');
1504 int argumentsExpr = JS('int', '#.indexOf(#)', match, r'\$argumentsExpr\$');
1505 int expr = JS('int', '#.indexOf(#)', match, r'\$expr\$');
1506 int method = JS('int', '#.indexOf(#)', match, r'\$method\$');
1507 int receiver = JS('int', '#.indexOf(#)', match, r'\$receiver\$');
1508
1509 // Replace the patterns with a regular expression wildcard.
1510 // Note: in a perfect world, one would use "(.*)", but not in
1511 // JavaScript, "." does not match newlines.
1512 String pattern = JS('String',
1513 r"#.replace('\\$arguments\\$', '((?:x|[^x])*)')"
1514 r".replace('\\$argumentsExpr\\$', '((?:x|[^x])*)')"
1515 r".replace('\\$expr\\$', '((?:x|[^x])*)')"
1516 r".replace('\\$method\\$', '((?:x|[^x])*)')"
1517 r".replace('\\$receiver\\$', '((?:x|[^x])*)')",
1518 message);
1519
1520 return new TypeErrorDecoder(arguments,
1521 argumentsExpr,
1522 expr,
1523 method,
1524 receiver,
1525 pattern);
1526 }
1527
1528 /// Provokes a TypeError and returns its message.
1529 ///
1530 /// The error is provoked so all known variable content can be recognized and
1531 /// a pattern can be inferred.
1532 static String provokeCallErrorOn(expression) {
1533 // This function is carefully created to maximize the possibility
1534 // of decoding the TypeError message and turning it into a general
1535 // pattern.
1536 //
1537 // The idea is to inject something known into something unknown. The
1538 // unknown entity is the error message that the browser provides with a
1539 // TypeError. It is a human readable message, possibly localized in a
1540 // language no dart2js engineer understand. We assume that $name$ would
1541 // never naturally occur in a human readable error message, yet it is easy
1542 // to decode.
1543 //
1544 // For example, evaluate this in V8 version 3.13.7.6:
1545 //
1546 // var $expr$ = null; $expr$.$method$()
1547 //
1548 // The VM throws an instance of TypeError whose message property contains
1549 // "Cannot call method '$method$' of null". We can then reasonably assume
1550 // that if the string contains $method$, that's where the method name will
1551 // be in general. Call this automatically reverse engineering the error
1552 // format string in V8.
1553 //
1554 // So the error message from V8 is turned into this regular expression:
1555 //
1556 // "Cannot call method '(.*)' of null"
1557 //
1558 // Similarly, if we evaluate:
1559 //
1560 // var $expr$ = {toString: function() { return '$receiver$'; }};
1561 // $expr$.$method$()
1562 //
1563 // We get this message: "Object $receiver$ has no method '$method$'"
1564 //
1565 // Which is turned into this regular expression:
1566 //
1567 // "Object (.*) has no method '(.*)'"
1568 //
1569 // Firefox/jsshell is slightly different, it tries to include the source
1570 // code that caused the exception, so we get this message: "$expr$.$method$
1571 // is not a function" which is turned into this regular expression:
1572 //
1573 // "(.*)\\.(.*) is not a function"
1574
1575 var function = JS('', r"""function($expr$) {
1576 var $argumentsExpr$ = '$arguments$';
1577 try {
1578 $expr$.$method$($argumentsExpr$);
1579 } catch (e) {
1580 return e.message;
1581 }
1582 }""");
1583 return JS('String', '(#)(#)', function, expression);
1584 }
1585
1586 /// Similar to [provokeCallErrorOn], but provokes an error directly on
1587 /// literal "null" expression.
1588 static String provokeCallErrorOnNull() {
1589 // See [provokeCallErrorOn] for a detailed explanation.
1590 var function = JS('', r"""function() {
1591 var $argumentsExpr$ = '$arguments$';
1592 try {
1593 null.$method$($argumentsExpr$);
1594 } catch (e) {
1595 return e.message;
1596 }
1597 }""");
1598 return JS('String', '(#)()', function);
1599 }
1600
1601 /// Similar to [provokeCallErrorOnNull], but provokes an error directly on
1602 /// (void 0), that is, "undefined".
1603 static String provokeCallErrorOnUndefined() {
1604 // See [provokeCallErrorOn] for a detailed explanation.
1605 var function = JS('', r"""function() {
1606 var $argumentsExpr$ = '$arguments$';
1607 try {
1608 (void 0).$method$($argumentsExpr$);
1609 } catch (e) {
1610 return e.message;
1611 }
1612 }""");
1613 return JS('String', '(#)()', function);
1614 }
1615
1616 /// Similar to [provokeCallErrorOn], but provokes a property access
1617 /// error.
1618 static String provokePropertyErrorOn(expression) {
1619 // See [provokeCallErrorOn] for a detailed explanation.
1620 var function = JS('', r"""function($expr$) {
1621 try {
1622 $expr$.$method$;
1623 } catch (e) {
1624 return e.message;
1625 }
1626 }""");
1627 return JS('String', '(#)(#)', function, expression);
1628 }
1629
1630 /// Similar to [provokePropertyErrorOn], but provokes an property access
1631 /// error directly on literal "null" expression.
1632 static String provokePropertyErrorOnNull() {
1633 // See [provokeCallErrorOn] for a detailed explanation.
1634 var function = JS('', r"""function() {
1635 try {
1636 null.$method$;
1637 } catch (e) {
1638 return e.message;
1639 }
1640 }""");
1641 return JS('String', '(#)()', function);
1642 }
1643
1644 /// Similar to [provokePropertyErrorOnNull], but provokes an property access
1645 /// error directly on (void 0), that is, "undefined".
1646 static String provokePropertyErrorOnUndefined() {
1647 // See [provokeCallErrorOn] for a detailed explanation.
1648 var function = JS('', r"""function() {
1649 try {
1650 (void 0).$method$;
1651 } catch (e) {
1652 return e.message;
1653 }
1654 }""");
1655 return JS('String', '(#)()', function);
1656 }
1657 }
1658
1659 class NullError extends Error implements NoSuchMethodError { 509 class NullError extends Error implements NoSuchMethodError {
1660 final String _message; 510 final String _message;
1661 final String _method; 511 final String _method;
1662 512
1663 NullError(this._message, match) 513 NullError(this._message, match)
1664 : _method = match == null ? null : JS('', '#.method', match); 514 : _method = match == null ? null : JS('', '#.method', match);
1665 515
1666 String toString() { 516 String toString() {
1667 if (_method == null) return 'NullError: $_message'; 517 if (_method == null) return 'NullError: $_message';
1668 return 'NullError: Cannot call "$_method" on null'; 518 return 'NullError: Cannot call "$_method" on null';
(...skipping 22 matching lines...) Expand all
1691 541
1692 class UnknownJsTypeError extends Error { 542 class UnknownJsTypeError extends Error {
1693 final String _message; 543 final String _message;
1694 544
1695 UnknownJsTypeError(this._message); 545 UnknownJsTypeError(this._message);
1696 546
1697 String toString() => _message.isEmpty ? 'Error' : 'Error: $_message'; 547 String toString() => _message.isEmpty ? 'Error' : 'Error: $_message';
1698 } 548 }
1699 549
1700 /** 550 /**
1701 * Called from catch blocks in generated code to extract the Dart
1702 * exception from the thrown value. The thrown value may have been
1703 * created by [wrapException] or it may be a 'native' JS exception.
1704 *
1705 * Some native exceptions are mapped to new Dart instances, others are
1706 * returned unmodified.
1707 */
1708 unwrapException(ex) {
1709 /// If error implements Error, save [ex] in [error.$thrownJsError].
1710 /// Otherwise, do nothing. Later, the stack trace can then be extraced from
1711 /// [ex].
1712 saveStackTrace(error) {
1713 if (error is Error) {
1714 var thrownStackTrace = JS('', r'#.$thrownJsError', error);
1715 if (thrownStackTrace == null) {
1716 JS('void', r'#.$thrownJsError = #', error, ex);
1717 }
1718 }
1719 return error;
1720 }
1721
1722 // Note that we are checking if the object has the property. If it
1723 // has, it could be set to null if the thrown value is null.
1724 if (ex == null) return null;
1725 if (JS('bool', 'typeof # !== "object"', ex)) return ex;
1726
1727 if (JS('bool', r'"dartException" in #', ex)) {
1728 return saveStackTrace(JS('', r'#.dartException', ex));
1729 } else if (!JS('bool', r'"message" in #', ex)) {
1730 return ex;
1731 }
1732
1733 // Grab hold of the exception message. This field is available on
1734 // all supported browsers.
1735 var message = JS('var', r'#.message', ex);
1736
1737 // Internet Explorer has an error number. This is the most reliable way to
1738 // detect specific errors, so check for this first.
1739 if (JS('bool', '"number" in #', ex)
1740 && JS('bool', 'typeof #.number == "number"', ex)) {
1741 int number = JS('int', '#.number', ex);
1742
1743 // From http://msdn.microsoft.com/en-us/library/ie/hc53e755(v=vs.94).aspx
1744 // "number" is a 32-bit word. The error code is the low 16 bits, and the
1745 // facility code is the upper 16 bits.
1746 var ieErrorCode = number & 0xffff;
1747 var ieFacilityNumber = (number >> 16) & 0x1fff;
1748
1749 // http://msdn.microsoft.com/en-us/library/aa264975(v=vs.60).aspx
1750 // http://msdn.microsoft.com/en-us/library/ie/1dk3k160(v=vs.94).aspx
1751 if (ieFacilityNumber == 10) {
1752 switch (ieErrorCode) {
1753 case 438:
1754 return saveStackTrace(
1755 new JsNoSuchMethodError('$message (Error $ieErrorCode)', null));
1756 case 445:
1757 case 5007:
1758 return saveStackTrace(
1759 new NullError('$message (Error $ieErrorCode)', null));
1760 }
1761 }
1762 }
1763
1764 if (JS('bool', r'# instanceof TypeError', ex)) {
1765 var match;
1766 // Using JS to give type hints to the compiler to help tree-shaking.
1767 // TODO(ahe): That should be unnecessary due to type inference.
1768 var nsme =
1769 JS('TypeErrorDecoder', '#', TypeErrorDecoder.noSuchMethodPattern);
1770 var notClosure =
1771 JS('TypeErrorDecoder', '#', TypeErrorDecoder.notClosurePattern);
1772 var nullCall =
1773 JS('TypeErrorDecoder', '#', TypeErrorDecoder.nullCallPattern);
1774 var nullLiteralCall =
1775 JS('TypeErrorDecoder', '#', TypeErrorDecoder.nullLiteralCallPattern);
1776 var undefCall =
1777 JS('TypeErrorDecoder', '#', TypeErrorDecoder.undefinedCallPattern);
1778 var undefLiteralCall =
1779 JS('TypeErrorDecoder', '#',
1780 TypeErrorDecoder.undefinedLiteralCallPattern);
1781 var nullProperty =
1782 JS('TypeErrorDecoder', '#', TypeErrorDecoder.nullPropertyPattern);
1783 var nullLiteralProperty =
1784 JS('TypeErrorDecoder', '#',
1785 TypeErrorDecoder.nullLiteralPropertyPattern);
1786 var undefProperty =
1787 JS('TypeErrorDecoder', '#', TypeErrorDecoder.undefinedPropertyPattern);
1788 var undefLiteralProperty =
1789 JS('TypeErrorDecoder', '#',
1790 TypeErrorDecoder.undefinedLiteralPropertyPattern);
1791 if ((match = nsme.matchTypeError(message)) != null) {
1792 return saveStackTrace(new JsNoSuchMethodError(message, match));
1793 } else if ((match = notClosure.matchTypeError(message)) != null) {
1794 // notClosure may match "({c:null}).c()" or "({c:1}).c()", so we
1795 // cannot tell if this an attempt to invoke call on null or a
1796 // non-function object.
1797 // But we do know the method name is "call".
1798 JS('', '#.method = "call"', match);
1799 return saveStackTrace(new JsNoSuchMethodError(message, match));
1800 } else if ((match = nullCall.matchTypeError(message)) != null ||
1801 (match = nullLiteralCall.matchTypeError(message)) != null ||
1802 (match = undefCall.matchTypeError(message)) != null ||
1803 (match = undefLiteralCall.matchTypeError(message)) != null ||
1804 (match = nullProperty.matchTypeError(message)) != null ||
1805 (match = nullLiteralCall.matchTypeError(message)) != null ||
1806 (match = undefProperty.matchTypeError(message)) != null ||
1807 (match = undefLiteralProperty.matchTypeError(message)) != null) {
1808 return saveStackTrace(new NullError(message, match));
1809 }
1810
1811 // If we cannot determine what kind of error this is, we fall back
1812 // to reporting this as a generic error. It's probably better than
1813 // nothing.
1814 return saveStackTrace(
1815 new UnknownJsTypeError(message is String ? message : ''));
1816 }
1817
1818 if (JS('bool', r'# instanceof RangeError', ex)) {
1819 if (message is String && contains(message, 'call stack')) {
1820 return new StackOverflowError();
1821 }
1822
1823 // In general, a RangeError is thrown when trying to pass a number
1824 // as an argument to a function that does not allow a range that
1825 // includes that number.
1826 return saveStackTrace(new ArgumentError());
1827 }
1828
1829 // Check for the Firefox specific stack overflow signal.
1830 if (JS('bool',
1831 r'typeof InternalError == "function" && # instanceof InternalError',
1832 ex)) {
1833 if (message is String && message == 'too much recursion') {
1834 return new StackOverflowError();
1835 }
1836 }
1837
1838 // Just return the exception. We should not wrap it because in case
1839 // the exception comes from the DOM, it is a JavaScript
1840 // object backed by a native Dart class.
1841 return ex;
1842 }
1843
1844 /**
1845 * Called by generated code to fetch the stack trace from an 551 * Called by generated code to fetch the stack trace from an
1846 * exception. Should never return null. 552 * exception. Should never return null.
1847 */ 553 */
1848 StackTrace getTraceFromException(exception) => new _StackTrace(exception); 554 StackTrace getTraceFromException(exception) => new _StackTrace(exception);
1849 555
1850 class _StackTrace implements StackTrace { 556 class _StackTrace implements StackTrace {
1851 var _exception; 557 var _exception;
1852 String _trace; 558 String _trace;
1853 _StackTrace(this._exception); 559 _StackTrace(this._exception);
1854 560
(...skipping 26 matching lines...) Expand all
1881 int index = 0; 587 int index = 0;
1882 int length = getLength(keyValuePairs); 588 int length = getLength(keyValuePairs);
1883 while (index < length) { 589 while (index < length) {
1884 var key = getIndex(keyValuePairs, index++); 590 var key = getIndex(keyValuePairs, index++);
1885 var value = getIndex(keyValuePairs, index++); 591 var value = getIndex(keyValuePairs, index++);
1886 result[key] = value; 592 result[key] = value;
1887 } 593 }
1888 return result; 594 return result;
1889 } 595 }
1890 596
1891 invokeClosure(Function closure,
1892 var isolate,
1893 int numberOfArguments,
1894 var arg1,
1895 var arg2,
1896 var arg3,
1897 var arg4) {
1898 if (numberOfArguments == 0) {
1899 return JS_CALL_IN_ISOLATE(isolate, () => closure());
1900 } else if (numberOfArguments == 1) {
1901 return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1));
1902 } else if (numberOfArguments == 2) {
1903 return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2));
1904 } else if (numberOfArguments == 3) {
1905 return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2, arg3));
1906 } else if (numberOfArguments == 4) {
1907 return JS_CALL_IN_ISOLATE(isolate, () => closure(arg1, arg2, arg3, arg4));
1908 } else {
1909 throw new Exception(
1910 'Unsupported number of arguments for wrapped closure');
1911 }
1912 }
1913
1914 /** 597 /**
1915 * Called by generated code to convert a Dart closure to a JS 598 * Called by generated code to convert a Dart closure to a JS
1916 * closure when the Dart closure is passed to the DOM. 599 * closure when the Dart closure is passed to the DOM.
1917 */ 600 */
1918 convertDartClosureToJS(closure, int arity) { 601 convertDartClosureToJS(closure, int arity) {
1919 // TODO(vsm): Dart2JS wraps closures to: 602 // TODO(vsm): Dart2JS wraps closures to:
1920 // (a) adjust the calling convention, and 603 // (a) adjust the calling convention, and
1921 // (b) record the source isolate 604 // (b) record the source isolate
1922 // Do we need either? 605 // Do we need either?
1923 // See: https://github.com/dart-lang/dev_compiler/issues/164 606 // See: https://github.com/dart-lang/dev_compiler/issues/164
1924 return closure; 607 return closure;
1925 } 608 }
1926 609
1927 /**
1928 * Super class for Dart closures.
1929 */
1930 abstract class Closure implements Function {
1931 // TODO(ahe): These constants must be in sync with
1932 // reflection_data_parser.dart.
1933 static const FUNCTION_INDEX = 0;
1934 static const NAME_INDEX = 1;
1935 static const CALL_NAME_INDEX = 2;
1936 static const REQUIRED_PARAMETER_INDEX = 3;
1937 static const OPTIONAL_PARAMETER_INDEX = 4;
1938 static const DEFAULT_ARGUMENTS_INDEX = 5;
1939
1940 /**
1941 * Global counter to prevent reusing function code objects.
1942 *
1943 * V8 will share the underlying function code objects when the same string is
1944 * passed to "new Function". Shared function code objects can lead to
1945 * sub-optimal performance due to polymorhism, and can be prevented by
1946 * ensuring the strings are different.
1947 */
1948 static int functionCounter = 0;
1949
1950 Closure();
1951
1952 /**
1953 * Creates a new closure class for use by implicit getters associated with a
1954 * method.
1955 *
1956 * In other words, creates a tear-off closure.
1957 *
1958 * Called from [closureFromTearOff] as well as from reflection when tearing
1959 * of a method via [:getField:].
1960 *
1961 * This method assumes that [functions] was created by the JavaScript function
1962 * `addStubs` in `reflection_data_parser.dart`. That is, a list of JavaScript
1963 * function objects with properties `$stubName` and `$callName`.
1964 *
1965 * Further assumes that [reflectionInfo] is the end of the array created by
1966 * [dart2js.js_emitter.ContainerBuilder.addMemberMethod] starting with
1967 * required parameter count.
1968 *
1969 * Caution: this function may be called when building constants.
1970 * TODO(ahe): Don't call this function when building constants.
1971 */
1972 static fromTearOff(receiver,
1973 List functions,
1974 List reflectionInfo,
1975 bool isStatic,
1976 jsArguments,
1977 String propertyName) {
1978 JS_EFFECT(() {
1979 BoundClosure.receiverOf(JS('BoundClosure', 'void 0'));
1980 BoundClosure.selfOf(JS('BoundClosure', 'void 0'));
1981 });
1982 // TODO(ahe): All the place below using \$ should be rewritten to go
1983 // through the namer.
1984 var function = JS('', '#[#]', functions, 0);
1985 String name = JS('String|Null', '#.\$stubName', function);
1986 String callName = JS('String|Null', '#.\$callName', function);
1987
1988 JS('', '#.\$reflectionInfo = #', function, reflectionInfo);
1989 ReflectionInfo info = new ReflectionInfo(function);
1990
1991 var functionType = info.functionType;
1992
1993 // function tmp() {};
1994 // tmp.prototype = BC.prototype;
1995 // var proto = new tmp;
1996 // for each computed prototype property:
1997 // proto[property] = ...;
1998 // proto._init = BC;
1999 // var dynClosureConstructor =
2000 // new Function('self', 'target', 'receiver', 'name',
2001 // 'this._init(self, target, receiver, name)');
2002 // proto.constructor = dynClosureConstructor;
2003 // dynClosureConstructor.prototype = proto;
2004 // return dynClosureConstructor;
2005
2006 // We need to create a new subclass of either TearOffClosure or
2007 // BoundClosure. For this, we need to create an object whose prototype is
2008 // the prototype is either TearOffClosure.prototype or
2009 // BoundClosure.prototype, respectively in pseudo JavaScript code. The
2010 // simplest way to access the JavaScript construction function of a Dart
2011 // class is to create an instance and access its constructor property. The
2012 // newly created instance could in theory be used directly as the
2013 // prototype, but it might include additional fields that we don't need.
2014 // So we only use the new instance to access the constructor property and
2015 // use Object.create to create the desired prototype.
2016 var prototype = isStatic
2017 ? JS('TearOffClosure', 'Object.create(#.constructor.prototype)',
2018 new TearOffClosure())
2019 : JS('BoundClosure', 'Object.create(#.constructor.prototype)',
2020 new BoundClosure(null, null, null, null));
2021
2022 JS('', '#.\$initialize = #', prototype, JS('', '#.constructor', prototype));
2023 var constructor = isStatic
2024 ? JS('', 'function(){this.\$initialize()}')
2025 : isCsp
2026 ? JS('', 'function(a,b,c,d) {this.\$initialize(a,b,c,d)}')
2027 : JS('',
2028 'new Function("a","b","c","d",'
2029 '"this.\$initialize(a,b,c,d);"+#)',
2030 functionCounter++);
2031
2032 // It is necessary to set the constructor property, otherwise it will be
2033 // "Object".
2034 JS('', '#.constructor = #', prototype, constructor);
2035
2036 JS('', '#.prototype = #', constructor, prototype);
2037
2038 // Create a closure and "monkey" patch it with call stubs.
2039 var trampoline = function;
2040 var isIntercepted = false;
2041 if (!isStatic) {
2042 if (JS('bool', '#.length == 1', jsArguments)) {
2043 // Intercepted call.
2044 isIntercepted = true;
2045 }
2046 trampoline = forwardCallTo(receiver, function, isIntercepted);
2047 JS('', '#.\$reflectionInfo = #', trampoline, reflectionInfo);
2048 } else {
2049 JS('', '#.\$name = #', prototype, propertyName);
2050 }
2051
2052 var signatureFunction;
2053 if (JS('bool', 'typeof # == "number"', functionType)) {
2054 var metadata = JS_EMBEDDED_GLOBAL('', METADATA);
2055 // It is ok, if the access is inlined into the JS. The access is safe in
2056 // and outside the function. In fact we prefer if there is a textual
2057 // inlining.
2058 signatureFunction =
2059 JS('', '(function(s){return function(){return #[s]}})(#)',
2060 metadata,
2061 functionType);
2062 } else if (!isStatic
2063 && JS('bool', 'typeof # == "function"', functionType)) {
2064 var getReceiver = isIntercepted
2065 ? RAW_DART_FUNCTION_REF(BoundClosure.receiverOf)
2066 : RAW_DART_FUNCTION_REF(BoundClosure.selfOf);
2067 signatureFunction = JS(
2068 '',
2069 'function(f,r){'
2070 'return function(){'
2071 'return f.apply({\$receiver:r(this)},arguments)'
2072 '}'
2073 '}(#,#)', functionType, getReceiver);
2074 } else {
2075 throw 'Error in reflectionInfo.';
2076 }
2077
2078 JS('', '#[#] = #', prototype, JS_SIGNATURE_NAME(), signatureFunction);
2079
2080 JS('', '#[#] = #', prototype, callName, trampoline);
2081 for (int i = 1; i < functions.length; i++) {
2082 var stub = functions[i];
2083 var stubCallName = JS('String|Null', '#.\$callName', stub);
2084 if (stubCallName != null) {
2085 JS('', '#[#] = #', prototype, stubCallName,
2086 isStatic ? stub : forwardCallTo(receiver, stub, isIntercepted));
2087 }
2088 }
2089
2090 JS('', '#["call*"] = #', prototype, trampoline);
2091
2092 return constructor;
2093 }
2094
2095 static cspForwardCall(int arity, bool isSuperCall, String stubName,
2096 function) {
2097 var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf);
2098 // Handle intercepted stub-names with the default slow case.
2099 if (isSuperCall) arity = -1;
2100 switch (arity) {
2101 case 0:
2102 return JS(
2103 '',
2104 'function(n,S){'
2105 'return function(){'
2106 'return S(this)[n]()'
2107 '}'
2108 '}(#,#)', stubName, getSelf);
2109 case 1:
2110 return JS(
2111 '',
2112 'function(n,S){'
2113 'return function(a){'
2114 'return S(this)[n](a)'
2115 '}'
2116 '}(#,#)', stubName, getSelf);
2117 case 2:
2118 return JS(
2119 '',
2120 'function(n,S){'
2121 'return function(a,b){'
2122 'return S(this)[n](a,b)'
2123 '}'
2124 '}(#,#)', stubName, getSelf);
2125 case 3:
2126 return JS(
2127 '',
2128 'function(n,S){'
2129 'return function(a,b,c){'
2130 'return S(this)[n](a,b,c)'
2131 '}'
2132 '}(#,#)', stubName, getSelf);
2133 case 4:
2134 return JS(
2135 '',
2136 'function(n,S){'
2137 'return function(a,b,c,d){'
2138 'return S(this)[n](a,b,c,d)'
2139 '}'
2140 '}(#,#)', stubName, getSelf);
2141 case 5:
2142 return JS(
2143 '',
2144 'function(n,S){'
2145 'return function(a,b,c,d,e){'
2146 'return S(this)[n](a,b,c,d,e)'
2147 '}'
2148 '}(#,#)', stubName, getSelf);
2149 default:
2150 return JS(
2151 '',
2152 'function(f,s){'
2153 'return function(){'
2154 'return f.apply(s(this),arguments)'
2155 '}'
2156 '}(#,#)', function, getSelf);
2157 }
2158 }
2159
2160 static bool get isCsp => JS('bool', 'typeof dart_precompiled == "function"');
2161
2162 static forwardCallTo(receiver, function, bool isIntercepted) {
2163 if (isIntercepted) return forwardInterceptedCallTo(receiver, function);
2164 String stubName = JS('String|Null', '#.\$stubName', function);
2165 int arity = JS('int', '#.length', function);
2166 var lookedUpFunction = JS("", "#[#]", receiver, stubName);
2167 // The receiver[stubName] may not be equal to the function if we try to
2168 // forward to a super-method. Especially when we create a bound closure
2169 // of a super-call we need to make sure that we don't forward back to the
2170 // dynamically looked up function.
2171 bool isSuperCall = !identical(function, lookedUpFunction);
2172
2173 if (isCsp || isSuperCall || arity >= 27) {
2174 return cspForwardCall(arity, isSuperCall, stubName, function);
2175 }
2176
2177 if (arity == 0) {
2178 return JS(
2179 '',
2180 '(new Function(#))()',
2181 'return function(){'
2182 'return this.${BoundClosure.selfFieldName()}.$stubName();'
2183 '${functionCounter++}'
2184 '}');
2185 }
2186 assert (1 <= arity && arity < 27);
2187 String arguments = JS(
2188 'String',
2189 '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")',
2190 arity);
2191 return JS(
2192 '',
2193 '(new Function(#))()',
2194 'return function($arguments){'
2195 'return this.${BoundClosure.selfFieldName()}.$stubName($arguments);'
2196 '${functionCounter++}'
2197 '}');
2198 }
2199
2200 static cspForwardInterceptedCall(int arity, bool isSuperCall,
2201 String name, function) {
2202 var getSelf = RAW_DART_FUNCTION_REF(BoundClosure.selfOf);
2203 var getReceiver = RAW_DART_FUNCTION_REF(BoundClosure.receiverOf);
2204 // Handle intercepted stub-names with the default slow case.
2205 if (isSuperCall) arity = -1;
2206 switch (arity) {
2207 case 0:
2208 // Intercepted functions always takes at least one argument (the
2209 // receiver).
2210 throw new RuntimeError('Intercepted function with no arguments.');
2211 case 1:
2212 return JS(
2213 '',
2214 'function(n,s,r){'
2215 'return function(){'
2216 'return s(this)[n](r(this))'
2217 '}'
2218 '}(#,#,#)', name, getSelf, getReceiver);
2219 case 2:
2220 return JS(
2221 '',
2222 'function(n,s,r){'
2223 'return function(a){'
2224 'return s(this)[n](r(this),a)'
2225 '}'
2226 '}(#,#,#)', name, getSelf, getReceiver);
2227 case 3:
2228 return JS(
2229 '',
2230 'function(n,s,r){'
2231 'return function(a,b){'
2232 'return s(this)[n](r(this),a,b)'
2233 '}'
2234 '}(#,#,#)', name, getSelf, getReceiver);
2235 case 4:
2236 return JS(
2237 '',
2238 'function(n,s,r){'
2239 'return function(a,b,c){'
2240 'return s(this)[n](r(this),a,b,c)'
2241 '}'
2242 '}(#,#,#)', name, getSelf, getReceiver);
2243 case 5:
2244 return JS(
2245 '',
2246 'function(n,s,r){'
2247 'return function(a,b,c,d){'
2248 'return s(this)[n](r(this),a,b,c,d)'
2249 '}'
2250 '}(#,#,#)', name, getSelf, getReceiver);
2251 case 6:
2252 return JS(
2253 '',
2254 'function(n,s,r){'
2255 'return function(a,b,c,d,e){'
2256 'return s(this)[n](r(this),a,b,c,d,e)'
2257 '}'
2258 '}(#,#,#)', name, getSelf, getReceiver);
2259 default:
2260 return JS(
2261 '',
2262 'function(f,s,r,a){'
2263 'return function(){'
2264 'a=[r(this)];'
2265 'Array.prototype.push.apply(a,arguments);'
2266 'return f.apply(s(this),a)'
2267 '}'
2268 '}(#,#,#)', function, getSelf, getReceiver);
2269 }
2270 }
2271
2272 static forwardInterceptedCallTo(receiver, function) {
2273 String selfField = BoundClosure.selfFieldName();
2274 String receiverField = BoundClosure.receiverFieldName();
2275 String stubName = JS('String|Null', '#.\$stubName', function);
2276 int arity = JS('int', '#.length', function);
2277 bool isCsp = JS('bool', 'typeof dart_precompiled == "function"');
2278 var lookedUpFunction = JS("", "#[#]", receiver, stubName);
2279 // The receiver[stubName] may not be equal to the function if we try to
2280 // forward to a super-method. Especially when we create a bound closure
2281 // of a super-call we need to make sure that we don't forward back to the
2282 // dynamically looked up function.
2283 bool isSuperCall = !identical(function, lookedUpFunction);
2284
2285 if (isCsp || isSuperCall || arity >= 28) {
2286 return cspForwardInterceptedCall(arity, isSuperCall, stubName,
2287 function);
2288 }
2289 if (arity == 1) {
2290 return JS(
2291 '',
2292 '(new Function(#))()',
2293 'return function(){'
2294 'return this.$selfField.$stubName(this.$receiverField);'
2295 '${functionCounter++}'
2296 '}');
2297 }
2298 assert(1 < arity && arity < 28);
2299 String arguments = JS(
2300 'String',
2301 '"abcdefghijklmnopqrstuvwxyz".split("").splice(0,#).join(",")',
2302 arity - 1);
2303 return JS(
2304 '',
2305 '(new Function(#))()',
2306 'return function($arguments){'
2307 'return this.$selfField.$stubName(this.$receiverField, $arguments);'
2308 '${functionCounter++}'
2309 '}');
2310 }
2311
2312 // The backend adds a special getter of the form
2313 //
2314 // Closure get call => this;
2315 //
2316 // to allow tearing off a closure from itself. We do this magically in the
2317 // backend rather than simply adding it here, as we do not want this getter
2318 // to be visible to resolution and the generation of extra stubs.
2319
2320 String toString() => "Closure";
2321 }
2322
2323 /// Called from implicit method getter (aka tear-off).
2324 closureFromTearOff(receiver,
2325 functions,
2326 reflectionInfo,
2327 isStatic,
2328 jsArguments,
2329 name) {
2330 return Closure.fromTearOff(
2331 receiver,
2332 JSArray.markFixedList(functions),
2333 JSArray.markFixedList(reflectionInfo),
2334 JS('bool', '!!#', isStatic),
2335 jsArguments,
2336 JS('String', '#', name));
2337 }
2338
2339 /// Represents an implicit closure of a function.
2340 class TearOffClosure extends Closure {
2341 }
2342
2343 /// Represents a 'tear-off' closure, that is an instance method bound
2344 /// to a specific receiver (instance).
2345 class BoundClosure extends TearOffClosure {
2346 /// The receiver or interceptor.
2347 // TODO(ahe): This could just be the interceptor, we always know if
2348 // we need the interceptor when generating the call method.
2349 final _self;
2350
2351 /// The method.
2352 final _target;
2353
2354 /// The receiver. Null if [_self] is not an interceptor.
2355 final _receiver;
2356
2357 /// The name of the function. Only used by the mirror system.
2358 final String _name;
2359
2360 BoundClosure(this._self, this._target, this._receiver, this._name);
2361
2362 bool operator==(other) {
2363 if (identical(this, other)) return true;
2364 if (other is! BoundClosure) return false;
2365 return JS('bool', '# === # && # === # && # === #',
2366 _self, other._self,
2367 _target, other._target,
2368 _receiver, other._receiver);
2369 }
2370
2371 int get hashCode {
2372 int receiverHashCode;
2373 if (_receiver == null) {
2374 // A bound closure on a regular Dart object, just use the
2375 // identity hash code.
2376 receiverHashCode = Primitives.objectHashCode(_self);
2377 } else if (JS('String', 'typeof #', _receiver) != 'object') {
2378 // A bound closure on a primitive JavaScript type. We
2379 // use the hashCode method we define for those primitive types.
2380 receiverHashCode = _receiver.hashCode;
2381 } else {
2382 // A bound closure on an intercepted native class, just use the
2383 // identity hash code.
2384 receiverHashCode = Primitives.objectHashCode(_receiver);
2385 }
2386 return receiverHashCode ^ Primitives.objectHashCode(_target);
2387 }
2388
2389 @NoInline()
2390 static selfOf(BoundClosure closure) => closure._self;
2391
2392 static targetOf(BoundClosure closure) => closure._target;
2393
2394 @NoInline()
2395 static receiverOf(BoundClosure closure) => closure._receiver;
2396
2397 static nameOf(BoundClosure closure) => closure._name;
2398
2399 static String selfFieldNameCache;
2400
2401 static String selfFieldName() {
2402 if (selfFieldNameCache == null) {
2403 selfFieldNameCache = computeFieldNamed('self');
2404 }
2405 return selfFieldNameCache;
2406 }
2407
2408 static String receiverFieldNameCache;
2409
2410 static String receiverFieldName() {
2411 if (receiverFieldNameCache == null) {
2412 receiverFieldNameCache = computeFieldNamed('receiver');
2413 }
2414 return receiverFieldNameCache;
2415 }
2416
2417 @NoInline() @NoSideEffects()
2418 static String computeFieldNamed(String fieldName) {
2419 var template = new BoundClosure('self', 'target', 'receiver', 'name');
2420 var names = JSArray.markFixedList(
2421 JS('', 'Object.getOwnPropertyNames(#)', template));
2422 for (int i = 0; i < names.length; i++) {
2423 var name = names[i];
2424 if (JS('bool', '#[#] === #', template, name, fieldName)) {
2425 return JS('String', '#', name);
2426 }
2427 }
2428 }
2429 }
2430
2431 bool jsHasOwnProperty(var jsObject, String property) { 610 bool jsHasOwnProperty(var jsObject, String property) {
2432 return JS('bool', r'#.hasOwnProperty(#)', jsObject, property); 611 return JS('bool', r'#.hasOwnProperty(#)', jsObject, property);
2433 } 612 }
2434 613
2435 jsPropertyAccess(var jsObject, String property) { 614 jsPropertyAccess(var jsObject, String property) {
2436 return JS('var', r'#[#]', jsObject, property); 615 return JS('var', r'#[#]', jsObject, property);
2437 } 616 }
2438 617
2439 /** 618 /**
2440 * Called at the end of unaborted switch cases to get the singleton 619 * Called at the end of unaborted switch cases to get the singleton
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
2520 * @JSName('title') 699 * @JSName('title')
2521 * String $dom_title; 700 * String $dom_title;
2522 * } 701 * }
2523 */ 702 */
2524 class JSName { 703 class JSName {
2525 final String name; 704 final String name;
2526 const JSName(this.name); 705 const JSName(this.name);
2527 } 706 }
2528 707
2529 /** 708 /**
2530 * The following methods are called by the runtime to implement
2531 * checked mode and casts. We specialize each primitive type (eg int, bool), and
2532 * use the compiler's convention to do is-checks on regular objects.
2533 */
2534 boolConversionCheck(value) {
2535 if (value is bool) return value;
2536 // One of the following checks will always fail.
2537 boolTypeCheck(value);
2538 assert(value != null);
2539 return false;
2540 }
2541
2542 stringTypeCheck(value) {
2543 if (value == null) return value;
2544 if (value is String) return value;
2545 throw new TypeErrorImplementation(value, 'String');
2546 }
2547
2548 stringTypeCast(value) {
2549 if (value is String || value == null) return value;
2550 // TODO(lrn): When reified types are available, pass value.class and String.
2551 throw new CastErrorImplementation(
2552 Primitives.objectTypeName(value), 'String');
2553 }
2554
2555 doubleTypeCheck(value) {
2556 if (value == null) return value;
2557 if (value is double) return value;
2558 throw new TypeErrorImplementation(value, 'double');
2559 }
2560
2561 doubleTypeCast(value) {
2562 if (value is double || value == null) return value;
2563 throw new CastErrorImplementation(
2564 Primitives.objectTypeName(value), 'double');
2565 }
2566
2567 numTypeCheck(value) {
2568 if (value == null) return value;
2569 if (value is num) return value;
2570 throw new TypeErrorImplementation(value, 'num');
2571 }
2572
2573 numTypeCast(value) {
2574 if (value is num || value == null) return value;
2575 throw new CastErrorImplementation(
2576 Primitives.objectTypeName(value), 'num');
2577 }
2578
2579 boolTypeCheck(value) {
2580 if (value == null) return value;
2581 if (value is bool) return value;
2582 throw new TypeErrorImplementation(value, 'bool');
2583 }
2584
2585 boolTypeCast(value) {
2586 if (value is bool || value == null) return value;
2587 throw new CastErrorImplementation(
2588 Primitives.objectTypeName(value), 'bool');
2589 }
2590
2591 intTypeCheck(value) {
2592 if (value == null) return value;
2593 if (value is int) return value;
2594 throw new TypeErrorImplementation(value, 'int');
2595 }
2596
2597 intTypeCast(value) {
2598 if (value is int || value == null) return value;
2599 throw new CastErrorImplementation(
2600 Primitives.objectTypeName(value), 'int');
2601 }
2602
2603 void propertyTypeError(value, property) {
2604 // Cuts the property name to the class name.
2605 String name = property.substring(3, property.length);
2606 throw new TypeErrorImplementation(value, name);
2607 }
2608
2609 void propertyTypeCastError(value, property) {
2610 // Cuts the property name to the class name.
2611 String actualType = Primitives.objectTypeName(value);
2612 String expectedType = property.substring(3, property.length);
2613 throw new CastErrorImplementation(actualType, expectedType);
2614 }
2615
2616 /**
2617 * For types that are not supertypes of native (eg DOM) types,
2618 * we emit a simple property check to check that an object implements
2619 * that type.
2620 */
2621 propertyTypeCheck(value, property) {
2622 if (value == null) return value;
2623 if (JS('bool', '!!#[#]', value, property)) return value;
2624 propertyTypeError(value, property);
2625 }
2626
2627 /**
2628 * For types that are not supertypes of native (eg DOM) types,
2629 * we emit a simple property check to check that an object implements
2630 * that type.
2631 */
2632 propertyTypeCast(value, property) {
2633 if (value == null || JS('bool', '!!#[#]', value, property)) return value;
2634 propertyTypeCastError(value, property);
2635 }
2636
2637 /**
2638 * For types that are supertypes of native (eg DOM) types, we use the
2639 * interceptor for the class because we cannot add a JS property to the
2640 * prototype at load time.
2641 */
2642 interceptedTypeCheck(value, property) {
2643 if (value == null) return value;
2644 if ((identical(JS('String', 'typeof #', value), 'object'))
2645 && JS('bool', '#[#]', getInterceptor(value), property)) {
2646 return value;
2647 }
2648 propertyTypeError(value, property);
2649 }
2650
2651 /**
2652 * For types that are supertypes of native (eg DOM) types, we use the
2653 * interceptor for the class because we cannot add a JS property to the
2654 * prototype at load time.
2655 */
2656 interceptedTypeCast(value, property) {
2657 if (value == null
2658 || ((JS('bool', 'typeof # === "object"', value))
2659 && JS('bool', '#[#]', getInterceptor(value), property))) {
2660 return value;
2661 }
2662 propertyTypeCastError(value, property);
2663 }
2664
2665 /**
2666 * Specialization of the type check for num and String and their
2667 * supertype since [value] can be a JS primitive.
2668 */
2669 numberOrStringSuperTypeCheck(value, property) {
2670 if (value == null) return value;
2671 if (value is String) return value;
2672 if (value is num) return value;
2673 if (JS('bool', '!!#[#]', value, property)) return value;
2674 propertyTypeError(value, property);
2675 }
2676
2677 numberOrStringSuperTypeCast(value, property) {
2678 if (value is String) return value;
2679 if (value is num) return value;
2680 return propertyTypeCast(value, property);
2681 }
2682
2683 numberOrStringSuperNativeTypeCheck(value, property) {
2684 if (value == null) return value;
2685 if (value is String) return value;
2686 if (value is num) return value;
2687 if (JS('bool', '#[#]', getInterceptor(value), property)) return value;
2688 propertyTypeError(value, property);
2689 }
2690
2691 numberOrStringSuperNativeTypeCast(value, property) {
2692 if (value == null) return value;
2693 if (value is String) return value;
2694 if (value is num) return value;
2695 if (JS('bool', '#[#]', getInterceptor(value), property)) return value;
2696 propertyTypeCastError(value, property);
2697 }
2698
2699 /**
2700 * Specialization of the type check for String and its supertype
2701 * since [value] can be a JS primitive.
2702 */
2703 stringSuperTypeCheck(value, property) {
2704 if (value == null) return value;
2705 if (value is String) return value;
2706 if (JS('bool', '!!#[#]', value, property)) return value;
2707 propertyTypeError(value, property);
2708 }
2709
2710 stringSuperTypeCast(value, property) {
2711 if (value is String) return value;
2712 return propertyTypeCast(value, property);
2713 }
2714
2715 stringSuperNativeTypeCheck(value, property) {
2716 if (value == null) return value;
2717 if (value is String) return value;
2718 if (JS('bool', '#[#]', getInterceptor(value), property)) return value;
2719 propertyTypeError(value, property);
2720 }
2721
2722 stringSuperNativeTypeCast(value, property) {
2723 if (value is String || value == null) return value;
2724 if (JS('bool', '#[#]', getInterceptor(value), property)) return value;
2725 propertyTypeCastError(value, property);
2726 }
2727
2728 /**
2729 * Specialization of the type check for List and its supertypes,
2730 * since [value] can be a JS array.
2731 */
2732 listTypeCheck(value) {
2733 if (value == null) return value;
2734 if (value is List) return value;
2735 throw new TypeErrorImplementation(value, 'List');
2736 }
2737
2738 listTypeCast(value) {
2739 if (value is List || value == null) return value;
2740 throw new CastErrorImplementation(
2741 Primitives.objectTypeName(value), 'List');
2742 }
2743
2744 listSuperTypeCheck(value, property) {
2745 if (value == null) return value;
2746 if (value is List) return value;
2747 if (JS('bool', '!!#[#]', value, property)) return value;
2748 propertyTypeError(value, property);
2749 }
2750
2751 listSuperTypeCast(value, property) {
2752 if (value is List) return value;
2753 return propertyTypeCast(value, property);
2754 }
2755
2756 listSuperNativeTypeCheck(value, property) {
2757 if (value == null) return value;
2758 if (value is List) return value;
2759 if (JS('bool', '#[#]', getInterceptor(value), property)) return value;
2760 propertyTypeError(value, property);
2761 }
2762
2763 listSuperNativeTypeCast(value, property) {
2764 if (value is List || value == null) return value;
2765 if (JS('bool', '#[#]', getInterceptor(value), property)) return value;
2766 propertyTypeCastError(value, property);
2767 }
2768
2769 voidTypeCheck(value) {
2770 if (value == null) return value;
2771 throw new TypeErrorImplementation(value, 'void');
2772 }
2773
2774 checkMalformedType(value, message) {
2775 if (value == null) return value;
2776 throw new TypeErrorImplementation.fromMessage(message);
2777 }
2778
2779 @NoInline()
2780 void checkDeferredIsLoaded(String loadId, String uri) {
2781 if (!_loadedLibraries.contains(loadId)) {
2782 throw new DeferredNotLoadedError(uri);
2783 }
2784 }
2785
2786 /**
2787 * Special interface recognized by the compiler and implemented by DOM 709 * Special interface recognized by the compiler and implemented by DOM
2788 * objects that support integer indexing. This interface is not 710 * objects that support integer indexing. This interface is not
2789 * visible to anyone, and is only injected into special libraries. 711 * visible to anyone, and is only injected into special libraries.
2790 */ 712 */
2791 abstract class JavaScriptIndexingBehavior extends JSMutableIndexable { 713 abstract class JavaScriptIndexingBehavior extends JSMutableIndexable {
2792 } 714 }
2793 715
2794 // TODO(lrn): These exceptions should be implemented in core. 716 // TODO(lrn): These exceptions should be implemented in core.
2795 // When they are, remove the 'Implementation' here. 717 // When they are, remove the 'Implementation' here.
2796 718
(...skipping 27 matching lines...) Expand all
2824 746
2825 String toString() => message; 747 String toString() => message;
2826 } 748 }
2827 749
2828 class FallThroughErrorImplementation extends FallThroughError { 750 class FallThroughErrorImplementation extends FallThroughError {
2829 FallThroughErrorImplementation(); 751 FallThroughErrorImplementation();
2830 String toString() => "Switch case fall-through."; 752 String toString() => "Switch case fall-through.";
2831 } 753 }
2832 754
2833 /** 755 /**
2834 * Helper function for implementing asserts. The compiler treats this specially.
2835 */
2836 void assertHelper(condition) {
2837 // Do a bool check first because it is common and faster than 'is Function'.
2838 if (condition is !bool) {
2839 if (condition is Function) condition = condition();
2840 if (condition is !bool) {
2841 throw new TypeErrorImplementation(condition, 'bool');
2842 }
2843 }
2844 // Compare to true to avoid boolean conversion check in checked
2845 // mode.
2846 if (true != condition) throw new AssertionError();
2847 }
2848
2849 /**
2850 * Called by generated code when a method that must be statically
2851 * resolved cannot be found.
2852 */
2853 void throwNoSuchMethod(obj, name, arguments, expectedArgumentNames) {
2854 Symbol memberName = new _symbol_dev.Symbol.unvalidated(name);
2855 throw new NoSuchMethodError(obj, memberName, arguments,
2856 new Map<Symbol, dynamic>(),
2857 expectedArgumentNames);
2858 }
2859
2860 /**
2861 * Called by generated code when a static field's initializer references the
2862 * field that is currently being initialized.
2863 */
2864 void throwCyclicInit(String staticName) {
2865 throw new CyclicInitializationError(
2866 "Cyclic initialization for static $staticName");
2867 }
2868
2869 /**
2870 * Error thrown when a runtime error occurs. 756 * Error thrown when a runtime error occurs.
2871 */ 757 */
2872 class RuntimeError extends Error { 758 class RuntimeError extends Error {
2873 final message; 759 final message;
2874 RuntimeError(this.message); 760 RuntimeError(this.message);
2875 String toString() => "RuntimeError: $message"; 761 String toString() => "RuntimeError: $message";
2876 } 762 }
2877 763
2878 class DeferredNotLoadedError extends Error implements NoSuchMethodError {
2879 String libraryName;
2880
2881 DeferredNotLoadedError(this.libraryName);
2882
2883 String toString() {
2884 return "Deferred library $libraryName was not loaded.";
2885 }
2886 }
2887
2888 abstract class RuntimeType {
2889 const RuntimeType();
2890
2891 toRti();
2892 }
2893
2894 class RuntimeFunctionType extends RuntimeType {
2895 final RuntimeType returnType;
2896 final List<RuntimeType> parameterTypes;
2897 final List<RuntimeType> optionalParameterTypes;
2898 final namedParameters;
2899
2900 static var /* bool */ inAssert = false;
2901
2902 RuntimeFunctionType(this.returnType,
2903 this.parameterTypes,
2904 this.optionalParameterTypes,
2905 this.namedParameters);
2906
2907 bool get isVoid => returnType is VoidRuntimeType;
2908
2909 /// Called from generated code. [expression] is a Dart object and this method
2910 /// returns true if [this] is a supertype of [expression].
2911 @NoInline() @NoSideEffects()
2912 bool _isTest(expression) {
2913 var functionTypeObject = _extractFunctionTypeObjectFrom(expression);
2914 return functionTypeObject == null
2915 ? false
2916 : isFunctionSubtype(functionTypeObject, toRti());
2917 }
2918
2919 @NoInline() @NoSideEffects()
2920 _asCheck(expression) {
2921 // Type inferrer doesn't think this is called with dynamic arguments.
2922 return _check(JS('', '#', expression), true);
2923 }
2924
2925 @NoInline() @NoSideEffects()
2926 _assertCheck(expression) {
2927 if (inAssert) return null;
2928 inAssert = true; // Don't try to check this library itself.
2929 try {
2930 // Type inferrer don't think this is called with dynamic arguments.
2931 return _check(JS('', '#', expression), false);
2932 } finally {
2933 inAssert = false;
2934 }
2935 }
2936
2937 _check(expression, bool isCast) {
2938 if (expression == null) return null;
2939 if (_isTest(expression)) return expression;
2940
2941 var self = new FunctionTypeInfoDecoderRing(toRti()).toString();
2942 if (isCast) {
2943 var functionTypeObject = _extractFunctionTypeObjectFrom(expression);
2944 var pretty;
2945 if (functionTypeObject != null) {
2946 pretty = new FunctionTypeInfoDecoderRing(functionTypeObject).toString();
2947 } else {
2948 pretty = Primitives.objectTypeName(expression);
2949 }
2950 throw new CastErrorImplementation(pretty, self);
2951 } else {
2952 // TODO(ahe): Pass "pretty" function-type to TypeErrorImplementation?
2953 throw new TypeErrorImplementation(expression, self);
2954 }
2955 }
2956
2957 _extractFunctionTypeObjectFrom(o) {
2958 var interceptor = getInterceptor(o);
2959 return JS('bool', '# in #', JS_SIGNATURE_NAME(), interceptor)
2960 ? JS('', '#[#]()', interceptor, JS_SIGNATURE_NAME())
2961 : null;
2962 }
2963
2964 toRti() {
2965 var result = JS('=Object', '{ #: "dynafunc" }', JS_FUNCTION_TYPE_TAG());
2966 if (isVoid) {
2967 JS('', '#[#] = true', result, JS_FUNCTION_TYPE_VOID_RETURN_TAG());
2968 } else {
2969 if (returnType is! DynamicRuntimeType) {
2970 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_RETURN_TYPE_TAG(),
2971 returnType.toRti());
2972 }
2973 }
2974 if (parameterTypes != null && !parameterTypes.isEmpty) {
2975 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG(),
2976 listToRti(parameterTypes));
2977 }
2978
2979 if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) {
2980 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG(),
2981 listToRti(optionalParameterTypes));
2982 }
2983
2984 if (namedParameters != null) {
2985 var namedRti = JS('=Object', 'Object.create(null)');
2986 var keys = extractKeys(namedParameters);
2987 for (var i = 0; i < keys.length; i++) {
2988 var name = keys[i];
2989 var rti = JS('', '#[#]', namedParameters, name).toRti();
2990 JS('', '#[#] = #', namedRti, name, rti);
2991 }
2992 JS('', '#[#] = #', result, JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG(),
2993 namedRti);
2994 }
2995
2996 return result;
2997 }
2998
2999 static listToRti(list) {
3000 list = JS('JSFixedArray', '#', list);
3001 var result = JS('JSExtendableArray', '[]');
3002 for (var i = 0; i < list.length; i++) {
3003 JS('', '#.push(#)', result, list[i].toRti());
3004 }
3005 return result;
3006 }
3007
3008 String toString() {
3009 String result = '(';
3010 bool needsComma = false;
3011 if (parameterTypes != null) {
3012 for (var i = 0; i < parameterTypes.length; i++) {
3013 RuntimeType type = parameterTypes[i];
3014 if (needsComma) result += ', ';
3015 result += '$type';
3016 needsComma = true;
3017 }
3018 }
3019 if (optionalParameterTypes != null && !optionalParameterTypes.isEmpty) {
3020 if (needsComma) result += ', ';
3021 needsComma = false;
3022 result += '[';
3023 for (var i = 0; i < optionalParameterTypes.length; i++) {
3024 RuntimeType type = optionalParameterTypes[i];
3025 if (needsComma) result += ', ';
3026 result += '$type';
3027 needsComma = true;
3028 }
3029 result += ']';
3030 } else if (namedParameters != null) {
3031 if (needsComma) result += ', ';
3032 needsComma = false;
3033 result += '{';
3034 var keys = extractKeys(namedParameters);
3035 for (var i = 0; i < keys.length; i++) {
3036 var name = keys[i];
3037 if (needsComma) result += ', ';
3038 var rti = JS('', '#[#]', namedParameters, name).toRti();
3039 result += '$rti ${JS("String", "#", name)}';
3040 needsComma = true;
3041 }
3042 result += '}';
3043 }
3044
3045 result += ') -> $returnType';
3046 return result;
3047 }
3048 }
3049
3050 RuntimeFunctionType buildFunctionType(returnType,
3051 parameterTypes,
3052 optionalParameterTypes) {
3053 return new RuntimeFunctionType(
3054 returnType,
3055 parameterTypes,
3056 optionalParameterTypes,
3057 null);
3058 }
3059
3060 RuntimeFunctionType buildNamedFunctionType(returnType,
3061 parameterTypes,
3062 namedParameters) {
3063 return new RuntimeFunctionType(
3064 returnType,
3065 parameterTypes,
3066 null,
3067 namedParameters);
3068 }
3069
3070 RuntimeType buildInterfaceType(rti, typeArguments) {
3071 String name = JS('String|Null', r'#.name', rti);
3072 if (typeArguments == null || typeArguments.isEmpty) {
3073 return new RuntimeTypePlain(name);
3074 }
3075 return new RuntimeTypeGeneric(name, typeArguments, null);
3076 }
3077
3078 class DynamicRuntimeType extends RuntimeType {
3079 const DynamicRuntimeType();
3080
3081 String toString() => 'dynamic';
3082
3083 toRti() => null;
3084 }
3085
3086 RuntimeType getDynamicRuntimeType() => const DynamicRuntimeType();
3087
3088 class VoidRuntimeType extends RuntimeType {
3089 const VoidRuntimeType();
3090
3091 String toString() => 'void';
3092
3093 toRti() => throw 'internal error';
3094 }
3095
3096 RuntimeType getVoidRuntimeType() => const VoidRuntimeType();
3097
3098 /**
3099 * Meta helper for function type tests.
3100 *
3101 * A "meta helper" is a helper function that is never called but simulates how
3102 * generated code behaves as far as resolution and type inference is concerned.
3103 */
3104 functionTypeTestMetaHelper() {
3105 var dyn = JS('', 'x');
3106 var dyn2 = JS('', 'x');
3107 List fixedListOrNull = JS('JSFixedArray|Null', 'x');
3108 List fixedListOrNull2 = JS('JSFixedArray|Null', 'x');
3109 List fixedList = JS('JSFixedArray', 'x');
3110 // TODO(ahe): Can we use [UnknownJavaScriptObject] below?
3111 var /* UnknownJavaScriptObject */ jsObject = JS('=Object', 'x');
3112
3113 buildFunctionType(dyn, fixedListOrNull, fixedListOrNull2);
3114 buildNamedFunctionType(dyn, fixedList, jsObject);
3115 buildInterfaceType(dyn, fixedListOrNull);
3116 getDynamicRuntimeType();
3117 getVoidRuntimeType();
3118 convertRtiToRuntimeType(dyn);
3119 dyn._isTest(dyn2);
3120 dyn._asCheck(dyn2);
3121 dyn._assertCheck(dyn2);
3122 }
3123
3124 RuntimeType convertRtiToRuntimeType(rti) {
3125 if (rti == null) {
3126 return getDynamicRuntimeType();
3127 } else if (JS('bool', 'typeof # == "function"', rti)) {
3128 return new RuntimeTypePlain(JS('String', r'rti.name'));
3129 } else if (JS('bool', '#.constructor == Array', rti)) {
3130 List list = JS('JSFixedArray', '#', rti);
3131 String name = JS('String', r'#.name', list[0]);
3132 List arguments = [];
3133 for (int i = 1; i < list.length; i++) {
3134 arguments.add(convertRtiToRuntimeType(list[i]));
3135 }
3136 return new RuntimeTypeGeneric(name, arguments, rti);
3137 } else if (JS('bool', '"func" in #', rti)) {
3138 return new FunctionTypeInfoDecoderRing(rti).toRuntimeType();
3139 } else {
3140 throw new RuntimeError(
3141 "Cannot convert "
3142 "'${JS('String', 'JSON.stringify(#)', rti)}' to RuntimeType.");
3143 }
3144 }
3145
3146 class RuntimeTypePlain extends RuntimeType {
3147 final String name;
3148
3149 RuntimeTypePlain(this.name);
3150
3151 toRti() {
3152 var allClasses = JS_EMBEDDED_GLOBAL('', ALL_CLASSES);
3153 var rti = JS('', '#[#]', allClasses, name);
3154 if (rti == null) throw "no type for '$name'";
3155 return rti;
3156 }
3157
3158 String toString() => name;
3159 }
3160
3161 class RuntimeTypeGeneric extends RuntimeType {
3162 final String name;
3163 final List<RuntimeType> arguments;
3164 var rti;
3165
3166 RuntimeTypeGeneric(this.name, this.arguments, this.rti);
3167
3168 toRti() {
3169 if (rti != null) return rti;
3170 var allClasses = JS_EMBEDDED_GLOBAL('', ALL_CLASSES);
3171 var result = JS('JSExtendableArray', '[#[#]]', allClasses, name);
3172 if (result[0] == null) {
3173 throw "no type for '$name<...>'";
3174 }
3175 for (RuntimeType argument in arguments) {
3176 JS('', '#.push(#)', result, argument.toRti());
3177 }
3178 return rti = result;
3179 }
3180
3181 String toString() => '$name<${arguments.join(", ")}>';
3182 }
3183
3184 class FunctionTypeInfoDecoderRing {
3185 final _typeData;
3186 String _cachedToString;
3187
3188 FunctionTypeInfoDecoderRing(this._typeData);
3189
3190 bool get _hasReturnType => JS('bool', '"ret" in #', _typeData);
3191 get _returnType => JS('', '#.ret', _typeData);
3192
3193 bool get _isVoid => JS('bool', '!!#.void', _typeData);
3194
3195 bool get _hasArguments => JS('bool', '"args" in #', _typeData);
3196 List get _arguments => JS('JSExtendableArray', '#.args', _typeData);
3197
3198 bool get _hasOptionalArguments => JS('bool', '"opt" in #', _typeData);
3199 List get _optionalArguments => JS('JSExtendableArray', '#.opt', _typeData);
3200
3201 bool get _hasNamedArguments => JS('bool', '"named" in #', _typeData);
3202 get _namedArguments => JS('=Object', '#.named', _typeData);
3203
3204 RuntimeType toRuntimeType() {
3205 // TODO(ahe): Implement this (and update return type).
3206 return const DynamicRuntimeType();
3207 }
3208
3209 String _convert(type) {
3210 String result = runtimeTypeToString(type);
3211 if (result != null) return result;
3212 if (JS('bool', '"func" in #', type)) {
3213 return new FunctionTypeInfoDecoderRing(type).toString();
3214 } else {
3215 throw 'bad type';
3216 }
3217 }
3218
3219 String toString() {
3220 if (_cachedToString != null) return _cachedToString;
3221 var s = "(";
3222 var sep = '';
3223 if (_hasArguments) {
3224 for (var argument in _arguments) {
3225 s += sep;
3226 s += _convert(argument);
3227 sep = ', ';
3228 }
3229 }
3230 if (_hasOptionalArguments) {
3231 s += '$sep[';
3232 sep = '';
3233 for (var argument in _optionalArguments) {
3234 s += sep;
3235 s += _convert(argument);
3236 sep = ', ';
3237 }
3238 s += ']';
3239 }
3240 if (_hasNamedArguments) {
3241 s += '$sep{';
3242 sep = '';
3243 for (var name in extractKeys(_namedArguments)) {
3244 s += sep;
3245 s += '$name: ';
3246 s += _convert(JS('', '#[#]', _namedArguments, name));
3247 sep = ', ';
3248 }
3249 s += '}';
3250 }
3251 s += ') -> ';
3252 if (_isVoid) {
3253 s += 'void';
3254 } else if (_hasReturnType) {
3255 s += _convert(_returnType);
3256 } else {
3257 s += 'dynamic';
3258 }
3259 return _cachedToString = "$s";
3260 }
3261 }
3262
3263 // TODO(ahe): Remove this class and call noSuchMethod instead.
3264 class UnimplementedNoSuchMethodError extends Error
3265 implements NoSuchMethodError {
3266 final String _message;
3267
3268 UnimplementedNoSuchMethodError(this._message);
3269
3270 String toString() => "Unsupported operation: $_message";
3271 }
3272
3273 /** 764 /**
3274 * Creates a random number with 64 bits of randomness. 765 * Creates a random number with 64 bits of randomness.
3275 * 766 *
3276 * This will be truncated to the 53 bits available in a double. 767 * This will be truncated to the 53 bits available in a double.
3277 */ 768 */
3278 int random64() { 769 int random64() {
3279 // TODO(lrn): Use a secure random source. 770 // TODO(lrn): Use a secure random source.
3280 int int32a = JS("int", "(Math.random() * 0x100000000) >>> 0"); 771 int int32a = JS("int", "(Math.random() * 0x100000000) >>> 0");
3281 int int32b = JS("int", "(Math.random() * 0x100000000) >>> 0"); 772 int int32b = JS("int", "(Math.random() * 0x100000000) >>> 0");
3282 return int32a + int32b * 0x100000000; 773 return int32a + int32b * 0x100000000;
3283 } 774 }
3284 775
3285 String jsonEncodeNative(String string) { 776 String jsonEncodeNative(String string) {
3286 return JS("String", "JSON.stringify(#)", string); 777 return JS("String", "JSON.stringify(#)", string);
3287 } 778 }
3288
3289 /**
3290 * Returns a property name for placing data on JavaScript objects shared between
3291 * DOM isolates. This happens when multiple programs are loaded in the same
3292 * JavaScript context (i.e. page). The name is based on [name] but with an
3293 * additional part that is unique for each isolate.
3294 *
3295 * The form of the name is '___dart_$name_$id'.
3296 */
3297 String getIsolateAffinityTag(String name) {
3298 var isolateTagGetter =
3299 JS_EMBEDDED_GLOBAL('', GET_ISOLATE_TAG);
3300 return JS('String', '#(#)', isolateTagGetter, name);
3301 }
3302
3303 typedef Future<Null> LoadLibraryFunctionType();
3304
3305 LoadLibraryFunctionType _loadLibraryWrapper(String loadId) {
3306 return () => loadDeferredLibrary(loadId);
3307 }
3308
3309 final Map<String, Future<Null>> _loadingLibraries = <String, Future<Null>>{};
3310 final Set<String> _loadedLibraries = new Set<String>();
3311
3312 typedef void DeferredLoadCallback();
3313
3314 // Function that will be called every time a new deferred import is loaded.
3315 DeferredLoadCallback deferredLoadHook;
3316
3317 Future<Null> loadDeferredLibrary(String loadId) {
3318 // For each loadId there is a list of hunk-uris to load, and a corresponding
3319 // list of hashes. These are stored in the app-global scope.
3320 var urisMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_URIS);
3321 List<String> uris = JS('JSExtendableArray|Null', '#[#]', urisMap, loadId);
3322 var hashesMap = JS_EMBEDDED_GLOBAL('', DEFERRED_LIBRARY_HASHES);
3323 List<String> hashes = JS('JSExtendableArray|Null', '#[#]', hashesMap, loadId);
3324 if (uris == null) return new Future.value(null);
3325 // The indices into `uris` and `hashes` that we want to load.
3326 List<int> indices = new List.generate(uris.length, (i) => i);
3327 var isHunkLoaded = JS_EMBEDDED_GLOBAL('', IS_HUNK_LOADED);
3328 var isHunkInitialized = JS_EMBEDDED_GLOBAL('', IS_HUNK_INITIALIZED);
3329 // Filter away indices for hunks that have already been loaded.
3330 List<int> indicesToLoad = indices
3331 .where((int i) => !JS('bool','#(#)', isHunkLoaded, hashes[i]))
3332 .toList();
3333 return Future.wait(indicesToLoad
3334 .map((int i) => _loadHunk(uris[i]))).then((_) {
3335 // Now all hunks have been loaded, we run the needed initializers.
3336 List<int> indicesToInitialize = indices
3337 .where((int i) => !JS('bool','#(#)', isHunkInitialized, hashes[i]))
3338 .toList(); // Load the needed hunks.
3339 for (int i in indicesToInitialize) {
3340 var initializer = JS_EMBEDDED_GLOBAL('', INITIALIZE_LOADED_HUNK);
3341 JS('void', '#(#)', initializer, hashes[i]);
3342 }
3343 bool updated = _loadedLibraries.add(loadId);
3344 if (updated && deferredLoadHook != null) {
3345 deferredLoadHook();
3346 }
3347 });
3348 }
3349
3350 Future<Null> _loadHunk(String hunkName) {
3351 // TODO(ahe): Validate libraryName. Kasper points out that you want
3352 // to be able to experiment with the effect of toggling @DeferLoad,
3353 // so perhaps we should silently ignore "bad" library names.
3354 Future<Null> future = _loadingLibraries[hunkName];
3355 if (future != null) {
3356 return future.then((_) => null);
3357 }
3358
3359 String uri = IsolateNatives.thisScript;
3360
3361 int index = uri.lastIndexOf('/');
3362 uri = '${uri.substring(0, index + 1)}$hunkName';
3363
3364 if (Primitives.isJsshell || Primitives.isD8) {
3365 // TODO(ahe): Move this code to a JavaScript command helper script that is
3366 // not included in generated output.
3367 return _loadingLibraries[hunkName] = new Future<Null>(() {
3368 try {
3369 // Create a new function to avoid getting access to current function
3370 // context.
3371 JS('void', '(new Function(#))()', 'load("$uri")');
3372 } catch (error, stackTrace) {
3373 throw new DeferredLoadException("Loading $uri failed.");
3374 }
3375 return null;
3376 });
3377 } else if (isWorker()) {
3378 // We are in a web worker. Load the code with an XMLHttpRequest.
3379 return _loadingLibraries[hunkName] = new Future<Null>(() {
3380 Completer completer = new Completer<Null>();
3381 enterJsAsync();
3382 Future<Null> leavingFuture = completer.future.whenComplete(() {
3383 leaveJsAsync();
3384 });
3385
3386 int index = uri.lastIndexOf('/');
3387 uri = '${uri.substring(0, index + 1)}$hunkName';
3388 var xhr = JS('dynamic', 'new XMLHttpRequest()');
3389 JS('void', '#.open("GET", #)', xhr, uri);
3390 JS('void', '#.addEventListener("load", #, false)',
3391 xhr, convertDartClosureToJS((event) {
3392 if (JS('int', '#.status', xhr) != 200) {
3393 completer.completeError(
3394 new DeferredLoadException("Loading $uri failed."));
3395 return;
3396 }
3397 String code = JS('String', '#.responseText', xhr);
3398 try {
3399 // Create a new function to avoid getting access to current function
3400 // context.
3401 JS('void', '(new Function(#))()', code);
3402 } catch (error, stackTrace) {
3403 completer.completeError(
3404 new DeferredLoadException("Evaluating $uri failed."));
3405 return;
3406 }
3407 completer.complete(null);
3408 }, 1));
3409
3410 var fail = convertDartClosureToJS((event) {
3411 new DeferredLoadException("Loading $uri failed.");
3412 }, 1);
3413 JS('void', '#.addEventListener("error", #, false)', xhr, fail);
3414 JS('void', '#.addEventListener("abort", #, false)', xhr, fail);
3415
3416 JS('void', '#.send()', xhr);
3417 return leavingFuture;
3418 });
3419 }
3420 // We are in a dom-context.
3421 return _loadingLibraries[hunkName] = new Future<Null>(() {
3422 Completer completer = new Completer<Null>();
3423 // Inject a script tag.
3424 var script = JS('', 'document.createElement("script")');
3425 JS('', '#.type = "text/javascript"', script);
3426 JS('', '#.src = #', script, uri);
3427 JS('', '#.addEventListener("load", #, false)',
3428 script, convertDartClosureToJS((event) {
3429 completer.complete(null);
3430 }, 1));
3431 JS('', '#.addEventListener("error", #, false)',
3432 script, convertDartClosureToJS((event) {
3433 completer.completeError(
3434 new DeferredLoadException("Loading $uri failed."));
3435 }, 1));
3436 JS('', 'document.body.appendChild(#)', script);
3437
3438 return completer.future;
3439 });
3440 }
3441
3442 class MainError extends Error implements NoSuchMethodError {
3443 final String _message;
3444
3445 MainError(this._message);
3446
3447 String toString() => 'NoSuchMethodError: $_message';
3448 }
3449
3450 void missingMain() {
3451 throw new MainError("No top-level function named 'main'.");
3452 }
3453
3454 void badMain() {
3455 throw new MainError("'main' is not a function.");
3456 }
3457
3458 void mainHasTooManyParameters() {
3459 throw new MainError("'main' expects too many parameters.");
3460 }
OLDNEW
« no previous file with comments | « tool/input_sdk/private/isolate_serialization.dart ('k') | tool/input_sdk/private/js_mirrors.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698