mirror of
https://github.com/github/codeql.git
synced 2025-12-16 08:43:11 +01:00
Kotlin: Avoid infinite recursion when extracting recursive interfaces
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.github.codeql
|
package com.github.codeql
|
||||||
|
|
||||||
|
import com.github.codeql.utils.ClassInstanceStack
|
||||||
import com.github.codeql.utils.isExternalFileClassMember
|
import com.github.codeql.utils.isExternalFileClassMember
|
||||||
import com.semmle.extractor.java.OdasaOutput
|
import com.semmle.extractor.java.OdasaOutput
|
||||||
import com.semmle.util.data.StringDigestor
|
import com.semmle.util.data.StringDigestor
|
||||||
@@ -18,6 +19,7 @@ class ExternalDeclExtractor(
|
|||||||
val compression: Compression,
|
val compression: Compression,
|
||||||
val invocationTrapFile: String,
|
val invocationTrapFile: String,
|
||||||
val sourceFilePath: String,
|
val sourceFilePath: String,
|
||||||
|
val classInstanceStack: ClassInstanceStack,
|
||||||
val primitiveTypeMapping: PrimitiveTypeMapping,
|
val primitiveTypeMapping: PrimitiveTypeMapping,
|
||||||
val pluginContext: IrPluginContext,
|
val pluginContext: IrPluginContext,
|
||||||
val globalExtensionState: KotlinExtractorGlobalState,
|
val globalExtensionState: KotlinExtractorGlobalState,
|
||||||
@@ -163,6 +165,7 @@ class ExternalDeclExtractor(
|
|||||||
binaryPath,
|
binaryPath,
|
||||||
manager,
|
manager,
|
||||||
this,
|
this,
|
||||||
|
classInstanceStack,
|
||||||
primitiveTypeMapping,
|
primitiveTypeMapping,
|
||||||
pluginContext,
|
pluginContext,
|
||||||
KotlinFileExtractor.DeclarationStack(),
|
KotlinFileExtractor.DeclarationStack(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.github.codeql
|
package com.github.codeql
|
||||||
|
|
||||||
|
import com.github.codeql.utils.ClassInstanceStack
|
||||||
import com.github.codeql.utils.versions.usesK2
|
import com.github.codeql.utils.versions.usesK2
|
||||||
import com.semmle.util.files.FileUtil
|
import com.semmle.util.files.FileUtil
|
||||||
import com.semmle.util.trap.pathtransformers.PathTransformer
|
import com.semmle.util.trap.pathtransformers.PathTransformer
|
||||||
@@ -151,6 +152,7 @@ class KotlinExtractorExtension(
|
|||||||
}
|
}
|
||||||
val compression = getCompression(logger)
|
val compression = getCompression(logger)
|
||||||
|
|
||||||
|
val classInstanceStack = ClassInstanceStack()
|
||||||
val primitiveTypeMapping = PrimitiveTypeMapping(logger, pluginContext)
|
val primitiveTypeMapping = PrimitiveTypeMapping(logger, pluginContext)
|
||||||
// FIXME: FileUtil expects a static global logger
|
// FIXME: FileUtil expects a static global logger
|
||||||
// which should be provided by SLF4J's factory facility. For now we set it here.
|
// which should be provided by SLF4J's factory facility. For now we set it here.
|
||||||
@@ -182,6 +184,7 @@ class KotlinExtractorExtension(
|
|||||||
trapDir,
|
trapDir,
|
||||||
srcDir,
|
srcDir,
|
||||||
file,
|
file,
|
||||||
|
classInstanceStack,
|
||||||
primitiveTypeMapping,
|
primitiveTypeMapping,
|
||||||
pluginContext,
|
pluginContext,
|
||||||
globalExtensionState
|
globalExtensionState
|
||||||
@@ -358,6 +361,7 @@ private fun doFile(
|
|||||||
dbTrapDir: File,
|
dbTrapDir: File,
|
||||||
dbSrcDir: File,
|
dbSrcDir: File,
|
||||||
srcFile: IrFile,
|
srcFile: IrFile,
|
||||||
|
classInstanceStack: ClassInstanceStack,
|
||||||
primitiveTypeMapping: PrimitiveTypeMapping,
|
primitiveTypeMapping: PrimitiveTypeMapping,
|
||||||
pluginContext: IrPluginContext,
|
pluginContext: IrPluginContext,
|
||||||
globalExtensionState: KotlinExtractorGlobalState
|
globalExtensionState: KotlinExtractorGlobalState
|
||||||
@@ -415,6 +419,7 @@ private fun doFile(
|
|||||||
compression,
|
compression,
|
||||||
invocationTrapFile,
|
invocationTrapFile,
|
||||||
srcFilePath,
|
srcFilePath,
|
||||||
|
classInstanceStack,
|
||||||
primitiveTypeMapping,
|
primitiveTypeMapping,
|
||||||
pluginContext,
|
pluginContext,
|
||||||
globalExtensionState,
|
globalExtensionState,
|
||||||
@@ -429,6 +434,7 @@ private fun doFile(
|
|||||||
srcFilePath,
|
srcFilePath,
|
||||||
null,
|
null,
|
||||||
externalDeclExtractor,
|
externalDeclExtractor,
|
||||||
|
classInstanceStack,
|
||||||
primitiveTypeMapping,
|
primitiveTypeMapping,
|
||||||
pluginContext,
|
pluginContext,
|
||||||
KotlinFileExtractor.DeclarationStack(),
|
KotlinFileExtractor.DeclarationStack(),
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ open class KotlinFileExtractor(
|
|||||||
val filePath: String,
|
val filePath: String,
|
||||||
dependencyCollector: OdasaOutput.TrapFileManager?,
|
dependencyCollector: OdasaOutput.TrapFileManager?,
|
||||||
externalClassExtractor: ExternalDeclExtractor,
|
externalClassExtractor: ExternalDeclExtractor,
|
||||||
|
classInstanceStack: ClassInstanceStack,
|
||||||
primitiveTypeMapping: PrimitiveTypeMapping,
|
primitiveTypeMapping: PrimitiveTypeMapping,
|
||||||
pluginContext: IrPluginContext,
|
pluginContext: IrPluginContext,
|
||||||
val declarationStack: DeclarationStack,
|
val declarationStack: DeclarationStack,
|
||||||
@@ -72,6 +73,7 @@ open class KotlinFileExtractor(
|
|||||||
tw,
|
tw,
|
||||||
dependencyCollector,
|
dependencyCollector,
|
||||||
externalClassExtractor,
|
externalClassExtractor,
|
||||||
|
classInstanceStack,
|
||||||
primitiveTypeMapping,
|
primitiveTypeMapping,
|
||||||
pluginContext,
|
pluginContext,
|
||||||
globalExtensionState
|
globalExtensionState
|
||||||
@@ -496,12 +498,17 @@ open class KotlinFileExtractor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
extractClassModifiers(c, id)
|
extractClassModifiers(c, id)
|
||||||
extractClassSupertypes(
|
classInstanceStack.push(c)
|
||||||
c,
|
try {
|
||||||
id,
|
extractClassSupertypes(
|
||||||
if (argsIncludingOuterClasses == null) ExtractSupertypesMode.Raw
|
c,
|
||||||
else ExtractSupertypesMode.Specialised(argsIncludingOuterClasses)
|
id,
|
||||||
)
|
if (argsIncludingOuterClasses == null) ExtractSupertypesMode.Raw
|
||||||
|
else ExtractSupertypesMode.Specialised(argsIncludingOuterClasses)
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
classInstanceStack.pop()
|
||||||
|
}
|
||||||
|
|
||||||
val locId = getLocation(c, argsIncludingOuterClasses)
|
val locId = getLocation(c, argsIncludingOuterClasses)
|
||||||
tw.writeHasLocation(id, locId)
|
tw.writeHasLocation(id, locId)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ open class KotlinUsesExtractor(
|
|||||||
open val tw: TrapWriter,
|
open val tw: TrapWriter,
|
||||||
val dependencyCollector: OdasaOutput.TrapFileManager?,
|
val dependencyCollector: OdasaOutput.TrapFileManager?,
|
||||||
val externalClassExtractor: ExternalDeclExtractor,
|
val externalClassExtractor: ExternalDeclExtractor,
|
||||||
|
val classInstanceStack: ClassInstanceStack,
|
||||||
val primitiveTypeMapping: PrimitiveTypeMapping,
|
val primitiveTypeMapping: PrimitiveTypeMapping,
|
||||||
val pluginContext: IrPluginContext,
|
val pluginContext: IrPluginContext,
|
||||||
val globalExtensionState: KotlinExtractorGlobalState
|
val globalExtensionState: KotlinExtractorGlobalState
|
||||||
@@ -182,6 +183,7 @@ open class KotlinUsesExtractor(
|
|||||||
filePath,
|
filePath,
|
||||||
dependencyCollector,
|
dependencyCollector,
|
||||||
externalClassExtractor,
|
externalClassExtractor,
|
||||||
|
classInstanceStack,
|
||||||
primitiveTypeMapping,
|
primitiveTypeMapping,
|
||||||
pluginContext,
|
pluginContext,
|
||||||
newDeclarationStack,
|
newDeclarationStack,
|
||||||
@@ -199,6 +201,7 @@ open class KotlinUsesExtractor(
|
|||||||
clsFile.path,
|
clsFile.path,
|
||||||
dependencyCollector,
|
dependencyCollector,
|
||||||
externalClassExtractor,
|
externalClassExtractor,
|
||||||
|
classInstanceStack,
|
||||||
primitiveTypeMapping,
|
primitiveTypeMapping,
|
||||||
pluginContext,
|
pluginContext,
|
||||||
newDeclarationStack,
|
newDeclarationStack,
|
||||||
@@ -537,6 +540,19 @@ open class KotlinUsesExtractor(
|
|||||||
return Pair(p?.first ?: c, p?.second ?: argsIncludingOuterClassesBeforeReplacement)
|
return Pair(p?.first ?: c, p?.second ?: argsIncludingOuterClassesBeforeReplacement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun avoidInfiniteRecursion(
|
||||||
|
pair: Pair<IrClass, List<IrTypeArgument>?>
|
||||||
|
): Pair<IrClass, List<IrTypeArgument>?> {
|
||||||
|
val c = pair.first
|
||||||
|
val args = pair.second
|
||||||
|
if (args != null && args.isNotEmpty() && classInstanceStack.possiblyCyclicExtraction(c, args)) {
|
||||||
|
logger.warn("Making use of ${c.name} a raw type to avoid infinite recursion")
|
||||||
|
return Pair(c, null)
|
||||||
|
} else {
|
||||||
|
return pair
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// `typeArgs` can be null to describe a raw generic type.
|
// `typeArgs` can be null to describe a raw generic type.
|
||||||
// For non-generic types it will be zero-length list.
|
// For non-generic types it will be zero-length list.
|
||||||
private fun addClassLabel(
|
private fun addClassLabel(
|
||||||
@@ -545,7 +561,7 @@ open class KotlinUsesExtractor(
|
|||||||
inReceiverContext: Boolean = false
|
inReceiverContext: Boolean = false
|
||||||
): TypeResult<DbClassorinterface> {
|
): TypeResult<DbClassorinterface> {
|
||||||
val replaced =
|
val replaced =
|
||||||
tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement)
|
avoidInfiniteRecursion(tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement))
|
||||||
val replacedClass = replaced.first
|
val replacedClass = replaced.first
|
||||||
val replacedArgsIncludingOuterClasses = replaced.second
|
val replacedArgsIncludingOuterClasses = replaced.second
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.github.codeql.utils
|
||||||
|
|
||||||
|
import java.util.Stack
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
|
||||||
|
class ClassInstanceStack {
|
||||||
|
private val stack: Stack<IrClass> = Stack()
|
||||||
|
|
||||||
|
fun push(c: IrClass) = stack.push(c)
|
||||||
|
fun pop() = stack.pop()
|
||||||
|
|
||||||
|
private fun checkTypeArgs(sym: IrClassSymbol, args: List<IrTypeArgument>): Boolean {
|
||||||
|
for (arg in args) {
|
||||||
|
if (arg is IrTypeProjection) {
|
||||||
|
if (checkType(sym, arg.type)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkType(sym: IrClassSymbol, type: IrType): Boolean {
|
||||||
|
if (type is IrSimpleType) {
|
||||||
|
val decl = type.classifier.owner
|
||||||
|
if (decl.symbol == sym) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (checkTypeArgs(sym, type.arguments)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun possiblyCyclicExtraction(classToCheck: IrClass, args: List<IrTypeArgument>): Boolean {
|
||||||
|
for (c in stack) {
|
||||||
|
if (c.symbol == classToCheck.symbol && checkTypeArgs(c.symbol, args)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user