Chromium Code Reviews

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/class_property_model.dart

Issue 2691433004: Support virtual fields in DDC without requiring the @virtual annotation.
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
« no previous file with comments | « no previous file | pkg/dev_compiler/lib/src/compiler/code_generator.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, 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 import 'dart:collection' show HashSet; 5 import 'dart:collection' show HashMap, HashSet;
6 6
7 import 'package:analyzer/analyzer.dart' as analyzer;
7 import 'package:analyzer/dart/ast/ast.dart' show Identifier; 8 import 'package:analyzer/dart/ast/ast.dart' show Identifier;
8 import 'package:analyzer/dart/element/element.dart'; 9 import 'package:analyzer/dart/element/element.dart';
9 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl; 10 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl;
10 11
11 import '../js_ast/js_ast.dart' as JS; 12 import '../js_ast/js_ast.dart' as JS;
12 import 'element_helpers.dart'; 13 import 'element_helpers.dart';
13 import 'js_names.dart' as JS; 14 import 'js_names.dart' as JS;
14 15
15 /// Tracks how fields, getters and setters are represented when emitting JS. 16 /// Tracks how fields, getters and setters are represented when emitting JS.
16 /// 17 ///
17 /// Dart classes have implicit features that must be made explicit: 18 /// Dart classes have implicit features that must be made explicit:
18 /// 19 ///
19 /// - virtual fields induce a getter and setter pair. 20 /// - virtual fields induce a getter and setter pair.
20 /// - getters and setters are independent. 21 /// - getters and setters are independent.
21 /// - getters and setters can be overridden. 22 /// - getters and setters can be overridden.
22 /// 23 ///
23 class ClassPropertyModel { 24 class ClassPropertyModel {
24 /// Fields that are virtual, that is, they must be generated as a property 25 /// Fields that are virtual, that is, they must be generated as a property
25 /// pair in JavaScript. 26 /// pair in JavaScript.
26 /// 27 ///
27 /// The value property stores the symbol used for the field's storage slot. 28 /// The value property stores the symbol used for the field's storage slot.
28 final virtualFields = <FieldElement, JS.TemporaryId>{}; 29 final virtualFields = <FieldElement, JS.TemporaryId>{};
29 30
31 /// Fields that have been overridden by the current class and must be
32 /// virtualized.
33 ///
34 /// These fields are the elements of the super class, and not the
35 /// overriding elements.
36 ///
37 /// These elements must point to real fields (not getters).
38 final virtualizedFields = <FieldElement, JS.TemporaryId>{};
39
30 /// Static fields that are overridden, this does not matter for Dart but in 40 /// Static fields that are overridden, this does not matter for Dart but in
31 /// JS we need to take care initializing these because JS classes inherit 41 /// JS we need to take care initializing these because JS classes inherit
32 /// statics. 42 /// statics.
33 final staticFieldOverrides = new HashSet<FieldElement>(); 43 final staticFieldOverrides = new HashSet<FieldElement>();
34 44
35 /// The set of inherited getters, used because JS getters/setters are paired, 45 /// The set of inherited getters, used because JS getters/setters are paired,
36 /// so if we're generating a setter we may need to emit a getter that calls 46 /// so if we're generating a setter we may need to emit a getter that calls
37 /// super. 47 /// super.
38 final inheritedGetters = new HashSet<String>(); 48 final inheritedGetters = new HashSet<String>();
39 49
40 /// The set of inherited setters, used because JS getters/setters are paired, 50 /// The set of inherited setters, used because JS getters/setters are paired,
41 /// so if we're generating a getter we may need to emit a setter that calls 51 /// so if we're generating a getter we may need to emit a setter that calls
42 /// super. 52 /// super.
43 final inheritedSetters = new HashSet<String>(); 53 final inheritedSetters = new HashSet<String>();
44 54
45 ClassPropertyModel.build( 55 ClassPropertyModel.build(
46 ClassElement classElem, Iterable<ExecutableElement> extensionMembers) { 56 ClassElement classElem, Iterable<ExecutableElement> extensionMembers) {
57 LibraryElement classLibrary = classElem.library;
58 HashMap<String, FieldElementImpl> overriddenFields = new HashMap();
59 HashSet<String> alreadyVirtual = new HashSet();
60
47 // Visit superclasses to collect information about their fields/accessors. 61 // Visit superclasses to collect information about their fields/accessors.
48 // This is expensive so we try to collect everything in one pass. 62 // This is expensive so we try to collect everything in one pass.
49 for (var base in getSuperclasses(classElem)) { 63 for (var base in getSuperclasses(classElem)) {
50 for (var accessor in base.accessors) { 64 for (var accessor in base.accessors) {
51 // For getter/setter pairs only process them once. 65 // For getter/setter pairs only process them once.
52 if (accessor.correspondingGetter != null) continue; 66 if (accessor.correspondingGetter != null) continue;
53 67
54 var field = accessor.variable; 68 var field = accessor.variable;
55 var name = field.name; 69 var name = field.name;
70
56 // Ignore private names from other libraries. 71 // Ignore private names from other libraries.
57 if (Identifier.isPrivateName(name) && 72 if (Identifier.isPrivateName(name) &&
58 accessor.library != classElem.library) { 73 accessor.library != classLibrary) {
59 continue; 74 continue;
60 } 75 }
61 76
62 if (field.getter?.isAbstract == false) inheritedGetters.add(name); 77 // Ignore abstract getters and setters.
63 if (field.setter?.isAbstract == false) inheritedSetters.add(name); 78 if (!(field.getter?.isAbstract == false ||
79 field.setter?.isAbstract == false)) {
80 continue;
81 }
82
83 if (!field.isStatic && !alreadyVirtual.contains(name)) {
84 // Synthetic field means there is a getter or setter and the member
85 // is therefore already virtual.
86 if (field.isSynthetic) {
87 alreadyVirtual.add(name);
88 } else if (field is FieldElementImpl) {
89 if (overriddenFields.containsKey(name)) {
90 // The field is already overridden by a super class.
91 // class A { int x; } // Second time the field is added.
92 // class B extends A { int x; } // First time the field is added.
93 // class C extends B { ... };
94 alreadyVirtual.add(name);
95 } else {
96 overriddenFields[name] = field;
97 }
98 }
99 }
100
101 if (field.getter?.isAbstract == false) {
102 inheritedGetters.add(name);
103 }
104 if (field.setter?.isAbstract == false) {
105 inheritedSetters.add(name);
106 }
64 } 107 }
65 } 108 }
66 109
67 var extensionNames = 110 var extensionNames =
68 new HashSet<String>.from(extensionMembers.map((e) => e.name)); 111 new HashSet<String>.from(extensionMembers.map((e) => e.name));
69 112
70 // Visit accessors in the current class, and see if they need to be 113 // Visit accessors in the current class, and see if they need to be
71 // generated differently based on the inherited fields/accessors. 114 // generated differently based on the inherited fields/accessors.
72 for (var accessor in classElem.accessors) { 115 for (var accessor in classElem.accessors) {
73 // For getter/setter pairs only process them once. 116 // For getter/setter pairs only process them once.
74 if (accessor.correspondingGetter != null) continue; 117 if (accessor.correspondingGetter != null) continue;
75 // Also ignore abstract fields. 118 // Also ignore abstract fields.
76 if (accessor.isAbstract) continue; 119 if (accessor.isAbstract) continue;
77 120
78 var field = accessor.variable; 121 var field = accessor.variable;
79 var name = field.name; 122 var name = field.name;
123
124 if (!field.isStatic &&
125 !alreadyVirtual.contains(name) &&
126 overriddenFields.containsKey(name)) {
127 JS.TemporaryId id = new JS.TemporaryId(name);
128 var superField = overriddenFields[name];
129 virtualizedFields[superField] = id;
130 alreadyVirtual.add(name);
131 }
132
80 // Is it a field? 133 // Is it a field?
81 if (!field.isSynthetic && field is FieldElementImpl) { 134 if (!field.isSynthetic && field is FieldElementImpl) {
82 if (inheritedGetters.contains(name) || 135 if (inheritedGetters.contains(name) ||
83 inheritedSetters.contains(name) || 136 inheritedSetters.contains(name) ||
84 extensionNames.contains(name) || 137 extensionNames.contains(name)) {
85 field.isVirtual) {
86 if (field.isStatic) { 138 if (field.isStatic) {
87 staticFieldOverrides.add(field); 139 staticFieldOverrides.add(field);
88 } else { 140 } else {
89 virtualFields[field] = new JS.TemporaryId(name); 141 virtualFields[field] = new JS.TemporaryId(name);
90 } 142 }
91 } 143 }
92 } 144 }
93 } 145 }
146
147 // Run through all bodies and find all `super.x` accesses. Then take their
148 // names (`x`) and see if they need to be virtualized.
149 for (var name in SuperFinder.accessedSuperProperties(classElem)) {
150 if (!alreadyVirtual.contains(name) &&
151 overriddenFields.containsKey(name)) {
152 JS.TemporaryId id = new JS.TemporaryId(name);
153 var superField = overriddenFields[name];
154 virtualizedFields[superField] = id;
155 }
156 }
94 } 157 }
95 } 158 }
159
160 class SuperFinder extends analyzer.GeneralizingAstVisitor<Object> {
161 final accessed = new HashSet<String>();
162
163 static Iterable<String> accessedSuperProperties(ClassElement element) {
164 // When compiling the SDK, only one out of ~50 classes has any references to
165 // super at all, so we avoid searching the method bodies in the common case.
166 if (!element.hasReferenceToSuper) return const <String>[];
167 SuperFinder finder = new SuperFinder();
168 element.computeNode().accept(finder);
169 return finder.accessed;
170 }
171
172 @override
173 analyzer.PropertyAccess visitPropertyAccess(analyzer.PropertyAccess node) {
174 if (node.realTarget is analyzer.SuperExpression) {
175 var name = node.propertyName.name;
176 accessed.add(name);
177 }
178 return node;
179 }
180 }
OLDNEW
« no previous file with comments | « no previous file | pkg/dev_compiler/lib/src/compiler/code_generator.dart » ('j') | no next file with comments »

Powered by Google App Engine