Skip to content
Snippets Groups Projects
Commit 334f8260 authored by Jesper's avatar Jesper
Browse files

Add --optimize-imports option

The --optimize-import options can be enabled to reduce the number of unused
imports in generated code.

fixes #310 (bitbucket)
parent fabff616
Branches
No related tags found
No related merge requests found
......@@ -3,6 +3,9 @@
* Added fluent interface for initializing the components of an AST node
(component setting methods return the AST node itself).
See https://bitbucket.org/jastadd/jastadd2/pull-requests/10
* Added option --optimize-imports which can be enabled to
reduce the number of unused imports in generated code.
See https://bitbucket.org/jastadd/jastadd2/issues/310/optimize-imports
2019-03-18 Jesper Öqvist <jesper.oqvist@cs.lth.se>
......
......@@ -29,6 +29,7 @@
import org.jastadd.ast.AST.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
......@@ -228,17 +229,8 @@ aspect JastAddCodeGen {
public void InterfaceDecl.jastAddGen(boolean publicModifier) {
File file = grammar().targetJavaFile(name());
try {
PrintStream stream = new PrintStream(new FileOutputStream(file));
if ( !config().license.isEmpty()) {
stream.println(config().license);
}
// TODO(joqvist): move to template.
if (!config().packageName().isEmpty()) {
stream.println("package " + config().packageName() + ";\n");
}
stream.print(grammar().genImportsList());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream stream = new PrintStream(bos, true, "UTF-8");
stream.println(docComment());
stream.println(typeDeclarationString());
......@@ -248,7 +240,22 @@ aspect JastAddCodeGen {
stream.println("}");
stream.close();
} catch (FileNotFoundException f) {
String code = new String(bos.toByteArray(), StandardCharsets.UTF_8);
PrintStream fstream = new PrintStream(new FileOutputStream(file));
if ( !config().license.isEmpty()) {
fstream.println(config().license);
}
// TODO(joqvist): move to template.
if (!config().packageName().isEmpty()) {
fstream.println("package " + config().packageName() + ";\n");
}
fstream.println(grammar().genImportsList(code));
fstream.print(code);
fstream.close();
} catch (IOException e) {
System.err.println("Could not create file " + file.getName() + " in " + file.getParent());
System.exit(1);
}
......@@ -259,24 +266,28 @@ aspect JastAddCodeGen {
public void EnumDecl.jastAddGen(boolean publicModifier) {
File file = grammar().targetJavaFile(name());
try {
PrintStream stream = new PrintStream(new FileOutputStream(file));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream stream = new PrintStream(bos, true, "UTF-8");
stream.println(Unparser.unparse(simpleNode));
stream.close();
String code = new String(bos.toByteArray(), StandardCharsets.UTF_8);
PrintStream fstream = new PrintStream(new FileOutputStream(file));
if (!config().license.isEmpty()) {
stream.println(config().license);
fstream.println(config().license);
}
// TODO(joqvist): move to template.
if (!config().packageName().isEmpty()) {
stream.println("package " + config().packageName() + ";\n");
fstream.println("package " + config().packageName() + ";\n");
}
stream.print(grammar().genImportsList());
stream.println(docComment());
stream.println(Unparser.unparse(simpleNode));
stream.close();
} catch (FileNotFoundException f) {
System.err.println("Could not create file " + file.getName() + " in " + file.getParent());
fstream.print(grammar().genImportsList(code));
fstream.println(docComment());
fstream.println(code);
fstream.close();
} catch (IOException f) {
System.err.format("Could not create file %s in %s.%n", file.getName(), file.getParent());
System.exit(1);
}
}
......@@ -284,18 +295,8 @@ aspect JastAddCodeGen {
public void ClassDecl.jastAddGen(boolean publicModifier) {
File file = grammar().targetJavaFile(name());
try {
PrintStream stream = new PrintStream(new FileOutputStream(file));
if ( !config().license.isEmpty()) {
stream.println(config().license);
}
// TODO(joqvist): move to template.
if (!config().packageName().isEmpty()) {
stream.println("package " + config().packageName() + ";\n");
}
stream.print(grammar().genImportsList());
stream.println(docComment());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream stream = new PrintStream(bos, true, "UTF-8");
stream.println(typeDeclarationString());
StringBuffer buf = new StringBuffer();
for (ClassBodyObject obj : classBodyDecls) {
......@@ -306,39 +307,38 @@ aspect JastAddCodeGen {
buf.append("\n\n");
}
stream.println(buf.toString());
emitAbstractSyns(stream);
emitInhDeclarations(stream);
stream.println("}");
stream.close();
} catch (FileNotFoundException f) {
System.err.format("Could not create file %s in %s.%n", file.getName(), file.getParent());
System.exit(1);
}
}
public void ASTDecl.jastAddGen(boolean publicModifier) {
File file = grammar().targetJavaFile(name());
try {
PrintStream stream = new PrintStream(new FileOutputStream(file));
// Insert comment notifying that this is a generated file.
stream.format("/* This file was generated with %s */%n",
JastAdd.getLongVersionString());
String code = new String(bos.toByteArray(), StandardCharsets.UTF_8);
PrintStream fstream = new PrintStream(new FileOutputStream(file));
if ( !config().license.isEmpty()) {
stream.println(config().license);
fstream.println(config().license);
}
// TODO(joqvist): move to template.
if (!config().packageName().isEmpty()) {
stream.format("package %s;%n", config().packageName());
fstream.println("package " + config().packageName() + ";\n");
}
fstream.print(grammar().genImportsList(code));
fstream.println(docComment());
fstream.println(code);
fstream.println("}");
fstream.close();
} catch (IOException f) {
System.err.format("Could not create file %s in %s.%n", file.getName(), file.getParent());
System.exit(1);
}
}
stream.print(grammar().genImportsList());
public void ASTDecl.jastAddGen(boolean publicModifier) {
File file = grammar().targetJavaFile(name());
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream stream = new PrintStream(bos, true, "UTF-8");
stream.println(docComment());
stream.print("public ");
if (hasAbstract()) {
stream.print("abstract ");
......@@ -363,11 +363,30 @@ aspect JastAddCodeGen {
}
stream.print(jastAddImplementsList());
stream.println(" {");
jastAddAttributes(stream);
stream.println("}");
stream.close();
} catch (FileNotFoundException f) {
String code = new String(bos.toByteArray(), StandardCharsets.UTF_8);
PrintStream fstream = new PrintStream(new FileOutputStream(file));
// Insert comment notifying that this is a generated file.
fstream.format("/* This file was generated with %s */%n",
JastAdd.getLongVersionString());
if (!config().license.isEmpty()) {
fstream.println(config().license);
}
// TODO(joqvist): move to template.
if (!config().packageName().isEmpty()) {
fstream.format("package %s;%n", config().packageName());
}
fstream.print(grammar().genImportsList(code));
fstream.println(docComment());
fstream.println(code);
fstream.println("}");
fstream.close();
} catch (IOException f) {
System.err.format("Could not create file %s in %s%n", file.getName(), file.getParent());
System.exit(1);
}
......
/* Copyright (c) 2005-2016, The JastAdd Team
/* Copyright (c) 2005-2019, The JastAdd Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
......@@ -118,32 +118,134 @@ aspect JragCodeGen {
throw new Error("unhandled cache mode");
}
public class ImportDecl {
public final String modifier;
public final String name;
public String Grammar.genImportsList() {
Set imports = new LinkedHashSet();
public ImportDecl(String modifier, String name) {
this.modifier = modifier;
this.name = name;
}
public ImportDecl(String name) {
this("", name);
}
@Override public int hashCode() {
return getClass().hashCode() ^ modifier.hashCode() ^ name.hashCode();
}
@Override public boolean equals(Object other) {
if (other == null || getClass() != other.getClass()) {
return false;
}
ImportDecl o = (ImportDecl) other;
return o.name.equals(name) && o.modifier.equals(modifier);
}
@Override public String toString() {
return String.format("import%s %s;", modifier, name);
}
}
public class MultiImportDecl extends ImportDecl {
public MultiImportDecl(String modifier, String name) {
super(modifier, name);
}
public MultiImportDecl(String name) {
this("", name);
}
}
class ImportSet {
public final Map<String, Node> all = new HashMap<String, Node>();
public final Map<String, Node> unmatched = new HashMap<String, Node>();
static class Node {
public final Collection<ImportDecl> decls = new ArrayList<ImportDecl>();
public boolean matched = false;
}
public ImportSet(Collection<ImportDecl> imports) {
for (ImportDecl decl : imports) {
if (decl instanceof MultiImportDecl) {
continue;
}
int i = decl.name.lastIndexOf('.');
String name = decl.name.substring(i+1);
Node node = all.get(name);
if (node == null) {
node = new Node();
all.put(name, node);
unmatched.put(name, node);
}
node.decls.add(decl);
}
}
}
public Set<ImportDecl> Grammar.importDecls() {
Set<ImportDecl> imports = new LinkedHashSet<ImportDecl>();
if (config().concurrentEval()) {
// Extra import declarations needed for concurrent code generation.
imports.add("import java.util.concurrent.atomic.AtomicInteger;");
imports.add("import java.util.concurrent.atomic.AtomicReference;");
imports.add("import java.util.concurrent.Future;");
imports.add("import java.util.concurrent.Executors;");
imports.add("import java.util.concurrent.ExecutorService;");
imports.add("import java.util.concurrent.ExecutionException;");
imports.add("import java.util.concurrent.Callable;");
imports.add("import java.util.concurrent.ConcurrentMap;");
imports.add("import java.util.concurrent.ConcurrentHashMap;");
imports.add("import java.util.ArrayList;");
imports.add("import java.util.LinkedList;");
imports.add("import java.util.Collection;");
imports.add(new ImportDecl("java.util.concurrent.atomic.AtomicInteger"));
imports.add(new ImportDecl("java.util.concurrent.atomic.AtomicReference"));
imports.add(new ImportDecl("java.util.concurrent.Future"));
imports.add(new ImportDecl("java.util.concurrent.Executors"));
imports.add(new ImportDecl("java.util.concurrent.ExecutorService"));
imports.add(new ImportDecl("java.util.concurrent.ExecutionException"));
imports.add(new ImportDecl("java.util.concurrent.Callable"));
imports.add(new ImportDecl("java.util.concurrent.ConcurrentMap"));
imports.add(new ImportDecl("java.util.concurrent.ConcurrentHashMap"));
imports.add(new ImportDecl("java.util.ArrayList"));
imports.add(new ImportDecl("java.util.LinkedList"));
imports.add(new ImportDecl("java.util.Collection"));
}
for (org.jastadd.jrag.AST.ASTCompilationUnit u : compilationUnits) {
imports.addAll(Unparser.getImports(u));
}
return imports;
}
public String Grammar.genImportsList(String code) {
Set<ImportDecl> imports = importDecls();
ImportSet set = new ImportSet(imports);
boolean optimize = config().optimizeImports.value();
if (optimize) {
for (int i = 0; i < code.length(); ++i) {
if (Character.isJavaIdentifierStart(code.charAt(i))) {
int end = i+1;
while (end < code.length() && Character.isJavaIdentifierPart(code.charAt(end))) {
end += 1;
}
String id = code.substring(i, end);
if (set.unmatched.containsKey(id)) {
set.unmatched.get(id).matched = true;
set.unmatched.remove(id);
if (set.unmatched.isEmpty()) {
break;
}
}
i = end-1;
}
}
}
StringBuilder buf = new StringBuilder();
for (Iterator iter = imports.iterator(); iter.hasNext(); ) {
buf.append(iter.next());
for (ImportDecl decl : imports) {
if (decl instanceof MultiImportDecl) {
buf.append(decl);
buf.append('\n');
}
}
for (ImportSet.Node node : set.all.values()) {
if (!optimize || node.matched) {
for (ImportDecl decl : node.decls) {
buf.append(decl);
buf.append('\n');
}
}
}
return buf.toString();
}
......
......@@ -408,6 +408,10 @@ public class Configuration {
.templateVariable("EmptyContainerSingletons")
.nonStandard();
public final Option<Boolean> optimizeImports = new FlagOption("optimize-imports",
"remove unused imports in generated code")
.nonStandard();
/**
* Indicates if there were unknown command-line options
*/
......@@ -494,6 +498,9 @@ public class Configuration {
allOptions.add(numThreadsOption);
allOptions.add(concurrentMap);
// New since 2.3.4
allOptions.add(optimizeImports);
// Deprecated in 2.1.5.
allOptions.add(doxygenOption);
allOptions.add(cacheAllOption);
......
/* Copyright (c) 2005-2013, The JastAdd Team
/* Copyright (c) 2005-2019, The JastAdd Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
......@@ -298,6 +298,10 @@ public class JastAddTask extends Task {
setOption(config.emptyContainerSingletons, enable);
}
public void setOptimizeImports(boolean enable) {
setOption(config.optimizeImports, enable);
}
@Override
public void execute() throws BuildException {
System.err.println("generating node types and weaving aspects");
......
......@@ -4,18 +4,20 @@ import java.util.Set;
import java.util.LinkedHashSet;
import org.jastadd.jrag.AST.*;
import org.jastadd.ast.AST.ImportDecl;
import org.jastadd.ast.AST.MultiImportDecl;
public class Unparser implements JragParserVisitor {
public static Set getImports(ASTCompilationUnit self) {
Set imports = new LinkedHashSet();
public static Set<ImportDecl> getImports(ASTCompilationUnit self) {
Set<ImportDecl> imports = new LinkedHashSet<ImportDecl>();
for (int i = 0; i < self.jjtGetNumChildren(); i++) {
Unparser.getImports((SimpleNode) self.jjtGetChild(i), imports);
}
return imports;
}
public static void getImports(SimpleNode self, Set imports) {
public static void getImports(SimpleNode self, Set<ImportDecl> imports) {
if (self instanceof ASTImportDeclaration) {
Token t = new Token();
t.next = self.firstToken;
......@@ -27,7 +29,18 @@ public class Unparser implements JragParserVisitor {
}
buf.append(Util.addUnicodeEscapes(t.image));
}
imports.add(buf.toString().trim());
String decl = buf.toString().trim();
String modifier = "";
if (decl.startsWith("import static")) {
modifier = " static";
}
String name = decl.substring(7 + modifier.length());
name = name.substring(0, name.length()-1);
if (name.endsWith(".*")) {
imports.add(new MultiImportDecl(modifier, name));
} else {
imports.add(new ImportDecl(modifier, name));
}
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment