all: bare metal support (fix `-freestanding`) (#9624)

pull/9730/head
crthpl 2021-04-13 22:50:50 -07:00 committed by GitHub
parent 711e309eef
commit 14434cc86a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1316 additions and 482 deletions

View File

@ -80,7 +80,7 @@ fn main() {
verror(err.msg)
return
}
if is_atty(0) == 0 && files.len == 0 {
if os.is_atty(0) == 0 && files.len == 0 {
foptions.format_pipe()
exit(0)
}

View File

@ -26,7 +26,7 @@ mut:
vstartup_lines []string // lines in the `VSTARTUP` file
}
const is_stdin_a_pipe = (is_atty(0) == 0)
const is_stdin_a_pipe = (os.is_atty(0) == 0)
const vexe = os.getenv('VEXE')

View File

@ -35,6 +35,7 @@ const (
'vlib/v/tests/generics_test.v', /* multi_generic_args<Foo<int>, Foo<int> >(...) becomes .... Foo<int>>(...) which does not parse */
'vlib/v/tests/string_interpolation_test.v' /* TODO byteptr: &byte.str() behaves differently than byteptr.str() */,
'vlib/v/gen/js/tests/js.v', /* local `hello` fn, gets replaced with module `hello` aliased as `hl` */
'vlib/v/gen/c/cheaders.v' /* the preprocessor directives are formated to the V standard, even though they are in a string literal */,
]
vfmt_verify_list = [
'cmd/',

View File

@ -15,12 +15,12 @@ const (
'vlib/builtin/js/jsfns_node.js.v',
'vlib/builtin/js/jsfns.js.v',
'vlib/builtin/js/jsfns_browser.js.v',
// error: expr(): bad token `asm`, on `asm {}`
'vlib/builtin/bare/linuxsys_bare.v',
// total chaos (duplicated code several times) in array_eq_test.v
'vlib/builtin/array_eq_test.v',
// the fn args are removed, then `cb fn (picohttpparser.Request, mut picohttpparser.Response)` can not be reparsed
'vlib/picoev/picoev.v',
// the preprocessor directives are formated to the V standard, even though they are in a string literal
'vlib/v/gen/c/cheaders.v',
]
)

View File

@ -24,6 +24,47 @@ see also `v help build`.
Build the executable without dependency on libc.
Supported only on `linux` targets currently.
-bare-builtin-dir <bare-builtin-dir>
Use with `-freestanding`. This specifies the directory to the
implementation of some basic builtin functions. The list is as follows:
memcpy(dest &byte, src &byte, n int) &byte
Moves n bytes from dest to src, and returns dest
[export: 'malloc']
__malloc(n int) &byte
Allocates n bytes of memory and returns the pointer to the first byte
strlen(s &byte) int
Returns the amount of bytes until the first `0`, starting at s
realloc(old_area &byte, new_size int) &byte
Allocates a new area of size new_size, copies old_area
to the new area, and returns a pointer to the new area.
memset(s &byte, c int, n int) &byte
Sets n bytes starting at s to c (c is casted to a char)
and returns s.
memmove(dest &byte, src &byte, n int) &byte
Like memcpy, but guaranteed to work if dest and src overlap.
[export: 'calloc']
__calloc(nmemb int, size int) &byte
Like malloc, but sets all the bytes to `0` first.
getchar() int
Read one character from stdin and return it.
memcmp(a &byte, b &byte, n int) int
Compare two buffers of length n. If a and b are equal, return 0.
Otherwise, return the difference between the first different letter.
[export: 'free']
__free(ptr &byte)
Free the block of memory ptr allocated by malloc.
vsprintf(str &char, format &char, ap va_list) int
See `man vsprintf`.
vsnprintf(str &char, size int, format &char, ap va_list) int
See `man vsnprintf`.
bare_print(buf &byte, len u64)
Print len charecters from the buffer pointed to by buf to stdout.
bare_eprint(buf &byte, len u64)
Print len charecters from the buffer pointed to by buf to stderr.
The module decleration should be `builtin`. The default linux
implementation can be found in `vlib/builtin/linux_bare`.
-os <os>, -target-os <os>
Change the target OS that V tries to compile for.
By default, the target OS is the host system.

View File

@ -58,7 +58,7 @@ fn main() {
if args.len == 0 || args[0] in ['-', 'repl'] {
// Running `./v` without args launches repl
if args.len == 0 {
if is_atty(0) != 0 {
if os.is_atty(0) != 0 {
println('Welcome to the V REPL (for help with V itself, type `exit`, then run `v help`).')
} else {
mut args_and_flags := util.join_env_vflags_and_os_args()[1..].clone()

View File

@ -1,7 +1,7 @@
// Binary Search Tree example by @SleepyRoy
// TODO: make Node.value generic once it's robust enough
// TODO: `return match` instead of returns everywhere inside match
// TODO: `return match` instead of returns everywhere inside match
struct Empty {}
@ -121,22 +121,46 @@ fn delete(tree Tree, x f64) Tree {
}
fn main() {
mut tree := Tree(Empty{})
input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7]
for i in input {
tree = insert(tree, i)
}
println('[1] after insertion tree size is ${size(tree)}') // 11
del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5]
for i in del {
tree = delete(tree, i)
}
print('[2] after deletion tree size is ${size(tree)}, ') // 7
print('and these elements were deleted: ') // 0.0 0.3 0.6 1.0
for i in input {
if !search(tree, i) {
print('$i ')
$if !freestanding {
mut tree := Tree(Empty{})
input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7]
for i in input {
tree = insert(tree, i)
}
println('[1] after insertion tree size is ${size(tree)}') // 11
del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5]
for i in del {
tree = delete(tree, i)
}
print('[2] after deletion tree size is ${size(tree)}, ') // 7
print('and these elements were deleted: ') // 0.0 0.3 0.6 1.0
for i in input {
if !search(tree, i) {
print('$i ')
}
}
println('')
} $else {
mut tree := Tree(Empty{})
input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7]
for i in input {
tree = insert(tree, i)
}
print('[1] after insertion tree size is ') // 11
println(size(tree))
del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5]
for i in del {
tree = delete(tree, i)
}
print('[2] after deletion tree size is ') // 7
print(size(tree))
print(', and these elements were deleted: ') // 0.0 0.3 0.6 1.0
for i in input {
if !search(tree, i) {
print(i)
print(' ')
}
}
println('')
}
println('')
}

View File

@ -136,7 +136,11 @@ pub fn (a array) repeat(count int) array {
// sort_with_compare sorts array in-place using given `compare` function as comparator.
pub fn (mut a array) sort_with_compare(compare voidptr) {
C.qsort(mut a.data, a.len, a.element_size, compare)
$if freestanding {
panic('sort does not work with -freestanding')
} $else {
C.qsort(mut a.data, a.len, a.element_size, compare)
}
}
// insert inserts a value in the array at index `i`

View File

@ -1,5 +0,0 @@
module builtin
struct string {
len int
}

View File

@ -18,12 +18,17 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
// module is less likely to change than function, etc...
// During edits, the line number will change most frequently,
// so it is last
eprintln('================ V panic ================')
eprintln(' module: $mod')
eprintln(' function: ${fn_name}()')
eprintln(' message: $s')
eprintln(' file: $file:$line_no')
eprintln('=========================================')
$if !freestanding {
eprintln('================ V panic ================')
eprintln(' module: $mod')
eprintln(' function: ${fn_name}()')
eprintln(' message: $s')
eprintln(' file: $file:$line_no')
eprintln('=========================================')
} $else {
eprint('V panic: ')
eprintln(s)
}
$if exit_after_panic_message ? {
C.exit(1)
} $else {
@ -38,7 +43,9 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) {
}
C.exit(1)
}
print_backtrace_skipping_top_frames(1)
$if !freestanding {
print_backtrace_skipping_top_frames(1)
}
$if panics_break_into_debugger ? {
break_if_debugger_attached()
}
@ -54,7 +61,8 @@ pub fn panic_optional_not_set(s string) {
// panic prints a nice error message, then exits the process with exit code of 1.
// It also shows a backtrace on most platforms.
pub fn panic(s string) {
eprintln('V panic: $s')
eprint('V panic: ')
eprintln(s)
$if exit_after_panic_message ? {
C.exit(1)
} $else {
@ -69,7 +77,9 @@ pub fn panic(s string) {
}
C.exit(1)
}
print_backtrace_skipping_top_frames(1)
$if !freestanding {
print_backtrace_skipping_top_frames(1)
}
$if panics_break_into_debugger ? {
break_if_debugger_attached()
}
@ -80,14 +90,13 @@ pub fn panic(s string) {
// eprintln prints a message with a line end, to stderr. Both stderr and stdout are flushed.
pub fn eprintln(s string) {
C.fflush(C.stdout)
C.fflush(C.stderr)
// eprintln is used in panics, so it should not fail at all
$if android {
$if freestanding {
// flushing is only a thing with C.FILE from stdio.h, not on the syscall level
if s.str == 0 {
C.fprintf(C.stderr, c'eprintln(NIL)\n')
bare_eprint(c'eprintln(NIL)\n', 14)
} else {
C.fprintf(C.stderr, c'%.*s\n', s.len, s.str)
bare_eprint(s.str, u64(s.len))
bare_eprint(c'\n', 1)
}
} $else $if ios {
if s.str == 0 {
@ -96,25 +105,35 @@ pub fn eprintln(s string) {
C.WrappedNSLog(s.str)
}
} $else {
if s.str == 0 {
C.write(2, c'eprintln(NIL)\n', 14)
} else {
C.write(2, s.str, s.len)
C.write(2, c'\n', 1)
C.fflush(C.stdout)
C.fflush(C.stderr)
// eprintln is used in panics, so it should not fail at all
$if android {
if s.str == 0 {
C.fprintf(C.stderr, c'eprintln(NIL)\n')
} else {
C.fprintf(C.stderr, c'%.*s\n', s.len, s.str)
}
} $else {
if s.str == 0 {
C.write(2, c'eprintln(NIL)\n', 14)
} else {
C.write(2, s.str, s.len)
C.write(2, c'\n', 1)
}
}
C.fflush(C.stderr)
}
C.fflush(C.stderr)
}
// eprint prints a message to stderr. Both stderr and stdout are flushed.
pub fn eprint(s string) {
C.fflush(C.stdout)
C.fflush(C.stderr)
$if android {
$if freestanding {
// flushing is only a thing with C.FILE from stdio.h, not on the syscall level
if s.str == 0 {
C.fprintf(C.stderr, c'eprint(NIL)')
bare_eprint(c'eprint(NIL)\n', 12)
} else {
C.fprintf(C.stderr, c'%.*s', s.len, s.str)
bare_eprint(s.str, u64(s.len))
}
} $else $if ios {
// TODO: Implement a buffer as NSLog doesn't have a "print"
@ -124,13 +143,23 @@ pub fn eprint(s string) {
C.WrappedNSLog(s.str)
}
} $else {
if s.str == 0 {
C.write(2, c'eprint(NIL)', 11)
} else {
C.write(2, s.str, s.len)
C.fflush(C.stdout)
C.fflush(C.stderr)
$if android {
if s.str == 0 {
C.fprintf(C.stderr, c'eprint(NIL)')
} else {
C.fprintf(C.stderr, c'%.*s', s.len, s.str)
}
} $else {
if s.str == 0 {
C.write(2, c'eprint(NIL)', 11)
} else {
C.write(2, s.str, s.len)
}
}
C.fflush(C.stderr)
}
C.fflush(C.stderr)
}
// print prints a message to stdout. Unlike `println` stdout is not automatically flushed.
@ -141,6 +170,8 @@ pub fn print(s string) {
} $else $if ios {
// TODO: Implement a buffer as NSLog doesn't have a "print"
C.WrappedNSLog(s.str)
} $else $if freestanding {
bare_print(s.str, u64(s.len))
} $else {
C.write(1, s.str, s.len)
}
@ -160,6 +191,9 @@ pub fn println(s string) {
C.fprintf(C.stdout, c'println(NIL)\n')
} $else $if ios {
C.WrappedNSLog(c'println(NIL)')
} $else $if freestanding {
bare_print(s.str, u64(s.len))
bare_print(c'println(NIL)\n', 13)
} $else {
C.write(1, c'println(NIL)\n', 13)
}
@ -169,6 +203,9 @@ pub fn println(s string) {
C.fprintf(C.stdout, c'%.*s\n', s.len, s.str)
} $else $if ios {
C.WrappedNSLog(s.str)
} $else $if freestanding {
bare_print(s.str, u64(s.len))
bare_print(c'\n', 1)
} $else {
C.write(1, s.str, s.len)
C.write(1, c'\n', 1)
@ -205,6 +242,14 @@ pub fn malloc(n int) &byte {
unsafe {
res = C.GC_MALLOC(n)
}
} $else $if freestanding {
mut e := Errno{}
res, e = mm_alloc(u64(n))
if e != .enoerror {
eprint('malloc() failed: ')
eprintln(e.str())
panic('malloc() failed')
}
} $else {
res = unsafe { C.malloc(n) }
}
@ -365,18 +410,6 @@ pub fn memdup(src voidptr, sz int) voidptr {
}
}
// is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal
pub fn is_atty(fd int) int {
$if windows {
mut mode := u32(0)
osfh := voidptr(C._get_osfhandle(fd))
C.GetConsoleMode(osfh, voidptr(&mode))
return int(mode)
} $else {
return C.isatty(fd)
}
}
[inline]
fn v_fixed_index(i int, len int) int {
$if !no_bounds_checking ? {

View File

@ -13,6 +13,12 @@ pub fn isnil(v voidptr) bool {
return v == 0
}
[deprecated: 'use os.is_atty(x) instead']
pub fn is_atty(fd int) int {
panic('use os.is_atty(x) instead')
return 0
}
/*
fn on_panic(f fn(int)int) {
// TODO
@ -26,7 +32,9 @@ pub fn print_backtrace() {
// 1 frame for print_backtrace itself
// ... print the rest of the backtrace frames ...
// => top 2 frames should be skipped, since they will not be informative to the developer
print_backtrace_skipping_top_frames(2)
$if !freestanding {
print_backtrace_skipping_top_frames(2)
}
}
struct VCastTypeIndexName {

View File

@ -85,55 +85,57 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool {
$if no_backtrace ? {
return false
} $else {
$if tinyc {
C.tcc_backtrace(c'Backtrace')
return false
}
buffer := [100]voidptr{}
nr_ptrs := C.backtrace(&buffer[0], 100)
if nr_ptrs < 2 {
eprintln('C.backtrace returned less than 2 frames')
return false
}
nr_actual_frames := nr_ptrs - skipframes
mut sframes := []string{}
//////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames)
csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames)
for i in 0 .. nr_actual_frames {
sframes << unsafe { tos2(&byte(csymbols[i])) }
}
for sframe in sframes {
executable := sframe.all_before('(')
addr := sframe.all_after('[').all_before(']')
beforeaddr := sframe.all_before('[')
cmd := 'addr2line -e $executable $addr'
// taken from os, to avoid depending on the os module inside builtin.v
f := C.popen(&char(cmd.str), c'r')
if isnil(f) {
eprintln(sframe)
continue
$if !freestanding {
$if tinyc {
C.tcc_backtrace(c'Backtrace')
return false
}
buf := [1000]byte{}
mut output := ''
unsafe {
bp := &buf[0]
for C.fgets(&char(bp), 1000, f) != 0 {
output += tos(bp, vstrlen(bp))
buffer := [100]voidptr{}
nr_ptrs := C.backtrace(&buffer[0], 100)
if nr_ptrs < 2 {
eprintln('C.backtrace returned less than 2 frames')
return false
}
nr_actual_frames := nr_ptrs - skipframes
mut sframes := []string{}
//////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames)
csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames)
for i in 0 .. nr_actual_frames {
sframes << unsafe { tos2(&byte(csymbols[i])) }
}
for sframe in sframes {
executable := sframe.all_before('(')
addr := sframe.all_after('[').all_before(']')
beforeaddr := sframe.all_before('[')
cmd := 'addr2line -e $executable $addr'
// taken from os, to avoid depending on the os module inside builtin.v
f := C.popen(&char(cmd.str), c'r')
if isnil(f) {
eprintln(sframe)
continue
}
buf := [1000]byte{}
mut output := ''
unsafe {
bp := &buf[0]
for C.fgets(&char(bp), 1000, f) != 0 {
output += tos(bp, vstrlen(bp))
}
}
output = output.trim_space() + ':'
if C.pclose(f) != 0 {
eprintln(sframe)
continue
}
if output in ['??:0:', '??:?:'] {
output = ''
}
// See http://wiki.dwarfstd.org/index.php?title=Path_Discriminators
// NB: it is shortened here to just d. , just so that it fits, and so
// that the common error file:lineno: line format is enforced.
output = output.replace(' (discriminator', ': (d.')
eprintln('${output:-55s} | ${addr:14s} | $beforeaddr')
}
output = output.trim_space() + ':'
if C.pclose(f) != 0 {
eprintln(sframe)
continue
}
if output in ['??:0:', '??:?:'] {
output = ''
}
// See http://wiki.dwarfstd.org/index.php?title=Path_Discriminators
// NB: it is shortened here to just d. , just so that it fits, and so
// that the common error file:lineno: line format is enforced.
output = output.replace(' (discriminator', ': (d.')
eprintln('${output:-55s} | ${addr:14s} | $beforeaddr')
}
}
return true

View File

@ -77,11 +77,18 @@ fn restore_codepage() {
C.SetConsoleOutputCP(g_original_codepage)
}
fn is_terminal(fd int) int {
mut mode := u32(0)
osfh := voidptr(C._get_osfhandle(fd))
C.GetConsoleMode(osfh, voidptr(&mode))
return int(mode)
}
fn builtin_init() {
g_original_codepage = C.GetConsoleOutputCP()
C.SetConsoleOutputCP(C.CP_UTF8)
C.atexit(restore_codepage)
if is_atty(1) > 0 {
if is_terminal(1) > 0 {
C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing
C.SetConsoleMode(C.GetStdHandle(C.STD_ERROR_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing
unsafe {

View File

@ -0,0 +1,130 @@
module builtin
[unsafe]
pub fn memcpy(dest &byte, src &byte, n int) &byte {
unsafe {
for i in 0 .. n {
dest[i] = src[i]
}
return dest
}
}
[export: 'malloc']
[unsafe]
fn __malloc(n int) &byte {
return unsafe { malloc(n) }
}
[unsafe]
fn strlen(s &byte) int {
mut i := 0
for ; unsafe { s[i] } != 0; i++ {}
return i
}
[unsafe]
fn realloc(old_area &byte, new_size int) &byte {
if old_area == 0 {
return unsafe { malloc(new_size) }
}
if new_size == 0 {
unsafe { free(old_area) }
return 0
}
old_size := unsafe { *(&u64(old_area - sizeof(u64))) }
if new_size <= old_size {
return old_area
} else {
new_area := unsafe { malloc(new_size) }
unsafe { memmove(new_area, old_area, int(old_size)) }
unsafe { free(old_area) }
return new_area
}
}
[unsafe]
fn memset(_s &byte, _c int, n int) &byte {
c := char(_c)
mut s := unsafe { &char(_s) }
for i in 0 .. int(n) {
unsafe {
s[i] = c
}
}
return s
}
[unsafe]
fn memmove(dest &byte, src &byte, n int) &byte {
mut temp_buf := unsafe { malloc(n) }
for i in 0 .. int(n) {
unsafe {
temp_buf[i] = src[i]
}
}
for i in 0 .. int(n) {
unsafe {
dest[i] = temp_buf[i]
}
}
unsafe { free(temp_buf) }
return dest
}
[export: 'calloc']
[unsafe]
fn __calloc(nmemb int, size int) &byte {
new_area := unsafe { malloc(nmemb * size) }
unsafe { memset(new_area, 0, nmemb * size) }
return new_area
}
fn getchar() int {
x := byte(0)
sys_read(C.stdin, &x, 1)
return int(x)
}
fn memcmp(a &byte, b &byte, n int) int {
for i in 0 .. int(n) {
if unsafe { a[i] != b[i] } {
unsafe {
return a[i] - b[i]
}
}
}
return 0
}
[export: 'free']
[unsafe]
fn __free(ptr &byte) {
err := mm_free(ptr)
if err != .enoerror {
eprintln('free error:')
panic(err)
}
}
fn vsprintf(str &char, format &char, ap &byte) int {
panic('vsprintf(): string interpolation is not supported in `-freestanding`')
}
fn vsnprintf(str &char, size int, format &char, ap &byte) int {
panic('vsnprintf(): string interpolation is not supported in `-freestanding`')
}
// not really needed
fn bare_read(buf &byte, count u64) (i64, Errno) {
return sys_read(0, buf, count)
}
fn bare_print(buf &byte, len u64) {
sys_write(1, buf, len)
}
fn bare_eprint(buf &byte, len u64) {
sys_write(2, buf, len)
}

View File

@ -0,0 +1,431 @@
module builtin
enum SigIndex {
si_signo = 0x00
si_code = 0x02
si_pid = 0x04
si_uid = 0x05
si_status = 0x06
si_size = 0x80
}
enum Signo {
sighup = 1 // Hangup.
sigint = 2 // Interactive attention signal.
sigquit = 3 // Quit.
sigill = 4 // Illegal instruction.
sigtrap = 5 // Trace/breakpoint trap.
sigabrt = 6 // Abnormal termination.
sigbus = 7
sigfpe = 8 // Erroneous arithmetic operation.
sigkill = 9 // Killed.
sigusr1 = 10
sigsegv = 11 // Invalid access to memory.
sigusr2 = 12
sigpipe = 13 // Broken pipe.
sigalrm = 14 // Alarm clock.
sigterm = 15 // Termination request.
sigstkflt = 16
sigchld = 17
sigcont = 18
sigstop = 19
sigtstp = 20
sigttin = 21 // Background read from control terminal.
sigttou = 22 // Background write to control terminal.
sigurg = 23
sigxcpu = 24 // CPU time limit exceeded.
sigxfsz = 25 // File size limit exceeded.
sigvtalrm = 26 // Virtual timer expired.
sigprof = 27 // Profiling timer expired.
sigwinch = 28
sigpoll = 29
sigsys = 31
}
// List of all the errors returned by syscalls
enum Errno {
enoerror = 0x00000000
eperm = 0x00000001
enoent = 0x00000002
esrch = 0x00000003
eintr = 0x00000004
eio = 0x00000005
enxio = 0x00000006
e2big = 0x00000007
enoexec = 0x00000008
ebadf = 0x00000009
echild = 0x0000000a
eagain = 0x0000000b
enomem = 0x0000000c
eacces = 0x0000000d
efault = 0x0000000e
enotblk = 0x0000000f
ebusy = 0x00000010
eexist = 0x00000011
exdev = 0x00000012
enodev = 0x00000013
enotdir = 0x00000014
eisdir = 0x00000015
einval = 0x00000016
enfile = 0x00000017
emfile = 0x00000018
enotty = 0x00000019
etxtbsy = 0x0000001a
efbig = 0x0000001b
enospc = 0x0000001c
espipe = 0x0000001d
erofs = 0x0000001e
emlink = 0x0000001f
epipe = 0x00000020
edom = 0x00000021
erange = 0x00000022
}
enum MemProt {
prot_read = 0x1
prot_write = 0x2
prot_exec = 0x4
prot_none = 0x0
prot_growsdown = 0x01000000
prot_growsup = 0x02000000
}
enum MapFlags {
map_shared = 0x01
map_private = 0x02
map_shared_validate = 0x03
map_type = 0x0f
map_fixed = 0x10
map_file = 0x00
map_anonymous = 0x20
map_huge_shift = 26
map_huge_mask = 0x3f
}
// const (
// fcntlf_dupfd = 0x00000000
// fcntlf_exlck = 0x00000004
// fcntlf_getfd = 0x00000001
// fcntlf_getfl = 0x00000003
// fcntlf_getlk = 0x00000005
// fcntlf_getlk64 = 0x0000000c
// fcntlf_getown = 0x00000009
// fcntlf_getowner_uids = 0x00000011
// fcntlf_getown_ex = 0x00000010
// fcntlf_getsig = 0x0000000b
// fcntlf_ofd_getlk = 0x00000024
// fcntlf_ofd_setlk = 0x00000025
// fcntlf_ofd_setlkw = 0x00000026
// fcntlf_owner_pgrp = 0x00000002
// fcntlf_owner_pid = 0x00000001
// fcntlf_owner_tid = 0x00000000
// fcntlf_rdlck = 0x00000000
// fcntlf_setfd = 0x00000002
// fcntlf_setfl = 0x00000004
// fcntlf_setlk = 0x00000006
// fcntlf_setlk64 = 0x0000000d
// fcntlf_setlkw = 0x00000007
// fcntlf_setlkw64 = 0x0000000e
// fcntlf_setown = 0x00000008
// fcntlf_setown_ex = 0x0000000f
// fcntlf_setsig = 0x0000000a
// fcntlf_shlck = 0x00000008
// fcntlf_unlck = 0x00000002
// fcntlf_wrlck = 0x00000001
// fcntllock_ex = 0x00000002
// fcntllock_mand = 0x00000020
// fcntllock_nb = 0x00000004
// fcntllock_read = 0x00000040
// fcntllock_rw = 0x000000c0
// fcntllock_sh = 0x00000001
// fcntllock_un = 0x00000008
// fcntllock_write = 0x00000080
// fcntlo_accmode = 0x00000003
// fcntlo_append = 0x00000400
// fcntlo_cloexec = 0x00080000
// fcntlo_creat = 0x00000040
// fcntlo_direct = 0x00004000
// fcntlo_directory = 0x00010000
// fcntlo_dsync = 0x00001000
// fcntlo_excl = 0x00000080
// fcntlo_largefile = 0x00008000
// fcntlo_ndelay = 0x00000800
// fcntlo_noatime = 0x00040000
// fcntlo_noctty = 0x00000100
// fcntlo_nofollow = 0x00020000
// fcntlo_nonblock = 0x00000800
// fcntlo_path = 0x00200000
// fcntlo_rdonly = 0x00000000
// fcntlo_rdwr = 0x00000002
// fcntlo_trunc = 0x00000200
// fcntlo_wronly = 0x00000001
// )
/*
Paraphrased from "man 2 waitid" on Linux
Upon successful return, waitid() fills in the
following fields of the siginfo_t structure
pointed to by infop:
si_pid, offset 0x10, int index 0x04:
The process ID of the child.
si_uid: offset 0x14, int index 0x05
The real user ID of the child.
si_signo: offset 0x00, int index 0x00
Always set to SIGCHLD.
si_status: ofset 0x18, int index 0x06
1 the exit status of the child, as given to _exit(2)
(or exit(3)) (sc_sys.cld_exited)
2 the signal that caused the child to terminate, stop,
or continue.
3 The si_code field can be used to determine how to
interpret this field.
si_code, set to one of (enum Wi_si_code), offset 0x08, int index 0x02:
CLD_EXITED (child called _exit(2));
CLD_KILLED (child killed by signal);
CLD_DUMPED (child killed by signal, and dumped core);
CLD_STOPPED (child stopped by signal);
CLD_TRAPPED (traced child has trapped);
CLD_CONTINUED (child continued by SIGCONT).
*/
const (
wp_sys_wnohang = u64(0x00000001)
wp_sys_wuntraced = u64(0x00000002)
wp_sys_wstopped = u64(0x00000002)
wp_sys_wexited = u64(0x00000004)
wp_sys_wcontinued = u64(0x00000008)
wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status.
wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group
wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type
wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children
)
// First argument to waitid:
enum WiWhich {
p_all = 0
p_pid = 1
p_pgid = 2
}
enum WiSiCode {
cld_exited = 1 // child has exited
cld_killed = 2 // child was killed
cld_dumped = 3 // child terminated abnormally
cld_trapped = 4 // traced child has trapped
cld_stopped = 5 // child has stopped
cld_continued = 6 // stopped child has continued
}
fn split_int_errno(rc_in u64) (i64, Errno) {
rc := i64(rc_in)
if rc < 0 {
return i64(-1), Errno(-rc)
}
return rc, Errno.enoerror
}
// 0 sys_read
fn sys_read(fd i64, buf &byte, count u64) (i64, Errno) {
return split_int_errno(sys_call3(0, u64(fd), u64(buf), count))
}
// 1 sys_write
fn sys_write(fd i64, buf &byte, count u64) (i64, Errno) {
return split_int_errno(sys_call3(1, u64(fd), u64(buf), count))
}
// 2 sys_open
fn sys_open(filename &byte, flags i64, mode int) (i64, Errno) {
return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode)))
}
// 3 sys_close
fn sys_close(fd i64) Errno {
return Errno(-i64(sys_call1(3, u64(fd))))
}
// 9 sys_mmap
fn sys_mmap(addr &byte, len u64, prot MemProt, flags MapFlags, fildes u64, off u64) (&byte, Errno) {
rc := sys_call6(9, u64(addr), len, u64(prot), u64(flags), fildes, off)
a, e := split_int_errno(rc)
return &byte(a), e
}
// 11 sys_munmap
fn sys_munmap(addr voidptr, len u64) Errno {
return Errno(-sys_call2(11, u64(addr), len))
}
// 22 sys_pipe
fn sys_pipe(filedes &int) Errno {
return Errno(sys_call1(22, u64(filedes)))
}
// 24 sys_sched_yield
fn sys_sched_yield() Errno {
return Errno(sys_call0(24))
}
// 28 sys_madvise
fn sys_madvise(addr voidptr, len u64, advice int) Errno {
return Errno(sys_call3(28, u64(addr), len, u64(advice)))
}
// 39 sys_getpid
fn sys_getpid() int {
return int(sys_call0(39))
}
// 57 sys_fork
fn sys_fork() int {
return int(sys_call0(57))
}
// 58 sys_vfork
fn sys_vfork() int {
return int(sys_call0(58))
}
// 33 sys_dup2
fn sys_dup2(oldfd int, newfd int) (i64, Errno) {
return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd)))
}
// 59 sys_execve
fn sys_execve(filename &byte, argv []&byte, envp []&byte) int {
return int(sys_call3(59, u64(filename), argv.data, envp.data))
}
// 60 sys_exit
fn sys_exit(ec int) {
sys_call1(60, u64(ec))
}
// 102 sys_getuid
fn sys_getuid() int {
return int(sys_call0(102))
}
// 247 sys_waitid
fn sys_waitid(which WiWhich, pid int, infop &int, options int, ru voidptr) Errno {
return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru)))
}
fn sys_call0(scn u64) u64 {
mut res := u64(0)
asm amd64 {
syscall
; =a (res)
; a (scn)
}
return res
}
fn sys_call1(scn u64, arg1 u64) u64 {
mut res := u64(0)
asm amd64 {
syscall
; =a (res)
; a (scn)
D (arg1)
}
return res
}
fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 {
mut res := u64(0)
asm amd64 {
syscall
; =a (res)
; a (scn)
D (arg1)
S (arg2)
}
return res
}
fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 {
mut res := u64(0)
asm amd64 {
syscall
; =a (res)
; a (scn)
D (arg1)
S (arg2)
d (arg3)
}
return res
}
fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 {
mut res := u64(0)
asm amd64 {
mov r10, arg4
syscall
; =a (res)
; a (scn)
D (arg1)
S (arg2)
d (arg3)
r (arg4)
; r10
}
return res
}
fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 {
mut res := u64(0)
asm amd64 {
mov r10, arg4
mov r8, arg5
syscall
; =a (res)
; a (scn)
D (arg1)
S (arg2)
d (arg3)
r (arg4)
r (arg5)
; r10
r8
}
return res
}
fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64, arg6 u64) u64 {
mut res := u64(0)
asm amd64 {
mov r10, arg4
mov r8, arg5
mov r9, arg6
syscall
; =a (res)
; a (scn)
D (arg1)
S (arg2)
d (arg3)
r (arg4)
r (arg5)
r (arg6)
; r10
r8
r9
}
return res
}
asm amd64 {
.globl _start
_start:
call main
mov rax, 60
xor rdi, rdi
syscall
ret
}

View File

@ -0,0 +1,29 @@
module builtin
fn mm_alloc(size u64) (&byte, Errno) {
// BEGIN CONSTS
// the constants need to be here, since the initialization of other constants,
// which happen before these ones would, require malloc
mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write))
map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous))
// END CONSTS
a, e := sys_mmap(&byte(0), size + sizeof(u64), mem_prot, map_flags, -1, 0)
if e == .enoerror {
unsafe {
mut ap := &u64(a)
*ap = size
x2 := &byte(a + sizeof(u64))
return x2, e
}
}
return &byte(0), e
}
fn mm_free(addr &byte) Errno {
unsafe {
ap := &u64(addr - sizeof(u64))
size := *ap
return sys_munmap(addr - sizeof(u64), size + sizeof(u64))
}
}

