New scheme for copy descriptors names
This commit is contained in:
committed by
KonstantinAnisimov
parent
34e49ffb82
commit
8235c8b2be
+10
-13
@@ -49,15 +49,13 @@ internal class DeepCopyIrTreeWithDescriptors(val targetScope: ScopeWithIr,
|
|||||||
|
|
||||||
private val descriptorSubstituteMap: MutableMap<DeclarationDescriptor, DeclarationDescriptor> = mutableMapOf()
|
private val descriptorSubstituteMap: MutableMap<DeclarationDescriptor, DeclarationDescriptor> = mutableMapOf()
|
||||||
private var typeSubstitutor: TypeSubstitutor? = null
|
private var typeSubstitutor: TypeSubstitutor? = null
|
||||||
private var inlinedFunctionName = ""
|
|
||||||
private var nameIndex = 0
|
private var nameIndex = 0
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
fun copy(irElement: IrElement, typeArgsMap: Map <TypeParameterDescriptor, KotlinType>?, functionName: String): IrElement {
|
fun copy(irElement: IrElement, typeArgumentsMap: Map <TypeParameterDescriptor, KotlinType>?): IrElement {
|
||||||
|
|
||||||
typeSubstitutor = createTypeSubstitutor(typeArgsMap)
|
typeSubstitutor = createTypeSubstitutor(typeArgumentsMap)
|
||||||
inlinedFunctionName = functionName
|
|
||||||
descriptorSubstituteMap.clear()
|
descriptorSubstituteMap.clear()
|
||||||
irElement.acceptChildrenVoid(DescriptorCollector())
|
irElement.acceptChildrenVoid(DescriptorCollector())
|
||||||
// Transform calls to object that might be returned from inline function call.
|
// Transform calls to object that might be returned from inline function call.
|
||||||
@@ -220,7 +218,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetScope: ScopeWithIr,
|
|||||||
val oldDescriptor = declaration.descriptor
|
val oldDescriptor = declaration.descriptor
|
||||||
val newDescriptor = IrTemporaryVariableDescriptorImpl(
|
val newDescriptor = IrTemporaryVariableDescriptorImpl(
|
||||||
containingDeclaration = targetScope.scope.scopeOwner,
|
containingDeclaration = targetScope.scope.scopeOwner,
|
||||||
name = generateName(oldDescriptor.name),
|
name = generateCopyName(oldDescriptor.name),
|
||||||
outType = substituteType(oldDescriptor.type)!!,
|
outType = substituteType(oldDescriptor.type)!!,
|
||||||
isMutable = oldDescriptor.isVar)
|
isMutable = oldDescriptor.isVar)
|
||||||
descriptorSubstituteMap[oldDescriptor] = newDescriptor
|
descriptorSubstituteMap[oldDescriptor] = newDescriptor
|
||||||
@@ -230,12 +228,11 @@ internal class DeepCopyIrTreeWithDescriptors(val targetScope: ScopeWithIr,
|
|||||||
|
|
||||||
//--- Copy descriptors ------------------------------------------------//
|
//--- Copy descriptors ------------------------------------------------//
|
||||||
|
|
||||||
private fun generateName(name: Name): Name {
|
private fun generateCopyName(name: Name): Name {
|
||||||
|
|
||||||
val containingName = targetScope.scope.scopeOwner.name.toString() // Name of inline target (function we inline in)
|
|
||||||
val declarationName = name.toString() // Name of declaration
|
val declarationName = name.toString() // Name of declaration
|
||||||
val indexStr = (nameIndex++).toString() // Unique for inline target index
|
val indexStr = (nameIndex++).toString() // Unique for inline target index
|
||||||
return Name.identifier(containingName + "_" + inlinedFunctionName + "_" + declarationName + "_" + indexStr)
|
return Name.identifier(declarationName + "_" + indexStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------//
|
//---------------------------------------------------------------------//
|
||||||
@@ -258,7 +255,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetScope: ScopeWithIr,
|
|||||||
return SimpleFunctionDescriptorImpl.create(
|
return SimpleFunctionDescriptorImpl.create(
|
||||||
/* containingDeclaration = */ newContainingDeclaration,
|
/* containingDeclaration = */ newContainingDeclaration,
|
||||||
/* annotations = */ oldDescriptor.annotations,
|
/* annotations = */ oldDescriptor.annotations,
|
||||||
/* name = */ generateName(oldDescriptor.name),
|
/* name = */ generateCopyName(oldDescriptor.name),
|
||||||
/* kind = */ oldDescriptor.kind,
|
/* kind = */ oldDescriptor.kind,
|
||||||
/* source = */ oldDescriptor.source
|
/* source = */ oldDescriptor.source
|
||||||
).apply {
|
).apply {
|
||||||
@@ -401,7 +398,7 @@ internal class DeepCopyIrTreeWithDescriptors(val targetScope: ScopeWithIr,
|
|||||||
val newName = if (DescriptorUtils.isAnonymousObject(oldDescriptor)) // Anonymous objects are identified by their name.
|
val newName = if (DescriptorUtils.isAnonymousObject(oldDescriptor)) // Anonymous objects are identified by their name.
|
||||||
oldDescriptor.name // We need to preserve it for LocalDeclarationsLowering.
|
oldDescriptor.name // We need to preserve it for LocalDeclarationsLowering.
|
||||||
else
|
else
|
||||||
generateName(oldDescriptor.name)
|
generateCopyName(oldDescriptor.name)
|
||||||
return ClassDescriptorImpl(
|
return ClassDescriptorImpl(
|
||||||
/* containingDeclaration = */ newContainingDeclaration,
|
/* containingDeclaration = */ newContainingDeclaration,
|
||||||
/* name = */ newName,
|
/* name = */ newName,
|
||||||
@@ -601,10 +598,10 @@ internal class DeepCopyIrTreeWithDescriptors(val targetScope: ScopeWithIr,
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun createTypeSubstitutor(typeArgsMap: Map <TypeParameterDescriptor, KotlinType>?): TypeSubstitutor? {
|
private fun createTypeSubstitutor(typeArgumentsMap: Map <TypeParameterDescriptor, KotlinType>?): TypeSubstitutor? {
|
||||||
|
|
||||||
if (typeArgsMap == null) return null
|
if (typeArgumentsMap == null) return null
|
||||||
val substitutionContext = typeArgsMap.entries.associate {
|
val substitutionContext = typeArgumentsMap.entries.associate {
|
||||||
(typeParameter, typeArgument) ->
|
(typeParameter, typeArgument) ->
|
||||||
typeParameter.typeConstructor to TypeProjectionImpl(typeArgument)
|
typeParameter.typeConstructor to TypeProjectionImpl(typeArgument)
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-85
@@ -44,7 +44,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
|||||||
|
|
||||||
internal class FunctionInlining(val context: Context): IrElementTransformerVoidWithContext() {
|
internal class FunctionInlining(val context: Context): IrElementTransformerVoidWithContext() {
|
||||||
|
|
||||||
private var copyWithDescriptors: DeepCopyIrTreeWithDescriptors? = null
|
private var copyIrElement: DeepCopyIrTreeWithDescriptors? = null
|
||||||
private val deserializer = DeserializerDriver(context)
|
private val deserializer = DeserializerDriver(context)
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
@@ -61,13 +61,11 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
val irCall = super.visitCall(expression) as IrCall
|
val irCall = super.visitCall(expression) as IrCall
|
||||||
val functionDescriptor = irCall.descriptor as FunctionDescriptor
|
val functionDescriptor = irCall.descriptor as FunctionDescriptor
|
||||||
if (functionDescriptor.needsInlining) {
|
if (functionDescriptor.needsInlining) {
|
||||||
copyWithDescriptors = DeepCopyIrTreeWithDescriptors(currentScope!!, context)
|
copyIrElement = DeepCopyIrTreeWithDescriptors(currentScope!!, context)
|
||||||
val functionDeclaration = getFunctionDeclaration(irCall)
|
val functionDeclaration = getFunctionDeclaration(irCall)
|
||||||
if (functionDeclaration == null) return irCall
|
if (functionDeclaration == null) return irCall
|
||||||
|
|
||||||
functionDeclaration.transformChildrenVoid(this) // Process recursive inline.
|
functionDeclaration.transformChildrenVoid(this) // Process recursive inline.
|
||||||
|
|
||||||
|
|
||||||
return inlineFunction(irCall, functionDeclaration) // Return newly created IrInlineBody instead of IrCall.
|
return inlineFunction(irCall, functionDeclaration) // Return newly created IrInlineBody instead of IrCall.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,57 +76,28 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
|
|
||||||
private fun inlineFunction(irCall: IrCall, functionDeclaration: IrFunction): IrExpression {
|
private fun inlineFunction(irCall: IrCall, functionDeclaration: IrFunction): IrExpression {
|
||||||
|
|
||||||
|
|
||||||
val evaluatedParameters = evaluateParameters(irCall, functionDeclaration)
|
val evaluatedParameters = evaluateParameters(irCall, functionDeclaration)
|
||||||
val parameterToArgument = evaluatedParameters.parameters
|
val parameterSubstituteMap = evaluatedParameters.parameters
|
||||||
val evaluationStatements = evaluatedParameters.statements
|
val evaluationStatements = evaluatedParameters.statements
|
||||||
|
|
||||||
val statements = (functionDeclaration.body as IrBlockBody).statements
|
val typeArgumentsMap = (irCall as IrMemberAccessExpressionBase).typeArguments
|
||||||
val returnType = functionDeclaration.descriptor.returnType!!
|
val copyFunctionDeclaration = copyIrElement!!.copy( // Create copy of original function.
|
||||||
val inlineFunctionBody1 = IrInlineFunctionBody(
|
functionDeclaration,
|
||||||
startOffset = functionDeclaration.startOffset,
|
typeArgumentsMap
|
||||||
endOffset = functionDeclaration.endOffset,
|
) as IrFunction
|
||||||
|
|
||||||
|
val statements = (copyFunctionDeclaration.body as IrBlockBody).statements
|
||||||
|
val returnType = copyFunctionDeclaration.descriptor.returnType!!
|
||||||
|
val inlineFunctionBody = IrInlineFunctionBody(
|
||||||
|
startOffset = copyFunctionDeclaration.startOffset,
|
||||||
|
endOffset = copyFunctionDeclaration.endOffset,
|
||||||
type = returnType,
|
type = returnType,
|
||||||
descriptor = functionDeclaration.descriptor.original,
|
descriptor = copyFunctionDeclaration.descriptor.original,
|
||||||
origin = null,
|
origin = null,
|
||||||
statements = statements
|
statements = statements
|
||||||
)
|
)
|
||||||
|
|
||||||
val functionName = functionDeclaration.descriptor.name.toString()
|
val transformer = ParameterSubstitutor(parameterSubstituteMap)
|
||||||
val typeArgsMap = (irCall as IrMemberAccessExpressionBase).typeArguments
|
|
||||||
val inlineFunctionBody = copyWithDescriptors!!.copy(inlineFunctionBody1, typeArgsMap, functionName) as IrInlineFunctionBody // Create copy of original function body.
|
|
||||||
|
|
||||||
val transformer = ParameterSubstitutor(parameterToArgument)
|
|
||||||
inlineFunctionBody.transformChildrenVoid(transformer) // Replace parameters with expression.
|
|
||||||
inlineFunctionBody.statements.addAll(0, evaluationStatements)
|
|
||||||
|
|
||||||
return inlineFunctionBody // Replace call site with InlineFunctionBody.
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun inlineLambda(irCall: IrCall, functionDeclaration: IrFunction): IrExpression {
|
|
||||||
|
|
||||||
val evaluatedParameters = evaluateParameters(irCall, functionDeclaration)
|
|
||||||
val parameterToArgument = evaluatedParameters.parameters
|
|
||||||
val evaluationStatements = evaluatedParameters.statements
|
|
||||||
|
|
||||||
val statements = (functionDeclaration.body as IrBlockBody).statements
|
|
||||||
val returnType = functionDeclaration.descriptor.returnType!!
|
|
||||||
val inlineFunctionBody1 = IrInlineFunctionBody(
|
|
||||||
startOffset = functionDeclaration.startOffset,
|
|
||||||
endOffset = functionDeclaration.endOffset,
|
|
||||||
type = returnType,
|
|
||||||
descriptor = functionDeclaration.descriptor.original,
|
|
||||||
origin = null,
|
|
||||||
statements = statements
|
|
||||||
)
|
|
||||||
|
|
||||||
val functionName = functionDeclaration.descriptor.name.toString()
|
|
||||||
val typeArgsMap = (irCall as IrMemberAccessExpressionBase).typeArguments
|
|
||||||
val inlineFunctionBody = copyWithDescriptors!!.copy(inlineFunctionBody1, typeArgsMap, functionName) as IrInlineFunctionBody // Create copy of original function body.
|
|
||||||
|
|
||||||
val transformer = ParameterSubstitutor(parameterToArgument)
|
|
||||||
inlineFunctionBody.transformChildrenVoid(transformer) // Replace parameters with expression.
|
inlineFunctionBody.transformChildrenVoid(transformer) // Replace parameters with expression.
|
||||||
inlineFunctionBody.statements.addAll(0, evaluationStatements)
|
inlineFunctionBody.statements.addAll(0, evaluationStatements)
|
||||||
|
|
||||||
@@ -150,7 +119,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
val argument = substituteMap[descriptor] // Find expression to replace this parameter.
|
val argument = substituteMap[descriptor] // Find expression to replace this parameter.
|
||||||
if (argument == null) return newExpression // If there is no such expression - do nothing
|
if (argument == null) return newExpression // If there is no such expression - do nothing
|
||||||
|
|
||||||
val newArgument = copyWithDescriptors!!.copy(argument, null, "lambda")
|
val newArgument = copyIrElement!!.copy(argument, null)
|
||||||
return newArgument as IrExpression
|
return newArgument as IrExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +139,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
val functionDeclaration = getLambdaFunction(lambdaArgument)
|
val functionDeclaration = getLambdaFunction(lambdaArgument)
|
||||||
if (functionDeclaration == null) return return super.visitCall(expression) // TODO
|
if (functionDeclaration == null) return return super.visitCall(expression) // TODO
|
||||||
|
|
||||||
val newExpression = inlineLambda(expression, functionDeclaration)
|
val newExpression = inlineFunction(expression, functionDeclaration)
|
||||||
return newExpression.transform(this, null)
|
return newExpression.transform(this, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,8 +163,6 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: the following checks must be asserts, however it is not sane until the bugs are fixed.
|
|
||||||
|
|
||||||
val statements = lambdaArgument.statements
|
val statements = lambdaArgument.statements
|
||||||
if (statements.size != 2) return null
|
if (statements.size != 2) return null
|
||||||
|
|
||||||
@@ -233,39 +200,61 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private class ArgumentWithValue(val descriptor: ValueDescriptor, val value: IrExpression)
|
private fun getFunctionDeclaration(irCall: IrCall): IrFunction? {
|
||||||
|
|
||||||
|
val functionDescriptor = irCall.descriptor as FunctionDescriptor
|
||||||
|
val originalDescriptor = functionDescriptor.original
|
||||||
|
val functionDeclaration =
|
||||||
|
(context.ir.originalModuleIndex.functions[originalDescriptor] ?: // Function is declared in the current module.
|
||||||
|
deserializer.deserializeInlineBody(originalDescriptor) as IrFunction?) // Function is declared in another module.
|
||||||
|
return functionDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private class ParameterWithArgument(
|
||||||
|
val parameterDescriptor: ValueDescriptor,
|
||||||
|
val argument : IrExpression
|
||||||
|
)
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private class EvaluatedParameters(
|
||||||
|
val parameters: MutableMap<ValueDescriptor, IrExpression>,
|
||||||
|
val statements: MutableList<IrStatement>
|
||||||
|
)
|
||||||
|
|
||||||
//---------------------------------------------------------------------//
|
//---------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun getArguments(irCall: IrCall, declaration: IrFunction): MutableList<ArgumentWithValue> {
|
private fun buildParameterToArgumentMap(irCall: IrCall, declaration: IrFunction): MutableList<ParameterWithArgument> {
|
||||||
|
|
||||||
val result = mutableListOf<ArgumentWithValue>()
|
val result = mutableListOf<ParameterWithArgument>()
|
||||||
val descriptor = declaration.descriptor.original
|
val descriptor = declaration.descriptor.original
|
||||||
|
|
||||||
if (irCall.dispatchReceiver != null && descriptor.dispatchReceiverParameter != null)
|
if (irCall.dispatchReceiver != null && descriptor.dispatchReceiverParameter != null)
|
||||||
result += ArgumentWithValue(descriptor.dispatchReceiverParameter!!, irCall.dispatchReceiver!!)
|
result += ParameterWithArgument(descriptor.dispatchReceiverParameter!!, irCall.dispatchReceiver!!)
|
||||||
|
|
||||||
if (irCall.extensionReceiver != null && descriptor.extensionReceiverParameter != null)
|
if (irCall.extensionReceiver != null && descriptor.extensionReceiverParameter != null)
|
||||||
result += ArgumentWithValue(descriptor.extensionReceiverParameter!!, irCall.extensionReceiver!!)
|
result += ParameterWithArgument(descriptor.extensionReceiverParameter!!, irCall.extensionReceiver!!)
|
||||||
|
|
||||||
descriptor.valueParameters.forEach { parameter ->
|
descriptor.valueParameters.forEach { parameterDescriptor ->
|
||||||
val argument = irCall.getValueArgument(parameter.index)
|
val argument = irCall.getValueArgument(parameterDescriptor.index)
|
||||||
when {
|
when {
|
||||||
argument != null -> {
|
argument != null -> {
|
||||||
result += ArgumentWithValue(parameter, argument)
|
result += ParameterWithArgument(parameterDescriptor, argument)
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter.hasDefaultValue() -> {
|
parameterDescriptor.hasDefaultValue() -> {
|
||||||
val defaultArgument = declaration.getDefault(parameter)!!.expression
|
val defaultArgument = declaration.getDefault(parameterDescriptor)!!.expression
|
||||||
result += ArgumentWithValue(parameter, defaultArgument)
|
result += ParameterWithArgument(parameterDescriptor, defaultArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter.varargElementType != null -> {
|
parameterDescriptor.varargElementType != null -> {
|
||||||
val emptyArray = IrVarargImpl(irCall.startOffset, irCall.endOffset, parameter.type, parameter.varargElementType!!)
|
val emptyArray = IrVarargImpl(irCall.startOffset, irCall.endOffset, parameterDescriptor.type, parameterDescriptor.varargElementType!!)
|
||||||
result += ArgumentWithValue(parameter, emptyArray)
|
result += ParameterWithArgument(parameterDescriptor, emptyArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> throw Error("Incomplete expression: call to $descriptor has no argument at index ${parameter.index}")
|
else -> throw Error("Incomplete expression: call to $descriptor has no argument at index ${parameterDescriptor.index}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -273,19 +262,17 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private class EvaluatedParameters(val parameters: MutableMap<ValueDescriptor, IrExpression>, val statements: MutableList<IrStatement>)
|
|
||||||
|
|
||||||
private fun evaluateParameters(irCall: IrCall, functionDeclaration: IrFunction): EvaluatedParameters {
|
private fun evaluateParameters(irCall: IrCall, functionDeclaration: IrFunction): EvaluatedParameters {
|
||||||
|
|
||||||
val parametersOld = getArguments(irCall, functionDeclaration) // Create map call_site_argument -> inline_function_parameter.
|
val parametersOld = buildParameterToArgumentMap(irCall, functionDeclaration) // Create map call_site_argument -> inline_function_parameter.
|
||||||
val parametersNew = mutableMapOf<ValueDescriptor, IrExpression> ()
|
val parametersNew = mutableMapOf<ValueDescriptor, IrExpression> ()
|
||||||
val statements = mutableListOf<IrStatement>()
|
val statements = mutableListOf<IrStatement>()
|
||||||
parametersOld.forEach {
|
parametersOld.forEach {
|
||||||
val parameter = it.descriptor.original as ValueDescriptor
|
val parameterDescriptor = it.parameterDescriptor.original as ValueDescriptor
|
||||||
val argument = it.value
|
val argument = it.argument
|
||||||
|
|
||||||
if (!needsEvaluation(argument)) {
|
if (!needsEvaluation(argument)) {
|
||||||
parametersNew[parameter] = argument
|
parametersNew[parameterDescriptor] = argument
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,22 +284,10 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
|
|||||||
val startOffset = currentScope.irElement.startOffset
|
val startOffset = currentScope.irElement.startOffset
|
||||||
val endOffset = currentScope.irElement.endOffset
|
val endOffset = currentScope.irElement.endOffset
|
||||||
val getVal = IrGetValueImpl(startOffset, endOffset, newVar.descriptor) // Create new IR element representing access the new variable.
|
val getVal = IrGetValueImpl(startOffset, endOffset, newVar.descriptor) // Create new IR element representing access the new variable.
|
||||||
parametersNew[parameter] = getVal // Parameter will be replaced with the new variable.
|
parametersNew[parameterDescriptor] = getVal // Parameter will be replaced with the new variable.
|
||||||
}
|
}
|
||||||
return EvaluatedParameters(parametersNew, statements)
|
return EvaluatedParameters(parametersNew, statements)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun getFunctionDeclaration(irCall: IrCall): IrFunction? {
|
|
||||||
|
|
||||||
val functionDescriptor = irCall.descriptor as FunctionDescriptor
|
|
||||||
val originalDescriptor = functionDescriptor.original
|
|
||||||
val functionDeclaration =
|
|
||||||
(context.ir.originalModuleIndex.functions[originalDescriptor] ?: // Function is declared in the current module.
|
|
||||||
deserializer.deserializeInlineBody(originalDescriptor) as IrFunction?) // Function is declared in another module.
|
|
||||||
return functionDeclaration
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user