force C function definitions

pull/2871/head
Alexander Medvednikov 2019-11-24 06:27:02 +03:00
parent 1f93bb5a9a
commit e9e931fe4a
22 changed files with 227 additions and 84 deletions

View File

@ -19,6 +19,7 @@
+ bring back generics
- bring back forum/blog and vpm
- wrap up orm
+ bring back vweb
- fix vorum, migrate to orm
- wrap up memory management
- remove all compiler memory leaks
@ -42,6 +43,6 @@
+ bare metal support
+ inline assembly
+ x64 machine code generation (ELF)
- require implicit C.fn definitions, add all missing definitions
- require explicit C.fn definitions, add all missing definitions

View File

@ -132,6 +132,7 @@ TODO
print_backtrace()
#endif
*/
free(C.malloc(n))
ptr := C.malloc(n)
if isnil(ptr) {
panic('malloc($n) failed')

View File

@ -29,6 +29,66 @@ fn C.realpath(byteptr, byteptr) &char
fn C.chmod(byteptr, int)
fn C.printf(byteptr, ...byteptr)
fn C.fputs(byteptr)
fn C.fflush(byteptr) int
// TODO define args in these functions
fn C.fseek() int
fn C.fopen() int
fn C.fwrite() int
fn C.fclose() int
fn C.pclose() int
fn C.system() int
fn C.setenv() int
fn C.unsetenv() int
fn C.access() int
fn C.remove() int
fn C.rmdir() int
fn C.chdir() int
fn C.fread() int
fn C.rewind() int
fn C.stat() int
fn C.rename() int
fn C.fgets() int
fn C.memset() int
fn C.sigemptyset() int
fn C.getcwd() int
fn C.signal() int
fn C.mktime() int
fn C.gettimeofday() int
fn C.sleep() int
fn C.usleep() int
fn C.opendir() voidptr
fn C.closedir() int
fn C.mkdir() int
fn C.srand() int
fn C.atof() int
fn C.tolower() int
fn C.toupper() int
fn C.getchar() int
fn C.strerror() *C.char
fn C.snprintf() int
fn C.fprintf(byteptr, ...byteptr)
fn C.WIFEXITED() bool
fn C.WEXITSTATUS() int
fn C.WIFSIGNALED() bool
fn C.WTERMSIG() int
fn C.DEFAULT_LE() bool
fn C.DEFAULT_EQ() bool
fn C.DEFAULT_GT() bool
fn C.DEFAULT_EQUAL() bool
fn C.DEFAULT_NOT_EQUAL() bool
fn C.DEFAULT_LT() bool
fn C.DEFAULT_GE() bool
fn C.isatty() int
// Windows
fn C._setmode(int, int)
fn C._fileno(int) int
@ -42,3 +102,4 @@ fn C.ReadFile(hFile voidptr, lpBuffer voidptr, nNumberOfBytesToRead u32, lpNumbe
fn C.GetFileAttributesW(lpFileName byteptr) u32
fn C.RegQueryValueExW(hKey voidptr, lpValueName &u16, lpReserved &u32, lpType &u32, lpData byteptr, lpcbData &u32) int
fn C.RegOpenKeyExW(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) int
fn C.RemoveDirectory() int

View File

@ -95,6 +95,7 @@ pub fn tos2(s byteptr) string {
}
}
// Same as `tos2`, but for char*, to avoid warnings
pub fn tos3(s *C.char) string {
if s == 0 {
panic('tos3: nil string')

View File

@ -37,6 +37,8 @@ fn (cb &Clipboard) has_ownership() bool {
return false
}
fn C.OSAtomicCompareAndSwapLong()
fn (cb &Clipboard) set_text(text string) bool {
#NSString *ns_clip;
mut ret := false

View File

@ -1266,7 +1266,7 @@ fn replace_generic_type(gen_type string, ti &TypeInst) string {
args_r << replace_generic_type(arg, ti)
}
mut t := 'fn (' + args_r.join(',') + ')'
if ret_t.len > 0 {
if ret_t.len > 0 {
t += ' ' + replace_generic_type(ret_t, ti)
}
typ = t
@ -1379,7 +1379,7 @@ fn (p mut Parser) register_multi_return_stuct(types []string) string {
return typ
}
// save the tokens for the generic funciton body (between `{}`)
// save the tokens for the generic funciton body (between `{}`)
// the function signature isn't saved, it is reconstructed from Fn
fn (p mut Parser) save_generic_tmpl(f mut Fn, pos int) {
mut cbr_depth := 1

View File

@ -1555,8 +1555,6 @@ fn (p mut Parser) get_const_type(name string, is_ptr bool) string {
}
fn (p mut Parser) get_c_func_type(name string) string {
//p.warn(name + ' ' + p.expected_type)
//}
f := Fn {
name: name
is_c: true
@ -1564,15 +1562,15 @@ fn (p mut Parser) get_c_func_type(name string) string {
p.is_c_fn_call = true
p.fn_call(mut f, 0, '', '')
p.is_c_fn_call = false
// Try looking it up. Maybe its defined with "C.fn_name() fn_type",
// then we know what type it returns
// C functions must be defined with `C.fn_name() fn_type`
cfn := p.table.find_fn(name) or {
// Not Found? Return 'void*'
//return 'cvoid' //'void*'
//if p.expected_type != '' && p.expected_type != 'void' {
//p.warn('\n e=$p.expected_type define imported C function with ' +
//'`fn C.$name([args]) [return_type]`\n')
//}
// Is the user trying to do `var := C.foo()` or `bar(C.foo())`
// without declaring `foo`?
// Do not allow it.
if !name.starts_with('gl') && !name.starts_with('glad') {
p.error('undefined C function `$f.name`\n' +
'define it with `fn C.$name([args]) [return_type]`')
}
return 'void*'
}
// println("C fn $name has type $cfn.typ")

View File

@ -89,7 +89,7 @@ pub fn (g mut Gen) generate_elf_footer() {
g.write64_at(file_size, g.file_size_pos+8)
// Create the binary
f := os.create(g.out_name) or { panic(err) }
C.chmod(g.out_name.str, 0775)
os.chmod(g.out_name, 0775)
f.write_bytes(g.buf.data, g.buf.len)
f.close()
println('x64 elf binary has been successfully generated')

View File

@ -8,6 +8,8 @@ module rand
#flag darwin -framework Security
fn C.SecRandomCopyBytes() int
pub fn read(bytes_needed int) ?[]byte {
mut buffer := malloc(bytes_needed)
status := C.SecRandomCopyBytes(0, bytes_needed, buffer)

View File

@ -43,6 +43,8 @@ pub fn get_array<T>(p Params, name string, def T) []T {
return []
}
fn C.map_set() // TODO remove hack
// TODO: make this a method after generics are fixed.
pub fn get_map<T>(p Params, name string, valueTyp T) map[string]T {
param, _ := p.get_param(name, "")
@ -55,6 +57,7 @@ pub fn get_map<T>(p Params, name string, valueTyp T) map[string]T {
keys = C.new_array_from_c_array_no_alloc(len, len, sizeof(T), param.keys)
for i, key in keys {
//the most simple way to set map value without knowing the typ
// TODO remove
C.map_set(&ret, key, param.value + i * sizeof(T))
}
}

View File

@ -34,6 +34,10 @@ import (
#include "ft2build.h"
#include FT_FREETYPE_H
fn C.FT_Init_FreeType() voidptr
fn C.FT_New_Face() voidptr
fn C.FT_Set_Pixel_Sizes()
const (

View File

@ -153,6 +153,11 @@ pub fn (s Shader) use() {
gl.use_program(s.program_id)
}
fn C.glGetUniformLocation() int
fn C.glUniformMatrix4fv()
fn C.glUniform1i()
fn C.glUniform3f()
pub fn (s Shader) uni_location(key string) int {
return C.glGetUniformLocation(s.program_id, key.str)
}

View File

@ -4,7 +4,7 @@
module http
import strings
import strings
// On linux, prefer a localy build openssl, because it is
// much more likely for it to be newer, than the system
@ -24,67 +24,87 @@ import strings
#include <openssl/ssl.h>
struct C.SSL {
}
}
fn C.SSL_library_init()
fn C.TLSv1_2_method() voidptr
fn C.SSL_CTX_set_options()
fn C.SSL_CTX_new() voidptr
fn C.SSL_CTX_set_verify_depth()
fn C.SSL_CTX_load_verify_locations() int
fn C.BIO_new_ssl_connect() voidptr
fn C.BIO_set_conn_hostname() int
fn C.BIO_get_ssl()
fn C.SSL_set_cipher_list() int
fn C.BIO_do_connect() int
fn C.BIO_do_handshake() int
fn C.SSL_get_peer_certificate() int
fn C.SSL_get_verify_result() int
fn C.SSL_set_tlsext_host_name() int
fn C.BIO_puts()
fn C.BIO_read()
fn C.BIO_free_all()
fn C.SSL_CTX_free()
fn init() int {
C.SSL_library_init()
C.SSL_library_init()
return 1
}
fn (req &Request) ssl_do(port int, method, host_name, path string) ?Response {
//ssl_method := C.SSLv23_method()
ssl_method := C.TLSv1_2_method()
if isnil(method) {
}
ctx := C.SSL_CTX_new(ssl_method)
if isnil(ctx) {
}
C.SSL_CTX_set_verify_depth(ctx, 4)
flags := C.SSL_OP_NO_SSLv2 | C.SSL_OP_NO_SSLv3 | C.SSL_OP_NO_COMPRESSION
C.SSL_CTX_set_options(ctx, flags)
mut res := C.SSL_CTX_load_verify_locations(ctx, 'random-org-chain.pem', 0)
//ssl_method := C.SSLv23_method()
ssl_method := C.TLSv1_2_method()
if isnil(method) {
}
ctx := C.SSL_CTX_new(ssl_method)
if isnil(ctx) {
}
C.SSL_CTX_set_verify_depth(ctx, 4)
flags := C.SSL_OP_NO_SSLv2 | C.SSL_OP_NO_SSLv3 | C.SSL_OP_NO_COMPRESSION
C.SSL_CTX_set_options(ctx, flags)
mut res := C.SSL_CTX_load_verify_locations(ctx, 'random-org-chain.pem', 0)
if res != 1 {
}
web := C.BIO_new_ssl_connect(ctx)
if isnil(ctx) {
}
}
web := C.BIO_new_ssl_connect(ctx)
if isnil(ctx) {
}
addr := host_name + ':' + port.str()
res = C.BIO_set_conn_hostname(web, addr.str)
res = C.BIO_set_conn_hostname(web, addr.str)
if res != 1 {
}
ssl := &C.SSL{!}
C.BIO_get_ssl(web, &ssl)
if isnil(ssl) {
}
preferred_ciphers := 'HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4'
res = C.SSL_set_cipher_list(ssl, preferred_ciphers.str)
}
ssl := &C.SSL{!}
C.BIO_get_ssl(web, &ssl)
if isnil(ssl) {
}
preferred_ciphers := 'HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4'
res = C.SSL_set_cipher_list(ssl, preferred_ciphers.str)
if res != 1 {
}
res = C.SSL_set_tlsext_host_name(ssl, host_name.str)
res = C.BIO_do_connect(web)
res = C.BIO_do_handshake(web)
cert := C.SSL_get_peer_certificate(ssl)
res = C.SSL_get_verify_result(ssl)
}
res = C.SSL_set_tlsext_host_name(ssl, host_name.str)
res = C.BIO_do_connect(web)
res = C.BIO_do_handshake(web)
cert := C.SSL_get_peer_certificate(ssl)
res = C.SSL_get_verify_result(ssl)
///////
s := req.build_request_headers(method, host_name, path)
C.BIO_puts(web, s.str)
mut sb := strings.new_builder(100)
C.BIO_puts(web, s.str)
mut sb := strings.new_builder(100)
for {
buff := [1536]byte
len := int(C.BIO_read(web, buff, 1536) )
if len > 0 {
sb.write(tos(buff, len))
}
buff := [1536]byte
len := int(C.BIO_read(web, buff, 1536) )
if len > 0 {
sb.write(tos(buff, len))
}
else {
break
}
}
if !isnil(web) {
break
}
}
if !isnil(web) {
C.BIO_free_all(web)
}
if !isnil(ctx) {
C.SSL_CTX_free(ctx)
}
if !isnil(ctx) {
C.SSL_CTX_free(ctx)
}
return parse_response(sb.str())

View File

@ -97,6 +97,13 @@ fn jsdecode_string(root &C.cJSON) string {
return tos_clone(root.valuestring)// , _strlen(root.valuestring))
}
fn C.cJSON_IsTrue() bool
fn C.cJSON_CreateNumber() &C.cJSON
fn C.cJSON_CreateBool() &C.cJSON
fn C.cJSON_CreateString() &C.cJSON
fn C.cJSON_Parse() &C.cJSON
fn C.cJSON_PrintUnformatted() byteptr
fn jsdecode_bool(root &C.cJSON) bool {
if isnil(root) {
return false

View File

@ -28,6 +28,7 @@ fn C.lgamma(x f64) f64
fn C.pow(x f64, y f64) f64
fn C.round(x f64) f64
fn C.sin(x f64) f64
fn C.sinh(x f64) f64
fn C.sqrt(x f64) f64
fn C.tgamma(x f64) f64
fn C.tan(x f64) f64

View File

@ -1,6 +1,8 @@
module net
// hostname returns the host name reported by the kernel.
fn C.gethostname() int
// hostname returns the host name reported by the kernel.
pub fn hostname() ?string {
mut name := [256]byte
// https://www.ietf.org/rfc/rfc1035.txt

View File

@ -36,15 +36,31 @@ mut:
}
struct C.sockaddr_storage {}
fn C.socket() int
fn C.setsockopt() int
fn C.htonl() int
fn C.htons() int
fn C.bind() int
fn C.listen() int
fn C.accept() int
fn C.getaddrinfo() int
fn C.connect() int
fn C.send() int
fn C.recv() int
fn C.read() int
fn C.shutdown() int
fn C.close() int
fn C.ntohs() int
fn C.getsockname() int
// create socket
pub fn socket(family int, _type int, proto int) ?Socket {
pub fn new_socket(family int, _type int, proto int) ?Socket {
sockfd := C.socket(family, _type, proto)
one:=1
// This is needed so that there are no problems with reusing the
// same port after the application exits.
C.setsockopt(sockfd, C.SOL_SOCKET, C.SO_REUSEADDR, &one, sizeof(int))
C.setsockopt(sockfd, C.SOL_SOCKET, C.SO_REUSEADDR, &one, 4)//sizeof(int))
if sockfd == 0 {
return error('net.socket: failed')
}
@ -58,16 +74,16 @@ pub fn socket(family int, _type int, proto int) ?Socket {
}
pub fn socket_udp() ?Socket {
return socket(C.AF_INET, C.SOCK_DGRAM, C.IPPROTO_UDP)
return new_socket(C.AF_INET, C.SOCK_DGRAM, C.IPPROTO_UDP)
}
// set socket options
pub fn (s Socket) setsockopt(level int, optname int, optvalue &int) ?int {
res := C.setsockopt(s.sockfd, level, optname, optvalue, C.sizeof(optvalue))
res := C.setsockopt(s.sockfd, level, optname, optvalue, sizeof(int))
if res < 0 {
return error('net.setsocketopt: failed with $res')
}
return int(res)
return res
}
// bind socket to port
@ -77,7 +93,7 @@ pub fn (s Socket) bind(port int) ?int {
addr.sin_port = C.htons(port)
addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY)
size := 16 // sizeof(C.sockaddr_in)
res := int(C.bind(s.sockfd, &addr, size))
res := C.bind(s.sockfd, &addr, size)
if res < 0 {
return error('net.bind: failed with $res')
}
@ -87,7 +103,7 @@ pub fn (s Socket) bind(port int) ?int {
// put socket into passive mode and wait to receive
pub fn (s Socket) listen() ?int {
backlog := 128
res := int(C.listen(s.sockfd, backlog))
res := C.listen(s.sockfd, backlog)
if res < 0 {
return error('net.listen: failed with $res')
}
@ -107,7 +123,7 @@ pub fn (s Socket) listen_backlog(backlog int) ?int {
if res < 0 {
return error('net.listen_backlog: failed with $res')
}
return int(res)
return res
}
// helper method to create, bind, and listen given port number
@ -115,7 +131,7 @@ pub fn listen(port int) ?Socket {
$if debug {
println('net.listen($port)')
}
s := socket(C.AF_INET, C.SOCK_STREAM, 0) or {
s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) or {
return error(err)
}
bind_res := s.bind(port) or {
@ -167,17 +183,17 @@ pub fn (s Socket) connect(address string, port int) ?int {
error_message := os.get_error_msg(net.error_code())
return error('net.connect: getaddrinfo failed "$error_message"')
}
res := int(C.connect(s.sockfd, info.ai_addr, info.ai_addrlen))
res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen)
if res < 0 {
error_message := os.get_error_msg(net.error_code())
return error('net.connect: connect failed "$error_message"')
}
return int(res)
return res
}
// helper method to create socket and connect
pub fn dial(address string, port int) ?Socket {
s := socket(C.AF_INET, C.SOCK_STREAM, 0) or {
s := new_socket(C.AF_INET, C.SOCK_STREAM, 0) or {
return error(err)
}
res := s.connect(address, port) or {
@ -188,7 +204,7 @@ pub fn dial(address string, port int) ?Socket {
// send string data to socket
pub fn (s Socket) send(buf byteptr, len int) ?int {
res := int( C.send(s.sockfd, buf, len, MSG_NOSIGNAL) )
res := C.send(s.sockfd, buf, len, MSG_NOSIGNAL)
if res < 0 {
return error('net.send: failed with $res')
}
@ -198,18 +214,18 @@ pub fn (s Socket) send(buf byteptr, len int) ?int {
// receive string data from socket
pub fn (s Socket) recv(bufsize int) (byteptr, int) {
buf := malloc(bufsize)
res := int( C.recv(s.sockfd, buf, bufsize, 0) )
res := C.recv(s.sockfd, buf, bufsize, 0)
return buf, res
}
// TODO: remove cread/2 and crecv/2 when the Go net interface is done
pub fn (s Socket) cread( buffer byteptr, buffersize int ) int {
return int( C.read(s.sockfd, buffer, buffersize) )
return C.read(s.sockfd, buffer, buffersize)
}
// Receive a message from the socket, and place it in a preallocated buffer buf,
// with maximum message size bufsize. Returns the length of the received message.
pub fn (s Socket) crecv( buffer byteptr, buffersize int ) int {
return int( C.recv(s.sockfd, buffer, buffersize, 0) )
return C.recv(s.sockfd, buffer, buffersize, 0)
}
// shutdown and close socket
@ -250,7 +266,7 @@ pub const (
// write - write a string with CRLF after it over the socket s
pub fn (s Socket) write(str string) ?int {
line := '$str$CRLF'
res := int( C.send(s.sockfd, line.str, line.len, MSG_NOSIGNAL) )
res := C.send(s.sockfd, line.str, line.len, MSG_NOSIGNAL)
if res < 0 { return error('net.write: failed with $res') }
return res
}
@ -299,6 +315,6 @@ pub fn (s Socket) read_line() string {
pub fn (s Socket) get_port() int {
mut addr := C.sockaddr_in {}
size := 16 // sizeof(sockaddr_in)
sockname_res := C.getsockname(s.sockfd, &addr, &size)
return int(C.ntohs(addr.sin_port))
C.getsockname(s.sockfd, &addr, &size)
return C.ntohs(addr.sin_port)
}

View File

@ -1026,3 +1026,8 @@ pub fn tmpdir() string {
}
return path
}
pub fn chmod(path string, mode int) {
C.chmod(path.str, mode)
}

View File

@ -18,11 +18,11 @@ pub fn init_os_args(argc int, argv &byteptr) []string {
// get_error_msg return error code representation in string.
pub fn get_error_msg(code int) string {
_ptr_text := C.strerror(code) // voidptr?
if _ptr_text == 0 {
ptr_text := C.strerror(code) // voidptr?
if ptr_text == 0 {
return ''
}
return tos(_ptr_text, vstrlen(_ptr_text))
return tos3(ptr_text)
}
pub fn ls(path string) ?[]string {

View File

@ -34,6 +34,11 @@ pub fn (db DB) q_int(query string) int {
}
fn C.sqlite3_column_text(voidptr, int) byteptr
fn C.sqlite3_column_int(voidptr, int) int
fn C.sqlite3_open()
fn C.sqlite3_step() int
fn C.sqlite3_prepare_v2()
fn C.sqlite3_finalize()
pub fn (db DB) q_string(query string) string {
stmt := &C.sqlite3_stmt{!}

View File

@ -21,6 +21,10 @@ mut:
ext string
}
fn C.stbi_load() voidptr
fn C.stbi_image_free()
fn C.stbi_set_flip_vertically_on_load()
pub fn load(path string) Image {
ext := path.all_after('.')
mut res := Image {

View File

@ -6,6 +6,11 @@ module sync
#include <pthread.h>
fn C.pthread_mutex_init()
fn C.pthread_mutex_lock()
fn C.pthread_mutex_unlock()
//[init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
pub struct Mutex {
mutex C.pthread_mutex_t