Skip to content

Commit

Permalink
Update code to match ArkAnalyzer upstream
Browse files Browse the repository at this point in the history
Remove fields initializers (moved into special init method).
Make Method's body nullable since it might be absent.
Bump ArkAnalyzer, switch to using a special neo-branch in the fork.
Update JSON dumps of ArkIR for samples.
Use kotlin("test") for Kotlin-style assertions in tests.
  • Loading branch information
Lipen committed Aug 6, 2024
1 parent 00164e3 commit c379624
Show file tree
Hide file tree
Showing 59 changed files with 15,765 additions and 8,315 deletions.
16 changes: 3 additions & 13 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ jobs:

- name: Set up ArkAnalyzer
run: |
REPO_URL="https://gitee.com/openharmony-sig/arkanalyzer.git"
REPO_URL="https://gitee.com/Lipenx/arkanalyzer.git"
DEST_DIR="arkanalyzer"
MAX_RETRIES=10
RETRY_DELAY=3 # Delay between retries in seconds
BRANCH="neo/2024-08-06"
for ((i=1; i<=MAX_RETRIES; i++)); do
git clone --depth=1 $REPO_URL $DEST_DIR && break
git clone --depth=1 --branch $BRANCH $REPO_URL $DEST_DIR && break
echo "Clone failed, retrying in $RETRY_DELAY seconds..."
sleep "$RETRY_DELAY"
done
Expand All @@ -78,17 +79,6 @@ jobs:
echo "ARKANALYZER_DIR=$(realpath $DEST_DIR)" >> $GITHUB_ENV
cd $DEST_DIR
# checkout master on 2024-07-17
rev=a9d9fd6070fce5896d8e760ed7fd175b62b16605
for ((i=1; i<=MAX_RETRIES; i++)); do
git fetch --depth=1 origin $rev && break
echo "Fetch failed, retrying in $RETRY_DELAY seconds..."
sleep "$RETRY_DELAY"
done
git switch --detach $rev
npm install
npm run build
Expand Down
2 changes: 2 additions & 0 deletions buildSrc/src/main/kotlin/Tests.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.testing.Test
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import java.util.*

