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.
|
// 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 {
|
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 {
|
if count < 0 {
|
||||||
panic('array.repeat: count is negative: $count')
|
panic('array.repeat: count is negative: $count')
|
||||||
}
|
}
|
||||||
|
@ -121,13 +129,10 @@ pub fn (a array) repeat(count int) array {
|
||||||
len: count * a.len
|
len: count * a.len
|
||||||
cap: count * a.len
|
cap: count * a.len
|
||||||
}
|
}
|
||||||
size_of_array := int(sizeof(array))
|
|
||||||
for i in 0 .. count {
|
for i in 0 .. count {
|
||||||
if a.len > 0 && a.element_size == size_of_array {
|
if a.len > 0 && depth > 0 {
|
||||||
ary := array{}
|
ary_clone := unsafe { a.clone_to_depth(depth - 1) }
|
||||||
unsafe { C.memcpy(&ary, a.data, size_of_array) }
|
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) }
|
||||||
ary_clone := ary.clone()
|
|
||||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) }
|
|
||||||
} else {
|
} else {
|
||||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) }
|
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()
|
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.
|
// 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 {
|
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
|
mut size := a.cap * a.element_size
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
size++
|
size++
|
||||||
|
@ -348,57 +364,20 @@ pub fn (a &array) clone() array {
|
||||||
cap: a.cap
|
cap: a.cap
|
||||||
}
|
}
|
||||||
// Recursively clone-generated elements if array element is array type
|
// Recursively clone-generated elements if array element is array type
|
||||||
size_of_array := int(sizeof(array))
|
if depth > 0 {
|
||||||
if a.element_size == size_of_array {
|
|
||||||
mut is_elem_array := true
|
|
||||||
for i in 0 .. a.len {
|
for i in 0 .. a.len {
|
||||||
ar := array{}
|
ar := array{}
|
||||||
unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) }
|
unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
|
||||||
if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 {
|
ar_clone := unsafe { ar.clone_to_depth(depth - 1) }
|
||||||
is_elem_array = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ar_clone := ar.clone()
|
|
||||||
unsafe { arr.set_unsafe(i, &ar_clone) }
|
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
|
// 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
|
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 {
|
if count < 0 {
|
||||||
panic('array.repeat: count is negative: $count')
|
panic('array.repeat: count is negative: $count')
|
||||||
}
|
}
|
||||||
|
@ -732,16 +720,16 @@ fn (a array) repeat_noscan(count int) array {
|
||||||
}
|
}
|
||||||
arr := array{
|
arr := array{
|
||||||
element_size: a.element_size
|
element_size: a.element_size
|
||||||
data: vcalloc_noscan(size)
|
data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
|
||||||
len: count * a.len
|
len: count * a.len
|
||||||
cap: count * a.len
|
cap: count * a.len
|
||||||
}
|
}
|
||||||
size_of_array := int(sizeof(array))
|
size_of_array := int(sizeof(array))
|
||||||
for i in 0 .. count {
|
for i in 0 .. count {
|
||||||
if a.len > 0 && a.element_size == size_of_array {
|
if a.len > 0 && depth > 0 {
|
||||||
ary := array{}
|
ary := array{}
|
||||||
unsafe { C.memcpy(&ary, a.data, size_of_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) }
|
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) }
|
||||||
} else {
|
} else {
|
||||||
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) }
|
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
|
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
|
mut size := a.cap * a.element_size
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
size++
|
size++
|
||||||
}
|
}
|
||||||
mut arr := array{
|
mut arr := array{
|
||||||
element_size: a.element_size
|
element_size: a.element_size
|
||||||
data: vcalloc_noscan(size)
|
data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
|
||||||
len: a.len
|
len: a.len
|
||||||
cap: a.cap
|
cap: a.cap
|
||||||
}
|
}
|
||||||
// Recursively clone-generated elements if array element is array type
|
// Recursively clone-generated elements if array element is array type
|
||||||
size_of_array := int(sizeof(array))
|
if depth > 0 {
|
||||||
if a.element_size == size_of_array {
|
|
||||||
mut is_elem_array := true
|
|
||||||
for i in 0 .. a.len {
|
for i in 0 .. a.len {
|
||||||
ar := array{}
|
ar := array{}
|
||||||
unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) }
|
unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) }
|
||||||
if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 {
|
ar_clone := unsafe { ar.clone_to_depth_noscan(depth - 1) }
|
||||||
is_elem_array = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ar_clone := ar.clone()
|
|
||||||
unsafe { arr.set_unsafe(i, &ar_clone) }
|
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 {
|
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() {
|
fn test_right() {
|
||||||
a := [1, 2, 3, 4]
|
a := [1, 2, 3, 4]
|
||||||
c := a[1..a.len]
|
c := a[1..a.len]
|
||||||
|
@ -367,7 +382,7 @@ fn test_copy() {
|
||||||
assert b[2] == 3
|
assert b[2] == 3
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
fn test_mutli_array_clone() {
|
fn test_multi_array_clone() {
|
||||||
// 2d array_int
|
// 2d array_int
|
||||||
mut a2_1 := [[1, 2, 3], [4, 5, 6]]
|
mut a2_1 := [[1, 2, 3], [4, 5, 6]]
|
||||||
mut a2_2 := a2_1.clone()
|
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]
|
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 {
|
a := [1, 2, 3].map(fn (i int) int {
|
||||||
return i + 1
|
return i + 1
|
||||||
})
|
})
|
||||||
|
@ -987,7 +1002,7 @@ fn test_array_with_cap() {
|
||||||
assert a5.cap == 10
|
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}}
|
mut a := [][]int{len: 2, init: []int{len: 3, init: 0}}
|
||||||
a[0][0] = 1
|
a[0][0] = 1
|
||||||
assert '$a' == '[[1, 0, 0], [0, 0, 0]]'
|
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 {
|
if add_eq {
|
||||||
g.write('=')
|
g.write('=')
|
||||||
}
|
}
|
||||||
g.write(' array_clone_static(')
|
g.write(' array_clone_static_to_depth(')
|
||||||
g.expr(val)
|
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 {
|
} else if g.is_autofree && right_sym.kind == .string {
|
||||||
if add_eq {
|
if add_eq {
|
||||||
g.write('=')
|
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
|
// returns true if `t` includes any pointer(s) - during garbage collection heap regions
|
||||||
// that contain no pointers do not have to be scanned
|
// that contain no pointers do not have to be scanned
|
||||||
pub fn (mut g Gen) contains_ptr(el_typ ast.Type) bool {
|
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')
|
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'] {
|
if node.name in ['close', 'try_pop', 'try_push'] {
|
||||||
name = 'sync__Channel_$node.name'
|
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' {
|
if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name == 'str' {
|
||||||
g.write('ptr_str(')
|
g.write('ptr_str(')
|
||||||
} else {
|
} else {
|
||||||
|
if array_depth >= 0 {
|
||||||
|
name = name + '_to_depth'
|
||||||
|
}
|
||||||
g.write('${name}(')
|
g.write('${name}(')
|
||||||
}
|
}
|
||||||
if node.receiver_type.is_ptr() && (!node.left_type.is_ptr()
|
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)
|
g.call_args(node)
|
||||||
|
if array_depth >= 0 {
|
||||||
|
g.write(', $array_depth')
|
||||||
|
}
|
||||||
g.write(')')
|
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.set_unsafe',
|
||||||
'21.get_with_check' /* used for `x := a[i] or {}` */,
|
'21.get_with_check' /* used for `x := a[i] or {}` */,
|
||||||
'21.clone_static',
|
'21.clone_static',
|
||||||
|
'21.clone_static_to_depth',
|
||||||
|
'21.clone_to_depth',
|
||||||
'21.first',
|
'21.first',
|
||||||
'21.last',
|
'21.last',
|
||||||
'21.pointers' /* TODO: handle generic methods calling array primitives more precisely in pool_test.v */,
|
'21.pointers' /* TODO: handle generic methods calling array primitives more precisely in pool_test.v */,
|
||||||
'21.reverse',
|
'21.reverse',
|
||||||
'21.repeat',
|
'21.repeat',
|
||||||
|
'21.repeat_to_depth',
|
||||||
'21.slice',
|
'21.slice',
|
||||||
'21.slice2',
|
'21.slice2',
|
||||||
'59.get',
|
'59.get',
|
||||||
|
|
Loading…
Reference in New Issue