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

Side by Side Diff: pkg/smoke/lib/codegen/generator.dart

Issue 362043006: Add support in smoke for generating static configurations in pieces. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 5 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 to generate code that can initialize the `StaticConfiguration` in 5 /// Library to generate code that can initialize the `StaticConfiguration` in
6 /// `package:smoke/static.dart`. 6 /// `package:smoke/static.dart`.
7 /// 7 ///
8 /// This library doesn't have any specific logic to extract information from 8 /// This library doesn't have any specific logic to extract information from
9 /// Dart source code. To extract code using the analyzer, take a look at the 9 /// Dart source code. To extract code using the analyzer, take a look at the
10 /// `smoke.codegen.recorder` library. 10 /// `smoke.codegen.recorder` library.
11 library smoke.codegen.generator; 11 library smoke.codegen.generator;
12 12
13 import 'dart:collection' show SplayTreeMap, SplayTreeSet; 13 import 'dart:collection' show SplayTreeMap, SplayTreeSet;
14 14
15 import 'package:smoke/src/common.dart' show compareLists, compareMaps; 15 import 'package:smoke/src/common.dart' show compareLists, compareMaps;
16 16
17 /// Collects the necessary information and generates code to initialize a 17 /// Collects the necessary information and generates code to initialize a
18 /// `StaticConfiguration`. After setting up the generator by calling 18 /// `StaticConfiguration`. After setting up the generator by calling
19 /// [addGetter], [addSetter], [addSymbol], and [addDeclaration], you can 19 /// [addGetter], [addSetter], [addSymbol], and [addDeclaration], you can
20 /// retrieve the generated code using the following three methods: 20 /// retrieve the generated code using the following three methods:
21 /// 21 ///
22 /// * [writeImports] writes a list of imports directives, 22 /// * [writeImports] writes a list of imports directives,
23 /// * [writeTopLevelDeclarations] writes additional declarations used to 23 /// * [writeTopLevelDeclarations] writes additional declarations used to
24 /// represent mixin classes by name in the generated code. 24 /// represent mixin classes by name in the generated code.
25 /// * [writeInitCall] writes the actual code that allocates the static 25 /// * [writeStaticConfiguration] writes the actual code that allocates the
26 /// configuration. 26 /// static configuration.
27 /// 27 ///
28 /// You'd need to include all three in your generated code, since the 28 /// You'd need to include all three in your generated code, since the
29 /// initialization code refers to symbols that are only available from the 29 /// initialization code refers to symbols that are only available from the
30 /// generated imports or the generated top-level declarations. 30 /// generated imports or the generated top-level declarations.
31 class SmokeCodeGenerator { 31 class SmokeCodeGenerator {
32 // Note: we use SplayTreeSet/Map here and below to keep generated code sorted. 32 // Note: we use SplayTreeSet/Map here and below to keep generated code sorted.
33 /// Names used as getters via smoke. 33 /// Names used as getters via smoke.
34 final Set<String> _getters = new SplayTreeSet(); 34 final Set<String> _getters = new SplayTreeSet();
35 35
36 /// Names used as setters via smoke. 36 /// Names used as setters via smoke.
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 /// Register that we might try to read declarations of [type], even if no 126 /// Register that we might try to read declarations of [type], even if no
127 /// declaration exists. This informs the smoke system that querying for a 127 /// declaration exists. This informs the smoke system that querying for a
128 /// member in this class might be intentional and not an error. 128 /// member in this class might be intentional and not an error.
129 void addEmptyDeclaration(TypeIdentifier type) { 129 void addEmptyDeclaration(TypeIdentifier type) {
130 _addLibrary(type.importUrl); 130 _addLibrary(type.importUrl);
131 _declarations.putIfAbsent(type, 131 _declarations.putIfAbsent(type,
132 () => new SplayTreeMap<String, _DeclarationCode>()); 132 () => new SplayTreeMap<String, _DeclarationCode>());
133 } 133 }
134 134
135 /// Writes to [buffer] a line for each import that is needed by the generated 135 /// Writes to [buffer] a line for each import that is needed by the generated
136 /// code. The code added by [writeInitCall] depends on these imports. 136 /// code. The code added by [writeStaticConfiguration] depends on these
137 /// imports.
137 void writeImports(StringBuffer buffer) { 138 void writeImports(StringBuffer buffer) {
138 DEFAULT_IMPORTS.forEach((i) => buffer.writeln(i)); 139 DEFAULT_IMPORTS.forEach((i) => buffer.writeln(i));
139 _libraryPrefix.forEach((url, prefix) { 140 _libraryPrefix.forEach((url, prefix) {
140 buffer.writeln("import '$url' as $prefix;"); 141 buffer.writeln("import '$url' as $prefix;");
141 }); 142 });
142 } 143 }
143 144
144 /// Writes to [buffer] top-level declarations that are used by the code 145 /// Writes to [buffer] top-level declarations that are used by the code
145 /// generated in [writeInitCall]. These are typically declarations of empty 146 /// generated in [writeStaticConfiguration]. These are typically declarations
146 /// classes that are then used as placeholders for mixin superclasses. 147 /// of empty classes that are then used as placeholders for mixin
148 /// superclasses.
147 void writeTopLevelDeclarations(StringBuffer buffer) { 149 void writeTopLevelDeclarations(StringBuffer buffer) {
148 var types = new Set() 150 var types = new Set()
149 ..addAll(_parents.keys) 151 ..addAll(_parents.keys)
150 ..addAll(_parents.values) 152 ..addAll(_parents.values)
151 ..addAll(_declarations.keys) 153 ..addAll(_declarations.keys)
152 ..addAll(_declarations.values.expand( 154 ..addAll(_declarations.values.expand(
153 (m) => m.values.map((d) => d.type))) 155 (m) => m.values.map((d) => d.type)))
154 ..removeWhere((t) => t.importUrl != null); 156 ..removeWhere((t) => t.importUrl != null);
155 for (var type in types) { 157 for (var type in types) {
156 buffer.write('abstract class ${type.name} {}'); 158 buffer.write('abstract class ${type.name} {}');
157 if (type.comment != null) buffer.write(' // ${type.comment}'); 159 if (type.comment != null) buffer.write(' // ${type.comment}');
158 buffer.writeln(); 160 buffer.writeln();
159 } 161 }
160 } 162 }
161 163
162 /// Appends to [buffer] code that will initialize smoke's static 164 /// Appends to [buffer] code that will create smoke's static configuration.
163 /// configuration. For example, the code might be of the form: 165 /// For example, the code might be of the form:
164 /// 166 ///
165 /// useGeneratedCode(new StaticConfiguration( 167 /// new StaticConfiguration(
166 /// getters: { 168 /// getters: {
167 /// #i: (o) => o.i, 169 /// #i: (o) => o.i,
168 /// ... 170 /// ...
169 /// names: { 171 /// names: {
170 /// #i: "i", 172 /// #i: "i",
171 /// })); 173 /// })
174 ///
175 /// Callers of this code can assign this expression to a variable, and should
176 /// generate code that invokes `useGeneratedCode`.
172 /// 177 ///
173 /// The optional [indent] argument is used for formatting purposes. All 178 /// The optional [indent] argument is used for formatting purposes. All
174 /// entries in each map (getters, setters, names, declarations, parents) are 179 /// entries in each map (getters, setters, names, declarations, parents) are
175 /// sorted alphabetically. 180 /// sorted alphabetically.
176 /// 181 ///
177 /// **Note**: this code assumes that imports from [writeImports] and top-level 182 /// **Note**: this code assumes that imports from [writeImports] and top-level
178 /// declarations from [writeTopLevelDeclarations] are included in the same 183 /// declarations from [writeTopLevelDeclarations] are included in the same
179 /// library where this code will live. 184 /// library where this code will live.
180 void writeInitCall(StringBuffer buffer, [int indent = 2]) { 185 void writeStaticConfiguration(StringBuffer buffer, [int indent = 2]) {
181 final spaces = new List.filled(indent, ' ').join(''); 186 final spaces = ' ' * (indent + 4);
182 var args = {}; 187 var args = {};
183 188
184 if (_getters.isNotEmpty) { 189 if (_getters.isNotEmpty) {
185 args['getters'] = _getters.map((n) => '${_symbol(n)}: (o) => o.$n'); 190 args['getters'] = _getters.map((n) => '${_symbol(n)}: (o) => o.$n');
186 } 191 }
187 if (_setters.isNotEmpty) { 192 if (_setters.isNotEmpty) {
188 args['setters'] = _setters.map( 193 args['setters'] = _setters.map(
189 (n) => '${_symbol(n)}: (o, v) { o.$n = v; }'); 194 (n) => '${_symbol(n)}: (o, v) { o.$n = v; }');
190 } 195 }
191 196
192 if (_parents.isNotEmpty) { 197 if (_parents.isNotEmpty) {
193 var parentsMap = []; 198 var parentsMap = [];
194 _parents.forEach((child, parent) { 199 _parents.forEach((child, parent) {
195 var parent = _parents[child]; 200 var parent = _parents[child];
196 parentsMap.add('${child.asCode(_libraryPrefix)}: ' 201 parentsMap.add('${child.asCode(_libraryPrefix)}: '
197 '${parent.asCode(_libraryPrefix)}'); 202 '${parent.asCode(_libraryPrefix)}');
198 }); 203 });
199 args['parents'] = parentsMap; 204 args['parents'] = parentsMap;
200 } 205 }
201 206
202 if (_declarations.isNotEmpty) { 207 if (_declarations.isNotEmpty) {
203 var declarations = []; 208 var declarations = [];
204 _declarations.forEach((type, members) { 209 _declarations.forEach((type, members) {
205 final sb = new StringBuffer() 210 final sb = new StringBuffer()
206 ..write(type.asCode(_libraryPrefix)) 211 ..write(type.asCode(_libraryPrefix))
207 ..write(': '); 212 ..write(': ');
208 if (members.isEmpty) { 213 if (members.isEmpty) {
209 sb.write('const {}'); 214 sb.write('{}');
210 } else { 215 } else {
211 sb.write('{\n'); 216 sb.write('{\n');
212 members.forEach((name, decl) { 217 members.forEach((name, decl) {
213 var decl = members[name].asCode(_libraryPrefix); 218 var decl = members[name].asCode(_libraryPrefix);
214 sb.write('$spaces ${_symbol(name)}: $decl,\n'); 219 sb.write('${spaces} ${_symbol(name)}: $decl,\n');
215 }); 220 });
216 sb.write('$spaces }'); 221 sb.write('${spaces} }');
217 } 222 }
218 declarations.add(sb.toString()); 223 declarations.add(sb.toString());
219 }); 224 });
220 args['declarations'] = declarations; 225 args['declarations'] = declarations;
221 } 226 }
222 227
223 if (_staticMethods.isNotEmpty) { 228 if (_staticMethods.isNotEmpty) {
224 var methods = []; 229 var methods = [];
225 _staticMethods.forEach((type, members) { 230 _staticMethods.forEach((type, members) {
226 var className = type.asCode(_libraryPrefix); 231 var className = type.asCode(_libraryPrefix);
227 final sb = new StringBuffer() 232 final sb = new StringBuffer()
228 ..write(className) 233 ..write(className)
229 ..write(': '); 234 ..write(': ');
230 if (members.isEmpty) { 235 if (members.isEmpty) {
231 sb.write('const {}'); 236 sb.write('{}');
232 } else { 237 } else {
233 sb.write('{\n'); 238 sb.write('{\n');
234 for (var name in members) { 239 for (var name in members) {
235 sb.write('$spaces ${_symbol(name)}: $className.$name,\n'); 240 sb.write('${spaces} ${_symbol(name)}: $className.$name,\n');
236 } 241 }
237 sb.write('$spaces }'); 242 sb.write('${spaces} }');
238 } 243 }
239 methods.add(sb.toString()); 244 methods.add(sb.toString());
240 }); 245 });
241 args['staticMethods'] = methods; 246 args['staticMethods'] = methods;
242 } 247 }
243 248
244 if (_names.isNotEmpty) { 249 if (_names.isNotEmpty) {
245 args['names'] = _names.map((n) => "${_symbol(n)}: r'$n'"); 250 args['names'] = _names.map((n) => "${_symbol(n)}: r'$n'");
246 } 251 }
247 252
248 buffer..write(spaces) 253 buffer..writeln('new StaticConfiguration(')
249 ..writeln('useGeneratedCode(new StaticConfiguration(') 254 ..write('${spaces}checkedMode: false');
jakemac 2014/07/23 20:05:02 I think technically these ..'s should line up
Siggi Cherem (dart-lang) 2014/07/23 20:37:13 we have debated a bit about it in the past - the s
jakemac 2014/07/23 20:40:51 Acknowledged.
250 ..write('$spaces checkedMode: false');
251 255
252 args.forEach((name, mapContents) { 256 args.forEach((name, mapContents) {
253 buffer.writeln(','); 257 buffer.writeln(',');
254 // TODO(sigmund): use const map when Type can be keys (dartbug.com/17123) 258 // TODO(sigmund): use const map when Type can be keys (dartbug.com/17123)
255 buffer.writeln('$spaces $name: {'); 259 buffer.writeln('${spaces}$name: {');
256 for (var entry in mapContents) { 260 for (var entry in mapContents) {
257 buffer.writeln('$spaces $entry,'); 261 buffer.writeln('${spaces} $entry,');
258 } 262 }
259 buffer.write('$spaces }'); 263 buffer.write('${spaces}}');
260 }); 264 });
261 buffer.writeln('));'); 265 buffer.write(')');
262 } 266 }
263 267
264 /// Adds a library that needs to be imported. 268 /// Adds a library that needs to be imported.
265 void _addLibrary(String url) { 269 void _addLibrary(String url) {
266 if (url == null || url == 'dart:core') return; 270 if (url == null || url == 'dart:core') return;
267 _libraryPrefix.putIfAbsent(url, () => 'smoke_${_libraryPrefix.length}'); 271 _libraryPrefix.putIfAbsent(url, () => 'smoke_${_libraryPrefix.length}');
268 } 272 }
269 } 273 }
270 274
271 /// Information used to generate code that allocates a `Declaration` object. 275 /// Information used to generate code that allocates a `Declaration` object.
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
486 '^(?:$_publicIdentifierRE(?:\$|[.](?!\$)))+?\$'); 490 '^(?:$_publicIdentifierRE(?:\$|[.](?!\$)))+?\$');
487 491
488 /// Operator names allowed as symbols. The name of the oeprators is the same as 492 /// Operator names allowed as symbols. The name of the oeprators is the same as
489 /// the operator itself except for unary minus, where the name is "unary-". 493 /// the operator itself except for unary minus, where the name is "unary-".
490 const String _operatorRE = 494 const String _operatorRE =
491 r'(?:[\-+*/%&|^]|\[\]=?|==|~/?|<[<=]?|>[>=]?|unary-)'; 495 r'(?:[\-+*/%&|^]|\[\]=?|==|~/?|<[<=]?|>[>=]?|unary-)';
492 496
493 /// Pattern that matches public symbols. 497 /// Pattern that matches public symbols.
494 final RegExp _publicSymbolPattern = new RegExp( 498 final RegExp _publicSymbolPattern = new RegExp(
495 '^(?:$_operatorRE\$|$_publicIdentifierRE(?:=?\$|[.](?!\$)))+?\$'); 499 '^(?:$_operatorRE\$|$_publicIdentifierRE(?:=?\$|[.](?!\$)))+?\$');
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698