Avoid duplication of imports introduced during JS inlining

This commit is contained in:
Alexey Andreev
2017-07-14 16:46:04 +03:00
parent f2b2e20331
commit 8c256b24dc
19 changed files with 374 additions and 76 deletions
@@ -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
}
}
@@ -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)
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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