OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 dart2js.type_mask_system; | 5 library dart2js.type_mask_system; |
6 | 6 |
7 import '../common/names.dart' show Selectors, Identifiers; | 7 import '../common/names.dart' show Selectors, Identifiers; |
8 import '../compiler.dart' as dart2js show Compiler; | 8 import '../compiler.dart' as dart2js show Compiler; |
9 import '../constants/values.dart'; | 9 import '../constants/values.dart'; |
10 import '../dart_types.dart' as types; | 10 import '../dart_types.dart' as types; |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 if (_numStringBoolType == null) { | 97 if (_numStringBoolType == null) { |
98 // Build the number+string+bool type. To make containment tests more | 98 // Build the number+string+bool type. To make containment tests more |
99 // inclusive, we use the num, String, bool types for this, not | 99 // inclusive, we use the num, String, bool types for this, not |
100 // the JSNumber, JSString, JSBool subclasses. | 100 // the JSNumber, JSString, JSBool subclasses. |
101 TypeMask anyNum = | 101 TypeMask anyNum = |
102 new TypeMask.nonNullSubtype(classWorld.numClass, classWorld); | 102 new TypeMask.nonNullSubtype(classWorld.numClass, classWorld); |
103 TypeMask anyString = | 103 TypeMask anyString = |
104 new TypeMask.nonNullSubtype(classWorld.stringClass, classWorld); | 104 new TypeMask.nonNullSubtype(classWorld.stringClass, classWorld); |
105 TypeMask anyBool = | 105 TypeMask anyBool = |
106 new TypeMask.nonNullSubtype(classWorld.boolClass, classWorld); | 106 new TypeMask.nonNullSubtype(classWorld.boolClass, classWorld); |
107 _numStringBoolType = | 107 _numStringBoolType = new TypeMask.unionOf( |
108 new TypeMask.unionOf(<TypeMask>[anyNum, anyString, anyBool], | 108 <TypeMask>[anyNum, anyString, anyBool], classWorld); |
109 classWorld); | |
110 } | 109 } |
111 return _numStringBoolType; | 110 return _numStringBoolType; |
112 } | 111 } |
113 | 112 |
114 @override | 113 @override |
115 TypeMask get fixedLengthType { | 114 TypeMask get fixedLengthType { |
116 if (_fixedLengthType == null) { | 115 if (_fixedLengthType == null) { |
117 List<TypeMask> fixedLengthTypes = | 116 List<TypeMask> fixedLengthTypes = <TypeMask>[ |
118 <TypeMask>[stringType, backend.fixedArrayType]; | 117 stringType, |
| 118 backend.fixedArrayType |
| 119 ]; |
119 if (classWorld.isInstantiated(helpers.typedArrayClass)) { | 120 if (classWorld.isInstantiated(helpers.typedArrayClass)) { |
120 fixedLengthTypes.add(nonNullSubclass(helpers.typedArrayClass)); | 121 fixedLengthTypes.add(nonNullSubclass(helpers.typedArrayClass)); |
121 } | 122 } |
122 _fixedLengthType = new TypeMask.unionOf(fixedLengthTypes, classWorld); | 123 _fixedLengthType = new TypeMask.unionOf(fixedLengthTypes, classWorld); |
123 } | 124 } |
124 return _fixedLengthType; | 125 return _fixedLengthType; |
125 } | 126 } |
126 | 127 |
127 @override | 128 @override |
128 TypeMask get interceptorType { | 129 TypeMask get interceptorType { |
129 if (_interceptorType == null) { | 130 if (_interceptorType == null) { |
130 _interceptorType = | 131 _interceptorType = |
131 new TypeMask.nonNullSubtype(helpers.jsInterceptorClass, classWorld); | 132 new TypeMask.nonNullSubtype(helpers.jsInterceptorClass, classWorld); |
132 } | 133 } |
133 return _interceptorType; | 134 return _interceptorType; |
134 } | 135 } |
135 | 136 |
136 @override | 137 @override |
137 TypeMask get interceptedTypes { // Does not include null. | 138 TypeMask get interceptedTypes { |
| 139 // Does not include null. |
138 if (_interceptedTypes == null) { | 140 if (_interceptedTypes == null) { |
139 // We redundantly include subtypes of num/string/bool as intercepted | 141 // We redundantly include subtypes of num/string/bool as intercepted |
140 // types, because the type system does not infer that their | 142 // types, because the type system does not infer that their |
141 // implementations are all subclasses of Interceptor. | 143 // implementations are all subclasses of Interceptor. |
142 _interceptedTypes = new TypeMask.unionOf( | 144 _interceptedTypes = new TypeMask.unionOf( |
143 <TypeMask>[interceptorType, numStringBoolType], classWorld); | 145 <TypeMask>[interceptorType, numStringBoolType], classWorld); |
144 } | 146 } |
145 return _interceptedTypes; | 147 return _interceptedTypes; |
146 } | 148 } |
147 | 149 |
148 TypeMask get _indexableTypeTest { | 150 TypeMask get _indexableTypeTest { |
149 if (__indexableTypeTest == null) { | 151 if (__indexableTypeTest == null) { |
150 // Make a TypeMask containing Indexable and (redundantly) subtypes of | 152 // Make a TypeMask containing Indexable and (redundantly) subtypes of |
151 // string because the type inference does not infer that all strings are | 153 // string because the type inference does not infer that all strings are |
152 // indexables. | 154 // indexables. |
153 TypeMask indexable = | 155 TypeMask indexable = |
154 new TypeMask.nonNullSubtype(helpers.jsIndexableClass, classWorld); | 156 new TypeMask.nonNullSubtype(helpers.jsIndexableClass, classWorld); |
155 TypeMask anyString = | 157 TypeMask anyString = |
156 new TypeMask.nonNullSubtype(classWorld.stringClass, classWorld); | 158 new TypeMask.nonNullSubtype(classWorld.stringClass, classWorld); |
157 __indexableTypeTest = new TypeMask.unionOf( | 159 __indexableTypeTest = |
158 <TypeMask>[indexable, anyString], | 160 new TypeMask.unionOf(<TypeMask>[indexable, anyString], classWorld); |
159 classWorld); | |
160 } | 161 } |
161 return __indexableTypeTest; | 162 return __indexableTypeTest; |
162 } | 163 } |
163 | 164 |
164 ClassElement get jsNullClass => helpers.jsNullClass; | 165 ClassElement get jsNullClass => helpers.jsNullClass; |
165 | 166 |
166 BackendHelpers get helpers => backend.helpers; | 167 BackendHelpers get helpers => backend.helpers; |
167 | 168 |
168 // TODO(karlklose): remove compiler here. | 169 // TODO(karlklose): remove compiler here. |
169 TypeMaskSystem(dart2js.Compiler compiler) | 170 TypeMaskSystem(dart2js.Compiler compiler) |
170 : inferrer = compiler.typesTask, | 171 : inferrer = compiler.typesTask, |
171 classWorld = compiler.world, | 172 classWorld = compiler.world, |
172 backend = compiler.backend { | 173 backend = compiler.backend {} |
173 } | |
174 | 174 |
175 @override | 175 @override |
176 bool methodIgnoresReceiverArgument(FunctionElement function) { | 176 bool methodIgnoresReceiverArgument(FunctionElement function) { |
177 assert(backend.isInterceptedMethod(function)); | 177 assert(backend.isInterceptedMethod(function)); |
178 ClassElement clazz = function.enclosingClass.declaration; | 178 ClassElement clazz = function.enclosingClass.declaration; |
179 return !clazz.isSubclassOf(helpers.jsInterceptorClass) && | 179 return !clazz.isSubclassOf(helpers.jsInterceptorClass) && |
180 !classWorld.isUsedAsMixin(clazz); | 180 !classWorld.isUsedAsMixin(clazz); |
181 } | 181 } |
182 | 182 |
183 @override | 183 @override |
184 bool targetIgnoresReceiverArgument(TypeMask type, Selector selector) { | 184 bool targetIgnoresReceiverArgument(TypeMask type, Selector selector) { |
185 // Check if any of the possible targets depend on the extra receiver | 185 // Check if any of the possible targets depend on the extra receiver |
186 // argument. Mixins do this, and tear-offs always needs the extra receiver | 186 // argument. Mixins do this, and tear-offs always needs the extra receiver |
187 // argument because BoundClosure uses it for equality and hash code. | 187 // argument because BoundClosure uses it for equality and hash code. |
188 // TODO(15933): Make automatically generated property extraction | 188 // TODO(15933): Make automatically generated property extraction |
189 // closures work with the dummy receiver optimization. | 189 // closures work with the dummy receiver optimization. |
190 bool needsReceiver(Element target) { | 190 bool needsReceiver(Element target) { |
191 if (target is! FunctionElement) return false; | 191 if (target is! FunctionElement) return false; |
192 FunctionElement function = target; | 192 FunctionElement function = target; |
193 return selector.isGetter && !function.isGetter || | 193 return selector.isGetter && !function.isGetter || |
194 !methodIgnoresReceiverArgument(function); | 194 !methodIgnoresReceiverArgument(function); |
195 } | 195 } |
196 return !classWorld.allFunctions.filter(selector, type).any(needsReceiver); | 196 return !classWorld.allFunctions.filter(selector, type).any(needsReceiver); |
197 } | 197 } |
198 | 198 |
199 @override | 199 @override |
200 Element locateSingleElement(TypeMask mask, Selector selector) { | 200 Element locateSingleElement(TypeMask mask, Selector selector) { |
201 return mask.locateSingleElement(selector, mask, classWorld.compiler); | 201 return mask.locateSingleElement(selector, mask, classWorld.compiler); |
202 } | 202 } |
203 | 203 |
204 @override | 204 @override |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 return a.union(b, classWorld); | 264 return a.union(b, classWorld); |
265 } | 265 } |
266 | 266 |
267 @override | 267 @override |
268 TypeMask intersection(TypeMask a, TypeMask b) { | 268 TypeMask intersection(TypeMask a, TypeMask b) { |
269 if (a == null) return b; | 269 if (a == null) return b; |
270 if (b == null) return a; | 270 if (b == null) return a; |
271 return a.intersection(b, classWorld); | 271 return a.intersection(b, classWorld); |
272 } | 272 } |
273 | 273 |
274 void associateConstantValueWithElement(ConstantValue constant, | 274 void associateConstantValueWithElement( |
275 Element element) { | 275 ConstantValue constant, Element element) { |
276 // TODO(25093): Replace this code with an approach that works for anonymous | 276 // TODO(25093): Replace this code with an approach that works for anonymous |
277 // constants and non-constant literals. | 277 // constants and non-constant literals. |
278 if (constant is ListConstantValue || constant is MapConstantValue) { | 278 if (constant is ListConstantValue || constant is MapConstantValue) { |
279 // Inferred type is usually better (e.g. a ContainerTypeMask) but is | 279 // Inferred type is usually better (e.g. a ContainerTypeMask) but is |
280 // occasionally less general. | 280 // occasionally less general. |
281 TypeMask computed = computeTypeMask(inferrer.compiler, constant); | 281 TypeMask computed = computeTypeMask(inferrer.compiler, constant); |
282 TypeMask inferred = inferrer.getGuaranteedTypeOfElement(element); | 282 TypeMask inferred = inferrer.getGuaranteedTypeOfElement(element); |
283 TypeMask best = intersection(inferred, computed); | 283 TypeMask best = intersection(inferred, computed); |
284 assert(!best.isEmptyOrNull); | 284 assert(!best.isEmptyOrNull); |
285 _constantMasks[constant] = best; | 285 _constantMasks[constant] = best; |
286 } | 286 } |
287 } | 287 } |
288 | 288 |
289 @override | 289 @override |
290 TypeMask getTypeOf(ConstantValue constant) { | 290 TypeMask getTypeOf(ConstantValue constant) { |
291 return _constantMasks[constant] ?? | 291 return _constantMasks[constant] ?? |
292 computeTypeMask(inferrer.compiler, constant); | 292 computeTypeMask(inferrer.compiler, constant); |
293 } | 293 } |
294 | 294 |
295 @override | 295 @override |
296 ConstantValue getConstantOf(TypeMask mask) { | 296 ConstantValue getConstantOf(TypeMask mask) { |
297 if (!mask.isValue) return null; | 297 if (!mask.isValue) return null; |
298 if (mask.isNullable) return null; // e.g. 'true or null'. | 298 if (mask.isNullable) return null; // e.g. 'true or null'. |
299 ValueTypeMask valueMask = mask; | 299 ValueTypeMask valueMask = mask; |
300 if (valueMask.value.isBool) return valueMask.value; | 300 if (valueMask.value.isBool) return valueMask.value; |
301 // TODO(sra): Consider other values. Be careful with large strings. | 301 // TODO(sra): Consider other values. Be careful with large strings. |
302 return null; | 302 return null; |
303 } | 303 } |
304 | 304 |
305 @override | 305 @override |
306 TypeMask nonNullExact(ClassElement element) { | 306 TypeMask nonNullExact(ClassElement element) { |
307 // TODO(johnniwinther): I don't think the follow is valid anymore. | 307 // TODO(johnniwinther): I don't think the follow is valid anymore. |
308 // The class world does not know about classes created by | 308 // The class world does not know about classes created by |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 | 413 |
414 @override | 414 @override |
415 bool isDefinitelyFixedArray(TypeMask t, {bool allowNull: false}) { | 415 bool isDefinitelyFixedArray(TypeMask t, {bool allowNull: false}) { |
416 if (!allowNull && t.isNullable) return false; | 416 if (!allowNull && t.isNullable) return false; |
417 return t.nonNullable().satisfies(helpers.jsFixedArrayClass, classWorld); | 417 return t.nonNullable().satisfies(helpers.jsFixedArrayClass, classWorld); |
418 } | 418 } |
419 | 419 |
420 @override | 420 @override |
421 bool isDefinitelyExtendableArray(TypeMask t, {bool allowNull: false}) { | 421 bool isDefinitelyExtendableArray(TypeMask t, {bool allowNull: false}) { |
422 if (!allowNull && t.isNullable) return false; | 422 if (!allowNull && t.isNullable) return false; |
423 return t.nonNullable().satisfies(helpers.jsExtendableArrayClass, | 423 return t |
424 classWorld); | 424 .nonNullable() |
| 425 .satisfies(helpers.jsExtendableArrayClass, classWorld); |
425 } | 426 } |
426 | 427 |
427 @override | 428 @override |
428 bool isDefinitelyIndexable(TypeMask t, {bool allowNull: false}) { | 429 bool isDefinitelyIndexable(TypeMask t, {bool allowNull: false}) { |
429 if (!allowNull && t.isNullable) return false; | 430 if (!allowNull && t.isNullable) return false; |
430 return _indexableTypeTest.containsMask(t.nonNullable(), classWorld); | 431 return _indexableTypeTest.containsMask(t.nonNullable(), classWorld); |
431 } | 432 } |
432 | 433 |
433 @override | 434 @override |
434 bool isDefinitelyMutableIndexable(TypeMask t, {bool allowNull: false}) { | 435 bool isDefinitelyMutableIndexable(TypeMask t, {bool allowNull: false}) { |
435 if (!allowNull && t.isNullable) return false; | 436 if (!allowNull && t.isNullable) return false; |
436 return t.nonNullable().satisfies(helpers.jsMutableIndexableClass, | 437 return t |
437 classWorld); | 438 .nonNullable() |
| 439 .satisfies(helpers.jsMutableIndexableClass, classWorld); |
438 } | 440 } |
439 | 441 |
440 @override | 442 @override |
441 bool isDefinitelyFixedLengthIndexable(TypeMask t, {bool allowNull: false}) { | 443 bool isDefinitelyFixedLengthIndexable(TypeMask t, {bool allowNull: false}) { |
442 if (!allowNull && t.isNullable) return false; | 444 if (!allowNull && t.isNullable) return false; |
443 return fixedLengthType.containsMask(t.nonNullable(), classWorld); | 445 return fixedLengthType.containsMask(t.nonNullable(), classWorld); |
444 } | 446 } |
445 | 447 |
446 @override | 448 @override |
447 bool isDefinitelyIntercepted(TypeMask t, {bool allowNull}) { | 449 bool isDefinitelyIntercepted(TypeMask t, {bool allowNull}) { |
(...skipping 25 matching lines...) Expand all Loading... |
473 @override | 475 @override |
474 bool areDisjoint(TypeMask leftType, TypeMask rightType) => | 476 bool areDisjoint(TypeMask leftType, TypeMask rightType) => |
475 leftType.isDisjoint(rightType, classWorld); | 477 leftType.isDisjoint(rightType, classWorld); |
476 | 478 |
477 @override | 479 @override |
478 bool isMorePreciseOrEqual(TypeMask t1, TypeMask t2) { | 480 bool isMorePreciseOrEqual(TypeMask t1, TypeMask t2) { |
479 return t2.containsMask(t1, classWorld); | 481 return t2.containsMask(t1, classWorld); |
480 } | 482 } |
481 | 483 |
482 @override | 484 @override |
483 AbstractBool isSubtypeOf(TypeMask value, | 485 AbstractBool isSubtypeOf(TypeMask value, types.DartType type, |
484 types.DartType type, | 486 {bool allowNull}) { |
485 {bool allowNull}) { | |
486 assert(allowNull != null); | 487 assert(allowNull != null); |
487 if (type is types.DynamicType) { | 488 if (type is types.DynamicType) { |
488 return AbstractBool.True; | 489 return AbstractBool.True; |
489 } | 490 } |
490 if (type is types.InterfaceType) { | 491 if (type is types.InterfaceType) { |
491 TypeMask typeAsMask = allowNull | 492 TypeMask typeAsMask = allowNull |
492 ? new TypeMask.subtype(type.element, classWorld) | 493 ? new TypeMask.subtype(type.element, classWorld) |
493 : new TypeMask.nonNullSubtype(type.element, classWorld); | 494 : new TypeMask.nonNullSubtype(type.element, classWorld); |
494 if (areDisjoint(value, typeAsMask)) { | 495 if (areDisjoint(value, typeAsMask)) { |
495 // Disprove the subtype relation based on the class alone. | 496 // Disprove the subtype relation based on the class alone. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 ClassElement element = type.element; | 537 ClassElement element = type.element; |
537 if (element.isObject) { | 538 if (element.isObject) { |
538 return dynamicType; | 539 return dynamicType; |
539 } | 540 } |
540 if (element == classWorld.nullClass) { | 541 if (element == classWorld.nullClass) { |
541 return nullType; | 542 return nullType; |
542 } | 543 } |
543 if (element == classWorld.stringClass) { | 544 if (element == classWorld.stringClass) { |
544 return stringType; | 545 return stringType; |
545 } | 546 } |
546 if (element == classWorld.numClass || | 547 if (element == classWorld.numClass || element == classWorld.doubleClass) { |
547 element == classWorld.doubleClass) { | |
548 return numType; | 548 return numType; |
549 } | 549 } |
550 if (element == classWorld.intClass) { | 550 if (element == classWorld.intClass) { |
551 return intType; | 551 return intType; |
552 } | 552 } |
553 if (element == classWorld.boolClass) { | 553 if (element == classWorld.boolClass) { |
554 return boolType; | 554 return boolType; |
555 } | 555 } |
556 return new TypeMask.nonNullSubtype(element, classWorld); | 556 return new TypeMask.nonNullSubtype(element, classWorld); |
557 } | 557 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
606 TypeMask result = container.typeMap[key]; | 606 TypeMask result = container.typeMap[key]; |
607 if (result != null) return result; | 607 if (result != null) return result; |
608 } | 608 } |
609 } | 609 } |
610 if (container is ContainerTypeMask) { | 610 if (container is ContainerTypeMask) { |
611 return container.elementType; | 611 return container.elementType; |
612 } | 612 } |
613 return null; | 613 return null; |
614 } | 614 } |
615 } | 615 } |
OLD | NEW |