comparison graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/codewriter/AbstractCodeWriter.java @ 10597:79041ab43660

Truffle-DSL: API-change: Renamed truffle.api.codegen to truffle.api.dsl for all projects and packages.
author Christian Humer <christian.humer@gmail.com>
date Mon, 01 Jul 2013 20:58:32 +0200
parents
children 58f09779319c
comparison
equal deleted inserted replaced
10596:f43eb2f1bbbc 10597:79041ab43660
1 /*
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.truffle.dsl.processor.codewriter;
24
25 import static com.oracle.truffle.dsl.processor.Utils.*;
26
27 import java.io.*;
28 import java.util.*;
29
30 import javax.lang.model.element.*;
31 import javax.lang.model.type.*;
32 import javax.lang.model.util.*;
33
34 import com.oracle.truffle.dsl.processor.*;
35 import com.oracle.truffle.dsl.processor.ast.*;
36
37 public abstract class AbstractCodeWriter extends CodeElementScanner<Void, Void> {
38
39 private static final int LINE_LENGTH = 200;
40 private static final int LINE_WRAP_INDENTS = 3;
41 private static final String IDENT_STRING = " ";
42 private static final String LN = "\n"; /* unix style */
43
44 protected Writer writer;
45 private int indent;
46 private boolean newLine;
47 private int lineLength;
48 private boolean lineWrapping = false;
49
50 private OrganizedImports imports;
51
52 public void visitCompilationUnit(CodeCompilationUnit e) {
53 for (TypeElement clazz : e.getEnclosedElements()) {
54 clazz.accept(this, null);
55 }
56 }
57
58 protected abstract Writer createWriter(CodeTypeElement clazz) throws IOException;
59
60 @Override
61 public Void visitType(CodeTypeElement e, Void p) {
62 if (e.isTopLevelClass()) {
63 Writer w = null;
64 try {
65 imports = OrganizedImports.organize(e);
66 w = new TrimTrailingSpaceWriter(createWriter(e));
67 writer = w;
68 writeRootClass(e);
69 } catch (IOException ex) {
70 throw new RuntimeException(ex);
71 } finally {
72 if (w != null) {
73 try {
74 w.close();
75 } catch (Throwable e1) {
76 // see eclipse bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=361378
77 // TODO temporary suppress errors on close.
78 }
79 }
80 writer = null;
81 }
82 } else {
83 writeClassImpl(e);
84 }
85 return null;
86 }
87
88 private void writeRootClass(CodeTypeElement e) {
89 writeHeader();
90 write("package ").write(e.getPackageName()).write(";").writeLn();
91 writeEmptyLn();
92
93 Set<CodeImport> generateImports = imports.generateImports();
94 List<CodeImport> typeImports = new ArrayList<>();
95 List<CodeImport> staticImports = new ArrayList<>();
96
97 for (CodeImport codeImport : generateImports) {
98 if (codeImport.isStaticImport()) {
99 staticImports.add(codeImport);
100 } else {
101 typeImports.add(codeImport);
102 }
103 }
104 Collections.sort(typeImports);
105 Collections.sort(staticImports);
106
107 for (CodeImport imp : staticImports) {
108 imp.accept(this, null);
109 writeLn();
110 }
111 if (!staticImports.isEmpty()) {
112 writeEmptyLn();
113 }
114
115 for (CodeImport imp : typeImports) {
116 imp.accept(this, null);
117 writeLn();
118 }
119 if (!typeImports.isEmpty()) {
120 writeEmptyLn();
121 }
122
123 writeClassImpl(e);
124 }
125
126 private String useImport(Element enclosedType, TypeMirror type) {
127 if (imports != null) {
128 return imports.createTypeReference(enclosedType, type);
129 } else {
130 return Utils.getSimpleName(type);
131 }
132 }
133
134 private void writeClassImpl(CodeTypeElement e) {
135 for (AnnotationMirror annotation : e.getAnnotationMirrors()) {
136 visitAnnotation(e, annotation);
137 writeLn();
138 }
139
140 writeModifiers(e.getModifiers());
141 if (e.getKind() == ElementKind.ENUM) {
142 write("enum ");
143 } else {
144 write("class ");
145 }
146 write(e.getSimpleName());
147 if (e.getSuperclass() != null && !getQualifiedName(e.getSuperclass()).equals("java.lang.Object")) {
148 write(" extends ").write(useImport(e, e.getSuperclass()));
149 }
150 if (e.getImplements().size() > 0) {
151 write(" implements ");
152 for (int i = 0; i < e.getImplements().size(); i++) {
153 write(useImport(e, e.getImplements().get(i)));
154 if (i < e.getImplements().size() - 1) {
155 write(", ");
156 }
157 }
158 }
159
160 write(" {").writeLn();
161 writeEmptyLn();
162 indent(1);
163
164 List<VariableElement> staticFields = getStaticFields(e);
165 List<VariableElement> instanceFields = getInstanceFields(e);
166
167 for (int i = 0; i < staticFields.size(); i++) {
168 VariableElement field = staticFields.get(i);
169 field.accept(this, null);
170 if (e.getKind() == ElementKind.ENUM && i < staticFields.size() - 1) {
171 write(",");
172 writeLn();
173 } else {
174 write(";");
175 writeLn();
176 }
177 }
178
179 if (staticFields.size() > 0) {
180 writeEmptyLn();
181 }
182
183 for (VariableElement field : instanceFields) {
184 field.accept(this, null);
185 write(";");
186 writeLn();
187 }
188 if (instanceFields.size() > 0) {
189 writeEmptyLn();
190 }
191
192 for (ExecutableElement method : ElementFilter.constructorsIn(e.getEnclosedElements())) {
193 method.accept(this, null);
194 }
195
196 for (ExecutableElement method : getInstanceMethods(e)) {
197 method.accept(this, null);
198 }
199
200 for (ExecutableElement method : getStaticMethods(e)) {
201 method.accept(this, null);
202 }
203
204 for (TypeElement clazz : e.getInnerClasses()) {
205 clazz.accept(this, null);
206 }
207
208 dedent(1);
209 write("}");
210 writeEmptyLn();
211 }
212
213 private static List<VariableElement> getStaticFields(CodeTypeElement clazz) {
214 List<VariableElement> staticFields = new ArrayList<>();
215 for (VariableElement field : clazz.getFields()) {
216 if (field.getModifiers().contains(Modifier.STATIC)) {
217 staticFields.add(field);
218 }
219 }
220 return staticFields;
221 }
222
223 private static List<VariableElement> getInstanceFields(CodeTypeElement clazz) {
224 List<VariableElement> instanceFields = new ArrayList<>();
225 for (VariableElement field : clazz.getFields()) {
226 if (!field.getModifiers().contains(Modifier.STATIC)) {
227 instanceFields.add(field);
228 }
229 }
230 return instanceFields;
231 }
232
233 private static List<ExecutableElement> getStaticMethods(CodeTypeElement clazz) {
234 List<ExecutableElement> staticMethods = new ArrayList<>();
235 for (ExecutableElement method : clazz.getMethods()) {
236 if (method.getModifiers().contains(Modifier.STATIC)) {
237 staticMethods.add(method);
238 }
239 }
240 return staticMethods;
241 }
242
243 private static List<ExecutableElement> getInstanceMethods(CodeTypeElement clazz) {
244 List<ExecutableElement> instanceMethods = new ArrayList<>();
245 for (ExecutableElement method : clazz.getMethods()) {
246 if (!method.getModifiers().contains(Modifier.STATIC)) {
247 instanceMethods.add(method);
248 }
249 }
250 return instanceMethods;
251 }
252
253 @Override
254 public Void visitVariable(VariableElement f, Void p) {
255 Element parent = f.getEnclosingElement();
256
257 for (AnnotationMirror annotation : f.getAnnotationMirrors()) {
258 visitAnnotation(f, annotation);
259 write(" ");
260 }
261
262 CodeTree init = null;
263 if (f instanceof CodeVariableElement) {
264 init = ((CodeVariableElement) f).getInit();
265 }
266
267 if (parent.getKind() == ElementKind.ENUM && f.getModifiers().contains(Modifier.STATIC)) {
268 write(f.getSimpleName());
269 if (init != null) {
270 if (init != null) {
271 write("(");
272 init.acceptCodeElementScanner(this, p);
273 write(")");
274 }
275 }
276 } else {
277 writeModifiers(f.getModifiers());
278 write(useImport(f, f.asType()));
279
280 if (f.getEnclosingElement().getKind() == ElementKind.METHOD) {
281 ExecutableElement method = (ExecutableElement) f.getEnclosingElement();
282 if (method.isVarArgs() && method.getParameters().indexOf(f) == method.getParameters().size() - 1) {
283 write("...");
284 }
285 }
286
287 write(" ");
288 write(f.getSimpleName());
289 if (init != null) {
290 write(" = ");
291 init.acceptCodeElementScanner(this, p);
292 }
293 }
294 return null;
295 }
296
297 public void visitAnnotation(Element enclosedElement, AnnotationMirror e) {
298 write("@").write(useImport(enclosedElement, e.getAnnotationType()));
299
300 if (!e.getElementValues().isEmpty()) {
301 write("(");
302 final ExecutableElement defaultElement = findExecutableElement(e.getAnnotationType(), "value");
303
304 Map<? extends ExecutableElement, ? extends AnnotationValue> values = e.getElementValues();
305 if (defaultElement != null && values.size() == 1 && values.get(defaultElement) != null) {
306 visitAnnotationValue(enclosedElement, values.get(defaultElement));
307 } else {
308 Set<? extends ExecutableElement> methodsSet = values.keySet();
309 List<ExecutableElement> methodsList = new ArrayList<>();
310 for (ExecutableElement method : methodsSet) {
311 if (values.get(method) == null) {
312 continue;
313 }
314 methodsList.add(method);
315 }
316
317 Collections.sort(methodsList, new Comparator<ExecutableElement>() {
318
319 @Override
320 public int compare(ExecutableElement o1, ExecutableElement o2) {
321 return o1.getSimpleName().toString().compareTo(o2.getSimpleName().toString());
322 }
323 });
324
325 for (int i = 0; i < methodsList.size(); i++) {
326 ExecutableElement method = methodsList.get(i);
327 AnnotationValue value = values.get(method);
328 write(method.getSimpleName().toString());
329 write(" = ");
330 visitAnnotationValue(enclosedElement, value);
331
332 if (i < methodsList.size() - 1) {
333 write(", ");
334 }
335 }
336 }
337
338 write(")");
339 }
340 }
341
342 public void visitAnnotationValue(Element enclosedElement, AnnotationValue e) {
343 e.accept(new AnnotationValueWriterVisitor(enclosedElement), null);
344 }
345
346 private class AnnotationValueWriterVisitor extends AbstractAnnotationValueVisitor7<Void, Void> {
347
348 private final Element enclosedElement;
349
350 public AnnotationValueWriterVisitor(Element enclosedElement) {
351 this.enclosedElement = enclosedElement;
352 }
353
354 @Override
355 public Void visitBoolean(boolean b, Void p) {
356 write(Boolean.toString(b));
357 return null;
358 }
359
360 @Override
361 public Void visitByte(byte b, Void p) {
362 write(Byte.toString(b));
363 return null;
364 }
365
366 @Override
367 public Void visitChar(char c, Void p) {
368 write(Character.toString(c));
369 return null;
370 }
371
372 @Override
373 public Void visitDouble(double d, Void p) {
374 write(Double.toString(d));
375 return null;
376 }
377
378 @Override
379 public Void visitFloat(float f, Void p) {
380 write(Float.toString(f));
381 return null;
382 }
383
384 @Override
385 public Void visitInt(int i, Void p) {
386 write(Integer.toString(i));
387 return null;
388 }
389
390 @Override
391 public Void visitLong(long i, Void p) {
392 write(Long.toString(i));
393 return null;
394 }
395
396 @Override
397 public Void visitShort(short s, Void p) {
398 write(Short.toString(s));
399 return null;
400 }
401
402 @Override
403 public Void visitString(String s, Void p) {
404 write("\"");
405 write(s);
406 write("\"");
407 return null;
408 }
409
410 @Override
411 public Void visitType(TypeMirror t, Void p) {
412 write(useImport(enclosedElement, t));
413 write(".class");
414 return null;
415 }
416
417 @Override
418 public Void visitEnumConstant(VariableElement c, Void p) {
419 write(useImport(enclosedElement, c.asType()));
420 write(".");
421 write(c.getSimpleName().toString());
422 return null;
423 }
424
425 @Override
426 public Void visitAnnotation(AnnotationMirror a, Void p) {
427 AbstractCodeWriter.this.visitAnnotation(enclosedElement, a);
428 return null;
429 }
430
431 @Override
432 public Void visitArray(List<? extends AnnotationValue> vals, Void p) {
433 write("{");
434 for (int i = 0; i < vals.size(); i++) {
435 AnnotationValue value = vals.get(i);
436 AbstractCodeWriter.this.visitAnnotationValue(enclosedElement, value);
437 if (i < vals.size() - 1) {
438 write(", ");
439 }
440 }
441 write("}");
442 return null;
443 }
444 }
445
446 public ExecutableElement findExecutableElement(DeclaredType type, String name) {
447 List<? extends ExecutableElement> elements = ElementFilter.methodsIn(type.asElement().getEnclosedElements());
448 for (ExecutableElement executableElement : elements) {
449 if (executableElement.getSimpleName().toString().equals(name)) {
450 return executableElement;
451 }
452 }
453 return null;
454 }
455
456 @Override
457 public void visitImport(CodeImport e, Void p) {
458 if (e.isStaticImport()) {
459 write("import static ").write(e.getImportString()).write(";");
460 } else {
461 write("import ").write(e.getImportString()).write(";");
462 }
463 }
464
465 @Override
466 public Void visitExecutable(CodeExecutableElement e, Void p) {
467 for (AnnotationMirror annotation : e.getAnnotationMirrors()) {
468 visitAnnotation(e, annotation);
469 writeLn();
470 }
471
472 writeModifiers(e.getModifiers());
473
474 if (e.getReturnType() != null) {
475 write(useImport(e, e.getReturnType()));
476 write(" ");
477 }
478 write(e.getSimpleName());
479 write("(");
480
481 for (int i = 0; i < e.getParameters().size(); i++) {
482 VariableElement param = e.getParameters().get(i);
483 param.accept(this, p);
484 if (i < e.getParameters().size() - 1) {
485 write(", ");
486 }
487 }
488 write(")");
489
490 List<TypeMirror> throwables = e.getThrownTypes();
491 if (throwables.size() > 0) {
492 write(" throws ");
493 for (int i = 0; i < throwables.size(); i++) {
494 write(useImport(e, throwables.get(i)));
495 if (i < throwables.size() - 1) {
496 write(", ");
497 }
498 }
499 }
500
501 if (e.getModifiers().contains(Modifier.ABSTRACT)) {
502 writeLn(";");
503 } else if (e.getBodyTree() != null) {
504 writeLn(" {");
505 indent(1);
506 e.getBodyTree().acceptCodeElementScanner(this, p);
507 dedent(1);
508 writeLn("}");
509 } else if (e.getBody() != null) {
510 write(" {");
511 write(e.getBody());
512 writeLn("}");
513 } else {
514 writeLn("{ }");
515 }
516 writeEmptyLn();
517 return null;
518 }
519
520 @Override
521 public void visitTree(CodeTree e, Void p) {
522 CodeTreeKind kind = e.getCodeKind();
523
524 switch (kind) {
525 case COMMA_GROUP:
526 List<CodeTree> children = e.getEnclosedElements();
527 for (int i = 0; i < children.size(); i++) {
528 children.get(i).acceptCodeElementScanner(this, p);
529 if (i < e.getEnclosedElements().size() - 1) {
530 write(", ");
531 }
532 }
533 break;
534 case GROUP:
535 for (CodeTree tree : e.getEnclosedElements()) {
536 tree.acceptCodeElementScanner(this, p);
537 }
538 break;
539 case INDENT:
540 indent(1);
541 for (CodeTree tree : e.getEnclosedElements()) {
542 tree.acceptCodeElementScanner(this, p);
543 }
544 dedent(1);
545 break;
546 case NEW_LINE:
547 writeLn();
548 break;
549 case STRING:
550 if (e.getString() != null) {
551 write(e.getString());
552 } else {
553 write("null");
554 }
555 break;
556 case STATIC_FIELD_REFERENCE:
557 if (e.getString() != null) {
558 write(imports.createStaticFieldReference(e, e.getType(), e.getString()));
559 } else {
560 write("null");
561 }
562 break;
563 case STATIC_METHOD_REFERENCE:
564 if (e.getString() != null) {
565 write(imports.createStaticMethodReference(e, e.getType(), e.getString()));
566 } else {
567 write("null");
568 }
569 break;
570 case TYPE:
571 write(useImport(e, e.getType()));
572 break;
573 default:
574 assert false;
575 return;
576 }
577 }
578
579 protected void writeHeader() {
580 // default implementation does nothing
581 }
582
583 private void writeModifiers(Set<Modifier> modifiers) {
584 if (modifiers != null) {
585 for (Modifier modifier : modifiers) {
586 write(modifier.toString());
587 write(" ");
588 }
589 }
590 }
591
592 protected void indent(int count) {
593 indent += count;
594 }
595
596 protected void dedent(int count) {
597 indent -= count;
598 }
599
600 protected void writeLn() {
601 writeLn("");
602 }
603
604 protected void writeLn(String text) {
605 write(text);
606 write(LN);
607 lineLength = 0;
608 newLine = true;
609 if (lineWrapping) {
610 dedent(LINE_WRAP_INDENTS);
611 lineWrapping = false;
612 }
613 lineWrapping = false;
614 }
615
616 protected void writeEmptyLn() {
617 writeLn();
618 }
619
620 private AbstractCodeWriter write(Name name) {
621 return write(name.toString());
622 }
623
624 private AbstractCodeWriter write(String m) {
625 try {
626 lineLength += m.length();
627 if (newLine && m != LN) {
628 writeIndent();
629 newLine = false;
630 }
631 if (lineLength > LINE_LENGTH && m.length() > 0) {
632 char firstChar = m.charAt(0);
633 if (Character.isAlphabetic(firstChar)) {
634 if (!lineWrapping) {
635 indent(LINE_WRAP_INDENTS);
636 }
637 lineWrapping = true;
638 lineLength = 0;
639 write(LN);
640 writeIndent();
641 }
642 }
643 writer.write(m);
644 } catch (IOException e) {
645 throw new RuntimeException(e);
646 }
647 return this;
648 }
649
650 private void writeIndent() throws IOException {
651 for (int i = 0; i < indent; i++) {
652 lineLength += IDENT_STRING.length();
653 writer.write(IDENT_STRING);
654 }
655 }
656
657 private static class TrimTrailingSpaceWriter extends Writer {
658
659 private final Writer delegate;
660 private final StringBuilder buffer = new StringBuilder();
661
662 public TrimTrailingSpaceWriter(Writer delegate) {
663 this.delegate = delegate;
664 }
665
666 @Override
667 public void close() throws IOException {
668 this.delegate.close();
669 }
670
671 @Override
672 public void flush() throws IOException {
673 this.delegate.flush();
674 }
675
676 @Override
677 public void write(char[] cbuf, int off, int len) throws IOException {
678 buffer.append(cbuf, off, len);
679 int newLinePoint = buffer.indexOf(LN);
680
681 if (newLinePoint != -1) {
682 String lhs = trimTrailing(buffer.substring(0, newLinePoint));
683 delegate.write(lhs);
684 delegate.write(LN);
685 buffer.delete(0, newLinePoint + 1);
686 }
687 }
688
689 private static String trimTrailing(String s) {
690 int cut = 0;
691 for (int i = s.length() - 1; i >= 0; i--) {
692 if (Character.isWhitespace(s.charAt(i))) {
693 cut++;
694 } else {
695 break;
696 }
697 }
698 if (cut > 0) {
699 return s.substring(0, s.length() - cut);
700 }
701 return s;
702 }
703 }
704
705 }