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

Side by Side Diff: lib/src/emitters.dart

Issue 19497002: Reducing the amount of code we generate in the compiler: We still continue (Closed) Base URL: git@github.com:dart-lang/web-ui.git@master
Patch Set: Created 7 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
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 /** Collects several code emitters for the template tool. */ 5 /** Collects several code emitters for the template tool. */
6 library emitters; 6 library emitters;
7 7
8 import 'package:csslib/parser.dart' as css; 8 import 'package:csslib/parser.dart' as css;
9 import 'package:csslib/visitor.dart'; 9 import 'package:csslib/visitor.dart';
10 import 'package:html5lib/dom.dart'; 10 import 'package:html5lib/dom.dart';
11 import 'package:html5lib/dom_parsing.dart'; 11 import 'package:html5lib/dom_parsing.dart';
12 import 'package:html5lib/parser.dart'; 12 import 'package:html5lib/parser.dart';
13 import 'package:source_maps/span.dart' show Span, FileLocation; 13 import 'package:source_maps/span.dart' show Span, FileLocation;
14 14
15 import 'code_printer.dart';
15 import 'compiler.dart'; 16 import 'compiler.dart';
16 import 'code_printer.dart';
17 import 'dart_parser.dart' show DartCodeInfo; 17 import 'dart_parser.dart' show DartCodeInfo;
18 import 'html5_utils.dart';
18 import 'html_css_fixup.dart'; 19 import 'html_css_fixup.dart';
19 import 'html5_utils.dart';
20 import 'info.dart'; 20 import 'info.dart';
21 import 'messages.dart'; 21 import 'messages.dart';
22 import 'options.dart';
22 import 'paths.dart'; 23 import 'paths.dart';
23 import 'refactor.dart'; 24 import 'refactor.dart';
24 import 'utils.dart'; 25 import 'utils.dart';
25 26
26 /**
27 * Context used by an emitter. Typically representing where to generate code
28 * and additional information, such as total number of generated identifiers.
29 */
30 class Context {
31 final Declarations statics;
32 final CodePrinter printer;
33 final bool isClass;
34
35 Context({Declarations statics, CodePrinter printer, bool isClass: false,
36 int indent: 0})
37 : this.statics = statics != null
38 ? statics : new Declarations(indent, staticKeyword: isClass),
39 this.isClass = isClass,
40 this.printer = printer != null
41 ? printer : new CodePrinter(isClass ? indent + 1 : indent);
42 }
43
44 27
45 /** Only x-tag name element selectors are emitted as [is="x-"]. */ 28 /** Only x-tag name element selectors are emitted as [is="x-"]. */
46 class CssEmitter extends CssPrinter { 29 class CssEmitter extends CssPrinter {
47 final Set _componentsTag; 30 final Set _componentsTag;
48 31
49 CssEmitter(this._componentsTag); 32 CssEmitter(this._componentsTag);
50 33
51 /** 34 /**
52 * If element selector is a component's tag name, then change selector to 35 * If element selector is a component's tag name, then change selector to
53 * find element who's is attribute's the component's name. 36 * find element who's is attribute's the component's name.
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 String emitComponentStyleSheet(StyleSheet ss, String tagName, 175 String emitComponentStyleSheet(StyleSheet ss, String tagName,
193 CssPolyfillKind polyfillKind) => 176 CssPolyfillKind polyfillKind) =>
194 (new ComponentCssEmitter(tagName, polyfillKind) 177 (new ComponentCssEmitter(tagName, polyfillKind)
195 ..visitTree(ss, pretty: true)).toString(); 178 ..visitTree(ss, pretty: true)).toString();
196 179
197 /** Generates the class corresponding to a single web component. */ 180 /** Generates the class corresponding to a single web component. */
198 class WebComponentEmitter { 181 class WebComponentEmitter {
199 final Messages messages; 182 final Messages messages;
200 final FileInfo _fileInfo; 183 final FileInfo _fileInfo;
201 final CssPolyfillKind cssPolyfillKind; 184 final CssPolyfillKind cssPolyfillKind;
202 Context _context;
203 185
204 WebComponentEmitter(this._fileInfo, this.messages, this.cssPolyfillKind) 186 WebComponentEmitter(this._fileInfo, this.messages, this.cssPolyfillKind);
205 : _context = new Context(isClass: true, indent: 1);
206 187
207 CodePrinter run(ComponentInfo info, PathMapper pathMapper, 188 CodePrinter run(ComponentInfo info, PathMapper pathMapper,
208 TextEditTransaction transaction) { 189 TextEditTransaction transaction) {
209 var templateNode = info.elementNode; 190 var templateNode = info.element.nodes.firstWhere(
191 (n) => n.tagName == 'template', orElse: () => null);
210 192
211 // elementNode is pointing at template tag (no attributes). 193 if (templateNode != null) {
212 assert(templateNode.tagName == 'element');
213 for (var node in templateNode.nodes) {
214 if (node.tagName == 'template') {
215 templateNode = node;
216 break;
217 }
218 }
219
220 if (info.element.attributes['apply-author-styles'] != null) {
221 _context.printer.addLine('if (__root is autogenerated.ShadowRoot) '
222 '__root.applyAuthorStyles = true;');
223 // TODO(jmesserly): warn at runtime if apply-author-styles was not set,
224 // and we don't have Shadow DOM support? In that case, styles won't have
225 // proper encapsulation.
226 }
227
228 if (templateNode.tagName == 'template') {
229 if (!info.styleSheets.isEmpty && !messages.options.processCss) { 194 if (!info.styleSheets.isEmpty && !messages.options.processCss) {
230 // TODO(terry): Only one style tag per component. 195 // TODO(terry): Only one style tag per component.
231 196
232 // TODO(jmesserly): csslib + html5lib should work together. 197 // TODO(jmesserly): csslib + html5lib should work together.
233 // We shouldn't need to call a different function to serialize CSS. 198 // We shouldn't need to call a different function to serialize CSS.
234 // Calling innerHtml on a StyleElement should be enought - like a real 199 // Calling innerHtml on a StyleElement should be enought - like a real
235 // browser. CSSOM and DOM should work together in the same tree. 200 // browser. CSSOM and DOM should work together in the same tree.
236 var styleText = emitComponentStyleSheet( 201 var styleText = emitComponentStyleSheet(
237 info.styleSheets[0], info.tagName, cssPolyfillKind); 202 info.styleSheets[0], info.tagName, cssPolyfillKind);
238 203
239 templateNode.insertBefore( 204 templateNode.insertBefore(
240 new Element.html('<style>\n$styleText\n</style>'), 205 new Element.html('<style>\n$styleText\n</style>'),
241 templateNode.hasChildNodes() ? templateNode.children[0] : null); 206 templateNode.hasChildNodes() ? templateNode.children[0] : null);
242 } 207 }
243
244 _context.statics.add('final', '__shadowTemplate',
245 templateNode.sourceSpan,
246 "new autogenerated.DocumentFragment.html('''"
247 "${escapeDartString(templateNode.innerHtml, triple: true)}"
248 "''')");
249
250 var syntax = templateNode.attributes['syntax'];
251 if (syntax != null) {
252 syntax = escapeDartString(syntax);
253 syntax = ", autogenerated.TemplateElement.syntax['$syntax']";
254 } else {
255 syntax = "";
256 }
257
258 _context.printer
259 ..addLine("__root.nodes.add(cloneTemplate(__shadowTemplate));")
260 ..addLine("autogenerated_mdv.bindModel(__root, this$syntax);");
261 } 208 }
262 209
263 bool hasExtends = info.extendsComponent != null; 210 bool hasExtends = info.extendsComponent != null;
264 var codeInfo = info.userCode; 211 var codeInfo = info.userCode;
265 var classDecl = info.classDeclaration; 212 var classDecl = info.classDeclaration;
266 if (classDecl == null) return null; 213 if (classDecl == null) return null;
267 214
268 if (transaction == null) { 215 if (transaction == null) {
269 transaction = new TextEditTransaction(codeInfo.code, codeInfo.sourceFile); 216 transaction = new TextEditTransaction(codeInfo.code, codeInfo.sourceFile);
270 } 217 }
271 218
272 // Expand the headers to include polymer imports, unless they are already 219 // Expand the headers to include polymer imports, unless they are already
273 // present. 220 // present.
274 var libraryName = (codeInfo.libraryName != null) 221 var libraryName = (codeInfo.libraryName != null)
275 ? codeInfo.libraryName 222 ? codeInfo.libraryName
276 : info.tagName.replaceAll(new RegExp('[-./]'), '_'); 223 : info.tagName.replaceAll(new RegExp('[-./]'), '_');
277 var header = new CodePrinter(0); 224 var header = new CodePrinter(0);
278 header.add(_header(path.basename(info.declaringFile.inputUrl.resolvedPath), 225 header.add(_header(path.basename(info.declaringFile.inputUrl.resolvedPath),
279 libraryName)); 226 libraryName));
280 emitImports(codeInfo, info, pathMapper, header); 227 emitImports(codeInfo, info, pathMapper, header);
281 header.addLine(''); 228 header.addLine('');
282 transaction.edit(0, codeInfo.directivesEnd, header); 229 transaction.edit(0, codeInfo.directivesEnd, header);
283 230
284 var mangle = cssPolyfillKind == CssPolyfillKind.MANGLED_POLYFILL;
285 var cssMapExpression = createCssSelectorsExpression(info, mangle);
286 var classBody = new CodePrinter(1) 231 var classBody = new CodePrinter(1)
287 ..add('\n') 232 ..add('\n')
288 ..addLine('/** Autogenerated from the template. */')
289 ..addLine('')
290 // TODO(terry): Remove [_css]; after migrating to getScopedCss.
291 ..addLine('autogenerated.ScopedCssMapper _css;')
292 ..addLine('')
293 ..addLine('/** This field is deprecated, use getShadowRoot instead. */')
294 ..addLine('get _root => getShadowRoot("${info.tagName}");')
295 ..add(_context.statics)
296 ..addLine('')
297 ..addLine('void initShadow() {')
298 ..addLine(hasExtends ? ' super.initShadow();' : null)
299 ..addLine(' var __root = createShadowRoot("${info.tagName}");')
300 ..addLine(' shadowRootReady(__root, "${info.tagName}");')
301 ..addLine(' setScopedCss("${info.tagName}", '
302 'new autogenerated.ScopedCssMapper($cssMapExpression));')
303 // TODO(terry): Remove assignment after migrating to getScopedCss.
304 ..addLine(' _css = getScopedCss("${info.tagName}");')
305 ..add(_context.printer)
306 ..addLine('}')
307 ..addLine('')
308 ..addLine('/** Original code from the component. */'); 233 ..addLine('/** Original code from the component. */');
309 234
310 var pos = classDecl.leftBracket.end; 235 var pos = classDecl.leftBracket.end;
311 transaction.edit(pos, pos, classBody); 236 transaction.edit(pos, pos, classBody);
312 237
313 // Emit all the code in a single printer, keeping track of source-maps. 238 // Emit all the code in a single printer, keeping track of source-maps.
314 return transaction.commit(); 239 return transaction.commit();
315 } 240 }
316 } 241 }
317 242
318 /** Generates the class corresponding to the main html page. */ 243 /** The code that will be used to bootstrap the application. */
319 class EntryPointEmitter { 244 CodePrinter generateBootstrapCode(
320 final GlobalInfo global; 245 FileInfo info, FileInfo userMainInfo, GlobalInfo global,
321 final FileInfo _fileInfo; 246 PathMapper pathMapper, CompilerOptions options) {
322 Context _context;
323 247
324 EntryPointEmitter(this._fileInfo, this.global) 248 var printer = new CodePrinter(0)
325 : _context = new Context(indent: 1); 249 ..addLine('library app_bootstrap;')
250 ..addLine('')
251 ..addLine("import 'package:polymer/polymer.dart' as polymer;");
326 252
327 CodePrinter run(PathMapper pathMapper, TextEditTransaction transaction, 253 if (userMainInfo.userCode != null) {
328 bool rewriteUrls) { 254 printer..addLine('')
255 ..addLine("import '${pathMapper.importUrlFor(info, userMainInfo)}' "
256 "as userMain;\n");
257 }
329 258
330 var filePath = _fileInfo.inputUrl.resolvedPath; 259 for (var c in global.components.values) {
260 if (c.hasConflict) continue;
261 printer.addLine("import '${pathMapper.importUrlFor(info, c)}';");
262 }
331 263
332 var codeInfo = _fileInfo.userCode; 264 printer..addLine('')
333 if (codeInfo == null) { 265 ..addLine('void main() {')
334 assert(transaction == null); 266 ..indent += 1;
335 codeInfo = new DartCodeInfo(null, null, [], 'main(){\n}', null);
336 }
337 267
338 if (transaction == null) { 268 if (userMainInfo.userCode != null) printer.addLine('userMain.main();');
339 transaction = new TextEditTransaction(codeInfo.code, codeInfo.sourceFile);
340 }
341 269
342 var libraryName = codeInfo.libraryName != null 270 for (var c in global.components.values) {
343 ? codeInfo.libraryName : _fileInfo.libraryName; 271 if (c.hasConflict) continue;
344 var header = new CodePrinter(0); 272 var tagName = escapeDartString(c.tagName);
345 header.add(_header(path.basename(filePath), libraryName)); 273 var cssMapExpression = createCssSelectorsExpression(c,
346 emitImports(codeInfo, _fileInfo, pathMapper, header, global); 274 CssPolyfillKind.of(options, c));
347 header..addLine('') 275 printer
348 ..addLine('') 276 ..addLine("polymer.setScopedCss('$tagName', $cssMapExpression);")
349 ..addLine('// Original code'); 277 ..addLine("polymer.registerPolymerElement("
350 transaction.edit(0, codeInfo.directivesEnd, header); 278 "'$tagName', () => new ${c.className}());");
279 }
351 280
352 var printer = (transaction.commit()) 281 return printer
353 ..addLine('') 282 ..indent -= 1
354 ..addLine('// Additional generated code') 283 ..addLine('}');
355 ..addLine('void init_autogenerated() {') 284 }
356 ..indent += 1;
357 285
358 for (var c in global.components.values) {
359 if (c.hasConflict) continue;
360 var tagName = escapeDartString(c.tagName);
361 286
362 printer.addLine("autogenerated.registerPolymerElement("
363 "new autogenerated.Element.html("
364 "'${escapeDartString(c.elementNode.outerHtml)}')"
365 ", () => new ${c.className}());");
366 }
367
368 return printer
369 ..add(_context.statics)
370 ..add(_context.printer)
371 ..indent -= 1
372 ..addLine('}');
373 }
374 }
375 287
376 /** 288 /**
377 * List of HTML4 elements which could have relative URL resource: 289 * List of HTML4 elements which could have relative URL resource:
378 * 290 *
379 * <body background=url>, <img src=url>, <input src=url> 291 * <body background=url>, <img src=url>, <input src=url>
380 * 292 *
381 * HTML 5: 293 * HTML 5:
382 * 294 *
383 * <audio src=url>, <command icon=url>, <embed src=url>, 295 * <audio src=url>, <command icon=url>, <embed src=url>,
384 * <source src=url>, <video poster=url> and <video src=url> 296 * <source src=url>, <video poster=url> and <video src=url>
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
444 var file = codeInfo.sourceFile; 356 var file = codeInfo.sourceFile;
445 for (var d in codeInfo.directives) { 357 for (var d in codeInfo.directives) {
446 addUnique(d.toString(), file != null ? file.location(d.offset) : null); 358 addUnique(d.toString(), file != null ? file.location(d.offset) : null);
447 } 359 }
448 } 360 }
449 361
450 final shadowDomJS = new RegExp(r'shadowdom\..*\.js', caseSensitive: false); 362 final shadowDomJS = new RegExp(r'shadowdom\..*\.js', caseSensitive: false);
451 363
452 /** Trim down the html for the main html page. */ 364 /** Trim down the html for the main html page. */
453 void transformMainHtml(Document document, FileInfo fileInfo, 365 void transformMainHtml(Document document, FileInfo fileInfo,
454 PathMapper pathMapper, bool hasCss, bool rewriteUrls, Messages messages) { 366 PathMapper pathMapper, bool hasCss, bool rewriteUrls,
367 Messages messages, GlobalInfo global) {
455 var filePath = fileInfo.inputUrl.resolvedPath; 368 var filePath = fileInfo.inputUrl.resolvedPath;
456 369
457 bool dartLoaderFound = false; 370 bool dartLoaderFound = false;
458 bool shadowDomFound = false; 371 bool shadowDomFound = false;
459 for (var tag in document.queryAll('script')) { 372 for (var tag in document.queryAll('script')) {
460 var src = tag.attributes['src']; 373 var src = tag.attributes['src'];
461 if (src != null) { 374 if (src != null) {
462 var last = src.split('/').last; 375 var last = src.split('/').last;
463 if (last == 'dart.js' || last == 'testing.js') { 376 if (last == 'dart.js' || last == 'testing.js') {
464 dartLoaderFound = true; 377 dartLoaderFound = true;
465 } else if (shadowDomJS.hasMatch(last)) { 378 } else if (shadowDomJS.hasMatch(last)) {
466 shadowDomFound = true; 379 shadowDomFound = true;
467 } 380 }
468 } 381 }
469 if (tag.attributes['type'] == 'application/dart') { 382 if (tag.attributes['type'] == 'application/dart') {
470 tag.remove(); 383 tag.remove();
471 } else if (src != null && rewriteUrls) { 384 } else if (src != null && rewriteUrls) {
472 tag.attributes["src"] = pathMapper.transformUrl(filePath, src); 385 tag.attributes["src"] = pathMapper.transformUrl(filePath, src);
473 } 386 }
474 } 387 }
388
475 for (var tag in document.queryAll('link')) { 389 for (var tag in document.queryAll('link')) {
476 var href = tag.attributes['href']; 390 var href = tag.attributes['href'];
477 var rel = tag.attributes['rel']; 391 var rel = tag.attributes['rel'];
478 if (rel == 'component' || rel == 'components' || rel == 'import') { 392 if (rel == 'component' || rel == 'components' || rel == 'import') {
479 tag.remove(); 393 tag.remove();
480 } else if (href != null && rewriteUrls && !hasCss) { 394 } else if (href != null && rewriteUrls && !hasCss) {
481 // Only rewrite URL if rewrite on and we're not CSS polyfilling. 395 // Only rewrite URL if rewrite on and we're not CSS polyfilling.
482 tag.attributes['href'] = pathMapper.transformUrl(filePath, href); 396 tag.attributes['href'] = pathMapper.transformUrl(filePath, href);
483 } 397 }
484 } 398 }
(...skipping 20 matching lines...) Expand all
505 for (var i = styles.length - 1; i > 0 ; i--) { 419 for (var i = styles.length - 1; i > 0 ; i--) {
506 styles[i].remove(); 420 styles[i].remove();
507 } 421 }
508 } 422 }
509 423
510 // TODO(jmesserly): put this in the global CSS file? 424 // TODO(jmesserly): put this in the global CSS file?
511 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# css-additions 425 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# css-additions
512 document.head.nodes.insert(0, parseFragment( 426 document.head.nodes.insert(0, parseFragment(
513 '<style>template { display: none; }</style>')); 427 '<style>template { display: none; }</style>'));
514 428
429 // Move all <element> declarations to the main HTML file
430 // TODO(sigmund): remove this once we have HTMLImports implemented.
431 for (var c in global.components.values) {
432 document.body.nodes.insert(0, new Text('\n'));
433 var fragment = c.element;
434 for (var tag in fragment.queryAll('script')) {
435 // TODO(sigmund): leave script tags around when we start using "boot.js"
436 if (tag.attributes['type'] == 'application/dart') {
437 tag.remove();
438 }
439 }
440 document.body.nodes.insert(0, fragment);
441 }
442
443
515 if (!shadowDomFound) { 444 if (!shadowDomFound) {
516 // TODO(jmesserly): we probably shouldn't add this automatically. 445 // TODO(jmesserly): we probably shouldn't add this automatically.
517 document.body.nodes.add(parseFragment('<script type="text/javascript" ' 446 document.body.nodes.add(parseFragment('<script type="text/javascript" '
518 'src="packages/shadow_dom/shadow_dom.debug.js"></script>\n')); 447 'src="packages/shadow_dom/shadow_dom.debug.js"></script>\n'));
519 } 448 }
520 if (!dartLoaderFound) { 449 if (!dartLoaderFound) {
521 // TODO(jmesserly): turn this warning on. 450 // TODO(jmesserly): turn this warning on.
522 //messages.warning('Missing script to load Dart. ' 451 //messages.warning('Missing script to load Dart. '
523 // 'Please add this line to your HTML file: $dartLoader', 452 // 'Please add this line to your HTML file: $dartLoader',
524 // document.body.sourceSpan); 453 // document.body.sourceSpan);
454 // TODO(sigmund): switch to 'boot.js'
525 document.body.nodes.add(parseFragment('<script type="text/javascript" ' 455 document.body.nodes.add(parseFragment('<script type="text/javascript" '
526 'src="packages/browser/dart.js"></script>\n')); 456 'src="packages/browser/dart.js"></script>\n'));
527 } 457 }
528 458
529 // Insert the "auto-generated" comment after the doctype, otherwise IE will 459 // Insert the "auto-generated" comment after the doctype, otherwise IE will
530 // go into quirks mode. 460 // go into quirks mode.
531 int commentIndex = 0; 461 int commentIndex = 0;
532 DocumentType doctype = find(document.nodes, (n) => n is DocumentType); 462 DocumentType doctype = find(document.nodes, (n) => n is DocumentType);
533 if (doctype != null) { 463 if (doctype != null) {
534 commentIndex = document.nodes.indexOf(doctype) + 1; 464 commentIndex = document.nodes.indexOf(doctype) + 1;
(...skipping 20 matching lines...) Expand all
555 // Auto-generated from $filename. 485 // Auto-generated from $filename.
556 // DO NOT EDIT. 486 // DO NOT EDIT.
557 $lib 487 $lib
558 import 'dart:html' as autogenerated; 488 import 'dart:html' as autogenerated;
559 import 'dart:svg' as autogenerated_svg; 489 import 'dart:svg' as autogenerated_svg;
560 import 'package:mdv/mdv.dart' as autogenerated_mdv; 490 import 'package:mdv/mdv.dart' as autogenerated_mdv;
561 import 'package:observe/observe.dart' as __observe; 491 import 'package:observe/observe.dart' as __observe;
562 import 'package:polymer/polymer.dart' as autogenerated; 492 import 'package:polymer/polymer.dart' as autogenerated;
563 """; 493 """;
564 } 494 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698