Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
0030972
Refactor Date model lookup preparation
CaelmBleidd Oct 13, 2026
032afa9
Separate unresolved value construction blocks
CaelmBleidd Oct 14, 2026
79f9390
Fix TypeScript subtype relation and enumeration contracts
CaelmBleidd Oct 14, 2026
521c529
Preserve runtime kinds when extracting conditional values
CaelmBleidd Oct 14, 2026
eb00e46
Preserve conditional payloads and primitive array storage descriptors
CaelmBleidd Oct 14, 2026
54dc43a
Isolate artificial locals when cloning TypeScript states
CaelmBleidd Oct 14, 2026
2e3b3a5
Share guarded UTF-16 storage helpers
CaelmBleidd Oct 14, 2026
0ea8295
Preserve selected guards in numeric conversion
CaelmBleidd Oct 14, 2026
72fca1d
Correct fake equality operands and clarify value encoding
CaelmBleidd Oct 14, 2026
f3ec301
Preserve runtime limitation outcomes when observers fail
CaelmBleidd Oct 14, 2026
4bcd3c7
Select Calls witness project by function identity
CaelmBleidd Oct 14, 2026
6cff81a
Reject malformed number encodings in numeric domains
CaelmBleidd Oct 14, 2026
aae1102
Resolve module defaults without confusing re-export aliases
CaelmBleidd Oct 14, 2026
3465406
Validate identifier, numeric domain, and coverage string boundaries
CaelmBleidd Oct 14, 2026
136d68c
Preserve original Istanbul coverage map keys
CaelmBleidd Oct 14, 2026
32eb538
Model primitive string coercion in loose equality
CaelmBleidd Oct 14, 2026
2b2b141
Report unmodeled object string loose equality as unsupported
CaelmBleidd Oct 14, 2026
f53b70a
Convert invalid Istanbul source paths into typed diagnostics
CaelmBleidd Oct 14, 2026
295e0ce
Bound hierarchy traversal and preserve method call kinds
CaelmBleidd Oct 14, 2026
2808dd1
Track fake reference provenance without address offsets
CaelmBleidd Oct 14, 2026
58b82c9
Resolve absolute imports by exact file signatures
CaelmBleidd Oct 14, 2026
aa504d0
Compare primitive strings by UTF-16 lexical order
CaelmBleidd Oct 14, 2026
8f820d1
Treat generic defaults independently from constructor bounds
CaelmBleidd Oct 14, 2026
2d58256
Preserve runtime addition boundaries and JavaScript remainder semantics
CaelmBleidd Oct 14, 2026
fc12da2
Clarify runtime storage predicates and field type lookup
CaelmBleidd Oct 14, 2026
5243f4d
Name the modeled string backing condition
CaelmBleidd Oct 14, 2026
c2dc5ad
Name projection capability case arguments
CaelmBleidd Oct 14, 2026
b30dd37
Name precondition projection arguments
CaelmBleidd Oct 14, 2026
d7f5710
Name coverage mapping branch predicates
CaelmBleidd Oct 14, 2026
fd37350
Extract compatible runtime payloads for typed static field writes
CaelmBleidd Oct 14, 2026
1f57da6
Assert clean reachable states in domain projection tests
CaelmBleidd Oct 14, 2026
97db16b
Validate object intersection input fields across declaring members
CaelmBleidd Oct 14, 2026
01c9557
Use imported union type in candidate input resolution
CaelmBleidd Oct 14, 2026
d2c577b
Report call graph resolution failures during Calls preflight
CaelmBleidd Oct 14, 2026
c78c171
Resolve instance call arguments in explicit ordered blocks
CaelmBleidd Oct 14, 2026
a0225d1
Clarify Calls source root containment checks
CaelmBleidd Oct 14, 2026
45ea7cf
Revoke dense array proofs after own-slot deletion
CaelmBleidd Oct 14, 2026
7d3e773
Name Calls coverage replay and universe predicates
CaelmBleidd Oct 14, 2026
cfe6b2d
Dispatch project instance methods through their runtime hierarchy
CaelmBleidd Oct 14, 2026
baef1e5
Respect lexical owners when resolving static calls
CaelmBleidd Oct 14, 2026
bc9376a
Simplify global and local register operations
CaelmBleidd Oct 14, 2026
9779301
Verify frozen source checkouts throughout legacy Calls runs
CaelmBleidd Oct 14, 2026
cd1500e
Clarify synthetic method parameter construction
CaelmBleidd Oct 14, 2026
1dd8ab9
Bound compatibility array growth and preserve legal call arities
CaelmBleidd Oct 14, 2026
8b80465
Resolve unknown call inputs with explicit ordered control flow
CaelmBleidd Oct 14, 2026
0147f86
Freeze selected native frontend runtime identity
CaelmBleidd Oct 14, 2026
54b2ac6
Preserve bounded array copy semantics and own-slot provenance
CaelmBleidd Oct 14, 2026
71d29ed
Keep replay execution-kind errors outside source-throw observation
CaelmBleidd Oct 14, 2026
b6be90a
Preserve backed string exception witnesses
CaelmBleidd Oct 14, 2026
babeaae
Bound legacy array fill and preserve boxed values
CaelmBleidd Oct 14, 2026
48b1190
Decode added object fields from their physical storage
CaelmBleidd Oct 14, 2026
c7b33f3
Clarify source target observer predicates
CaelmBleidd Oct 14, 2026
af329a0
Ignore evaluated extra array model arguments
CaelmBleidd Oct 14, 2026
e4a75ce
Separate runtime telemetry setup from event handler
CaelmBleidd Oct 14, 2026
3eb8daf
Stop unsupported raw TypeScript statements explicitly
CaelmBleidd Oct 14, 2026
bad193b
Accept ignored extra arguments in the String replaceAll model
CaelmBleidd Oct 14, 2026
f3a31a6
Resolve source bindings before numeric builtin local fallbacks
CaelmBleidd Oct 14, 2026
82e08a3
Separate String replaceAll and substitution model operations
CaelmBleidd Oct 14, 2026
759fa12
Name the frontend numeric snapshot guard before update resolution
CaelmBleidd Oct 14, 2026
80ec4fd
Clarify legacy array approximations and remove unused sort threading
CaelmBleidd Oct 14, 2026
971a864
Preserve target mode when validating stored Calls witnesses
CaelmBleidd Oct 14, 2026
97be34c
Clarify coverage replay types and plain-object encoding
CaelmBleidd Oct 14, 2026
3bd3181
Separate static initialization bookkeeping from source field names
CaelmBleidd Oct 14, 2026
23be54e
Use relational coercion for inclusive TypeScript comparisons
CaelmBleidd Oct 14, 2026
0b7c68f
Name the Date argument-prefix conversion predicate
CaelmBleidd Oct 14, 2026
9c441af
Name Date constructor normalization requirements before branching
CaelmBleidd Oct 15, 2026
43f0601
Reuse the canonical TypeError signature in Error storage
CaelmBleidd Oct 15, 2026
c8796b0
fix(ts): preserve census input completeness and outcome status
CaelmBleidd Oct 15, 2026
b6a6c91
fix(ts): isolate promise callbacks and preserve async result lifecycle
CaelmBleidd Oct 15, 2026
8df3b09
fix(ts-tests): declare boolean result for Number.isNaN DSL calls
CaelmBleidd Oct 15, 2026
b07c4e5
test(ts): assert exhaustive while-break reachability outcomes
CaelmBleidd Oct 15, 2026
0091ae9
fix(ts): preserve registered Promise identity in resolve
CaelmBleidd Oct 15, 2026
154ff8d
refactor(ts): centralize default-scope local name routing
CaelmBleidd Oct 15, 2026
a7a8e56
refactor(ts-tests): mark import diagnostics manual and resolve aliases
CaelmBleidd Oct 15, 2026
e62a531
fix(calls): classify stopped residual paths as unsupported
CaelmBleidd Oct 15, 2026
4119ff9
fix(ts-census): cap method execution by remaining project budget
CaelmBleidd Oct 15, 2026
e48ebf6
fix(ts-census): keep deadline diagnostic accurate after final method
CaelmBleidd Oct 15, 2026
83fbe8e
refactor(calls): name raw result target identity validation
CaelmBleidd Oct 15, 2026
966b5e2
fix(calls): preserve raw input during summarization
CaelmBleidd Oct 15, 2026
836b490
fix(ts-tests): compare infinities and assert signed zero
CaelmBleidd Oct 15, 2026
fcd3efc
fix(ts-census): retain observed child handles for timeout cleanup
CaelmBleidd Oct 15, 2026
4ce6f4a
fix(ts): ignore failed terminal reachability callbacks
CaelmBleidd Oct 15, 2026
8a209b0
fix(ts-build): resolve timeout units and report SDK generation failure
CaelmBleidd Oct 15, 2026
6c1bc64
fix(ts): isolate state visualization output and process failures
CaelmBleidd Oct 15, 2026
05d6b57
refactor(calls-tests): separate fixture setup stages
CaelmBleidd Oct 15, 2026
d87bf0b
refactor(pbt-tests): remove unused resource root helper
CaelmBleidd Oct 15, 2026
0f17c3b
refactor(ts-tests): remove discarded analysis result wrappers
CaelmBleidd Oct 15, 2026
26806b2
fix(ts): ignore extra arguments in compatibility array join
CaelmBleidd Oct 15, 2026
57b2291
Pin the reviewed JacoDB frontend for TypeScript integration
CaelmBleidd Oct 15, 2026
198c267
Fix array metadata initialization and awaited rejection compilation
CaelmBleidd Oct 15, 2026
aa78048
Repair regression compilation and retain PBT failure reports
CaelmBleidd Oct 15, 2026
5f332f8
Prune infeasible TypeScript entries and address strict lint
CaelmBleidd Oct 15, 2026
4e84f6c
Bound legacy array fill and simplify call approximations
CaelmBleidd Oct 15, 2026
acf4c5d
Align TypeScript regression fixtures with supported contracts
CaelmBleidd Oct 15, 2026
51c8d36
Separate replay scenarios and address TypeScript test lint
CaelmBleidd Oct 15, 2026
ad5669f
Align replay case file name with its declaration
CaelmBleidd Oct 15, 2026
b92f23a
Resolve Calls Detekt warnings and split experiment tests
CaelmBleidd Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ jobs:
- name: Run TS PBT integration baseline
run: env -u ARKANALYZER_DIR ETS_IR_PROVIDER=ts-frontend ./gradlew :usvm-ts-pbt:check :usvm-ts-fast-check:check

