[debugger] FragmentLocalFunctionPatchLowering: remove pointless visiting

`FragmentLocalFunctionPatchLowering.visitSimpleFunction`
uses a transformer with an overridden `visitCall`.
Inside it visits every nested call recursively in two places:
in the beginning (1, line 59), and via super method
if the current call doesn't refer to a local function (2, line 64).
It leads to a combinatorial explosion: if there are N nested calls
in one expression, then the number of `visitCall` invocations
will be `pow(2, N)`.

Thus, it will take forever to compile certain expressions. A perfect
example of a long nested call is string interpolation: string templates
are being compiled to a chain of `StringBuilder::append` calls. Here's
why IDEA-325225 happens -- the code in the issue has large string
templates, and the resulting `StringBuilder::append` chains may contain
up to 30 links, making for around 1 billion `visitCall` invocations.

However, the visiting (2) is obsolete. First, the visiting (1) will
process all the children of an expression, replacing them with the
right local function calls, if necessary, recursively, up the stack.
Second, the expression itself will be transformed, if necessary, in
that same method, after all its children are processed. That means we
can get rid of one visiting and avoid the combinatorial explosion, yet
all the local function calls will still be correctly processed.

Fixes IDEA-325225, contributes to IDEA-325146


Merge-request: KT-MR-11104
Merged-by: Alexander Kuznetsov <Aleksander.Kuznetsov@jetbrains.com>
This commit is contained in:
Alexander Kuznetsov
2023-07-17 15:21:05 +00:00
committed by Space Team
parent df0e4e0b53
commit ece7dce2ae
@@ -61,7 +61,7 @@ internal class FragmentLocalFunctionPatchLowering(
is IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER -> context.mapping.defaultArgumentsOriginalFunction[expression.symbol.owner]
else -> expression.symbol.owner
}
val localsData = localDeclarationsData[localDeclarationsDataKey] ?: return super.visitCall(expression)
val localsData = localDeclarationsData[localDeclarationsDataKey] ?: return expression
val remappedTarget: LocalDeclarationsLowering.LocalFunctionContext = localsData.localContext
val irBuilder = context.createJvmIrBuilder(declaration.symbol)