Avoid duplication of imports introduced during JS inlining
This commit is contained in:
+91
-13
@@ -47071,6 +47071,15 @@ public final class DebugJsAstProtoBuf {
|
||||
* <code>optional int32 local_name_id = 3;</code>
|
||||
*/
|
||||
int getLocalNameId();
|
||||
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
boolean hasImported();
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
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_;
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public boolean hasImported() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
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_ ;
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public boolean hasImported() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public boolean getImported() {
|
||||
return imported_;
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public Builder setImported(boolean value) {
|
||||
bitField0_ |= 0x00000008;
|
||||
imported_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<JsStatement>()));
|
||||
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<JsCallInfo> it = inlineCallInfos.descendingIterator();
|
||||
|
||||
@@ -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<JsStatement>) {
|
||||
val existingImports = mutableMapOf<String, JsName>()
|
||||
val replacements = mutableMapOf<JsName, JsExpression>()
|
||||
removeDuplicateImports(statements, existingImports, replacements)
|
||||
|
||||
for (statement in statements) {
|
||||
replaceNames(statement, replacements)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeDuplicateImports(
|
||||
statements: MutableList<JsStatement>,
|
||||
existingImports: MutableMap<String, JsName>,
|
||||
replacements: MutableMap<JsName, JsExpression>
|
||||
) {
|
||||
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++
|
||||
}
|
||||
}
|
||||
@@ -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<JsContinue, JsStatement> {
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -23,11 +23,16 @@ class NameReplacingVisitor(private val replaceMap: Map<JsName, JsExpression>) :
|
||||
override fun endVisit(x: JsNameRef, ctx: JsContext<JsNode>) {
|
||||
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)
|
||||
|
||||
+2
-2
@@ -61,12 +61,12 @@ class SourceMapLocationRemapper(private val sourceMap: SourceMap) {
|
||||
}
|
||||
|
||||
|
||||
for (node in nodes.asSequence().filterIsInstance<SourceInfoAwareJsNode>()) {
|
||||
for (node in nodes.asSequence().filterIsInstance<SourceInfoAwareJsNode>()) {
|
||||
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
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -91,7 +91,9 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable<Fi
|
||||
}
|
||||
|
||||
for (nameBinding in fragment.nameBindings) {
|
||||
nameBinding.name.imported = nameBinding.key in fragment.imports
|
||||
if (nameBinding.key in fragment.imports) {
|
||||
nameBinding.name.imported = true
|
||||
}
|
||||
}
|
||||
|
||||
return fragment
|
||||
@@ -438,6 +440,9 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable<Fi
|
||||
if (nameProto.hasLocalNameId()) {
|
||||
name.localAlias = deserializeName(nameProto.localNameId)
|
||||
}
|
||||
if (nameProto.hasImported()) {
|
||||
name.imported = nameProto.imported
|
||||
}
|
||||
nameCache[id] = name
|
||||
name
|
||||
}
|
||||
|
||||
@@ -34763,6 +34763,15 @@ public final class JsAstProtoBuf {
|
||||
* <code>optional int32 local_name_id = 3;</code>
|
||||
*/
|
||||
int getLocalNameId();
|
||||
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
boolean hasImported();
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
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_;
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public boolean hasImported() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
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_ ;
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public boolean hasImported() {
|
||||
return ((bitField0_ & 0x00000008) == 0x00000008);
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public boolean getImported() {
|
||||
return imported_;
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public Builder setImported(boolean value) {
|
||||
bitField0_ |= 0x00000008;
|
||||
imported_ = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>optional bool imported = 4;</code>
|
||||
*/
|
||||
public Builder clearImported() {
|
||||
bitField0_ = (bitField0_ & ~0x00000008);
|
||||
imported_ = false;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:org.jetbrains.kotlin.serialization.js.ast.Name)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,12 @@ class JsAstSerializer(private val pathResolver: (File) -> String) {
|
||||
private val nameMap = mutableMapOf<JsName, Int>()
|
||||
private val stringMap = mutableMapOf<String, Int>()
|
||||
private val fileStack: Deque<String> = ArrayDeque()
|
||||
private val importedNames = mutableSetOf<JsName>()
|
||||
|
||||
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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<String> excludedScopes = exceptFunction != null ? Collections.singleton(exceptFunction) : Collections.emptySet();
|
||||
|
||||
@@ -24,17 +24,19 @@ import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
|
||||
class PropertyReferenceCollector : RecursiveJsVisitor() {
|
||||
|
||||
private val identReadSet = hashSetOf<String>()
|
||||
private val identReadMap = hashMapOf<String, Int>()
|
||||
private val identWriteMap = hashMapOf<String, Int>()
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
// 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
|
||||
@@ -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
|
||||
// LINES: 1 1 1 1 1 6 2 2 3 3 4 4 1 1 10 2 2 9 2 3 3 4 4
|
||||
+1
-1
@@ -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
|
||||
// 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
|
||||
Reference in New Issue
Block a user