Refactor target API. Add ability to generate wast and C from wasm target

This commit is contained in:
Alexey Andreev 2016-09-03 22:28:28 +03:00
parent de4128b377
commit 9143714168
13 changed files with 133 additions and 79 deletions

View File

@ -198,8 +198,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
} }
@Override @Override
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget target) { public void emit(ListableClassHolderSource classes, BuildTarget target, String outputName) {
try (Writer writer = new OutputStreamWriter(output, "UTF-8")) { try (OutputStream output = target.createResource(outputName);
Writer writer = new OutputStreamWriter(output, "UTF-8")) {
emit(classes, writer, target); emit(classes, writer, target);
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException(e); throw new RenderingException(e);

View File

@ -17,6 +17,8 @@ package org.teavm.backend.wasm;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -66,6 +68,8 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.patches.ClassPatch; import org.teavm.backend.wasm.patches.ClassPatch;
import org.teavm.backend.wasm.render.WasmBinaryRenderer; import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryWriter; import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.backend.wasm.render.WasmCRenderer;
import org.teavm.backend.wasm.render.WasmRenderer;
import org.teavm.dependency.ClassDependency; import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
@ -110,6 +114,8 @@ import org.teavm.vm.spi.TeaVMHostExtension;
public class WasmTarget implements TeaVMTarget { public class WasmTarget implements TeaVMTarget {
private TeaVMTargetController controller; private TeaVMTargetController controller;
private boolean debugging; private boolean debugging;
private boolean wastEmitted;
private boolean cEmitted;
@Override @Override
public void setController(TeaVMTargetController controller) { public void setController(TeaVMTargetController controller) {
@ -149,6 +155,22 @@ public class WasmTarget implements TeaVMTarget {
this.debugging = debugging; this.debugging = debugging;
} }
public boolean isWastEmitted() {
return wastEmitted;
}
public void setWastEmitted(boolean wastEmitted) {
this.wastEmitted = wastEmitted;
}
public boolean isCEmitted() {
return cEmitted;
}
public void setCEmitted(boolean cEmitted) {
this.cEmitted = cEmitted;
}
@Override @Override
public void contributeDependencies(DependencyChecker dependencyChecker) { public void contributeDependencies(DependencyChecker dependencyChecker) {
for (Class type : Arrays.asList(int.class, long.class, float.class, double.class)) { for (Class type : Arrays.asList(int.class, long.class, float.class, double.class)) {
@ -189,7 +211,8 @@ public class WasmTarget implements TeaVMTarget {
} }
@Override @Override
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget) { public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName)
throws IOException {
WasmModule module = new WasmModule(); WasmModule module = new WasmModule();
WasmFunction initFunction = new WasmFunction("__start__"); WasmFunction initFunction = new WasmFunction("__start__");
@ -263,11 +286,41 @@ public class WasmTarget implements TeaVMTarget {
WasmBinaryRenderer renderer = new WasmBinaryRenderer(writer); WasmBinaryRenderer renderer = new WasmBinaryRenderer(writer);
renderer.render(module); renderer.render(module);
try { try (OutputStream output = buildTarget.createResource(outputName)) {
output.write(writer.getData()); output.write(writer.getData());
output.flush(); output.flush();
} catch (IOException e) { }
throw new RuntimeException(e);
if (wastEmitted) {
emitWast(module, buildTarget, getBaseName(outputName) + ".wast");
}
if (cEmitted) {
emitC(module, buildTarget, getBaseName(outputName) + ".c");
}
}
private String getBaseName(String name) {
int index = name.lastIndexOf('.');
return index < 0 ? name : name.substring(0, index);
}
private void emitWast(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
WasmRenderer renderer = new WasmRenderer();
renderer.setLineNumbersEmitted(debugging);
renderer.render(module);
try (OutputStream output = buildTarget.createResource(outputName);
Writer writer = new OutputStreamWriter(output, "UTF-8")) {
writer.write(renderer.toString());
}
}
private void emitC(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
WasmCRenderer renderer = new WasmCRenderer();
renderer.setLineNumbersEmitted(debugging);
renderer.render(module);
try (OutputStream output = buildTarget.createResource(outputName);
Writer writer = new OutputStreamWriter(output, "UTF-8")) {
writer.write(renderer.toString());
} }
} }

View File

@ -45,7 +45,7 @@ class CSingleLine extends CLine {
@Override @Override
void render(WasmCRenderer target) { void render(WasmCRenderer target) {
if (target.outputLineNumbers) { if (target.lineNumbersEmitted) {
TextLocation location = this.location; TextLocation location = this.location;
if (location == null) { if (location == null) {
location = target.lastReportedLocation; location = target.lastReportedLocation;

View File

@ -29,15 +29,15 @@ public class WasmCRenderer {
private int indentLevel; private int indentLevel;
String currentFile = ""; String currentFile = "";
int currentLine = -1; int currentLine = -1;
boolean outputLineNumbers; boolean lineNumbersEmitted;
TextLocation lastReportedLocation; TextLocation lastReportedLocation;
public boolean isOutputLineNumbers() { public boolean isLineNumbersEmitted() {
return outputLineNumbers; return lineNumbersEmitted;
} }
public void setOutputLineNumbers(boolean outputLineNumbers) { public void setLineNumbersEmitted(boolean value) {
this.outputLineNumbers = outputLineNumbers; this.lineNumbersEmitted = value;
} }
void indent() { void indent() {

View File

@ -45,6 +45,14 @@ public class WasmRenderer {
return this; return this;
} }
public boolean isLineNumbersEmitted() {
return visitor.lineNumbersEmitted;
}
public void setLineNumbersEmitted(boolean value) {
visitor.lineNumbersEmitted = value;
}
public void render(WasmModule module) { public void render(WasmModule module) {
visitor.open().append("module"); visitor.open().append("module");
renderMemory(module); renderMemory(module);

View File

@ -67,6 +67,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
private Map<WasmBlock, String> blockIdentifiers = new HashMap<>(); private Map<WasmBlock, String> blockIdentifiers = new HashMap<>();
private int indentLevel; private int indentLevel;
private boolean lfDeferred; private boolean lfDeferred;
boolean lineNumbersEmitted;
List<WasmSignature> signatureList = new ArrayList<>(); List<WasmSignature> signatureList = new ArrayList<>();
Map<WasmSignature, Integer> signatureMap = new HashMap<>(); Map<WasmSignature, Integer> signatureMap = new HashMap<>();
@ -97,7 +98,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
} }
WasmRenderingVisitor line(WasmExpression expression) { WasmRenderingVisitor line(WasmExpression expression) {
if (expression.getLocation() != null) { if (expression.getLocation() != null && lineNumbersEmitted) {
lf().append(";; " + expression.getLocation().getFileName() + ":" + expression.getLocation().getLine()); lf().append(";; " + expression.getLocation().getFileName() + ":" + expression.getLocation().getLine());
} }
lf().append(expression); lf().append(expression);

View File

@ -15,15 +15,12 @@
*/ */
package org.teavm.vm; package org.teavm.vm;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
/**
*
* @author Alexey Andreev
*/
public class DirectoryBuildTarget implements BuildTarget { public class DirectoryBuildTarget implements BuildTarget {
private File directory; private File directory;
@ -40,6 +37,6 @@ public class DirectoryBuildTarget implements BuildTarget {
dir.mkdirs(); dir.mkdirs();
} }
} }
return new FileOutputStream(new File(directory, fileName)); return new BufferedOutputStream(new FileOutputStream(new File(directory, fileName)), 65536);
} }
} }

View File

@ -16,10 +16,7 @@
package org.teavm.vm; package org.teavm.vm;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -329,11 +326,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
* actual generation happens and no exceptions thrown, but you can further call * actual generation happens and no exceptions thrown, but you can further call
* {@link #getProblemProvider()} to learn the build state.</p> * {@link #getProblemProvider()} to learn the build state.</p>
* *
* @param output where to generate JavaScript. Should not be null.
* @param buildTarget where to generate additional resources. Can be null, but if there are * @param buildTarget where to generate additional resources. Can be null, but if there are
* plugins or inteceptors that generate additional resources, the build process will fail. * plugins or interceptors that generate additional resources, the build process will fail.
* @param outputName name of output file within buildTarget. Should not be null.
*/ */
public void build(OutputStream output, BuildTarget buildTarget) { public void build(BuildTarget buildTarget, String outputName) {
target.setController(targetController); target.setController(targetController);
// Check dependencies // Check dependencies
@ -381,7 +378,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
// Render // Render
target.emit(classSet, output, buildTarget); try {
target.emit(classSet, buildTarget, outputName);
} catch (IOException e) {
throw new RuntimeException("Error generating output files", e);
}
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -513,13 +514,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
public void build(File dir, String fileName) { public void build(File dir, String fileName) {
try (OutputStream output = new FileOutputStream(new File(dir, fileName))) { build(new DirectoryBuildTarget(dir), fileName);
build(output, new DirectoryBuildTarget(dir));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Platform does not support UTF-8", e);
} catch (IOException e) {
throw new RenderingException("IO error occurred", e);
}
} }
/** /**

View File

@ -15,7 +15,7 @@
*/ */
package org.teavm.vm; package org.teavm.vm;
import java.io.OutputStream; import java.io.IOException;
import java.util.List; import java.util.List;
import org.teavm.dependency.DependencyChecker; import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyListener;
@ -36,5 +36,5 @@ public interface TeaVMTarget {
void contributeDependencies(DependencyChecker dependencyChecker); void contributeDependencies(DependencyChecker dependencyChecker);
void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget buildTarget); void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException;
} }

View File

@ -78,7 +78,7 @@ public class ClassValueTest {
TeaVM vm = new TeaVMBuilder(new JavaScriptTarget()).build(); TeaVM vm = new TeaVMBuilder(new JavaScriptTarget()).build();
vm.installPlugins(); vm.installPlugins();
vm.entryPoint(new MethodReference(getClass().getName(), methodName, ValueType.VOID)); vm.entryPoint(new MethodReference(getClass().getName(), methodName, ValueType.VOID));
vm.build(new ByteArrayOutputStream(), null); vm.build(fileName -> new ByteArrayOutputStream(), "tmp");
if (!vm.getProblemProvider().getSevereProblems().isEmpty()) { if (!vm.getProblemProvider().getSevereProblems().isEmpty()) {
fail("Code compiled with errors:\n" + describeProblems(vm)); fail("Code compiled with errors:\n" + describeProblems(vm));
} }

View File

@ -99,7 +99,7 @@ public class JSOTest {
TeaVM vm = new TeaVMBuilder(new JavaScriptTarget()).build(); TeaVM vm = new TeaVMBuilder(new JavaScriptTarget()).build();
vm.installPlugins(); vm.installPlugins();
vm.entryPoint("org/teavm/metaprogramming/test", new MethodReference(JSOTest.class, methodName, void.class)); vm.entryPoint("org/teavm/metaprogramming/test", new MethodReference(JSOTest.class, methodName, void.class));
vm.build(new ByteArrayOutputStream(), null); vm.build(name -> new ByteArrayOutputStream(), "tmp");
return vm.getProblemProvider().getSevereProblems(); return vm.getProblemProvider().getSevereProblems();
} }
} }

View File

@ -15,7 +15,6 @@
*/ */
package org.teavm.tooling; package org.teavm.tooling;
import java.io.BufferedOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -406,44 +405,46 @@ public class TeaVMTool implements BaseTeaVMTool {
} }
} }
targetDirectory.mkdirs(); targetDirectory.mkdirs();
try (OutputStream output = new BufferedOutputStream(
new FileOutputStream(new File(targetDirectory, getResolvedTargetFileName())), 65536)) {
Writer writer = new OutputStreamWriter(output, "UTF-8");
if (runtime == RuntimeCopyOperation.MERGED) {
javaScriptTarget.add(runtimeInjector);
}
vm.build(output, new DirectoryBuildTarget(targetDirectory));
if (vm.wasCancelled()) {
log.info("Build cancelled");
cancelled = true;
return;
}
ProblemProvider problemProvider = vm.getProblemProvider(); if (runtime == RuntimeCopyOperation.MERGED) {
if (problemProvider.getProblems().isEmpty()) { javaScriptTarget.add(runtimeInjector);
log.info("Output file successfully built"); }
} else if (problemProvider.getSevereProblems().isEmpty()) { BuildTarget buildTarget = new DirectoryBuildTarget(targetDirectory);
log.info("Output file built with warnings"); String outputName = getResolvedTargetFileName();
TeaVMProblemRenderer.describeProblems(vm, log); vm.build(buildTarget, outputName);
} else { if (vm.wasCancelled()) {
log.info("Output file built with errors"); log.info("Build cancelled");
TeaVMProblemRenderer.describeProblems(vm, log); cancelled = true;
} return;
}
if (targetType == TeaVMTargetType.JAVASCRIPT) { ProblemProvider problemProvider = vm.getProblemProvider();
if (problemProvider.getProblems().isEmpty()) {
log.info("Output file successfully built");
} else if (problemProvider.getSevereProblems().isEmpty()) {
log.info("Output file built with warnings");
TeaVMProblemRenderer.describeProblems(vm, log);
} else {
log.info("Output file built with errors");
TeaVMProblemRenderer.describeProblems(vm, log);
}
if (targetType == TeaVMTargetType.JAVASCRIPT) {
try (OutputStream output = buildTarget.createResource(outputName)) {
Writer writer = new OutputStreamWriter(output, "UTF-8");
additionalJavaScriptOutput(writer); additionalJavaScriptOutput(writer);
} }
}
if (incremental) { if (incremental) {
programCache.flush(); programCache.flush();
if (astCache != null) { if (astCache != null) {
astCache.flush(); astCache.flush();
}
cachedClassSource.flush();
symbolTable.flush();
fileTable.flush();
log.info("Cache updated");
} }
cachedClassSource.flush();
symbolTable.flush();
fileTable.flush();
log.info("Cache updated");
} }
} catch (IOException e) { } catch (IOException e) {
throw new TeaVMToolException("IO error occurred", e); throw new TeaVMToolException("IO error occurred", e);

View File

@ -360,16 +360,14 @@ public class TeaVMTestRunner extends Runner {
applyProperties(method.getDeclaringClass(), properties); applyProperties(method.getDeclaringClass(), properties);
vm.setProperties(properties); vm.setProperties(properties);
try (OutputStream innerWriter = new FileOutputStream(outputFile)) { MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException",
MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException", Throwable.class, String.class);
Throwable.class, String.class); vm.entryPoint("runTest", new MethodReference(TestEntryPoint.class, "run", void.class)).async();
vm.entryPoint("runTest", new MethodReference(TestEntryPoint.class, "run", void.class)).async(); vm.entryPoint("extractException", exceptionMsg);
vm.entryPoint("extractException", exceptionMsg); vm.build(new DirectoryBuildTarget(outputFile.getParentFile()), outputFile.getName());
vm.build(innerWriter, new DirectoryBuildTarget(outputDir)); if (!vm.getProblemProvider().getProblems().isEmpty()) {
if (!vm.getProblemProvider().getProblems().isEmpty()) { result.success = false;
result.success = false; result.errorMessage = buildErrorMessage(vm);
result.errorMessage = buildErrorMessage(vm);
}
} }
return result; return result;