Skip to content

Commit 7d9e27d

Browse files
committed
Kotlin: Avoid infinite recursion when extracting recursive interfaces
1 parent 1a1b5cb commit 7d9e27d

File tree

5 files changed

+51
-2
lines changed

5 files changed

+51
-2
lines changed

java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.codeql
22

3+
import com.github.codeql.utils.ClassInstanceStack
34
import com.github.codeql.utils.isExternalFileClassMember
45
import com.semmle.extractor.java.OdasaOutput
56
import com.semmle.util.data.StringDigestor
@@ -18,6 +19,7 @@ class ExternalDeclExtractor(
1819
val compression: Compression,
1920
val invocationTrapFile: String,
2021
val sourceFilePath: String,
22+
val classInstanceStack: ClassInstanceStack,
2123
val primitiveTypeMapping: PrimitiveTypeMapping,
2224
val pluginContext: IrPluginContext,
2325
val globalExtensionState: KotlinExtractorGlobalState,
@@ -163,6 +165,7 @@ class ExternalDeclExtractor(
163165
binaryPath,
164166
manager,
165167
this,
168+
classInstanceStack,
166169
primitiveTypeMapping,
167170
pluginContext,
168171
KotlinFileExtractor.DeclarationStack(),

java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.codeql
22

3+
import com.github.codeql.utils.ClassInstanceStack
34
import com.github.codeql.utils.versions.usesK2
45
import com.semmle.util.files.FileUtil
56
import com.semmle.util.trap.pathtransformers.PathTransformer
@@ -151,6 +152,7 @@ class KotlinExtractorExtension(
151152
}
152153
val compression = getCompression(logger)
153154

155+
val classInstanceStack = ClassInstanceStack()
154156
val primitiveTypeMapping = PrimitiveTypeMapping(logger, pluginContext)
155157
// FIXME: FileUtil expects a static global logger
156158
// which should be provided by SLF4J's factory facility. For now we set it here.
@@ -182,6 +184,7 @@ class KotlinExtractorExtension(
182184
trapDir,
183185
srcDir,
184186
file,
187+
classInstanceStack,
185188
primitiveTypeMapping,
186189
pluginContext,
187190
globalExtensionState
@@ -358,6 +361,7 @@ private fun doFile(
358361
dbTrapDir: File,
359362
dbSrcDir: File,
360363
srcFile: IrFile,
364+
classInstanceStack: ClassInstanceStack,
361365
primitiveTypeMapping: PrimitiveTypeMapping,
362366
pluginContext: IrPluginContext,
363367
globalExtensionState: KotlinExtractorGlobalState
@@ -415,6 +419,7 @@ private fun doFile(
415419
compression,
416420
invocationTrapFile,
417421
srcFilePath,
422+
classInstanceStack,
418423
primitiveTypeMapping,
419424
pluginContext,
420425
globalExtensionState,
@@ -429,6 +434,7 @@ private fun doFile(
429434
srcFilePath,
430435
null,
431436
externalDeclExtractor,
437+
classInstanceStack,
432438
primitiveTypeMapping,
433439
pluginContext,
434440
KotlinFileExtractor.DeclarationStack(),

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ open class KotlinFileExtractor(
6262
val filePath: String,
6363
dependencyCollector: OdasaOutput.TrapFileManager?,
6464
externalClassExtractor: ExternalDeclExtractor,
65+
classInstanceStack: ClassInstanceStack,
6566
primitiveTypeMapping: PrimitiveTypeMapping,
6667
pluginContext: IrPluginContext,
6768
val declarationStack: DeclarationStack,
@@ -72,6 +73,7 @@ open class KotlinFileExtractor(
7273
tw,
7374
dependencyCollector,
7475
externalClassExtractor,
76+
classInstanceStack,
7577
primitiveTypeMapping,
7678
pluginContext,
7779
globalExtensionState
@@ -433,7 +435,12 @@ open class KotlinFileExtractor(
433435
) {
434436
DeclarationStackAdjuster(c).use {
435437
if (shouldExtractOutline) {
436-
extractClassWithoutMembers(c, argsIncludingOuterClasses)
438+
classInstanceStack.push(c)
439+
try {
440+
extractClassWithoutMembers(c, argsIncludingOuterClasses)
441+
} finally {
442+
classInstanceStack.pop()
443+
}
437444
}
438445

439446
if (shouldExtractDetails) {

java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ open class KotlinUsesExtractor(
4949
open val tw: TrapWriter,
5050
val dependencyCollector: OdasaOutput.TrapFileManager?,
5151
val externalClassExtractor: ExternalDeclExtractor,
52+
val classInstanceStack: ClassInstanceStack,
5253
val primitiveTypeMapping: PrimitiveTypeMapping,
5354
val pluginContext: IrPluginContext,
5455
val globalExtensionState: KotlinExtractorGlobalState
@@ -182,6 +183,7 @@ open class KotlinUsesExtractor(
182183
filePath,
183184
dependencyCollector,
184185
externalClassExtractor,
186+
classInstanceStack,
185187
primitiveTypeMapping,
186188
pluginContext,
187189
newDeclarationStack,
@@ -199,6 +201,7 @@ open class KotlinUsesExtractor(
199201
clsFile.path,
200202
dependencyCollector,
201203
externalClassExtractor,
204+
classInstanceStack,
202205
primitiveTypeMapping,
203206
pluginContext,
204207
newDeclarationStack,
@@ -537,6 +540,19 @@ open class KotlinUsesExtractor(
537540
return Pair(p?.first ?: c, p?.second ?: argsIncludingOuterClassesBeforeReplacement)
538541
}
539542

543+
private fun avoidInfiniteRecursion(
544+
pair: Pair<IrClass, List<IrTypeArgument>?>
545+
): Pair<IrClass, List<IrTypeArgument>?> {
546+
val c = pair.first
547+
val args = pair.second
548+
if (args != null && args.isNotEmpty() && classInstanceStack.isBeingExtracted(c)) {
549+
logger.warn("Making use of {c.name} a raw type to avoid infinite recursion")
550+
return Pair(c, null)
551+
} else {
552+
return pair
553+
}
554+
}
555+
540556
// `typeArgs` can be null to describe a raw generic type.
541557
// For non-generic types it will be zero-length list.
542558
private fun addClassLabel(
@@ -545,7 +561,7 @@ open class KotlinUsesExtractor(
545561
inReceiverContext: Boolean = false
546562
): TypeResult<DbClassorinterface> {
547563
val replaced =
548-
tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement)
564+
avoidInfiniteRecursion(tryReplaceType(cBeforeReplacement, argsIncludingOuterClassesBeforeReplacement))
549565
val replacedClass = replaced.first
550566
val replacedArgsIncludingOuterClasses = replaced.second
551567

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.github.codeql.utils
2+
3+
import java.util.Stack
4+
import org.jetbrains.kotlin.ir.declarations.IrClass
5+
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
6+
7+
class ClassInstanceStack {
8+
private val stack: Stack<IrClassSymbol> = Stack()
9+
10+
fun push(c: IrClass) = stack.push(c.symbol)
11+
fun pop() = stack.pop()
12+
13+
fun isBeingExtracted(classToCheck: IrClass): Boolean {
14+
return classToCheck.symbol in stack
15+
}
16+
}
17+

0 commit comments

Comments
 (0)