JS: create appropriate nameBinding's upon inlining

Otherwise JsName's don't get linked properly upon deserialization.
As a result a function/constructor doesn't get renamed at
call site during the name conflict resolution phase.
This commit is contained in:
Anton Bannykh
2018-08-16 15:05:36 +03:00
parent 3b4a49eebf
commit 8e5b2fe657
2 changed files with 41 additions and 8 deletions
@@ -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.InliningContext;
import org.jetbrains.kotlin.js.inline.context.NamingContext; import org.jetbrains.kotlin.js.inline.context.NamingContext;
import org.jetbrains.kotlin.js.inline.util.*; 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.js.translate.expression.InlineMetadata;
import org.jetbrains.kotlin.resolve.inline.InlineStrategy; import org.jetbrains.kotlin.resolve.inline.InlineStrategy;
@@ -62,7 +61,13 @@ public class JsInliner extends JsVisitorWithContextImpl {
node -> node instanceof JsInvocation && hasToBeInlined((JsInvocation) node); node -> node instanceof JsInvocation && hasToBeInlined((JsInvocation) node);
private int inlineFunctionDepth; private int inlineFunctionDepth;
private final Map<JsWrapperKey, Map<JsName, JsExpression>> replacementsInducedByWrappers = new HashMap<>(); private final Map<JsWrapperKey, Map<JsName, JsNameRef>> replacementsInducedByWrappers = new HashMap<>();
private final Map<JsName, String> inverseNameBindings;
private Map<JsName, String> existingNameBindings = new HashMap<>();
private final List<JsNameBinding> additionalNameBindings = new ArrayList<>();
public static void process( public static void process(
@NotNull JsConfig.Reporter reporter, @NotNull JsConfig.Reporter reporter,
@@ -75,6 +80,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
) { ) {
Map<JsName, FunctionWithWrapper> functions = CollectUtilsKt.collectNamedFunctionsAndWrappers(fragments); Map<JsName, FunctionWithWrapper> functions = CollectUtilsKt.collectNamedFunctionsAndWrappers(fragments);
Map<String, FunctionWithWrapper> accessors = CollectUtilsKt.collectAccessors(fragments); Map<String, FunctionWithWrapper> accessors = CollectUtilsKt.collectAccessors(fragments);
Map<JsName, String> inverseNameBindings = CollectUtilsKt.collectNameBindings(fragments);
DummyAccessorInvocationTransformer accessorInvocationTransformer = new DummyAccessorInvocationTransformer(); DummyAccessorInvocationTransformer accessorInvocationTransformer = new DummyAccessorInvocationTransformer();
for (JsProgramFragment fragment : fragmentsToProcess) { for (JsProgramFragment fragment : fragmentsToProcess) {
@@ -82,7 +88,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
accessorInvocationTransformer.accept(fragment.getInitializerBlock()); accessorInvocationTransformer.accept(fragment.getInitializerBlock());
} }
FunctionReader functionReader = new FunctionReader(reporter, config, currentModuleName, fragments); 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) { for (JsStatement statement : importStatements) {
inliner.processImportStatement(statement); inliner.processImportStatement(statement);
@@ -90,6 +96,8 @@ public class JsInliner extends JsVisitorWithContextImpl {
for (JsProgramFragment fragment : fragmentsToProcess) { for (JsProgramFragment fragment : fragmentsToProcess) {
inliner.existingImports.clear(); inliner.existingImports.clear();
inliner.additionalNameBindings.clear();
inliner.existingNameBindings = CollectUtilsKt.collectNameBindings(Collections.singletonList(fragment));
inliner.inliningContexts.push(inliner.new JsInliningContext(inliner.new ListContext<JsStatement>())); inliner.inliningContexts.push(inliner.new JsInliningContext(inliner.new ListContext<JsStatement>()));
inliner.acceptStatement(fragment.getDeclarationBlock()); inliner.acceptStatement(fragment.getDeclarationBlock());
@@ -103,6 +111,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
inliner.inliningContexts.pop(); inliner.inliningContexts.pop();
fragment.getInitializerBlock().getStatements().addAll(0, initWrapper.getStatements()); fragment.getInitializerBlock().getStatements().addAll(0, initWrapper.getStatements());
fragment.getNameBindings().addAll(inliner.additionalNameBindings);
} }
for (JsProgramFragment fragment : fragmentsToProcess) { for (JsProgramFragment fragment : fragmentsToProcess) {
@@ -117,6 +126,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
@NotNull JsConfig config, @NotNull JsConfig config,
@NotNull Map<JsName, FunctionWithWrapper> functions, @NotNull Map<JsName, FunctionWithWrapper> functions,
@NotNull Map<String, FunctionWithWrapper> accessors, @NotNull Map<String, FunctionWithWrapper> accessors,
@NotNull Map<JsName, String> inverseNameBindings,
@NotNull FunctionReader functionReader, @NotNull FunctionReader functionReader,
@NotNull DiagnosticSink trace @NotNull DiagnosticSink trace
) { ) {
@@ -127,6 +137,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
namedFunctionsSet.add(functionWithWrapper.getFunction()); namedFunctionsSet.add(functionWithWrapper.getFunction());
} }
this.accessors = accessors; this.accessors = accessors;
this.inverseNameBindings = inverseNameBindings;
this.functionReader = functionReader; this.functionReader = functionReader;
this.trace = trace; this.trace = trace;
@@ -396,10 +407,10 @@ public class JsInliner extends JsVisitorWithContextImpl {
@NotNull InliningContext inliningContext @NotNull InliningContext inliningContext
) { ) {
// Apparently we should avoid this trick when we implement fair support for crossinline // Apparently we should avoid this trick when we implement fair support for crossinline
Function<JsWrapperKey, Map<JsName, JsExpression>> replacementGen = k -> { Function<JsWrapperKey, Map<JsName, JsNameRef>> replacementGen = k -> {
JsContext ctx = k.context; JsContext ctx = k.context;
Map<JsName, JsExpression> newReplacements = new HashMap<>(); Map<JsName, JsNameRef> newReplacements = new HashMap<>();
List<JsStatement> copiedStatements = new ArrayList<>(); List<JsStatement> copiedStatements = new ArrayList<>();
for (JsStatement statement : wrapper.getStatements()) { for (JsStatement statement : wrapper.getStatements()) {
@@ -427,7 +438,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
} }
if (name != existingName) { if (name != existingName) {
JsExpression replacement = pureFqn(existingName, null); JsNameRef replacement = pureFqn(existingName, null);
newReplacements.put(name, replacement); newReplacements.put(name, replacement);
} }
@@ -445,7 +456,7 @@ public class JsInliner extends JsVisitorWithContextImpl {
for (JsName name : definedNames) { for (JsName name : definedNames) {
JsName alias = JsScope.declareTemporaryName(name.getIdent()); JsName alias = JsScope.declareTemporaryName(name.getIdent());
alias.copyMetadataFrom(name); alias.copyMetadataFrom(name);
JsExpression replacement = pureFqn(alias, null); JsNameRef replacement = pureFqn(alias, null);
newReplacements.put(name, replacement); newReplacements.put(name, replacement);
} }
@@ -464,9 +475,21 @@ public class JsInliner extends JsVisitorWithContextImpl {
}; };
JsWrapperKey key = new JsWrapperKey(inliningContext.getStatementContextBeforeCurrentFunction(), originalFunction); JsWrapperKey key = new JsWrapperKey(inliningContext.getStatementContextBeforeCurrentFunction(), originalFunction);
Map<JsName, JsExpression> replacements = replacementsInducedByWrappers.computeIfAbsent(key, replacementGen); Map<JsName, JsNameRef> replacements = replacementsInducedByWrappers.computeIfAbsent(key, replacementGen);
RewriteUtilsKt.replaceNames(function, replacements); 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) { private static void replaceExpressionsWithLocalAliases(@NotNull JsStatement statement) {
@@ -230,6 +230,16 @@ fun collectAccessors(fragments: List<JsProgramFragment>): Map<String, FunctionWi
return result return result
} }
fun collectNameBindings(fragments: List<JsProgramFragment>): Map<JsName, String> {
val result = mutableMapOf<JsName, String>()
for (fragment in fragments) {
for (binding in fragment.nameBindings) {
result[binding.name] = binding.key
}
}
return result
}
fun extractFunction(expression: JsExpression) = when (expression) { fun extractFunction(expression: JsExpression) = when (expression) {
is JsFunction -> FunctionWithWrapper(expression, null) is JsFunction -> FunctionWithWrapper(expression, null)
else -> InlineMetadata.decompose(expression)?.function ?: InlineMetadata.tryExtractFunction(expression) else -> InlineMetadata.decompose(expression)?.function ?: InlineMetadata.tryExtractFunction(expression)