- name: Upload Gradle reports
if: (!cancelled())
uses: actions/upload-artifact@v4
with:
name: gradle-reports-ts-pbt
path: |
usvm-ts-pbt/build/test-results/**/*.xml
usvm-ts-pbt/build/reports/tests/
usvm-ts-fast-check/build/test-results/**/*.xml
usvm-ts-fast-check/build/reports/tests/
retention-days: 1

lint:
runs-on: ubuntu-latest
steps:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ internal fun runCoverageCell(
search = search,
replayErrors = events.count { it.error != null },
completionMismatches = events.indices.count { index ->
events[index].replay?.completion != null &&
events[index].replay?.completion != search.candidates[index].completion
val replayCompletion = events[index].replay?.completion
replayCompletion != null && replayCompletion != search.candidates[index].completion
},
replayElapsedMillis = events.sumOf { event -> event.replay?.replayElapsedMillis ?: 0L },
cellElapsedMillis = cellStarted.elapsedNow().inWholeMilliseconds,
Expand Down Expand Up @@ -235,11 +235,12 @@ private fun parseCoverageCellRequest(
)
require(universeRecord.identity == expectedUniverseIdentity) { "Coverage universe identity differs" }
val universe = universeRecord.universe
require(source.function.sourceFile in universe.sourceFiles && universe.supportedStatementKeys.isNotEmpty()) {
val includesEntrySource = source.function.sourceFile in universe.sourceFiles
val hasSupportedStatements = universe.supportedStatementKeys.isNotEmpty()
require(includesEntrySource && hasSupportedStatements) {
"Coverage universe does not include the entry source statements"
}


val identity = CallsCoverageCellIdentity(
schemaVersion = 3,
toolRevision = runtimeToolRevision,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import org.usvm.ts.pbt.backend.PropertyCoverageRequest
import org.usvm.ts.pbt.backend.PropertyRunConfiguration
import org.usvm.ts.pbt.backend.PropertyRunStatus
import org.usvm.ts.pbt.backend.SourceFileCoverage
import org.usvm.ts.pbt.backend.SourceRange
import org.usvm.ts.pbt.fastcheck.FastCheckBackend
import org.usvm.ts.pbt.fastcheck.FastCheckRuntime
import org.usvm.ts.pbt.fastcheck.TypeScriptSourceInspector
import org.usvm.ts.pbt.model.BooleanDomain
import org.usvm.ts.pbt.model.ExecutionKind
import org.usvm.ts.pbt.model.JsConcreteValue
import org.usvm.ts.pbt.model.PropertyDefinition
import org.usvm.ts.pbt.model.PropertyId
Expand Down Expand Up @@ -71,7 +73,7 @@ internal class OriginalTypeScriptCoverageReplayer(
}

private fun prepareReplay(): CoverageReplaySetup {
require(function.entryPoint.executionKind == org.usvm.ts.pbt.model.ExecutionKind.SYNC) {
require(function.entryPoint.executionKind == ExecutionKind.SYNC) {
"Coverage replay requires a synchronous entry point"
}
val source = sourceRoot.resolve(function.sourceFile).normalize()
Expand Down Expand Up @@ -262,7 +264,7 @@ private fun collectHits(
}
}

private fun org.usvm.ts.pbt.backend.SourceRange.stableKey(): String =
private fun SourceRange.stableKey(): String =
"${start.line}:${start.column}-${end.line}:${end.column}"

private fun coverageWrapper(
Expand Down Expand Up @@ -302,8 +304,9 @@ private fun coverageWrapper(
return { kind: 'number', value: 'finite', bits };
}
if (Array.isArray(value)) return { kind: 'array', elements: value.map(encodeValue) };
if (value !== null && typeof value === 'object' &&
(Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) {
const isPlainObject = value !== null && typeof value === 'object' &&
(Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
if (isPlainObject) {
const fields = Object.fromEntries(Object.entries(value).map(([name, field]) => {
if (['__proto__', 'constructor', 'prototype'].includes(name)) {
throw new Error('Unsupported replay object property name');
Expand Down
67 changes: 46 additions & 21 deletions usvm-ts-calls/src/main/kotlin/org/usvm/ts/calls/CallsExperiment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,28 @@ internal data class CallsExperimentManifest(
"Tool revision must identify a clean Git commit"
}
require(toolRevision == modelSet.toolRevision) { "Tool and model-set revisions must match" }

val functions = projects.flatMap(CallsProjectCase::functions)
require(functions.map(CallsFunctionCase::functionId).distinct().size == functions.size) {
val uniqueFunctionIds = functions.map(CallsFunctionCase::functionId).distinct()
require(uniqueFunctionIds.size == functions.size) {
"Function IDs must be unique"
}
require(functions.all { function -> function.sourceFile == function.entryPoint.module }) {
val sourceFilesMatchEntryPoints = functions.all { function ->
function.sourceFile == function.entryPoint.module
}
require(sourceFilesMatchEntryPoints) {
"Function source files must match their replay entry-point modules"
}

val targets = functions.flatMap(CallsFunctionCase::targets)
require(targets.map(CallsSourceTarget::targetId).distinct().size == targets.size) {
val uniqueTargetIds = targets.map(CallsSourceTarget::targetId).distinct()
require(uniqueTargetIds.size == targets.size) {
"Target IDs must be unique"
}
require(
functions.all { function ->
function.targets.all { target -> target.sourcePath == function.sourceFile }
},
) {
val targetsBelongToFunctionSourceFiles = functions.all { function ->
function.targets.all { target -> target.sourcePath == function.sourceFile }
}
require(targetsBelongToFunctionSourceFiles) {
"Every target must belong to its function source file"
}
}
Expand Down Expand Up @@ -267,12 +273,12 @@ internal object CallsBuildIdentity {
}

val toolRevision: String by lazy {
checkNotNull(properties.getProperty("tool.revision")).takeIf(String::isNotBlank)
properties.getProperty("tool.revision")?.takeIf(String::isNotBlank)
?: error("Missing tool revision in calls build identity")
}

val nativeFrontendRevision: String by lazy {
checkNotNull(properties.getProperty("native.frontend.revision")).takeIf(String::isNotBlank)
properties.getProperty("native.frontend.revision")?.takeIf(String::isNotBlank)
?: error("Missing native frontend revision in calls build identity")
}
}
Expand All @@ -282,6 +288,8 @@ internal class CallsExperimentRunner(
private val targetReplayer: CallsTargetReplayer,
private val runtimeToolRevision: String = CallsBuildIdentity.toolRevision,
private val eventRecordWriter: ((Path, CallsRawRecord) -> Unit)? = null,
private val sourceCheckoutVerifier: (Path, String) -> Unit = ::verifyCallsGitCheckout,
private val frontendRuntimeVerifier: (String) -> Unit = ::verifyCallsExternalFrontendRuntime,
) {
fun run(
manifest: CallsExperimentManifest,
Expand Down Expand Up @@ -348,6 +356,15 @@ internal class CallsExperimentRunner(
resultRows = resultRows,
),
)

manifest.projects.forEach { project ->
val sourceRoot = manifestDirectory.resolve(project.sourceRoot).normalize().toRealPath()
sourceCheckoutVerifier(sourceRoot, project.revision)
}
if (!manifest.nativeFrontendRevision.startsWith("bundled:")) {
frontendRuntimeVerifier(manifest.nativeFrontendRevision)
}

Files.move(
partialOutput,
rawOutput,
Expand Down Expand Up @@ -415,6 +432,8 @@ internal class CallsExperimentRunner(
profile: CallsExperimentProfile,
appendUnknownCall: (CallsRawRecord) -> Unit,
): CallsTargetResult {
sourceCheckoutVerifier(sourceRoot, project.revision)

val request = CallsSymbolicSearchRequest(
sourceRoot = sourceRoot,
project = project,
Expand Down Expand Up @@ -443,14 +462,19 @@ internal class CallsExperimentRunner(
),
)
eventWrites.requireComplete()
sourceCheckoutVerifier(sourceRoot, project.revision)

val replay = symbolic.inputs?.let { inputs ->
targetReplayer.replay(
val replayResult = targetReplayer.replay(
sourceRoots = listOf(sourceRoot),
entryPoint = function.entryPoint,
inputs = inputs,
target = target,
timeoutMillis = manifest.perTargetBudgetMillis,
)
sourceCheckoutVerifier(sourceRoot, project.revision)

replayResult
}

return CallsTargetResult(
Expand Down Expand Up @@ -543,16 +567,17 @@ internal object CallsRawResultsReader {
require(results.all { result -> result.experimentId == metadata.experimentId }) {
"Result experiment ID does not match metadata"
}
require(
results.all { result ->
val identity = targetIdentities[Triple(result.projectId, result.functionId, result.targetId)]
identity != null &&
result.revision == identity.revision &&
result.development == identity.development &&
result.siteId == identity.siteId &&
result.targetMode == identity.targetMode
},
) { "Result target identity does not match metadata" }
val resultsMatchTargetIdentities = results.all { result ->
val identity = targetIdentities[Triple(result.projectId, result.functionId, result.targetId)]
identity != null &&
result.revision == identity.revision &&
result.development == identity.development &&
result.siteId == identity.siteId &&
result.targetMode == identity.targetMode
}

require(resultsMatchTargetIdentities) { "Result target identity does not match metadata" }

val resultKeys = results.map { result ->
ResultKey(
projectId = result.projectId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ fun main(args: Array<String>) {
"coverage-cell" -> runCoverageCell(args.drop(1))
"replay-witness" -> replayWitness(args.drop(1))
"summarize" -> summarize(args.drop(1))
"frontend-identity" -> System.out.appendLine(frontendIdentity(args.drop(1)))
else -> error(usage())
}
}
Expand Down Expand Up @@ -82,6 +83,9 @@ private fun summarize(args: List<String>) {
val rawInput = Path.of(args[0]).toAbsolutePath().normalize()
val output = Path.of(args[1]).toAbsolutePath().normalize()
val summary = CallsExperimentAggregator.summarize(rawInput)
val outputIsRawInput = rawInput == output || (Files.exists(output) && Files.isSameFile(rawInput, output))
require(!outputIsRawInput) { "Summary output must differ from raw input" }

val json = Json {
encodeDefaults = true
explicitNulls = false
Expand All @@ -91,6 +95,12 @@ private fun summarize(args: List<String>) {
Files.writeString(output, json.encodeToUtf8SafeString(summary) + "\n")
}

internal fun frontendIdentity(args: List<String>): String {
require(args.size == 1) { usage() }
val frontendDirectory = Path.of(args.single()).toRealPath()
return callsExternalFrontendIdentity(frontendDirectory)
}

private fun usage(): String = """
Usage:
calls class-catalog
Expand All @@ -102,6 +112,7 @@ private fun usage(): String = """
calls coverage-cell <source-manifest.json> <project-index> <function-index> <profile> <seed> <output-directory> <budget-ms> <solver-limit-ms> <candidate-cap> <universe.json>
calls replay-witness <frozen-manifest.json> <results.jsonl> <project-id> <function-id> <target-id> <profile> <seed>
calls summarize <results.jsonl> <summary.json>
calls frontend-identity <external-frontend-directory>
""".trimIndent()

private const val REPLAY_WITNESS_ARGUMENT_COUNT = 7
Loading
Loading