builtin: fix memory corruption in array `.clone()` and `.repeat()` (#10394)
parent
3dc4d13160
commit
a2243054a5
|
@ -107,7 +107,15 @@ fn (mut a array) ensure_cap(required int) {
|
|||
}
|
||||
|
||||
// repeat returns a new array with the given array elements repeated given times.
|
||||
// `cgen` will replace this with an apropriate call to `repeat_to_depth()`
|
||||
pub fn (a array) repeat(count int) array {
|
||||
return unsafe { a.repeat_to_depth(count, 0) }
|
||||
}
|
||||
|
||||
// version of `repeat()` that handles multi dimensional arrays
|
||||
// `unsafe` to call directly because `depth` is not checked
|
||||
[unsafe]
|
||||
pub fn (a array) repeat_to_depth(count int, depth int) array {
|
||||
if count < 0 {
|
||||
panic('array.repeat: count is negative: $count')
|
||||
}
|
||||
|
@ -121,13 +129,10 @@ pub fn (a array) repeat(count int) array {
|
|||
len: count * a.len
|
||||
cap: count * a.len
|
||||
}
|
||||
size_of_array := int(sizeof(array))
|
||||
for i in 0 .. count {
|
||||
if a.len > 0 && a.element_size == size_of_array {
|
||||
ary := array{}
|
||||
unsafe { C.memcpy(&ary, a.data, size_of_array) }
|
||||
ary_clone := ary.clone()
|
||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) }
|
||||
if a.len > 0 && depth > 0 {
|
||||
ary_clone := unsafe { a.clone_to_depth(depth - 1) }
|
||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) }
|
||||
} else {
|
||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) }
|
||||
}
|
||||
|
@ -335,8 +340,19 @@ fn (a array) clone_static() array {
|
|||
return a.clone()
|
||||
}
|
||||
|
||||
fn (a array) clone_static_to_depth(depth int) array {
|
||||
return unsafe { a.clone_to_depth(depth) }
|
||||
}
|
||||
|
||||
// clone returns an independent copy of a given array.
|
||||
// this will be overwritten by `cgen` with an apropriate call to `.clone_to_depth()`
|
||||
pub fn (a &array) clone() array {
|
||||
return unsafe { a.clone_to_depth(0) }
|
||||
}
|
||||
|
||||
// recursively clone given array - `unsafe` when called directly because depth is not checked
|
||||
[unsafe]
|
||||
pub fn (a &array) clone_to_depth(depth int) array {
|
||||
mut size := a.cap * a.element_size
|
||||
if size == 0 {
|
||||
size++
|
||||
|
@ -348,57 +364,20 @@ pub fn (a &array) clone() array {
|
|||
cap: a.cap
|
||||
}
|
||||
// Recursively clone-generated elements if array element is array type
|
||||
size_of_array := int(sizeof(array))
|
||||
if a.element_size == size_of_array {
|
||||
mut is_elem_array := true
|
||||
if depth > 0 {
|
||||
for i in 0 .. a.len {
|
||||
ar := array{}
|
||||
unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) }
|
||||
if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 {
|
||||
is_elem_array = false
|
||||
break
|
||||
}
|
||||
ar_clone := ar.clone()
|
||||
unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
|
||||
ar_clone := unsafe { ar.clone_to_depth(depth - 1) }
|
||||
unsafe { arr.set_unsafe(i, &ar_clone) }
|
||||
}
|
||||
if is_elem_array {
|
||||
return arr
|
||||
return arr
|
||||
} else {
|
||||
if !isnil(a.data) {
|
||||
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
if !isnil(a.data) {
|
||||
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
fn (a &array) slice_clone(start int, _end int) array {
|
||||
mut end := _end
|
||||
$if !no_bounds_checking ? {
|
||||
if start > end {
|
||||
panic('array.slice: invalid slice index ($start > $end)')
|
||||
}
|
||||
if end > a.len {
|
||||
panic('array.slice: slice bounds out of range ($end >= $a.len)')
|
||||
}
|
||||
if start < 0 {
|
||||
panic('array.slice: slice bounds out of range ($start < 0)')
|
||||
}
|
||||
}
|
||||
mut data := &byte(0)
|
||||
offset := start * a.element_size
|
||||
unsafe {
|
||||
data = &byte(a.data) + offset
|
||||
}
|
||||
l := end - start
|
||||
res := array{
|
||||
element_size: a.element_size
|
||||
data: data
|
||||
offset: offset
|
||||
len: l
|
||||
cap: l
|
||||
}
|
||||
return res.clone()
|
||||
}
|
||||
|
||||
// we manually inline this for single operations for performance without -prod
|
||||
|
@ -722,7 +701,16 @@ fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr
|
|||
return arr
|
||||
}
|
||||
|
||||
fn (a array) repeat_noscan(count int) array {
|
||||
// repeat returns a new array with the given array elements repeated given times.
|
||||
// `cgen` will replace this with an apropriate call to `repeat_to_depth()`
|
||||
pub fn (a array) repeat_noscan(count int) array {
|
||||
return unsafe { a.repeat_to_depth_noscan(count, 0) }
|
||||
}
|
||||
|
||||
// version of `repeat()` that handles multi dimensional arrays
|
||||
// `unsafe` to call directly because `depth` is not checked
|
||||
[unsafe]
|
||||
fn (a array) repeat_to_depth_noscan(count int, depth int) array {
|
||||
if count < 0 {
|
||||
panic('array.repeat: count is negative: $count')
|
||||
}
|
||||
|
@ -732,16 +720,16 @@ fn (a array) repeat_noscan(count int) array {
|
|||
}
|
||||
arr := array{
|
||||
element_size: a.element_size
|
||||
data: vcalloc_noscan(size)
|
||||
data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
|
||||
len: count * a.len
|
||||
cap: count * a.len
|
||||
}
|
||||
size_of_array := int(sizeof(array))
|
||||
for i in 0 .. count {
|
||||
if a.len > 0 && a.element_size == size_of_array {
|
||||
if a.len > 0 && depth > 0 {
|
||||
ary := array{}
|
||||
unsafe { C.memcpy(&ary, a.data, size_of_array) }
|
||||
ary_clone := ary.clone()
|
||||
ary_clone := unsafe { ary.clone_to_depth_noscan(depth - 1) }
|
||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) }
|
||||
} else {
|
||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) }
|
||||
|
@ -750,67 +738,32 @@ fn (a array) repeat_noscan(count int) array {
|
|||
return arr
|
||||
}
|
||||
|
||||
pub fn (a &array) clone_noscan() array {
|
||||
pub fn (a &array) clone_to_depth_noscan(depth int) array {
|
||||
mut size := a.cap * a.element_size
|
||||
if size == 0 {
|
||||
size++
|
||||
}
|
||||
mut arr := array{
|
||||
element_size: a.element_size
|
||||
data: vcalloc_noscan(size)
|
||||
data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
|
||||
len: a.len
|
||||
cap: a.cap
|
||||
}
|
||||
// Recursively clone-generated elements if array element is array type
|
||||
size_of_array := int(sizeof(array))
|
||||
if a.element_size == size_of_array {
|
||||
mut is_elem_array := true
|
||||
if depth > 0 {
|
||||
for i in 0 .. a.len {
|
||||
ar := array{}
|
||||
unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) }
|
||||
if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 {
|
||||
is_elem_array = false
|
||||
break
|
||||
}
|
||||
ar_clone := ar.clone()
|
||||
unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
|
||||
ar_clone := unsafe { ar.clone_to_depth_noscan(depth - 1) }
|
||||
unsafe { arr.set_unsafe(i, &ar_clone) }
|
||||
}
|
||||
if is_elem_array {
|
||||
return arr
|
||||
return arr
|
||||
} else {
|
||||
if !isnil(a.data) {
|
||||
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
if !isnil(a.data) {
|
||||
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
fn (a &array) slice_clone_noscan(start int, _end int) array {
|
||||
mut end := _end
|
||||
$if !no_bounds_checking ? {
|
||||
if start > end {
|
||||
panic('array.slice: invalid slice index ($start > $end)')
|
||||
}
|
||||
if end > a.len {
|
||||
panic('array.slice: slice bounds out of range ($end >= $a.len)')
|
||||
}
|
||||
if start < 0 {
|
||||
panic('array.slice: slice bounds out of range ($start < 0)')
|
||||
}
|
||||
}
|
||||
mut data := &byte(0)
|
||||
unsafe {
|
||||
data = &byte(a.data) + start * a.element_size
|
||||
}
|
||||
l := end - start
|
||||
res := array{
|
||||
element_size: a.element_size
|
||||
data: data
|
||||
len: l
|
||||
cap: l
|
||||
}
|
||||
return res.clone_noscan()
|
||||
}
|
||||
|
||||
fn (a array) reverse_noscan() array {
|
||||
|
|
|
@ -233,6 +233,21 @@ fn test_repeat() {
|
|||
}
|
||||
}
|
||||
|
||||
fn test_deep_repeat() {
|
||||
mut a3 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]]
|
||||
r := a3.repeat(3)
|
||||
a3[1][1][0] = 17
|
||||
assert r == [
|
||||
[[1, 1], [2, 2], [3, 3]],
|
||||
[[4, 4], [5, 5], [6, 6]],
|
||||
[[1, 1], [2, 2], [3, 3]],
|
||||
[[4, 4], [5, 5], [6, 6]],
|
||||
[[1, 1], [2, 2], [3, 3]],
|
||||
[[4, 4], [5, 5], [6, 6]],
|
||||
]
|
||||
assert a3 == [[[1, 1], [2, 2], [3, 3]], [[4, 4], [17, 5], [6, 6]]]
|
||||
}
|
||||
|
||||
fn test_right() {
|
||||
a := [1, 2, 3, 4]
|
||||
c := a[1..a.len]
|
||||
|
@ -367,7 +382,7 @@ fn test_copy() {
|
|||
assert b[2] == 3
|
||||
}
|
||||
*/
|
||||
fn test_mutli_array_clone() {
|
||||
fn test_multi_array_clone() {
|
||||
// 2d array_int
|
||||
mut a2_1 := [[1, 2, 3], [4, 5, 6]]
|
||||
mut a2_2 := a2_1.clone()
|
||||
|
@ -647,7 +662,7 @@ fn test_anon_fn_map() {
|
|||
assert [1, 2, 3].map(add_num) == [2, 3, 4]
|
||||
}
|
||||
|
||||
fn test_mutli_anon_fn_map() {
|
||||
fn test_multi_anon_fn_map() {
|
||||
a := [1, 2, 3].map(fn (i int) int {
|
||||
return i + 1
|
||||
})
|
||||
|
@ -987,7 +1002,7 @@ fn test_array_with_cap() {
|
|||
assert a5.cap == 10
|
||||
}
|
||||
|
||||
fn test_mutli_array_index() {
|
||||
fn test_multi_array_index() {
|
||||
mut a := [][]int{len: 2, init: []int{len: 3, init: 0}}
|
||||
a[0][0] = 1
|
||||
assert '$a' == '[[1, 0, 0], [0, 0, 0]]'
|
||||
|
|
|
@ -2775,9 +2775,11 @@ fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym ast.TypeSymbol, add_
|
|||
if add_eq {
|
||||
g.write('=')
|
||||
}
|
||||
g.write(' array_clone_static(')
|
||||
g.write(' array_clone_static_to_depth(')
|
||||
g.expr(val)
|
||||
g.write(')')
|
||||
elem_type := (right_sym.info as ast.Array).elem_type
|
||||
array_depth := g.get_array_depth(elem_type)
|
||||
g.write(', $array_depth)')
|
||||
} else if g.is_autofree && right_sym.kind == .string {
|
||||
if add_eq {
|
||||
g.write('=')
|
||||
|
@ -6601,6 +6603,17 @@ fn (mut g Gen) trace(fbase string, message string) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (mut g Gen) get_array_depth(el_typ ast.Type) int {
|
||||
typ := g.unwrap_generic(el_typ)
|
||||
sym := g.table.get_final_type_symbol(typ)
|
||||
if sym.kind == .array {
|
||||
info := sym.info as ast.Array
|
||||
return 1 + g.get_array_depth(info.elem_type)
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if `t` includes any pointer(s) - during garbage collection heap regions
|
||||
// that contain no pointers do not have to be scanned
|
||||
pub fn (mut g Gen) contains_ptr(el_typ ast.Type) bool {
|
||||
|
|
|
@ -649,7 +649,16 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
}
|
||||
}
|
||||
mut name := util.no_dots('${receiver_type_name}_$node.name')
|
||||
if left_sym.kind == .chan {
|
||||
mut array_depth := -1
|
||||
if left_sym.kind == .array {
|
||||
if node.name in ['clone', 'repeat'] {
|
||||
elem_type := (left_sym.info as ast.Array).elem_type
|
||||
array_depth = g.get_array_depth(elem_type)
|
||||
if node.name == 'repeat' {
|
||||
array_depth++
|
||||
}
|
||||
}
|
||||
} else if left_sym.kind == .chan {
|
||||
if node.name in ['close', 'try_pop', 'try_push'] {
|
||||
name = 'sync__Channel_$node.name'
|
||||
}
|
||||
|
@ -702,6 +711,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name == 'str' {
|
||||
g.write('ptr_str(')
|
||||
} else {
|
||||
if array_depth >= 0 {
|
||||
name = name + '_to_depth'
|
||||
}
|
||||
g.write('${name}(')
|
||||
}
|
||||
if node.receiver_type.is_ptr() && (!node.left_type.is_ptr()
|
||||
|
@ -773,6 +785,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
*/
|
||||
// ///////
|
||||
g.call_args(node)
|
||||
if array_depth >= 0 {
|
||||
g.write(', $array_depth')
|
||||
}
|
||||
g.write(')')
|
||||
}
|
||||
|
||||
|
|
|
@ -83,11 +83,14 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F
|
|||
'21.set_unsafe',
|
||||
'21.get_with_check' /* used for `x := a[i] or {}` */,
|
||||
'21.clone_static',
|
||||
'21.clone_static_to_depth',
|
||||
'21.clone_to_depth',
|
||||
'21.first',
|
||||
'21.last',
|
||||
'21.pointers' /* TODO: handle generic methods calling array primitives more precisely in pool_test.v */,
|
||||
'21.reverse',
|
||||
'21.repeat',
|
||||
'21.repeat_to_depth',
|
||||
'21.slice',
|
||||
'21.slice2',
|
||||
'59.get',
|
||||
|
|
Loading…
Reference in New Issue