diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 521f4c177..c9c183601 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -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); diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 3676b8161..3cc17cc78 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -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()); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java b/core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java index 1505d604e..c13ae2a75 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/CSingleLine.java @@ -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; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java index 22f99ee18..a56c1b8f1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java @@ -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() { diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java index 327ecdb28..e9453c089 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderer.java @@ -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); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index c27d250aa..e70e3ac79 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -67,6 +67,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { private Map blockIdentifiers = new HashMap<>(); private int indentLevel; private boolean lfDeferred; + boolean lineNumbersEmitted; List signatureList = new ArrayList<>(); Map 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); diff --git a/core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java b/core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java index d9b902d15..4f9cc9f57 100644 --- a/core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java +++ b/core/src/main/java/org/teavm/vm/DirectoryBuildTarget.java @@ -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); } } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 62b9d3235..15bc01776 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -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.

* - * @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); } /** diff --git a/core/src/main/java/org/teavm/vm/TeaVMTarget.java b/core/src/main/java/org/teavm/vm/TeaVMTarget.java index 28253d79e..04df7b096 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTarget.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTarget.java @@ -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; } diff --git a/tests/src/test/java/org/teavm/dependency/ClassValueTest.java b/tests/src/test/java/org/teavm/dependency/ClassValueTest.java index fc956f1e8..e4e84c477 100644 --- a/tests/src/test/java/org/teavm/dependency/ClassValueTest.java +++ b/tests/src/test/java/org/teavm/dependency/ClassValueTest.java @@ -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)); } diff --git a/tests/src/test/java/org/teavm/tests/JSOTest.java b/tests/src/test/java/org/teavm/tests/JSOTest.java index f4fd609ec..277310b8e 100644 --- a/tests/src/test/java/org/teavm/tests/JSOTest.java +++ b/tests/src/test/java/org/teavm/tests/JSOTest.java @@ -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(); } } diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index e7a3c7241..491c10721 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -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); diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java index 656225218..acb8f5e8b 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java +++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java @@ -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;