object Tests {
Expand All @@ -10,6 +11,7 @@ object Tests {
fun Test.setup(jacocoTestReport: TaskProvider<*>) {
testLogging {
events("passed", "skipped", "failed")
exceptionFormat = TestExceptionFormat.FULL
}
finalizedBy(jacocoTestReport) // report is always generated after tests run
val majorJavaVersion =
Expand Down
1 change: 1 addition & 0 deletions jacodb-ets/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies {
implementation(Libs.kotlinx_serialization_json)
implementation(Libs.jdot)

testImplementation(kotlin("test"))
testImplementation(project(":jacodb-analysis"))
testImplementation(testFixtures(project(":jacodb-core")))
testImplementation(Libs.mockk)
Expand Down
2 changes: 1 addition & 1 deletion jacodb-ets/src/main/kotlin/org/jacodb/ets/base/EtsRef.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ interface EtsFieldRef : EtsRef, EtsLValue {
}

data class EtsInstanceFieldRef(
val instance: EtsEntity, // Local
val instance: EtsLocal,
override val field: EtsFieldSignature,
) : EtsFieldRef {
override fun toString(): String {
Expand Down
82 changes: 29 additions & 53 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ class EtsMethodBuilder(
// so we just *unsafely* extract the type name from the "pseudo-local" here:
"instanceof" -> EtsInstanceOfExpr(left, (right as EtsLocal).name)

// TODO: Currently, ArkIR treats "in" operation just as BinopExpr.
// Ideally, it would be represented as a separate `ArkInExpr`,
// or at least as `ArkConditionExpr`, since it inherently has a boolean type.
Ops.IN -> EtsInExpr(left, right) // Note: `type` is ignored here!

Check warning on line 345 in jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Convert.kt#L345

Added line #L345 was not covered by tests

else -> error("Unknown binop: ${value.op}")
}
}
Expand All @@ -356,7 +361,10 @@ class EtsMethodBuilder(
Ops.LT_EQ -> EtsLtEqExpr(left, right)
Ops.GT -> EtsGtExpr(left, right)
Ops.GT_EQ -> EtsGtEqExpr(left, right)
Ops.IN -> EtsInExpr(left, right)

// TODO: see above
// Ops.IN -> EtsInExpr(left, right)

else -> error("Unknown relop: ${value.op}")
}
}
Expand Down Expand Up @@ -401,7 +409,7 @@ class EtsMethodBuilder(
val field = convertToEtsFieldSignature(fieldRef.field)
return when (fieldRef) {
is InstanceFieldRefDto -> EtsInstanceFieldRef(
instance = convertToEtsEntity(fieldRef.instance), // as Local
instance = convertToEtsEntity(fieldRef.instance) as EtsLocal, // safe cast
field = field,
)

Expand Down Expand Up @@ -470,7 +478,7 @@ fun convertToEtsClass(classDto: ClassDto): EtsClass {
successors = emptyList(),
predecessors = emptyList(),
stmts = listOf(
ReturnStmtDto(arg = ThisRefDto(type = ClassTypeDto(classSignatureDto)))
ReturnVoidStmtDto
)
)
val cfg = CfgDto(blocks = listOf(zeroBlock))
Expand All @@ -489,69 +497,34 @@ fun convertToEtsClass(classDto: ClassDto): EtsClass {
)
}

fun isStaticField(field: FieldDto): Boolean {
val modifiers = field.modifiers ?: return false
return modifiers.contains(ModifierDto.StringItem("StaticKeyword"))
}

val signature = EtsClassSignature(
name = classDto.signature.name,
namespace = null, // TODO
file = null, // TODO
)

val superclassSignature = classDto.superClassName?.takeIf { it != "" }?.let { spName ->
val superClassSignature = classDto.superClassName?.takeIf { it != "" }?.let { name ->
EtsClassSignature(
name = spName,
name = name,
namespace = null, // TODO
file = null, // TODO
)
}

val fields = classDto.fields.map { convertToEtsField(it) }

val (methodDtos, ctorDtos) = classDto.methods.partition { it.signature.name != "constructor" }
check(ctorDtos.size <= 1) { "Class should not have multiple constructors" }
val ctorDto = ctorDtos.singleOrNull() ?: defaultConstructorDto(classDto.signature)
val ctorDto = ctorDtos.firstOrNull() ?: defaultConstructorDto(classDto.signature)

val fields = classDto.fields.map { convertToEtsField(it) }
val methods = methodDtos.map { convertToEtsMethod(it) }

val initializers = classDto.fields.mapNotNull {
if (it.initializer != null && !isStaticField(it)) {
AssignStmtDto(
left = InstanceFieldRefDto(
instance = ThisRefDto(ClassTypeDto(classDto.signature)),
field = it.signature,
),
right = it.initializer,
)
} else null
}

val ctorBlocks = ctorDto.body.cfg.blocks
val ctorStartingBlock = ctorBlocks.single { it.id == 0 }

check(ctorStartingBlock.predecessors.isEmpty()) {
"Starting block should not have predecessors, or else the (prepended) initializers will be evaluated multiple times"
}

val newStartingBlock = ctorStartingBlock.copy(
stmts = initializers + ctorStartingBlock.stmts
)
val ctorWithInitializersDto = ctorDto.copy(
body = ctorDto.body.copy(
cfg = CfgDto(
blocks = ctorBlocks - ctorStartingBlock + newStartingBlock
)
)
)
val ctor = convertToEtsMethod(ctorWithInitializersDto)
val ctor = convertToEtsMethod(ctorDto)

return EtsClassImpl(
signature = signature,
fields = fields,
methods = methods,
ctor = ctor,
superClass = superclassSignature
superClass = superClassSignature,
)
}

Expand Down Expand Up @@ -691,17 +664,20 @@ fun convertToEtsMethodSignature(method: MethodSignatureDto): EtsMethodSignature

fun convertToEtsMethod(method: MethodDto): EtsMethod {
val signature = convertToEtsMethodSignature(method.signature)
// Note: locals are not used in the current implementation
// val locals = method.body.locals.map {
// convertToEtsEntity(it) as EtsLocal // safe cast
// }
val localsCount = method.body.locals.size
val modifiers = method.modifiers
.filterIsInstance<ModifierDto.StringItem>()
.map { it.modifier }
val builder = EtsMethodBuilder(signature, localsCount, modifiers)
val etsMethod = builder.build(method.body.cfg)
return etsMethod
if (method.body != null) {
// Note: locals are not used in the current implementation
// val locals = method.body.locals.map {
// convertToEtsEntity(it) as EtsLocal // safe cast
// }
val localsCount = method.body.locals.size
val builder = EtsMethodBuilder(signature, localsCount, modifiers)
return builder.build(method.body.cfg)
} else {
return EtsMethodImpl(signature, modifiers = modifiers)
}
}

fun convertToEtsField(field: FieldDto): EtsField {
Expand Down
3 changes: 1 addition & 2 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/dto/Model.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ data class FieldDto(
val modifiers: List<ModifierDto>? = null,
@SerialName("questionToken") val isOptional: Boolean = false, // '?'
@SerialName("exclamationToken") val isDefinitelyAssigned: Boolean = false, // '!'
val initializer: ValueDto? = null,
)

@Serializable(with = ModifierSerializer::class)
Expand All @@ -93,7 +92,7 @@ data class MethodDto(
val signature: MethodSignatureDto,
val modifiers: List<ModifierDto>,
val typeParameters: List<TypeDto>,
val body: BodyDto,
val body: BodyDto? = null,
)

@Serializable
Expand Down
114 changes: 58 additions & 56 deletions jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,72 +105,74 @@ fun EtsFileDto.toDot(useLR: Boolean = false): String {
// Link class to method:
lines += """ "$c" -> "$m" [dir=none];"""

// Link method to its first basic block:
if (method.body.cfg.blocks.isNotEmpty()) {
val b = blockId(clazz, method, method.body.cfg.blocks.first().id)
lines += """ "$m" -> "${b}.0" [lhead="$b"];"""
}

// Basic blocks with instructions:
method.body.cfg.blocks.forEach { bb ->
// BLOCK
val b = blockId(clazz, method, bb.id)
val blabel = blockLabel(bb)
lines += ""
lines += """ subgraph "$b" {"""
lines += """ cluster=true;"""
lines += """ label="$blabel";"""

// Empty block:
if (bb.stmts.isEmpty()) {
lines += """ "${b}.0" [shape=box, label="NOP"];"""
}

// Instructions inside basic block:
bb.stmts.forEachIndexed { i, inst ->
val label = statementLabel(inst)
lines += """ "${b}.${i}" [shape=box, label="$label"];"""
if (method.body != null) {
// Link method to its first basic block:
if (method.body.cfg.blocks.isNotEmpty()) {
val b = blockId(clazz, method, method.body.cfg.blocks.first().id)
lines += """ "$m" -> "${b}.0" [lhead="$b"];"""

Check warning on line 112 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L111-L112

Added lines #L111 - L112 were not covered by tests
}

// Instructions chain:
if (bb.stmts.isNotEmpty()) {
val ids = List(bb.stmts.size) { i ->
"${b}.${i}"
// Basic blocks with instructions:
method.body.cfg.blocks.forEach { bb ->

Check warning on line 116 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L116

Added line #L116 was not covered by tests
// BLOCK
val b = blockId(clazz, method, bb.id)
val blabel = blockLabel(bb)
lines += ""
lines += """ subgraph "$b" {"""
lines += """ cluster=true;"""
lines += """ label="$blabel";"""

Check warning on line 123 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L118-L123

Added lines #L118 - L123 were not covered by tests

// Empty block:
if (bb.stmts.isEmpty()) {
lines += """ "${b}.0" [shape=box, label="NOP"];"""

Check warning on line 127 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L127

Added line #L127 was not covered by tests
}
lines += " ${ids.joinToString(" -> ") { "\"$it\"" }};"
}

lines += " }"
}
// Instructions inside basic block:
bb.stmts.forEachIndexed { i, inst ->
val label = statementLabel(inst)
lines += """ "${b}.${i}" [shape=box, label="$label"];"""
}

Check warning on line 134 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L131-L134

Added lines #L131 - L134 were not covered by tests

// Links to successor blocks:
method.body.cfg.blocks.forEach { bb ->
val last = bb.stmts.lastOrNull()
val i = if (bb.stmts.isNotEmpty()) bb.stmts.lastIndex else 0
when (last ?: NopStmtDto) {
is IfStmtDto -> {
for ((j, succ) in bb.successors.withIndex()) {
val b = blockId(clazz, method, bb.id)
val bs = blockId(clazz, method, succ)
val label = if (j == 0) "true" else "false"
lines += """ "${b}.${i}" -> "${bs}.0" [lhead="$bs", label="$label"];"""
// Instructions chain:
if (bb.stmts.isNotEmpty()) {
val ids = List(bb.stmts.size) { i ->
"${b}.${i}"

Check warning on line 139 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L139

Added line #L139 was not covered by tests
}
lines += " ${ids.joinToString(" -> ") { "\"$it\"" }};"

Check warning on line 141 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L141

Added line #L141 was not covered by tests
}

is SwitchStmtDto -> {
for ((j, succ) in bb.successors.withIndex()) {
val b = blockId(clazz, method, bb.id)
val bs = blockId(clazz, method, succ)
val label = if (j == 0) "default" else "case ${j - 1}"
lines += """ "${b}.${i}" -> "${bs}.0" [lhead="$b", label="$label"];"""
lines += " }"
}

Check warning on line 145 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L144-L145

Added lines #L144 - L145 were not covered by tests

// Links to successor blocks:
method.body.cfg.blocks.forEach { bb ->
val last = bb.stmts.lastOrNull()

Check warning on line 149 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L148-L149

Added lines #L148 - L149 were not covered by tests
val i = if (bb.stmts.isNotEmpty()) bb.stmts.lastIndex else 0
when (last ?: NopStmtDto) {
is IfStmtDto -> {
for ((j, succ) in bb.successors.withIndex()) {
val b = blockId(clazz, method, bb.id)
val bs = blockId(clazz, method, succ)

Check warning on line 155 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L154-L155

Added lines #L154 - L155 were not covered by tests
val label = if (j == 0) "true" else "false"
lines += """ "${b}.${i}" -> "${bs}.0" [lhead="$bs", label="$label"];"""

Check warning on line 157 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L157

Added line #L157 was not covered by tests
}
}

is SwitchStmtDto -> {
for ((j, succ) in bb.successors.withIndex()) {
val b = blockId(clazz, method, bb.id)
val bs = blockId(clazz, method, succ)

Check warning on line 164 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L163-L164

Added lines #L163 - L164 were not covered by tests
val label = if (j == 0) "default" else "case ${j - 1}"
lines += """ "${b}.${i}" -> "${bs}.0" [lhead="$b", label="$label"];"""

Check warning on line 166 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L166

Added line #L166 was not covered by tests
}
}
}

else -> {
for (succ in bb.successors) {
val b = blockId(clazz, method, bb.id)
val bs = blockId(clazz, method, succ)
lines += """ "${b}.${i}" -> "${bs}.0" [lhead="$bs"];"""
else -> {
for (succ in bb.successors) {
val b = blockId(clazz, method, bb.id)
val bs = blockId(clazz, method, succ)
lines += """ "${b}.${i}" -> "${bs}.0" [lhead="$bs"];"""

Check warning on line 174 in jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt

View check run for this annotation

Codecov / codecov/patch

jacodb-ets/src/main/kotlin/org/jacodb/ets/utils/EtsFileDtoToDot.kt#L172-L174

Added lines #L172 - L174 were not covered by tests
}
}
}
}
Expand Down
Loading

0 comments on commit c379624

Please sign in to comment.