Skip to content

Commit b02da29

Browse files
Add special objects handlers to type processor (#279)
--------- Co-authored-by: Alexey Menshutin <alex.menshutin99@gmail.com>
1 parent 9a0b139 commit b02da29

File tree

3 files changed

+173
-59
lines changed

3 files changed

+173
-59
lines changed

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

Lines changed: 96 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -209,59 +209,102 @@ sealed interface EtsTypeFact {
209209
) : EtsTypeFact
210210

211211
companion object {
212-
internal val allStringProperties = listOf(
213-
"length",
214-
"constructor",
215-
"anchor",
216-
"at",
217-
"big",
218-
"blink",
219-
"bold",
220-
"charAt",
221-
"charCodeAt",
222-
"codePointAt",
223-
"concat",
224-
"endsWith",
225-
"fontcolor",
226-
"fontsize",
227-
"fixed",
228-
"includes",
229-
"indexOf",
230-
"isWellFormed",
231-
"italics",
232-
"lastIndexOf",
233-
"link",
234-
"localeCompare",
235-
"match",
236-
"matchAll",
237-
"normalize",
238-
"padEnd",
239-
"padStart",
240-
"repeat",
241-
"replace",
242-
"replaceAll",
243-
"search",
244-
"slice",
245-
"small",
246-
"split",
247-
"strike",
248-
"sub",
249-
"substr",
250-
"substring",
251-
"sup",
252-
"startsWith",
253-
"toString",
254-
"toWellFormed",
255-
"trim",
256-
"trimStart",
257-
"trimLeft",
258-
"trimEnd",
259-
"trimRight",
260-
"toLocaleLowerCase",
261-
"toLocaleUpperCase",
262-
"toLowerCase",
263-
"toUpperCase",
264-
"valueOf",
212+
internal val allStringProperties = mapOf(
213+
"length" to NumberEtsTypeFact,
214+
"constructor" to FunctionEtsTypeFact,
215+
"anchor" to FunctionEtsTypeFact,
216+
"at" to FunctionEtsTypeFact,
217+
"big" to FunctionEtsTypeFact,
218+
"blink" to FunctionEtsTypeFact,
219+
"bold" to FunctionEtsTypeFact,
220+
"charAt" to FunctionEtsTypeFact,
221+
"charCodeAt" to FunctionEtsTypeFact,
222+
"codePointAt" to FunctionEtsTypeFact,
223+
"concat" to FunctionEtsTypeFact,
224+
"endsWith" to FunctionEtsTypeFact,
225+
"fixed" to FunctionEtsTypeFact,
226+
"fontcolor" to FunctionEtsTypeFact,
227+
"fontsize" to FunctionEtsTypeFact,
228+
"includes" to FunctionEtsTypeFact,
229+
"indexOf" to FunctionEtsTypeFact,
230+
"isWellFormed" to FunctionEtsTypeFact,
231+
"italics" to FunctionEtsTypeFact,
232+
"lastIndexOf" to FunctionEtsTypeFact,
233+
"link" to FunctionEtsTypeFact,
234+
"localeCompare" to FunctionEtsTypeFact,
235+
"match" to FunctionEtsTypeFact,
236+
"matchAll" to FunctionEtsTypeFact,
237+
"normalize" to FunctionEtsTypeFact,
238+
"padEnd" to FunctionEtsTypeFact,
239+
"padStart" to FunctionEtsTypeFact,
240+
"repeat" to FunctionEtsTypeFact,
241+
"replace" to FunctionEtsTypeFact,
242+
"replaceAll" to FunctionEtsTypeFact,
243+
"search" to FunctionEtsTypeFact,
244+
"slice" to FunctionEtsTypeFact,
245+
"small" to FunctionEtsTypeFact,
246+
"split" to FunctionEtsTypeFact,
247+
"strike" to FunctionEtsTypeFact,
248+
"sub" to FunctionEtsTypeFact,
249+
"substr" to FunctionEtsTypeFact,
250+
"substring" to FunctionEtsTypeFact,
251+
"sup" to FunctionEtsTypeFact,
252+
"startsWith" to FunctionEtsTypeFact,
253+
"toString" to FunctionEtsTypeFact,
254+
"toWellFormed" to FunctionEtsTypeFact,
255+
"trim" to FunctionEtsTypeFact,
256+
"trimStart" to FunctionEtsTypeFact,
257+
"trimLeft" to FunctionEtsTypeFact,
258+
"trimEnd" to FunctionEtsTypeFact,
259+
"trimRight" to FunctionEtsTypeFact,
260+
"toLocaleLowerCase" to FunctionEtsTypeFact,
261+
"toLocaleUpperCase" to FunctionEtsTypeFact,
262+
"toLowerCase" to FunctionEtsTypeFact,
263+
"toUpperCase" to FunctionEtsTypeFact,
264+
"valueOf" to FunctionEtsTypeFact,
265+
)
266+
267+
internal val allArrayProperties = mapOf(
268+
"constructor" to FunctionEtsTypeFact,
269+
"length" to NumberEtsTypeFact,
270+
"at" to FunctionEtsTypeFact,
271+
"concat" to FunctionEtsTypeFact,
272+
"copyWithin" to FunctionEtsTypeFact,
273+
"entries" to FunctionEtsTypeFact,
274+
"every" to FunctionEtsTypeFact,
275+
"fill" to FunctionEtsTypeFact,
276+
"filter" to FunctionEtsTypeFact,
277+
"find" to FunctionEtsTypeFact,
278+
"findIndex" to FunctionEtsTypeFact,
279+
"findLast" to FunctionEtsTypeFact,
280+
"findLastIndex" to FunctionEtsTypeFact,
281+
"flat" to FunctionEtsTypeFact,
282+
"flatMap" to FunctionEtsTypeFact,
283+
"forEach" to FunctionEtsTypeFact,
284+
"includes" to FunctionEtsTypeFact,
285+
"indexOf" to FunctionEtsTypeFact,
286+
"join" to FunctionEtsTypeFact,
287+
"keys" to FunctionEtsTypeFact,
288+
"lastIndexOf" to FunctionEtsTypeFact,
289+
"map" to FunctionEtsTypeFact,
290+
"pop" to FunctionEtsTypeFact,
291+
"push" to FunctionEtsTypeFact,
292+
"reduce" to FunctionEtsTypeFact,
293+
"reduceRight" to FunctionEtsTypeFact,
294+
"reverse" to FunctionEtsTypeFact,
295+
"shift" to FunctionEtsTypeFact,
296+
"slice" to FunctionEtsTypeFact,
297+
"some" to FunctionEtsTypeFact,
298+
"sort" to FunctionEtsTypeFact,
299+
"splice" to FunctionEtsTypeFact,
300+
"toLocaleString" to FunctionEtsTypeFact,
301+
"toReversed" to FunctionEtsTypeFact,
302+
"toSorted" to FunctionEtsTypeFact,
303+
"toSpliced" to FunctionEtsTypeFact,
304+
"toString" to FunctionEtsTypeFact,
305+
"unshift" to FunctionEtsTypeFact,
306+
"values" to FunctionEtsTypeFact,
307+
"with" to FunctionEtsTypeFact,
265308
)
266309

267310
fun mkUnionType(vararg types: EtsTypeFact): EtsTypeFact = mkUnionType(types.toHashSet())

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

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import org.jacodb.ets.model.EtsType
2424
import org.usvm.dataflow.ts.infer.EtsTypeFact.AnyEtsTypeFact
2525
import org.usvm.dataflow.ts.infer.EtsTypeFact.ArrayEtsTypeFact
2626
import org.usvm.dataflow.ts.infer.EtsTypeFact.BooleanEtsTypeFact
27+
import org.usvm.dataflow.ts.infer.EtsTypeFact.Companion.allArrayProperties
28+
import org.usvm.dataflow.ts.infer.EtsTypeFact.Companion.allStringProperties
2729
import org.usvm.dataflow.ts.infer.EtsTypeFact.Companion.mkIntersectionType
2830
import org.usvm.dataflow.ts.infer.EtsTypeFact.Companion.mkUnionType
2931
import org.usvm.dataflow.ts.infer.EtsTypeFact.FunctionEtsTypeFact
@@ -76,7 +78,16 @@ class TypeFactProcessor(
7678

7779
is AnyEtsTypeFact -> type
7880

79-
is StringEtsTypeFact,
81+
is StringEtsTypeFact -> {
82+
when (other) {
83+
is ObjectEtsTypeFact -> intersect(other, type)
84+
is UnionEtsTypeFact -> intersect(other, type)
85+
is IntersectionEtsTypeFact -> intersect(other, type)
86+
is GuardedTypeFact -> intersect(other, type)
87+
else -> null
88+
}
89+
}
90+
8091
is NumberEtsTypeFact,
8192
is BooleanEtsTypeFact,
8293
is NullEtsTypeFact,
@@ -113,12 +124,15 @@ class TypeFactProcessor(
113124
}
114125
}
115126

127+
is ObjectEtsTypeFact -> intersect(other, type)
128+
116129
else -> null
117130
}
118131

119132
is ObjectEtsTypeFact -> when (other) {
120133
is ObjectEtsTypeFact -> intersect(type, other)
121134
is StringEtsTypeFact -> intersect(type, other)
135+
is ArrayEtsTypeFact -> intersect(type, other)
122136
is FunctionEtsTypeFact -> mkIntersectionType(type, other)
123137
is UnionEtsTypeFact -> intersect(other, type)
124138
is IntersectionEtsTypeFact -> intersect(other, type)
@@ -192,16 +206,43 @@ class TypeFactProcessor(
192206
if (obj.cls == EtsStringType) return string
193207
if (obj.cls != null) return null
194208

209+
// Check intersection is string
210+
val intersectionIsString = obj.properties.all { (name, type) ->
211+
val stringPropertyType = allStringProperties[name] ?: return@all false
212+
intersect(stringPropertyType, type) == stringPropertyType
213+
}
214+
if (intersectionIsString) {
215+
return StringEtsTypeFact
216+
}
217+
195218
val intersectionProperties = obj.properties
196-
.filter { it.key in EtsTypeFact.allStringProperties }
197-
.mapValues { (_, type) ->
198-
// TODO: intersect with the corresponding type of String's property
199-
type
219+
.mapValues { (name, type) ->
220+
val stringPropertyType = allStringProperties[name] ?: return@mapValues type
221+
intersect(stringPropertyType, type) ?: return null
200222
}
201223

202224
return ObjectEtsTypeFact(null, intersectionProperties)
203225
}
204226

227+
private fun intersect(obj: ObjectEtsTypeFact, array: ArrayEtsTypeFact): EtsTypeFact? {
228+
// Check intersection is array
229+
val intersectionIsArray = obj.properties.all { (name, type) ->
230+
val arrayPropertyType = allArrayProperties[name] ?: return@all false
231+
intersect(arrayPropertyType, type) == arrayPropertyType
232+
}
233+
if (intersectionIsArray) {
234+
return array
235+
}
236+
237+
val intersectionProperties = obj.properties
238+
.mapValues { (name, type) ->
239+
val arrayPropertyType = allArrayProperties[name] ?: return@mapValues type
240+
intersect(arrayPropertyType, type) ?: return null
241+
}
242+
243+
return ObjectEtsTypeFact(obj.cls, intersectionProperties)
244+
}
245+
205246
private fun union(unionType: UnionEtsTypeFact, other: EtsTypeFact): EtsTypeFact {
206247
val result = hashSetOf<EtsTypeFact>()
207248
for (type in unionType.types) {
@@ -261,7 +302,7 @@ class TypeFactProcessor(
261302
if (obj.cls != null) return mkUnionType(obj, string)
262303

263304
for (p in obj.properties.keys) {
264-
if (p !in EtsTypeFact.allStringProperties) {
305+
if (p !in allStringProperties) {
265306
return mkUnionType(obj, string)
266307
}
267308
}

usvm-ts-dataflow/src/test/resources/ts/testcases.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,36 @@ class CaseAssignLocalToArrayElementNumber {
215215

216216
// ----------------------------------------
217217

218+
// Case `y := x.length, x: any[]`
219+
class CaseAssignLocalToArrayLength {
220+
entrypoint(x: any[]) {
221+
let y = x.length; // y: number
222+
x[0] = y; // x: Array<any>
223+
this.infer(x);
224+
}
225+
226+
infer(a: any) {
227+
const EXPECTED_ARG_0 = "Array<any>";
228+
}
229+
}
230+
231+
// ----------------------------------------
232+
233+
// Case `y := x.length, x: string`
234+
class CaseAssignLocalToStringLength {
235+
entrypoint(x: any) {
236+
x = "abacaba"; // x: string
237+
let y = x.length; // y: number
238+
this.infer(x);
239+
}
240+
241+
infer(a: any) {
242+
const EXPECTED_ARG_0 = "string";
243+
}
244+
}
245+
246+
// ----------------------------------------
247+
218248
interface ICustom {
219249
a: number;
220250
b: string;

0 commit comments

Comments
 (0)