Skip to content

Commit b01fdc7

Browse files
authored
Number of Object's inheritors changes between runs #203 (#205)
1 parent 0d51aaf commit b01fdc7

File tree

3 files changed

+84
-28
lines changed

3 files changed

+84
-28
lines changed

jacodb-core/src/main/kotlin/org/jacodb/impl/features/HierarchyExtension.kt

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ import org.jacodb.impl.storage.defaultBatchSize
3232
import org.jacodb.impl.storage.jooq.tables.references.CLASSES
3333
import org.jacodb.impl.storage.jooq.tables.references.CLASSHIERARCHIES
3434
import org.jacodb.impl.storage.jooq.tables.references.SYMBOLS
35+
import org.jooq.Condition
3536
import org.jooq.Record3
3637
import org.jooq.SelectConditionStep
38+
import org.jooq.impl.DSL
3739
import java.util.concurrent.Future
3840

3941
@Suppress("SqlResolve")
@@ -169,25 +171,28 @@ private fun SelectConditionStep<Record3<Long?, String?, Long?>>.batchingProcess(
169171
}
170172
}
171173

174+
private fun List<Long>.where(offset: Long?): Condition {
175+
return when (offset) {
176+
null -> CLASSES.LOCATION_ID.`in`(this)
177+
else -> CLASSES.LOCATION_ID.`in`(this).and(CLASSES.ID.greaterThan(offset))
178+
}
179+
}
180+
172181
internal fun JcClasspath.allClassesExceptObject(direct: Boolean): Sequence<PersistenceClassSource> {
173182
val locationIds = registeredLocations.map { it.id }
174183
if (direct) {
175184
return BatchedSequence(defaultBatchSize) { offset, batchSize ->
176185
db.persistence.read { jooq ->
177-
val whereCondition = if (offset == null) {
178-
CLASSES.LOCATION_ID.`in`(locationIds)
179-
} else {
180-
CLASSES.LOCATION_ID.`in`(locationIds).and(
181-
CLASSES.ID.greaterThan(offset)
182-
)
183-
}
186+
val whereCondition = locationIds.where(offset)
184187
jooq.select(CLASSES.ID, SYMBOLS.NAME, CLASSES.LOCATION_ID)
185188
.from(CLASSES)
186189
.join(SYMBOLS).on(SYMBOLS.ID.eq(CLASSES.NAME))
187190
.where(
188191
whereCondition
189-
.and(CLASSES.ID.notIn(jooq.select(CLASSHIERARCHIES.SUPER_ID).from(
190-
CLASSHIERARCHIES)))
192+
.and(DSL.notExists(
193+
jooq.select(CLASSHIERARCHIES.ID).from(CLASSHIERARCHIES)
194+
.where(CLASSHIERARCHIES.CLASS_ID.eq(CLASSES.ID).and(CLASSHIERARCHIES.IS_CLASS_REF.eq(true)))
195+
))
191196
.and(SYMBOLS.NAME.notEqual(JAVA_OBJECT))
192197
)
193198
.batchingProcess(this, batchSize)
@@ -196,13 +201,7 @@ internal fun JcClasspath.allClassesExceptObject(direct: Boolean): Sequence<Persi
196201
}
197202
return BatchedSequence(defaultBatchSize) { offset, batchSize ->
198203
db.persistence.read { jooq ->
199-
val whereCondition = if (offset == null) {
200-
CLASSES.LOCATION_ID.`in`(locationIds)
201-
} else {
202-
CLASSES.LOCATION_ID.`in`(locationIds).and(
203-
CLASSES.ID.greaterThan(offset)
204-
)
205-
}
204+
val whereCondition = locationIds.where(offset)
206205

207206
jooq.select(CLASSES.ID, SYMBOLS.NAME, CLASSES.LOCATION_ID)
208207
.from(CLASSES)

jacodb-core/src/main/kotlin/org/jacodb/impl/features/InMemoryHierarchy.kt

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@
1616

1717
package org.jacodb.impl.features
1818

19-
import org.jacodb.api.ByteCodeIndexer
20-
import org.jacodb.api.ClassSource
21-
import org.jacodb.api.JcClassOrInterface
22-
import org.jacodb.api.JcClasspath
23-
import org.jacodb.api.JcDatabase
24-
import org.jacodb.api.JcDatabasePersistence
25-
import org.jacodb.api.JcFeature
26-
import org.jacodb.api.JcSignal
27-
import org.jacodb.api.RegisteredLocation
19+
import org.jacodb.api.*
2820
import org.jacodb.api.ext.JAVA_OBJECT
2921
import org.jacodb.impl.fs.PersistenceClassSource
3022
import org.jacodb.impl.fs.className
@@ -33,6 +25,7 @@ import org.jacodb.impl.storage.defaultBatchSize
3325
import org.jacodb.impl.storage.jooq.tables.references.CLASSES
3426
import org.jacodb.impl.storage.jooq.tables.references.CLASSHIERARCHIES
3527
import org.jacodb.impl.storage.jooq.tables.references.SYMBOLS
28+
import org.jacodb.impl.storage.withoutAutoCommit
3629
import org.jooq.DSLContext
3730
import org.jooq.impl.DSL
3831
import org.objectweb.asm.Type
@@ -45,19 +38,21 @@ typealias InMemoryHierarchyCache = ConcurrentHashMap<Long, ConcurrentHashMap<Lon
4538
private val objectJvmName = Type.getInternalName(Any::class.java)
4639

4740
class InMemoryHierarchyIndexer(
48-
private val persistence: JcDatabasePersistence,
41+
persistence: JcDatabasePersistence,
4942
private val location: RegisteredLocation,
5043
private val hierarchy: InMemoryHierarchyCache
5144
) : ByteCodeIndexer {
5245

46+
private val interner = persistence.symbolInterner
47+
5348
override fun index(classNode: ClassNode) {
54-
val clazzSymbolId = persistence.findSymbolId(classNode.name.className) ?: return
49+
val clazzSymbolId = interner.findOrNew(classNode.name.className)
5550
val superName = classNode.superName
5651
val superclasses = when {
5752
superName != null && superName != objectJvmName -> classNode.interfaces + superName
5853
else -> classNode.interfaces
5954
}
60-
superclasses.mapNotNull { persistence.findSymbolId(it.className) }
55+
superclasses.map { interner.findOrNew(it.className) }
6156
.forEach {
6257
hierarchy.getOrPut(it) { ConcurrentHashMap() }
6358
.getOrPut(location.id) { ConcurrentHashMap.newKeySet() }
@@ -66,6 +61,9 @@ class InMemoryHierarchyIndexer(
6661
}
6762

6863
override fun flush(jooq: DSLContext) {
64+
jooq.withoutAutoCommit { conn ->
65+
interner.flush(conn)
66+
}
6967
}
7068
}
7169

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2022 UnitTestBot contributors (utbot.org)
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jacodb.testing
18+
19+
import kotlinx.coroutines.runBlocking
20+
import org.jacodb.api.JcClasspath
21+
import org.jacodb.api.ext.JAVA_OBJECT
22+
import org.jacodb.impl.features.hierarchyExt
23+
import org.junit.jupiter.api.AfterEach
24+
import org.junit.jupiter.api.Assertions
25+
import org.junit.jupiter.api.Test
26+
27+
@LifecycleTest
28+
class SubclassesTest : BaseTest() {
29+
30+
companion object : WithGlobalDB()
31+
32+
private val withDB = WithDB()
33+
34+
private val anotherDb = withDB.db
35+
private val anotherCp: JcClasspath by lazy {
36+
runBlocking {
37+
anotherDb.awaitBackgroundJobs()
38+
anotherDb.classpath(allClasspath)
39+
}
40+
}
41+
42+
@Test
43+
fun `Object subclasses should be the same`() {
44+
runBlocking {
45+
val hierarchy = cp.hierarchyExt()
46+
val anotherHierarchy = anotherCp.hierarchyExt()
47+
Assertions.assertEquals(
48+
hierarchy.findSubClasses(JAVA_OBJECT, false, includeOwn = true).count(),
49+
anotherHierarchy.findSubClasses(JAVA_OBJECT, false, includeOwn = true).count()
50+
)
51+
}
52+
}
53+
54+
@AfterEach
55+
fun `cleanup another db`() = runBlocking {
56+
withDB.cleanup()
57+
}
58+
59+
}

0 commit comments

Comments
 (0)