From 8f68c47225e53245018f010bf60bc2a302edbf19 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Thu, 7 Feb 2019 18:22:41 +0300 Subject: [PATCH] JS: support inline function definitions without wrappers Apparently function literals are translated without hiding the local declarations in the wrapFunction call. Which makes sense. Apart from the fact that it would also make sense to do the same for private inline functions and function literals inside other inline functions. --- .../kotlin/js/inline/InlineAstVisitor.kt | 6 ++-- .../jetbrains/kotlin/js/inline/JsInliner.kt | 22 ++++++++---- .../context/FunctionDefinitionLoader.kt | 34 +++++++++---------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InlineAstVisitor.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InlineAstVisitor.kt index fc659177aa0..68f1fa5bd07 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InlineAstVisitor.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InlineAstVisitor.kt @@ -27,13 +27,13 @@ class InlineAstVisitor( override fun visit(x: JsInvocation, ctx: JsContext<*>): Boolean { // Is it `defineInlineFunction('tag', ...)`? InlineMetadata.decompose(x)?.let { - jsInliner.process(InlineFunctionDefinition(it.function, it.tag.value), x, scope.fragment) + jsInliner.process(InlineFunctionDefinition(it.function, it.tag.value), x, scope.fragment, scope) return false } // Is it `wrapFunction(...)`? InlineMetadata.tryExtractFunction(x)?.let { - jsInliner.process(InlineFunctionDefinition(it, null), x, scope.fragment) + jsInliner.process(InlineFunctionDefinition(it, null), x, scope.fragment, scope) return false } @@ -108,7 +108,7 @@ class InlineAstVisitor( private fun hasToBeInlined(call: JsInvocation): Boolean { val strategy = call.inlineStrategy - return if (strategy == null || !strategy.isInline) false else jsInliner.functionDefinitionLoader.hasFunctionDefinition(call, scope.fragment) + return if (strategy == null || !strategy.isInline) false else jsInliner.functionDefinitionLoader.hasFunctionDefinition(call, scope) } private fun patchReturnsFromSecondaryConstructor(function: JsFunction) { diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.kt index b6fa2d09beb..fdfe92902b3 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/JsInliner.kt @@ -33,23 +33,31 @@ class JsInliner( } } - fun process(inlineFn: InlineFunctionDefinition, call: JsInvocation?, definitionFragment: JsProgramFragment) { + fun process( + inlineFn: InlineFunctionDefinition, + call: JsInvocation?, + definitionFragment: JsProgramFragment, + callsiteScope: InliningScope + ) { // Old fragments are already processed if (definitionFragment !in translationResult.newFragments) return cycleReporter.processInlineFunction(inlineFn.fn, call) { - val wrapperBody = inlineFn.fn.wrapperBody + val (fn, wrapperBody) = inlineFn.fn - checkNotNull(wrapperBody) { "All unprocessed inline functions should be generated with wrappers" } - - ImportIntoWrapperInliningScope.process(wrapperBody, definitionFragment) { scope -> - InlineAstVisitor(this, scope).accept(wrapperBody) + if (wrapperBody != null) { + ImportIntoWrapperInliningScope.process(wrapperBody, definitionFragment) { scope -> + InlineAstVisitor(this, scope).accept(wrapperBody) + } + } else { + // e.g. lambda in a non-inline context + InlineAstVisitor(this, callsiteScope).accept(fn) } } } fun inline(scope: InliningScope, call: JsInvocation, currentStatement: JsStatement?): InlineableResult { - val definition = functionDefinitionLoader.getFunctionDefinition(call, scope.fragment) + val definition = functionDefinitionLoader.getFunctionDefinition(call, scope) val function = scope.importFunctionDefinition(definition) diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionDefinitionLoader.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionDefinitionLoader.kt index 5ef90d43cb6..a79ec55ed59 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionDefinitionLoader.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/context/FunctionDefinitionLoader.kt @@ -28,12 +28,12 @@ import java.util.HashMap class FunctionDefinitionLoader( private val inliner: JsInliner ) { - fun getFunctionDefinition(call: JsInvocation, fragment: JsProgramFragment): InlineFunctionDefinition { - return getFunctionDefinitionImpl(call, fragment)!! + fun getFunctionDefinition(call: JsInvocation, scope: InliningScope): InlineFunctionDefinition { + return getFunctionDefinitionImpl(call, scope)!! } - fun hasFunctionDefinition(call: JsInvocation, fragment: JsProgramFragment): Boolean { - return getFunctionDefinitionImpl(call, fragment) != null + fun hasFunctionDefinition(call: JsInvocation, scope: InliningScope): Boolean { + return getFunctionDefinitionImpl(call, scope) != null } val functionsByFunctionNodes = HashMap() @@ -68,11 +68,11 @@ class FunctionDefinitionLoader( * 5. Qualifier can be JsNameRef with ref to case [3] * in case of local function with closure. */ - private fun getFunctionDefinitionImpl(call: JsInvocation, fragment: JsProgramFragment): InlineFunctionDefinition? { + private fun getFunctionDefinitionImpl(call: JsInvocation, scope: InliningScope): InlineFunctionDefinition? { // Ensure we have the local function information - loadFragment(fragment) + loadFragment(scope.fragment) - return lookUpFunctionDirect(call) ?: lookUpFunctionIndirect(call, fragment) ?: lookUpFunctionExternal(call, fragment) + return lookUpFunctionDirect(call, scope) ?: lookUpFunctionIndirect(call, scope) ?: lookUpFunctionExternal(call, scope.fragment) } private val functionReader = FunctionReader(inliner.reporter, inliner.config) @@ -91,14 +91,15 @@ class FunctionDefinitionLoader( fragmentInfo[fragment]?.run { accessors[functionTag] } - private fun lookUpFunctionIndirect(call: JsInvocation, fragment: JsProgramFragment): InlineFunctionDefinition? { + private fun lookUpFunctionIndirect(call: JsInvocation, scope: InliningScope): InlineFunctionDefinition? { /** Try loading via `descriptor` metadata. * In case of private inline properties that's the only way... */ call.descriptor?.let { descriptor -> - fragmentInfo[fragment]?.let { info -> + fragmentInfo[scope.fragment]?.let { info -> info.localAccessors[descriptor]?.let { fn -> return InlineFunctionDefinition(fn, null).also { def -> - inliner.process(def, call, fragment) + // Definition fragment is that same as call site fragment + inliner.process(def, call, scope.fragment, scope) } } } @@ -116,10 +117,10 @@ class FunctionDefinitionLoader( return when (qualifier) { is JsInvocation -> { tryExtractCallableReference(qualifier) ?: getSimpleName(qualifier)?.let { simpleName -> - lookUpStaticFunction(simpleName, fragment)?.let { if (isFunctionCreator(it.function)) it else null } + lookUpStaticFunction(simpleName, scope.fragment)?.let { if (isFunctionCreator(it.function)) it else null } } } - is JsNameRef -> lookUpStaticFunction(qualifier.name, fragment) + is JsNameRef -> lookUpStaticFunction(qualifier.name, scope.fragment) // Since we could get functionWithWrapper as a simple function directly from staticRef (which always points on implementation) // we should check if we have a known wrapper for it @@ -127,9 +128,8 @@ class FunctionDefinitionLoader( else -> null }?.let { InlineFunctionDefinition(it, null).also { definition -> - if (fragment in inliner.translationResult.newFragments) { - inliner.process(definition, call, fragment) - } + // Definition fragment is that same as call site fragment + inliner.process(definition, call, scope.fragment, scope) } } } @@ -154,7 +154,7 @@ class FunctionDefinitionLoader( } } - private fun lookUpFunctionDirect(call: JsInvocation): InlineFunctionDefinition? { + private fun lookUpFunctionDirect(call: JsInvocation, callsiteScope: InliningScope): InlineFunctionDefinition? { val descriptor = call.descriptor ?: return null val tag = Namer.getFunctionTag(descriptor, inliner.config) @@ -166,7 +166,7 @@ class FunctionDefinitionLoader( val definition = InlineFunctionDefinition(fn, tag) // Make sure definition has it's own inline calls inlined. - inliner.process(definition, call, definitionFragment) + inliner.process(definition, call, definitionFragment, callsiteScope) return definition }