JS: fix inline cycle reporting

This commit is contained in:
Anton Bannykh
2018-12-20 15:22:02 +03:00
parent b5b770c804
commit e4b081e1bf
3 changed files with 35 additions and 43 deletions
@@ -57,41 +57,36 @@ class InlinerCycleReporter(
} }
fun processInlineFunction(definition: FunctionWithWrapper, call: JsInvocation?, doProcess: () -> Unit) { fun processInlineFunction(definition: FunctionWithWrapper, call: JsInvocation?, doProcess: () -> Unit) {
when (functionVisitingState[definition.function]) {
VisitedState.IN_PROCESS -> {
reportInlineCycle(call, definition.function)
return
}
VisitedState.PROCESSED -> return
}
val function = definition.function val function = definition.function
functionVisitingState[function] = VisitedState.IN_PROCESS if (call != null) {
currentNamedFunction?.let {
val result = withFunction(function, doProcess) inlineCallInfos.add(JsCallInfo(call, it))
functionVisitingState[function] = VisitedState.PROCESSED
return result
}
fun <T> inlineCall(call: JsInvocation, doInline: () -> T): T {
currentNamedFunction?.let {
inlineCallInfos.add(JsCallInfo(call, it))
}
val result = doInline()
if (!inlineCallInfos.isEmpty()) {
if (inlineCallInfos.last.call == call) {
inlineCallInfos.removeLast()
} }
} }
return result try {
when (functionVisitingState[definition.function]) {
VisitedState.IN_PROCESS -> {
reportInlineCycle(call, definition.function)
return
}
VisitedState.PROCESSED -> return
}
functionVisitingState[function] = VisitedState.IN_PROCESS
withFunction(function, doProcess)
functionVisitingState[function] = VisitedState.PROCESSED
} finally {
if (!inlineCallInfos.isEmpty()) {
if (inlineCallInfos.last.call == call) {
inlineCallInfos.removeLast()
}
}
}
} }
private fun reportInlineCycle(call: JsInvocation?, calledFunction: JsFunction) { private fun reportInlineCycle(call: JsInvocation?, calledFunction: JsFunction) {
@@ -60,21 +60,18 @@ class JsInliner(
fun inline(scope: InliningScope, call: JsInvocation, currentStatement: JsStatement?): InlineableResult { fun inline(scope: InliningScope, call: JsInvocation, currentStatement: JsStatement?): InlineableResult {
val definition = functionContext.getFunctionDefinition(call, scope) val definition = functionContext.getFunctionDefinition(call, scope)
return cycleReporter.inlineCall(call) { val function = scope.importFunctionDefinition(definition)
val function = scope.importFunctionDefinition(definition) val inliningContext = InliningContext(currentStatement)
val inliningContext = InliningContext(currentStatement) val (inlineableBody, resultExpression) = FunctionInlineMutator.getInlineableCallReplacement(call, function, inliningContext)
val (inlineableBody, resultExpression) = FunctionInlineMutator.getInlineableCallReplacement(call, function, inliningContext) // body of inline function can contain call to lambdas that need to be inlined
scope.process(inlineableBody)
// body of inline function can contain call to lambdas that need to be inlined // TODO shouldn't we process the resultExpression qualifier along with the lambda inlining?
scope.process(inlineableBody) resultExpression?.synthetic = true
// TODO shouldn't we process the resultExpression qualifier along with the lambda inlining? return InlineableResult(JsBlock(inliningContext.previousStatements + inlineableBody), resultExpression)
resultExpression?.synthetic = true
InlineableResult(JsBlock(inliningContext.previousStatements + inlineableBody), resultExpression)
}
} }
} }
@@ -39,6 +39,7 @@ class FunctionContext(
fun scopeForFragment(fragment: JsProgramFragment) = if (fragment in newFragments) { fun scopeForFragment(fragment: JsProgramFragment) = if (fragment in newFragments) {
inliningScopeCache.computeIfAbsent(fragment) { inliningScopeCache.computeIfAbsent(fragment) {
loadFragment(fragment)
ProgramFragmentInliningScope(fragment) ProgramFragmentInliningScope(fragment)
} }
} else null } else null
@@ -75,8 +76,7 @@ class FunctionContext(
*/ */
private fun getFunctionDefinitionImpl(call: JsInvocation, scope: InliningScope): InlineFunctionDefinition? { private fun getFunctionDefinitionImpl(call: JsInvocation, scope: InliningScope): InlineFunctionDefinition? {
// Ensure we have the local function information // Ensure we have the local function information
// TODO is this necessary? assert(scope.fragment in inliningScopeCache)
loadFragment(scope.fragment)
return lookUpFunctionDirect(call) ?: lookUpFunctionIndirect(call, scope) ?: lookUpFunctionExternal(call, scope.fragment) return lookUpFunctionDirect(call) ?: lookUpFunctionIndirect(call, scope) ?: lookUpFunctionExternal(call, scope.fragment)
} }