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

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

Powered by Google App Engine
This is Rietveld 408576698