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 c27370e2106..b96940b4f05 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 @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.js.inline.context.FunctionContext; import org.jetbrains.kotlin.js.inline.context.InliningContext; import org.jetbrains.kotlin.js.inline.context.NamingContext; import org.jetbrains.kotlin.js.inline.util.*; -import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.expression.InlineMetadata; import org.jetbrains.kotlin.resolve.inline.InlineStrategy; @@ -62,7 +61,13 @@ public class JsInliner extends JsVisitorWithContextImpl { node -> node instanceof JsInvocation && hasToBeInlined((JsInvocation) node); private int inlineFunctionDepth; - private final Map> replacementsInducedByWrappers = new HashMap<>(); + private final Map> replacementsInducedByWrappers = new HashMap<>(); + + private final Map inverseNameBindings; + + private Map existingNameBindings = new HashMap<>(); + + private final List additionalNameBindings = new ArrayList<>(); public static void process( @NotNull JsConfig.Reporter reporter, @@ -75,6 +80,7 @@ public class JsInliner extends JsVisitorWithContextImpl { ) { Map functions = CollectUtilsKt.collectNamedFunctionsAndWrappers(fragments); Map accessors = CollectUtilsKt.collectAccessors(fragments); + Map inverseNameBindings = CollectUtilsKt.collectNameBindings(fragments); DummyAccessorInvocationTransformer accessorInvocationTransformer = new DummyAccessorInvocationTransformer(); for (JsProgramFragment fragment : fragmentsToProcess) { @@ -82,7 +88,7 @@ public class JsInliner extends JsVisitorWithContextImpl { accessorInvocationTransformer.accept(fragment.getInitializerBlock()); } FunctionReader functionReader = new FunctionReader(reporter, config, currentModuleName, fragments); - JsInliner inliner = new JsInliner(config, functions, accessors, functionReader, trace); + JsInliner inliner = new JsInliner(config, functions, accessors, inverseNameBindings, functionReader, trace); for (JsStatement statement : importStatements) { inliner.processImportStatement(statement); @@ -90,6 +96,8 @@ public class JsInliner extends JsVisitorWithContextImpl { for (JsProgramFragment fragment : fragmentsToProcess) { inliner.existingImports.clear(); + inliner.additionalNameBindings.clear(); + inliner.existingNameBindings = CollectUtilsKt.collectNameBindings(Collections.singletonList(fragment)); inliner.inliningContexts.push(inliner.new JsInliningContext(inliner.new ListContext())); inliner.acceptStatement(fragment.getDeclarationBlock()); @@ -103,6 +111,7 @@ public class JsInliner extends JsVisitorWithContextImpl { inliner.inliningContexts.pop(); fragment.getInitializerBlock().getStatements().addAll(0, initWrapper.getStatements()); + fragment.getNameBindings().addAll(inliner.additionalNameBindings); } for (JsProgramFragment fragment : fragmentsToProcess) { @@ -117,6 +126,7 @@ public class JsInliner extends JsVisitorWithContextImpl { @NotNull JsConfig config, @NotNull Map functions, @NotNull Map accessors, + @NotNull Map inverseNameBindings, @NotNull FunctionReader functionReader, @NotNull DiagnosticSink trace ) { @@ -127,6 +137,7 @@ public class JsInliner extends JsVisitorWithContextImpl { namedFunctionsSet.add(functionWithWrapper.getFunction()); } this.accessors = accessors; + this.inverseNameBindings = inverseNameBindings; this.functionReader = functionReader; this.trace = trace; @@ -396,10 +407,10 @@ public class JsInliner extends JsVisitorWithContextImpl { @NotNull InliningContext inliningContext ) { // Apparently we should avoid this trick when we implement fair support for crossinline - Function> replacementGen = k -> { + Function> replacementGen = k -> { JsContext ctx = k.context; - Map newReplacements = new HashMap<>(); + Map newReplacements = new HashMap<>(); List copiedStatements = new ArrayList<>(); for (JsStatement statement : wrapper.getStatements()) { @@ -427,7 +438,7 @@ public class JsInliner extends JsVisitorWithContextImpl { } if (name != existingName) { - JsExpression replacement = pureFqn(existingName, null); + JsNameRef replacement = pureFqn(existingName, null); newReplacements.put(name, replacement); } @@ -445,7 +456,7 @@ public class JsInliner extends JsVisitorWithContextImpl { for (JsName name : definedNames) { JsName alias = JsScope.declareTemporaryName(name.getIdent()); alias.copyMetadataFrom(name); - JsExpression replacement = pureFqn(alias, null); + JsNameRef replacement = pureFqn(alias, null); newReplacements.put(name, replacement); } @@ -464,9 +475,21 @@ public class JsInliner extends JsVisitorWithContextImpl { }; JsWrapperKey key = new JsWrapperKey(inliningContext.getStatementContextBeforeCurrentFunction(), originalFunction); - Map replacements = replacementsInducedByWrappers.computeIfAbsent(key, replacementGen); + Map replacements = replacementsInducedByWrappers.computeIfAbsent(key, replacementGen); RewriteUtilsKt.replaceNames(function, replacements); + + // Copy nameBinding's for inlined localAlias'es + for (JsNameRef nameRef : replacements.values()) { + JsName name = nameRef.getName(); + if (name != null && !existingNameBindings.containsKey(name)) { + String tag = inverseNameBindings.get(name); + if (tag != null) { + existingNameBindings.put(name, tag); + additionalNameBindings.add(new JsNameBinding(tag, name)); + } + } + } } private static void replaceExpressionsWithLocalAliases(@NotNull JsStatement statement) { 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 29a6c2f33e9..15b9e4a5ddd 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 @@ -230,6 +230,16 @@ fun collectAccessors(fragments: List): Map): Map { + val result = mutableMapOf() + for (fragment in fragments) { + for (binding in fragment.nameBindings) { + result[binding.name] = binding.key + } + } + return result +} + fun extractFunction(expression: JsExpression) = when (expression) { is JsFunction -> FunctionWithWrapper(expression, null) else -> InlineMetadata.decompose(expression)?.function ?: InlineMetadata.tryExtractFunction(expression)