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