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
public void emit(ListableClassHolderSource classes, OutputStream output, BuildTarget target) {
try (Writer writer = new OutputStreamWriter(output, "UTF-8")) {
public void emit(ListableClassHolderSource classes, BuildTarget target, String outputName) {
try (OutputStream output = target.createResource(outputName);
Writer writer = new OutputStreamWriter(output, "UTF-8")) {
emit(classes, writer, target);
} catch (IOException e) {
throw new RenderingException(e);

View File

@ -17,6 +17,8 @@ package org.teavm.backend.wasm;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
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.render.WasmBinaryRenderer;
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.DependencyChecker;
import org.teavm.dependency.DependencyListener;
@ -110,6 +114,8 @@ import org.teavm.vm.spi.TeaVMHostExtension;
public class WasmTarget implements TeaVMTarget {
private TeaVMTargetController controller;
private boolean debugging;
private boolean wastEmitted;
private boolean cEmitted;
@Override
public void setController(TeaVMTargetController controller) {
@ -149,6 +155,22 @@ public class WasmTarget implements TeaVMTarget {
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
public void contributeDependencies(DependencyChecker dependencyChecker) {
for (Class type : Arrays.asList(int.class, long.class, float.class, double.class)) {
@ -189,7 +211,8 @@ public class WasmTarget implements TeaVMTarget {
}
@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();
WasmFunction initFunction = new WasmFunction("__start__");
@ -263,11 +286,41 @@ public class WasmTarget implements TeaVMTarget {
WasmBinaryRenderer renderer = new WasmBinaryRenderer(writer);
renderer.render(module);
try {
try (OutputStream output = buildTarget.createResource(outputName)) {
output.write(writer.getData());
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
void render(WasmCRenderer target) {
if (target.outputLineNumbers) {
if (target.lineNumbersEmitted) {
TextLocation location = this.location;
if (location == null) {
location = target.lastReportedLocation;

View File

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

View File

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

View File

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

View File

@ -15,15 +15,12 @@
*/
package org.teavm.vm;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
*
* @author Alexey Andreev
*/
public class DirectoryBuildTarget implements BuildTarget {
private File directory;
@ -40,6 +37,6 @@ public class DirectoryBuildTarget implements BuildTarget {
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;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
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
* {@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
* 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);
// Check dependencies
@ -381,7 +378,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
}
// 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")
@ -513,13 +514,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
}
public void build(File dir, String fileName) {
try (OutputStream output = new FileOutputStream(new File(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);
}
build(new DirectoryBuildTarget(dir), fileName);
}
/**

View File

@ -15,7 +15,7 @@
*/
package org.teavm.vm;
import java.io.OutputStream;
import java.io.IOException;
import java.util.List;
import org.teavm.dependency.DependencyChecker;
import org.teavm.dependency.DependencyListener;
@ -36,5 +36,5 @@ public interface TeaVMTarget {
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();
vm.installPlugins();
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()) {
fail("Code compiled with errors:\n" + describeProblems(vm));
}

View File

@ -99,7 +99,7 @@ public class JSOTest {
TeaVM vm = new TeaVMBuilder(new JavaScriptTarget()).build();
vm.installPlugins();
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();
}
}

View File

@ -15,7 +15,6 @@
*/
package org.teavm.tooling;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@ -406,44 +405,46 @@ public class TeaVMTool implements BaseTeaVMTool {
}
}
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 (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 (runtime == RuntimeCopyOperation.MERGED) {
javaScriptTarget.add(runtimeInjector);
}
BuildTarget buildTarget = new DirectoryBuildTarget(targetDirectory);
String outputName = getResolvedTargetFileName();
vm.build(buildTarget, outputName);
if (vm.wasCancelled()) {
log.info("Build cancelled");
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);
}
}
if (incremental) {
programCache.flush();
if (astCache != null) {
astCache.flush();
}
cachedClassSource.flush();
symbolTable.flush();
fileTable.flush();
log.info("Cache updated");
if (incremental) {
programCache.flush();
if (astCache != null) {
astCache.flush();
}
cachedClassSource.flush();
symbolTable.flush();
fileTable.flush();
log.info("Cache updated");
}
} catch (IOException e) {
throw new TeaVMToolException("IO error occurred", e);

View File

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