View File

@ -997,7 +997,11 @@ pub fn (s string) to_lower() string {
unsafe {
mut b := malloc(s.len + 1)
for i in 0 .. s.len {
b[i] = byte(C.tolower(s.str[i]))
if s.str[i] >= `A` && s.str[i] <= `Z` {
b[i] = s.str[i] + 32
} else {
b[i] = s.str[i]
}
}
b[s.len] = 0
return tos(b, s.len)
@ -1021,7 +1025,11 @@ pub fn (s string) to_upper() string {
unsafe {
mut b := malloc(s.len + 1)
for i in 0 .. s.len {
b[i] = byte(C.toupper(s.str[i]))
if s.str[i] >= `a` && s.str[i] <= `z` {
b[i] = s.str[i] - 32
} else {
b[i] = s.str[i]
}
}
b[s.len] = 0
return tos(b, s.len)
@ -1539,7 +1547,11 @@ pub fn (s &string) free() {
return
}
if s.is_lit == -98761234 {
C.printf(c'double string.free() detected\n')
$if freestanding {
bare_eprint(c'double string.free() detected\n', u64(unsafe { C.strlen(c'double string.free() detected\n') }))
} $else {
C.printf(c'double string.free() detected\n')
}
return
}
if s.is_lit == 1 || s.len == 0 {

View File

@ -629,3 +629,15 @@ pub fn execute_or_panic(cmd string) Result {
}
return res
}
// is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal
pub fn is_atty(fd int) int {
$if windows {
mut mode := u32(0)
osfh := voidptr(C._get_osfhandle(fd))
C.GetConsoleMode(osfh, voidptr(&mode))
return int(mode)
} $else {
return C.isatty(fd)
}
}

View File

@ -125,8 +125,8 @@ fn supports_escape_sequences(fd int) bool {
return true
}
// 4 is enable_virtual_terminal_processing
return (is_atty(fd) & 0x0004) > 0
return (os.is_atty(fd) & 0x0004) > 0
} $else {
return is_atty(fd) > 0
return os.is_atty(fd) > 0
}
}

View File

@ -17,7 +17,7 @@ fn C.ioctl(fd int, request u64, arg voidptr) int
// get_terminal_size returns a number of colums and rows of terminal window.
pub fn get_terminal_size() (int, int) {
if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
return default_columns_size, default_rows_size
}
w := C.winsize{}
@ -27,7 +27,7 @@ pub fn get_terminal_size() (int, int) {
// get_cursor_position returns a Coord containing the current cursor position
pub fn get_cursor_position() Coord {
if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
return Coord{
x: 0
y: 0
@ -89,7 +89,7 @@ pub fn get_cursor_position() Coord {
// set_terminal_title change the terminal title
pub fn set_terminal_title(title string) bool {
if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
return true
}
print('\033]0')

View File

@ -57,7 +57,7 @@ fn C.ScrollConsoleScreenBuffer(output C.HANDLE, scroll_rect &C.SMALL_RECT, clip_
// get_terminal_size returns a number of colums and rows of terminal window.
pub fn get_terminal_size() (int, int) {
if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
info := C.CONSOLE_SCREEN_BUFFER_INFO{}
if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) {
columns := int(info.srWindow.Right - info.srWindow.Left + 1)
@ -71,7 +71,7 @@ pub fn get_terminal_size() (int, int) {
// get_cursor_position returns a Coord containing the current cursor position
pub fn get_cursor_position() Coord {
mut res := Coord{}
if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' {
info := C.CONSOLE_SCREEN_BUFFER_INFO{}
if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) {
res.x = info.dwCursorPosition.X

View File

@ -60,7 +60,7 @@ fn (mut ctx Context) termios_setup() ? {
// store the current title, so restore_terminal_state can get it back
save_title()
if !ctx.cfg.skip_init_checks && !(is_atty(C.STDIN_FILENO) != 0 && is_atty(C.STDOUT_FILENO) != 0) {
if !ctx.cfg.skip_init_checks && !(os.is_atty(C.STDIN_FILENO) != 0 && os.is_atty(C.STDOUT_FILENO) != 0) {
return error('not running under a TTY')
}

View File

@ -3804,7 +3804,7 @@ fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
stmt.pos)
}
if c.pref.backend == .js {
c.error('inline assembly is not supported in js backend', stmt.pos)
c.error('inline assembly is not supported in the js backend', stmt.pos)
}
if c.pref.backend == .c && c.pref.ccompiler_type == .msvc {
c.error('msvc compiler does not support inline assembly', stmt.pos)
@ -3857,16 +3857,7 @@ fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) {
fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) {
match mut arg {
ast.AsmAlias {
name := arg.name
if name !in aliases && name !in stmt.local_labels && name !in c.file.global_labels {
mut possible := aliases.clone()
possible << stmt.local_labels
possible << c.file.global_labels
suggestion := util.new_suggestion(name, possible)
c.error(suggestion.say('alias or label `$arg.name` does not exist'), arg.pos)
}
}
ast.AsmAlias {}
ast.AsmAddressing {
if arg.scale !in [-1, 1, 2, 4, 8] {
c.error('scale must be one of 1, 2, 4, or 8', arg.pos)
@ -5368,7 +5359,19 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
} else if !is_comptime_type_is_expr {
found_branch = true // If a branch wasn't skipped, the rest must be
}
if !c.skip_flags || c.pref.output_cross_c {
if !c.skip_flags {
c.stmts(branch.stmts)
} else if c.pref.output_cross_c {
mut is_freestanding_block := false
if branch.cond is ast.Ident {
if branch.cond.name == 'freestanding' {
is_freestanding_block = true
}
}
if is_freestanding_block {
branch.stmts = []
node.branches[i].stmts = []
}
c.stmts(branch.stmts)
} else if !is_comptime_type_is_expr {
node.branches[i].stmts = []
@ -5603,7 +5606,7 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool {
'glibc' { return false } // TODO
'prealloc' { return !c.pref.prealloc }
'no_bounds_checking' { return cond.name !in c.pref.compile_defines_all }
'freestanding' { return !c.pref.is_bare }
'freestanding' { return !c.pref.is_bare || c.pref.output_cross_c }
else { return false }
}
} else if cond.name !in c.pref.compile_defines_all {

View File

@ -1,5 +0,0 @@
vlib/v/checker/tests/asm_alias_does_not_exist.vv:2:11: error: alias or label `a` does not exist
1 | asm amd64 {
2 | mov ebx, a
| ^
3 | }

View File

@ -1,3 +0,0 @@
asm amd64 {
mov ebx, a
}

View File

@ -392,7 +392,6 @@ pub fn (mut g Gen) init() {
} else {
g.cheaders.writeln(c_headers)
}
g.definitions.writeln('void _STR_PRINT_ARG(const char*, char**, int*, int*, int, ...);')
g.definitions.writeln('string _STR(const char*, int, ...);')
g.definitions.writeln('string _STR_TMP(const char*, ...);')
}
@ -1893,7 +1892,7 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
ast.AsmAlias {
name := arg.name
if name in stmt.local_labels || name in stmt.global_labels
|| name in g.file.global_labels {
|| name in g.file.global_labels || stmt.is_top_level {
asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name }
g.write(asm_formatted_name)
} else {
@ -5466,7 +5465,9 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) {
g.type_definitions.writeln('} $name;')
}
} else {
g.type_definitions.writeln('typedef pthread_t $name;')
if !g.pref.is_bare {
g.type_definitions.writeln('typedef pthread_t $name;')
}
}
}
ast.SumType {

View File

@ -5,18 +5,18 @@ module c
// for each constant, during C code generation.
const (
// V_COMMIT_HASH is generated by cmd/tools/gen_vc.v .
c_commit_hash_default = '
c_commit_hash_default = '
#ifndef V_COMMIT_HASH
#define V_COMMIT_HASH "@@@"
#endif
'
// V_CURRENT_COMMIT_HASH is updated, when V is rebuilt inside a git repo.
c_current_commit_hash_default = '
c_current_commit_hash_default = '
#ifndef V_CURRENT_COMMIT_HASH
#define V_CURRENT_COMMIT_HASH "@@@"
#endif
'
c_concurrency_helpers = '
c_concurrency_helpers = '
typedef struct __shared_map __shared_map;
struct __shared_map { map val; sync__RwMutex mtx; };
static inline voidptr __dup_shared_map(voidptr src, int sz) {
@ -47,7 +47,136 @@ static inline void __sort_ptr(uintptr_t a[], bool b[], int l)
}
}
'
c_common_macros = '
c_str_fn_defs = '
void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize, int guess, ...) {
va_list args;
va_start(args, guess);
// NB: (*memsize - *nbytes) === how much free space is left at the end of the current buffer refbufp
// *memsize === total length of the buffer refbufp
// *nbytes === already occupied bytes of buffer refbufp
// guess === how many bytes were taken during the current vsnprintf run
for(;;) {
int remaining_space = *memsize - *nbytes;
if (guess < remaining_space) {
guess = vsnprintf(*refbufp + *nbytes, *memsize - *nbytes, fmt, args);
if (guess < remaining_space) { // result did fit into buffer
*nbytes += guess;
break;
}
}
// increase buffer (somewhat exponentially)
*memsize += guess + 3 * (*memsize) / 2;
#ifdef _VGCBOEHM
*refbufp = (char*)GC_REALLOC((void*)*refbufp, *memsize);
#else
*refbufp = (char*)realloc((void*)*refbufp, *memsize);
#endif
}
va_end(args);
}
string _STR(const char *fmt, int nfmts, ...) {
va_list argptr;
int memsize = 128;
int nbytes = 0;
#ifdef _VGCBOEHM
char* buf = (char*)GC_MALLOC(memsize);
#else
char* buf = (char*)malloc(memsize);
#endif
va_start(argptr, nfmts);
for (int i=0; i<nfmts; ++i) {
int k = strlen(fmt);
bool is_fspec = false;
for (int j=0; j<k; ++j) {
if (fmt[j] == \'%\') {
j++;
if (fmt[j] != \'%\') {
is_fspec = true;
break;
}
}
}
if (is_fspec) {
char f = fmt[k-1];
char fup = f & 0xdf; // toupper
bool l = fmt[k-2] == \'l\';
bool ll = l && fmt[k-3] == \'l\';
if (f == \'u\' || fup == \'X\' || f == \'o\' || f == \'d\' || f == \'c\') { // int...
if (ll) _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+16, va_arg(argptr, long long));
else if (l) _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+10, va_arg(argptr, long));
else _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+8, va_arg(argptr, int));
} else if (fup >= \'E\' && fup <= \'G\') { // floating point
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+10, va_arg(argptr, double));
} else if (f == \'p\') {
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+14, va_arg(argptr, void*));
} else if (f == \'C\') { // a C string
char* sptr = va_arg(argptr, char*);
char* fmt_no_c = (char*)v_malloc(k+4);
memcpy(fmt_no_c, fmt, k);
fmt_no_c[k-2]=\'C\';
fmt_no_c[k-1]=\'"\';
fmt_no_c[k]=\'%\';
fmt_no_c[k+1]=\'s\';
fmt_no_c[k+2]=\'"\';
fmt_no_c[k+3]=0;
_STR_PRINT_ARG(fmt_no_c, &buf, &nbytes, &memsize, k + 4 + 100, sptr);
v_free(fmt_no_c);
} else if (f == \'s\') { // v string
string s = va_arg(argptr, string);
if (fmt[k-4] == \'*\') { // %*.*s
int fwidth = va_arg(argptr, int);
if (fwidth < 0)
fwidth -= (s.len - utf8_str_visible_length(s));
else
fwidth += (s.len - utf8_str_visible_length(s));
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, fwidth, s.len, s.str);
} else { // %.*s
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, s.len, s.str);
}
} else {
//v_panic(tos3(\'Invaid format specifier\'));
}
} else {
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k);
}
fmt += k+1;
}
va_end(argptr);
buf[nbytes] = 0;
#ifdef DEBUG_ALLOC
//puts(\'_STR:\');
puts(buf);
#endif
#if _VAUTOFREE
//g_cur_str = (byteptr)buf;
#endif
return tos2((byteptr)buf);
}
string _STR_TMP(const char *fmt, ...) {
va_list argptr;
va_start(argptr, fmt);
size_t len = vsnprintf(0, 0, fmt, argptr) + 1;
va_end(argptr);
va_start(argptr, fmt);
vsprintf((char *)g_str_buf, fmt, argptr);
va_end(argptr);
#ifdef DEBUG_ALLOC
//puts(\'_STR_TMP:\');
//puts(g_str_buf);
#endif
string res = tos(g_str_buf, len);
res.is_lit = 1;
return res;
} // endof _STR_TMP
'
c_common_macros = '
#define EMPTY_VARG_INITIALIZATION 0
#define EMPTY_STRUCT_DECLARATION
#define EMPTY_STRUCT_INITIALIZATION 0
@ -126,12 +255,6 @@ static inline void __sort_ptr(uintptr_t a[], bool b[], int l)
#define V64_PRINTFORMAT "0x%llx"
#endif
#endif
'
c_headers = '
// c_headers
typedef int (*qsort_callback_func)(const void*, const void*);
#include <stdio.h> // TODO remove all these includes, define all function signatures and types manually
#include <stdlib.h>
#if defined(_WIN32) || defined(__CYGWIN__)
#define VV_EXPORTED_SYMBOL extern __declspec(dllexport)
@ -167,144 +290,8 @@ typedef int (*qsort_callback_func)(const void*, const void*);
#undef __has_include
#endif
#ifndef _WIN32
#if defined __has_include
#if __has_include (<execinfo.h>)
#include <execinfo.h>
#else
// Most probably musl OR __ANDROID__ ...
int backtrace (void **__array, int __size) { return 0; }
char **backtrace_symbols (void *const *__array, int __size){ return 0; }
void backtrace_symbols_fd (void *const *__array, int __size, int __fd){}
#endif
#endif
#endif
//#include "fns.h"
#include <signal.h>
#include <stdarg.h> // for va_list
#include <string.h> // memcpy
#if INTPTR_MAX == INT32_MAX
#define TARGET_IS_32BIT 1
#elif INTPTR_MAX == INT64_MAX
#define TARGET_IS_64BIT 1
#else
#error "The environment is not 32 or 64-bit."
#endif
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
#define TARGET_ORDER_IS_BIG
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IX86)
#define TARGET_ORDER_IS_LITTLE
#else
#error "Unknown architecture endianness"
#endif
#ifndef _WIN32
#include <ctype.h>
#include <locale.h> // tolower
#include <sys/time.h>
#include <unistd.h> // sleep
extern char **environ;
#endif
#if defined(__CYGWIN__) && !defined(_WIN32)
#error Cygwin is not supported, please use MinGW or Visual Studio.
#endif
#ifdef __linux__
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __FreeBSD__
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __DragonFly__
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __OpenBSD__
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __NetBSD__
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __sun
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
$c_common_macros
#ifdef _WIN32
#define WINVER 0x0600
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0600
#ifndef WIN32_FULL
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <io.h> // _waccess
#include <direct.h> // _wgetcwd
#ifdef _MSC_VER
// On MSVC these are the same (as long as /volatile:ms is passed)
#define _Atomic volatile
// MSVC cannot parse some things properly
#undef EMPTY_STRUCT_DECLARATION
#undef OPTION_CAST
#define EMPTY_STRUCT_DECLARATION int ____dummy_variable
#define OPTION_CAST(x)
#undef __NOINLINE
#undef __IRQHANDLER
#define __NOINLINE __declspec(noinline)
#define __IRQHANDLER __declspec(naked)
#include <dbghelp.h>
#pragma comment(lib, "Dbghelp")
#endif
#else
#include <pthread.h>
#ifndef PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
// musl does not have that
#define pthread_rwlockattr_setkind_np(a, b)
#endif
#endif
// g_live_info is used by live.info()
static void* g_live_info = NULL;
//============================== HELPER C MACROS =============================*/
//#define tos4(s, slen) ((string){.str=(s), .len=(slen)})
// `"" s` is used to enforce a string literal argument
#define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1})
// take the address of an rvalue
#define ADDR(type, expr) (&((type[]){expr}[0]))
// copy something to the heap
#define HEAP(type, expr) ((type*)memdup((void*)&((type[]){expr}[0]), sizeof(type)))
#define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many(arr, tmp.data, tmp.len);}
#define _IN_MAP(val, m) map_exists(m, val)
'
c_unsigned_comparison_functions = '
// unsigned/signed comparisons
static inline bool _us32_gt(uint32_t a, int32_t b) { return a > INT32_MAX || (int32_t)a > b; }
static inline bool _us32_ge(uint32_t a, int32_t b) { return a >= INT32_MAX || (int32_t)a >= b; }
@ -318,32 +305,8 @@ static inline bool _us64_eq(uint64_t a, int64_t b) { return a <= INT64_MAX && (i
static inline bool _us64_ne(uint64_t a, int64_t b) { return a > INT64_MAX || (int64_t)a != b; }
static inline bool _us64_le(uint64_t a, int64_t b) { return a <= INT64_MAX && (int64_t)a <= b; }
static inline bool _us64_lt(uint64_t a, int64_t b) { return a < INT64_MAX && (int64_t)a < b; }
#if defined(__MINGW32__) || defined(__MINGW64__) || (defined(_WIN32) && defined(__TINYC__))
#undef PRId64
#undef PRIi64
#undef PRIo64
#undef PRIu64
#undef PRIx64
#undef PRIX64
#define PRId64 "lld"
#define PRIi64 "lli"
#define PRIo64 "llo"
#define PRIu64 "llu"
#define PRIx64 "llx"
#define PRIX64 "llX"
#endif
//================================== GLOBALS =================================*/
//byte g_str_buf[1024];
byte* g_str_buf;
int load_so(byteptr);
void reload_so();
void _vinit(int ___argc, voidptr ___argv);
void _vcleanup();
#define sigaction_size sizeof(sigaction);
#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) )
'
c_wyhash = '
// ============== wyhash ==============
#ifndef wyhash_final_version_3
#define wyhash_final_version_3
@ -363,7 +326,6 @@ void _vcleanup();
//includes
#include <stdint.h>
#include <string.h>
#if defined(_MSC_VER) && defined(_M_X64)
#include <intrin.h>
#pragma intrinsic(_umul128)
@ -527,15 +489,215 @@ static inline void make_secret(uint64_t seed, uint64_t *secret){
}
}
#endif
'
c_helper_macros = '//============================== HELPER C MACROS =============================*/
//#define tos4(s, slen) ((string){.str=(s), .len=(slen)})
// `"" s` is used to enforce a string literal argument
#define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1})
// take the address of an rvalue
#define ADDR(type, expr) (&((type[]){expr}[0]))
// copy something to the heap
#define HEAP(type, expr) ((type*)memdup((void*)&((type[]){expr}[0]), sizeof(type)))
#define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many(arr, tmp.data, tmp.len);}
#define _IN_MAP(val, m) map_exists(m, val)
'
c_headers =
c_helper_macros + c_unsigned_comparison_functions + c_common_macros + r'
// c_headers
typedef int (*qsort_callback_func)(const void*, const void*);
#include <stdio.h> // TODO remove all these includes, define all function signatures and types manually
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32) || defined(__CYGWIN__)
#define VV_EXPORTED_SYMBOL extern __declspec(dllexport)
#define VV_LOCAL_SYMBOL static
#else
// 4 < gcc < 5 is used by some older Ubuntu LTS and Centos versions,
// and does not support __has_attribute(visibility) ...
#ifndef __has_attribute
#define __has_attribute(x) 0 // Compatibility with non-clang compilers.
#endif
#if (defined(__GNUC__) && (__GNUC__ >= 4)) || (defined(__clang__) && __has_attribute(visibility))
#ifdef ARM
#define VV_EXPORTED_SYMBOL extern __attribute__((externally_visible,visibility("default")))
#else
#define VV_EXPORTED_SYMBOL extern __attribute__((visibility("default")))
#endif
#define VV_LOCAL_SYMBOL __attribute__ ((visibility ("hidden")))
#else
#define VV_EXPORTED_SYMBOL extern
#define VV_LOCAL_SYMBOL static
#endif
#endif
#if defined(__TINYC__) && defined(__has_include)
// tcc does not support has_include properly yet, turn it off completely
#undef __has_include
#endif
#ifndef _WIN32
#if defined __has_include
#if __has_include (<execinfo.h>)
#include <execinfo.h>
#else
// Most probably musl OR __ANDROID__ ...
int backtrace (void **__array, int __size) { return 0; }
char **backtrace_symbols (void *const *__array, int __size){ return 0; }
void backtrace_symbols_fd (void *const *__array, int __size, int __fd){}
#endif
#endif
#endif
//#include "fns.h"
#include <signal.h>
#include <stdarg.h> // for va_list
//================================== GLOBALS =================================*/
//byte g_str_buf[1024];
byte* g_str_buf;
int load_so(byteptr);
void reload_so();
void _vinit(int ___argc, voidptr ___argv);
void _vcleanup();
#define sigaction_size sizeof(sigaction);
#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) )
void v_free(voidptr ptr);
voidptr memdup(voidptr src, int sz);
static voidptr memfreedup(voidptr ptr, voidptr src, int sz) {
v_free(ptr);
v_free(ptr); // heloe
return memdup(src, sz);
}
'
c_builtin_types = '
#if INTPTR_MAX == INT32_MAX
#define TARGET_IS_32BIT 1
#elif INTPTR_MAX == INT64_MAX
#define TARGET_IS_64BIT 1
#else
#error "The environment is not 32 or 64-bit."
#endif
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
#define TARGET_ORDER_IS_BIG
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IX86)
#define TARGET_ORDER_IS_LITTLE
#else
#error "Unknown architecture endianness"
#endif
#ifndef _WIN32
#include <ctype.h>
#include <locale.h> // tolower
#include <sys/time.h>
#include <unistd.h> // sleep
extern char **environ;
#endif
#if defined(__CYGWIN__) && !defined(_WIN32)
#error Cygwin is not supported, please use MinGW or Visual Studio.
#endif
#ifdef __linux__
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __FreeBSD__
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __DragonFly__
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __OpenBSD__
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __NetBSD__
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef __sun
#include <sys/types.h>
#include <sys/wait.h> // os__wait uses wait on nix
#endif
#ifdef _WIN32
#define WINVER 0x0600
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0600
#ifndef WIN32_FULL
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <io.h> // _waccess
#include <direct.h> // _wgetcwd
#ifdef _MSC_VER
// On MSVC these are the same (as long as /volatile:ms is passed)
#define _Atomic volatile
// MSVC cannot parse some things properly
#undef EMPTY_STRUCT_DECLARATION
#undef OPTION_CAST
#define EMPTY_STRUCT_DECLARATION int ____dummy_variable
#define OPTION_CAST(x)
#undef __NOINLINE
#undef __IRQHANDLER
#define __NOINLINE __declspec(noinline)
#define __IRQHANDLER __declspec(naked)
#include <dbghelp.h>
#pragma comment(lib, "Dbghelp")
#endif
#else
#include <pthread.h>
#ifndef PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
// musl does not have that
#define pthread_rwlockattr_setkind_np(a, b)
#endif
#endif
// g_live_info is used by live.info()
static void* g_live_info = NULL;
#if defined(__MINGW32__) || defined(__MINGW64__) || (defined(_WIN32) && defined(__TINYC__))
#undef PRId64
#undef PRIi64
#undef PRIo64
#undef PRIu64
#undef PRIx64
#undef PRIX64
#define PRId64 "lld"
#define PRIi64 "lli"
#define PRIo64 "llo"
#define PRIu64 "llu"
#define PRIx64 "llx"
#define PRIX64 "llX"
#endif
#ifdef _VFREESTANDING
#undef _VFREESTANDING
#endif
' + c_wyhash
c_builtin_types = '
//================================== builtin types ================================*/
typedef int64_t i64;
typedef int16_t i16;
@ -569,12 +731,79 @@ typedef bool (*MapEqFn)(voidptr, voidptr);
typedef void (*MapCloneFn)(voidptr, voidptr);
typedef void (*MapFreeFn)(voidptr);
'
bare_c_headers = '
$c_common_macros
bare_c_headers = c_helper_macros + c_unsigned_comparison_functions +
c_common_macros +
'
#ifndef exit
#define exit(rc) sys_exit(rc)
void sys_exit (int);
#endif
'
#define stdin 0
#define stdout 1
#define stderr 2
#define _VFREESTANDING
typedef long unsigned int size_t;
// Memory allocation related headers
// void *malloc(int size);
byte *calloc(int nitems, int size);
byte *realloc(byte *ptr, int size);
byte *memcpy(byte *dest, byte *src, int n);
byte *memset(byte *s, int c, int n);
byte *memmove(byte *dest, byte *src, int n);
// Backtrace headers
int backtrace(void **buffer, int size);
char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
// I/O related headers
typedef void FILE;
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int getc(FILE *stream);
int getchar(void);
int ungetc(int c, FILE *stream);
// char manipulation headers
int toupper(int c);
int tolower(int c);
// int toupper_l(int c, locale_t locale);
// int tolower_l(int c, locale_t locale);
int isatty(int fd);
// varargs implementation, TODO: works on tcc and gcc, but is very unportable and hacky
typedef __builtin_va_list va_list;
#define va_start(a, b) __builtin_va_start(a, b)
#define va_end(a) __builtin_va_end(a)
#define va_arg(a, b) __builtin_va_arg(a, b)
#define va_copy(a, b) __builtin_va_copy(a, b)
//================================== GLOBALS =================================*/
//byte g_str_buf[1024];
byte* g_str_buf;
int load_so(byteptr);
void reload_so();
void _vinit(int ___argc, voidptr ___argv);
void _vcleanup();
#define sigaction_size sizeof(sigaction);
#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) )
void v_free(voidptr ptr);
voidptr memdup(voidptr src, int sz);
static voidptr memfreedup(voidptr ptr, voidptr src, int sz) {
v_free(ptr); // heloe
return memdup(src, sz);
}
' +
c_wyhash
)

View File

@ -6,136 +6,7 @@ import v.ast
import v.util
fn (mut g Gen) write_str_fn_definitions() {
// _STR function can't be defined in vlib
g.writeln('
void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize, int guess, ...) {
va_list args;
va_start(args, guess);
// NB: (*memsize - *nbytes) === how much free space is left at the end of the current buffer refbufp
// *memsize === total length of the buffer refbufp
// *nbytes === already occupied bytes of buffer refbufp
// guess === how many bytes were taken during the current vsnprintf run
for(;;) {
int remaining_space = *memsize - *nbytes;
if (guess < remaining_space) {
guess = vsnprintf(*refbufp + *nbytes, *memsize - *nbytes, fmt, args);
if (guess < remaining_space) { // result did fit into buffer
*nbytes += guess;
break;
}
}
// increase buffer (somewhat exponentially)
*memsize += guess + 3 * (*memsize) / 2;
#ifdef _VGCBOEHM
*refbufp = (char*)GC_REALLOC((void*)*refbufp, *memsize);
#else
*refbufp = (char*)realloc((void*)*refbufp, *memsize);
#endif
}
va_end(args);
}
string _STR(const char *fmt, int nfmts, ...) {
va_list argptr;
int memsize = 128;
int nbytes = 0;
#ifdef _VGCBOEHM
char* buf = (char*)GC_MALLOC(memsize);
#else
char* buf = (char*)malloc(memsize);
#endif
va_start(argptr, nfmts);
for (int i=0; i<nfmts; ++i) {
int k = strlen(fmt);
bool is_fspec = false;
for (int j=0; j<k; ++j) {
if (fmt[j] == \'%\') {
j++;
if (fmt[j] != \'%\') {
is_fspec = true;
break;
}
}
}
if (is_fspec) {
char f = fmt[k-1];
char fup = f & 0xdf; // toupper
bool l = fmt[k-2] == \'l\';
bool ll = l && fmt[k-3] == \'l\';
if (f == \'u\' || fup == \'X\' || f == \'o\' || f == \'d\' || f == \'c\') { // int...
if (ll) _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+16, va_arg(argptr, long long));
else if (l) _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+10, va_arg(argptr, long));
else _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+8, va_arg(argptr, int));
} else if (fup >= \'E\' && fup <= \'G\') { // floating point
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+10, va_arg(argptr, double));
} else if (f == \'p\') {
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+14, va_arg(argptr, void*));
} else if (f == \'C\') { // a C string
char* sptr = va_arg(argptr, char*);
char* fmt_no_c = (char*)v_malloc(k+4);
memcpy(fmt_no_c, fmt, k);
fmt_no_c[k-2]=\'C\';
fmt_no_c[k-1]=\'"\';
fmt_no_c[k]=\'%\';
fmt_no_c[k+1]=\'s\';
fmt_no_c[k+2]=\'"\';
fmt_no_c[k+3]=0;
_STR_PRINT_ARG(fmt_no_c, &buf, &nbytes, &memsize, k + 4 + 100, sptr);
v_free(fmt_no_c);
} else if (f == \'s\') { // v string
string s = va_arg(argptr, string);
if (fmt[k-4] == \'*\') { // %*.*s
int fwidth = va_arg(argptr, int);
if (fwidth < 0)
fwidth -= (s.len - utf8_str_visible_length(s));
else
fwidth += (s.len - utf8_str_visible_length(s));
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, fwidth, s.len, s.str);
} else { // %.*s
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, s.len, s.str);
}
} else {
//v_panic(tos3(\'Invaid format specifier\'));
}
} else {
_STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k);
}
fmt += k+1;
}
va_end(argptr);
buf[nbytes] = 0;
#ifdef DEBUG_ALLOC
//puts(\'_STR:\');
puts(buf);
#endif
#if _VAUTOFREE
//g_cur_str = (byteptr)buf;
#endif
return tos2((byteptr)buf);
}
string _STR_TMP(const char *fmt, ...) {
va_list argptr;
va_start(argptr, fmt);
size_t len = vsnprintf(0, 0, fmt, argptr) + 1;
va_end(argptr);
va_start(argptr, fmt);
vsprintf((char *)g_str_buf, fmt, argptr);
va_end(argptr);
#ifdef DEBUG_ALLOC
//puts(\'_STR_TMP:\');
//puts(g_str_buf);
#endif
string res = tos(g_str_buf, len);
res.is_lit = 1;
return res;
} // endof _STR_TMP
')
g.writeln(c_str_fn_defs)
}
fn (mut g Gen) string_literal(node ast.StringLiteral) {

View File

@ -916,6 +916,9 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
mut args := []ast.AsmArg{}
args_loop: for {
if p.prev_tok.position().line_nr < p.tok.position().line_nr {
break
}
match p.tok.kind {
.name {
args << p.reg_or_alias()
@ -976,6 +979,9 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt {
} else {
break
}
// if p.prev_tok.position().line_nr < p.tok.position().line_nr {
// break
// }
}
mut comments := []ast.Comment{}
for p.tok.kind == .comment {

View File

@ -596,6 +596,9 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
res.path += '.v'
}
}
if !res.is_bare && res.bare_builtin_dir != '' {
eprintln('`-bare-builtin-dir` must be used with `-freestanding`')
}
if command == 'build-module' {
res.build_mode = .build_module
res.path = args[command_pos + 1]

View File

@ -8,9 +8,6 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
files.sort()
mut all_v_files := []string{}
for file in files {
if prefs.is_bare && os.join_path(dir, file).ends_with('/vlib/builtin/builtin_nix.c.v') {
continue
}
if !file.ends_with('.v') && !file.ends_with('.vh') {
continue
}
@ -123,22 +120,22 @@ pub fn (prefs &Preferences) should_compile_c(file string) bool {
// Probably something like `a.js.v`.
return false
}
if prefs.is_bare && file.ends_with('.freestanding.v') {
return true
}
if prefs.os == .all {
return true
}
if !prefs.is_bare && file.ends_with('.freestanding.v') {
if prefs.backend != .x64 && file.ends_with('_x64.v') {
return false
}
if (file.ends_with('_windows.c.v') || file.ends_with('_windows.v')) && prefs.os != .windows {
if prefs.os != .windows && (file.ends_with('_windows.c.v') || file.ends_with('_windows.v')) {
return false
}
if (file.ends_with('_linux.c.v') || file.ends_with('_linux.v')) && prefs.os != .linux {
if prefs.os != .linux && (file.ends_with('_linux.c.v') || file.ends_with('_linux.v')) {
return false
}
if (file.ends_with('_darwin.c.v') || file.ends_with('_darwin.v')) && prefs.os != .macos {
return false
}
if (file.ends_with('_macos.c.v') || file.ends_with('_macos.v')) && prefs.os != .macos {
if prefs.os != .macos && (file.ends_with('_darwin.c.v') || file.ends_with('_darwin.v')) {
return false
}
if (file.ends_with('_ios.c.v') || file.ends_with('_ios.v')) && prefs.os != .ios {
@ -147,25 +144,28 @@ pub fn (prefs &Preferences) should_compile_c(file string) bool {
if file.ends_with('_nix.c.v') && prefs.os == .windows {
return false
}
if file.ends_with('_android.c.v') && prefs.os != .android {
if prefs.os != .macos && (file.ends_with('_macos.c.v') || file.ends_with('_macos.v')) {
return false
}
if file.ends_with('_freebsd.c.v') && prefs.os != .freebsd {
if prefs.os == .windows && file.ends_with('_nix.c.v') {
return false
}
if file.ends_with('_openbsd.c.v') && prefs.os != .openbsd {
if prefs.os != .android && file.ends_with('_android.c.v') {
return false
}
if file.ends_with('_netbsd.c.v') && prefs.os != .netbsd {
if prefs.os != .freebsd && file.ends_with('_freebsd.c.v') {
return false
}
if file.ends_with('_dragonfly.c.v') && prefs.os != .dragonfly {
if prefs.os != .openbsd && file.ends_with('_openbsd.c.v') {
return false
}
if file.ends_with('_solaris.c.v') && prefs.os != .solaris {
if prefs.os != .netbsd && file.ends_with('_netbsd.c.v') {
return false
}
if file.ends_with('_x64.v') && prefs.backend != .x64 {
if prefs.os != .dragonfly && file.ends_with('_dragonfly.c.v') {
return false
}
if prefs.os != .solaris && file.ends_with('_solaris.c.v') {
return false
}
return true