|
2 | 2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | 3 | // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
|
| 5 | +import 'dart:typed_data'; |
| 6 | + |
5 | 7 | import 'package:analyzer/dart/ast/ast.dart'; |
| 8 | +import 'package:analyzer/dart/ast/visitor.dart'; |
| 9 | +import 'package:analyzer/dart/element/element2.dart'; |
6 | 10 | import 'package:analyzer/src/dart/ast/ast.dart'; |
7 | 11 | import 'package:analyzer/src/fine/manifest_context.dart'; |
8 | 12 | import 'package:analyzer/src/summary2/data_reader.dart'; |
9 | 13 | import 'package:analyzer/src/summary2/data_writer.dart'; |
10 | | - |
11 | | -sealed class ManifestNode { |
12 | | - bool match(MatchContext context, AstNode node); |
13 | | - |
14 | | - void write(BufferedSink sink); |
15 | | - |
16 | | - static ManifestNode encode(EncodeContext context, AstNode node) { |
17 | | - switch (node) { |
18 | | - case Annotation(): |
19 | | - return ManifestNodeAnnotation.encode(context, node); |
20 | | - case IntegerLiteral(): |
21 | | - return ManifestNodeIntegerLiteral.encode(node); |
22 | | - case SimpleIdentifier(): |
23 | | - return ManifestNodeSimpleIdentifier.encode(context, node); |
24 | | - default: |
25 | | - throw UnimplementedError('(${node.runtimeType}) $node'); |
| 14 | +import 'package:analyzer/src/utilities/extensions/collection.dart'; |
| 15 | +import 'package:collection/collection.dart'; |
| 16 | + |
| 17 | +/// Enough information to decide if the node is the same. |
| 18 | +/// |
| 19 | +/// We don't store ASTs, instead we rely on the fact that the same tokens |
| 20 | +/// are parsed into the same AST (when the same language features, which is |
| 21 | +/// ensured outside). |
| 22 | +/// |
| 23 | +/// In addition we record all referenced elements. |
| 24 | +class ManifestNode { |
| 25 | + /// The concatenated lexemes of all tokens. |
| 26 | + final String tokenBuffer; |
| 27 | + |
| 28 | + /// The length of each token in [tokenBuffer]. |
| 29 | + final Uint32List tokenLengthList; |
| 30 | + |
| 31 | + /// All unique elements referenced by this node. |
| 32 | + final List<ManifestElement> elements; |
| 33 | + |
| 34 | + /// For each property in the AST structure summarized by this manifest that |
| 35 | + /// might point to an element, `0` if the element pointer is `null`; otherwise |
| 36 | + /// one plus the index of the associated manifest element in [elements]. |
| 37 | + /// |
| 38 | + /// The order of this list reflects the AST structure, according to the |
| 39 | + /// behavior of [_ElementCollector]. |
| 40 | + final Uint32List elementIndexList; |
| 41 | + |
| 42 | + factory ManifestNode.encode(EncodeContext context, AstNode node) { |
| 43 | + var buffer = StringBuffer(); |
| 44 | + var lengthList = <int>[]; |
| 45 | + |
| 46 | + var token = node.beginToken; |
| 47 | + while (true) { |
| 48 | + buffer.write(token.lexeme); |
| 49 | + lengthList.add(token.lexeme.length); |
| 50 | + if (token == node.endToken) { |
| 51 | + break; |
| 52 | + } |
| 53 | + token = token.next ?? (throw StateError('endToken not found')); |
26 | 54 | } |
27 | | - } |
28 | 55 |
|
29 | | - static ManifestNode read(SummaryDataReader reader) { |
30 | | - var kind = reader.readEnum(_ManifestNodeKind.values); |
31 | | - switch (kind) { |
32 | | - case _ManifestNodeKind.annotation: |
33 | | - return ManifestNodeAnnotation.read(reader); |
34 | | - case _ManifestNodeKind.integerLiteral: |
35 | | - return ManifestNodeIntegerLiteral.read(reader); |
36 | | - case _ManifestNodeKind.simpleIdentifier: |
37 | | - return ManifestNodeSimpleIdentifier.read(reader); |
38 | | - } |
39 | | - } |
| 56 | + var collector = _ElementCollector(); |
| 57 | + node.accept(collector); |
40 | 58 |
|
41 | | - static ManifestNode? readOptional(SummaryDataReader reader) { |
42 | | - return reader.readOptionalObject(() => ManifestNode.read(reader)); |
| 59 | + return ManifestNode._( |
| 60 | + tokenBuffer: buffer.toString(), |
| 61 | + tokenLengthList: Uint32List.fromList(lengthList), |
| 62 | + elements: collector.map.keys |
| 63 | + .map((element) => ManifestElement.encode(context, element)) |
| 64 | + .toFixedList(), |
| 65 | + elementIndexList: Uint32List.fromList(collector.elementIndexList), |
| 66 | + ); |
43 | 67 | } |
44 | | -} |
45 | 68 |
|
46 | | -class ManifestNodeAnnotation extends ManifestNode { |
47 | | - final ManifestNodeSimpleIdentifier name; |
| 69 | + factory ManifestNode.read(SummaryDataReader reader) { |
| 70 | + return ManifestNode._( |
| 71 | + tokenBuffer: reader.readStringUtf8(), |
| 72 | + tokenLengthList: reader.readUInt30List(), |
| 73 | + elements: ManifestElement.readList(reader), |
| 74 | + elementIndexList: reader.readUInt30List(), |
| 75 | + ); |
| 76 | + } |
48 | 77 |
|
49 | | - ManifestNodeAnnotation({ |
50 | | - required this.name, |
| 78 | + ManifestNode._({ |
| 79 | + required this.tokenBuffer, |
| 80 | + required this.tokenLengthList, |
| 81 | + required this.elements, |
| 82 | + required this.elementIndexList, |
51 | 83 | }); |
52 | 84 |
|
53 | | - factory ManifestNodeAnnotation.encode( |
54 | | - EncodeContext context, |
55 | | - Annotation node, |
56 | | - ) { |
57 | | - if (node.name case SimpleIdentifier identifier) { |
58 | | - return ManifestNodeAnnotation( |
59 | | - name: ManifestNodeSimpleIdentifier.encode(context, identifier), |
60 | | - ); |
61 | | - } else { |
62 | | - throw UnimplementedError('(${node.runtimeType}) $node'); |
| 85 | + bool match(MatchContext context, AstNode node) { |
| 86 | + var tokenIndex = 0; |
| 87 | + var tokenOffset = 0; |
| 88 | + var token = node.beginToken; |
| 89 | + while (true) { |
| 90 | + var tokenLength = token.lexeme.length; |
| 91 | + if (tokenLengthList[tokenIndex++] != tokenLength) { |
| 92 | + return false; |
| 93 | + } |
| 94 | + |
| 95 | + if (!tokenBuffer.startsWith(token.lexeme, tokenOffset)) { |
| 96 | + return false; |
| 97 | + } |
| 98 | + tokenOffset += tokenLength; |
| 99 | + |
| 100 | + if (token == node.endToken) { |
| 101 | + break; |
| 102 | + } |
| 103 | + token = token.next ?? (throw StateError('endToken not found')); |
63 | 104 | } |
64 | | - } |
65 | 105 |
|
66 | | - factory ManifestNodeAnnotation.read(SummaryDataReader reader) { |
67 | | - return ManifestNodeAnnotation( |
68 | | - name: ManifestNodeSimpleIdentifier.read(reader), |
69 | | - ); |
70 | | - } |
| 106 | + var collector = _ElementCollector(); |
| 107 | + node.accept(collector); |
71 | 108 |
|
72 | | - @override |
73 | | - bool match(MatchContext context, AstNode node) { |
74 | | - if (node is! Annotation) { |
| 109 | + // Must reference the same elements. |
| 110 | + if (collector.map.length != elements.length) { |
75 | 111 | return false; |
76 | 112 | } |
| 113 | + for (var (index, element) in collector.map.keys.indexed) { |
| 114 | + if (!elements[index].match(context, element)) { |
| 115 | + return false; |
| 116 | + } |
| 117 | + } |
77 | 118 |
|
78 | | - if (!name.match(context, node.name)) { |
| 119 | + // Must reference elements in the same order. |
| 120 | + if (!const ListEquality<int>().equals( |
| 121 | + collector.elementIndexList, |
| 122 | + elementIndexList, |
| 123 | + )) { |
79 | 124 | return false; |
80 | 125 | } |
81 | 126 |
|
82 | 127 | return true; |
83 | 128 | } |
84 | 129 |
|
85 | | - @override |
86 | 130 | void write(BufferedSink sink) { |
87 | | - sink.writeEnum(_ManifestNodeKind.annotation); |
88 | | - name.writeNoTag(sink); |
89 | | - } |
90 | | -} |
91 | | - |
92 | | -class ManifestNodeIntegerLiteral extends ManifestNode { |
93 | | - final int? value; |
94 | | - |
95 | | - ManifestNodeIntegerLiteral({ |
96 | | - required this.value, |
97 | | - }); |
98 | | - |
99 | | - factory ManifestNodeIntegerLiteral.encode(IntegerLiteral node) { |
100 | | - return ManifestNodeIntegerLiteral( |
101 | | - value: node.value, |
102 | | - ); |
103 | | - } |
104 | | - |
105 | | - factory ManifestNodeIntegerLiteral.read(SummaryDataReader reader) { |
106 | | - return ManifestNodeIntegerLiteral( |
107 | | - value: reader.readOptionalInt64(), |
108 | | - ); |
| 131 | + sink.writeStringUtf8(tokenBuffer); |
| 132 | + sink.writeUint30List(tokenLengthList); |
| 133 | + sink.writeList(elements, (e) => e.write(sink)); |
| 134 | + sink.writeUint30List(elementIndexList); |
109 | 135 | } |
110 | 136 |
|
111 | | - @override |
112 | | - bool match(MatchContext context, AstNode node) { |
113 | | - return node is IntegerLiteral && node.value == value; |
114 | | - } |
115 | | - |
116 | | - @override |
117 | | - void write(BufferedSink sink) { |
118 | | - sink.writeEnum(_ManifestNodeKind.integerLiteral); |
119 | | - sink.writeOptionalInt64(value); |
| 137 | + static ManifestNode? readOptional(SummaryDataReader reader) { |
| 138 | + return reader.readOptionalObject(() => ManifestNode.read(reader)); |
120 | 139 | } |
121 | 140 | } |
122 | 141 |
|
123 | | -class ManifestNodeSimpleIdentifier extends ManifestNode { |
124 | | - final String name; |
125 | | - final ManifestElement? element; |
| 142 | +class _ElementCollector extends ThrowingAstVisitor<void> { |
| 143 | + static const int _nullIndex = 0; |
126 | 144 |
|
127 | | - ManifestNodeSimpleIdentifier({ |
128 | | - required this.name, |
129 | | - required this.element, |
130 | | - }); |
| 145 | + final Map<Element2, int> map = Map.identity(); |
| 146 | + final List<int> elementIndexList = []; |
131 | 147 |
|
132 | | - factory ManifestNodeSimpleIdentifier.encode( |
133 | | - EncodeContext context, |
134 | | - SimpleIdentifier node, |
135 | | - ) { |
136 | | - var element = node.element; |
137 | | - return ManifestNodeSimpleIdentifier( |
138 | | - name: node.name, |
139 | | - element: |
140 | | - element != null ? ManifestElement.encode(context, element) : null, |
141 | | - ); |
| 148 | + @override |
| 149 | + void visitAnnotation(Annotation node) { |
| 150 | + // TODO(scheglov): implement visitAnnotation |
142 | 151 | } |
143 | 152 |
|
144 | | - factory ManifestNodeSimpleIdentifier.read(SummaryDataReader reader) { |
145 | | - return ManifestNodeSimpleIdentifier( |
146 | | - name: reader.readStringUtf8(), |
147 | | - element: reader.readOptionalObject( |
148 | | - () => ManifestElement.read(reader), |
149 | | - ), |
150 | | - ); |
| 153 | + @override |
| 154 | + void visitBinaryExpression(BinaryExpression node) { |
| 155 | + node.leftOperand.accept(this); |
| 156 | + _addElement(node.element); |
| 157 | + node.rightOperand.accept(this); |
151 | 158 | } |
152 | 159 |
|
153 | 160 | @override |
154 | | - bool match(MatchContext context, AstNode node) { |
155 | | - if (node is! SimpleIdentifier) { |
156 | | - return false; |
157 | | - } |
| 161 | + void visitIntegerLiteral(IntegerLiteral node) {} |
158 | 162 |
|
159 | | - if (node.name != name) { |
160 | | - return false; |
161 | | - } |
162 | | - |
163 | | - var element = this.element; |
164 | | - var nodeElement = node.element; |
165 | | - if (element == null && nodeElement == null) { |
166 | | - } else if (element == null || nodeElement == null) { |
167 | | - return false; |
168 | | - } else if (!element.match(context, nodeElement)) { |
169 | | - return false; |
170 | | - } |
171 | | - |
172 | | - return true; |
| 163 | + @override |
| 164 | + void visitParenthesizedExpression(ParenthesizedExpression node) { |
| 165 | + node.visitChildren(this); |
173 | 166 | } |
174 | 167 |
|
175 | 168 | @override |
176 | | - void write(BufferedSink sink) { |
177 | | - sink.writeEnum(_ManifestNodeKind.simpleIdentifier); |
178 | | - writeNoTag(sink); |
| 169 | + void visitSimpleIdentifier(SimpleIdentifier node) { |
| 170 | + _addElement(node.element); |
179 | 171 | } |
180 | 172 |
|
181 | | - void writeNoTag(BufferedSink sink) { |
182 | | - sink.writeStringUtf8(name); |
183 | | - sink.writeOptionalObject(element, (it) => it.write(sink)); |
| 173 | + void _addElement(Element2? element) { |
| 174 | + if (element == null) { |
| 175 | + elementIndexList.add(_nullIndex); |
| 176 | + } else { |
| 177 | + var index = map[element] ??= 1 + map.length; |
| 178 | + elementIndexList.add(index); |
| 179 | + } |
184 | 180 | } |
185 | 181 | } |
186 | 182 |
|
187 | | -enum _ManifestNodeKind { |
188 | | - annotation, |
189 | | - integerLiteral, |
190 | | - simpleIdentifier, |
191 | | -} |
192 | | - |
193 | 183 | extension ManifestNodeOrNullExtension on ManifestNode? { |
194 | 184 | bool match(MatchContext context, AstNode? node) { |
195 | 185 | var self = this; |
196 | | - if (self == null && node == null) { |
197 | | - return true; |
198 | | - } else if (self == null || node == null) { |
199 | | - return false; |
200 | | - } else { |
| 186 | + if (self != null && node != null) { |
201 | 187 | return self.match(context, node); |
| 188 | + } else { |
| 189 | + return self == null && node == null; |
202 | 190 | } |
203 | 191 | } |
204 | 192 |
|
|
0 commit comments