Skip to content

Commit

Permalink
Implement generating for multiple CompilationUnits in ObjectiveCHeade…
Browse files Browse the repository at this point in the history
…rGenerator and ObjectiveCImplementationGenerator.

	Change on 2015/03/17 by mthvedt <mthvedt@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=88850171
  • Loading branch information
mthvedt authored and tomball committed Mar 18, 2015
1 parent 2361d44 commit b349105
Show file tree
Hide file tree
Showing 19 changed files with 539 additions and 146 deletions.
Expand Up @@ -177,12 +177,34 @@ private void processJarFile(String filename) {
}
}

private GenerationUnit createGenerationUnit(String sourceName, String outputPath) {
GenerationUnit unit = new GenerationUnit(sourceName);
unit.setOutputPath(outputPath);
GenerationUnit prev = unitMap.put(outputPath, unit);
assert prev == null;
units.add(unit);
return unit;
}

/**
* Adds the given InputFile to this GenerationBatch,
* creating GenerationUnits and inferring unit names/output paths as necessary.
*/
protected void addSource(InputFile file) {
if (Options.useSourceDirectories()) {
GenerationUnit unit;

if (Options.combineSourceJars()) {
String outputPath = file.getContainingPath();
// If there's no separator, this results in 0
int lastPathComponentIndex = outputPath.lastIndexOf(File.separatorChar) + 1;
assert outputPath.matches(".*j(ar|ava)");
outputPath = outputPath.substring(lastPathComponentIndex, outputPath.lastIndexOf("."));
unit = unitMap.get(outputPath);
if (unit == null) {
unit = createGenerationUnit(file.getContainingPath(), outputPath);
unit.setName(NameTable.camelCasePath(outputPath));
}
} else if (Options.useSourceDirectories()) {
String outputPath = file.getUnitName();
outputPath = outputPath.substring(0, outputPath.lastIndexOf(".java"));
if (unitMap.containsKey(outputPath)) {
Expand All @@ -192,17 +214,30 @@ protected void addSource(InputFile file) {
+ file.getUnitName() + " duplicated on path " + file.getPath());
return;
}
GenerationUnit unit = new GenerationUnit(file.getPath());
// TranslationProcessor will figure out the name
unit.addInputFile(file);
unit.setOutputPath(outputPath);
unitMap.put(outputPath, unit);
units.add(unit);
unit = createGenerationUnit(file.getPath(), outputPath);
} else {
// GenerationUnit with singleton file and unknown name/output path.
GenerationUnit unit = new GenerationUnit(file.getPath());
unit.addInputFile(file);
// GenerationUnit with singleton file and not-yet-known name and output path.
unit = new GenerationUnit(file.getPath());
units.add(unit);
}

unit.addInputFile(file);
}

/**
* Testing method. Add a source forcing some output path.
* Sets the 'sourcefile' to the given output path plus ".testfile".
* In normal operation, addSource consults {@link Options} and determines the correct
* output path.
*/
@VisibleForTesting
void addSource(InputFile file, String outputPath) {
GenerationUnit unit = unitMap.get(outputPath);
if (unit == null) {
unit = createGenerationUnit(outputPath + ".testfile", outputPath);
// Get a nice looking name for testing purposes
unit.setName(NameTable.camelCasePath(outputPath));
}
unit.addInputFile(file);
}
}
23 changes: 19 additions & 4 deletions translator/src/main/java/com/google/devtools/j2objc/Options.java
Expand Up @@ -138,6 +138,9 @@ public static enum OutputStyleOption {
/** Use the relative directory of the input file. */
SOURCE,

/** Use the relative directory of the input file, even (especially) if it is a jar. */
SOURCE_COMBINED,

/** Don't use a relative directory. */
NONE
}
Expand Down Expand Up @@ -258,6 +261,8 @@ public static String[] load(String[] args) throws IOException {
outputStyle = OutputStyleOption.NONE;
} else if (arg.equals("--preserve-full-paths")) {
outputStyle = OutputStyleOption.SOURCE;
} else if (arg.equals("-XcombineJars")) {
outputStyle = OutputStyleOption.SOURCE_COMBINED;
} else if (arg.equals("-use-arc")) {
checkMemoryManagementOption(MemoryManagementOption.ARC);
} else if (arg.equals("-g")) {
Expand Down Expand Up @@ -330,7 +335,12 @@ public static String[] load(String[] args) throws IOException {

if (shouldPreProcess() && buildClosure) {
ErrorUtil.error("--build-closure is not supported with "
+ "--use-header-mappings or --preserve-full-paths");
+ "--use-header-mappings, -XcombineJars or --preserve-full-paths");
}

if (outputStyle == OutputStyleOption.SOURCE_COMBINED && segmentedHeaders) {
// TODO(mthvedt): Implement -XcombineJars support for segmented headers.
ErrorUtil.error("--segmented-headers not yet supported with -XcombineJars");
}

if (memoryManagementOption == null) {
Expand Down Expand Up @@ -510,10 +520,15 @@ public static boolean usePackageDirectories() {
* which the input files were read.
*/
public static boolean useSourceDirectories() {
return outputStyle == OutputStyleOption.SOURCE;
return outputStyle == OutputStyleOption.SOURCE
|| outputStyle == OutputStyleOption.SOURCE_COMBINED;
}

public static boolean combineSourceJars() {
return outputStyle == OutputStyleOption.SOURCE_COMBINED;
}

public static void setPackageDirectories(OutputStyleOption style) {
public static void setOutputStyle(OutputStyleOption style) {
outputStyle = style;
}

Expand Down Expand Up @@ -804,6 +819,6 @@ public static void resetHidePrivateMembers() {
}

public static boolean shouldPreProcess() {
return Options.getHeaderMappingFiles() != null && Options.useSourceDirectories();
return Options.useSourceDirectories() || Options.combineSourceJars();
}
}
Expand Up @@ -172,6 +172,7 @@ protected void processCompiledGenerationUnit(GenerationUnit unit) {

boolean isDead = true;
for (CompilationUnit compUnit : unit.getCompilationUnits()) {
compUnit.setGenerationContext();
applyMutations(compUnit, deadCodeMap, ticker);
ticker.tick("Tree mutations for " + compUnit.getMainTypeName());
isDead &= compUnit.getTypes().isEmpty()
Expand Down
Expand Up @@ -17,6 +17,7 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.devtools.j2objc.file.InputFile;
import com.google.devtools.j2objc.types.Types;
import com.google.devtools.j2objc.util.NameTable;

import org.eclipse.jdt.core.dom.ASTNode;
Expand All @@ -41,11 +42,16 @@ public class CompilationUnit extends TreeNode {
ChildList.create(NativeDeclaration.class, this);
private final ChildList<AbstractTypeDeclaration> types =
ChildList.create(AbstractTypeDeclaration.class, this);
private final NameTable nameTable;
private final Types typesService;

public CompilationUnit(
org.eclipse.jdt.core.dom.CompilationUnit jdtNode, InputFile inputFile,
String mainTypeName, String source) {
super(jdtNode);
this.nameTable = NameTable.newNameTable();
this.typesService = Types.newTypes(jdtNode);
setGenerationContext();
this.inputFile = Preconditions.checkNotNull(inputFile);
Preconditions.checkNotNull(mainTypeName);
if (mainTypeName.endsWith(NameTable.PACKAGE_INFO_FILE_NAME)) {
Expand Down Expand Up @@ -74,8 +80,23 @@ public CompilationUnit(
}
}

/**
* Sets the mutable global state that's particular to each CompilationUnit.
* Many of the operations in the ast, gen, translate, types, and util
* packages require these to be set to a given CompilationUnit before operations are performed
* on that unit.
* Using this method concurrently with NameTable/Types
* is incredibly, unbelievably, astoundingly not thread safe.
*/
public void setGenerationContext() {
typesService.setInstance();
nameTable.setInstance();
}

public CompilationUnit(CompilationUnit other) {
super(other);
nameTable = other.nameTable;
typesService = other.typesService;
inputFile = other.getInputFile();
mainTypeName = other.getMainTypeName();
source = other.getSource();
Expand Down
Expand Up @@ -30,8 +30,10 @@
import com.google.devtools.j2objc.ast.EnumConstantDeclaration;
import com.google.devtools.j2objc.ast.EnumDeclaration;
import com.google.devtools.j2objc.ast.FunctionDeclaration;
import com.google.devtools.j2objc.ast.Javadoc;
import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.NativeDeclaration;
import com.google.devtools.j2objc.ast.PackageDeclaration;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.Type;
import com.google.devtools.j2objc.ast.TypeDeclaration;
Expand All @@ -42,12 +44,11 @@
import com.google.devtools.j2objc.util.BindingUtil;
import com.google.devtools.j2objc.util.NameTable;

import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.Modifier;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.*;

/**
* Generates Objective-C header files from compilation units.
Expand All @@ -57,8 +58,7 @@
public class ObjectiveCHeaderGenerator extends ObjectiveCSourceFileGenerator {

/**
* Generate an Objective-C header file for each type declared in a specified
* compilation unit.
* Generate an Objective-C header file for each type declared in the given {@link GenerationUnit}.
*/
public static void generate(GenerationUnit unit) {
new ObjectiveCHeaderGenerator(unit).generate();
Expand All @@ -74,24 +74,104 @@ protected String getSuffix() {
}

public void generate() {
CompilationUnit unit = getUnit();
println(J2ObjC.getFileHeader(getGenerationUnit().getSourceName()));

generateFileHeader();

if (unit.getPackage().getJavadoc() != null && Options.docCommentsEnabled()) {
newline();
printDocComment(unit.getPackage().getJavadoc());
Map<ITypeBinding, AbstractTypeDeclaration> declaredTypes =
new HashMap<ITypeBinding, AbstractTypeDeclaration>();
Map<String, ITypeBinding> declaredTypeNames = new HashMap<String, ITypeBinding>();
Map<AbstractTypeDeclaration, CompilationUnit> decls =
new LinkedHashMap<AbstractTypeDeclaration, CompilationUnit>();
Set<PackageDeclaration> packagesToDoc = new LinkedHashSet<PackageDeclaration>();

// First, gather everything we need to generate.
// We do this first because we'll be reordering it later.
for (CompilationUnit unit: getGenerationUnit().getCompilationUnits()) {
unit.setGenerationContext();

// It would be nice if we could put the PackageDeclarations and AbstractTypeDeclarations
// in the same list of 'things to generate'.
// TODO(mthvedt): Puzzle--figure out a way to do that in Java's type system
// that is worth the effort.
PackageDeclaration pkg = unit.getPackage();
if (pkg.getJavadoc() != null && Options.docCommentsEnabled()) {
packagesToDoc.add(pkg);
}

for (AbstractTypeDeclaration type : unit.getTypes()) {
decls.put(type, unit);
declaredTypes.put(type.getTypeBinding(), type);
declaredTypeNames.put(NameTable.getFullName(type.getTypeBinding()), type.getTypeBinding());
}
}

// We order the type declarations so that the inheritance tree appears in the correct order.
// The ordering is minimal; a type is reordered only if a subtype is immediately following.
ArrayList<ITypeBinding> orderedDeclarationBindings = new ArrayList<ITypeBinding>();
for (Map.Entry<AbstractTypeDeclaration, CompilationUnit> e: decls.entrySet()) {
e.getValue().setGenerationContext();
orderSuperinterfaces(
e.getKey().getTypeBinding(), orderedDeclarationBindings, declaredTypeNames);
}

Set<AbstractTypeDeclaration> seenDecls = new HashSet<AbstractTypeDeclaration>();
for (ITypeBinding declBinding: orderedDeclarationBindings) {
AbstractTypeDeclaration decl = declaredTypes.get(declBinding);
CompilationUnit unit = decls.get(decl);
if (!seenDecls.add(decl)) {
continue;
}

unit.setGenerationContext();

// Print package docs before the first type in the package. (See above comments and TODO.)
if (Options.docCommentsEnabled() && packagesToDoc.contains(unit.getPackage())) {
newline();
printDocComment(unit.getPackage().getJavadoc());
packagesToDoc.remove(unit.getPackage());
}

generateType(decl);
}

for (AbstractTypeDeclaration type : unit.getTypes()) {
generateType(type);
for (PackageDeclaration pkg : packagesToDoc) {
newline();
printDocComment(pkg.getJavadoc());
}

generateFileFooter();
save(getOutputPath());
}

private void orderSuperinterfaces(ITypeBinding type, List<ITypeBinding> sortedDecls,
Map<String, ITypeBinding> declaredTypeNames) {
// In Objective-C, you can't declare a protocol or interface
// forward of its implementing interfaces.
if (!type.isAnnotation()) {
// Annotations don't have overridable supertypes in generated Objective-C code
ITypeBinding superBinding = type.getSuperclass();
if (superBinding != null) {
// The map lookup ensures we get the correct ITypeBinding corresponding to a given
// CompilationUnit. The Eclipse parser may generate alternate
// definitions of this ITypeBinding that aren't equal to the one we want.
superBinding = declaredTypeNames.get(NameTable.getFullName(superBinding));
if (superBinding != null) {
orderSuperinterfaces(superBinding, sortedDecls, declaredTypeNames);
}
}

for (ITypeBinding superinterface : type.getInterfaces()) {
superinterface = declaredTypeNames.get(NameTable.getFullName(superinterface));
if (superinterface != null) {
orderSuperinterfaces(superinterface, sortedDecls, declaredTypeNames);
}
}
}

sortedDecls.add(type);
}

protected void generateType(AbstractTypeDeclaration node) {
ITypeBinding binding = node.getTypeBinding();

Expand Down Expand Up @@ -416,7 +496,7 @@ protected void generateFileHeader() {
newline();

HeaderImportCollector collector = new HeaderImportCollector();
collector.collect(getUnit());
collector.collect(getGenerationUnit().getCompilationUnits());

printForwardDeclarations(collector.getForwardDeclarations());

Expand Down

0 comments on commit b349105

Please sign in to comment.