Skip to content

Commit 06624d8

Browse files
authored
[TS] Support PtrCall (#311)
1 parent 63db70a commit 06624d8

File tree

23 files changed

+847
-356
lines changed

23 files changed

+847
-356
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ jobs:
130130
DEST_DIR="arkanalyzer"
131131
MAX_RETRIES=10
132132
RETRY_DELAY=3 # Delay between retries in seconds
133-
BRANCH="neo/2025-06-24"
133+
BRANCH="neo/2025-07-18"
134134
135135
for ((i=1; i<=MAX_RETRIES; i++)); do
136136
git clone --depth=1 --branch $BRANCH $REPO_URL $DEST_DIR && break
@@ -177,6 +177,9 @@ jobs:
177177
- name: Setup Gradle
178178
uses: gradle/actions/setup-gradle@v4
179179

180+
- name: Show project list
181+
run: ./gradlew projects
182+
180183
- name: Validate Project List
181184
run: ./gradlew validateProjectList
182185

buildSrc/src/main/kotlin/Dependencies.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ object Versions {
66
const val clikt = "5.0.0"
77
const val detekt = "1.23.7"
88
const val ini4j = "0.5.4"
9-
const val jacodb = "081adc271e"
9+
const val jacodb = "5acbadfed0"
1010
const val juliet = "1.3.2"
1111
const val junit = "5.9.3"
1212
const val kotlin = "2.1.0"

settings.gradle.kts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pluginManagement {
1212

1313
plugins {
1414
// https://plugins.gradle.org/plugin/com.gradle.develocity
15-
id("com.gradle.develocity") version("4.0.2")
15+
id("com.gradle.develocity") version "4.0.2"
1616
}
1717

1818
develocity {
@@ -55,4 +55,18 @@ findProject(":usvm-python:usvm-python-commons")?.name = "usvm-python-commons"
5555
// Actually, `includeBuild("../jacodb")` is enough, but there is a bug in IDEA when path is a symlink.
5656
// As a workaround, we convert it to a real absolute path.
5757
// See IDEA bug: https://youtrack.jetbrains.com/issue/IDEA-329756
58-
// includeBuild(file("../jacodb").toPath().toRealPath().toAbsolutePath())
58+
// val jacodbPath = file("jacodb").takeIf { it.exists() }
59+
// ?: file("../jacodb").takeIf { it.exists() }
60+
// ?: error("Local JacoDB directory not found")
61+
// includeBuild(jacodbPath.toPath().toRealPath().toAbsolutePath()) {
62+
// dependencySubstitution {
63+
// all {
64+
// val requested = requested
65+
// if (requested is ModuleComponentSelector && requested.group == "com.github.UnitTestBot.jacodb") {
66+
// val targetProject = ":${requested.module}"
67+
// useTarget(project(targetProject))
68+
// logger.info("Substituting ${requested.group}:${requested.module} with $targetProject")
69+
// }
70+
// }
71+
// }
72+
// }

usvm-core/src/main/kotlin/org/usvm/Context.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ open class UContext<USizeSort : USort>(
9494
val addressSort: UAddressSort = mkUninterpretedSort("Address")
9595
val nullRef: UNullRef = UNullRef(this)
9696

97-
fun mkNullRef(): USymbolicHeapRef {
97+
open fun mkNullRef(): USymbolicHeapRef {
9898
return nullRef
9999
}
100100

usvm-ts-dataflow/src/main/kotlin/org/usvm/dataflow/ts/infer/dto/EtsTypeToDto.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import org.jacodb.ets.dto.EnumValueTypeDto
2525
import org.jacodb.ets.dto.FunctionTypeDto
2626
import org.jacodb.ets.dto.GenericTypeDto
2727
import org.jacodb.ets.dto.IntersectionTypeDto
28+
import org.jacodb.ets.dto.LexicalEnvTypeDto
2829
import org.jacodb.ets.dto.LiteralTypeDto
2930
import org.jacodb.ets.dto.LocalSignatureDto
3031
import org.jacodb.ets.dto.NeverTypeDto
@@ -48,6 +49,7 @@ import org.jacodb.ets.model.EtsEnumValueType
4849
import org.jacodb.ets.model.EtsFunctionType
4950
import org.jacodb.ets.model.EtsGenericType
5051
import org.jacodb.ets.model.EtsIntersectionType
52+
import org.jacodb.ets.model.EtsLexicalEnvType
5153
import org.jacodb.ets.model.EtsLiteralType
5254
import org.jacodb.ets.model.EtsNeverType
5355
import org.jacodb.ets.model.EtsNullType
@@ -104,6 +106,14 @@ private object EtsTypeToDto : EtsType.Visitor<TypeDto> {
104106
)
105107
}
106108

109+
override fun visit(type: EtsLexicalEnvType): TypeDto {
110+
@Suppress("DEPRECATION")
111+
return LexicalEnvTypeDto(
112+
method = type.nestedMethod.toDto(),
113+
closures = type.closures.map { it.toDto() },
114+
)
115+
}
116+
107117
override fun visit(type: EtsEnumValueType): TypeDto {
108118
return EnumValueTypeDto(
109119
signature = type.signature.toDto(),

usvm-ts-dataflow/src/main/kotlin/org/usvm/dataflow/ts/infer/dto/EtsValueToDto.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
package org.usvm.dataflow.ts.infer.dto
1818

1919
import org.jacodb.ets.dto.ArrayRefDto
20+
import org.jacodb.ets.dto.CaughtExceptionRefDto
21+
import org.jacodb.ets.dto.ClosureFieldRefDto
2022
import org.jacodb.ets.dto.ConstantDto
23+
import org.jacodb.ets.dto.GlobalRefDto
2124
import org.jacodb.ets.dto.InstanceFieldRefDto
2225
import org.jacodb.ets.dto.LocalDto
2326
import org.jacodb.ets.dto.ParameterRefDto
@@ -26,7 +29,10 @@ import org.jacodb.ets.dto.ThisRefDto
2629
import org.jacodb.ets.dto.ValueDto
2730
import org.jacodb.ets.model.EtsArrayAccess
2831
import org.jacodb.ets.model.EtsBooleanConstant
32+
import org.jacodb.ets.model.EtsCaughtExceptionRef
33+
import org.jacodb.ets.model.EtsClosureFieldRef
2934
import org.jacodb.ets.model.EtsConstant
35+
import org.jacodb.ets.model.EtsGlobalRef
3036
import org.jacodb.ets.model.EtsInstanceFieldRef
3137
import org.jacodb.ets.model.EtsLocal
3238
import org.jacodb.ets.model.EtsNullConstant
@@ -112,4 +118,25 @@ private object EtsValueToDto : EtsValue.Visitor<ValueDto> {
112118
field = value.field.toDto(),
113119
)
114120
}
121+
122+
override fun visit(value: EtsCaughtExceptionRef): ValueDto {
123+
return CaughtExceptionRefDto(
124+
type = value.type.toDto(),
125+
)
126+
}
127+
128+
override fun visit(value: EtsGlobalRef): ValueDto {
129+
return GlobalRefDto(
130+
name = value.name,
131+
ref = value.ref?.toDto(),
132+
)
133+
}
134+
135+
override fun visit(value: EtsClosureFieldRef): ValueDto {
136+
return ClosureFieldRefDto(
137+
base = value.base.toDto(),
138+
fieldName = value.fieldName,
139+
type = value.type.toDto(),
140+
)
141+
}
115142
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.usvm.api
2+
3+
import org.jacodb.ets.model.EtsMethodSignature
4+
import org.jacodb.ets.model.EtsVoidType
5+
import org.usvm.UAddressSort
6+
import org.usvm.UExpr
7+
import org.usvm.machine.expr.TsUnresolvedSort
8+
import org.usvm.machine.interpreter.TsStepScope
9+
import org.usvm.machine.state.TsMethodResult
10+
import org.usvm.machine.types.mkFakeValue
11+
12+
fun mockMethodCall(
13+
scope: TsStepScope,
14+
method: EtsMethodSignature,
15+
) {
16+
scope.doWithState {
17+
val result: UExpr<*>
18+
if (method.returnType is EtsVoidType) {
19+
result = ctx.mkUndefinedValue()
20+
} else {
21+
val sort = ctx.typeToSort(method.returnType)
22+
result = when (sort) {
23+
is UAddressSort -> makeSymbolicRefUntyped()
24+
25+
is TsUnresolvedSort -> ctx.mkFakeValue(
26+
scope,
27+
makeSymbolicPrimitive(ctx.boolSort),
28+
makeSymbolicPrimitive(ctx.fp64Sort),
29+
makeSymbolicRefUntyped()
30+
)
31+
32+
else -> makeSymbolicPrimitive(sort)
33+
}
34+
}
35+
36+
methodResult = TsMethodResult.Success.MockedCall(result, method)
37+
}
38+
}

usvm-ts/src/main/kotlin/org/usvm/machine/TsContext.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,16 @@ class TsContext(
5858
val voidSort: TsVoidSort by lazy { TsVoidSort(this) }
5959
val voidValue: TsVoidValue by lazy { TsVoidValue(this) }
6060

61+
@Deprecated("Use mkUndefinedValue() or mkTsNullValue() instead")
62+
override fun mkNullRef(): Nothing {
63+
error("Use mkUndefinedValue() or mkTsNullValue() instead of mkNullRef() in TS context")
64+
}
65+
6166
/**
6267
* In TS we treat undefined value as a null reference in other objects.
6368
* For real null represented in the language we create another reference.
6469
*/
65-
private val undefinedValue: UHeapRef = mkNullRef()
70+
private val undefinedValue: UHeapRef = nullRef
6671
fun mkUndefinedValue(): UHeapRef = undefinedValue
6772

6873
private val nullValue: UConcreteHeapRef = mkConcreteHeapRef(addressCounter.freshStaticAddress())
@@ -181,7 +186,7 @@ class TsContext(
181186
}
182187

183188
fun createFakeObjectRef(): UConcreteHeapRef {
184-
val address = mkAddressCounter().freshAllocatedAddress() + MAGIC_OFFSET
189+
val address = addressCounter.freshAllocatedAddress() + MAGIC_OFFSET
185190
return mkConcreteHeapRef(address)
186191
}
187192

@@ -263,6 +268,14 @@ class TsContext(
263268
ref
264269
}
265270
}
271+
272+
// This is an identifier for a special function representing the 'resolve' function used in promises.
273+
// It is not a real function in the code, but we need it to handle promise resolution.
274+
val resolveFunctionRef: UConcreteHeapRef = allocateConcreteRef()
275+
276+
// This is an identifier for a special function representing the 'reject' function used in promises.
277+
// It is not a real function in the code, but we need it to handle promise rejection.
278+
val rejectFunctionRef: UConcreteHeapRef = allocateConcreteRef()
266279
}
267280

268281
class Constants {

usvm-ts/src/main/kotlin/org/usvm/machine/TsMethodCall.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import org.jacodb.ets.model.EtsStmtLocation
77
import org.usvm.UExpr
88

99
sealed interface TsMethodCall : EtsStmt {
10-
val instance: UExpr<*>?
10+
val instance: UExpr<*>
1111
val args: List<UExpr<*>>
1212
val returnSite: EtsStmt
1313

@@ -21,7 +21,7 @@ sealed interface TsMethodCall : EtsStmt {
2121

2222
class TsVirtualMethodCallStmt(
2323
val callee: EtsMethodSignature,
24-
override val instance: UExpr<*>?,
24+
override val instance: UExpr<*>,
2525
override val args: List<UExpr<*>>,
2626
override val returnSite: EtsStmt,
2727
) : TsMethodCall {
@@ -38,7 +38,7 @@ class TsVirtualMethodCallStmt(
3838
// and not wrapped in array (if calling a vararg method)
3939
class TsConcreteMethodCallStmt(
4040
val callee: EtsMethod,
41-
override val instance: UExpr<*>?,
41+
override val instance: UExpr<*>,
4242
override val args: List<UExpr<*>>,
4343
override val returnSite: EtsStmt,
4444
) : TsMethodCall {

0 commit comments

Comments
 (0)