Merge branch 'master' of https://github.com/vlang/v into add-rsa

pull/13324/head
Louis Schmieder 2022-02-02 10:07:31 +00:00 committed by GitHub
commit 8f9e37a58f
27 changed files with 519 additions and 125 deletions

View File

@ -55,6 +55,9 @@ jobs:
cd '${{env.MY_V_PATH}}'
ls -la
make
## prebuild cmd/tools/builders/js_builder, to minimise the
## chances of a sporadic "Killed" when running the tests later
./v -b js run examples/hello_world.v
- name: v doctor
run: |
cd '${{env.MY_V_PATH}}'

View File

@ -21,6 +21,7 @@ const (
hide_warnings = '-hide-warnings' in os.args || '-w' in os.args
show_progress = os.getenv('GITHUB_JOB') == '' && '-silent' !in os.args
non_option_args = cmdline.only_non_options(os.args[2..])
is_verbose = os.getenv('VERBOSE') != ''
)
struct CheckResult {
@ -75,7 +76,7 @@ fn main() {
res += mdfile.check()
}
if res.errors == 0 && show_progress {
term.clear_previous_line()
clear_previous_line()
}
if res.warnings > 0 || res.errors > 0 || res.oks > 0 {
println('\nWarnings: $res.warnings | Errors: $res.errors | OKs: $res.oks')
@ -131,9 +132,7 @@ fn eline(file_path string, lnumber int, column int, message string) string {
return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(rtext(' error: $message'))
}
const (
default_command = 'compile'
)
const default_command = 'compile'
struct VCodeExample {
mut:
@ -160,7 +159,7 @@ mut:
fn (mut f MDFile) progress(message string) {
if show_progress {
term.clear_previous_line()
clear_previous_line()
println('File: ${f.path:-30s}, Lines: ${f.lines.len:5}, $message')
}
}
@ -394,6 +393,7 @@ fn (mut f MDFile) debug() {
}
fn cmdexecute(cmd string) int {
verbose_println(cmd)
res := os.execute(cmd)
if res.exit_code < 0 {
return 1
@ -405,6 +405,7 @@ fn cmdexecute(cmd string) int {
}
fn silent_cmdexecute(cmd string) int {
verbose_println(cmd)
res := os.execute(cmd)
return res.exit_code
}
@ -426,6 +427,7 @@ fn (mut f MDFile) check_examples() CheckResult {
}
fname := os.base(f.path).replace('.md', '_md')
uid := rand.ulid()
cfile := os.join_path(os.temp_dir(), '${uid}.c')
vfile := os.join_path(os.temp_dir(), 'check_${fname}_example_${e.sline}__${e.eline}__${uid}.v')
mut should_cleanup_vfile := true
// eprintln('>>> checking example $vfile ...')
@ -438,8 +440,7 @@ fn (mut f MDFile) check_examples() CheckResult {
fmt_res := if nofmt { 0 } else { get_fmt_exit_code(vfile, vexe) }
match command {
'compile' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o x.c ${os.quoted_path(vfile)}')
os.rm('x.c') or {}
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors ${os.quoted_path(vfile)}')
if res != 0 || fmt_res != 0 {
if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to compile'))
@ -454,9 +455,26 @@ fn (mut f MDFile) check_examples() CheckResult {
}
oks++
}
'cgen' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm(cfile) or {}
if res != 0 || fmt_res != 0 {
if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to generate C code'))
}
if fmt_res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example is not formatted'))
}
eprintln(vcontent)
should_cleanup_vfile = false
errors++
continue
}
oks++
}
'globals' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -enable-globals -o x.c ${os.quoted_path(vfile)}')
os.rm('x.c') or {}
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -enable-globals -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm(cfile) or {}
if res != 0 || fmt_res != 0 {
if res != 0 {
eprintln(eline(f.path, e.sline, 0, '`example failed to compile with -enable-globals'))
@ -472,7 +490,8 @@ fn (mut f MDFile) check_examples() CheckResult {
oks++
}
'live' {
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -live -o x.c ${os.quoted_path(vfile)}')
res := cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -live -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm(cfile) or {}
if res != 0 || fmt_res != 0 {
if res != 0 {
eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live'))
@ -488,8 +507,8 @@ fn (mut f MDFile) check_examples() CheckResult {
oks++
}
'failcompile' {
res := silent_cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o x.c ${os.quoted_path(vfile)}')
os.rm('x.c') or {}
res := silent_cmdexecute('${os.quoted_path(vexe)} -w -Wfatal-errors -o ${os.quoted_path(cfile)} ${os.quoted_path(vfile)}')
os.rm(cfile) or {}
if res == 0 || fmt_res != 0 {
if res == 0 {
eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled'))
@ -533,7 +552,7 @@ fn (mut f MDFile) check_examples() CheckResult {
}
'nofmt' {}
else {
eprintln(eline(f.path, e.sline, 0, 'unrecognized command: "$command", use one of: wip/ignore/compile/failcompile/oksyntax/badsyntax'))
eprintln(eline(f.path, e.sline, 0, 'unrecognized command: "$command", use one of: wip/ignore/compile/cgen/failcompile/oksyntax/badsyntax/nofmt'))
should_cleanup_vfile = false
errors++
}
@ -548,3 +567,16 @@ fn (mut f MDFile) check_examples() CheckResult {
oks: oks
}
}
fn verbose_println(message string) {
if is_verbose {
println(message)
}
}
fn clear_previous_line() {
if is_verbose {
return
}
term.clear_previous_line()
}

View File

@ -259,7 +259,15 @@ fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path
file_bak := '${file}.bak'
os.cp(file, file_bak) or {}
}
mut perms_to_restore := u32(0)
$if !windows {
fm := os.inode(file)
perms_to_restore = fm.bitmask()
}
os.mv_by_cp(formatted_file_path, file) or { panic(err) }
$if !windows {
os.chmod(file, int(perms_to_restore)) or { panic(err) }
}
eprintln('Reformatted file: $file')
} else {
eprintln('Already formatted file: $file')

View File

@ -13,6 +13,7 @@ Flags:
NB: There are several special keywords, which you can put after the code fences for v.
These are:
compile - Default, can be omitted. The example will be compiled and formatting is verified.
cgen - The example produces C code, which may not be compilable (when external libs are not installed). Formatting is verified.
live - Compile hot reload examples with the ´-live´ flag set and verify formatting.
ignore - Ignore the example, useful for examples that just use the syntax highlighting
failcompile - Known failing compilation. Useful for examples demonstrating compiler errors.

View File

@ -163,7 +163,7 @@ For more details and troubleshooting, please visit the [vab GitHub repository](h
<!--
NB: there are several special keywords, which you can put after the code fences for v:
compile, live, ignore, failcompile, oksyntax, badsyntax, wip, nofmt
compile, cgen, live, ignore, failcompile, oksyntax, badsyntax, wip, nofmt
For more details, do: `v check-md`
-->
@ -1393,7 +1393,7 @@ println(s)
You can check the current type of a sum type using `is` and its negated form `!is`.
You can do it either in an `if`:
```v
```v cgen
struct Abc {
val string
}
@ -2789,38 +2789,52 @@ For more information, see [Dynamic casts](#dynamic-casts).
#### Interface method definitions
Also unlike Go, an interface may implement a method.
These methods are not implemented by structs which implement that interface.
Also unlike Go, an interface can have it's own methods, similar to how
structs can have their methods. These 'interface methods' do not have
to be implemented, by structs which implement that interface.
They are just a convenient way to write `i.some_function()` instead of
`some_function(i)`, similar to how struct methods can be looked at, as
a convenience for writing `s.xyz()` instead of `xyz(s)`.
When a struct is wrapped in an interface that has implemented a method
with the same name as one implemented by this struct, only the method
implemented on the interface is called.
N.B. This feature is NOT a "default implementation" like in C#.
For example, if a struct `cat` is wrapped in an interface `a`, that has
implemented a method with the same name `speak`, as a method implemented by
the struct, and you do `a.speak()`, *only* the interface method is called:
```v
struct Cat {}
fn (c Cat) speak() string {
return 'meow!'
}
interface Adoptable {}
fn (a Adoptable) speak() string {
return 'adopt me!'
}
fn new_adoptable() Adoptable {
return Cat{}
struct Cat {}
fn (c Cat) speak() string {
return 'meow!'
}
struct Dog {}
fn main() {
cat := Cat{}
assert cat.speak() == 'meow!'
a := new_adoptable()
assert a.speak() == 'adopt me!'
assert dump(cat.speak()) == 'meow!'
//
a := Adoptable(cat)
assert dump(a.speak()) == 'adopt me!' // call Adoptable's `speak`
if a is Cat {
println(a.speak()) // meow!
// Inside this `if` however, V knows that `a` is not just any
// kind of Adoptable, but actually a Cat, so it will use the
// Cat `speak`, NOT the Adoptable `speak`:
dump(a.speak()) // meow!
}
//
b := Adoptable(Dog{})
assert dump(b.speak()) == 'adopt me!' // call Adoptable's `speak`
// if b is Dog {
// dump(b.speak()) // error: unknown method or field: Dog.speak
// }
}
```
@ -5102,40 +5116,42 @@ For all supported options check the latest help:
#### `$if` condition
```v
// Support for multiple conditions in one branch
$if ios || android {
println('Running on a mobile device!')
}
$if linux && x64 {
println('64-bit Linux.')
}
// Usage as expression
os := $if windows { 'Windows' } $else { 'UNIX' }
println('Using $os')
// $else-$if branches
$if tinyc {
println('tinyc')
} $else $if clang {
println('clang')
} $else $if gcc {
println('gcc')
} $else {
println('different compiler')
}
$if test {
println('testing')
}
// v -cg ...
$if debug {
println('debugging')
}
// v -prod ...
$if prod {
println('production build')
}
// v -d option ...
$if option ? {
println('custom option')
fn main() {
// Support for multiple conditions in one branch
$if ios || android {
println('Running on a mobile device!')
}
$if linux && x64 {
println('64-bit Linux.')
}
// Usage as expression
os := $if windows { 'Windows' } $else { 'UNIX' }
println('Using $os')
// $else-$if branches
$if tinyc {
println('tinyc')
} $else $if clang {
println('clang')
} $else $if gcc {
println('gcc')
} $else {
println('different compiler')
}
$if test {
println('testing')
}
// v -cg ...
$if debug {
println('debugging')
}
// v -prod ...
$if prod {
println('production build')
}
// v -d option ...
$if option ? {
println('custom option')
}
}
```

View File

@ -204,7 +204,9 @@ pub fn (mut a array) insert(i int, val voidptr) {
panic('array.insert: index out of range (i == $i, a.len == $a.len)')
}
}
a.ensure_cap(a.len + 1)
if a.len >= a.cap {
a.ensure_cap(a.len + 1)
}
unsafe {
vmemmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size)
a.set_unsafe(i, val)
@ -569,7 +571,9 @@ fn (mut a array) set(i int, val voidptr) {
}
fn (mut a array) push(val voidptr) {
a.ensure_cap(a.len + 1)
if a.len >= a.cap {
a.ensure_cap(a.len + 1)
}
unsafe { vmemmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) }
a.len++
}
@ -578,16 +582,15 @@ fn (mut a array) push(val voidptr) {
// `val` is array.data and user facing usage is `a << [1,2,3]`
[unsafe]
pub fn (mut a3 array) push_many(val voidptr, size int) {
a3.ensure_cap(a3.len + size)
if a3.data == val && !isnil(a3.data) {
// handle `arr << arr`
copy := a3.clone()
a3.ensure_cap(a3.len + size)
unsafe {
// vmemcpy(a.data, copy.data, copy.element_size * copy.len)
vmemcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size)
}
} else {
a3.ensure_cap(a3.len + size)
if !isnil(a3.data) && !isnil(val) {
unsafe { vmemcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) }
}

View File

@ -1166,7 +1166,9 @@ fn test_array_int_pop() {
assert a.len == 3
assert z == 4
x1 := a.pop()
println(x1)
x2 := a.pop()
println(x2)
final := a.pop()
assert final == 1
}

View File

@ -99,6 +99,7 @@ fn test_hex() {
b := 1234
assert b.hex() == '4d2'
b1 := -1
println(b1)
// assert b1.hex() == 'ffffffff'
// unsigned tests
// assert u8(12).hex() == '0c'

View File

@ -372,6 +372,9 @@ fn test_map_assign() {
'r': u16(6)
's': 5
}}
println(a)
println(b)
println(c)
}
fn test_postfix_op_directly() {

View File

@ -1,4 +1,4 @@
import strings
// import strings
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
@ -636,6 +636,7 @@ fn test_for_loop_two() {
fn test_quote() {
a := `'`
println(a)
println('testing double quotes')
b := 'hi'
assert b == 'hi'
@ -727,6 +728,7 @@ fn test_raw() {
fn test_raw_with_quotes() {
raw := r"some'" + r'"thing' // " should be escaped in the generated C code
println(raw)
// assert raw[0] == `s`
// assert raw[5] == `"`
// assert raw[6] == `t`

View File

@ -7,7 +7,7 @@ user's keyboard/mouse input.
## Example:
```v
```v cgen
module main
import gg

View File

@ -20,6 +20,22 @@ pub:
execute bool
}
// bitmask returns a 3 bit sequence in the order RWE where
// the bit is set to 1 if the value is true or 0 otherwise.
pub fn (p FilePermission) bitmask() u32 {
mut mask := u32(0)
if p.read {
mask |= 4
}
if p.write {
mask |= 2
}
if p.execute {
mask |= 1
}
return mask
}
struct FileMode {
pub:
typ FileType
@ -28,6 +44,12 @@ pub:
others FilePermission
}
// bitmask returns a 9 bit sequence in the order owner + group + others.
// This is a valid bitmask to use with `os.chmod`.
pub fn (m FileMode) bitmask() u32 {
return m.owner.bitmask() << 6 | m.group.bitmask() << 3 | m.others.bitmask()
}
// inode returns the mode of the file/inode containing inode type and permission information
// it supports windows for regular files but it doesn't matter if you use owner, group or others when checking permissions on windows
pub fn inode(path string) FileMode {

View File

@ -41,3 +41,16 @@ fn test_inode_file_owner_permission() {
assert mode.owner.write
assert !mode.owner.execute
}
fn test_inode_file_permissions_bitmask() {
if user_os() == 'windows' {
println('> skipping ${@FN} on windows')
return
}
filename := './test3.txt'
mut file := open_file(filename, 'w', 0o641) or { return }
file.close()
mode := inode(filename)
rm(filename) or {}
assert mode.bitmask() == 0o641
}

View File

@ -2024,6 +2024,11 @@ pub fn (mut re RE) match_base(in_txt &byte, in_txt_len int) (int, int) {
re.prog[state.pc].group_rep++ // increase repetitions
// println("GROUP $group_index END ${re.prog[state.pc].group_rep}")
if re.prog[state.pc].group_rep > in_txt_len - 1 {
m_state = .ist_quant_ng
continue
}
m_state = .ist_quant_pg
continue
}

View File

@ -160,6 +160,11 @@ match_test_suite = [
TestItem{"a", r"\S+",0,1},
TestItem{"aaaa", r"\S+",0,4},
TestItem{"aaaa ", r"\S+",0,4},
// multiple dot char
TestItem{"aba", r"a*(b*)*a",0,3},
TestItem{"/*x*/", r"/\**(.*)\**/",0,5},
TestItem{"/*x*/", r"/*(.*)*/",0,5},
]
)
@ -591,6 +596,21 @@ fn test_regex_func(){
}
}
fn my_repl_1(re regex.RE, in_txt string, start int, end int) string {
s0 := re.get_group_by_id(in_txt,0)
println("[$start, $end] => ${s0}")
return "a" + s0.to_upper()
}
fn test_regex_func_replace1(){
txt := "abbabbbabbbbaabba"
query := r"a(b+)"
mut re := regex.regex_opt(query) or { panic(err) }
result := re.replace_by_fn(txt, my_repl_1)
assert result == "aBBaBBBaBBBBaaBBa"
}
fn my_repl(re regex.RE, in_txt string, start int, end int) string {
s0 := re.get_group_by_id(in_txt,0)[0..1] + "X"
s1 := re.get_group_by_id(in_txt,1)[0..1] + "X"
@ -598,7 +618,6 @@ fn my_repl(re regex.RE, in_txt string, start int, end int) string {
return "${s0}${s1}${s2}"
}
// test regex replace function
fn test_regex_func_replace(){
filler := "E il primo dei tre regni dell'Oltretomba cristiano visitato da Dante nel corso del viaggio, con la guida di Virgilio."

View File

@ -212,7 +212,7 @@ pub fn (mut re RE) find(in_txt string) (int, int) {
[direct_array_access]
pub fn (mut re RE) find_from(in_txt string, start int) (int, int) {
old_flag := re.flag
re.flag |= f_src // enable search mode
// re.flag |= f_src // enable search mode
mut i := start
if i < 0 {

View File

@ -11,7 +11,7 @@ Each `.h` file in the sokol source code is well-documented as can be seen here:
## Example from `@VROOTDIR/examples/sokol/sounds/simple_sin_tones.v`:
```v
```v cgen
import time
import math
import sokol.audio

View File

@ -446,6 +446,18 @@ pub fn (t &Table) find_method_with_embeds(sym &TypeSymbol, method_name string) ?
}
}
pub fn (t &Table) get_embed_methods(sym &TypeSymbol) []Fn {
mut methods := []Fn{}
if sym.info is Struct {
for embed in sym.info.embeds {
embed_sym := t.sym(embed)
methods << embed_sym.methods
methods << t.get_embed_methods(embed_sym)
}
}
return methods
}
fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructField {
if sym.kind != .aggregate {
t.panic('Unexpected type symbol: $sym.kind')
@ -1428,7 +1440,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
if typ == 0 {
return none
}
return typ.derive_add_muls(generic_type).clear_flag(.generic)
if typ.has_flag(.generic) {
return typ.derive_add_muls(generic_type).set_flag(.generic)
} else {
return typ.derive_add_muls(generic_type).clear_flag(.generic)
}
}
match mut sym.info {
Array {
@ -1443,7 +1459,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
}
if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) {
idx := t.find_or_register_array_with_dims(typ, dims)
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
if typ.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
}
}
}
ArrayFixed {
@ -1451,7 +1471,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
concrete_types)
{
idx := t.find_or_register_array_fixed(typ, sym.info.size, None{})
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
if typ.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
}
}
}
Chan {
@ -1459,16 +1483,24 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
concrete_types)
{
idx := t.find_or_register_chan(typ, typ.nr_muls() > 0)
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
if typ.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
}
}
}
FnType {
mut func := sym.info.func
mut has_generic := false
if func.return_type.has_flag(.generic) {
if typ := t.resolve_generic_to_concrete(func.return_type, generic_names,
concrete_types)
{
func.return_type = typ
if typ.has_flag(.generic) {
has_generic = true
}
}
}
func.params = func.params.clone()
@ -1478,12 +1510,19 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
concrete_types)
{
param.typ = typ
if typ.has_flag(.generic) {
has_generic = true
}
}
}
}
func.name = ''
idx := t.find_or_register_fn_type('', func, true, false)
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
if has_generic {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
}
}
MultiReturn {
mut types := []Type{}
@ -1498,7 +1537,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
}
if type_changed {
idx := t.find_or_register_multi_return(types)
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
if types.any(it.has_flag(.generic)) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
}
}
}
Map {
@ -1519,7 +1562,11 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
}
if type_changed {
idx := t.find_or_register_map(unwrapped_key_type, unwrapped_value_type)
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
if unwrapped_key_type.has_flag(.generic) || unwrapped_value_type.has_flag(.generic) {
return new_type(idx).derive_add_muls(generic_type).set_flag(.generic)
} else {
return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic)
}
}
}
Struct, Interface, SumType {

View File

@ -963,6 +963,13 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
}
if func.generic_names.len > 0 {
if has_generic {
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
node.concrete_types)
{
if typ.has_flag(.generic) {
node.return_type = typ
}
}
return node.return_type
} else if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
concrete_types)

View File

@ -2471,10 +2471,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
}
// Generic dereferencing logic
neither_void := ast.voidptr_type !in [got_type, expected_type]
to_shared := expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
&& !expected_type.has_flag(.optional)
// from_shared := got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f)
if to_shared {
if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
&& !expected_type.has_flag(.optional) {
shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*`
if got_type_raw.is_ptr() {
g.error('cannot convert reference to `shared`', expr.pos())
@ -2492,6 +2490,13 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
g.is_shared = old_is_shared
g.writeln('}, sizeof($shared_styp))')
return
} else if got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) {
if expected_type.is_ptr() {
g.write('&')
}
g.expr(expr)
g.write('->val')
return
}
if got_is_ptr && !expected_is_ptr && neither_void && exp_sym.kind != .placeholder
&& expr !is ast.InfixExpr {
@ -3604,10 +3609,15 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
}
// struct embedding
if sym.info in [ast.Struct, ast.Aggregate] {
for embed in node.from_embed_types {
for i, embed in node.from_embed_types {
embed_sym := g.table.sym(embed)
embed_name := embed_sym.embed_name()
if node.expr_type.is_ptr() {
is_left_ptr := if i == 0 {
node.expr_type.is_ptr()
} else {
node.from_embed_types[i - 1].is_ptr()
}
if is_left_ptr {
g.write('->')
} else {
g.write('.')
@ -6310,10 +6320,6 @@ fn (mut g Gen) size_of(node ast.SizeOf) {
g.write('sizeof(${util.no_dots(styp)})')
}
fn (g &Gen) is_importing_os() bool {
return 'os' in g.table.imports
}
fn (mut g Gen) go_expr(node ast.GoExpr) {
line := g.go_before_stmt(0)
mut handle := ''
@ -6797,16 +6803,13 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
}
else {}
}
if st_sym.info is ast.Struct {
for embed in st_sym.info.embeds {
embed_sym := g.table.sym(embed)
for embed_method in embed_sym.methods {
if embed_method.name !in method_names {
methods << embed_method
}
}
t_methods := g.table.get_embed_methods(st_sym)
for t_method in t_methods {
if t_method.name !in method_names {
methods << t_method
}
}
for method in methods {
mut name := method.name
if inter_info.parent_type.has_flag(.generic) {
@ -6834,6 +6837,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
styp := g.cc_type(method.params[0].typ, true)
mut method_call := '${styp}_$name'
if !method.params[0].typ.is_ptr() {
method_call = '${cctype}_$name'
// inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); }
iwpostfix := '_Interface_${interface_name}_method_wrapper'
methods_wrapper.write_string('static inline ${g.typ(method.return_type)} ${cctype}_$name${iwpostfix}(')
@ -6865,9 +6869,13 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x)
embed_sym := g.table.sym(embed_types.last())
method_name := '${embed_sym.cname}_$method.name'
methods_wrapper.write_string('${method_name}(${fargs[0]}')
for embed in embed_types {
for idx_embed, embed in embed_types {
esym := g.table.sym(embed)
methods_wrapper.write_string('->$esym.embed_name()')
if idx_embed == 0 || embed_types[idx_embed - 1].is_any_kind_of_pointer() {
methods_wrapper.write_string('->$esym.embed_name()')
} else {
methods_wrapper.write_string('.$esym.embed_name()')
}
}
methods_wrapper.writeln('${fargs[1..].join(', ')});')
} else {

View File

@ -0,0 +1,26 @@
fn test_generics_with_complex_nested_generics_type() {
mut buf := []byte{}
initial<string, u64>(buf)
}
fn initial<K, V>(buf []byte) map[K]V {
mut ret := map[K]V{}
for _ in 0 .. 3 {
k := get<K>(buf)
v := get<V>(buf)
ret[k] = v
}
println(ret)
assert '$ret' == "{'get': 22}"
return ret
}
fn get<T>(buf []byte) T {
$if T is string {
return buf.bytestr() + 'get'
} $else $if T is u64 {
return u64(22)
} $else {
panic('oops!')
}
}

View File

@ -0,0 +1,91 @@
fn test_interface_with_multi_nested_embed() {
mut win := &Window{}
mut ll := &LinearLayout{}
mut lbl := &Label{
x: 10
y: 20
}
ll.add(mut lbl)
win.add(mut ll)
win.init()
}
[heap]
pub struct Window {
mut:
initables []&Initable
}
interface Initable {
mut:
init(&Window)
}
pub fn (mut w Window) add(mut initable Initable) {
w.initables << initable
}
interface Container {
mut:
layout()
}
fn (mut w Window) init() {
for wd in w.initables {
if mut wd is Container {
mut c := wd as Container
c.layout()
}
}
}
pub struct Rect {
mut:
x int
y int
}
pub fn (r Rect) get_pos() (int, int) {
return r.x, r.y
}
pub struct LayoutBase {
Rect
}
pub struct Base {
LayoutBase
}
pub fn (mut b Base) init(window &Window) {}
[heap]
pub struct Label {
Base
}
pub interface Layoutable {
get_pos() (int, int)
}
[heap]
pub struct LinearLayout {
Base
mut:
layoutables []Layoutable
}
pub fn (mut ll LinearLayout) add(mut l Layoutable) {
ll.layoutables << l
}
pub fn (mut ll LinearLayout) layout() {
for mut wl in ll.layoutables {
x, y := wl.get_pos()
println('$x, $y')
assert x == 10
assert y == 20
}
}

View File

@ -0,0 +1,57 @@
fn test_interface_with_multi_nested_embed() {
mut ll := &LinearLayout{}
mut lbl := &Label{
x: 10
y: 20
}
ll.add(mut lbl)
println(ll)
assert ll.x == 10
assert ll.y == 20
}
pub struct Rect {
mut:
x int
y int
}
pub fn (r Rect) get_pos() (int, int) {
return r.x, r.y
}
pub struct LayoutBase {
Rect
}
pub struct Base {
LayoutBase
}
pub fn (mut b Base) init() {}
[heap]
pub struct Label {
Base
}
pub interface Layoutable {
get_pos() (int, int)
mut:
init()
}
[heap]
pub struct LinearLayout {
Base
mut:
layoutables []Layoutable
}
pub fn (mut ll LinearLayout) add(mut l Layoutable) {
x, y := l.get_pos()
ll.x += x
ll.y += y
}

View File

@ -0,0 +1,22 @@
struct AA {
b shared BB
}
struct BB {
a &int
}
struct CC {
a BB
}
fn test_struct_shared_field_init() {
a := 3
table := &AA{
b: BB{&a}
}
c := CC{
a: table.b
}
assert *c.a.a == 3
}

View File

@ -7,6 +7,7 @@ import io
const (
sport = 12380
localserver = 'localhost:$sport'
exit_after_time = 12000 // milliseconds
vexe = os.getenv('VEXE')
vweb_logfile = os.getenv('VWEB_LOGFILE')
@ -116,7 +117,7 @@ fn assert_common_http_headers(x http.Response) ? {
}
fn test_http_client_index() ? {
x := http.get('http://127.0.0.1:$sport/') or { panic(err) }
x := http.get('http://$localserver/') or { panic(err) }
assert_common_http_headers(x) ?
assert x.header.get(.content_type) ? == 'text/plain'
assert x.text == 'Welcome to VWeb'
@ -124,9 +125,9 @@ fn test_http_client_index() ? {
fn test_http_client_404() ? {
url_404_list := [
'http://127.0.0.1:$sport/zxcnbnm',
'http://127.0.0.1:$sport/JHKAJA',
'http://127.0.0.1:$sport/unknown',
'http://$localserver/zxcnbnm',
'http://$localserver/JHKAJA',
'http://$localserver/unknown',
]
for url in url_404_list {
res := http.get(url) or { panic(err) }
@ -135,39 +136,39 @@ fn test_http_client_404() ? {
}
fn test_http_client_simple() ? {
x := http.get('http://127.0.0.1:$sport/simple') or { panic(err) }
x := http.get('http://$localserver/simple') or { panic(err) }
assert_common_http_headers(x) ?
assert x.header.get(.content_type) ? == 'text/plain'
assert x.text == 'A simple result'
}
fn test_http_client_html_page() ? {
x := http.get('http://127.0.0.1:$sport/html_page') or { panic(err) }
x := http.get('http://$localserver/html_page') or { panic(err) }
assert_common_http_headers(x) ?
assert x.header.get(.content_type) ? == 'text/html'
assert x.text == '<h1>ok</h1>'
}
fn test_http_client_settings_page() ? {
x := http.get('http://127.0.0.1:$sport/bilbo/settings') or { panic(err) }
x := http.get('http://$localserver/bilbo/settings') or { panic(err) }
assert_common_http_headers(x) ?
assert x.text == 'username: bilbo'
//
y := http.get('http://127.0.0.1:$sport/kent/settings') or { panic(err) }
y := http.get('http://$localserver/kent/settings') or { panic(err) }
assert_common_http_headers(y) ?
assert y.text == 'username: kent'
}
fn test_http_client_user_repo_settings_page() ? {
x := http.get('http://127.0.0.1:$sport/bilbo/gostamp/settings') or { panic(err) }
x := http.get('http://$localserver/bilbo/gostamp/settings') or { panic(err) }
assert_common_http_headers(x) ?
assert x.text == 'username: bilbo | repository: gostamp'
//
y := http.get('http://127.0.0.1:$sport/kent/golang/settings') or { panic(err) }
y := http.get('http://$localserver/kent/golang/settings') or { panic(err) }
assert_common_http_headers(y) ?
assert y.text == 'username: kent | repository: golang'
//
z := http.get('http://127.0.0.1:$sport/missing/golang/settings') or { panic(err) }
z := http.get('http://$localserver/missing/golang/settings') or { panic(err) }
assert z.status() == .not_found
}
@ -182,7 +183,7 @@ fn test_http_client_json_post() ? {
age: 123
}
json_for_ouser := json.encode(ouser)
mut x := http.post_json('http://127.0.0.1:$sport/json_echo', json_for_ouser) or { panic(err) }
mut x := http.post_json('http://$localserver/json_echo', json_for_ouser) or { panic(err) }
$if debug_net_socket_client ? {
eprintln('/json_echo endpoint response: $x')
}
@ -191,7 +192,7 @@ fn test_http_client_json_post() ? {
nuser := json.decode(User, x.text) or { User{} }
assert '$ouser' == '$nuser'
//
x = http.post_json('http://127.0.0.1:$sport/json', json_for_ouser) or { panic(err) }
x = http.post_json('http://$localserver/json', json_for_ouser) or { panic(err) }
$if debug_net_socket_client ? {
eprintln('/json endpoint response: $x')
}
@ -213,7 +214,7 @@ $contents\r
--$boundary--\r
'
mut x := http.fetch(
url: 'http://127.0.0.1:$sport/form_echo'
url: 'http://$localserver/form_echo'
method: .post
header: http.new_header(
key: .content_type
@ -228,7 +229,7 @@ $contents\r
}
fn test_http_client_shutdown_does_not_work_without_a_cookie() {
x := http.get('http://127.0.0.1:$sport/shutdown') or {
x := http.get('http://$localserver/shutdown') or {
assert err.msg == ''
return
}
@ -240,7 +241,7 @@ fn testsuite_end() {
// This test is guaranteed to be called last.
// It sends a request to the server to shutdown.
x := http.fetch(
url: 'http://127.0.0.1:$sport/shutdown'
url: 'http://$localserver/shutdown'
method: .get
cookies: {
'skey': 'superman'
@ -268,7 +269,7 @@ fn simple_tcp_client(config SimpleTcpClientConfig) ?string {
mut tries := 0
for tries < config.retries {
tries++
client = net.dial_tcp('127.0.0.1:$sport') or {
client = net.dial_tcp(localserver) or {
if tries > config.retries {
return err
}

View File

@ -43,9 +43,8 @@ fn main() {
timeout: timeout
global_config: config
}
eprintln('>> webserver: started on http://127.0.0.1:$app.port/ , with maximum runtime of $app.timeout milliseconds.')
// vweb.run<App>(mut app, http_port)
vweb.run(app, http_port)
eprintln('>> webserver: started on http://localhost:$app.port/ , with maximum runtime of $app.timeout milliseconds.')
vweb.run_at(app, 'localhost', http_port)
}
// pub fn (mut app App) init_server() {

View File

@ -376,10 +376,16 @@ interface DbInterface {
db voidptr
}
// run_app
[manualfree]
// run - start a new VWeb server, listening to all available addresses, at the specified `port`
pub fn run<T>(global_app &T, port int) {
mut l := net.listen_tcp(.ip6, ':$port') or { panic('failed to listen $err.code $err') }
run_at<T>(global_app, '', port)
}
// run_at - start a new VWeb server, listening only on a specific address `host`, at the specified `port`
// Example: `vweb.run_at(app, 'localhost', 8099)`
[manualfree]
pub fn run_at<T>(global_app &T, host string, port int) {
mut l := net.listen_tcp(.ip, '$host:$port') or { panic('failed to listen $err.code $err') }
// Parsing methods attributes
mut routes := map[string]Route{}