diff --git a/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java b/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
index 48e2dd0726a..a57332ccd12 100644
--- a/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
+++ b/build-common/test/org/jetbrains/kotlin/serialization/js/ast/DebugJsAstProtoBuf.java
@@ -47071,6 +47071,15 @@ public final class DebugJsAstProtoBuf {
* optional int32 local_name_id = 3;
*/
int getLocalNameId();
+
+ /**
+ * optional bool imported = 4;
+ */
+ boolean hasImported();
+ /**
+ * optional bool imported = 4;
+ */
+ boolean getImported();
}
/**
* Protobuf type {@code org.jetbrains.kotlin.serialization.js.ast.Name}
@@ -47139,6 +47148,11 @@ public final class DebugJsAstProtoBuf {
localNameId_ = input.readInt32();
break;
}
+ case 32: {
+ bitField0_ |= 0x00000008;
+ imported_ = input.readBool();
+ break;
+ }
}
}
} catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) {
@@ -47224,10 +47238,26 @@ public final class DebugJsAstProtoBuf {
return localNameId_;
}
+ public static final int IMPORTED_FIELD_NUMBER = 4;
+ private boolean imported_;
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean hasImported() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean getImported() {
+ return imported_;
+ }
+
private void initFields() {
temporary_ = false;
identifier_ = 0;
localNameId_ = 0;
+ imported_ = false;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -47255,6 +47285,9 @@ public final class DebugJsAstProtoBuf {
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeInt32(3, localNameId_);
}
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ output.writeBool(4, imported_);
+ }
getUnknownFields().writeTo(output);
}
@@ -47276,6 +47309,10 @@ public final class DebugJsAstProtoBuf {
size += org.jetbrains.kotlin.protobuf.CodedOutputStream
.computeInt32Size(3, localNameId_);
}
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ size += org.jetbrains.kotlin.protobuf.CodedOutputStream
+ .computeBoolSize(4, imported_);
+ }
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -47399,6 +47436,8 @@ public final class DebugJsAstProtoBuf {
bitField0_ = (bitField0_ & ~0x00000002);
localNameId_ = 0;
bitField0_ = (bitField0_ & ~0x00000004);
+ imported_ = false;
+ bitField0_ = (bitField0_ & ~0x00000008);
return this;
}
@@ -47439,6 +47478,10 @@ public final class DebugJsAstProtoBuf {
to_bitField0_ |= 0x00000004;
}
result.localNameId_ = localNameId_;
+ if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+ to_bitField0_ |= 0x00000008;
+ }
+ result.imported_ = imported_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -47464,6 +47507,9 @@ public final class DebugJsAstProtoBuf {
if (other.hasLocalNameId()) {
setLocalNameId(other.getLocalNameId());
}
+ if (other.hasImported()) {
+ setImported(other.getImported());
+ }
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -47591,6 +47637,38 @@ public final class DebugJsAstProtoBuf {
return this;
}
+ private boolean imported_ ;
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean hasImported() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean getImported() {
+ return imported_;
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public Builder setImported(boolean value) {
+ bitField0_ |= 0x00000008;
+ imported_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public Builder clearImported() {
+ bitField0_ = (bitField0_ & ~0x00000008);
+ imported_ = false;
+ onChanged();
+ return this;
+ }
+
// @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.serialization.js.ast.Name)
}
@@ -49082,19 +49160,19 @@ public final class DebugJsAstProtoBuf {
"ule\022\024\n\014signature_id\030\001 \002(\005\022\025\n\rexpression_" +
"id\030\002 \002(\005\"\034\n\013StringTable\022\r\n\005entry\030\001 \003(\t\"K" +
"\n\tNameTable\022>\n\005entry\030\001 \003(\0132/.org.jetbrai",
- "ns.kotlin.serialization.js.ast.Name\"D\n\004N" +
+ "ns.kotlin.serialization.js.ast.Name\"V\n\004N" +
"ame\022\021\n\ttemporary\030\001 \002(\010\022\022\n\nidentifier\030\002 \001" +
- "(\005\022\025\n\rlocal_name_id\030\003 \001(\005\"\346\001\n\005Chunk\022L\n\014s" +
- "tring_table\030\001 \002(\01326.org.jetbrains.kotlin" +
- ".serialization.js.ast.StringTable\022H\n\nnam" +
- "e_table\030\002 \002(\01324.org.jetbrains.kotlin.ser" +
- "ialization.js.ast.NameTable\022E\n\010fragment\030" +
- "\003 \002(\01323.org.jetbrains.kotlin.serializati" +
- "on.js.ast.Fragment*@\n\013SideEffects\022\021\n\rAFF" +
- "ECTS_STATE\020\001\022\024\n\020DEPENDS_ON_STATE\020\002\022\010\n\004PU",
- "RE\020\003*?\n\016InlineStrategy\022\017\n\013AS_FUNCTION\020\000\022" +
- "\014\n\010IN_PLACE\020\001\022\016\n\nNOT_INLINE\020\002B\024B\022DebugJs" +
- "AstProtoBuf"
+ "(\005\022\025\n\rlocal_name_id\030\003 \001(\005\022\020\n\010imported\030\004 " +
+ "\001(\010\"\346\001\n\005Chunk\022L\n\014string_table\030\001 \002(\01326.or" +
+ "g.jetbrains.kotlin.serialization.js.ast." +
+ "StringTable\022H\n\nname_table\030\002 \002(\01324.org.je" +
+ "tbrains.kotlin.serialization.js.ast.Name" +
+ "Table\022E\n\010fragment\030\003 \002(\01323.org.jetbrains." +
+ "kotlin.serialization.js.ast.Fragment*@\n\013" +
+ "SideEffects\022\021\n\rAFFECTS_STATE\020\001\022\024\n\020DEPEND",
+ "S_ON_STATE\020\002\022\010\n\004PURE\020\003*?\n\016InlineStrategy" +
+ "\022\017\n\013AS_FUNCTION\020\000\022\014\n\010IN_PLACE\020\001\022\016\n\nNOT_I" +
+ "NLINE\020\002B\024B\022DebugJsAstProtoBuf"
};
org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new org.jetbrains.kotlin.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
@@ -49431,7 +49509,7 @@ public final class DebugJsAstProtoBuf {
internal_static_org_jetbrains_kotlin_serialization_js_ast_Name_fieldAccessorTable = new
org.jetbrains.kotlin.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_org_jetbrains_kotlin_serialization_js_ast_Name_descriptor,
- new java.lang.String[] { "Temporary", "Identifier", "LocalNameId", });
+ new java.lang.String[] { "Temporary", "Identifier", "LocalNameId", "Imported", });
internal_static_org_jetbrains_kotlin_serialization_js_ast_Chunk_descriptor =
getDescriptor().getMessageTypes().get(54);
internal_static_org_jetbrains_kotlin_serialization_js_ast_Chunk_fieldAccessorTable = new
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
index f6d3127b423..f80e35e366e 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/FunctionReader.kt
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind
+import org.jetbrains.kotlin.js.backend.ast.metadata.imported
import org.jetbrains.kotlin.js.backend.ast.metadata.inlineStrategy
import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects
import org.jetbrains.kotlin.js.config.JsConfig
@@ -237,6 +238,12 @@ class FunctionReader(
}
})
+ wrapperStatements?.forEach {
+ if (it is JsVars && it.vars.size == 1 && it.vars[0].initExpression?.let { extractImportTag(it) } != null) {
+ it.vars[0].name.imported = true
+ }
+ }
+
return FunctionWithWrapper(function, wrapper)
}
}
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java
index f25a2956b4f..08d3cd58056 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.java
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.diagnostics.DiagnosticSink;
import org.jetbrains.kotlin.diagnostics.Errors;
-import org.jetbrains.kotlin.js.backend.JsToStringGenerationVisitor;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.config.JsConfig;
@@ -42,6 +41,7 @@ import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static org.jetbrains.kotlin.js.inline.util.CollectUtilsKt.getImportTag;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.flattenStatement;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
@@ -92,6 +92,8 @@ public class JsInliner extends JsVisitorWithContextImpl {
}
for (JsProgramFragment fragment : fragmentsToProcess) {
+ inliner.existingImports.clear();
+
inliner.inliningContexts.push(inliner.new JsInliningContext(inliner.new ListContext()));
inliner.acceptStatement(fragment.getDeclarationBlock());
@@ -146,18 +148,6 @@ public class JsInliner extends JsVisitorWithContextImpl {
}
}
- @Nullable
- private static String getImportTag(JsVars jsVars) {
- if (jsVars.getVars().size() == 1) {
- JsVars.JsVar jsVar = jsVars.getVars().get(0);
- if (jsVar.getInitExpression() != null) {
- return extractImportTag(jsVar.getInitExpression());
- }
- }
-
- return null;
- }
-
@Override
public boolean visit(@NotNull JsFunction function, @NotNull JsContext context) {
FunctionWithWrapper functionWithWrapper = functionsByFunctionNodes.get(function);
@@ -499,42 +489,6 @@ public class JsInliner extends JsVisitorWithContextImpl {
return namedFunctionsStack.peek();
}
- @Nullable
- private static String extractImportTag(@NotNull JsExpression expression) {
- StringBuilder sb = new StringBuilder();
- return extractImportTagImpl(expression, sb) ? sb.toString() : null;
- }
-
- private static boolean extractImportTagImpl(@NotNull JsExpression expression, @NotNull StringBuilder sb) {
- if (expression instanceof JsNameRef) {
- JsNameRef nameRef = (JsNameRef) expression;
- if (nameRef.getQualifier() != null) {
- if (!extractImportTagImpl(nameRef.getQualifier(), sb)) {
- return false;
- }
- sb.append('.');
- }
- sb.append(JsToStringGenerationVisitor.javaScriptString(nameRef.getIdent()));
- return true;
- }
- else if (expression instanceof JsArrayAccess) {
- JsArrayAccess arrayAccess = (JsArrayAccess) expression;
- if (!extractImportTagImpl(arrayAccess.getArrayExpression(), sb)) {
- return false;
- }
- sb.append(".");
- if (!(arrayAccess.getIndexExpression() instanceof JsStringLiteral)) {
- return false;
- }
- JsStringLiteral stringLiteral = (JsStringLiteral) arrayAccess.getIndexExpression();
- sb.append(JsToStringGenerationVisitor.javaScriptString(stringLiteral.getValue()));
- return true;
- }
- else {
- return false;
- }
- }
-
private void reportInlineCycle(@NotNull JsInvocation call, @NotNull JsFunction calledFunction) {
MetadataProperties.setInlineStrategy(call, InlineStrategy.NOT_INLINE);
Iterator it = inlineCallInfos.descendingIterator();
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/removeDuplicateImports.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/removeDuplicateImports.kt
new file mode 100644
index 00000000000..eaec502092e
--- /dev/null
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/removeDuplicateImports.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010-2017 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.kotlin.js.inline.clean
+
+import org.jetbrains.kotlin.js.backend.ast.*
+import org.jetbrains.kotlin.js.inline.util.getImportTag
+import org.jetbrains.kotlin.js.inline.util.replaceNames
+
+fun removeDuplicateImports(node: JsNode) {
+ node.accept(object : RecursiveJsVisitor() {
+ override fun visitBlock(x: JsBlock) {
+ super.visitBlock(x)
+ removeDuplicateImports(x.statements)
+ }
+ })
+}
+
+private fun removeDuplicateImports(statements: MutableList) {
+ val existingImports = mutableMapOf()
+ val replacements = mutableMapOf()
+ removeDuplicateImports(statements, existingImports, replacements)
+
+ for (statement in statements) {
+ replaceNames(statement, replacements)
+ }
+}
+
+private fun removeDuplicateImports(
+ statements: MutableList,
+ existingImports: MutableMap,
+ replacements: MutableMap
+) {
+ var index = 0
+ while (index < statements.size) {
+ val statement = statements[index]
+ if (statement is JsVars) {
+ val importTag = getImportTag(statement)
+ if (importTag != null) {
+ val name = statement.vars[0].name
+ val existingName = existingImports[importTag]
+ if (existingName != null) {
+ replacements[name] = existingName.makeRef()
+ statements.removeAt(index)
+ continue
+ }
+ else {
+ existingImports[importTag] = name
+ }
+ }
+ }
+ else if (statement is JsBlock) {
+ removeDuplicateImports(statement.statements, existingImports, replacements)
+ }
+
+ index++
+ }
+}
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt
index 42811422f5d..11109d33e58 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt
@@ -16,7 +16,9 @@
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.imported
import org.jetbrains.kotlin.js.backend.ast.metadata.staticRef
import org.jetbrains.kotlin.js.inline.util.collectors.InstanceCollector
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata
@@ -343,4 +345,43 @@ fun JsNode.collectBreakContinueTargets(): Map {
})
return targets
+}
+
+fun getImportTag(jsVars: JsVars): String? {
+ if (jsVars.vars.size == 1) {
+ val jsVar = jsVars.vars[0]
+ if (jsVar.initExpression != null && jsVar.name.imported) {
+ return extractImportTag(jsVar.initExpression)
+ }
+ }
+
+ return null
+}
+
+fun extractImportTag(expression: JsExpression): String? {
+ val sb = StringBuilder()
+ return if (extractImportTagImpl(expression, sb)) sb.toString() else null
+}
+
+private fun extractImportTagImpl(expression: JsExpression, sb: StringBuilder): Boolean {
+ when (expression) {
+ is JsNameRef -> {
+ val nameRef = expression
+ if (nameRef.qualifier != null) {
+ if (!extractImportTagImpl(nameRef.qualifier!!, sb)) return false
+ sb.append('.')
+ }
+ sb.append(JsToStringGenerationVisitor.javaScriptString(nameRef.ident))
+ return true
+ }
+ is JsArrayAccess -> {
+ val arrayAccess = expression
+ if (!extractImportTagImpl(arrayAccess.arrayExpression, sb)) return false
+ sb.append(".")
+ val stringLiteral = arrayAccess.indexExpression as? JsStringLiteral ?: return false
+ sb.append(JsToStringGenerationVisitor.javaScriptString(stringLiteral.value))
+ return true
+ }
+ else -> return false
+ }
}
\ No newline at end of file
diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/NameReplacingVisitor.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/NameReplacingVisitor.kt
index a819754977d..1cee5ab5ada 100644
--- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/NameReplacingVisitor.kt
+++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/rewriters/NameReplacingVisitor.kt
@@ -23,11 +23,16 @@ class NameReplacingVisitor(private val replaceMap: Map) :
override fun endVisit(x: JsNameRef, ctx: JsContext) {
if (x.qualifier != null) return
val replacement = replaceMap[x.name] ?: return
- val replacementCopy = replacement.deepCopy()
- if (x.source != null) {
- replacementCopy.source = x.source
+ if (replacement is JsNameRef) {
+ applyToNamedNode(x)
+ }
+ else {
+ val replacementCopy = replacement.deepCopy()
+ if (x.source != null) {
+ replacementCopy.source = x.source
+ }
+ ctx.replaceMe(accept(replacementCopy))
}
- ctx.replaceMe(accept(replacementCopy))
}
override fun endVisit(x: JsVars.JsVar, ctx: JsContext<*>) = applyToNamedNode(x)
diff --git a/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMapLocationRemapper.kt b/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMapLocationRemapper.kt
index 047b4cee5bc..077eca152d9 100644
--- a/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMapLocationRemapper.kt
+++ b/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMapLocationRemapper.kt
@@ -61,12 +61,12 @@ class SourceMapLocationRemapper(private val sourceMap: SourceMap) {
}
- for (node in nodes.asSequence().filterIsInstance()) {
+ for (node in nodes.asSequence().filterIsInstance()) {
val segment = findCorrespondingSegment(node)
val sourceFileName = segment?.sourceFileName
node.source = if (sourceFileName != null) {
val location = JsLocation(segment.sourceFileName, segment.sourceLineNumber, segment.sourceColumnNumber)
- JsLocationWithEmbeddedSource(location, sourceMap) { sourceMap.sourceContentResolver(segment.sourceFileName) }
+ JsLocationWithEmbeddedSource(location, null) { sourceMap.sourceContentResolver(segment.sourceFileName) }
}
else {
null
diff --git a/js/js.serializer/src/js-ast.proto b/js/js.serializer/src/js-ast.proto
index a05d4a8e4e9..6ff59c10c97 100644
--- a/js/js.serializer/src/js-ast.proto
+++ b/js/js.serializer/src/js-ast.proto
@@ -426,6 +426,7 @@ message Name {
required bool temporary = 1;
optional int32 identifier = 2;
optional int32 local_name_id = 3;
+ optional bool imported = 4 [default = false];
}
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
index 3f7884ff6ff..046143a0412 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt
@@ -91,7 +91,9 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterableoptional int32 local_name_id = 3;
*/
int getLocalNameId();
+
+ /**
+ * optional bool imported = 4;
+ */
+ boolean hasImported();
+ /**
+ * optional bool imported = 4;
+ */
+ boolean getImported();
}
/**
* Protobuf type {@code org.jetbrains.kotlin.serialization.js.ast.Name}
@@ -34829,6 +34838,11 @@ public final class JsAstProtoBuf {
localNameId_ = input.readInt32();
break;
}
+ case 32: {
+ bitField0_ |= 0x00000008;
+ imported_ = input.readBool();
+ break;
+ }
}
}
} catch (org.jetbrains.kotlin.protobuf.InvalidProtocolBufferException e) {
@@ -34908,10 +34922,26 @@ public final class JsAstProtoBuf {
return localNameId_;
}
+ public static final int IMPORTED_FIELD_NUMBER = 4;
+ private boolean imported_;
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean hasImported() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean getImported() {
+ return imported_;
+ }
+
private void initFields() {
temporary_ = false;
identifier_ = 0;
localNameId_ = 0;
+ imported_ = false;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -34939,6 +34969,9 @@ public final class JsAstProtoBuf {
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeInt32(3, localNameId_);
}
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ output.writeBool(4, imported_);
+ }
output.writeRawBytes(unknownFields);
}
@@ -34960,6 +34993,10 @@ public final class JsAstProtoBuf {
size += org.jetbrains.kotlin.protobuf.CodedOutputStream
.computeInt32Size(3, localNameId_);
}
+ if (((bitField0_ & 0x00000008) == 0x00000008)) {
+ size += org.jetbrains.kotlin.protobuf.CodedOutputStream
+ .computeBoolSize(4, imported_);
+ }
size += unknownFields.size();
memoizedSerializedSize = size;
return size;
@@ -35060,6 +35097,8 @@ public final class JsAstProtoBuf {
bitField0_ = (bitField0_ & ~0x00000002);
localNameId_ = 0;
bitField0_ = (bitField0_ & ~0x00000004);
+ imported_ = false;
+ bitField0_ = (bitField0_ & ~0x00000008);
return this;
}
@@ -35095,6 +35134,10 @@ public final class JsAstProtoBuf {
to_bitField0_ |= 0x00000004;
}
result.localNameId_ = localNameId_;
+ if (((from_bitField0_ & 0x00000008) == 0x00000008)) {
+ to_bitField0_ |= 0x00000008;
+ }
+ result.imported_ = imported_;
result.bitField0_ = to_bitField0_;
return result;
}
@@ -35110,6 +35153,9 @@ public final class JsAstProtoBuf {
if (other.hasLocalNameId()) {
setLocalNameId(other.getLocalNameId());
}
+ if (other.hasImported()) {
+ setImported(other.getImported());
+ }
setUnknownFields(
getUnknownFields().concat(other.unknownFields));
return this;
@@ -35238,6 +35284,38 @@ public final class JsAstProtoBuf {
return this;
}
+ private boolean imported_ ;
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean hasImported() {
+ return ((bitField0_ & 0x00000008) == 0x00000008);
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public boolean getImported() {
+ return imported_;
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public Builder setImported(boolean value) {
+ bitField0_ |= 0x00000008;
+ imported_ = value;
+
+ return this;
+ }
+ /**
+ * optional bool imported = 4;
+ */
+ public Builder clearImported() {
+ bitField0_ = (bitField0_ & ~0x00000008);
+ imported_ = false;
+
+ return this;
+ }
+
// @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.serialization.js.ast.Name)
}
diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
index da8e75a3e82..18b29dfdbd8 100644
--- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
+++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt
@@ -33,8 +33,12 @@ class JsAstSerializer(private val pathResolver: (File) -> String) {
private val nameMap = mutableMapOf()
private val stringMap = mutableMapOf()
private val fileStack: Deque = ArrayDeque()
+ private val importedNames = mutableSetOf()
fun serialize(fragment: JsProgramFragment, output: OutputStream) {
+ val namesBySignature = fragment.nameBindings.associate { it.key to it.name }
+ importedNames.clear()
+ importedNames += fragment.imports.map { namesBySignature[it.key]!! }
serialize(fragment).writeTo(output)
}
@@ -556,6 +560,10 @@ class JsAstSerializer(private val pathResolver: (File) -> String) {
builder.localNameId = serialize(it)
}
+ if (name.imported && name !in importedNames) {
+ builder.imported = true
+ }
+
val result = nameTableBuilder.entryCount
nameTableBuilder.addEntry(builder)
result
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java
index 76733085cdd..10abf51f10b 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java
@@ -4838,6 +4838,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName);
}
+ @TestMetadata("repeatedImport.kt")
+ public void testRepeatedImport() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/simple.kt");
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java
index 2cfed5aae30..8213e8a0c7d 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java
@@ -83,6 +83,13 @@ public class DirectiveTestUtils {
}
};
+ private static final DirectiveHandler PROPERTY_READ_COUNT = new DirectiveHandler("PROPERTY_READ_COUNT") {
+ @Override
+ void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
+ checkPropertyReadCount(ast, arguments.getNamedArgument("name"), Integer.parseInt(arguments.getNamedArgument("count")));
+ }
+ };
+
private static final DirectiveHandler FUNCTION_CALLED_IN_SCOPE = new DirectiveHandler("CHECK_CALLED_IN_SCOPE") {
@Override
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
@@ -299,6 +306,7 @@ public class DirectiveTestUtils {
PROPERTY_NOT_USED,
PROPERTY_NOT_READ_FROM,
PROPERTY_NOT_WRITTEN_TO,
+ PROPERTY_READ_COUNT,
PROPERTY_WRITE_COUNT,
FUNCTION_CALLED_IN_SCOPE,
FUNCTION_NOT_CALLED_IN_SCOPE,
@@ -340,12 +348,16 @@ public class DirectiveTestUtils {
}
}
+ private static void checkPropertyReadCount(JsNode node, String propertyName, int expectedCount) throws Exception {
+ PropertyReferenceCollector counter = PropertyReferenceCollector.Companion.collect(node);
+ assertEquals("Property read count: " + propertyName, expectedCount, counter.unqualifiedReadCount(propertyName));
+ }
+
private static void checkPropertyWriteCount(JsNode node, String propertyName, int expectedCount) throws Exception {
PropertyReferenceCollector counter = PropertyReferenceCollector.Companion.collect(node);
assertEquals("Property write count: " + propertyName, expectedCount, counter.unqualifiedWriteCount(propertyName));
}
-
public static void checkFunctionNotCalled(@NotNull JsNode node, @NotNull String functionName, @Nullable String exceptFunction)
throws Exception {
Set excludedScopes = exceptFunction != null ? Collections.singleton(exceptFunction) : Collections.emptySet();
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/PropertyReferenceCollector.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/PropertyReferenceCollector.kt
index f8422a8e2bd..0824d7e990c 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/PropertyReferenceCollector.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/PropertyReferenceCollector.kt
@@ -24,17 +24,19 @@ import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
class PropertyReferenceCollector : RecursiveJsVisitor() {
- private val identReadSet = hashSetOf()
+ private val identReadMap = hashMapOf()
private val identWriteMap = hashMapOf()
- fun hasUnqualifiedReads(expectedIdent: String) = expectedIdent in identReadSet
+ fun hasUnqualifiedReads(expectedIdent: String) = expectedIdent in identReadMap
fun hasUnqualifiedWrites(expectedIdent: String) = expectedIdent in identWriteMap
fun unqualifiedWriteCount(expectedIdent: String): Int = identWriteMap[expectedIdent] ?: 0
+ fun unqualifiedReadCount(expectedIdent: String): Int = identReadMap[expectedIdent] ?: 0
+
override fun visitNameRef(nameRef: JsNameRef) {
super.visitNameRef(nameRef)
- identReadSet.add(nameRef.ident)
+ identReadMap[nameRef.ident] = 1 + unqualifiedReadCount(nameRef.ident)
}
override fun visitBinaryExpression(x: JsBinaryOperation) {
diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java
index e3528f37573..a615cca820f 100644
--- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java
+++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer;
import org.jetbrains.kotlin.js.inline.JsInliner;
import org.jetbrains.kotlin.js.inline.clean.LabeledBlockToDoWhileTransformation;
+import org.jetbrains.kotlin.js.inline.clean.RemoveDuplicateImportsKt;
import org.jetbrains.kotlin.js.inline.clean.RemoveUnusedImportsKt;
import org.jetbrains.kotlin.js.inline.clean.ResolveTemporaryNamesKt;
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver;
@@ -185,6 +186,7 @@ public final class K2JSTranslator {
incrementalResults.processHeader(serializationUtil.serializeHeader(null).toByteArray());
}
+ RemoveDuplicateImportsKt.removeDuplicateImports(translationResult.getProgram());
ResolveTemporaryNamesKt.resolveTemporaryNames(translationResult.getProgram());
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled();
if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics);
diff --git a/js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt b/js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt
new file mode 100644
index 00000000000..3a690376d76
--- /dev/null
+++ b/js/js.translator/testData/box/inlineMultiModule/repeatedImport.kt
@@ -0,0 +1,28 @@
+// MODULE: lib
+// FILE: lib.kt
+fun foo(x: String) = "foo($x)"
+
+inline fun bar(x: String) = "bar(${foo(x)})"
+
+inline fun baz(x: String) = "baz(${foo(x)})"
+
+// MODULE: main(lib)
+// FILE: a.kt
+// PROPERTY_READ_COUNT: name=foo_61zpoe$ count=1
+fun test1() = bar("q")
+
+// FILE: b.kt
+// RECOMPILE
+fun test2() = baz("w")
+
+// FILE: main.kt
+// RECOMPILE
+fun box(): String {
+ val a = test1()
+ if (a != "bar(foo(q))") return "fail1: $a"
+
+ val b = test2()
+ if (b != "baz(foo(w))") return "fail2: $b"
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.translator/testData/lineNumbers/charBoxing.kt b/js/js.translator/testData/lineNumbers/charBoxing.kt
index 021a40e63d9..b7f2494ee8b 100644
--- a/js/js.translator/testData/lineNumbers/charBoxing.kt
+++ b/js/js.translator/testData/lineNumbers/charBoxing.kt
@@ -17,4 +17,4 @@ fun bar(a: String, b: Char, x: Int) {
a.foo(b)
}
-// LINES: 6 4 4 5 5 8 8 8 8 8 11 9 9 10 10 18 14 14 14 14 14 15 17 17 9 9 14 10 17 10 14 14 * 1 * 1
\ No newline at end of file
+// LINES: 6 4 4 5 5 8 8 8 8 8 8 8 11 9 9 10 10 8 8 8 8 18 14 14 14 14 14 15 17 17 9 9 14 10 17 10 14 14 * 1 * 1
\ No newline at end of file
diff --git a/js/js.translator/testData/lineNumbers/inlineLocalVarsRef.kt b/js/js.translator/testData/lineNumbers/inlineLocalVarsRef.kt
index 490dd46f90f..9e5bac2c238 100644
--- a/js/js.translator/testData/lineNumbers/inlineLocalVarsRef.kt
+++ b/js/js.translator/testData/lineNumbers/inlineLocalVarsRef.kt
@@ -9,4 +9,4 @@ fun bar() {
foo(42)
}
-// LINES: 1 1 1 1 1 6 2 2 3 3 4 4 10 2 2 9 2 3 3 4 4
\ No newline at end of file
+// LINES: 1 1 1 1 1 6 2 2 3 3 4 4 1 1 10 2 2 9 2 3 3 4 4
\ No newline at end of file
diff --git a/js/js.translator/testData/lineNumbers/inlineReturn.kt b/js/js.translator/testData/lineNumbers/inlineReturn.kt
index f090e20b336..46c862ad3b3 100644
--- a/js/js.translator/testData/lineNumbers/inlineReturn.kt
+++ b/js/js.translator/testData/lineNumbers/inlineReturn.kt
@@ -21,4 +21,4 @@ fun bar(x: Int) {
println("%")
}
-// LINES: 1 1 1 1 1 1 1 17 2 2 3 3 4 7 7 9 9 10 10 11 14 14 16 16 22 20 20 20 20 2 2 3 3 4 3 7 7 9 9 10 10 11 10 14 14 16 16 * 20 21 21
\ No newline at end of file
+// LINES: 1 1 1 1 1 1 1 17 2 2 3 3 4 7 7 9 9 10 10 11 14 14 16 16 1 1 22 20 20 20 20 2 2 3 3 4 3 7 7 9 9 10 10 11 10 14 14 16 16 * 20 21 21
\ No newline at end of file