JS: don't load binary AST unless required by the inliner

This commit is contained in:
Anton Bannykh
2018-11-22 15:20:25 +03:00
parent c83b6d46cc
commit 6d26bf9622
22 changed files with 1165 additions and 87 deletions
@@ -25,7 +25,7 @@ interface IncrementalResultsConsumer {
fun processHeader(headerMetadata: ByteArray)
/** processes new package part metadata and binary tree for compiled source file */
fun processPackagePart(sourceFile: File, packagePartMetadata: ByteArray, binaryAst: ByteArray)
fun processPackagePart(sourceFile: File, packagePartMetadata: ByteArray, binaryAst: ByteArray, header: ByteArray)
/**
* [inlineFunction] is expected to be a body of inline function (an instance of [JsNode]),
@@ -84,8 +84,8 @@ class IncrementalResultsConsumerImpl : IncrementalResultsConsumer {
this.headerMetadata = headerMetadata
}
override fun processPackagePart(sourceFile: File, packagePartMetadata: ByteArray, binaryAst: ByteArray) {
_packageParts.put(sourceFile, TranslationResultValue(packagePartMetadata, binaryAst))
override fun processPackagePart(sourceFile: File, packagePartMetadata: ByteArray, binaryAst: ByteArray, header: ByteArray) {
_packageParts.put(sourceFile, TranslationResultValue(packagePartMetadata, binaryAst, header))
}
override fun processInlineFunctions(functions: Collection<JsInlineFunctionHash>) {
@@ -16,5 +16,5 @@
package org.jetbrains.kotlin.incremental.js
data class TranslationResultValue(val metadata: ByteArray, val binaryAst: ByteArray)
data class TranslationResultValue(val metadata: ByteArray, val binaryAst: ByteArray, val header: ByteArray)
@@ -95,7 +95,8 @@ class JsInliner private constructor(
companion object {
// TODO decrypt
// TODO decrypt; Possibly error-prone due to late renaming
// TODO Possibly apply post-processing outside of FunctionReader?
// Since we compile each source file in its own context (and we may loose these context when performing incremental compilation)
// we don't use contexts to generate proper names for modules. Instead, we generate all necessary information during
// translation and rely on it here.
@@ -110,20 +111,29 @@ class JsInliner private constructor(
trace: DiagnosticSink,
translationResult: AstGenerationResult
) {
val functions = collectNamedFunctionsAndWrappers(translationResult.newFragments)
val accessors = collectAccessors(translationResult.fragments)
val inverseNameBindings = inverseNameBindings(*translationResult.fragments.toTypedArray())
val accessorInvocationTransformer = DummyAccessorInvocationTransformer()
for (fragment in translationResult.newFragments) {
accessorInvocationTransformer.accept<JsGlobalBlock>(fragment.declarationBlock)
accessorInvocationTransformer.accept<JsGlobalBlock>(fragment.initializerBlock)
}
val functionReader = FunctionReader(reporter, config, translationResult.innerModuleName, buildModuleNameMap(translationResult.fragments))
val moduleMap = translationResult.importedModuleList.associate { it.internalName to it }
val referencedInlineFunctionTags = collectInlineFunctionTags(config, translationResult.newFragments)
val fragments = referencedInlineFunctionTags.mapNotNull {
translationResult.inlineFunctionTagMap[it]?.let { unit ->
translationResult.translate(unit).fragment
}
}
val functions = collectNamedFunctionsAndWrappers(translationResult.newFragments)
val accessors = collectAccessors(fragments)
// TODO update on fragment loading
val inverseNameBindings = inverseNameBindings(*fragments.toTypedArray())
val functionReader = FunctionReader(reporter, config, translationResult.innerModuleName, buildModuleNameMap(fragments))
val moduleMap = fragments.asSequence().flatMap { it.importedModules.asSequence() }.associate { it.internalName to it }
val inliner = JsInliner(config, functionReader, functions, accessors, inverseNameBindings, moduleMap, trace)
for (fragment in translationResult.newFragments) {
@@ -153,6 +163,5 @@ class JsInliner private constructor(
return name2Tag
}
}
}
@@ -18,8 +18,11 @@ package org.jetbrains.kotlin.js.inline.util
import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor
import org.jetbrains.kotlin.js.backend.ast.metadata.imported
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.inline.util.collectors.InstanceCollector
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
@@ -235,6 +238,26 @@ fun extractFunction(expression: JsExpression) = when (expression) {
else -> InlineMetadata.decompose(expression)?.function ?: InlineMetadata.tryExtractFunction(expression)
}
fun collectInlineFunctionTags(config: JsConfig, fragments: List<JsProgramFragment>): Set<String> {
val result = mutableSetOf<String>()
for (fragment in fragments) {
collectInlineFunctionTags(config, fragment.declarationBlock, result)
collectInlineFunctionTags(config, fragment.initializerBlock, result)
}
return result
}
private fun collectInlineFunctionTags(config: JsConfig, scope: JsNode, result: MutableSet<String>) {
scope.accept(object : RecursiveJsVisitor() {
override fun visitInvocation(invocation: JsInvocation) {
invocation.descriptor?.let {
result.add(Namer.getFunctionTag(it, config))
}
super.visitInvocation(invocation)
}
})
}
fun <T : JsNode> collectInstances(klass: Class<T>, scope: JsNode): List<T> {
return with(InstanceCollector(klass, visitNestedDeclarations = false)) {
accept(scope)
+7
View File
@@ -457,4 +457,11 @@ message Chunk {
required StringTable string_table = 1;
required NameTable name_table = 2;
required Fragment fragment = 3;
}
// Header
//
message Header {
repeated string inline_function_tags = 1;
}
@@ -36958,6 +36958,460 @@ public final class JsAstProtoBuf {
// @@protoc_insertion_point(class_scope:org.jetbrains.kotlin.serialization.js.ast.Chunk)
}
public interface HeaderOrBuilder extends
// @@protoc_insertion_point(interface_extends:org.jetbrains.kotlin.serialization.js.ast.Header)
org.jetbrains.kotlin.protobuf.MessageLiteOrBuilder {
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
org.jetbrains.kotlin.protobuf.ProtocolStringList
getInlineFunctionTagsList();
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
int getInlineFunctionTagsCount();
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
java.lang.String getInlineFunctionTags(int index);
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
org.jetbrains.kotlin.protobuf.ByteString
getInlineFunctionTagsBytes(int index);
}
/**
* Protobuf type {@code org.jetbrains.kotlin.serialization.js.ast.Header}
*/
public static final class Header extends
org.jetbrains.kotlin.protobuf.GeneratedMessageLite implements
// @@protoc_insertion_point(message_implements:org.jetbrains.kotlin.serialization.js.ast.Header)
HeaderOrBuilder {
// Use Header.newBuilder() to construct.
private Header(org.jetbrains.kotlin.protobuf.GeneratedMessageLite.Builder builder) {
super(builder);
this.unknownFields = builder.getUnknownFields();
}
private Header(boolean noInit) { this.unknownFields = org.jetbrains.kotlin.protobuf.ByteString.EMPTY;}
private static final Header defaultInstance;
public static Header getDefaultInstance() {
return defaultInstance;
}
public Header getDefaultInstanceForType() {
return defaultInstance;
}
private final org.jetbrains.kotlin.protobuf.ByteString unknownFields;
private Header(
org.jetbrains.kotlin.protobuf.CodedInputStream input,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException {
initFields();
int mutable_bitField0_ = 0;
org.jetbrains.kotlin.protobuf.ByteString.Output unknownFieldsOutput =
org.jetbrains.kotlin.protobuf.ByteString.newOutput();
org.jetbrains.kotlin.protobuf.CodedOutputStream unknownFieldsCodedOutput =
org.jetbrains.kotlin.protobuf.CodedOutputStream.newInstance(
unknownFieldsOutput, 1);
try {
boolean done = false;
while (!done) {
int tag = input.readTag();
switch (tag) {
case 0:
done = true;
break;
default: {
if (!parseUnknownField(input, unknownFieldsCodedOutput,
extensionRegistry, tag)) {
done = true;
}
break;
}
case 10: {
org.jetbrains.kotlin.protobuf.ByteString bs = input.readBytes();
if (!((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
inlineFunctionTags_ = new org.jetbrains.kotlin.protobuf.LazyStringArrayList();
mutable_bitField0_ |= 0x00000001;
}
inlineFunctionTags_.add(bs);
break;
}
}
}
} catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(this);
} catch (java.io.IOException e) {
throw new org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException(
e.getMessage()).setUnfinishedMessage(this);
} finally {
if (((mutable_bitField0_ & 0x00000001) == 0x00000001)) {
inlineFunctionTags_ = inlineFunctionTags_.getUnmodifiableView();
}
try {
unknownFieldsCodedOutput.flush();
} catch (java.io.IOException e) {
// Should not happen
} finally {
unknownFields = unknownFieldsOutput.toByteString();
}
makeExtensionsImmutable();
}
}
public static org.jetbrains.kotlin.protobuf.Parser<Header> PARSER =
new org.jetbrains.kotlin.protobuf.AbstractParser<Header>() {
public Header parsePartialFrom(
org.jetbrains.kotlin.protobuf.CodedInputStream input,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException {
return new Header(input, extensionRegistry);
}
};
@java.lang.Override
public org.jetbrains.kotlin.protobuf.Parser<Header> getParserForType() {
return PARSER;
}
public static final int INLINE_FUNCTION_TAGS_FIELD_NUMBER = 1;
private org.jetbrains.kotlin.protobuf.LazyStringList inlineFunctionTags_;
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public org.jetbrains.kotlin.protobuf.ProtocolStringList
getInlineFunctionTagsList() {
return inlineFunctionTags_;
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public int getInlineFunctionTagsCount() {
return inlineFunctionTags_.size();
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public java.lang.String getInlineFunctionTags(int index) {
return inlineFunctionTags_.get(index);
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public org.jetbrains.kotlin.protobuf.ByteString
getInlineFunctionTagsBytes(int index) {
return inlineFunctionTags_.getByteString(index);
}
private void initFields() {
inlineFunctionTags_ = org.jetbrains.kotlin.protobuf.LazyStringArrayList.EMPTY;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
byte isInitialized = memoizedIsInitialized;
if (isInitialized == 1) return true;
if (isInitialized == 0) return false;
memoizedIsInitialized = 1;
return true;
}
public void writeTo(org.jetbrains.kotlin.protobuf.CodedOutputStream output)
throws java.io.IOException {
getSerializedSize();
for (int i = 0; i < inlineFunctionTags_.size(); i++) {
output.writeBytes(1, inlineFunctionTags_.getByteString(i));
}
output.writeRawBytes(unknownFields);
}
private int memoizedSerializedSize = -1;
public int getSerializedSize() {
int size = memoizedSerializedSize;
if (size != -1) return size;
size = 0;
{
int dataSize = 0;
for (int i = 0; i < inlineFunctionTags_.size(); i++) {
dataSize += org.jetbrains.kotlin.protobuf.CodedOutputStream
.computeBytesSizeNoTag(inlineFunctionTags_.getByteString(i));
}
size += dataSize;
size += 1 * getInlineFunctionTagsList().size();
}
size += unknownFields.size();
memoizedSerializedSize = size;
return size;
}
private static final long serialVersionUID = 0L;
@java.lang.Override
protected java.lang.Object writeReplace()
throws java.io.ObjectStreamException {
return super.writeReplace();
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(
org.jetbrains.kotlin.protobuf.ByteString data)
throws org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(
org.jetbrains.kotlin.protobuf.ByteString data,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(byte[] data)
throws org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(
byte[] data,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException {
return PARSER.parseFrom(data, extensionRegistry);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(java.io.InputStream input)
throws java.io.IOException {
return PARSER.parseFrom(input);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(
java.io.InputStream input,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return PARSER.parseFrom(input, extensionRegistry);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseDelimitedFrom(java.io.InputStream input)
throws java.io.IOException {
return PARSER.parseDelimitedFrom(input);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseDelimitedFrom(
java.io.InputStream input,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return PARSER.parseDelimitedFrom(input, extensionRegistry);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(
org.jetbrains.kotlin.protobuf.CodedInputStream input)
throws java.io.IOException {
return PARSER.parseFrom(input);
}
public static org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parseFrom(
org.jetbrains.kotlin.protobuf.CodedInputStream input,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
return PARSER.parseFrom(input, extensionRegistry);
}
public static Builder newBuilder() { return Builder.create(); }
public Builder newBuilderForType() { return newBuilder(); }
public static Builder newBuilder(org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header prototype) {
return newBuilder().mergeFrom(prototype);
}
public Builder toBuilder() { return newBuilder(this); }
/**
* Protobuf type {@code org.jetbrains.kotlin.serialization.js.ast.Header}
*/
public static final class Builder extends
org.jetbrains.kotlin.protobuf.GeneratedMessageLite.Builder<
org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header, Builder>
implements
// @@protoc_insertion_point(builder_implements:org.jetbrains.kotlin.serialization.js.ast.Header)
org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.HeaderOrBuilder {
// Construct using org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header.newBuilder()
private Builder() {
maybeForceBuilderInitialization();
}
private void maybeForceBuilderInitialization() {
}
private static Builder create() {
return new Builder();
}
public Builder clear() {
super.clear();
inlineFunctionTags_ = org.jetbrains.kotlin.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000001);
return this;
}
public Builder clone() {
return create().mergeFrom(buildPartial());
}
public org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header getDefaultInstanceForType() {
return org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header.getDefaultInstance();
}
public org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header build() {
org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header result = buildPartial();
if (!result.isInitialized()) {
throw newUninitializedMessageException(result);
}
return result;
}
public org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header buildPartial() {
org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header result = new org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header(this);
int from_bitField0_ = bitField0_;
if (((bitField0_ & 0x00000001) == 0x00000001)) {
inlineFunctionTags_ = inlineFunctionTags_.getUnmodifiableView();
bitField0_ = (bitField0_ & ~0x00000001);
}
result.inlineFunctionTags_ = inlineFunctionTags_;
return result;
}
public Builder mergeFrom(org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header other) {
if (other == org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header.getDefaultInstance()) return this;
if (!other.inlineFunctionTags_.isEmpty()) {
if (inlineFunctionTags_.isEmpty()) {
inlineFunctionTags_ = other.inlineFunctionTags_;
bitField0_ = (bitField0_ & ~0x00000001);
} else {
ensureInlineFunctionTagsIsMutable();
inlineFunctionTags_.addAll(other.inlineFunctionTags_);
}
}
setUnknownFields(
getUnknownFields().concat(other.unknownFields));
return this;
}
public final boolean isInitialized() {
return true;
}
public Builder mergeFrom(
org.jetbrains.kotlin.protobuf.CodedInputStream input,
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite extensionRegistry)
throws java.io.IOException {
org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header parsedMessage = null;
try {
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
} catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) {
parsedMessage = (org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf.Header) e.getUnfinishedMessage();
throw e;
} finally {
if (parsedMessage != null) {
mergeFrom(parsedMessage);
}
}
return this;
}
private int bitField0_;
private org.jetbrains.kotlin.protobuf.LazyStringList inlineFunctionTags_ = org.jetbrains.kotlin.protobuf.LazyStringArrayList.EMPTY;
private void ensureInlineFunctionTagsIsMutable() {
if (!((bitField0_ & 0x00000001) == 0x00000001)) {
inlineFunctionTags_ = new org.jetbrains.kotlin.protobuf.LazyStringArrayList(inlineFunctionTags_);
bitField0_ |= 0x00000001;
}
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public org.jetbrains.kotlin.protobuf.ProtocolStringList
getInlineFunctionTagsList() {
return inlineFunctionTags_.getUnmodifiableView();
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public int getInlineFunctionTagsCount() {
return inlineFunctionTags_.size();
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public java.lang.String getInlineFunctionTags(int index) {
return inlineFunctionTags_.get(index);
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public org.jetbrains.kotlin.protobuf.ByteString
getInlineFunctionTagsBytes(int index) {
return inlineFunctionTags_.getByteString(index);
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public Builder setInlineFunctionTags(
int index, java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
ensureInlineFunctionTagsIsMutable();
inlineFunctionTags_.set(index, value);
return this;
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public Builder addInlineFunctionTags(
java.lang.String value) {
if (value == null) {
throw new NullPointerException();
}
ensureInlineFunctionTagsIsMutable();
inlineFunctionTags_.add(value);
return this;
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public Builder addAllInlineFunctionTags(
java.lang.Iterable<java.lang.String> values) {
ensureInlineFunctionTagsIsMutable();
org.jetbrains.kotlin.protobuf.AbstractMessageLite.Builder.addAll(
values, inlineFunctionTags_);
return this;
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public Builder clearInlineFunctionTags() {
inlineFunctionTags_ = org.jetbrains.kotlin.protobuf.LazyStringArrayList.EMPTY;
bitField0_ = (bitField0_ & ~0x00000001);
return this;
}
/**
* <code>repeated string inline_function_tags = 1;</code>
*/
public Builder addInlineFunctionTagsBytes(
org.jetbrains.kotlin.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
ensureInlineFunctionTagsIsMutable();
inlineFunctionTags_.add(value);
return this;
}
// @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.serialization.js.ast.Header)
}
static {
defaultInstance = new Header(true);
defaultInstance.initFields();
}
// @@protoc_insertion_point(class_scope:org.jetbrains.kotlin.serialization.js.ast.Header)
}
static {
}
@@ -382,7 +382,7 @@ abstract class BasicBoxTest(
}
}
for ((sourceFile, data) in incrementalData.translatedFiles) {
sourceToTranslationUnit[sourceFile] = TranslationUnit.BinaryAst(data.binaryAst)
sourceToTranslationUnit[sourceFile] = TranslationUnit.BinaryAst(data.binaryAst, data.header)
}
val translationUnits = sourceToTranslationUnit.keys
.sortedBy { it.canonicalPath }
@@ -47,7 +47,9 @@ import org.jetbrains.kotlin.js.backend.ast.JsGlobalBlock
import org.jetbrains.kotlin.js.backend.ast.JsProgramFragment
import org.jetbrains.kotlin.js.coroutine.transformCoroutines
import org.jetbrains.kotlin.js.translate.general.AstGenerationResult
import org.jetbrains.kotlin.metadata.js.JsProtoBuf
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf
/**
* An entry point of translator.
@@ -129,7 +131,7 @@ class K2JSTranslator(private val config: JsConfig) {
expandIsCalls(translationResult.newFragments)
checkCanceled()
trySaveIncrementalData(files, translationResult, pathResolver, bindingTrace, moduleDescriptor)
trySaveIncrementalData(translationResult, pathResolver, bindingTrace, moduleDescriptor)
checkCanceled()
// Global phases
@@ -159,7 +161,6 @@ class K2JSTranslator(private val config: JsConfig) {
}
private fun trySaveIncrementalData(
files: List<KtFile>,
translationResult: AstGenerationResult,
pathResolver: SourceFilePathResolver,
bindingTrace: BindingTrace,
@@ -175,13 +176,14 @@ class K2JSTranslator(private val config: JsConfig) {
}
}
for (file in files) {
val fragment = translationResult.fragmentMap[file] ?: error("Could not find AST for file: $file")
for ((sourceUnit, fileTranslationResult) in translationResult.translatedSourceFiles) {
val file = sourceUnit.file
val fragment = fileTranslationResult.fragment
val output = ByteArrayOutputStream()
serializer.serialize(fragment, output)
val binaryAst = output.toByteArray()
val scope = translationResult.fileMemberScopes[file] ?: error("Could not find descriptors for file: $file")
val scope = fileTranslationResult.memberScope
val metadataVersion = config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)
val packagePart = KotlinJavascriptSerializationUtil.serializeDescriptors(
bindingTrace.bindingContext, moduleDescriptor, scope, file.packageFqName,
@@ -189,11 +191,21 @@ class K2JSTranslator(private val config: JsConfig) {
metadataVersion ?: JsMetadataVersion.INSTANCE
)
val header = serializeHeader(fileTranslationResult.inlineFunctionTags)
val ioFile = VfsUtilCore.virtualToIoFile(file.virtualFile)
incrementalResults.processPackagePart(ioFile, packagePart.toByteArray(), binaryAst)
incrementalResults.processPackagePart(ioFile, packagePart.toByteArray(), binaryAst, header)
}
val settings = config.configuration.languageVersionSettings
incrementalResults.processHeader(KotlinJavascriptSerializationUtil.serializeHeader(moduleDescriptor, null, settings).toByteArray())
}
fun serializeHeader(importedTags: Set<String>): ByteArray {
val output = ByteArrayOutputStream()
val headerBuilder = JsAstProtoBuf.Header.newBuilder()
headerBuilder.addAllInlineFunctionTags(importedTags)
headerBuilder.build().writeTo(output)
return output.toByteArray()
}
}
@@ -21,5 +21,5 @@ import org.jetbrains.kotlin.psi.KtFile
sealed class TranslationUnit {
class SourceFile(val file: KtFile) : TranslationUnit()
class BinaryAst(val data: ByteArray) : TranslationUnit()
class BinaryAst(val data: ByteArray, val header: ByteArray) : TranslationUnit()
}
@@ -518,6 +518,16 @@ public final class StaticContext {
return localDescriptor != null && DescriptorUtils.getContainingModule(localDescriptor) == currentModule;
}
private final Set<String> inlineFunctionTags = new HashSet<>();
@NotNull public Set<String> getInlineFunctionTags() {
return inlineFunctionTags;
}
public void reportInlineFunctionTag(@NotNull String tag) {
inlineFunctionTags.add(tag);
}
private final class InnerNameGenerator extends Generator<JsName> {
public InnerNameGenerator() {
addRule(descriptor -> {
@@ -958,4 +958,8 @@ public class TranslationContext {
public LanguageVersionSettings getLanguageVersionSettings() {
return CommonConfigurationKeysKt.getLanguageVersionSettings(staticContext.getConfig().getConfiguration());
}
public void reportInlineFunctionTag(@NotNull String tag) {
staticContext.reportInlineFunctionTag(tag);
}
}
@@ -34,6 +34,7 @@ class InlineMetadata(val tag: JsStringLiteral, val function: FunctionWithWrapper
val inliningContext = context.inlineFunctionContext!!
val block = JsBlock(inliningContext.importBlock.statements + inliningContext.prototypeBlock.statements +
inliningContext.declarationsBlock.statements + JsReturn(function))
context.reportInlineFunctionTag(tag.value);
return InlineMetadata(tag, FunctionWithWrapper(function, block))
}
@@ -19,28 +19,76 @@ package org.jetbrains.kotlin.js.translate.general
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.facade.TranslationUnit
import org.jetbrains.kotlin.protobuf.CodedInputStream
import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializer
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf
import java.io.ByteArrayInputStream
import java.io.File
import java.util.HashSet
class AstGenerationResult(
val fragments: List<JsProgramFragment>,
val fragmentMap: Map<KtFile, JsProgramFragment>,
val newFragments: List<JsProgramFragment>,
val fileMemberScopes: Map<KtFile, List<DeclarationDescriptor>>,
private val program: JsProgram,
private val moduleDescriptor: ModuleDescriptor,
private val moduleId: String,
private val moduleKind: ModuleKind
val units: Collection<TranslationUnit>,
val translatedSourceFiles: Map<TranslationUnit.SourceFile, SourceFileTranslationResult>,
val inlineFunctionTagMap: Map<String, TranslationUnit>,
moduleDescriptor: ModuleDescriptor,
config: JsConfig
) {
val innerModuleName = program.getScope().declareName("_");
// TODO remove
val newFragments = translatedSourceFiles.values.map { it.fragment }
// Only available after the merge
val importedModuleList: List<JsImportedModule>
get() = fragments.flatMap { it.importedModules }
get() = merger.importedModules
val innerModuleName: JsName
get() = merger.internalModuleName
private val cache = mutableMapOf<TranslationUnit.BinaryAst, DeserializedFileTranslationResult>()
private val merger = Merger(moduleDescriptor, config.moduleId, config.moduleKind)
private val sourceRoots = config.sourceMapRoots.map { File(it) }
private val deserializer = JsAstDeserializer(merger.program, sourceRoots)
fun translate(unit: TranslationUnit) =
when (unit) {
is TranslationUnit.SourceFile -> translatedSourceFiles[unit]!!
is TranslationUnit.BinaryAst -> cache.getOrPut(unit) {
// TODO Don't deserialize header twice
val headerData = unit.header
val header = JsAstProtoBuf.Header.parseFrom(CodedInputStream.newInstance(headerData))
DeserializedFileTranslationResult(
deserializer.deserialize(ByteArrayInputStream(unit.data)),
HashSet(header.inlineFunctionTagsList)
)
}
}
fun buildProgram(): JsProgram {
val merger = Merger(program, innerModuleName, moduleDescriptor, moduleId, moduleKind)
val fragments = units.map { translate(it).fragment }
fragments.forEach { merger.addFragment(it) }
return merger.buildProgram()
}
}
}
sealed class FileTranslationResult {
abstract val fragment: JsProgramFragment
abstract val inlineFunctionTags: Set<String>
}
class SourceFileTranslationResult(
override val fragment: JsProgramFragment,
override val inlineFunctionTags: Set<String>,
val memberScope: List<DeclarationDescriptor>
) : FileTranslationResult()
class DeserializedFileTranslationResult(
override val fragment: JsProgramFragment,
override val inlineFunctionTags: Set<String>
) : FileTranslationResult()
@@ -29,12 +29,14 @@ import org.jetbrains.kotlin.js.translate.utils.definePackageAlias
import org.jetbrains.kotlin.serialization.js.ModuleKind
class Merger(
val program: JsProgram,
val internalModuleName: JsName,
val module: ModuleDescriptor,
val moduleId: String,
val moduleKind: ModuleKind
) {
val program: JsProgram = JsProgram()
val internalModuleName = program.scope.declareName("_")
private val rootFunction = JsFunction(program.rootScope, JsBlock(), "root function").also {
it.parameters.add(JsParameter(internalModuleName))
}
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.js.facade.exceptions.UnsupportedFeatureException;
import org.jetbrains.kotlin.js.naming.NameSuggestion;
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.StaticContext;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
@@ -48,6 +47,7 @@ import org.jetbrains.kotlin.js.translate.utils.*;
import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator;
import org.jetbrains.kotlin.name.FqNameUnsafe;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.protobuf.CodedInputStream;
import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtFile;
@@ -58,17 +58,14 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializer;
import org.jetbrains.kotlin.serialization.js.ast.JsAstProtoBuf;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static org.jetbrains.kotlin.js.translate.general.ModuleWrapperTranslation.wrapIfNecessary;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.toStringLiteralList;
import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
@@ -323,51 +320,43 @@ public final class Translation {
@NotNull ModuleDescriptor moduleDescriptor,
@NotNull JsConfig config,
@NotNull SourceFilePathResolver sourceFilePathResolver
) {
) throws IOException {
JsProgram program = new JsProgram();
Map<String, TranslationUnit> inlineFunctionTagMap = new HashMap<>();
Map<KtFile, JsProgramFragment> fragmentMap = new HashMap<>();
List<JsProgramFragment> fragments = new ArrayList<>();
List<JsProgramFragment> newFragments = new ArrayList<>();
Map<TranslationUnit.SourceFile, SourceFileTranslationResult> translatedSourceFiles = new HashMap<>();
Map<KtFile, List<DeclarationDescriptor>> fileMemberScopes = new HashMap<>();
List<File> sourceRoots = config.getSourceMapRoots().stream().map(File::new).collect(Collectors.toList());
JsAstDeserializer deserializer = new JsAstDeserializer(program, sourceRoots);
for (TranslationUnit unit : units) {
if (unit instanceof TranslationUnit.SourceFile) {
KtFile file = ((TranslationUnit.SourceFile) unit).getFile();
TranslationUnit.SourceFile sourceFileUnit = ((TranslationUnit.SourceFile) unit);
KtFile file = sourceFileUnit.getFile();
StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor, sourceFilePathResolver, file.getPackageFqName().asString());
TranslationContext context = TranslationContext.rootContext(staticContext);
List<DeclarationDescriptor> fileMemberScope = new ArrayList<>();
translateFile(context, file, fileMemberScope);
JsProgramFragment fragment = staticContext.getFragment();
for (String tag : staticContext.getInlineFunctionTags()) {
inlineFunctionTagMap.put(tag, unit);
}
fragment.setTests(mayBeGenerateTests(context, file, fileMemberScope));
fragment.setMainFunction(maybeGenerateCallToMain(context, config, moduleDescriptor, fileMemberScope, mainCallParameters));
fragments.add(fragment);
newFragments.add(fragment);
fragmentMap.put(file, fragment);
fileMemberScopes.put(file, fileMemberScope);
translatedSourceFiles.put(sourceFileUnit, new SourceFileTranslationResult(fragment, staticContext.getInlineFunctionTags(), fileMemberScope));
}
else if (unit instanceof TranslationUnit.BinaryAst) {
byte[] astData = ((TranslationUnit.BinaryAst) unit).getData();
JsProgramFragment fragment = deserializer.deserialize(new ByteArrayInputStream(astData));
fragments.add(fragment);
byte[] headerData = ((TranslationUnit.BinaryAst) unit).getHeader();
JsAstProtoBuf.Header h = JsAstProtoBuf.Header.parseFrom(CodedInputStream.newInstance(headerData));
for (String tag : h.getInlineFunctionTagsList()) {
inlineFunctionTagMap.put(tag, unit);
}
}
}
return new AstGenerationResult(fragments,
fragmentMap,
newFragments,
fileMemberScopes,
program,
return new AstGenerationResult(units,
translatedSourceFiles,
inlineFunctionTagMap,
moduleDescriptor,
config.getModuleId(),
config.getModuleKind());
config);
}
private static void translateFile(