-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathtest_pointer_syntax.ml
More file actions
508 lines (451 loc) · 15.7 KB
/
Copy pathtest_pointer_syntax.ml
File metadata and controls
508 lines (451 loc) · 15.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
(*
* Copyright 2025 Multikernel Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*)
open Kernelscript.Ast
open Kernelscript.Parse
open Kernelscript.Type_checker
open Kernelscript.Symbol_table
open Kernelscript.Ir_generator
open Kernelscript.Ir
open Kernelscript.Ebpf_c_codegen
open Alcotest
open Printf
(** Helper functions *)
let make_pos line column = { line; column; filename = "test" }
let test_pos = make_pos 1 1
let check_parse_success program_text test_name =
try
let ast = parse_string program_text in
check bool test_name true (List.length ast > 0)
with
| Parse_error (msg, _) -> fail (test_name ^ " - Parse error: " ^ msg)
| exn -> fail (test_name ^ " - Unexpected error: " ^ Printexc.to_string exn)
let check_parse_failure program_text test_name expected_error =
try
let _ast = parse_string program_text in
fail (test_name ^ " - Expected parse failure but parsing succeeded")
with
| Parse_error (msg, _) ->
check bool (test_name ^ " - Contains expected error") true (String.contains msg (String.get expected_error 0))
| exn -> fail (test_name ^ " - Unexpected error type: " ^ Printexc.to_string exn)
let type_check_program program_text =
let ast = parse_string program_text in
let symbol_table = build_symbol_table ast in
let (annotated_ast, _typed_programs) = type_check_and_annotate_ast ast in
(ast, symbol_table, annotated_ast)
let check_type_success program_text test_name =
try
let (_ast, _st, annotated) = type_check_program program_text in
check bool test_name true (List.length annotated > 0)
with
| Type_error (msg, _) -> fail (test_name ^ " - Type error: " ^ msg)
| exn -> fail (test_name ^ " - Unexpected error: " ^ Printexc.to_string exn)
let check_type_failure program_text test_name expected_error =
try
let _result = type_check_program program_text in
fail (test_name ^ " - Expected type error but type checking succeeded")
with
| Type_error (msg, _) ->
check bool (test_name ^ " - Contains expected error") true (String.contains msg (String.get expected_error 0))
| exn -> fail (test_name ^ " - Unexpected error type: " ^ Printexc.to_string exn)
let generate_ir_from_program program_text entry_point =
let (_ast, symbol_table, annotated_ast) = type_check_program program_text in
generate_ir annotated_ast symbol_table entry_point
let contains_substr s sub =
let len_s = String.length s in
let len_sub = String.length sub in
let rec loop i =
if i > len_s - len_sub then false
else if String.sub s i len_sub = sub then true
else loop (i + 1)
in
loop 0
(** Test pointer type parsing *)
let test_pointer_type_parsing () =
let basic_pointer = "*u32" in
check_parse_success (sprintf "fn test(p: %s) -> u32 { return 0 }" basic_pointer)
"Basic pointer type parsing";
let nested_pointer = "**u32" in
check_parse_success (sprintf "fn test(p: %s) -> u32 { return 0 }" nested_pointer)
"Nested pointer type parsing";
let struct_pointer = "*Point" in
check_parse_success (sprintf "struct Point { x: u32 } fn test(p: %s) -> u32 { return 0 }" struct_pointer)
"Struct pointer type parsing";
let array_pointer = "*u32[10]" in
check_parse_success (sprintf "fn test(p: %s) -> u32 { return 0 }" array_pointer)
"Array pointer type parsing"
(** Test address-of operator parsing *)
let test_address_of_parsing () =
let simple_address_of = {|
fn test() -> u32 {
var x = 42
var ptr = &x
return 0
}
|} in
check_parse_success simple_address_of "Simple address-of parsing";
let field_address_of = {|
struct Point { x: u32, y: u32 }
fn test() -> u32 {
var p = Point { x: 10, y: 20 }
var ptr = &p.x
return 0
}
|} in
check_parse_success field_address_of "Field address-of parsing";
let array_address_of = {|
fn test() -> u32 {
var arr = [1, 2, 3, 4, 5]
var ptr = &arr[0]
return 0
}
|} in
check_parse_success array_address_of "Array element address-of parsing"
(** Test dereference operator parsing *)
let test_dereference_parsing () =
let simple_deref = {|
fn test(ptr: *u32) -> u32 {
return *ptr
}
|} in
check_parse_success simple_deref "Simple dereference parsing";
let nested_deref = {|
fn test(ptr: **u32) -> u32 {
return **ptr
}
|} in
check_parse_success nested_deref "Nested dereference parsing";
(* Dereference assignment (star-ptr = value) is not yet implemented in KernelScript *)
(* For now, just test that we can dereference in expressions *)
let deref_in_expr = {|
fn test(ptr: *u32, other: *u32) -> u32 {
var value = *ptr + *other
return value
}
|} in
check_parse_success deref_in_expr "Dereference in expressions parsing"
(** Test arrow access parsing *)
let test_arrow_access_parsing () =
let simple_arrow = {|
struct Point { x: u32, y: u32 }
fn test(p: *Point) -> u32 {
return p->x
}
|} in
check_parse_success simple_arrow "Simple arrow access parsing";
let chained_arrow = {|
struct Point { x: u32, y: u32 }
struct Line { start: *Point, end: *Point }
fn test(line: *Line) -> u32 {
return line->start->x
}
|} in
check_parse_success chained_arrow "Chained arrow access parsing";
let arrow_assignment = {|
struct Point { x: u32, y: u32 }
fn test(p: *Point) -> u32 {
p->x = 42
p->y = 24
return 0
}
|} in
check_parse_success arrow_assignment "Arrow assignment parsing"
(** Test complex pointer expressions *)
let test_complex_pointer_expressions () =
let complex_expr = {|
struct Point { x: u32, y: u32 }
fn test(p: *Point, q: *Point) -> u32 {
var sum = p->x + q->y
var addr = &sum
var updated_sum = *addr + 10
return updated_sum
}
|} in
check_parse_success complex_expr "Complex pointer expressions";
let conditional_pointer = {|
struct Point { x: u32, y: u32 }
fn test(p: *Point, condition: bool) -> u32 {
if (condition) {
p->x = 100
} else {
p->y = 200
}
return p->x + p->y
}
|} in
check_parse_success conditional_pointer "Conditional pointer operations"
(** Test pointer type checking *)
let test_pointer_type_checking () =
let valid_pointer_usage = {|
struct Point { x: u32, y: u32 }
fn update_point(p: *Point) -> u32 {
p->x = 10
p->y = 20
return p->x + p->y
}
fn main() -> i32 { return 0 }
|} in
check_type_success valid_pointer_usage "Valid pointer usage type checking";
let address_of_type_check = {|
fn test() -> u32 {
var x: u32 = 42
var ptr: *u32 = &x
return *ptr
}
fn main() -> i32 { return 0 }
|} in
check_type_success address_of_type_check "Address-of type checking";
let dereference_type_check = {|
fn test(ptr: *u32) -> u32 {
var value: u32 = *ptr
return value
}
fn main() -> i32 { return 0 }
|} in
check_type_success dereference_type_check "Dereference type checking"
(** Test pointer type errors *)
let test_pointer_type_errors () =
let invalid_dereference = {|
fn test() -> u32 {
var x: u32 = 42
return *x
}
fn main() -> i32 { return 0 }
|} in
check_type_failure invalid_dereference "Invalid dereference error" "Dereference requires pointer type";
let arrow_on_non_pointer = {|
struct Point { x: u32, y: u32 }
fn test() -> u32 {
var p = Point { x: 10, y: 20 }
return p->x
}
fn main() -> i32 { return 0 }
|} in
check_type_failure arrow_on_non_pointer "Arrow on non-pointer error" "Arrow access requires pointer";
(* Test a more obvious type error - trying to use string as pointer *)
let obvious_type_error = {|
fn test() -> u32 {
var s: str(10) = "hello"
return s->length
}
fn main() -> i32 { return 0 }
|} in
check_type_failure obvious_type_error "Obviously invalid pointer usage" "Arrow access requires pointer"
(** Test pointer field access *)
let test_pointer_field_access () =
let valid_field_access = {|
struct Point { x: u32, y: u32 }
struct Rectangle { top_left: Point, bottom_right: Point }
fn test(rect: *Rectangle) -> u32 {
return rect->top_left.x + rect->bottom_right.y
}
fn main() -> i32 { return 0 }
|} in
check_type_success valid_field_access "Valid pointer field access";
let mixed_access_patterns = {|
struct Point { x: u32, y: u32 }
struct Line { start: *Point, end: Point }
fn test(line: *Line) -> u32 {
var start_x = line->start->x
var end_y = line->end.y
return start_x + end_y
}
fn main() -> i32 { return 0 }
|} in
check_type_success mixed_access_patterns "Mixed pointer and direct field access"
(** Test pointer IR generation *)
let test_pointer_ir_generation () =
let simple_pointer_program = {|
struct Point { x: u32, y: u32 }
@helper
fn update_point(p: *Point) -> u32 {
p->x = 10
return p->x
}
@xdp
fn test_prog(ctx: *xdp_md) -> xdp_action {
return 2
}
|} in
try
let ir = generate_ir_from_program simple_pointer_program "update_point" in
(* Check that IR generation succeeds for pointer operations *)
(* Check that the IR has some basic structure - at least one program and one basic block *)
let programs = get_programs ir in
let has_programs = List.length programs > 0 in
let has_instructions = has_programs &&
List.exists (fun prog ->
List.length prog.entry_function.basic_blocks > 0
) programs in
check bool "IR contains programs and instructions" true has_instructions
with
| exn -> fail ("IR generation failed: " ^ Printexc.to_string exn)
(** Test pointer C code generation *)
let test_pointer_c_generation () =
let pointer_program = {|
struct Point { x: u32, y: u32 }
@helper
fn update_point(p: *Point) -> u32 {
p->x = 10
p->y = 20
return p->x + p->y
}
@xdp
fn test_prog(ctx: *xdp_md) -> xdp_action {
return 2
}
|} in
try
let ir = generate_ir_from_program pointer_program "update_point" in
let c_code = generate_c_multi_program ir in
(* Check that generated C code contains proper pointer syntax *)
check bool "C code contains arrow operator" true (contains_substr c_code "->");
check bool "C code contains struct Point" true (contains_substr c_code "struct Point");
check bool "C code contains pointer parameter" true (contains_substr c_code "struct Point*");
with
| exn -> fail ("C code generation failed: " ^ Printexc.to_string exn)
(** Test address-of and dereference IR/codegen *)
let test_address_of_dereference_codegen () =
let address_deref_program = {|
@helper
fn test_address_deref() -> u32 {
var x: u32 = 42
var ptr: *u32 = &x
var value: u32 = *ptr
return value
}
@xdp
fn test_prog(ctx: *xdp_md) -> xdp_action {
return 2
}
|} in
try
let ir = generate_ir_from_program address_deref_program "test_address_deref" in
let c_code = generate_c_multi_program ir in
(* The exact C code generation depends on implementation,
but should handle address-of and dereference safely *)
check bool "C code generation succeeds" true (String.length c_code > 0)
with
| exn -> fail ("Address-of/dereference codegen failed: " ^ Printexc.to_string exn)
(** Test userspace pointer code generation *)
let test_userspace_pointer_generation () =
(* Userspace pointer generation is complex and involves file I/O.
For now, just test that the syntax is valid and parseable. *)
let userspace_pointer_program = {|
struct Config { threshold: u32, enabled: bool }
fn process_config(cfg: *Config) -> i32 {
if (cfg->enabled) {
return cfg->threshold
}
return 0
}
fn main() -> i32 {
return 0
}
|} in
try
let (_ast, _symbol_table, annotated_ast) = type_check_program userspace_pointer_program in
check bool "Userspace pointer syntax is valid" true (List.length annotated_ast > 0)
with
| exn -> fail ("Userspace pointer syntax validation failed: " ^ Printexc.to_string exn)
(** Test pointer safety and bounds checking *)
let test_pointer_safety () =
let safety_program = {|
struct Point { x: u32, y: u32 }
@helper
fn safe_access(p: *Point) -> u32 {
if (p == null) {
return 0
}
return p->x + p->y
}
@xdp
fn test_prog(ctx: *xdp_md) -> xdp_action {
return 2
}
|} in
try
let ir = generate_ir_from_program safety_program "safe_access" in
let c_code = generate_c_multi_program ir in
(* Should generate safe pointer access code *)
check bool "Pointer safety codegen succeeds" true (String.length c_code > 0);
with
| exn -> fail ("Pointer safety codegen failed: " ^ Printexc.to_string exn)
(** Test complex nested pointer structures *)
let test_nested_pointer_structures () =
(* Avoid self-referential structs which can cause infinite recursion *)
let nested_program = {|
struct Point { x: u32, y: u32 }
struct Rectangle { top_left: *Point, bottom_right: *Point }
@helper
fn process_rectangle(rect: *Rectangle) -> u32 {
var width = rect->bottom_right->x - rect->top_left->x
var height = rect->bottom_right->y - rect->top_left->y
return width + height
}
@xdp
fn test_prog(ctx: *xdp_md) -> xdp_action {
return 2
}
|} in
try
let ir = generate_ir_from_program nested_program "process_rectangle" in
let c_code = generate_c_multi_program ir in
check bool "Nested structures C code generation" true (String.length c_code > 0);
with
| exn -> fail ("Nested pointer structures failed: " ^ Printexc.to_string exn)
(** Test pointer arithmetic edge cases *)
let test_pointer_edge_cases () =
(* Test null pointer handling - just test parsing for now *)
let null_pointer = {|
struct Point { x: u32, y: u32 }
fn test(p: *Point) -> u32 {
if (p != null) {
return p->x
}
return 0
}
fn main() -> i32 { return 0 }
|} in
check_type_success null_pointer "Null pointer handling";
(* Test pointer comparison *)
let pointer_comparison = {|
fn test(p1: *u32, p2: *u32) -> bool {
return p1 == p2
}
fn main() -> i32 { return 0 }
|} in
check_type_success pointer_comparison "Pointer comparison"
(** Test runner *)
let tests = [
"pointer type parsing", `Quick, test_pointer_type_parsing;
"address-of operator parsing", `Quick, test_address_of_parsing;
"dereference operator parsing", `Quick, test_dereference_parsing;
"arrow access parsing", `Quick, test_arrow_access_parsing;
"complex pointer expressions", `Quick, test_complex_pointer_expressions;
"pointer type checking", `Quick, test_pointer_type_checking;
"pointer type errors", `Quick, test_pointer_type_errors;
"pointer field access", `Quick, test_pointer_field_access;
"pointer IR generation", `Quick, test_pointer_ir_generation;
"pointer C code generation", `Quick, test_pointer_c_generation;
"address-of/dereference codegen", `Quick, test_address_of_dereference_codegen;
"userspace pointer generation", `Quick, test_userspace_pointer_generation;
"pointer safety", `Quick, test_pointer_safety;
"nested pointer structures", `Quick, test_nested_pointer_structures;
"pointer edge cases", `Quick, test_pointer_edge_cases;
]
let () = Alcotest.run "Pointer Syntax and Operations Tests" [
"pointer_tests", tests;
]