JS backend: improved sourcemap generation and moved SourceMap3Builder and JsSourceGenerationVisitor to kotlin sources from dart-ast(develar fork).

#KT-927 in progress

(cherry picked from commit 10fb0f9)
This commit is contained in:
develar
2013-08-23 22:12:37 +04:00
committed by Zalim Bashorov
parent e05ffd03c3
commit b3658472ac
11 changed files with 297 additions and 34 deletions
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2012 JetBrains s.r.o.
*
* 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.jetbrains.js.compiler;
import com.google.dart.compiler.backend.js.JsToStringGenerationVisitor;
import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.util.TextOutput;
import org.jetbrains.annotations.Nullable;
public class JsSourceGenerationVisitor extends JsToStringGenerationVisitor implements TextOutput.OutListener {
@Nullable
private final SourceMapBuilder sourceMapBuilder;
private Object pendingSourceInfo;
public JsSourceGenerationVisitor(TextOutput out, @Nullable SourceMapBuilder sourceMapBuilder) {
super(out);
this.sourceMapBuilder = sourceMapBuilder;
out.setOutListener(this);
}
@Override
public boolean visit(JsProgram program, JsContext ctx) {
return true;
}
@Override
public boolean visit(JsProgramFragment x, JsContext ctx) {
// Descend naturally.
return true;
}
@Override
public boolean visit(JsBlock x, JsContext ctx) {
printJsBlock(x, false, true);
return false;
}
@Override
public void newLined() {
if (sourceMapBuilder != null) {
sourceMapBuilder.newLine();
}
}
@Override
public void indentedAfterNewLine() {
if (pendingSourceInfo != null) {
assert sourceMapBuilder != null;
sourceMapBuilder.processSourceInfo(pendingSourceInfo);
pendingSourceInfo = null;
}
}
@Override
protected void doTraverse(JsVisitable node, JsContext context) {
if (sourceMapBuilder != null) {
Object sourceInfo = node.getSourceInfo();
if (sourceInfo != null) {
assert pendingSourceInfo == null;
if (p.isJustNewlined()) {
pendingSourceInfo = sourceInfo;
}
else {
sourceMapBuilder.processSourceInfo(sourceInfo);
}
}
}
super.doTraverse(node, context);
}
@Override
public void endVisit(JsProgram x, JsContext context) {
super.endVisit(x, context);
if (sourceMapBuilder != null) {
sourceMapBuilder.addLink();
}
}
}
@@ -0,0 +1,168 @@
package org.jetbrains.js.compiler.sourcemap;
import com.google.dart.compiler.common.SourceInfo;
import com.google.dart.compiler.util.TextOutput;
import com.intellij.util.PairConsumer;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.js.compiler.SourceMapBuilder;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class SourceMap3Builder implements SourceMapBuilder {
private final StringBuilder out = new StringBuilder(8192);
private final File generatedFile;
private final TextOutput textOutput;
private final PairConsumer<SourceMapBuilder, Object> sourceInfoConsumer;
private String lastSource;
private int lastSourceIndex;
private final TObjectIntHashMap<String> sources = new TObjectIntHashMap<String>() {
@Override
public int get(String key) {
int index = index(key);
return index < 0 ? -1 : _values[index];
}
};
private final List<String> orderedSources = new ArrayList<String>();
private int previousGeneratedColumn = -1;
private int previousSourceIndex;
private int previousSourceLine;
private int previousSourceColumn;
public SourceMap3Builder(File generatedFile, TextOutput textOutput, PairConsumer<SourceMapBuilder, Object> sourceInfoConsumer) {
this.generatedFile = generatedFile;
this.textOutput = textOutput;
this.sourceInfoConsumer = sourceInfoConsumer;
}
@Override
public File getOutFile() {
return new File(generatedFile.getParentFile(), generatedFile.getName() + ".map");
}
@Override
public String build() {
StringBuilder sb = new StringBuilder(out.length() + (128 * orderedSources.size()));
sb.append("{\"version\":3,\"file\":\"").append(generatedFile.getName()).append('"').append(',');
appendSources(sb);
sb.append(",\"names\":[");
sb.append("],\"mappings\":\"");
sb.append(out);
sb.append("\"}");
return sb.toString();
}
private void appendSources(StringBuilder sb) {
boolean isNotFirst = false;
sb.append('"').append("sources").append("\":[");
for (String source : orderedSources) {
if (isNotFirst) {
sb.append(',');
}
else {
isNotFirst = true;
}
sb.append('"').append("file://").append(source).append('"');
}
sb.append(']');
}
@Override
public void newLine() {
out.append(';');
previousGeneratedColumn = -1;
}
@Override
public void processSourceInfo(Object sourceInfo) {
if (sourceInfo instanceof SourceInfo) {
throw new UnsupportedOperationException("SourceInfo is not yet supported");
}
sourceInfoConsumer.consume(this, sourceInfo);
}
private int getSourceIndex(String source) {
if (source.equals(lastSource)) {
return lastSourceIndex;
}
int sourceIndex = sources.get(source);
if (sourceIndex == -1) {
sourceIndex = orderedSources.size();
sources.put(source, sourceIndex);
orderedSources.add(source);
}
lastSource = source;
lastSourceIndex = sourceIndex;
return sourceIndex;
}
@Override
public void addMapping(String source, int sourceLine, int sourceColumn) {
if (previousGeneratedColumn == -1) {
previousGeneratedColumn = 0;
}
else {
out.append(',');
}
Base64VLQ.encode(out, textOutput.getColumn() - previousGeneratedColumn);
previousGeneratedColumn = textOutput.getColumn();
int sourceIndex = getSourceIndex(source);
Base64VLQ.encode(out, sourceIndex - previousSourceIndex);
previousSourceIndex = sourceIndex;
Base64VLQ.encode(out, sourceLine - previousSourceLine);
previousSourceLine = sourceLine;
Base64VLQ.encode(out, sourceColumn - previousSourceColumn);
previousSourceColumn = sourceColumn;
}
@Override
public void addLink() {
textOutput.print("\n//@ sourceMappingURL=file://");
textOutput.print(getOutFile().getAbsolutePath());
}
private static final class Base64VLQ {
// A Base64 VLQ digit can represent 5 bits, so it is base-32.
private static final int VLQ_BASE_SHIFT = 5;
private static final int VLQ_BASE = 1 << VLQ_BASE_SHIFT;
// A mask of bits for a VLQ digit (11111), 31 decimal.
private static final int VLQ_BASE_MASK = VLQ_BASE - 1;
// The continuation bit is the 6th bit.
private static final int VLQ_CONTINUATION_BIT = VLQ_BASE;
private static final char[] BASE64_MAP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
private Base64VLQ() {
}
private static int toVLQSigned(int value) {
return value < 0 ? ((-value) << 1) + 1 : value << 1;
}
public static void encode(StringBuilder out, int value) {
value = toVLQSigned(value);
do {
int digit = value & VLQ_BASE_MASK;
value >>>= VLQ_BASE_SHIFT;
if (value > 0) {
digit |= VLQ_CONTINUATION_BIT;
}
out.append(BASE64_MAP[digit]);
}
while (value > 0);
}
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.k2js.facade;
import com.google.dart.compiler.backend.js.JsSourceGenerationVisitor;
import com.google.dart.compiler.backend.js.ast.JsProgram;
import com.google.dart.compiler.util.TextOutputImpl;
import com.intellij.openapi.project.Project;
@@ -25,7 +24,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.js.compiler.JsSourceGenerationVisitor;
import org.jetbrains.js.compiler.SourceMapBuilder;
import org.jetbrains.js.compiler.sourcemap.SourceMap3Builder;
import org.jetbrains.k2js.analyze.AnalyzerFacadeForJS;
import org.jetbrains.k2js.config.Config;
import org.jetbrains.k2js.facade.exceptions.TranslationException;
@@ -54,7 +55,7 @@ public final class K2JSTranslator {
K2JSTranslator translator = new K2JSTranslator(config);
File outFile = new File(outputPath);
TextOutputImpl output = new TextOutputImpl();
SourceMapBuilder sourceMapBuilder = config.isSourcemap() ? new SourceMapBuilder(outFile, output, new SourceMapBuilderConsumer()) : null;
SourceMapBuilder sourceMapBuilder = config.isSourcemap() ? new SourceMap3Builder(outFile, output, new SourceMapBuilderConsumer()) : null;
String programCode = translator.generateProgramCode(files, mainCall, output, sourceMapBuilder);
FileUtil.writeToFile(outFile, programCode);
if (sourceMapBuilder != null) {
@@ -36,6 +36,6 @@ class SourceMapBuilderConsumer implements PairConsumer<SourceMapBuilder, Object>
assert document != null;
int line = document.getLineNumber(offset);
int column = offset - document.getLineStartOffset(line);
builder.addMapping(file.getViewProvider().getVirtualFile().getUrl(), line, column);
builder.addMapping(file.getViewProvider().getVirtualFile().getPath(), line, column);
}
}
@@ -112,7 +112,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
jsBlock.getStatements().add(convertToStatement(jsNode));
}
}
return jsBlock;
return source(jsBlock, jetBlock);
}
@Override
@@ -120,10 +120,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
@NotNull TranslationContext context) {
JetExpression returnedExpression = jetReturnExpression.getReturnedExpression();
if (returnedExpression != null) {
return new JsReturn(translateAsExpression(returnedExpression, context));
}
return new JsReturn();
return source(new JsReturn(returnedExpression != null ? translateAsExpression(returnedExpression, context) : null), jetReturnExpression);
}
@Override
@@ -158,14 +155,14 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
context.aliasingContext().registerAlias(descriptor, alias);
}
return newVar(name, initializer);
return source(newVar(name, initializer), expression);
}
@Override
@NotNull
public JsNode visitCallExpression(@NotNull JetCallExpression expression,
@NotNull TranslationContext context) {
return CallExpressionTranslator.translate(expression, null, CallType.NORMAL, context);
return source(CallExpressionTranslator.translate(expression, null, CallType.NORMAL, context), expression);
}
@Override
@@ -181,10 +178,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
boolean isKotlinStatement = BindingUtils.isStatement(context.bindingContext(), expression);
boolean canBeJsExpression = thenNode instanceof JsExpression && elseNode instanceof JsExpression;
if (!isKotlinStatement && canBeJsExpression) {
return new JsConditional(testExpression, convertToExpression(thenNode), convertToExpression(elseNode));
return source(new JsConditional(testExpression, convertToExpression(thenNode), convertToExpression(elseNode)), expression);
}
else {
JsIf ifStatement = new JsIf(testExpression, convertToStatement(thenNode), elseNode == null ? null : convertToStatement(elseNode));
source(ifStatement, expression);
if (isKotlinStatement) {
return ifStatement;
}
@@ -198,12 +196,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression,
public JsExpression visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression,
@NotNull TranslationContext context) {
return ReferenceTranslator.translateSimpleName(expression, context);
}
@NotNull
private JsStatement translateNullableExpressionAsNotNullStatement(@Nullable JetExpression nullableExpression,
@NotNull TranslationContext context) {
@@ -245,7 +242,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
private JsNode createWhile(@NotNull JsWhile result, @NotNull JetWhileExpressionBase expression, @NotNull TranslationContext context) {
result.setCondition(translateConditionExpression(expression.getCondition(), context));
result.setBody(translateNullableExpressionAsNotNullStatement(expression.getBody(), context));
return result;
return source(result, expression);
}
@Override
@@ -256,7 +253,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
if (stringLiteral != null) {
return stringLiteral;
}
return resolveAsTemplate(expression, context);
return source(resolveAsTemplate(expression, context), expression);
}
@NotNull
@@ -316,10 +313,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Nullable
public JsNode visitWhenExpression(@NotNull JetWhenExpression expression,
@NotNull TranslationContext context) {
return Translation.translateWhenExpression(expression, context);
return WhenTranslator.translate(expression, context);
}
@Override
@NotNull
public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression,
@@ -367,7 +363,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
JsExpression alias = context.literalFunctionTranslator().translate(expression, descriptor, context);
JsName name = context.scope().declareFreshName(descriptor.getName().asString());
context.aliasingContext().registerAlias(descriptor, name.makeRef());
return new JsVars(new JsVars.JsVar(name, alias));
return source(new JsVars(new JsVars.JsVar(name, alias)), expression);
}
@Override
@@ -406,7 +402,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull TranslationContext context) {
JetExpression thrownExpression = expression.getThrownExpression();
assert thrownExpression != null : "Thrown expression must not be null";
return new JsThrow(translateAsExpression(thrownExpression, context));
return source(new JsThrow(translateAsExpression(thrownExpression, context)), expression);
}
@Override
@@ -424,6 +420,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), objectDeclarationName);
JsName propertyName = context.getNameForDescriptor(descriptor);
JsExpression value = ClassTranslator.generateClassCreation(expression, context);
return newVar(propertyName, value);
return source(newVar(propertyName, value), expression);
}
}
@@ -20,7 +20,10 @@ import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.k2js.config.Config;
import org.jetbrains.k2js.facade.MainCallParameters;
@@ -35,7 +38,6 @@ import org.jetbrains.k2js.translate.declaration.NamespaceDeclarationTranslator;
import org.jetbrains.k2js.translate.expression.ExpressionVisitor;
import org.jetbrains.k2js.translate.expression.FunctionTranslator;
import org.jetbrains.k2js.translate.expression.PatternTranslator;
import org.jetbrains.k2js.translate.expression.WhenTranslator;
import org.jetbrains.k2js.translate.reference.CallBuilder;
import org.jetbrains.k2js.translate.test.JSTestGenerator;
import org.jetbrains.k2js.translate.test.JSTester;
@@ -88,9 +90,7 @@ public final class Translation {
//NOTE: use with care
@NotNull
public static JsNode doTranslateExpression(JetExpression expression, TranslationContext context) {
JsNode jsNode = expression.accept(new ExpressionVisitor(), context);
jsNode.setSourceInfo(expression);
return jsNode;
return expression.accept(new ExpressionVisitor(), context);
}
@NotNull
@@ -105,12 +105,6 @@ public final class Translation {
return convertToStatement(translateExpression(expression, context));
}
@Nullable
public static JsNode translateWhenExpression(@NotNull JetWhenExpression expression,
@NotNull TranslationContext context) {
return WhenTranslator.translate(expression, context);
}
@NotNull
public static JsProgram generateAst(@NotNull BindingContext bindingContext,
@NotNull Collection<JetFile> files, @NotNull MainCallParameters mainCallParameters,
@@ -37,6 +37,7 @@ import static org.jetbrains.k2js.translate.operation.CompareToTranslator.isCompa
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.not;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.source;
import static org.jetbrains.k2js.translate.utils.PsiUtils.*;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression;
@@ -47,13 +48,15 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
public static JsExpression translate(@NotNull JetBinaryExpression expression,
@NotNull TranslationContext context) {
return (new BinaryOperationTranslator(expression, context).translate());
JsExpression jsExpression = new BinaryOperationTranslator(expression, context).translate();
return source(jsExpression, expression);
}
@NotNull
/*package*/ static JsExpression translateAsOverloadedCall(@NotNull JetBinaryExpression expression,
@NotNull TranslationContext context) {
return (new BinaryOperationTranslator(expression, context)).translateAsOverloadedBinaryOperation();
JsExpression jsExpression = (new BinaryOperationTranslator(expression, context)).translateAsOverloadedBinaryOperation();
return source(jsExpression, expression);
}
@NotNull
@@ -79,7 +79,7 @@ public final class CallTranslator extends AbstractTranslator {
}
@NotNull
/*package*/ JsExpression translate() {
/*package*/ JsExpression translate() {
JsExpression result = intrinsicInvocation();
if (result != null) {
return result;
@@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.Modality;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.k2js.translate.context.TranslationContext;
import java.util.Arrays;
@@ -296,4 +297,11 @@ public final class JsAstUtils {
public static JsObjectLiteral wrapValue(@NotNull JsExpression label, @NotNull JsExpression value) {
return new JsObjectLiteral(Collections.singletonList(new JsPropertyInitializer(label, value)));
}
@NotNull
public static <T extends JsNode> T source(@NotNull T jsNode, @NotNull JetElement ktElement) {
jsNode.setSourceInfo(ktElement);
return jsNode;
}
}