Adds source maps generation

This commit is contained in:
konsoletyper 2014-08-26 17:48:32 +04:00
parent 5b0506d158
commit 7cb3ce70c3
4 changed files with 204 additions and 5 deletions

View File

@ -62,6 +62,10 @@ public class TeaVMRunner {
.withDescription("Generate debug information")
.withLongOpt("debug")
.create('D'));
options.addOption(OptionBuilder
.withDescription("Generate source maps")
.withLongOpt("sourcemaps")
.create());
options.addOption(OptionBuilder
.withArgName("number")
.hasArg()
@ -126,6 +130,9 @@ public class TeaVMRunner {
}
if (commandLine.hasOption('D')) {
tool.setDebugInformation(new File(tool.getTargetDirectory(), tool.getTargetFileName() + ".teavmdbg"));
if (commandLine.hasOption("sourcemaps")) {
tool.setSourceMapsFileGenerated(true);
}
}
args = commandLine.getArgs();
if (args.length > 1) {

View File

@ -15,10 +15,7 @@
*/
package org.teavm.debugging;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.*;
import java.util.*;
import org.teavm.common.IntegerArray;
import org.teavm.model.MethodDescriptor;
@ -296,6 +293,10 @@ public class DebugInformation {
writer.write(this);
}
public void writeAsSourceMaps(Writer output, String sourceFile) throws IOException {
new SourceMapsWriter(output).write(sourceFile, this);
}
public static DebugInformation read(InputStream input) throws IOException {
DebugInformationReader reader = new DebugInformationReader(input);
return reader.read();

View File

@ -0,0 +1,159 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.debugging;
import java.io.IOException;
import java.io.Writer;
/**
*
* @author Alexey Andreev
*/
class SourceMapsWriter {
private static final String BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private Writer output;
private int lastLine;
private int lastColumn;
private int sourceLine;
private int lastSourceLine;
private int sourceFile;
private int lastSourceFile;
private boolean first;
public SourceMapsWriter(Writer output) {
this.output = output;
}
public void write(String generatedFile, DebugInformation debugInfo) throws IOException {
output.write("{\"version\":3");
output.write(",\"file\":\"");
writeEscapedString(generatedFile);
output.write("\"");
output.write(",\"sourceRoot\":\"\"");
output.write(",\"sources\":[");
for (int i = 0; i < debugInfo.fileNames.length; ++i) {
if (i > 0) {
output.write(',');
}
output.write("\"");
writeEscapedString(debugInfo.fileNames[i]);
output.write("\"");
}
output.write("]");
output.write(",\"names\":[]");
output.write(",\"mappings\":\"");
first = true;
lastLine = 0;
lastColumn = 0;
sourceLine = -1;
sourceFile = -1;
lastSourceFile = 0;
lastSourceLine = 0;
int i = 0;
int j = 0;
while (i < debugInfo.lineMapping.lines.length && j < debugInfo.fileMapping.lines.length) {
GeneratedLocation a = debugInfo.lineMapping.key(i);
GeneratedLocation b = debugInfo.fileMapping.key(j);
int cmp = a.compareTo(b);
if (cmp < 0) {
writeSegment(a, sourceFile, debugInfo.lineMapping.values[i++]);
} else if (cmp > 0) {
writeSegment(b, debugInfo.fileMapping.values[j++], sourceLine);
} else {
writeSegment(a, debugInfo.fileMapping.values[j++], debugInfo.lineMapping.values[i++] - 1);
}
}
while (i < debugInfo.lineMapping.lines.length) {
GeneratedLocation a = debugInfo.lineMapping.key(i);
writeSegment(a, sourceFile, debugInfo.lineMapping.values[i++]);
}
while (j < debugInfo.fileMapping.lines.length) {
GeneratedLocation b = debugInfo.fileMapping.key(j);
writeSegment(b, debugInfo.fileMapping.values[j++], sourceLine);
}
output.write("\"}");
}
private void writeSegment(GeneratedLocation loc, int sourceFile, int sourceLine) throws IOException {
while (loc.getLine() > lastLine) {
output.write(';');
++lastLine;
first = true;
lastColumn = 0;
}
if (!first) {
output.write(',');
}
writeVLQ(loc.getColumn() - lastColumn);
if (sourceFile >= 0 && sourceLine >= 0) {
writeVLQ(sourceFile - lastSourceFile);
writeVLQ(sourceLine - lastSourceLine);
writeVLQ(0);
lastSourceFile = sourceFile;
lastSourceLine = sourceLine;
}
lastColumn = loc.getColumn();
this.sourceFile = sourceFile;
this.sourceLine = sourceLine;
first = false;
}
private void writeEscapedString(String str) throws IOException {
for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i);
switch (c) {
case '\n':
output.write("\\n");
break;
case '\r':
output.write("\\r");
break;
case '\t':
output.write("\\t");
break;
case '\b':
output.write("\\b");
break;
case '\\':
output.write("\\\\");
break;
case '"':
output.write("\\\"");
break;
default:
output.write(c);
break;
}
}
}
private void writeVLQ(int number) throws IOException {
if (number < 0) {
number = ((-number) << 1) | 1;
} else {
number = number << 1;
}
do {
int digit = number & 0x1F;
int next = number >>> 5;
if (next != 0) {
digit |= 0x20;
}
output.write(BASE64_CHARS.charAt(digit));
number = next;
} while (number != 0);
}
}

View File

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.teavm.common.ThreadPoolFiniteExecutor;
import org.teavm.debugging.DebugInformation;
import org.teavm.debugging.DebugInformationBuilder;
import org.teavm.javascript.RenderingContext;
import org.teavm.model.ClassHolderTransformer;
@ -45,6 +46,8 @@ public class TeaVMTool {
private boolean mainPageIncluded;
private boolean bytecodeLogging;
private File debugInformation;
private String sourceMapsFileName;
private boolean sourceMapsFileGenerated;
private int numThreads = 1;
private List<ClassHolderTransformer> transformers = new ArrayList<>();
private List<ClassAlias> classAliases = new ArrayList<>();
@ -124,6 +127,22 @@ public class TeaVMTool {
this.numThreads = numThreads;
}
public String getSourceMapsFileName() {
return sourceMapsFileName;
}
public void setSourceMapsFileName(String sourceMapsFileName) {
this.sourceMapsFileName = sourceMapsFileName;
}
public boolean isSourceMapsFileGenerated() {
return sourceMapsFileGenerated;
}
public void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated) {
this.sourceMapsFileGenerated = sourceMapsFileGenerated;
}
public Properties getProperties() {
return properties;
}
@ -218,10 +237,23 @@ public class TeaVMTool {
vm.checkForMissingItems();
log.info("JavaScript file successfully built");
if (debugInformation != null) {
DebugInformation debugInfo = debugEmitter.getDebugInformation();
try (OutputStream debugInfoOut = new FileOutputStream(debugInformation)) {
debugEmitter.getDebugInformation().write(debugInfoOut);
debugInfo.write(debugInfoOut);
}
log.info("Debug information successfully written");
if (sourceMapsFileGenerated) {
String sourceMapsFileName = this.sourceMapsFileName;
if (sourceMapsFileName == null) {
sourceMapsFileName = targetFileName + ".map";
}
writer.append("\n//# sourceMappingURL=").append(sourceMapsFileName);
try (Writer sourceMapsOut = new OutputStreamWriter(new FileOutputStream(
new File(targetDirectory, sourceMapsFileName)), "UTF-8")) {
debugInfo.writeAsSourceMaps(sourceMapsOut, targetFileName);
}
log.info("Source maps successfully written");
}
}
}
if (runtime == RuntimeCopyOperation.SEPARATE) {