@@ -24,6 +24,7 @@ import org.usvm.machine.interpreter.PromiseState
2424import org.usvm.machine.interpreter.markResolved
2525import org.usvm.machine.interpreter.setResolvedValue
2626import org.usvm.sizeSort
27+ import org.usvm.types.first
2728import org.usvm.types.firstOrNull
2829import org.usvm.util.mkArrayIndexLValue
2930import org.usvm.util.mkArrayLengthLValue
@@ -546,7 +547,6 @@ private fun TsExprResolver.handleArrayJoin(
546547 * The `start` index is inclusive, and the `end` index is exclusive.
547548 * If `start` is not provided, it defaults to `0`.
548549 * If `end` is not provided, it defaults to the length of the array.
549- * This method returns the sliced array.
550550 *
551551 * https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.slice
552552 */
@@ -566,6 +566,7 @@ private fun TsExprResolver.handleArraySlice(
566566 }
567567 val startBv: UExpr <TsSizeSort > = when (start.sort) {
568568 sizeSort -> start.asExpr(sizeSort)
569+
569570 fp64Sort -> mkFpToBvExpr(
570571 roundingMode = fpRoundingModeSortDefaultValue(),
571572 value = start.asExpr(fp64Sort),
@@ -586,6 +587,7 @@ private fun TsExprResolver.handleArraySlice(
586587 }
587588 val endBv: UExpr <TsSizeSort > = when (end.sort) {
588589 sizeSort -> end.asExpr(sizeSort)
590+
589591 fp64Sort -> mkFpToBvExpr(
590592 roundingMode = fpRoundingModeSortDefaultValue(),
591593 value = end.asExpr(fp64Sort),
@@ -623,9 +625,20 @@ private fun TsExprResolver.handleArraySlice(
623625}
624626
625627/* *
626- * Handles the `Array.concat(...arrays)` method call.
627- * Concatenates the specified `arrays` to the end of the current array.
628- * This method modifies the array in place and returns the modified array.
628+ * Handles the `Array.concat(...items)` method call.
629+ * Returns a new array containing the elements of the original array followed by the array elements of each argument.
630+ *
631+ * ### Examples:
632+ * ```
633+ * let a = [1, 2];
634+ * let b = [3, 4];
635+ *
636+ * a.concat() -> [1, 2]
637+ * a.concat(b) -> [1, 2, 3, 4]
638+ * a.concat(b, 5) -> [1, 2, 3, 4, 5]
639+ *
640+ * a == [1, 2] // not modified
641+ * ```
629642 *
630643 * https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.concat
631644 */
@@ -641,37 +654,44 @@ private fun TsExprResolver.handleArrayConcat(
641654
642655 val args = expr.args.map { resolve(it) ? : return null }
643656
657+ logger.warn {
658+ " Array.concat() is not fully implemented, returning a symbolic array approximation"
659+ }
660+
644661 scope.calcOnState {
645- // Read the length of the array
646- val lengthLValue = mkArrayLengthLValue(array, arrayType)
647- val length = scope.calcOnState { memory.read(lengthLValue) }
648-
649- // Iterate over the provided arrays and concatenate them
650- args.forEach { arg ->
651- val otherArray = arg.asExpr(addressSort)
652-
653- // Read the length of the argument array
654- val argLengthLValue = mkArrayLengthLValue(otherArray, arrayType)
655- val argLength = memory.read(argLengthLValue)
656-
657- // Copy the elements from the argument array to the end of the current array
658- memory.memcpy(
659- srcRef = otherArray,
660- dstRef = array,
661- type = arrayType,
662- elementSort = elementSort,
663- fromSrc = 0 .toBv().asExpr(sizeSort),
664- fromDst = length,
665- length = argLength,
666- )
667-
668- // Update the length of the current array
669- val newLength = mkBvAddExpr(length, argLength)
670- scope.doWithState { memory.write(lengthLValue, newLength, guard = trueExpr) }
662+ // Allocate a new array for the concatenated result
663+ val resultArray = memory.allocConcrete(arrayType)
664+
665+ // Read the length of the original array
666+ val originalLengthLValue = mkArrayLengthLValue(array, arrayType)
667+ val originalLength = memory.read(originalLengthLValue)
668+
669+ // Calculate the total length of the result
670+ var totalLength = originalLength
671+ for (arg in args) {
672+ // For each array-like argument, add its length to the total length
673+ if (arg.sort == addressSort) {
674+ // TODO: handle empty type stream
675+ val argType = memory.typeStreamOf(arg.asExpr(addressSort)).first()
676+ if (argType is EtsArrayType ) {
677+ val argLengthLValue = mkArrayLengthLValue(arg.asExpr(addressSort), argType)
678+ val argLength = memory.read(argLengthLValue)
679+ totalLength = mkBvAddExpr(totalLength, argLength)
680+ continue
681+ }
682+ }
683+ // For non-array arguments, treat them as a single element
684+ totalLength = mkBvAddExpr(totalLength, 1 .toBv())
671685 }
672686
673- // Return the modified array
674- array
687+ // TODO: fill the resultArray
688+
689+ // Set the length of the result array
690+ val resultLengthLValue = mkArrayLengthLValue(resultArray, arrayType)
691+ memory.write(resultLengthLValue, totalLength, guard = trueExpr)
692+
693+ // Return the new concatenated array (with symbolic content)
694+ resultArray
675695 }
676696}
677697
@@ -699,7 +719,7 @@ private fun TsExprResolver.handleArrayIndexOf(
699719 val length = memory.read(lengthLValue)
700720
701721 // Create a symbolic index that is either -1 (not found) or a valid index
702- val symbolicResult = memory.mocker.createMockSymbol( null , sizeSort, ownership )
722+ val symbolicResult = makeSymbolicPrimitive( sizeSort)
703723
704724 // Add constraints: result is either -1 or in range [0, length-1]
705725 val notFound = mkEq(symbolicResult, mkBv(- 1 , sizeSort))
0 commit comments