Browse Source

[eval] adopt libuv (#9903)

* add luv to build dependencies

* eval.NativeString

* initial infrastructure; Loop; Handle; Idle

* UVError

* more friendly API for Loop & Idle

* Timer

* Async

* fixed decode_luv_handle

* sys.thread.EventLoop implementation on top of libuv loops

* fixed Sys.exit() inside of an event loop

* update EventLoop docs

* refactoring

* Buffer

* eval.integers.UInt64

* update Loop

* fix UInt64

* TODO

* SockAddr

* TCP

* Pipe

* TTY

* Stream

* UDP

* update Handle

* update sys.thread.EventLoop

* Signal

* make `loop` argument mandatory everywhere

* Int64

* fix EventLoop again

* Process

* Request

* DNS wip

* finished DNS

* File externs

* wip

* File implementations

* refactoring

* More refactoring; File.Sync

* FS_Event

* [eval] added `EvalThread.run` to be able to run Haxe code in non-Haxe threads

* Thread_pool

* Thread

* Once

* Mutex

* removed decode_nullable

* RwLock

* Semaphore

* Condition

* Barrier

* Env

* Time

* Path

* Random

* Network

* FS_poll

* Resource

* SystemInfo

* Pid

* minor

* Passwd

* Metrics

* minor

* Prepare

* Check

* Version

* fix xmldoc

* fix for OCaml 4.02.3

* [eval] fix sys.thread.EventLoop.cancel

* try luv fixed for Win32

* minor

* [tests] increase timeout

* minor
Aleksandr Kuzmenko 4 years ago
parent
commit
6dc7876fae
69 changed files with 5594 additions and 109 deletions
  1. 3 6
      .github/workflows/main.yml
  2. 1 0
      extra/azure-pipelines/build-windows.yml
  3. 1 0
      extra/github-actions/build-windows.yml
  4. 1 1
      opam
  5. 1 1
      src/dune
  6. 46 0
      src/macro/eval/EvalStackTrace.ml
  7. 9 2
      src/macro/eval/evalContext.ml
  8. 4 2
      src/macro/eval/evalDebugSocket.ml
  9. 4 0
      src/macro/eval/evalDecode.ml
  10. 1 1
      src/macro/eval/evalEncode.ml
  11. 1 0
      src/macro/eval/evalExceptions.ml
  12. 57 0
      src/macro/eval/evalHash.ml
  13. 2429 0
      src/macro/eval/evalLuv.ml
  14. 28 17
      src/macro/eval/evalMain.ml
  15. 34 0
      src/macro/eval/evalMisc.ml
  16. 3 1
      src/macro/eval/evalPrinting.ml
  17. 51 43
      src/macro/eval/evalStdLib.ml
  18. 51 32
      src/macro/eval/evalThread.ml
  19. 78 0
      src/macro/eval/evalValue.ml
  20. 141 0
      std/eval/_std/sys/thread/EventLoop.hx
  21. 4 0
      std/eval/integers/UInt64.hx
  22. 21 0
      std/eval/luv/Async.hx
  23. 23 0
      std/eval/luv/Barrier.hx
  24. 120 0
      std/eval/luv/Buffer.hx
  25. 26 0
      std/eval/luv/Check.hx
  26. 39 0
      std/eval/luv/Condition.hx
  27. 34 0
      std/eval/luv/ConnectedUdp.hx
  28. 73 0
      std/eval/luv/Dir.hx
  29. 69 0
      std/eval/luv/Dns.hx
  30. 23 0
      std/eval/luv/Env.hx
  31. 424 0
      std/eval/luv/File.hx
  32. 36 0
      std/eval/luv/FsEvent.hx
  33. 30 0
      std/eval/luv/FsPoll.hx
  34. 85 0
      std/eval/luv/Handle.hx
  35. 26 0
      std/eval/luv/Idle.hx
  36. 93 0
      std/eval/luv/Loop.hx
  37. 19 0
      std/eval/luv/LuvException.hx
  38. 13 0
      std/eval/luv/Metrics.hx
  39. 35 0
      std/eval/luv/Mutex.hx
  40. 36 0
      std/eval/luv/Network.hx
  41. 18 0
      std/eval/luv/Once.hx
  42. 6 0
      std/eval/luv/OsFd.hx
  43. 6 0
      std/eval/luv/OsSocket.hx
  44. 21 0
      std/eval/luv/Passwd.hx
  45. 37 0
      std/eval/luv/Path.hx
  46. 18 0
      std/eval/luv/Pid.hx
  47. 77 0
      std/eval/luv/Pipe.hx
  48. 26 0
      std/eval/luv/Prepare.hx
  49. 89 0
      std/eval/luv/Process.hx
  50. 26 0
      std/eval/luv/Random.hx
  51. 13 0
      std/eval/luv/Request.hx
  52. 78 0
      std/eval/luv/Resource.hx
  53. 40 0
      std/eval/luv/Result.hx
  54. 48 0
      std/eval/luv/RwLock.hx
  55. 33 0
      std/eval/luv/Semaphore.hx
  56. 57 0
      std/eval/luv/Signal.hx
  57. 47 0
      std/eval/luv/SockAddr.hx
  58. 111 0
      std/eval/luv/Stream.hx
  59. 39 0
      std/eval/luv/SystemInfo.hx
  60. 62 0
      std/eval/luv/Tcp.hx
  61. 34 0
      std/eval/luv/Thread.hx
  62. 31 0
      std/eval/luv/ThreadPool.hx
  63. 27 0
      std/eval/luv/Time.hx
  64. 40 0
      std/eval/luv/Timer.hx
  65. 63 0
      std/eval/luv/Tty.hx
  66. 187 0
      std/eval/luv/UVError.hx
  67. 121 0
      std/eval/luv/Udp.hx
  68. 44 0
      std/eval/luv/Version.hx
  69. 22 3
      std/sys/thread/EventLoop.hx

+ 3 - 6
.github/workflows/main.yml

@@ -75,6 +75,7 @@ jobs:
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1')
+          & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam pin add luv https://github.com/aantron/luv.git#somaxconn --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v')
       
@@ -161,6 +162,7 @@ jobs:
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1')
+          & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam pin add luv https://github.com/aantron/luv.git#somaxconn --yes 2>&1')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list')
           & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v')
       
@@ -628,12 +630,7 @@ jobs:
       
       - name: Install homebrew packages
         if: matrix.BREW_PACKAGES
-        run: |
-          brew uninstall [email protected]
-          brew uninstall [email protected]
-          brew untap local/openssl
-          brew untap local/python2
-          brew install ${{matrix.BREW_PACKAGES}}
+        run: brew install ${{matrix.BREW_PACKAGES}}
       
       - name: Test
         run: |

+ 1 - 0
extra/azure-pipelines/build-windows.yml

@@ -51,6 +51,7 @@ jobs:
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1')
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1')
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1')
+          & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'opam pin add luv https://github.com/aantron/luv.git#somaxconn --yes 2>&1')
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list')
           & "$(CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v')
         displayName: Install OCaml and OCaml libraries

+ 1 - 0
extra/github-actions/build-windows.yml

@@ -27,6 +27,7 @@
     & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1')
     & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1')
     & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1')
+    & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam pin add luv https://github.com/aantron/luv.git#somaxconn --yes 2>&1')
     & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list')
     & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v')
 

+ 1 - 1
opam

@@ -32,5 +32,5 @@ depends: [
   "conf-zlib"
   "conf-mbedtls"
   "conf-neko"
-  "integers"
+  "luv"
 ]

+ 1 - 1
src/dune

@@ -13,7 +13,7 @@
 		json
 		unix str threads dynlink
 		xml-light extlib ptmap sha
-		integers
+		luv
 	)
 	(modules (:standard \ haxe))
 	(preprocess (per_module

+ 46 - 0
src/macro/eval/EvalStackTrace.ml

@@ -0,0 +1,46 @@
+open Globals
+open EvalContext
+open EvalExceptions
+open EvalValue
+open EvalEncode
+open EvalDecode
+open EvalHash
+open EvalString
+
+let make_stack envs =
+	let l = DynArray.create () in
+	List.iter (fun (pos,kind) ->
+		let file_pos s =
+			let line1,col1,_,_ = Lexer.get_pos_coords pos in
+			encode_enum_value key_haxe_StackItem 2 [|s;create_unknown pos.pfile;vint line1;vint col1|] None
+		in
+		match kind with
+		| EKLocalFunction i ->
+			let local_function = encode_enum_value key_haxe_StackItem 4 [|vint i|] None in
+			DynArray.add l (file_pos local_function);
+		| EKMethod(st,sf) ->
+			let local_function = encode_enum_value key_haxe_StackItem 3 [|create_unknown (rev_hash st); create_unknown (rev_hash sf)|] None in
+			DynArray.add l (file_pos local_function);
+		| EKEntrypoint ->
+			()
+	) envs;
+	encode_array (DynArray.to_list l)
+
+let make_stack_value envs =
+	make_stack (List.map (fun env -> {pfile = rev_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax},env.env_info.kind) envs)
+
+let getCallStack = vfun0 (fun () ->
+	let ctx = get_ctx() in
+	let envs = call_stack (get_eval ctx) in
+	let envs = match envs with
+		| _ :: _ :: envs -> envs (* Skip calls to callStack() and getCallStack() *)
+		| _ -> envs
+	in
+	make_stack_value  envs
+)
+
+let getExceptionStack = vfun0 (fun () ->
+	let ctx = get_ctx() in
+	let envs = ctx.exception_stack in
+	make_stack (List.rev envs)
+)

+ 9 - 2
src/macro/eval/evalContext.ml

@@ -319,8 +319,15 @@ let s_debug_state = function
 (* Misc *)
 
 let get_eval ctx =
-    let id = Thread.id (Thread.self()) in
-    if id = 0 then ctx.eval else IntMap.find id ctx.evals
+	let id = Thread.id (Thread.self()) in
+	if id = 0 then
+		ctx.eval
+	else
+		try
+			IntMap.find id ctx.evals
+		with Not_found ->
+			die "Cannot run Haxe code in a non-Haxe thread" __LOC__
+
 
 let rec kind_name eval kind =
 	let rec loop kind env = match kind with

+ 4 - 2
src/macro/eval/evalDebugSocket.ml

@@ -85,6 +85,7 @@ let var_to_json name value vio env =
 		| VFunction _ | VFieldClosure _ -> "<fun>"
 		| VLazy f -> level2_value_repr (!f())
 		| VNativeString s -> string_repr s
+		| VHandle _ -> "<handle>"
 	in
 	let fields_string fields =
 		let l = List.map (fun (name, value) -> Printf.sprintf "%s: %s" (rev_hash name) (level2_value_repr value)) fields in
@@ -150,6 +151,7 @@ let var_to_json name value vio env =
 		| VLazy f -> value_string (!f())
 		| VNativeString s ->
 			jv "NativeString" (string_repr s) 0
+		| VHandle _ -> jv "Handle" "<handle>" 0
 	in
 	value_string value
 
@@ -270,7 +272,7 @@ let output_scope_vars env scope =
 let output_inner_vars v env =
 	let rec loop v = match v with
 		| VNull | VTrue | VFalse | VInt32 _ | VInt64 _ | VUInt64 _ | VFloat _
-		| VFunction _ | VFieldClosure _ | VNativeString _ -> []
+		| VFunction _ | VFieldClosure _ | VNativeString _ | VHandle _ -> []
 		| VEnumValue ve ->
 			begin match (get_static_prototype_raise (get_ctx()) ve.epath).pkind with
 				| PEnum names ->
@@ -433,7 +435,7 @@ module ValueCompletion = struct
 		in
 		let rec loop v = match v with
 			| VNull | VTrue | VFalse | VInt32 _ | VInt64 _ | VUInt64 _ | VFloat _
-			| VFunction _ | VFieldClosure _ | VNativeString _ ->
+			| VFunction _ | VFieldClosure _ | VNativeString _ | VHandle _->
 				[]
 			| VObject o ->
 				let fields = object_fields o in

+ 4 - 0
src/macro/eval/evalDecode.ml

@@ -61,6 +61,10 @@ let decode_native_string v = match v with
 	| VNativeString s -> s
 	| _ -> unexpected_value v "native string"
 
+let decode_handle v = match v with
+	| VHandle h -> h
+	| _ -> unexpected_value v "handle"
+
 let decode_bytes v = match v with
 	| VInstance {ikind=IBytes s} -> s
 	| _ -> unexpected_value v "string"

+ 1 - 1
src/macro/eval/evalEncode.ml

@@ -108,7 +108,7 @@ let vfun5 f = vstatic_function (fun vl -> match vl with
 	| [v0;v1;v2] -> f v0 v1 v2 vnull vnull
 	| [v0;v1;v2;v3] -> f v0 v1 v2 v3 vnull
 	| [v0;v1;v2;v3;v4] -> f v0 v1 v2 v3 v4
-| _ -> invalid_call_arg_number 5 (List.length  vl
+	| _ -> invalid_call_arg_number 5 (List.length  vl
 ))
 
 let vfun6 f = vstatic_function (fun vl -> match vl with

+ 1 - 0
src/macro/eval/evalExceptions.ml

@@ -79,6 +79,7 @@ let s_value_kind = function
 	| VFieldClosure _ -> "VFieldClosure"
 	| VLazy _ -> "VLazy"
 	| VNativeString _ -> "VNativeString"
+	| VHandle _ -> "VHandle"
 
 let unexpected_value : 'a . value -> string -> 'a = fun v s ->
 	let str = match v with

+ 57 - 0
src/macro/eval/evalHash.ml

@@ -43,6 +43,8 @@ let key_max = hash "max"
 let key_file = hash "file"
 let key_len = hash "len"
 let key_message = hash "message"
+let key_name = hash "name"
+let key_error = hash "error"
 let key_exception_message = hash "__exceptionMessage"
 let key_native_exception = hash "__nativeException"
 let key_native_stack = hash "__nativeStack"
@@ -146,3 +148,58 @@ let key_mbedtls_Entropy = hash "mbedtls.Entropy"
 let key_mbedtls_PkContext = hash "mbedtls.PkContext"
 let key_mbedtls_Ssl = hash "mbedtls.Ssl"
 let key_mbedtls_X509Crt = hash "mbedtls.X509Crt"
+
+let key_eval_luv_Result = hash "eval.luv.Result"
+let key_eval_luv_LuvException = hash "eval.luv.LuvException"
+let key_eval_luv_ReceiveHandle = hash "eval.luv.ReceiveHandle"
+let key_eval_luv_AddressFamily = hash "eval.luv.AddressFamily"
+let key_eval_luv_SocketType = hash "eval.luv.SocketType"
+let key_onExit = hash "onExit"
+let key_environment = hash "environment"
+let key_redirect = hash "redirect"
+let key_workingDirectory = hash "workingDirectory"
+let key_uid = hash "uid"
+let key_gid = hash "gid"
+let key_windowsVerbatimArguments = hash "windowsVerbatimArguments"
+let key_detached = hash "detached"
+let key_windowsHide = hash "windowsHide"
+let key_windowsHideConsole = hash "windowsHideConsole"
+let key_windowsHideGui = hash "windowsHideGui"
+let key_request = hash "request"
+let key_family = hash "family"
+let key_sockType = hash "sockType"
+let key_protocol = hash "protocol"
+let key_flags = hash "flags"
+let key_addr = hash "addr"
+let key_data = hash "data"
+let key_canonName = hash "canonName"
+let key_node = hash "node"
+let key_service = hash "service"
+let key_sec = hash "sec"
+let key_nsec = hash "nsec"
+let key_usec = hash "usec"
+let key_blksize = hash "blksize"
+let key_blocks = hash "blocks"
+let key_gen = hash "gen"
+let key_atim = hash "atim"
+let key_mtim = hash "mtim"
+let key_ctim = hash "ctim"
+let key_birthtim = hash "birthtim"
+let key_width = hash "width"
+let key_height = hash "height"
+let key_type = hash "type"
+let key_bsize = hash "bsize"
+let key_bfree = hash "bfree"
+let key_bavail = hash "bavail"
+let key_files = hash "files"
+let key_ffree = hash "ffree"
+let key_fspare = hash "fspare"
+let key_kind = hash "kind"
+let key_end = hash "end"
+let key_events = hash "events"
+let key_isInternal = hash "isInternal"
+let key_physical = hash "physical"
+let key_address = hash "address"
+let key_netmask = hash "netmask"
+let key_previous = hash "previous"
+let key_current = hash "current"

+ 2429 - 0
src/macro/eval/evalLuv.ml

@@ -0,0 +1,2429 @@
+module HaxeError = Error
+
+open Luv
+open Globals
+open EvalContext
+open EvalExceptions
+open EvalValue
+open EvalEncode
+open EvalDecode
+open EvalHash
+open EvalMisc
+open EvalField
+open EvalIntegers
+
+let encode_uv_error (e:Error.t) =
+	vint (match e with
+	| `E2BIG -> 0
+	| `EACCES -> 1
+	| `EADDRINUSE -> 2
+	| `EADDRNOTAVAIL -> 3
+	| `EAFNOSUPPORT -> 4
+	| `EAGAIN -> 5
+	| `EAI_ADDRFAMILY -> 6
+	| `EAI_AGAIN -> 7
+	| `EAI_BADFLAGS -> 8
+	| `EAI_BADHINTS -> 9
+	| `EAI_CANCELED -> 10
+	| `EAI_FAIL -> 11
+	| `EAI_FAMILY -> 12
+	| `EAI_MEMORY -> 13
+	| `EAI_NODATA -> 14
+	| `EAI_NONAME -> 15
+	| `EAI_OVERFLOW -> 16
+	| `EAI_PROTOCOL -> 17
+	| `EAI_SERVICE -> 18
+	| `EAI_SOCKTYPE -> 19
+	| `EALREADY -> 20
+	| `EBADF -> 21
+	| `EBUSY -> 22
+	| `ECANCELED -> 23
+	(* | `ECHARSET -> 24; not defined in Luv *)
+	| `ECONNABORTED -> 25
+	| `ECONNREFUSED -> 26
+	| `ECONNRESET -> 27
+	| `EDESTADDRREQ -> 28
+	| `EEXIST -> 29
+	| `EFAULT -> 30
+	| `EFBIG -> 31
+	| `EHOSTUNREACH -> 32
+	| `EINTR -> 33
+	| `EINVAL -> 34
+	| `EIO -> 35
+	| `EISCONN -> 36
+	| `EISDIR -> 37
+	| `ELOOP -> 38
+	| `EMFILE -> 39
+	| `EMSGSIZE -> 40
+	| `ENAMETOOLONG -> 41
+	| `ENETDOWN -> 42
+	| `ENETUNREACH -> 43
+	| `ENFILE -> 44
+	| `ENOBUFS -> 45
+	| `ENODEV -> 46
+	| `ENOENT -> 47
+	| `ENOMEM -> 48
+	| `ENONET -> 49
+	| `ENOPROTOOPT -> 50
+	| `ENOSPC -> 51
+	| `ENOSYS -> 52
+	| `ENOTCONN -> 53
+	| `ENOTDIR -> 54
+	| `ENOTEMPTY -> 55
+	| `ENOTSOCK -> 56
+	| `ENOTSUP -> 57
+	| `EPERM -> 58
+	| `EPIPE -> 59
+	| `EPROTO -> 60
+	| `EPROTONOSUPPORT -> 61
+	| `EPROTOTYPE -> 62
+	| `ERANGE -> 63
+	| `EROFS -> 64
+	| `ESHUTDOWN -> 65
+	| `ESPIPE -> 66
+	| `ESRCH -> 67
+	| `ETIMEDOUT -> 68
+	| `ETXTBSY -> 69
+	| `EXDEV -> 70
+	| `UNKNOWN -> 71
+	| `EOF -> 72
+	| `ENXIO -> 73
+	| `EMLINK -> 74
+	| `ENOTTY -> 75
+	| `EFTYPE -> 76
+	| `EILSEQ -> 77
+	)
+
+let decode_uv_error v : Error.t =
+	match decode_int v with
+	| 0 -> `E2BIG
+	| 1 -> `EACCES
+	| 2 -> `EADDRINUSE
+	| 3 -> `EADDRNOTAVAIL
+	| 4 -> `EAFNOSUPPORT
+	| 5 -> `EAGAIN
+	| 6 -> `EAI_ADDRFAMILY
+	| 7 -> `EAI_AGAIN
+	| 8 -> `EAI_BADFLAGS
+	| 9 -> `EAI_BADHINTS
+	| 10 -> `EAI_CANCELED
+	| 11 -> `EAI_FAIL
+	| 12 -> `EAI_FAMILY
+	| 13 -> `EAI_MEMORY
+	| 14 -> `EAI_NODATA
+	| 15 -> `EAI_NONAME
+	| 16 -> `EAI_OVERFLOW
+	| 17 -> `EAI_PROTOCOL
+	| 18 -> `EAI_SERVICE
+	| 19 -> `EAI_SOCKTYPE
+	| 20 -> `EALREADY
+	| 21 -> `EBADF
+	| 22 -> `EBUSY
+	| 23 -> `ECANCELED
+	(* | 24 -> `ECHARSET not defined in Luv *)
+	| 25 -> `ECONNABORTED
+	| 26 -> `ECONNREFUSED
+	| 27 -> `ECONNRESET
+	| 28 -> `EDESTADDRREQ
+	| 29 -> `EEXIST
+	| 30 -> `EFAULT
+	| 31 -> `EFBIG
+	| 32 -> `EHOSTUNREACH
+	| 33 -> `EINTR
+	| 34 -> `EINVAL
+	| 35 -> `EIO
+	| 36 -> `EISCONN
+	| 37 -> `EISDIR
+	| 38 -> `ELOOP
+	| 39 -> `EMFILE
+	| 40 -> `EMSGSIZE
+	| 41 -> `ENAMETOOLONG
+	| 42 -> `ENETDOWN
+	| 43 -> `ENETUNREACH
+	| 44 -> `ENFILE
+	| 45 -> `ENOBUFS
+	| 46 -> `ENODEV
+	| 47 -> `ENOENT
+	| 48 -> `ENOMEM
+	| 49 -> `ENONET
+	| 50 -> `ENOPROTOOPT
+	| 51 -> `ENOSPC
+	| 52 -> `ENOSYS
+	| 53 -> `ENOTCONN
+	| 54 -> `ENOTDIR
+	| 55 -> `ENOTEMPTY
+	| 56 -> `ENOTSOCK
+	| 57 -> `ENOTSUP
+	| 58 -> `EPERM
+	| 59 -> `EPIPE
+	| 60 -> `EPROTO
+	| 61 -> `EPROTONOSUPPORT
+	| 62 -> `EPROTOTYPE
+	| 63 -> `ERANGE
+	| 64 -> `EROFS
+	| 65 -> `ESHUTDOWN
+	| 66 -> `ESPIPE
+	| 67 -> `ESRCH
+	| 68 -> `ETIMEDOUT
+	| 69 -> `ETXTBSY
+	| 70 -> `EXDEV
+	| 71 -> `UNKNOWN
+	| 72 -> `EOF
+	| 73 -> `ENXIO
+	| 74 -> `EMLINK
+	| 75 -> `ENOTTY
+	| 76 -> `EFTYPE
+	| 77 -> `EILSEQ
+	| _ -> unexpected_value v "eval.luv.UVError"
+
+let luv_exception e =
+	let vi = encode_instance key_eval_luv_LuvException in
+	match vi with
+	| VInstance i ->
+		let msg = EvalString.create_unknown (Error.strerror e)
+		and error = encode_uv_error e in
+		set_instance_field i key_exception_message msg;
+		set_instance_field i key_native_exception error;
+		set_instance_field i key_error error;
+		let ctx = get_ctx() in
+		let eval = get_eval ctx in
+		(match eval.env with
+		| Some _ ->
+			let stack = EvalStackTrace.make_stack_value (call_stack eval) in
+			set_instance_field i key_native_stack stack;
+		| None -> ());
+		vi
+	| _ ->
+		die "" __LOC__
+
+let encode_result f result =
+	let index, args =
+		match result with
+		| Result.Ok r -> 0, [|f r|]
+		| Result.Error e -> 1, [|encode_uv_error e|]
+	in
+	encode_enum_value key_eval_luv_Result index args None
+
+let encode_callback encode_ok_value v_callback result =
+	let cb = prepare_callback v_callback 1 in
+	ignore(cb [encode_result encode_ok_value result])
+
+let encode_unit () =
+	vnull
+
+let encode_unit_result =
+	encode_result encode_unit
+
+let encode_unit_callback =
+	encode_callback encode_unit
+
+let resolve_result = function
+	| Result.Ok v -> v
+	| Result.Error e -> throw (luv_exception e) null_pos
+
+let decode_loop = function
+	| VHandle (HLoop t) -> t
+	| v -> unexpected_value v "eval.luv.Loop"
+
+let decode_luv_handle v : 'kind Luv.Handle.t =
+	match decode_handle v with
+	| HIdle t -> Handle.coerce t
+	| HTimer t -> Handle.coerce t
+	| HAsync t -> Handle.coerce t
+	| HPipe t -> Handle.coerce t
+	| HTcp t -> Handle.coerce t
+	| HTty t -> Handle.coerce t
+	| HUdp t -> Handle.coerce t
+	| HSignal t -> Handle.coerce t
+	| HProcess t -> Handle.coerce t
+	| HFsEvent t -> Handle.coerce t
+	| HFsPoll t -> Handle.coerce t
+	| HPrepare t -> Handle.coerce t
+	| HCheck t -> Handle.coerce t
+	(* TODO
+	| HPoll t -> Handle.coerce t
+	*)
+	| _ -> unexpected_value v "eval.luv.Handle"
+
+let decode_socket_handle v : [< `Stream of [< `Pipe | `TCP ] | `UDP ] Luv.Handle.t =
+	match decode_handle v with
+	| HTcp t -> Obj.magic t
+	| HUdp t -> Obj.magic t
+	| HPipe t -> Obj.magic t
+	| _ -> unexpected_value v "eval.luv.Handle.SocketHandle"
+
+let decode_stream v : 'kind Luv.Stream.t =
+	match decode_handle v with
+	| HTcp t -> Stream.coerce t
+	| HTty t -> Stream.coerce t
+	| HPipe t -> Stream.coerce t
+	| _ -> unexpected_value v "eval.luv.Stream"
+
+let decode_idle = function
+	| VHandle (HIdle t) -> t
+	| v -> unexpected_value v "eval.luv.Idle"
+
+let decode_timer = function
+	| VHandle (HTimer t) -> t
+	| v -> unexpected_value v "eval.luv.Timer"
+
+let decode_async = function
+	| VHandle (HAsync t) -> t
+	| v -> unexpected_value v "eval.luv.Async"
+
+let decode_buffer = function
+	| VHandle (HBuffer t) -> t
+	| v -> unexpected_value v "eval.luv.Buffer"
+
+let decode_buffers v =
+	List.map decode_buffer (decode_array v)
+
+let encode_buffer b =
+	VHandle (HBuffer b)
+
+let decode_sockaddr v =
+	match decode_handle v with
+	| HSockAddr t -> t
+	| _ -> unexpected_value v "eval.luv.SockAddr"
+
+let encode_sockaddr h =
+	VHandle (HSockAddr h)
+
+let decode_tcp = function
+	| VHandle (HTcp t) -> t
+	| v -> unexpected_value v "eval.luv.Tcp"
+
+let decode_udp = function
+	| VHandle (HUdp t) -> t
+	| v -> unexpected_value v "eval.luv.Udp"
+
+let encode_udp udp =
+	VHandle (HUdp udp)
+
+let decode_udp_membership v =
+	match decode_int v with
+	| 0 -> `LEAVE_GROUP
+	| 1 -> `JOIN_GROUP
+	| _ -> unexpected_value v "eval.luv.Udp.UdpMembership"
+
+let decode_socket_type v : Sockaddr.Socket_type.t =
+	match decode_enum v with
+	| 0, [] -> `STREAM
+	| 1, [] -> `DGRAM
+	| 2, [] -> `RAW
+	| 3, [v] -> `OTHER (decode_int v)
+	| _ -> unexpected_value v "eval.luv.SockAddr.SocketType"
+
+let decode_address_family v : Sockaddr.Address_family.t =
+	match decode_enum v with
+	| 0, [] -> `UNSPEC
+	| 1, [] -> `INET
+	| 2, [] -> `INET6
+	| 3, [v] -> `OTHER (decode_int v)
+	| _ -> unexpected_value v "eval.luv.SockAddr.AddressType"
+
+let encode_address_family (a:Sockaddr.Address_family.t) =
+	let index,args =
+		match a with
+		| `UNSPEC -> 0, [||]
+		| `INET -> 1, [||]
+		| `INET6 -> 2, [||]
+		| `OTHER i -> 3, [|vint i|]
+	in
+	encode_enum_value key_eval_luv_AddressFamily index args None
+
+let encode_socket_type (a:Sockaddr.Socket_type.t) =
+	let index,args =
+		match a with
+		| `STREAM -> 0, [||]
+		| `DGRAM -> 1, [||]
+		| `RAW -> 2, [||]
+		| `OTHER i -> 3, [|vint i|]
+	in
+	encode_enum_value key_eval_luv_SocketType index args None
+
+let decode_pipe = function
+	| VHandle (HPipe t) -> t
+	| v -> unexpected_value v "eval.luv.Pipe"
+
+let decode_tty = function
+	| VHandle (HTty t) -> t
+	| v -> unexpected_value v "eval.luv.Tty"
+
+let decode_file = function
+	| VHandle (HFile f) -> f
+	| v -> unexpected_value v "eval.luv.File"
+
+let decode_signal = function
+	| VHandle (HSignal t) -> t
+	| v -> unexpected_value v "eval.luv.Signal"
+
+let decode_process = function
+	| VHandle (HProcess t) -> t
+	| v -> unexpected_value v "eval.luv.Process"
+
+let decode_prepare = function
+	| VHandle (HPrepare t) -> t
+	| v -> unexpected_value v "eval.luv.Prepare"
+
+let decode_check = function
+	| VHandle (HCheck t) -> t
+	| v -> unexpected_value v "eval.luv.Check"
+
+let decode_file_mode v : File.Mode.t =
+	match decode_enum v with
+	| 0,[] -> `IRWXU
+	| 1,[] -> `IRUSR
+	| 2,[] -> `IWUSR
+	| 3,[] -> `IXUSR
+	| 4,[] -> `IRWXG
+	| 5,[] -> `IRGRP
+	| 6,[] -> `IWGRP
+	| 7,[] -> `IXGRP
+	| 8,[] -> `IRWXO
+	| 9,[] -> `IROTH
+	| 10,[] -> `IWOTH
+	| 11,[] -> `IXOTH
+	| 12,[] -> `ISUID
+	| 13,[] -> `ISGID
+	| 14,[] -> `ISVTX
+	| 15,[] -> `IFMT
+	| 16,[] -> `IFREG
+	| 17,[] -> `IFDIR
+	| 18,[] -> `IFBLK
+	| 19,[] -> `IFCHR
+	| 20,[] -> `IFLNK
+	| 21,[] -> `IFIFO
+	| 22,[v2] -> `NUMERIC (decode_int v2)
+	| _ -> unexpected_value v "eval.luv.File.FileMode"
+
+let decode_file_mode_list v =
+	List.map decode_file_mode (decode_array v)
+
+let decode_file_request = function
+	| VHandle (HFileRequest r) -> r
+	| v -> unexpected_value v "eval.luv.File.FileRequest"
+
+let encode_timespec (t:File.Stat.timespec) =
+	encode_obj [
+		key_sec, VInt64 (Signed.Long.to_int64 t.sec);
+		key_nsec, VInt64 (Signed.Long.to_int64 t.nsec)
+	]
+
+let decode_dir v =
+	match v with
+	| VHandle (HDir dir) -> dir
+	| _ -> unexpected_value v "eval.luv.Dir"
+
+let encode_dirent (de:File.Dirent.t) =
+	let kind =
+		match de.kind with
+		| `UNKNOWN -> 0
+		| `FILE -> 1
+		| `DIR -> 2
+		| `LINK -> 3
+		| `FIFO -> 4
+		| `SOCKET -> 5
+		| `CHAR -> 6
+		| `BLOCK -> 7
+	in
+	encode_obj [key_kind,vint kind; key_name,vnative_string de.name]
+
+let encode_scandir sd =
+	encode_obj [
+		key_next,vfun0 (fun() -> encode_nullable encode_dirent (File.scandir_next sd));
+		key_end,vfun0 (fun() -> File.scandir_end sd; vnull);
+	]
+
+let decode_int_flags v =
+	List.map decode_int (decode_array v)
+
+let decode_file_open_flag v : File.Open_flag.t =
+	match decode_int v with
+	| 0 -> `RDONLY
+	| 1 -> `WRONLY
+	| 2 -> `RDWR
+	| 3 -> `CREAT
+	| 4 -> `EXCL
+	| 5 -> `EXLOCK
+	| 6 -> `NOCTTY
+	| 7 -> `NOFOLLOW
+	| 8 -> `TEMPORARY
+	| 9 -> `TRUNC
+	| 10 -> `APPEND
+	| 11 -> `DIRECT
+	| 12 -> `DSYNC
+	| 13 -> `FILEMAP
+	| 14 -> `NOATIME
+	| 15 -> `NONBLOCK
+	| 16 -> `RANDOM
+	| 17 -> `SEQUENTIAL
+	| 18 -> `SHORT_LIVED
+	| 19 -> `SYMLINK
+	| 20 -> `SYNC
+	| _ -> unexpected_value v "eval.luv.File.FileOpenFlag"
+
+let encode_file_stat (s:File.Stat.t) =
+	encode_obj [
+		key_dev,VUInt64 s.dev;
+		key_mode, VHandle (HFileModeNumeric s.mode);
+		key_nlink,VUInt64 s.nlink;
+		key_uid,VUInt64 s.uid;
+		key_gid,VUInt64 s.gid;
+		key_rdev,VUInt64 s.rdev;
+		key_ino,VUInt64 s.ino;
+		key_size,VUInt64 s.size;
+		key_blksize,VUInt64 s.blksize;
+		key_blocks,VUInt64 s.blocks;
+		key_flags,VUInt64 s.flags;
+		key_gen,VUInt64 s.gen;
+		key_atim,encode_timespec s.atim;
+		key_mtim,encode_timespec s.mtim;
+		key_ctim,encode_timespec s.ctim;
+		key_birthtim,encode_timespec s.birthtim;
+	]
+
+let encode_file_statfs (s:File.Statfs.t) =
+	encode_obj [
+		key_type, VUInt64 s.type_;
+		key_bsize, VUInt64 s.bsize;
+		key_blocks, VUInt64 s.blocks;
+		key_bfree, VUInt64 s.bfree;
+		key_bavail, VUInt64 s.bavail;
+		key_files, VUInt64 s.files;
+		key_ffree, VUInt64 s.ffree;
+		key_fspare, match s.f_spare with u1, u2, u3, u4 -> encode_array [VUInt64 u1; VUInt64 u2; VUInt64 u3; VUInt64 u4]
+	]
+
+let decode_fs_event = function
+	| VHandle (HFsEvent e) -> e
+	| v -> unexpected_value v "eval.luv.FsEvent"
+
+let decode_mutex = function
+	| VHandle (HMutex m) -> m
+	| v -> unexpected_value v "eval.luv.Mutex"
+
+let decode_rwlock = function
+	| VHandle (HRwLock l) -> l
+	| v -> unexpected_value v "eval.luv.RwLock"
+
+let decode_semaphore = function
+	| VHandle (HSemaphore s) -> s
+	| v -> unexpected_value v "eval.luv.Semaphore"
+
+let decode_condition = function
+	| VHandle (HCondition c) -> c
+	| v -> unexpected_value v "eval.luv.Condition"
+
+let decode_barrier = function
+	| VHandle (HBarrier b) -> b
+	| v -> unexpected_value v "eval.luv.Barrier"
+
+let decode_fs_poll = function
+	| VHandle (HFsPoll p) -> p
+	| v -> unexpected_value v "eval.luv.FsPoll"
+
+let uv_error_fields = [
+	"toString", vfun1 (fun v ->
+		let e = decode_uv_error v in
+		EvalString.create_unknown (Error.strerror e)
+	);
+	"errName", vfun1 (fun v ->
+		let e = decode_uv_error v in
+		EvalString.create_unknown (Error.err_name e)
+	);
+	"translateSysError", vfun1 (fun v ->
+		let e = decode_int v in
+		encode_uv_error (Error.translate_sys_error e)
+	);
+	"setOnUnhandledException", vfun1 (fun v ->
+		let cb = prepare_callback v 1 in
+		Error.set_on_unhandled_exception (fun ex ->
+			let msg =
+				match ex with
+				| HaxeError.Error (Custom msg,_) ->
+					(* Eval interpreter rethrows runtime exceptions as `Custom "Exception message\nException stack"` *)
+					(try fst (ExtString.String.split msg "\n")
+					with _ -> msg)
+				| HaxeError.Error (err,_) -> HaxeError.error_msg err
+				| _ -> Printexc.to_string ex
+			in
+			let e = create_haxe_exception ~stack:(get_ctx()).exception_stack msg in
+			ignore(cb [e])
+		);
+		vnull
+	);
+]
+
+let loop_fields = [
+	"run", vfun2 (fun v1 v2 ->
+		let loop = decode_loop v1
+		and mode =
+			match decode_int v2 with
+			| 0 -> `DEFAULT
+			| 1 -> `ONCE
+			| 2 -> `NOWAIT
+			| _ -> unexpected_value v2 "valid loop run mode"
+		in
+		vbool (Loop.run ~loop ~mode ())
+	);
+	"stop", vfun1 (fun v ->
+		let loop = decode_loop v in
+		Loop.stop loop;
+		vnull
+	);
+	"init", vfun0 (fun () ->
+		encode_result (fun l -> VHandle (HLoop l)) (Loop.init())
+	);
+	"close", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_unit_result (Loop.close loop)
+	);
+	"alive", vfun1 (fun v ->
+		let loop = decode_loop v in
+		vbool (Loop.alive loop)
+	);
+	"defaultLoop", vfun0 (fun () ->
+		VHandle (HLoop (Loop.default()))
+	);
+	"libraryShutdown", vfun0 (fun () ->
+		Loop.library_shutdown();
+		vnull
+	);
+	"now", vfun1 (fun v ->
+		let loop = decode_loop v in
+		VUInt64 (Loop.now loop)
+	);
+	"updateTime", vfun1 (fun v ->
+		let loop = decode_loop v in
+		Loop.update_time loop;
+		vnull
+	);
+]
+
+let handle_fields = [
+	"close", vfun2 (fun v1 v2 ->
+		let handle = decode_luv_handle v1
+		and cb = prepare_callback v2 0 in
+		Handle.close handle (fun() -> ignore(cb []));
+		vnull
+	);
+	"isActive", vfun1 (fun v ->
+		let handle = decode_luv_handle v in
+		vbool (Handle.is_active handle)
+	);
+	"isClosing", vfun1 (fun v ->
+		let handle = decode_luv_handle v in
+		vbool (Handle.is_closing handle)
+	);
+	"ref", vfun1 (fun v ->
+		let handle = decode_luv_handle v in
+		Handle.ref handle;
+		vnull
+	);
+	"unref", vfun1 (fun v ->
+		let handle = decode_luv_handle v in
+		Handle.unref handle;
+		vnull
+	);
+	"hasRef", vfun1 (fun v ->
+		let handle = decode_luv_handle v in
+		vbool (Handle.has_ref handle)
+	);
+	"sendBufferSize", vfun1 (fun v ->
+		let handle = decode_socket_handle v in
+		encode_result vint (Handle.send_buffer_size handle)
+	);
+	"setSendBufferSize", vfun2 (fun v1 v2 ->
+		let handle = decode_socket_handle v1
+		and size = decode_int v2 in
+		encode_unit_result (Handle.set_send_buffer_size handle size)
+	);
+	"recvBufferSize", vfun1 (fun v ->
+		let handle = decode_socket_handle v in
+		encode_result vint (Handle.recv_buffer_size handle)
+	);
+	"setRendBufferSize", vfun2 (fun v1 v2 ->
+		let handle = decode_socket_handle v1
+		and size = decode_int v2 in
+		encode_unit_result (Handle.set_recv_buffer_size handle size)
+	);
+]
+
+let idle_fields = [
+	"init", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_result (fun i -> VHandle (HIdle i)) (Idle.init ~loop ())
+	);
+	"start", vfun2 (fun v1 v2 ->
+		let idle = decode_idle v1 in
+		let cb = prepare_callback v2 0 in
+		encode_unit_result (Idle.start idle (fun() -> ignore(cb [])));
+	);
+	"stop", vfun1 (fun v ->
+		let idle = decode_idle v in
+		encode_unit_result (Idle.stop idle)
+	);
+]
+
+let timer_fields = [
+	"init", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_result (fun i -> VHandle (HTimer i)) (Timer.init ~loop ())
+	);
+	"start", vfun4 (fun v1 v2 v3 v4 ->
+		let timer = decode_timer v1
+		and cb = prepare_callback v2 0
+		and timeout = decode_int v3
+		and repeat = default_int v4 0 in
+		encode_unit_result (Timer.start ~repeat timer timeout (fun() -> ignore(cb [])));
+	);
+	"stop", vfun1 (fun v ->
+		let timer = decode_timer v in
+		encode_unit_result (Timer.stop timer)
+	);
+	"again", vfun1 (fun v ->
+		let timer = decode_timer v in
+		encode_unit_result (Timer.again timer)
+	);
+	"set_repeat", vfun2 (fun v1 v2 ->
+		let timer = decode_timer v1
+		and repeat = decode_int v2 in
+		Timer.set_repeat timer repeat;
+		vint repeat
+	);
+	"get_repeat", vfun1 (fun v1 ->
+		let timer = decode_timer v1 in
+		vint (Timer.get_repeat timer)
+	);
+	"get_dueIn", vfun1 (fun v1 ->
+		let timer = decode_timer v1 in
+		vint (Timer.get_due_in timer)
+	);
+]
+
+let async_fields = [
+	"init", vfun2 (fun v1 v2 ->
+		let loop = decode_loop v1
+		and cb = prepare_callback v2 1 in
+		let callback async = ignore(cb [VHandle (HAsync async)]) in
+		encode_result (fun i -> VHandle (HAsync i)) (Async.init ~loop callback)
+	);
+	"send", vfun1 (fun v ->
+		let async = decode_async v in
+		encode_unit_result (Async.send async);
+	);
+]
+
+let buffer_get getter = vfun2 (fun v1 v2 ->
+	let buffer = decode_buffer v1
+	and index = decode_int v2 in
+	vint (int_of_char (getter buffer index))
+)
+
+let buffer_set setter = vfun3 (fun v1 v2 v3 ->
+	let buffer = decode_buffer v1
+	and index = decode_int v2
+	and byte = decode_int v3 in
+	setter buffer index (char_of_int byte);
+	v3
+)
+
+let buffer_fields = [
+	"create", vfun1 (fun v ->
+		let size = decode_int v in
+		encode_buffer (Buffer.create size)
+	);
+	"fromNativeString", vfun1 (fun v ->
+		let s = decode_native_string v in
+		encode_buffer (Buffer.from_string s)
+	);
+	"fromString", vfun1 (fun v ->
+		let s = decode_string v in
+		encode_buffer (Buffer.from_string s)
+	);
+	"fromBytes", vfun1 (fun v ->
+		let b = decode_bytes v in
+		encode_buffer (Buffer.from_bytes b)
+	);
+	"totalSize", vfun1 (fun v ->
+		let l = decode_buffers v in
+		vint (Buffer.total_size l)
+	);
+	"drop", vfun2 (fun v1 v2 ->
+		let l = decode_buffers v1
+		and count = decode_int v2
+		and encode_buffer buffer = encode_buffer buffer in
+		encode_array (List.map encode_buffer (Buffer.drop l count))
+	);
+	"size", vfun1 (fun v ->
+		let buffer = decode_buffer v in
+		vint (Buffer.size buffer)
+	);
+	"get", buffer_get Buffer.get;
+	"unsafeGet", buffer_get Buffer.unsafe_get;
+	"set", buffer_set Buffer.set;
+	"unsafeSet", buffer_set Buffer.unsafe_set;
+	"sub", vfun3 (fun v1 v2 v3 ->
+		let buffer = decode_buffer v1
+		and offset = decode_int v2
+		and length = decode_int v3 in
+		encode_buffer (Buffer.sub buffer offset length)
+	);
+	"blit", vfun2 (fun v1 v2 ->
+		let buffer = decode_buffer v1
+		and destination = decode_buffer v2 in
+		Buffer.blit buffer destination;
+		vnull
+	);
+	"fill", vfun2 (fun v1 v2 ->
+		let buffer = decode_buffer v1
+		and byte = decode_int v2 in
+		Buffer.fill buffer (char_of_int byte);
+		vnull
+	);
+	"toString", vfun1 (fun v ->
+		let buffer = decode_buffer v in
+		EvalString.create_unknown (Buffer.to_string buffer)
+	);
+	"toNativeString", vfun1 (fun v ->
+		let buffer = decode_buffer v in
+		vnative_string (Buffer.to_string buffer)
+	);
+	"toBytes", vfun1 (fun v ->
+		let buffer = decode_buffer v in
+		encode_bytes (Buffer.to_bytes buffer)
+	);
+	"blitToBytes", vfun3 (fun v1 v2 v3 ->
+		let buffer = decode_buffer v1
+		and destination = decode_bytes v2
+		and offset = decode_int v3 in
+		Buffer.blit_to_bytes buffer destination offset;
+		vnull
+	);
+	"blitFromBytes", vfun3 (fun v1 v2 v3 ->
+		let buffer = decode_buffer v1
+		and source = decode_bytes v2
+		and offset = decode_int v3 in
+		Buffer.blit_from_bytes buffer source offset;
+		vnull
+	);
+	"blitFromBytes", vfun3 (fun v1 v2 v3 ->
+		let buffer = decode_buffer v1
+		and source = decode_native_string v2
+		and offset = decode_int v3 in
+		Buffer.blit_from_string buffer source offset;
+		vnull
+	);
+]
+
+let sockaddr_fields = [
+	"get_port", vfun1 (fun v ->
+		let a = decode_sockaddr v in
+		encode_nullable vint (Sockaddr.port a)
+	);
+	"ipv4", vfun2 (fun v1 v2 ->
+		let host = decode_string v1
+		and port = decode_int v2 in
+		encode_result encode_sockaddr (Sockaddr.ipv4 host port)
+	);
+	"ipv6", vfun2 (fun v1 v2 ->
+		let host = decode_string v1
+		and port = decode_int v2 in
+		encode_result encode_sockaddr (Sockaddr.ipv6 host port)
+	);
+	"toString", vfun1 (fun v ->
+		let a = decode_sockaddr v in
+		match Sockaddr.to_string a with
+		| Some s -> EvalString.create_unknown s
+		| None -> EvalString.vstring (EvalString.create_ascii "")
+	);
+]
+
+let tcp_fields = [
+	"init", vfun2 (fun v1 v2 ->
+		let loop = decode_loop v1
+		and domain = decode_optional decode_address_family v2 in
+		let tcp = TCP.init ~loop ?domain () in
+		encode_result (fun t -> VHandle (HTcp t)) tcp
+	);
+	"noDelay", vfun2 (fun v1 v2 ->
+		let tcp = decode_tcp v1
+		and value = decode_bool v2 in
+		encode_unit_result (TCP.nodelay tcp value)
+	);
+	"keepAlive", vfun2 (fun v1 v2 ->
+		let tcp = decode_tcp v1
+		and value = decode_option decode_int v2 in
+		encode_unit_result (TCP.keepalive tcp value)
+	);
+	"simultaneousAccepts", vfun2 (fun v1 v2 ->
+		let tcp = decode_tcp v1
+		and value = decode_bool v2 in
+		encode_unit_result (TCP.simultaneous_accepts tcp value)
+	);
+	"bind", vfun3 (fun v1 v2 v3 ->
+		let tcp = decode_tcp v1
+		and addr = decode_sockaddr v2
+		and ipv6only = decode_optional decode_bool v3 in
+		encode_unit_result (TCP.bind ?ipv6only tcp addr)
+	);
+	"getSockName", vfun1 (fun v ->
+		let tcp = decode_tcp v in
+		encode_result encode_sockaddr (TCP.getsockname tcp)
+	);
+	"getPeerName", vfun1 (fun v ->
+		let tcp = decode_tcp v in
+		encode_result encode_sockaddr (TCP.getpeername tcp)
+	);
+	"connect", vfun3 (fun v1 v2 v3 ->
+		let tcp = decode_tcp v1
+		and addr = decode_sockaddr v2 in
+		TCP.connect tcp addr (encode_unit_callback v3);
+		vnull
+	);
+	"closeReset", vfun2 (fun v1 v2 ->
+		let tcp = decode_tcp v1 in
+		TCP.close_reset tcp (encode_unit_callback v2);
+		vnull
+	);
+]
+
+let udp_fields = [
+	"init", vfun3 (fun v1 v2 v3 ->
+		let loop = decode_loop v1
+		and domain = decode_optional decode_address_family v2
+		and recvmmsg = decode_optional decode_bool v3 in
+		let udp = UDP.init ~loop ?domain ?recvmmsg () in
+		encode_result encode_udp udp
+	);
+	"bind", vfun4 (fun v1 v2 v3 v4 ->
+		let udp = decode_udp v1
+		and addr = decode_sockaddr v2
+		and ipv6only = decode_optional decode_bool v3
+		and reuseaddr = decode_optional decode_bool v4 in
+		encode_unit_result (UDP.bind ?ipv6only ?reuseaddr udp addr)
+	);
+	"connect", vfun2 (fun v1 v2 ->
+		let udp = decode_udp v1
+		and addr = decode_sockaddr v2 in
+		match UDP.Connected.connect udp addr with
+		| Ok () -> encode_result encode_udp (Ok udp)
+		| Error e -> encode_result encode_udp (Error e)
+	);
+	"getSockName", vfun1 (fun v ->
+		let udp = decode_udp v in
+		encode_result encode_sockaddr (UDP.getsockname udp)
+	);
+	"setMembership", vfun4 (fun v1 v2 v3 v4 ->
+		let udp = decode_udp v1
+		and group = decode_string v2
+		and interface = decode_string v3
+		and membership = decode_udp_membership v4 in
+		encode_unit_result (UDP.set_membership udp ~group ~interface membership)
+	);
+	"setSourceMembership", vfun5 (fun v1 v2 v3 v4 v5 ->
+		let udp = decode_udp v1
+		and group = decode_string v2
+		and interface = decode_string v3
+		and source = decode_string v4
+		and membership = decode_udp_membership v5 in
+		encode_unit_result (UDP.set_source_membership udp ~group ~interface ~source membership)
+	);
+	"setMulticastLoop", vfun2 (fun v1 v2 ->
+		let udp = decode_udp v1
+		and value = decode_bool v2 in
+		encode_unit_result (UDP.set_multicast_loop udp value)
+	);
+	"setMulticastTtl", vfun2 (fun v1 v2 ->
+		let udp = decode_udp v1
+		and value = decode_int v2 in
+		encode_unit_result (UDP.set_multicast_ttl udp value)
+	);
+	"setMulticastInterface", vfun2 (fun v1 v2 ->
+		let udp = decode_udp v1
+		and value = decode_string v2 in
+		encode_unit_result (UDP.set_multicast_interface udp value)
+	);
+	"setBroadcast", vfun2 (fun v1 v2 ->
+		let udp = decode_udp v1
+		and value = decode_bool v2 in
+		encode_unit_result (UDP.set_broadcast udp value)
+	);
+	"setTtl", vfun2 (fun v1 v2 ->
+		let udp = decode_udp v1
+		and value = decode_int v2 in
+		encode_unit_result (UDP.set_ttl udp value)
+	);
+	"send", vfun4 (fun v1 v2 v3 v4 ->
+		let udp = decode_udp v1
+		and l = decode_buffers v2
+		and addr = decode_sockaddr v3 in
+		UDP.send udp l addr (encode_unit_callback v4);
+		vnull
+	);
+	"trySend", vfun3 (fun v1 v2 v3 ->
+		let udp = decode_udp v1
+		and l = decode_buffers v2
+		and addr = decode_sockaddr v3 in
+		encode_unit_result (UDP.try_send udp l addr)
+	);
+	"recvStart", vfun3 (fun v1 v2 v3 ->
+		let encode (buf,addr,flags) =
+			let encode_flag = function
+				| `PARTIAL -> vint 0
+				| `MMSG_CHUNK -> vint 1
+				| `MMSG_FREE -> vint 2
+			in
+			encode_obj [
+				key_data,encode_buffer buf;
+				key_addr,encode_option encode_sockaddr addr;
+				key_flags,encode_array (List.map encode_flag flags)
+			]
+		in
+		let udp = decode_udp v1
+		and callback = encode_callback encode v2
+		and allocate =
+			decode_optional (fun v ->
+				let cb = prepare_callback v 1 in
+				(fun i -> decode_buffer (cb [vint i]))
+			) v3
+		in
+		UDP.recv_start ?allocate udp callback;
+		vnull
+	);
+	"recvStop", vfun1 (fun v ->
+		let udp = decode_udp v in
+		encode_unit_result (UDP.recv_stop udp)
+	);
+	"getSendQueueSize", vfun1 (fun v ->
+		let udp = decode_udp v in
+		vint (UDP.get_send_queue_size udp)
+	);
+	"getSendQueueCount", vfun1 (fun v ->
+		let udp = decode_udp v in
+		vint (UDP.get_send_queue_count udp)
+	);
+]
+
+let connected_udp_fields = [
+	"disconnect", vfun1 (fun v ->
+		let udp = decode_udp v in
+		encode_unit_result (UDP.Connected.disconnect udp)
+	);
+	"getPeerName", vfun1 (fun v ->
+		let udp = decode_udp v in
+		encode_result encode_sockaddr (UDP.Connected.getpeername udp)
+	);
+	"send", vfun3 (fun v1 v2 v3 ->
+		let udp = decode_udp v1
+		and l = decode_buffers v2 in
+		UDP.Connected.send udp l (encode_unit_callback v3);
+		vnull
+	);
+	"send", vfun2 (fun v1 v2 ->
+		let udp = decode_udp v1
+		and l = decode_buffers v2 in
+		encode_unit_result (UDP.Connected.try_send udp l)
+	);
+]
+
+let pipe_fields = [
+	"init", vfun2 (fun v1 v2 ->
+		let loop = decode_loop v1
+		and for_handle_passing = decode_optional decode_bool v2 in
+		encode_result (fun p -> VHandle (HPipe p)) (Pipe.init ~loop ?for_handle_passing ())
+	);
+	"bind", vfun2 (fun v1 v2 ->
+		let pipe = decode_pipe v1
+		and name = decode_native_string v2 in
+		encode_unit_result (Pipe.bind pipe name)
+	);
+	"connect", vfun3 (fun v1 v2 v3 ->
+		let pipe = decode_pipe v1
+		and target = decode_native_string v2 in
+		Pipe.connect pipe target (encode_unit_callback v3);
+		vnull
+	);
+	"getSockName", vfun1 (fun v ->
+		let pipe = decode_pipe v in
+		encode_result vnative_string (Pipe.getsockname pipe)
+	);
+	"getPeerName", vfun1 (fun v ->
+		let pipe = decode_pipe v in
+		encode_result vnative_string (Pipe.getpeername pipe)
+	);
+	"pendingInstances", vfun2 (fun v1 v2 ->
+		let pipe = decode_pipe v1
+		and amount = decode_int v2 in
+		Pipe.pending_instances pipe amount;
+		vnull
+	);
+	"receiveHandle", vfun1 (fun v ->
+		let pipe = decode_pipe v in
+		let index,args =
+			match Pipe.receive_handle pipe with
+			| `None ->
+				0,[||]
+			| `TCP assoc ->
+				1,[|vfun1 (fun v -> encode_unit_result (assoc (decode_tcp v)))|]
+			| `Pipe assoc ->
+				2,[|vfun1 (fun v -> encode_unit_result (assoc (decode_pipe v)))|]
+		in
+		encode_enum_value key_eval_luv_ReceiveHandle index args None
+	);
+	"chmod", vfun2 (fun v1 v2 ->
+		let pipe = decode_pipe v1
+		and mode =
+			match decode_int v2 with
+			| 0 -> [`READABLE]
+			| 1 -> [`WRITABLE]
+			| 2 -> [`READABLE; `WRITABLE]
+			| _ -> unexpected_value v2 "eval.luv.Pipe.PipeMode"
+		in
+		encode_unit_result (Pipe.chmod pipe mode)
+	);
+]
+
+let tty_fields = [
+	"init", vfun2 (fun v1 v2 ->
+		let loop = decode_loop v1
+		and file = decode_file v2 in
+		encode_result (fun tty -> VHandle (HTty tty)) (TTY.init ~loop file)
+	);
+	"setMode", vfun2 (fun v1 v2 ->
+		let tty = decode_tty v1
+		and mode =
+			match decode_int v2 with
+			| 0 -> `NORMAL
+			| 1 -> `RAW
+			| 2 -> `IO
+			| _ -> unexpected_value v2 "eval.luv.Tty.TtyMode"
+		in
+		encode_unit_result (TTY.set_mode tty mode)
+	);
+	"resetMode", vfun0 (fun () ->
+		encode_unit_result (TTY.reset_mode ())
+	);
+	"getWinSize", vfun1 (fun v ->
+		let tty = decode_tty v in
+		let encode (w,h) = encode_obj [key_width,vint w; key_height,vint h] in
+		encode_result encode (TTY.get_winsize tty)
+	);
+	"setVTermState", vfun1 (fun v ->
+		let state =
+			match decode_int v with
+			| 0 -> `SUPPORTED
+			| 1 -> `UNSUPPORTED
+			| _ -> unexpected_value v "eval.luv.Tty.VTermState"
+		in
+		TTY.set_vterm_state state;
+		vnull
+	);
+	"getVTermState", vfun0 (fun () ->
+		let encode state =
+			vint (match state with
+			| `SUPPORTED -> 0
+			| `UNSUPPORTED -> 1)
+		in
+		encode_result encode (TTY.get_vterm_state())
+	);
+]
+
+let stream_fields = [
+	"shutdown", vfun2 (fun v1 v2 ->
+		let stream = decode_stream v1 in
+		Stream.shutdown stream (encode_unit_callback v2);
+		vnull
+	);
+	"listen", vfun2 (fun v1 v2 ->
+		let stream = decode_stream v1 in
+		Stream.listen stream (encode_unit_callback v2);
+		vnull
+	);
+	"accept", vfun2 (fun v1 v2 ->
+		let server = decode_stream v1
+		and client = decode_stream v2 in
+		encode_unit_result (Stream.accept server client)
+	);
+	"readStart", vfun3 (fun v1 v2 v3 ->
+		let stream = decode_stream v1
+		and callback = encode_callback encode_buffer v2
+		and allocate =
+			decode_optional (fun v ->
+				let cb = prepare_callback v 1 in
+				(fun i -> decode_buffer (cb [vint i]))
+			) v3
+		in
+		Stream.read_start ?allocate stream callback;
+		vnull
+	);
+	"readStop", vfun1 (fun v ->
+		let stream = decode_stream v in
+		encode_unit_result (Stream.read_stop stream)
+	);
+	"write", vfun3 (fun v1 v2 v3 ->
+		let stream = decode_stream v1
+		and data = decode_buffers v2
+		and callback =
+			let cb = prepare_callback v3 2 in
+			(fun result bytes_written ->
+				ignore(cb [encode_unit_result result; vint bytes_written])
+			)
+		in
+		Stream.write stream data callback;
+		vnull
+	);
+	"write2", vfun4 (fun v1 v2 v3 v4 ->
+		let stream = decode_pipe v1
+		and data = decode_buffers v2
+		and callback =
+			let cb = prepare_callback v4 2 in
+			(fun result bytes_written ->
+				ignore(cb [encode_unit_result result; vint bytes_written])
+			)
+		in
+		(match decode_enum v3 with
+		| 0,[vh] -> Stream.write2 stream data ~send_handle:(decode_tcp vh) callback
+		| 1,[vh] -> Stream.write2 stream data ~send_handle:(decode_pipe vh) callback
+		| _ -> unexpected_value v3 "eval.luv.Stream.SendHandle"
+		);
+		vnull
+	);
+	"tryWrite", vfun2 (fun v1 v2 ->
+		let stream = decode_pipe v1
+		and data = decode_buffers v2 in
+		encode_result vint (Stream.try_write stream data)
+	);
+	"isReadable", vfun1 (fun v ->
+		let stream = decode_pipe v in
+		vbool (Stream.is_readable stream)
+	);
+	"isWritable", vfun1 (fun v ->
+		let stream = decode_pipe v in
+		vbool (Stream.is_writable stream)
+	);
+	"setBlocking", vfun2 (fun v1 v2 ->
+		let stream = decode_pipe v1
+		and block = decode_bool v2 in
+		encode_unit_result (Stream.set_blocking stream block)
+	);
+]
+
+let signum_fields = [
+	"SIGABRT", vint Signal.sigabrt;
+	"SIGFPE", vint Signal.sigfpe;
+	"SIGHUP", vint Signal.sighup;
+	"SIGILL", vint Signal.sigill;
+	"SIGINT", vint Signal.sigint;
+	"SIGKILL", vint Signal.sigkill;
+	"SIGSEGV", vint Signal.sigsegv;
+	"SIGTERM", vint Signal.sigterm;
+	"SIGWINCH", vint Signal.sigwinch;
+]
+
+let signal_fields = [
+	"init", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_result (fun s -> VHandle (HSignal s)) (Signal.init ~loop ())
+	);
+	"start", vfun3 (fun v1 v2 v3 ->
+		let s = decode_signal v1
+		and signum = decode_int v2
+		and cb = prepare_callback v3 0 in
+		encode_unit_result (Signal.start s signum (fun() -> ignore(cb [])))
+	);
+	"startOneshot", vfun3 (fun v1 v2 v3 ->
+		let s = decode_signal v1
+		and signum = decode_int v2
+		and cb = prepare_callback v3 0 in
+		encode_unit_result (Signal.start_oneshot s signum (fun() -> ignore(cb [])))
+	);
+	"stop", vfun1 (fun v ->
+		let s = decode_signal v in
+		encode_unit_result (Signal.stop s)
+	);
+	"signum", vfun1 (fun v ->
+		let s = decode_signal v in
+		vint (Signal.signum s)
+	);
+]
+
+let process_fields = [
+	"stdin", vint Process.stdin;
+	"stdout", vint Process.stdout;
+	"stderr", vint Process.stderr;
+	"toParentPipe", vfun5 (fun v1 v2 v3 v4 v5 ->
+		let fd = decode_int v1
+		and parent_pipe = decode_pipe v2
+		and readable_in_child = decode_bool v3
+		and writable_in_child = decode_bool v4
+		and overlapped = decode_bool v5 in
+		let r = Process.to_parent_pipe ~fd ~parent_pipe ~readable_in_child ~writable_in_child ~overlapped () in
+		VHandle (HRedirection r)
+	);
+	"inheritFd", vfun2 (fun v1 v2 ->
+		let fd = decode_int v1
+		and from_parent_fd = decode_int v2 in
+		let r = Process.inherit_fd ~fd ~from_parent_fd () in
+		VHandle (HRedirection r)
+	);
+	"inheritStream", vfun2 (fun v1 v2 ->
+		let fd = decode_int v1
+		and from_parent_stream = decode_stream v2 in
+		let r = Process.inherit_stream ~fd ~from_parent_stream () in
+		VHandle (HRedirection r)
+	);
+	"spawn", vfun4 (fun v1 v2 v3 v4 ->
+		let loop = decode_loop v1
+		and cmd = decode_native_string v2
+		and args = List.map decode_native_string (decode_array v3) in
+		let result =
+			if v4 = VNull then
+				Process.spawn ~loop cmd args
+			else begin
+				let options = decode_object v4 in
+				let get name_hash f =
+					let v = object_field options name_hash in
+					decode_optional f v
+				in
+				let on_exit =
+					get key_onExit (fun v ->
+						let cb = prepare_callback v 3 in
+						(fun p ~exit_status ~term_signal ->
+							ignore(cb [VHandle (HProcess p); VInt64 exit_status; vint term_signal])
+						)
+					)
+				and environment =
+					get key_environment (fun v ->
+						match decode_instance v with
+						| { ikind = IStringMap m } ->
+							StringHashtbl.fold (fun k (_,v) acc -> (k, decode_native_string v) :: acc) m []
+						| _ ->
+							unexpected_value v "haxe.ds.Map<String,String>"
+					)
+				and redirect =
+					get key_redirect (fun v ->
+						List.map (fun v2 ->
+							match v2 with
+							| VHandle (HRedirection r) -> r
+							| _ -> unexpected_value v2 "eval.luv.Process.Redirection"
+						) (decode_array v)
+					)
+				and working_directory = get key_workingDirectory decode_native_string
+				and uid = get key_uid decode_int
+				and gid = get key_gid decode_int
+				and windows_verbatim_arguments = get key_windowsVerbatimArguments decode_bool
+				and detached = get key_detached decode_bool
+				and windows_hide = get key_windowsHide decode_bool
+				and windows_hide_console = get key_windowsHideConsole decode_bool
+				and windows_hide_gui = get key_windowsHideGui decode_bool
+				in
+				(* Process.spawn ~loop ?detached cmd args *)
+				Process.spawn ~loop ?on_exit ?environment ?working_directory ?redirect
+					?uid ?gid ?windows_verbatim_arguments ?detached ?windows_hide
+					?windows_hide_console ?windows_hide_gui cmd args
+			end
+		in
+		encode_result (fun p -> VHandle (HProcess p)) result
+	);
+	"disableStdioInheritance", vfun0 (fun() ->
+		Process.disable_stdio_inheritance();
+		vnull
+	);
+	"killPid", vfun2 (fun v1 v2 ->
+		let pid = decode_int v1
+		and sig_num = decode_int v2 in
+		encode_unit_result (Process.kill_pid ~pid sig_num)
+	);
+	"pid", vfun1 (fun v ->
+		let p = decode_process v in
+		vint (Process.pid p)
+	);
+]
+
+let request_fields = [
+	"cancel", vfun1 (fun v ->
+		encode_unit_result (match v with
+			| VHandle (HFileRequest r) -> Request.cancel r
+			| VHandle (HAddrRequest r) -> Request.cancel r
+			| VHandle (HNameRequest r) -> Request.cancel r
+			| VHandle (HRandomRequest r) -> Request.cancel r
+			| VHandle (HThreadPoolRequest r) -> Request.cancel r
+			| _ -> unexpected_value v "eval.luv.Request"
+		)
+	)
+]
+
+let dns_fields = [
+	"createAddrRequest", vfun0 (fun () ->
+		VHandle (HAddrRequest (DNS.Addr_info.Request.make()))
+	);
+	"createInfoRequest", vfun0 (fun () ->
+		VHandle (HNameRequest (DNS.Name_info.Request.make()))
+	);
+	"getAddrInfo", vfun5 (fun v1 v2 v3 v4 v5 ->
+		let loop = decode_loop v1
+		and node = decode_optional decode_string v2
+		and service = decode_optional decode_string v3
+		in
+		if node = None && service = None then
+			throw (create_haxe_exception "Either node or service has to be not null") null_pos
+		else begin
+			let callback =
+				let cb = prepare_callback v5 1 in
+				(fun result ->
+					let v =
+						encode_result (fun infos ->
+							encode_array (List.map (fun (info:DNS.Addr_info.t) ->
+								let fields = [
+									key_family,encode_address_family info.family;
+									key_sockType,encode_socket_type info.socktype;
+									key_protocol,vint info.protocol;
+									key_addr,encode_sockaddr info.addr;
+								] in
+								let fields =
+									match info.canonname with
+									| None -> fields
+									| Some s -> (key_canonName,EvalString.create_unknown s) :: fields
+								in
+								encode_obj fields
+							) infos)
+						) result
+					in
+					ignore(cb [v])
+				)
+			in
+			if v4 = VNull then
+				DNS.getaddrinfo ~loop ?node ?service () callback
+			else begin
+				let options = decode_object v4 in
+				let get name_hash f =
+					let v = object_field options name_hash in
+					decode_optional f v
+				in
+				let request =
+					get key_request (function
+						| VHandle (HAddrRequest r) -> r
+						| v -> unexpected_value v "eval.luv.Dns.AddrInfoRequest"
+					)
+				and family = get key_family decode_address_family
+				and socktype = get key_sockType decode_socket_type
+				and protocol = get key_protocol decode_int
+				and flags =
+					get key_flags (fun v ->
+						List.map (fun v ->
+							match decode_int v with
+							| 0 -> `PASSIVE
+							| 1 -> `CANONNAME
+							| 2 -> `NUMERICHOST
+							| 3 -> `NUMERICSERV
+							| 4 -> `V4MAPPED
+							| 5 -> `ALL
+							| 6 -> `ADDRCONFIG
+							| _ -> unexpected_value v "eval.luv.Dns.AddrInfoFlag"
+						) (decode_array v)
+					)
+				in
+				DNS.getaddrinfo ~loop ?request ?family ?socktype ?protocol ?flags ?service ?node () callback
+			end;
+			vnull
+		end
+	);
+	"getNameInfo", vfun4 (fun v1 v2 v3 v4 ->
+		let loop = decode_loop v1
+		and addr = decode_sockaddr v2
+		and callback =
+			let cb = prepare_callback v4 1 in
+			(fun result ->
+				let v =
+					encode_result (fun (node,service) ->
+						encode_obj [
+							key_node,encode_string node;
+							key_service,encode_string service;
+						]
+					) result
+				in
+				ignore(cb [v])
+			)
+		in
+		if v3 = VNull then
+			DNS.getnameinfo ~loop addr callback
+		else begin
+			let options = decode_object v3 in
+			let get name_hash f =
+				let v = object_field options name_hash in
+				decode_optional f v
+			in
+			let request =
+				get key_request (function
+					| VHandle (HNameRequest r) -> r
+					| v -> unexpected_value v "eval.luv.Dns.NameInfoRequest"
+				)
+			and flags =
+				get key_flags (fun v ->
+					List.map (fun v ->
+						match decode_int v with
+						| 0 -> `NAMEREQD
+						| 1 -> `DGRAM
+						| 2 -> `NOFQDN
+						| 3 -> `NUMERICHOST
+						| 4 -> `NUMERICSERV
+						| _ -> unexpected_value v "eval.luv.Dns.NameInfoFlag"
+					) (decode_array v)
+				)
+			in
+			DNS.getnameinfo ~loop ?request ?flags addr callback
+		end;
+		vnull
+	);
+]
+
+module F = struct
+	let async ~vloop ~vrequest fn =
+		let loop = Some (decode_loop vloop)
+		and request = decode_optional decode_file_request vrequest in
+		fn ?loop ?request
+
+	let path ~vpath fn =
+		fn (decode_native_string vpath)
+
+	let file ~vfile fn =
+		fn (decode_file vfile)
+
+	let dir ~vdir fn =
+		fn (decode_dir vdir)
+
+	let to_ ~vto fn =
+		let to_ = decode_native_string vto in
+		fn ~to_
+
+	let mode ~vmode fn =
+		fn (decode_file_mode_list vmode)
+
+	let mode_opt ~vmode fn =
+		let mode = decode_optional decode_file_mode_list vmode in
+		fn ?mode
+
+	let open_ ~vmode ~vpath ~vflags fn =
+		let flags = List.map decode_file_open_flag (decode_array vflags) in
+		(fn |> mode_opt ~vmode |> path ~vpath) flags
+
+	let rename ~vpath ~vto fn =
+		fn |> path ~vpath |> to_ ~vto
+
+	let mkdir ~vmode ~vpath fn =
+		fn |> mode_opt ~vmode |> path ~vpath
+
+	let data ~vfile_offset ~vfile ~vbuffers fn =
+		let file = decode_file vfile
+		and file_offset = Some (decode_i64 vfile_offset)
+		and buffers = decode_buffers vbuffers in
+		fn ?file_offset file buffers
+
+	let ftruncate ~vfile ~vlength fn =
+		let file = decode_file vfile
+		and length = decode_i64 vlength in
+		fn file length
+
+	let copyFile ~vflags ~vpath ~vto fn =
+		let flags = decode_int_flags vflags in
+		let excl = if List.mem 0 flags then Some true else None
+		and ficlone = if List.mem 1 flags then Some true else None
+		and ficlone_force = if List.mem 2 flags then Some true else None in
+		(fn ?excl ?ficlone ?ficlone_force) |> path ~vpath |> to_ ~vto
+
+	let sendFile ~vfile ~vto ~voffset ~vlength fn =
+		let to_ = decode_file vto
+		and offset = decode_i64 voffset
+		and length = decode_size_t vlength in
+		(fn |> file ~vfile) ~to_ ~offset length
+
+	let access ~vpath ~vflags fn =
+		let flags =
+			List.map (fun v ->
+				match decode_int v with
+				| 0 -> `F_OK
+				| 1 -> `R_OK
+				| 2 -> `W_OK
+				| 3 -> `X_OK
+				| _ -> unexpected_value v "eval.luv.File.FileAccessFlag"
+			) (decode_array vflags) in
+		(fn |> path ~vpath) flags
+
+	let utime ~vatime ~vmtime fn =
+		let atime = decode_float vatime
+		and mtime = decode_float vmtime in
+		fn ~atime ~mtime
+
+	let link ~vlink fn =
+		let link = decode_native_string vlink in
+		fn ~link
+
+	let symlink ~vflags fn =
+		let flags = decode_int_flags vflags in
+		let dir = if List.mem 0 flags then Some true else None
+		and junction = if List.mem 1 flags then Some true else None in
+		fn ?dir ?junction
+
+	let chown ~vuid ~vgid fn =
+		let uid = decode_int vuid
+		and gid = decode_int vgid in
+		fn ~uid ~gid
+
+	let readdir ~vdir ~vnumber_of_entries fn =
+		let number_of_entries = decode_optional decode_int vnumber_of_entries in
+		fn ?number_of_entries |> dir ~vdir
+end
+
+let file_fields = [
+	"get_stdin", VHandle (HFile File.stdin);
+	"get_stdout", VHandle (HFile File.stdout);
+	"get_stderr", VHandle (HFile File.stderr);
+	"createRequest", vfun0 (fun() ->
+		VHandle (HFileRequest (File.Request.make()))
+	);
+	"testMode", vfun2 (fun v1 v2 ->
+		let mask = decode_file_mode_list v1
+		and bits =
+			match v2 with
+			| VHandle (HFileModeNumeric m) -> m
+			| _ -> unexpected_value v2 "eval.luv.File.FileModeNumeric"
+		in
+		vbool (File.Mode.test mask bits)
+	);
+	"open", vfun6 (fun vloop vpath vflags vmode vrequest vcallback ->
+		let callback = encode_callback (fun f -> VHandle (HFile f)) vcallback in
+		(File.open_ |> F.async ~vloop ~vrequest |> F.open_ ~vmode ~vpath ~vflags) callback;
+		vnull
+	);
+	"close", vfun4 (fun vfile vloop vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.close |> F.async ~vloop ~vrequest |> F.file ~vfile) callback;
+		vnull
+	);
+	"read", vfun6 (fun vfile vloop vfile_offset vbuffers vrequest vcallback ->
+		let callback = encode_callback encode_size_t vcallback in
+		(File.read |> F.async ~vloop ~vrequest |> F.data ~vfile_offset ~vfile ~vbuffers) callback;
+		vnull
+	);
+	"write", vfun6 (fun vfile vloop vfile_offset vbuffers vrequest vcallback ->
+		let callback = encode_callback encode_size_t vcallback in
+		(File.write |> F.async ~vloop ~vrequest |> F.data ~vfile_offset ~vfile ~vbuffers) callback;
+		vnull
+	);
+	"unlink", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.unlink |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"rename", vfun5 (fun vloop vpath vto vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.rename |> F.async ~vloop ~vrequest |> F.rename ~vpath ~vto) callback;
+		vnull
+	);
+	"mkstemp", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback =
+			encode_callback (fun (n,file) ->
+				encode_obj [key_name,vnative_string n; key_file,VHandle (HFile file)]
+			) vcallback
+		in
+		(File.mkstemp |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"mkdtemp", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_callback vnative_string vcallback in
+		(File.mkdtemp |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"mkdir", vfun5 (fun vloop vpath vmode vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.mkdir |> F.async ~vloop ~vrequest |> F.mkdir ~vmode ~vpath) callback;
+		vnull
+	);
+	"rmdir", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.rmdir |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"stat", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_callback encode_file_stat vcallback in
+		(File.stat |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"lstat", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_callback encode_file_stat vcallback in
+		(File.lstat |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"fstat", vfun4 (fun vfile vloop vrequest vcallback ->
+		let callback = encode_callback encode_file_stat vcallback in
+		(File.fstat |> F.async ~vloop ~vrequest |> F.file ~vfile) callback;
+		vnull
+	);
+	"statFs", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_callback encode_file_statfs vcallback in
+		(File.statfs |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"fsync", vfun4 (fun vfile vloop vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.fsync |> F.async ~vloop ~vrequest |> F.file ~vfile) callback;
+		vnull
+	);
+	"fdataSync", vfun4 (fun vfile vloop vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.fdatasync |> F.async ~vloop ~vrequest |> F.file ~vfile) callback;
+		vnull
+	);
+	"ftruncate", vfun5 (fun vfile vloop vlength vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.ftruncate |> F.async ~vloop ~vrequest |> F.ftruncate ~vfile ~vlength) callback;
+		vnull
+	);
+	"copyFile", vfun6 (fun vloop vpath vto vflags vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.copyfile |> F.async ~vloop ~vrequest |> F.copyFile ~vflags ~vpath ~vto) callback;
+		vnull
+	);
+	"sendFile", vfun7 (fun vfile vloop vto voffset vlength vrequest vcallback ->
+		let callback = encode_callback encode_size_t vcallback in
+		(File.sendfile |> F.async ~vloop ~vrequest |> F.sendFile ~vfile ~vto ~voffset ~vlength) callback;
+		vnull
+	);
+	"access", vfun5 (fun vloop vpath vflags vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.access |> F.async ~vloop ~vrequest |> F.access ~vpath ~vflags) callback;
+		vnull
+	);
+	"chmod", vfun5 (fun vloop vpath vmode vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.chmod |> F.async ~vloop ~vrequest |> F.path ~vpath |> F.mode ~vmode) callback;
+		vnull
+	);
+	"fchmod", vfun5 (fun vfile vloop vmode vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.fchmod |> F.async ~vloop ~vrequest |> F.file ~vfile |> F.mode ~vmode) callback;
+		vnull
+	);
+	"utime", vfun6 (fun vloop vpath vatime vmtime vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.utime |> F.async ~vloop ~vrequest |> F.path ~vpath |> F.utime ~vatime ~vmtime) callback;
+		vnull
+	);
+	"lutime", vfun6 (fun vloop vpath vatime vmtime vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.lutime |> F.async ~vloop ~vrequest |> F.path ~vpath |> F.utime ~vatime ~vmtime) callback;
+		vnull
+	);
+	"futime", vfun6 (fun vfile vloop vatime vmtime vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.futime |> F.async ~vloop ~vrequest |> F.file ~vfile |> F.utime ~vatime ~vmtime) callback;
+		vnull
+	);
+	"link", vfun5 (fun vloop vpath vlink vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.link |> F.async ~vloop ~vrequest |> F.path ~vpath |> F.link ~vlink) callback;
+		vnull
+	);
+	"symlink", vfun6 (fun vloop vpath vlink vflags vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.symlink |> F.async ~vloop ~vrequest |> F.symlink ~vflags |> F.path ~vpath |> F.link ~vlink) callback;
+		vnull
+	);
+	"readLink", vfun4 (fun vloop vpath vrequest v4 ->
+		let callback = encode_callback vnative_string v4 in
+		(File.readlink |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"realPath", vfun4 (fun vloop vpath vrequest v4 ->
+		let callback = encode_callback vnative_string v4 in
+		(File.realpath |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"chown", vfun6 (fun vloop vpath vuid vgid vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.chown |> F.async ~vloop ~vrequest |> F.path ~vpath |> F.chown ~vuid ~vgid) callback;
+		vnull
+	);
+	"lchown", vfun6 (fun vloop vpath vuid vgid vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.lchown |> F.async ~vloop ~vrequest |> F.path ~vpath |> F.chown ~vuid ~vgid) callback;
+		vnull
+	);
+	"fchown", vfun6 (fun vloop vfile vuid vgid vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.fchown |> F.async ~vloop ~vrequest |> F.file ~vfile |> F.chown ~vuid ~vgid) callback;
+		vnull
+	);
+	"toInt", vfun1 (fun v ->
+		let file = decode_file v in
+		vint (File.to_int file)
+	);
+]
+
+let file_sync_fields = [
+	"open", vfun3 (fun vpath vflags vmode ->
+		File.Sync.open_ |> F.open_ ~vmode ~vpath ~vflags |> encode_result (fun f -> VHandle (HFile f))
+	);
+	"close", vfun1 (fun vfile ->
+		File.Sync.close |> F.file ~vfile |> encode_unit_result
+	);
+	"read", vfun3 (fun vfile vfile_offset vbuffers ->
+		File.Sync.read |> F.data ~vfile_offset ~vfile ~vbuffers |> encode_result encode_size_t
+	);
+	"write", vfun3 (fun vfile vfile_offset vbuffers ->
+		File.Sync.write |> F.data ~vfile_offset ~vfile ~vbuffers |> encode_result encode_size_t
+	);
+	"unlink", vfun1 (fun vpath ->
+		File.Sync.unlink |> F.path ~vpath |> encode_unit_result
+	);
+	"rename", vfun2 (fun vpath vto ->
+		File.Sync.rename |> F.rename ~vpath ~vto |> encode_unit_result
+	);
+	"mkstemp", vfun1 (fun vpath ->
+		let encode (n,file) =
+			encode_obj [key_name,vnative_string n; key_file,VHandle (HFile file)]
+		in
+		File.Sync.mkstemp |> F.path ~vpath |> encode_result encode
+	);
+	"mkdtemp", vfun1 (fun vpath ->
+		File.Sync.mkdtemp |> F.path ~vpath |> encode_result vnative_string
+	);
+	"mkdir", vfun2 (fun vpath vmode ->
+		File.Sync.mkdir |> F.mkdir ~vmode ~vpath |> encode_unit_result
+	);
+	"rmdir", vfun1 (fun vpath ->
+		File.Sync.rmdir |> F.path ~vpath |> encode_unit_result
+	);
+	"stat", vfun1 (fun vpath ->
+		File.Sync.stat |> F.path ~vpath |> encode_result encode_file_stat
+	);
+	"lstat", vfun1 (fun vpath ->
+		File.Sync.lstat |> F.path ~vpath |> encode_result encode_file_stat
+	);
+	"fstat", vfun1 (fun vfile ->
+		File.Sync.fstat |> F.file ~vfile |> encode_result encode_file_stat
+	);
+	"statFs", vfun1 (fun vpath ->
+		File.Sync.statfs |> F.path ~vpath |> encode_result encode_file_statfs
+	);
+	"fsync", vfun1 (fun vfile ->
+		File.Sync.fsync |> F.file ~vfile |> encode_unit_result
+	);
+	"fdataSync", vfun1 (fun vfile ->
+		File.Sync.fdatasync |> F.file ~vfile |> encode_unit_result
+	);
+	"ftruncate", vfun2 (fun vfile vlength ->
+		File.Sync.ftruncate |> F.ftruncate ~vfile ~vlength |> encode_unit_result
+	);
+	"copyFile", vfun3 (fun vpath vto vflags ->
+		File.Sync.copyfile |> F.copyFile ~vflags ~vpath ~vto |> encode_unit_result
+	);
+	"sendFile", vfun4 (fun vfile vto voffset vlength ->
+		File.Sync.sendfile |> F.sendFile ~vfile ~vto ~voffset ~vlength |> encode_result encode_size_t
+	);
+	"access", vfun2 (fun vpath vflags ->
+		File.Sync.access |> F.access ~vpath ~vflags |> encode_unit_result
+	);
+	"chmod", vfun2 (fun vpath vmode ->
+		File.Sync.chmod |> F.path ~vpath |> F.mode ~vmode |> encode_unit_result
+	);
+	"fchmod", vfun2 (fun vfile vmode ->
+		File.Sync.fchmod |> F.file ~vfile |> F.mode ~vmode |> encode_unit_result
+	);
+	"utime", vfun3 (fun vpath vatime vmtime ->
+		File.Sync.utime |> F.path ~vpath |> F.utime ~vatime ~vmtime |> encode_unit_result
+	);
+	"lutime", vfun3 (fun vpath vatime vmtime ->
+		File.Sync.lutime |> F.path ~vpath |> F.utime ~vatime ~vmtime |> encode_unit_result
+	);
+	"futime", vfun3 (fun vfile vatime vmtime ->
+		File.Sync.futime |> F.file ~vfile |> F.utime ~vatime ~vmtime |> encode_unit_result
+	);
+	"link", vfun2 (fun vpath vlink ->
+		File.Sync.link |> F.path ~vpath |> F.link ~vlink |> encode_unit_result
+	);
+	"symlink", vfun3 (fun vpath vlink vflags ->
+		File.Sync.symlink |> F.symlink ~vflags |> F.path ~vpath |> F.link ~vlink |> encode_unit_result
+	);
+	"readLink", vfun1 (fun vpath ->
+		File.Sync.readlink |> F.path ~vpath |> encode_result vnative_string
+	);
+	"realPath", vfun1 (fun vpath ->
+		File.Sync.realpath |> F.path ~vpath |> encode_result vnative_string
+	);
+	"chown", vfun3 (fun vpath vuid vgid ->
+		File.Sync.chown |> F.path ~vpath |> F.chown ~vuid ~vgid |> encode_unit_result
+	);
+	"lchown", vfun3 (fun vpath vuid vgid ->
+		File.Sync.lchown |> F.path ~vpath |> F.chown ~vuid ~vgid |> encode_unit_result
+	);
+	"fchown", vfun3 (fun vfile vuid vgid ->
+		File.Sync.fchown |> F.file ~vfile |> F.chown ~vuid ~vgid |> encode_unit_result
+	);
+]
+
+let dir_fields = [
+	"open", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_callback (fun dir -> VHandle (HDir dir)) vcallback in
+		(File.opendir |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+	"close", vfun4 (fun vdir vloop vrequest vcallback ->
+		let callback = encode_unit_callback vcallback in
+		(File.closedir |> F.async ~vloop ~vrequest |> F.dir ~vdir) callback;
+		vnull
+	);
+	"read", vfun5 (fun vdir vloop vnumber_of_entries vrequest vcallback ->
+		let callback =
+			encode_callback (fun a ->
+				encode_array_a (Array.map encode_dirent a)
+			) vcallback
+		in
+		(File.readdir |> F.async ~vloop ~vrequest |> F.readdir ~vnumber_of_entries ~vdir) callback;
+		vnull
+	);
+	"scan", vfun4 (fun vloop vpath vrequest vcallback ->
+		let callback = encode_callback encode_scandir vcallback in
+		(File.scandir |> F.async ~vloop ~vrequest |> F.path ~vpath) callback;
+		vnull
+	);
+]
+
+let dir_sync_fields = [
+	"open", vfun1 (fun vpath ->
+		File.Sync.opendir |> F.path ~vpath |> encode_result (fun dir -> VHandle (HDir dir))
+	);
+	"close", vfun1 (fun vdir ->
+		File.Sync.closedir |> F.dir ~vdir |> encode_unit_result
+	);
+	"read", vfun2 (fun vdir vnumber_of_entries ->
+		let encode a =
+			encode_array_a (Array.map encode_dirent a)
+		in
+		File.Sync.readdir |> F.readdir ~vnumber_of_entries ~vdir |> encode_result encode
+	);
+	"scan", vfun1 (fun vpath ->
+		File.Sync.scandir |> F.path ~vpath |> encode_result encode_scandir
+	);
+]
+
+let fs_event_fields = [
+	"init", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_result (fun e -> VHandle (HFsEvent e)) (FS_event.init ~loop ())
+	);
+	"start", vfun4 (fun v1 v2 v3 v4 ->
+		let event = decode_fs_event v1
+		and path = decode_native_string v2
+		and callback =
+			encode_callback (fun (file,events) ->
+				let vevents =
+					List.map (fun (e:FS_event.Event.t) ->
+						match e with
+						| `RENAME -> vint 0
+						| `CHANGE -> vint 1
+					) events
+				in
+				encode_obj [
+					key_file,vnative_string file;
+					key_events,encode_array vevents;
+				]
+			) v4
+		in
+		if v3 = VNull then
+			FS_event.start event path callback
+		else begin
+			let flags = decode_int_flags v3 in
+			let watch_entry = List.mem 0 flags
+			and stat = List.mem 1 flags
+			and recursive = List.mem 2 flags in
+			FS_event.start ~watch_entry ~stat ~recursive event path callback
+		end;
+		vnull
+	);
+	"stop", vfun1 (fun v ->
+		let event = decode_fs_event v in
+		encode_unit_result (FS_event.stop event)
+	);
+]
+
+let thread_pool_fields = [
+	"createRequest", vfun0 (fun() ->
+		VHandle (HThreadPoolRequest (Thread_pool.Request.make()))
+	);
+	"queueWork", vfun4 (fun v1 v2 v3 v4 ->
+		let loop = decode_loop v1
+		and request =
+			decode_optional (function
+				| VHandle (HThreadPoolRequest r) -> r
+				| v -> unexpected_value v "eval.luv.ThreadPool.ThreadPoolRequest"
+			) v2
+		and work =
+			let cb = prepare_callback v3 0 in
+			(fun() -> EvalThread.run (get_ctx()) (fun() -> cb []))
+		and callback = encode_unit_callback v4 in
+		Thread_pool.queue_work ~loop ?request work callback;
+		vnull
+	);
+	"setSize", vfun2 (fun v1 v2 ->
+		let size = decode_int v1
+		and if_not_already_set = decode_optional decode_bool v2 in
+		Thread_pool.set_size ?if_not_already_set size;
+		vnull
+	);
+]
+
+let thread_fields = [
+	"self", vfun0 (fun() ->
+		VHandle (HThread (Thread.self()))
+	);
+	"create", vfun2 (fun v1 v2 ->
+		let fn =
+			let cb = prepare_callback v1 0 in
+			(fun() -> EvalThread.run (get_ctx()) (fun() -> cb []))
+		and stack_size = decode_optional decode_int v2 in
+		encode_result (fun t -> VHandle (HThread t)) (Thread.create ?stack_size fn)
+	);
+	"join", vfun1 (fun v ->
+		let thread =
+			match v with
+			| VHandle (HThread t) -> t
+			| _ -> unexpected_value v "eval.luv.Thread"
+		in
+		encode_unit_result (Thread.join thread)
+	);
+]
+
+let once_fields = [
+	"init", vfun0 (fun() ->
+		encode_result (fun o -> VHandle (HOnce o)) (Once.init())
+	);
+	"once", vfun2 (fun v1 v2 ->
+		let once =
+			match v1 with
+			| VHandle (HOnce o) -> o
+			| _ -> unexpected_value v1 "eval.luv.Once"
+		and callback = prepare_callback v2 0 in
+		Once.once once (fun() -> ignore(callback []));
+		vnull
+	);
+]
+
+let mutex_fields = [
+	"init", vfun1 (fun v ->
+		let recursive = decode_optional decode_bool v in
+		encode_result (fun m -> VHandle (HMutex m)) (Mutex.init ?recursive ())
+	);
+	"destroy", vfun1 (fun v ->
+		Mutex.destroy (decode_mutex v);
+		vnull
+	);
+	"lock", vfun1 (fun v ->
+		Mutex.lock (decode_mutex v);
+		vnull
+	);
+	"tryLock", vfun1 (fun v ->
+		encode_unit_result (Mutex.trylock (decode_mutex v))
+	);
+	"unlock", vfun1 (fun v ->
+		Mutex.unlock (decode_mutex v);
+		vnull
+	);
+]
+
+let rwlock_fields = [
+	"init", vfun0 (fun() ->
+		encode_result (fun l -> VHandle (HRwLock l)) (Rwlock.init())
+	);
+	"destroy", vfun1 (fun v ->
+		Rwlock.destroy (decode_rwlock v);
+		vnull
+	);
+	"rdLock", vfun1 (fun v ->
+		Rwlock.rdlock (decode_rwlock v);
+		vnull
+	);
+	"rdTryLock", vfun1 (fun v ->
+		encode_unit_result (Rwlock.tryrdlock (decode_rwlock v))
+	);
+	"rdUnlock", vfun1 (fun v ->
+		Rwlock.rdunlock (decode_rwlock v);
+		vnull
+	);
+	"wrLock", vfun1 (fun v ->
+		Rwlock.wrlock (decode_rwlock v);
+		vnull
+	);
+	"wrTryLock", vfun1 (fun v ->
+		encode_unit_result (Rwlock.trywrlock (decode_rwlock v))
+	);
+	"wrUnlock", vfun1 (fun v ->
+		Rwlock.wrunlock (decode_rwlock v);
+		vnull
+	);
+]
+
+let semaphore_fields = [
+	"init", vfun1 (fun v ->
+		encode_result (fun s -> VHandle (HSemaphore s)) (Semaphore.init (decode_int v))
+	);
+	"destroy", vfun1 (fun v ->
+		Semaphore.destroy (decode_semaphore v);
+		vnull
+	);
+	"post", vfun1 (fun v ->
+		Semaphore.post (decode_semaphore v);
+		vnull
+	);
+	"wait", vfun1 (fun v ->
+		Semaphore.wait (decode_semaphore v);
+		vnull
+	);
+	"tryWait", vfun1 (fun v ->
+		encode_unit_result (Semaphore.trywait (decode_semaphore v))
+	);
+]
+
+let condition_fields = [
+	"init", vfun0 (fun() ->
+		encode_result (fun s -> VHandle (HCondition s)) (Condition.init ())
+	);
+	"destroy", vfun1 (fun v ->
+		Condition.destroy (decode_condition v);
+		vnull
+	);
+	"signal", vfun1 (fun v ->
+		Condition.signal (decode_condition v);
+		vnull
+	);
+	"broadcast", vfun1 (fun v ->
+		Condition.broadcast (decode_condition v);
+		vnull
+	);
+	"wait", vfun2 (fun v1 v2 ->
+		let condition = decode_condition v1
+		and mutex = decode_mutex v2 in
+		Condition.wait condition mutex;
+		vnull
+	);
+	"timedWait", vfun3 (fun v1 v2 v3 ->
+		let condition = decode_condition v1
+		and mutex = decode_mutex v2
+		and timeout = decode_int v3 in
+		encode_unit_result (Condition.timedwait condition mutex timeout)
+	);
+]
+
+let barrier_fields = [
+	"init", vfun1 (fun v ->
+		encode_result (fun b -> VHandle (HBarrier b)) (Barrier.init (decode_int v))
+	);
+	"destroy", vfun1 (fun v ->
+		Barrier.destroy (decode_barrier v);
+		vnull
+	);
+	"wait", vfun1 (fun v ->
+		vbool (Barrier.wait (decode_barrier v))
+	);
+]
+
+let env_fields = [
+	"getEnv", vfun1 (fun v ->
+		let name = decode_string v in
+		encode_result vnative_string (Env.getenv name)
+	);
+	"setEnv", vfun2 (fun v1 v2 ->
+		let name = decode_string v1
+		and value = decode_native_string v2 in
+		encode_unit_result (Env.setenv name ~value)
+	);
+	"environ", vfun0 (fun() ->
+		let encode env =
+			let map =
+				List.fold_left (fun map (name,value) ->
+					StringHashtbl.add map (EvalString.create_unknown_vstring name) (vnative_string value);
+					map
+				) (StringHashtbl.create()) env
+			in
+			encode_string_map_direct map
+		in
+		encode_result encode (Env.environ())
+	);
+]
+
+let time_fields = [
+	"getTimeOfDay", vfun0 (fun() ->
+		encode_result (fun (t:Time.t) ->
+			encode_obj [key_sec,VInt64 t.tv_sec; key_usec,vint32 t.tv_usec]
+		) (Time.gettimeofday())
+	);
+	"hrTime", vfun0 (fun() ->
+		VUInt64 (Time.hrtime())
+	);
+	"sleep", vfun1 (fun v ->
+		Time.sleep (decode_int v);
+		vnull
+	);
+]
+
+let path_fields = [
+	"exePath", vfun0 (fun() ->
+		encode_result vnative_string (Path.exepath())
+	);
+	"cwd", vfun0 (fun() ->
+		encode_result vnative_string (Path.cwd())
+	);
+	"chdir", vfun1 (fun v ->
+		encode_unit_result (Path.chdir (decode_native_string v))
+	);
+	"homedir", vfun0 (fun() ->
+		encode_result vnative_string (Path.homedir())
+	);
+	"tmpdir", vfun0 (fun() ->
+		encode_result vnative_string (Path.tmpdir())
+	);
+]
+
+let random_fields = [
+	"createRequest", vfun0 (fun() ->
+		VHandle (HRandomRequest (Random.Request.make()))
+	);
+	"random", vfun4 (fun v1 v2 v3 v4 ->
+		let loop = decode_loop v1
+		and buffer = decode_buffer v2
+		and request =
+			decode_optional (function
+				| VHandle (HRandomRequest r) -> r
+				| v -> unexpected_value v "eval.luv.Random.RandomRequest"
+			) v3
+		and callback = encode_unit_callback v4 in
+		Random.random ~loop ?request buffer callback;
+		vnull
+	);
+]
+
+let random_sync_fields = [
+	"random", vfun1(fun v ->
+		let buffer = decode_buffer v in
+		encode_unit_result (Random.Sync.random buffer)
+	);
+]
+
+let network_fields = [
+	"interfaceAddresses", vfun0 (fun() ->
+		encode_result (fun addresses ->
+			encode_array (List.map (fun (a:Network.Interface_address.t) ->
+				encode_obj [
+					key_name, encode_string a.name;
+					key_isInternal, vbool a.is_internal;
+					key_physical, vnative_string a.physical;
+					key_address, encode_sockaddr a.address;
+					key_netmask, encode_sockaddr a.netmask;
+				]
+			) addresses)
+		) (Network.interface_addresses())
+	);
+	"ifIndexToName", vfun1 (fun v ->
+		let index = decode_int v in
+		encode_result encode_string (Network.if_indextoname index)
+	);
+	"ifIndexToIid", vfun1 (fun v ->
+		let index = decode_int v in
+		encode_result encode_string (Network.if_indextoiid index)
+	);
+	"getHostName", vfun0 (fun() ->
+		encode_result encode_string (Network.gethostname())
+	);
+]
+
+let fs_poll_fields = [
+	"init", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_result (fun p -> VHandle (HFsPoll p)) (FS_poll.init ~loop ())
+	);
+	"start", vfun4 (fun v1 v2 v3 v4 ->
+		let poll = decode_fs_poll v1
+		and path = decode_native_string v2
+		and interval = decode_optional decode_int v3
+		and callback =
+			encode_callback (fun (previous,current) ->
+				encode_obj [
+					key_previous,encode_file_stat previous;
+					key_current,encode_file_stat current;
+				]
+			) v4
+		in
+		FS_poll.start ?interval poll path callback;
+		vnull
+	);
+	"stop", vfun1 (fun v ->
+		let poll = decode_fs_poll v in
+		encode_unit_result (FS_poll.stop poll)
+	);
+]
+
+let resource_fields = [
+	"uptime", vfun0 (fun() ->
+		encode_result vfloat (Resource.uptime());
+	);
+	"loadAvg", vfun0 (fun() ->
+		let m1,m5,m15 = Resource.loadavg() in
+		encode_array_a [|vfloat m1; vfloat m5; vfloat m15|];
+	);
+	"freeMemory", vfun0 (fun() ->
+		VUInt64 (Resource.free_memory())
+	);
+	"totalMemory", vfun0 (fun() ->
+		VUInt64 (Resource.total_memory())
+	);
+	"constrainedMemory", vfun0 (fun() ->
+		encode_nullable (fun u -> VUInt64 u) (Resource.constrained_memory())
+	);
+	"getPriority", vfun1 (fun v ->
+		let pid = decode_int v in
+		encode_result vint (Resource.getpriority pid)
+	);
+	"setPriority", vfun2 (fun v1 v2 ->
+		let pid = decode_int v1
+		and priority = decode_int v2 in
+		encode_unit_result (Resource.setpriority pid priority)
+	);
+	"residentSetMemory", vfun0 (fun() ->
+		encode_result encode_size_t (Resource.resident_set_memory())
+	);
+	"getRUsage", vfun0 (fun() ->
+		let encode_timeval (t:Resource.timeval) =
+			encode_obj [
+				key_sec, VInt64 (Signed.Long.to_int64 t.sec);
+				key_usec, VInt64 (Signed.Long.to_int64 t.usec)
+			]
+		in
+		let encode_rusage (r:Resource.rusage) =
+			encode_obj_s [
+				"utime", encode_timeval r.utime;
+				"stime", encode_timeval r.stime;
+				"maxrss", VUInt64 r.maxrss;
+				"ixrss", VUInt64 r.ixrss;
+				"idrss", VUInt64 r.idrss;
+				"isrss", VUInt64 r.isrss;
+				"minflt", VUInt64 r.minflt;
+				"majflt", VUInt64 r.majflt;
+				"nswap", VUInt64 r.nswap;
+				"inblock", VUInt64 r.inblock;
+				"oublock", VUInt64 r.oublock;
+				"msgsnd", VUInt64 r.msgsnd;
+				"msgrcv", VUInt64 r.msgrcv;
+				"nsignals", VUInt64 r.nsignals;
+				"nvcsw", VUInt64 r.nvcsw;
+				"nivcsw", VUInt64 r.nivcsw;
+			]
+		in
+		encode_result encode_rusage (Resource.getrusage())
+	);
+]
+
+let system_info_fields = [
+	"cpuInfo", vfun0 (fun() ->
+		let encode_info (i:System_info.CPU_info.t) =
+			encode_obj_s [
+				"model", encode_string i.model;
+				"speed", vint i.speed;
+				"times", encode_obj_s [
+					"user", VUInt64 i.times.user;
+					"nice", VUInt64 i.times.nice;
+					"sys", VUInt64 i.times.sys;
+					"idle", VUInt64 i.times.idle;
+					"irq", VUInt64 i.times.irq;
+				]
+			]
+		in
+		let encode l =
+			encode_array (List.map encode_info l)
+		in
+		encode_result encode (System_info.cpu_info());
+	);
+	"uname", vfun0 (fun() ->
+		encode_result (fun (u:System_info.Uname.t) ->
+			encode_obj_s [
+				"sysname", encode_string u.sysname;
+				"release", encode_string u.release;
+				"version", encode_string u.version;
+				"machine", encode_string u.machine;
+			]
+		) (System_info.uname())
+	);
+]
+
+let pid_fields = [
+	"getPid", vfun0 (fun() ->
+		vint (Pid.getpid())
+	);
+	"getPPid", vfun0 (fun() ->
+		vint (Pid.getppid())
+	);
+]
+
+let passwd_fields = [
+	"getPasswd", vfun0 (fun() ->
+		encode_result (fun (p:Passwd.t) ->
+			encode_obj_s [
+				"username",encode_string p.username;
+				"uid",vint p.uid;
+				"gid",vint p.gid;
+				"shell",encode_nullable encode_string p.shell;
+				"homedir",vnative_string p.homedir;
+			]
+		) (Passwd.get_passwd())
+	);
+]
+
+let metrics_fields = [
+	"idleTime", vfun1 (fun v ->
+		let loop = decode_loop v in
+		VUInt64 (Metrics.idle_time loop)
+	);
+]
+
+let prepare_fields = [
+	"init", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_result (fun i -> VHandle (HPrepare i)) (Prepare.init ~loop ())
+	);
+	"start", vfun2 (fun v1 v2 ->
+		let prepare = decode_prepare v1 in
+		let cb = prepare_callback v2 0 in
+		encode_unit_result (Prepare.start prepare (fun() -> ignore(cb [])));
+	);
+	"stop", vfun1 (fun v ->
+		let prepare = decode_prepare v in
+		encode_unit_result (Prepare.stop prepare)
+	);
+]
+
+let check_fields = [
+	"init", vfun1 (fun v ->
+		let loop = decode_loop v in
+		encode_result (fun i -> VHandle (HCheck i)) (Check.init ~loop ())
+	);
+	"start", vfun2 (fun v1 v2 ->
+		let check = decode_check v1 in
+		let cb = prepare_callback v2 0 in
+		encode_unit_result (Check.start check (fun() -> ignore(cb [])));
+	);
+	"stop", vfun1 (fun v ->
+		let check = decode_check v in
+		encode_unit_result (Check.stop check)
+	);
+]
+
+let version_fields = [
+	"string", vfun0 (fun() -> encode_string (Version.string()));
+	"major", vint (Version.major);
+	"minor", vint (Version.minor);
+	"patch", vint (Version.patch);
+	"isRelease", vbool (Version.is_release);
+	"suffix", encode_string (Version.suffix);
+	"hex", vint (Version.hex);
+]

+ 28 - 17
src/macro/eval/evalMain.ml

@@ -139,6 +139,20 @@ let create com api is_macro =
 		select ctx;
 		ignore(Event.sync(Event.receive eval.debug_channel));
 	end;
+	(* If no user-defined exception handler is set then follow libuv behavior.
+		Which is printing an error to stderr and exiting with code 2 *)
+	Luv.Error.set_on_unhandled_exception (fun ex ->
+		match ex with
+		| Sys_exit _ -> raise ex
+		| _ ->
+			let msg =
+				match ex with
+				| Error.Error (err,_) -> Error.error_msg err
+				| _ -> Printexc.to_string ex
+			in
+			Printf.eprintf "%s\n" msg;
+			exit 2
+	);
 	t();
 	ctx
 
@@ -199,7 +213,15 @@ let value_signature v =
 			incr cache_length;
 			f()
 	in
-	let function_count = ref 0 in
+	let custom_count = ref 0 in
+	(* Custom format: enumerate custom entities as name_char0, name_char1 etc. *)
+	let custom_name name_char =
+		cache v (fun () ->
+			addc 'F';
+			add (string_of_int !custom_count);
+			incr custom_count
+		)
+	in
 	let rec loop v = match v with
 		| VNull -> addc 'n'
 		| VTrue -> addc 't'
@@ -328,12 +350,9 @@ let value_signature v =
 		| VPrototype _ ->
 			die "" __LOC__
 		| VFunction _ | VFieldClosure _ ->
-			(* Custom format: enumerate functions as F0, F1 etc. *)
-			cache v (fun () ->
-				addc 'F';
-				add (string_of_int !function_count);
-				incr function_count
-			)
+			custom_name 'F'
+		| VHandle _ ->
+			custom_name 'H'
 		| VLazy f ->
 			loop (!f())
 	and loop_fields fields =
@@ -345,15 +364,7 @@ let value_signature v =
 	loop v;
 	Digest.string (Buffer.contents buf)
 
-let prepare_callback v n =
-	match v with
-	| VFunction _ | VFieldClosure _ ->
-		let ctx = get_ctx() in
-		(fun args -> match catch_exceptions ctx (fun() -> call_value v args) null_pos with
-			| Some v -> v
-			| None -> vnull)
-	| _ ->
-		raise Invalid_expr
+let prepare_callback = EvalMisc.prepare_callback
 
 let init ctx = ()
 
@@ -398,7 +409,7 @@ let compiler_error msg pos =
 		let eval = get_eval ctx in
 		(match eval.env with
 		| Some _ ->
-			let stack = EvalStdLib.StdNativeStackTrace.make_stack_value (call_stack eval) in
+			let stack = EvalStackTrace.make_stack_value (call_stack eval) in
 			set_instance_field i key_native_stack stack;
 		| None -> ());
 		exc vi

+ 34 - 0
src/macro/eval/evalMisc.ml

@@ -255,3 +255,37 @@ let get_binop_fun op p = match op with
 	| OpUShr -> op_ushr p
 	| OpMod -> op_mod p
 	| OpAssign | OpBoolAnd | OpBoolOr | OpAssignOp _ | OpInterval | OpArrow | OpIn -> die "" __LOC__
+
+let prepare_callback v n =
+	match v with
+	| VFunction _ | VFieldClosure _ ->
+		let ctx = get_ctx() in
+		(fun args -> match catch_exceptions ctx (fun() -> call_value v args) null_pos with
+			| Some v -> v
+			| None -> vnull)
+	| _ ->
+		raise MacroApi.Invalid_expr
+
+let create_haxe_exception ?stack msg =
+	let vi = encode_instance key_haxe_Exception in
+	match vi with
+	| VInstance i ->
+		let v_msg = create_unknown (msg) in
+		set_instance_field i key_exception_message v_msg;
+		set_instance_field i key_native_exception v_msg;
+		(match stack with
+		| Some stack ->
+			let stack = EvalStackTrace.make_stack stack in
+			set_instance_field i key_native_stack stack;
+		| None ->
+			let ctx = get_ctx() in
+			let eval = get_eval ctx in
+			match eval.env with
+			| Some _ ->
+				let stack = EvalStackTrace.make_stack_value (call_stack eval) in
+				set_instance_field i key_native_stack stack;
+			| None -> ()
+		);
+		vi
+	| _ ->
+		die "" __LOC__

+ 3 - 1
src/macro/eval/evalPrinting.ml

@@ -40,6 +40,7 @@ let rtrue = create_ascii "true"
 let rfalse = create_ascii "false"
 let rfun = create_ascii "#fun"
 let rclosure = create_ascii "#closure"
+let rhandle = create_ascii "#handle"
 
 let s_date d =
 	let open Unix in
@@ -120,12 +121,13 @@ and s_value depth v =
 		create_ascii (if String.unsafe_get s (len - 1) = '.' then String.sub s 0 (len - 1) else s)
 	| VFunction (f,_) -> rfun
 	| VFieldClosure _ -> rclosure
+	| VHandle _ -> rhandle
 	| VEnumValue ve -> s_enum_value depth ve
 	| VString s -> s
+	| VNativeString s -> create_unknown_vstring s
 	| VArray va -> s_array (depth + 1) va
 	| VVector vv -> s_vector (depth + 1) vv
 	| VInstance {ikind=IDate d} -> s_date d
-	| VNativeString s -> create_unknown_vstring s
 	| VInstance {ikind=IPos p} -> create_ascii ("#pos(" ^ Lexer.get_error_pos (Printf.sprintf "%s:%d:") p ^ ")") (* STODO: not ascii? *)
 	| VInstance {ikind=IRegex r} -> r.r_rex_string
 	| VInstance i -> (try call_to_string () with Not_found -> s_hash i.iproto.ppath)

+ 51 - 43
src/macro/eval/evalStdLib.ml

@@ -546,46 +546,6 @@ module StdBytesBuffer = struct
 	)
 end
 
-module StdNativeStackTrace = struct
-	let make_stack envs =
-		let l = DynArray.create () in
-		List.iter (fun (pos,kind) ->
-			let file_pos s =
-				let line1,col1,_,_ = Lexer.get_pos_coords pos in
-				encode_enum_value key_haxe_StackItem 2 [|s;create_unknown pos.pfile;vint line1;vint col1|] None
-			in
-			match kind with
-			| EKLocalFunction i ->
-				let local_function = encode_enum_value key_haxe_StackItem 4 [|vint i|] None in
-				DynArray.add l (file_pos local_function);
-			| EKMethod(st,sf) ->
-				let local_function = encode_enum_value key_haxe_StackItem 3 [|create_unknown (rev_hash st); create_unknown (rev_hash sf)|] None in
-				DynArray.add l (file_pos local_function);
-			| EKEntrypoint ->
-				()
-		) envs;
-		encode_array (DynArray.to_list l)
-
-	let make_stack_value envs =
-		make_stack (List.map (fun env -> {pfile = rev_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax},env.env_info.kind) envs)
-
-	let getCallStack = vfun0 (fun () ->
-		let ctx = get_ctx() in
-		let envs = call_stack (get_eval ctx) in
-		let envs = match envs with
-			| _ :: _ :: envs -> envs (* Skip calls to callStack() and getCallStack() *)
-			| _ -> envs
-		in
-		make_stack_value  envs
-	)
-
-	let getExceptionStack = vfun0 (fun () ->
-		let ctx = get_ctx() in
-		let envs = ctx.exception_stack in
-		make_stack (List.rev envs)
-	)
-end
-
 module StdCompress = struct
 	open Extc
 
@@ -3020,7 +2980,7 @@ module StdType = struct
 				7,[|get_static_prototype_as_value ctx ve.epath null_pos|]
 			| VLazy f ->
 				loop (!f())
-			| VInt64 _ | VUInt64 _ | VNativeString _ -> 8,[||]
+			| VInt64 _ | VUInt64 _ | VNativeString _ | VHandle _ -> 8,[||]
 		in
 		let i,vl = loop v in
 		encode_enum_value key_ValueType i vl None
@@ -3402,8 +3362,8 @@ let init_standard_library builtins =
 		"getBytes",StdBytesBuffer.getBytes;
 	];
 	init_fields builtins (["haxe"],"NativeStackTrace") [
-		"_callStack",StdNativeStackTrace.getCallStack;
-		"exceptionStack",StdNativeStackTrace.getExceptionStack;
+		"_callStack",EvalStackTrace.getCallStack;
+		"exceptionStack",EvalStackTrace.getExceptionStack;
 	] [];
 	init_fields builtins (["haxe";"zip"],"Compress") [
 		"run",StdCompress.run;
@@ -3756,4 +3716,52 @@ let init_standard_library builtins =
 	] [];
 	init_fields builtins (["eval";"integers";"_UInt64"],"UInt64_Impl_") EvalIntegers.uint64_fields [];
 	init_fields builtins (["eval";"integers";"_Int64"],"Int64_Impl_") EvalIntegers.int64_fields [];
+	init_fields builtins (["eval";"luv";"_UVError"],"UVError_Impl_") EvalLuv.uv_error_fields [];
+	init_fields builtins (["eval";"luv";"_Loop"],"Loop_Impl_") EvalLuv.loop_fields [];
+	init_fields builtins (["eval";"luv";"_Loop"],"LoopOption_Impl_") ["sigprof",vint Luv.Loop.Option.sigprof] [];
+	init_fields builtins (["eval";"luv";"_Handle"],"Handle_Impl_") EvalLuv.handle_fields [];
+	init_fields builtins (["eval";"luv";"_Idle"], "Idle_Impl_") EvalLuv.idle_fields [];
+	init_fields builtins (["eval";"luv";"_Async"], "Async_Impl_") EvalLuv.async_fields [];
+	init_fields builtins (["eval";"luv";"_Timer"], "Timer_Impl_") EvalLuv.timer_fields [];
+	init_fields builtins (["eval";"luv";"_Buffer"], "Buffer_Impl_") EvalLuv.buffer_fields [];
+	init_fields builtins (["eval";"luv";"_SockAddr"], "SockAddr_Impl_") EvalLuv.sockaddr_fields [];
+	init_fields builtins (["eval";"luv";"_Tcp"], "Tcp_Impl_") EvalLuv.tcp_fields [];
+	init_fields builtins (["eval";"luv";"_Udp"], "Udp_Impl_") EvalLuv.udp_fields [];
+	init_fields builtins (["eval";"luv";"_ConnectedUdp"], "ConnectedUdp_Impl_") EvalLuv.connected_udp_fields [];
+	init_fields builtins (["eval";"luv";"_Pipe"], "Pipe_Impl_") EvalLuv.pipe_fields [];
+	init_fields builtins (["eval";"luv";"_Tty"], "Tty_Impl_") EvalLuv.tty_fields [];
+	init_fields builtins (["eval";"luv";"_Stream"], "Stream_Impl_") EvalLuv.stream_fields [];
+	init_fields builtins (["eval";"luv";"_Signal"], "Signal_Impl_") EvalLuv.signal_fields [];
+	init_fields builtins (["eval";"luv";"_Signal"], "SigNum_Impl_") EvalLuv.signum_fields [];
+	init_fields builtins (["eval";"luv";"_Process"], "Process_Impl_") EvalLuv.process_fields [];
+	init_fields builtins (["eval";"luv";"_Request"], "Request_Impl_") EvalLuv.request_fields [];
+	init_fields builtins (["eval";"luv"], "Dns") EvalLuv.dns_fields [];
+	init_fields builtins (["eval";"luv";"_File"], "File_Impl_") EvalLuv.file_fields [];
+	init_fields builtins (["eval";"luv";"_Dir"], "Dir_Impl_") EvalLuv.dir_fields [];
+	init_fields builtins (["eval";"luv"], "FileSync") EvalLuv.file_sync_fields [];
+	init_fields builtins (["eval";"luv"], "DirSync") EvalLuv.dir_sync_fields [];
+	init_fields builtins (["eval";"luv";"_FsEvent"], "FsEvent_Impl_") EvalLuv.fs_event_fields [];
+	init_fields builtins (["eval";"luv"], "ThreadPool") EvalLuv.thread_pool_fields [];
+	init_fields builtins (["eval";"luv";"_Thread"], "Thread_Impl_") EvalLuv.thread_fields [];
+	init_fields builtins (["eval";"luv";"_Once"], "Once_Impl_") EvalLuv.once_fields [];
+	init_fields builtins (["eval";"luv";"_Mutex"], "Mutex_Impl_") EvalLuv.mutex_fields [];
+	init_fields builtins (["eval";"luv";"_RwLock"], "RwLock_Impl_") EvalLuv.rwlock_fields [];
+	init_fields builtins (["eval";"luv";"_Semaphore"], "Semaphore_Impl_") EvalLuv.semaphore_fields [];
+	init_fields builtins (["eval";"luv";"_Condition"], "Condition_Impl_") EvalLuv.condition_fields [];
+	init_fields builtins (["eval";"luv";"_Barrier"], "Barrier_Impl_") EvalLuv.barrier_fields [];
+	init_fields builtins (["eval";"luv"], "Env") EvalLuv.env_fields [];
+	init_fields builtins (["eval";"luv"], "Time") EvalLuv.time_fields [];
+	init_fields builtins (["eval";"luv"], "Path") EvalLuv.path_fields [];
+	init_fields builtins (["eval";"luv"], "Random") EvalLuv.random_fields [];
+	init_fields builtins (["eval";"luv"], "RandomSync") EvalLuv.random_sync_fields [];
+	init_fields builtins (["eval";"luv"], "Network") EvalLuv.network_fields [];
+	init_fields builtins (["eval";"luv";"_FsPoll"], "FsPoll_Impl_") EvalLuv.fs_poll_fields [];
+	init_fields builtins (["eval";"luv"], "Resource") EvalLuv.resource_fields [];
+	init_fields builtins (["eval";"luv"], "SystemInfo") EvalLuv.system_info_fields [];
+	init_fields builtins (["eval";"luv"], "Pid") EvalLuv.pid_fields [];
+	init_fields builtins (["eval";"luv"], "Passwd") EvalLuv.passwd_fields [];
+	init_fields builtins (["eval";"luv"], "Metrics") EvalLuv.metrics_fields [];
+	init_fields builtins (["eval";"luv";"_Prepare"], "Prepare_Impl_") EvalLuv.prepare_fields [];
+	init_fields builtins (["eval";"luv";"_Check"], "Check_Impl_") EvalLuv.check_fields [];
+	init_fields builtins (["eval";"luv"], "Version") EvalLuv.version_fields [];
 	EvalSsl.init_fields init_fields builtins

+ 51 - 32
src/macro/eval/evalThread.ml

@@ -77,42 +77,61 @@ let create_eval thread = {
 	caught_exception = vnull;
 }
 
-let spawn ctx f =
-	let f thread =
-		let id = Thread.id (Thread.self()) in
-		let maybe_send_thread_event reason = match ctx.debug.debug_socket with
-			| Some socket ->
-				socket.connection.send_thread_event id reason
-			| None ->
-				()
-		in
-		let new_eval = create_eval thread in
-		ctx.evals <- IntMap.add id new_eval ctx.evals;
-		let close () =
-			ctx.evals <- IntMap.remove id ctx.evals;
-			maybe_send_thread_event "exited";
-		in
-		try
-			maybe_send_thread_event "started";
-			ignore(f ());
-			close();
-		with
-		| RunTimeException(v,stack,p) ->
-			let msg = get_exc_error_message ctx v stack p in
-			prerr_endline msg;
-			close();
-		| Sys_exit i ->
-			close();
-			exit i;
-		| exc ->
-			close();
-			raise exc
+let run ctx f thread =
+	let id = Thread.id (Thread.self()) in
+	let maybe_send_thread_event reason = match ctx.debug.debug_socket with
+		| Some socket ->
+			socket.connection.send_thread_event id reason
+		| None ->
+			()
+	in
+	let new_eval = create_eval thread in
+	ctx.evals <- IntMap.add id new_eval ctx.evals;
+	let close () =
+		ctx.evals <- IntMap.remove id ctx.evals;
+		maybe_send_thread_event "exited";
 	in
+	try
+		maybe_send_thread_event "started";
+		ignore(f ());
+		close();
+	with
+	| RunTimeException(v,stack,p) ->
+		let msg = get_exc_error_message ctx v stack p in
+		prerr_endline msg;
+		close();
+	| Sys_exit i ->
+		close();
+		exit i;
+	| exc ->
+		close();
+		raise exc
+
+let spawn ctx f =
 	let thread = {
 		tthread = Obj.magic ();
 		tstorage = IntMap.empty;
 		tevents = vnull;
 		tdeque = Deque.create();
 	} in
-	thread.tthread <- Thread.create f thread;
-	thread
+	thread.tthread <- Thread.create (run ctx f) thread;
+	thread
+
+(**
+	Just executes `f` if called from a Haxe thread.
+	Otherwise creates Haxe thread data structures, runs `f` and then cleans up
+	created data.
+*)
+let run ctx f =
+	let id = Thread.id (Thread.self()) in
+	if IntMap.mem id ctx.evals then
+		ignore(f())
+	else begin
+		let thread = {
+			tthread = Thread.self();
+			tstorage = IntMap.empty;
+			tevents = vnull;
+			tdeque = Deque.create();
+		} in
+		run ctx f thread
+	end

+ 78 - 0
src/macro/eval/evalValue.ml

@@ -93,6 +93,40 @@ type vprototype_kind =
 	| PInstance
 	| PObject
 
+type vhandle =
+	| HLoop of Luv.Loop.t
+	| HIdle of Luv.Idle.t
+	| HTimer of Luv.Timer.t
+	| HAsync of Luv.Async.t
+	| HBuffer of Luv.Buffer.t
+	| HSockAddr of Luv.Sockaddr.t
+	| HTcp of Luv.TCP.t
+	| HUdp of Luv.UDP.t
+	| HPipe of Luv.Pipe.t
+	| HTty of Luv.TTY.t
+	| HFile of Luv.File.t
+	| HDir of Luv.File.Dir.t
+	| HSignal of Luv.Signal.t
+	| HProcess of Luv.Process.t
+	| HRedirection of Luv.Process.redirection
+	| HAddrRequest of Luv.DNS.Addr_info.Request.t
+	| HNameRequest of Luv.DNS.Name_info.Request.t
+	| HFileRequest of Luv.File.Request.t
+	| HRandomRequest of Luv.Random.Request.t
+	| HThreadPoolRequest of Luv.Thread_pool.Request.t
+	| HFileModeNumeric of Luv.File.Mode.numeric
+	| HFsEvent of Luv.FS_event.t
+	| HThread of Luv.Thread.t
+	| HOnce of Luv.Once.t
+	| HMutex of Luv.Mutex.t
+	| HRwLock of Luv.Rwlock.t
+	| HSemaphore of Luv.Semaphore.t
+	| HCondition of Luv.Condition.t
+	| HBarrier of Luv.Barrier.t
+	| HFsPoll of Luv.FS_poll.t
+	| HPrepare of Luv.Prepare.t
+	| HCheck of Luv.Check.t
+
 type value =
 	| VNull
 	| VTrue
@@ -110,6 +144,7 @@ type value =
 	| VFieldClosure of value * vfunc
 	| VLazy of (unit -> value) ref
 	| VNativeString of string
+	| VHandle of vhandle
 	| VInt64 of Signed.Int64.t
 	| VUInt64 of Unsigned.UInt64.t
 
@@ -224,6 +259,48 @@ and vlock = {
 	ldeque : vdeque;
 }
 
+let same_handle h1 h2 =
+	match h1, h2 with
+	| HLoop h1, HLoop h2 -> h1 == h2
+	| HIdle h1, HIdle h2 -> h1 == h2
+	| HTimer h1, HTimer h2 -> h1 == h2
+	| HAsync h1, HAsync h2 -> h1 == h2
+	| HBuffer h1, HBuffer h2 -> h1 == h2
+	| HSockAddr h1, HSockAddr h2 -> h1 == h2
+	| HTcp h1, HTcp h2 -> h1 == h2
+	| HPipe h1, HPipe h2 -> h1 == h2
+	| HTty h1, HTty h2 -> h1 == h2
+	| HFile h1, HFile h2 -> h1 == h2
+	| HDir h1, HDir h2 -> h1 == h2
+	| HUdp h1, HUdp h2 -> h1 == h2
+	| HSignal h1, HSignal h2 -> h1 == h2
+	| HProcess h1, HProcess h2 -> h1 == h2
+	| HRedirection h1, HRedirection h2 -> h1 == h2
+	| HFileRequest h1, HFileRequest h2 -> h1 == h2
+	| HNameRequest h1, HNameRequest h2 -> h1 == h2
+	| HAddrRequest h1, HAddrRequest h2 -> h1 == h2
+	| HRandomRequest h1, HRandomRequest h2 -> h1 == h2
+	| HThreadPoolRequest h1, HThreadPoolRequest h2 -> h1 == h2
+	| HFileModeNumeric h1, HFileModeNumeric h2 -> h1 == h2
+	| HFsEvent h1, HFsEvent h2 -> h1 == h2
+	| HThread h1, HThread h2 -> Luv.Thread.equal h1 h2
+	| HOnce h1, HOnce h2 -> h1 == h2
+	| HMutex h1, HMutex h2 -> h1 == h2
+	| HRwLock h1, HRwLock h2 -> h1 == h2
+	| HSemaphore h1, HSemaphore h2 -> h1 == h2
+	| HCondition h1, HCondition h2 -> h1 == h2
+	| HBarrier h1, HBarrier h2 -> h1 == h2
+	| HFsPoll h1, HFsPoll h2 -> h1 == h2
+	| HPrepare h1, HPrepare h2 -> h1 == h2
+	| HCheck h1, HCheck h2 -> h1 == h2
+	| HBuffer _,_ | HAsync _,_ | HTimer _, _ | HLoop _, _ | HIdle _, _ | HSockAddr _, _
+	| HTcp _, _ | HPipe _, _ | HTty _, _ | HFile _, _ | HUdp _, _ | HSignal _, _
+	| HProcess _, _ | HRedirection _, _| HFileRequest _, _ | HAddrRequest _, _
+	| HNameRequest _, _ | HRandomRequest _, _ | HThreadPoolRequest _, _
+	| HFileModeNumeric _, _ | HDir _, _ | HFsEvent _, _ | HThread _, _ | HOnce _, _
+	| HMutex _, _ | HRwLock _, _ | HSemaphore _, _ | HCondition _, _ | HBarrier _, _
+	| HFsPoll _, _ | HPrepare _, _ | HCheck _, _ -> false
+
 let rec equals a b = match a,b with
 	| VTrue,VTrue
 	| VFalse,VFalse
@@ -241,6 +318,7 @@ let rec equals a b = match a,b with
 	| VFunction(vf1,_),VFunction(vf2,_) -> vf1 == vf2
 	| VPrototype proto1,VPrototype proto2 -> proto1.ppath = proto2.ppath
 	| VNativeString s1,VNativeString s2 -> s1 = s2
+	| VHandle h1,VHandle h2 -> same_handle h1 h2
 	| VLazy f1,_ -> equals (!f1()) b
 	| _,VLazy f2 -> equals a (!f2())
 	| _ -> a == b

+ 141 - 0
std/eval/_std/sys/thread/EventLoop.hx

@@ -0,0 +1,141 @@
+package sys.thread;
+
+import eval.luv.Loop;
+import eval.luv.Async;
+import eval.luv.Timer as LuvTimer;
+
+@:coreApi
+enum NextEventTime {
+	Now;
+	Never;
+	AnyTime(time:Null<Float>);
+	At(time:Float);
+}
+
+abstract EventLoopHandle(Loop) from Loop to Loop {}
+
+abstract EventHandler(RegularEvent) from RegularEvent to RegularEvent {}
+
+private class RegularEvent {
+	public var timer:Null<LuvTimer>;
+	public var event:()->Void;
+
+	public function new(e:()->Void) {
+		event = e;
+	}
+
+	public function run() {
+		event();
+	}
+}
+
+@:coreApi
+class EventLoop {
+	public final handle:EventLoopHandle;
+
+	final mutex = new Mutex();
+	final oneTimeEvents = new Array<Null<()->Void>>();
+	var oneTimeEventsIdx = 0;
+	final wakeup:Async;
+	var promisedEventsCount = 0;
+	var pending:Array<()->Void> = [];
+	var looping = false;
+
+	public function new():Void {
+		handle = Loop.init().resolve();
+		wakeup = Async.init(handle, consumePending).resolve();
+		wakeup.unref();
+	}
+
+	public function repeat(event:()->Void, intervalMs:Int):EventHandler {
+		var e = new RegularEvent(event);
+		mutex.acquire();
+		pending.push(() -> {
+			e.timer = LuvTimer.init(handle).resolve();
+			e.timer.start(e.run, intervalMs, intervalMs < 1 ? 1 : intervalMs).resolve();
+		});
+		mutex.release();
+		wakeup.send();
+		return e;
+	}
+
+	public function cancel(eventHandler:EventHandler):Void {
+		mutex.acquire();
+		(eventHandler:RegularEvent).event = noop;
+		pending.push(() -> {
+			var timer = (eventHandler:RegularEvent).timer;
+			timer.stop().resolve();
+			timer.close(noop);
+		});
+		mutex.release();
+		wakeup.send();
+	}
+	static final noop = function() {}
+
+	public function promise():Void {
+		mutex.acquire();
+		++promisedEventsCount;
+		pending.push(refUnref);
+		mutex.release();
+		wakeup.send();
+	}
+
+	public function run(event:()->Void):Void {
+		mutex.acquire();
+		pending.push(event);
+		mutex.release();
+		wakeup.send();
+	}
+
+	public function runPromised(event:()->Void):Void {
+		mutex.acquire();
+		--promisedEventsCount;
+		pending.push(refUnref);
+		pending.push(event);
+		mutex.release();
+		wakeup.send();
+	}
+
+	function refUnref():Void {
+		if(promisedEventsCount > 0) {
+			wakeup.ref();
+		} else {
+			wakeup.unref();
+		}
+	}
+
+	public function progress():NextEventTime {
+		//TODO: throw if loop is already running
+		if((handle:Loop).run(NOWAIT)) {
+			return AnyTime(null);
+		} else {
+			return Never;
+		}
+	}
+
+	public function wait(?timeout:Float):Bool {
+		//TODO: throw if loop is already running
+		if(timeout == null) {
+			var timer = LuvTimer.init(handle).resolve();
+			timer.start(() -> {
+				timer.stop().resolve();
+				timer.close(() -> {});
+			}, Std.int(timeout * 1000));
+			return (handle:Loop).run(ONCE);
+		} else {
+			return (handle:Loop).run(ONCE);
+		}
+	}
+
+	public function loop():Void {
+		//TODO: throw if loop is already running
+		consumePending();
+		(handle:Loop).run(DEFAULT);
+	}
+
+	function consumePending(?_:Async):Void {
+		var p = pending;
+		pending = [];
+		for(fn in p) fn();
+	}
+}

+ 4 - 0
std/eval/integers/UInt64.hx

@@ -18,6 +18,10 @@ package eval.integers;
 
 	/**
 		Parse the given string value to an unsigned integer.
+<<<<<<< HEAD
+
+=======
+>>>>>>> development
 		Throws if the given string is not a valid representation of an unsigned
 		integer.
 	**/

+ 21 - 0
std/eval/luv/Async.hx

@@ -0,0 +1,21 @@
+package eval.luv;
+
+/**
+	Inter-loop communication.
+
+	@see https://aantron.github.io/luv/luv/Luv/Async
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Async to Handle {
+	/**
+		Allocates and initializes an async handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop, callback:(async:Async)->Void):Result<Async>;
+
+	/**
+		Triggers a call to the handle's callback by the handle's loop.
+	**/
+	public function send():Result<Result.NoData>;
+}

+ 23 - 0
std/eval/luv/Barrier.hx

@@ -0,0 +1,23 @@
+package eval.luv;
+
+/**
+	Barriers.
+
+	@see https://aantron.github.io/luv/luv/Luv/Barrier
+**/
+@:coreType abstract Barrier {
+	/**
+		Allocates and initializes a barrier.
+	**/
+	static public function init(count:Int):Result<Barrier>;
+
+	/**
+		Cleans up a barrier.
+	**/
+	public function destroy():Void;
+
+	/**
+		Waits on a barrier.
+	**/
+	public function wait():Bool;
+}

+ 120 - 0
std/eval/luv/Buffer.hx

@@ -0,0 +1,120 @@
+package eval.luv;
+
+import haxe.io.Bytes;
+
+/**
+	Data buffers.
+
+	@see https://aantron.github.io/luv/luv/Luv/Buffer
+**/
+@:coreType abstract Buffer {
+	/**
+		Allocates a fresh buffer of the given size.
+	**/
+	static public function create(size:Int):Buffer;
+
+	/**
+		Creates a buffer from a string.
+	**/
+	@:from static public function fromNativeString(s:NativeString):Buffer;
+
+	/**
+		Creates a buffer from a string.
+	**/
+	@:from static public function fromString(s:String):Buffer;
+
+	/**
+		Creates a buffer from bytes.
+	**/
+	@:from static public function fromBytes(b:Bytes):Buffer;
+
+	/**
+		Evaluates to the sum of the sizes of the buffers in the array.
+	**/
+	static public function totalSize(buffers:Array<Buffer>):Int;
+
+	/**
+		`Buffer.drop(buffers, count)` drops the first `count` bytes from `buffers`.
+
+		For example, if `buffers` contains two buffers of size 16, `Buffer.drop(buffers, 18)`
+		will evaluate to an array that has lost the reference to the first buffer,
+		and contains only a view into the second buffer of size 14.
+	**/
+	static public function drop(buffers:Array<Buffer>, count:Int):Array<Buffer>;
+
+	/**
+		Evaluates to the size of the buffer.
+	**/
+	public function size():Int;
+
+	/**
+		Retrieve a byte at the given index.
+	**/
+	@:arrayAccess public function get(index:Int):Int;
+
+	/**
+		Retrieve a byte at the given index without a bounds check.
+	**/
+	public function unsafeGet(index:Int):Int;
+
+	/**
+		Set byte value at the given index.
+	**/
+	@:arrayAccess public function set(index:Int, byte:Int):Int;
+
+	/**
+		Set byte value at the given index without a bounds check.
+	**/
+	public function unsafeSet(index:Int, byte:Int):Int;
+
+	/**
+		Creates a view into buffer that starts at the given `offset` and has the given `length`.
+
+		No data is copied.
+	**/
+	public function sub(offset:Int, length:Int):Buffer;
+
+	/**
+		Copies data from this buffer to destination.
+
+		The amount of data copied is the minimum of the two buffers' size.
+	**/
+	public function blit(destination:Buffer):Void;
+
+	/**
+		Fills the given buffer with the given byte.
+	**/
+	public function fill(byte:Int):Void;
+
+	/**
+		Creates a string with the same contents as the buffer.
+	**/
+	public function toString():String;
+
+	/**
+		Creates a native string with the same contents as the buffer.
+	**/
+	public function toNativeString():NativeString;
+
+	/**
+		Creates a `haxe.io.Bytes` instance with the same contents as this buffer.
+	**/
+	public function toBytes():Bytes;
+
+	/**
+		Copies data from a buffer to bytes buffer.
+	**/
+	public function blitToBytes(destination:Bytes, destinationOffset:Int):Void;
+
+	/**
+		Copies data from bytes to a buffer.
+	**/
+	public function blitFromBytes(source:Bytes, sourceOffset:Int):Void;
+
+	/**
+		Copies data from bytes to a buffer.
+
+		Note: `sourceOffset` is not a character offset but a byte offset.
+	**/
+	public function blitFromString(source:NativeString, sourceOffset:Int):Void;
+}

+ 26 - 0
std/eval/luv/Check.hx

@@ -0,0 +1,26 @@
+package eval.luv;
+
+/**
+	Post-I/O callback.
+
+	@see https://aantron.github.io/luv/luv/Luv/Check
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Check to Handle {
+	/**
+		Allocate and initialize a check handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop):Result<Check>;
+
+	/**
+		Starts the handle with the given callback.
+	**/
+	public function start(callback:()->Void):Result<Result.NoData>;
+
+	/**
+		Stops the handle.
+	**/
+	public function stop():Result<Result.NoData>;
+}

+ 39 - 0
std/eval/luv/Condition.hx

@@ -0,0 +1,39 @@
+package eval.luv;
+
+/**
+	Condition variables.
+
+	@see https://aantron.github.io/luv/luv/Luv/Condition
+**/
+@:coreType abstract Condition {
+	/**
+		Allocates and initializes a condition variable.
+	**/
+	static public function init():Result<Condition>;
+
+	/**
+		Cleans up a condition variable.
+	**/
+	public function destroy():Void;
+
+	/**
+		Signals a condition variable.
+	**/
+	public function signal():Void;
+
+	/**
+		Signals a condition variable, waking all waiters.
+	**/
+	public function broadcast():Void;
+
+	/**
+		Waits on a condition variable.
+	**/
+	public function wait(mutex:Mutex):Void;
+
+	/**
+		Waits on a condition variable with a timeout.
+		The timeout is given in nanoseconds.
+	**/
+	public function timedWait(mutex:Mutex, timeout:Int):Void;
+}

+ 34 - 0
std/eval/luv/ConnectedUdp.hx

@@ -0,0 +1,34 @@
+package eval.luv;
+
+import eval.luv.SockAddr;
+
+/**
+	Connected UDP sockets.
+
+	@see https://aantron.github.io/luv/luv/Luv/UDP/Connected
+**/
+@:forward
+@:using(eval.luv.Handle)
+abstract ConnectedUdp(Udp) to Udp to Handle {
+	/**
+		Removes the peer address assigned to the given socket.
+	**/
+	extern public function disconnect():Result<Result.NoData>;
+
+	/**
+		Retrieves the peer address assigned to the given socket.
+	**/
+	extern public function getPeerName():Result<SockAddr>;
+
+	/**
+		Like `eval.luv.UDP.send`, but the remote address used is the peer address
+		assigned to the socket.
+	**/
+	extern public function send(data:Array<Buffer>, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Like `eval.luv.UDP.trySend`, but the remote address used is the peer address
+		assigned to the socket.
+	**/
+	extern public function trySend(data:Array<Buffer>):Result<Result.NoData>;
+}

+ 73 - 0
std/eval/luv/Dir.hx

@@ -0,0 +1,73 @@
+package eval.luv;
+
+import eval.luv.File;
+
+enum abstract DirentKind(Int) {
+	var UNKNOWN = 0;
+	var FILE = 1;
+	var DIR = 2;
+	var LINK = 3;
+	var FIFO = 4;
+	var SOCKET = 5;
+	var CHAR = 6;
+	var BLOCK = 7;
+}
+
+typedef Dirent = {
+	var kind:DirentKind;
+	var name:NativeString;
+}
+
+typedef DirectoryScan = {
+	/**
+		Retrieves the next directory entry.
+	**/
+	function next():Null<Dirent>;
+
+	/**
+		Cleans up after a directory scan.
+	**/
+	function end():Void;
+}
+
+/**
+	@see https://aantron.github.io/luv/luv/Luv/File#module-Dir
+**/
+@:coreType abstract Dir {
+	/**
+		Opens the directory at the given path for listing.
+	**/
+	static public function open(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<Dir>)->Void):Void;
+
+	/**
+		Closes the directory.
+	**/
+	public function close(loop:Loop, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Retrieves a directory entry.
+	**/
+	public function read(loop:Loop, ?numberOfEntries:Int, ?request:FileRequest, callback:(result:Result<Array<Dirent>>)->Void):Void;
+
+	/**
+		Begins directory listing.
+	**/
+	static public function scan(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<DirectoryScan>)->Void):Void;
+}
+
+/**
+	Synchronous version of `eval.luv.Dir` API
+**/
+extern class DirSync {
+	@:inheritDoc(eval.luv.Dir.open)
+	static public function open(loop:Loop, path:NativeString):Result<Dir>;
+
+	@:inheritDoc(eval.luv.Dir.close)
+	static public function close(dir:Dir, loop:Loop):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.Dir.read)
+	static public function read(dir:Dir, loop:Loop, ?numberOfEntries:Int):Result<Array<Dirent>>;
+
+	@:inheritDoc(eval.luv.Dir.scan)
+	static public function scan(loop:Loop, path:NativeString):Result<DirectoryScan>;
+}

+ 69 - 0
std/eval/luv/Dns.hx

@@ -0,0 +1,69 @@
+package eval.luv;
+
+@:forward
+abstract AddrInfoRequest(Request) to Request {}
+
+typedef AddrInfo = {
+	var family:SockAddr.AddressFamily;
+	var sockType:SockAddr.SocketType;
+	var protocol:Int;
+	var addr:SockAddr;
+	var ?canonName:String;
+}
+
+typedef AddrInfoOptions = {
+	var ?request:AddrInfoRequest;
+	var ?family:SockAddr.AddressFamily;
+	var ?sockType:SockAddr.SocketType;
+	var ?protocol:Int;
+	var ?flags:Array<AddrInfoFlag>;
+}
+
+enum abstract AddrInfoFlag(Int) {
+	var PASSIVE = 0;
+	var CANONNAME = 1;
+	var NUMERICHOST = 2;
+	var NUMERICSERV = 3;
+	var V4MAPPED = 4;
+	var ALL = 5;
+	var ADDRCONFIG = 6;
+}
+
+@:forward
+abstract NameInfoRequest(Request) to Request {}
+
+enum abstract NameInfoFlag(Int) {
+	var NAMEREQD = 0;
+	var DGRAM = 1;
+	var NOFQDN = 2;
+	var NUMERICHOST = 3;
+	var NUMERICSERV = 4;
+}
+
+typedef NameInfoOptions = {
+	var ?request:NameInfoRequest;
+	var ?flags:Array<NameInfoFlag>;
+}
+
+/**
+	DNS queries.
+
+	@see https://aantron.github.io/luv/luv/Luv/Dns
+**/
+extern class Dns {
+
+	static function createAddrRequest():AddrInfoRequest;
+
+	static function createNameRequest():NameInfoRequest;
+
+	/**
+		Retrieves addresses.
+		Either `node` or `service` may be `null` but not both.
+	**/
+	static function getAddrInfo(loop:Loop, node:Null<String>, service:Null<String>, ?options:AddrInfoOptions, callback:(result:Result<Array<AddrInfo>>)->Void):Void;
+
+	/**
+		Retrieves host names.
+	**/
+	static function getNameInfo(loop:Loop, addr:SockAddr, ?options:NameInfoOptions, callback:(result:Result<{hostName:String, service:String}>)->Void):Void;
+}

+ 23 - 0
std/eval/luv/Env.hx

@@ -0,0 +1,23 @@
+package eval.luv;
+
+/**
+	Environment variables.
+
+	@see https://aantron.github.io/luv/luv/Luv/Env
+**/
+extern class Env {
+	/**
+		Retrieves the value of an environment variable.
+	**/
+	static function getEnv(name:String):Result<NativeString>;
+
+	/**
+		Sets an environment variable.
+	**/
+	static function setEnv(name:String, value:NativeString):Result<Result.NoData>;
+
+	/**
+		Retrieves all environment variables.
+	**/
+	static function environ():Result<Map<String,NativeString>>;
+}

+ 424 - 0
std/eval/luv/File.hx

@@ -0,0 +1,424 @@
+package eval.luv;
+
+import eval.integers.Int64;
+import eval.integers.UInt64;
+
+@:forward
+abstract FileRequest(Request) to Request {}
+
+enum abstract FileOpenFlag(Int) {
+	var RDONLY = 0;
+	var WRONLY = 1;
+	var RDWR = 2;
+	var CREAT = 3;
+	var EXCL = 4;
+	var EXLOCK = 5;
+	var NOCTTY = 6;
+	var NOFOLLOW = 7;
+	var TEMPORARY = 8;
+	var TRUNC = 9;
+	var APPEND = 10;
+	var DIRECT = 11;
+	var DSYNC = 12;
+	var FILEMAP = 13;
+	var NOATIME = 14;
+	var NONBLOCK = 15;
+	var RANDOM = 16;
+	var SEQUENTIAL = 17;
+	var SHORT_LIVED = 18;
+	var SYMLINK = 19;
+	var SYNC = 20;
+}
+
+/**
+	Permission bits.
+	@see https://aantron.github.io/luv/luv/Luv/File/Mode
+**/
+enum FileMode {
+	IRWXU;
+	IRUSR;
+	IWUSR;
+	IXUSR;
+	IRWXG;
+	IRGRP;
+	IWGRP;
+	IXGRP;
+	IRWXO;
+	IROTH;
+	IWOTH;
+	IXOTH;
+	ISUID;
+	ISGID;
+	ISVTX;
+	IFMT;
+	IFREG;
+	IFDIR;
+	IFBLK;
+	IFCHR;
+	IFLNK;
+	IFIFO;
+	NUMERIC(mode:Int);
+}
+
+/**
+	Abstract type for a bit field of permissions bits, i.e., an `int` in which
+	multiple bits may be set. These bit fields are returned by operations such
+	as `eval.luv.File.stat`
+**/
+@:coreType abstract FileModeNumeric {}
+
+typedef FileStatTimeSpec = {
+	var sec:Int64;
+	var nsec:Int64;
+}
+
+typedef FileStat = {
+	var dev:UInt64;
+	var mode:FileModeNumeric;
+	var nlink:UInt64;
+	var uid:UInt64;
+	var gid:UInt64;
+	var rdev:UInt64;
+	var ino:UInt64;
+	var size:UInt64;
+	var blksize:UInt64;
+	var blocks:UInt64;
+	var flags:UInt64;
+	var gen:UInt64;
+	var atim:FileStatTimeSpec;
+	var mtim:FileStatTimeSpec;
+	var ctim:FileStatTimeSpec;
+	var birthtim:FileStatTimeSpec;
+}
+
+typedef FileStatFs = {
+	var type:UInt64;
+	var bsize:UInt64;
+	var blocks:UInt64;
+	var bfree:UInt64;
+	var bavail:UInt64;
+	var files:UInt64;
+	var ffree:UInt64;
+	var fspare:Array<UInt64>;
+}
+
+enum abstract FileCopyFlag(Int) {
+	var COPYFILE_EXCL = 0;
+	var COPYFILE_FICLONE = 1;
+	var COPYFILE_FICLONE_FORCE = 2;
+}
+
+enum abstract FileAccessFlag(Int) {
+	var F_OK = 0;
+	var R_OK = 1;
+	var W_OK = 2;
+	var X_OK = 3;
+}
+
+enum abstract FileSymlinkFlag(Int) {
+	var SYMLINK_DIR = 0;
+	var SYMLINK_JUNCTION = 1;
+}
+
+/**
+	Files.
+
+	@see https://aantron.github.io/luv/luv/Luv/File
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract File to Handle {
+
+	extern static public final stdin:File;
+	extern static public final stdout:File;
+	extern static public final stderr:File;
+
+	static public function createRequest():FileRequest;
+
+	/**
+		Checks whether all the bits in `mask` are set in `bits`.
+
+		For example, if `bits` is equal to octal 0o644, then
+		`eval.luv.File.testMode [IRUSR] bits` evaluates to `true`.
+	**/
+	static public function testMode(mask:Array<FileMode>, bits:FileModeNumeric):Bool;
+
+	/**
+		Opens the file at the given path.
+		The default value of the `mode` argument is equal to octal `0o644`.
+	**/
+	static public function open(loop:Loop, path:NativeString, flags:Array<FileOpenFlag>, ?mode:Array<FileMode>, ?request:FileRequest, callback:(result:Result<File>)->Void):Void;
+
+	/**
+		Closes the file.
+	**/
+	public function close(loop:Loop, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Reads from the file.
+
+		The incoming data is written consecutively to into the given buffers.
+		The number of bytes that the operation tries to read is the total length
+		of the buffers.
+
+		End of file is indicated by `Result.Ok(0)`. Note that this is different
+		from `eval.luv.Stream.readStart`.
+	**/
+	public function read(loop:Loop, fileOffset:Int64, buffers:Array<Buffer>, ?request:FileRequest, callback:(result:Result<UInt64>)->Void):Void;
+
+	/**
+		Writes to the file.
+	**/
+	public function write(loop:Loop, fileOffset:Int64, buffers:Array<Buffer>, ?request:FileRequest, callback:(result:Result<UInt64>)->Void):Void;
+
+	/**
+		Deletes the file at the given path.
+	**/
+	static public function unlink(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Moves the file at the given path to the path given by `toPath`
+	**/
+	static public function rename(loop:Loop, path:NativeString, toPath:NativeString, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Creates a temporary file with name based on the given pattern.
+	**/
+	static public function mkstemp(loop:Loop, pattern:NativeString, ?request:FileRequest, callback:(result:Result<{name:NativeString,file:File}>)->Void):Void;
+
+	/**
+		Creates a temporary directory with name based on the given pattern.
+	**/
+	static public function mkdtemp(loop:Loop, pattern:NativeString, ?request:FileRequest, callback:(result:Result<NativeString>)->Void):Void;
+
+	/**
+		Creates a directory.
+	**/
+	static public function mkdir(loop:Loop, path:NativeString, ?mode:Array<FileMode>, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Deletes a directory.
+	**/
+	static public function rmdir(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Retrieves status information for the file at the given path.
+	**/
+	static public function stat(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<FileStat>)->Void):Void;
+
+	/**
+		Like `eval.luv.File.stat`, but does not dereference symlinks.
+	**/
+	static public function lstat(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<FileStat>)->Void):Void;
+
+	/**
+		Retrieves status information for this file.
+	**/
+	public function fstat(loop:Loop, ?request:FileRequest, callback:(result:Result<FileStat>)->Void):Void;
+
+	/**
+		Retrieves status information for the filesystem containing the given path.
+	**/
+	static public function statFs(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<FileStatFs>)->Void):Void;
+
+	/**
+		Flushes file changes to storage.
+	**/
+	public function fsync(loop:Loop, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Like `eval.luv.File.fsync`, but may omit some metadata.
+	**/
+	public function fdataSync(loop:Loop, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Truncates the given file to the given length.
+	**/
+	public function ftruncate(loop:Loop, length:Int64, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Copies the file at the given path to the path given by `toPath`.
+	**/
+	static public function copyFile(loop:Loop, path:NativeString, toPath:NativeString, ?flags:Array<FileCopyFlag>, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Transfers data between file descriptors.
+	**/
+	public function sendFile(loop:Loop, toFile:File, offset:Int64, length:UInt64, ?request:FileRequest, callback:(result:Result<UInt64>)->Void):Void;
+
+	/**
+		Checks whether the calling process can access the file at the given path.
+	**/
+	static public function access(loop:Loop, path:NativeString, flags:Array<FileAccessFlag>, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Changes permissions of the file at the given path.
+	**/
+	static public function chmod(loop:Loop, path:NativeString, mode:Array<FileMode>, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Changes permissions of the file.
+	**/
+	public function fchmod(loop:Loop, mode:Array<FileMode>, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Sets timestamps of the file at the given path.
+	**/
+	static public function utime(loop:Loop, path:NativeString, atime:Float, mtime:Float, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Sets timestamps of the file.
+	**/
+	public function futime(loop:Loop, atime:Float, mtime:Float, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Sets timestamps of the file at the given path without dereferencing symlinks.
+	**/
+	static public function lutime(loop:Loop, path:NativeString, atime:Float, mtime:Float, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Hardlinks a file at the location given by `link`.
+	**/
+	static public function link(loop:Loop, path:NativeString, link:NativeString, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Symlinks a file at the location given by `link`.
+	**/
+	static public function symlink(loop:Loop, path:NativeString, link:NativeString, ?flags:Array<FileSymlinkFlag>, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Reads the target path of a symlink.
+	**/
+	static public function readLink(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<NativeString>)->Void):Void;
+
+	/**
+		Resolves a real absolute path to the given file.
+	**/
+	static public function realPath(loop:Loop, path:NativeString, ?request:FileRequest, callback:(result:Result<NativeString>)->Void):Void;
+
+	/**
+		Changes owneship of the file at the given path.
+	**/
+	static public function chown(loop:Loop, path:NativeString, uid:Int, gid:Int, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Changes owneship of the file at the given path. without dereferencing symlinks.
+	**/
+	static public function lchown(loop:Loop, path:NativeString, uid:Int, gid:Int, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Changes owneship of the file.
+	**/
+	public function fchown(loop:Loop, uid:Int, gid:Int, ?request:FileRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Returns the integer representation of `eval.luv.File`.
+
+		`eval.luv.File` is defined as an integer file descriptor by libuv on all
+		platforms at the moment. This is a convenience function for interoperability
+		with `eval.luv.Process`, the API of which assumes that files are represented
+		by integers.
+	**/
+	public function toInt():Int;
+}
+
+/**
+	Synchronous version of `eval.luv.File` API
+**/
+extern class FileSync {
+	@:inheritDoc(eval.luv.File.open)
+	static function open(path:NativeString, flags:Array<FileOpenFlag>, ?mode:Array<FileMode>):Result<File>;
+
+	@:inheritDoc(eval.luv.File.close)
+	static function close(file:File):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.read)
+	static function read(file:File, fileOffset:Int64, buffers:Array<Buffer>):Result<UInt64>;
+
+	@:inheritDoc(eval.luv.File.write)
+	static function write(file:File, fileOffset:Int64, buffers:Array<Buffer>):Result<UInt64>;
+
+	@:inheritDoc(eval.luv.File.unlink)
+	static function unlink(path:NativeString):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.rename)
+	static function rename(path:NativeString, toPath:NativeString):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.mkstemp)
+	static function mkstemp(pattern:NativeString):Result<{name:NativeString,file:File}>;
+
+	@:inheritDoc(eval.luv.File.mkdtemp)
+	static function mkdtemp(pattern:NativeString):Result<NativeString>;
+
+	@:inheritDoc(eval.luv.File.mkdir)
+	static function mkdir(path:NativeString, ?mode:Array<FileMode>):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.rmdir)
+	static function rmdir(path:NativeString):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.stat)
+	static function stat(path:NativeString):Result<FileStat>;
+
+	@:inheritDoc(eval.luv.File.lstat)
+	static function lstat(path:NativeString):Result<FileStat>;
+
+	@:inheritDoc(eval.luv.File.fstat)
+	static function fstat(file:File):Result<FileStat>;
+
+	@:inheritDoc(eval.luv.File.statFs)
+	static function statFs(path:NativeString):Result<FileStatFs>;
+
+	@:inheritDoc(eval.luv.File.fsync)
+	static function fsync(file:File):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.fdataSync)
+	static function fdataSync(file:File):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.ftruncate)
+	static function ftruncate(file:File, length:Int64):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.copyFile)
+	static function copyFile(path:NativeString, toPath:NativeString, ?flags:Array<FileCopyFlag>):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.sendFile)
+	static function sendFile(file:File, toFile:File, offset:Int64, length:UInt64):Result<UInt64>;
+
+	@:inheritDoc(eval.luv.File.access)
+	static function access(path:NativeString, flags:Array<FileAccessFlag>):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.chmod)
+	static function chmod(path:NativeString, mode:Array<FileMode>):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.fchmod)
+	static function fchmod(file:File, mode:Array<FileMode>):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.utime)
+	static function utime(path:NativeString, atime:Float, mtime:Float):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.futime)
+	static function futime(file:File, atime:Float, mtime:Float):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.lutime)
+	static function lutime(path:NativeString, atime:Float, mtime:Float):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.link)
+	static function link(path:NativeString, link:NativeString):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.symlink)
+	static function symlink(path:NativeString, link:NativeString, ?flags:Array<FileSymlinkFlag>):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.readLink)
+	static function readLink(path:NativeString):Result<NativeString>;
+
+	@:inheritDoc(eval.luv.File.realPath)
+	static function realPath(path:NativeString):Result<NativeString>;
+
+	@:inheritDoc(eval.luv.File.chown)
+	static function chown(path:NativeString, uid:Int, gid:Int):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.lchown)
+	static function lchown(path:NativeString, uid:Int, gid:Int):Result<Result.NoData>;
+
+	@:inheritDoc(eval.luv.File.fchown)
+	static function fchown(file:File, uid:Int, gid:Int):Result<Result.NoData>;
+
+}

+ 36 - 0
std/eval/luv/FsEvent.hx

@@ -0,0 +1,36 @@
+package eval.luv;
+
+enum abstract FsEventType(Int) {
+	var RENAME = 0;
+	var CHANGE = 1;
+}
+
+enum abstract FsEventFlag(Int) {
+	var FS_EVENT_WATCH_ENTRY = 0;
+	var FS_EVENT_STAT = 1;
+	var FS_EVENT_RECURSIVE = 2;
+}
+
+/**
+	Filesystem events.
+
+	@see https://aantron.github.io/luv/luv/Luv/FS_event
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract FsEvent to Handle {
+	/**
+		Allocates and initializes an FS event handle.
+	**/
+	static public function init(loop:Loop):Result<FsEvent>;
+
+	/**
+		Starts the handle and watches the given path for changes.
+	**/
+	public function start(path:NativeString, ?flags:Array<FsEventFlag>, callback:(result:Result<{file:NativeString,events:Array<FsEventType>}>)->Void):Void;
+
+	/**
+		Stops the handle.
+	**/
+	public function stop():Result<Result.NoData>;
+
+}

+ 30 - 0
std/eval/luv/FsPoll.hx

@@ -0,0 +1,30 @@
+package eval.luv;
+
+import eval.luv.File;
+
+/**
+	Filesystem polling.
+
+	@see https://aantron.github.io/luv/luv/Luv/FS_poll
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract FsPoll to Handle {
+	/**
+		Allocates and initializes an FS polling handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop):Result<FsPoll>;
+
+	/**
+		Starts the handle and polls the given path for changes.
+
+		The default value of `interval` is 2000 (milliseconds).
+	**/
+	public function start(path:NativeString, ?interval:Int, callback:(result:Result<{previous:FileStat,current:FileStat}>)->Void):Void;
+
+	/**
+		Stops the handle.
+	**/
+	public function stop():Result<Result.NoData>;
+}

+ 85 - 0
std/eval/luv/Handle.hx

@@ -0,0 +1,85 @@
+package eval.luv;
+
+@:coreType abstract SocketHandle {}
+
+/**
+	Handles.
+
+	@see https://aantron.github.io/luv/luv/Luv/Handle
+**/
+@:coreType abstract Handle {
+	/**
+		Closes the given handle.
+	**/
+	extern static public function close(handle:Handle, callback:()->Void):Void;
+
+	/**
+		Returns `true` if the handle is active, `false` otherwise.
+	**/
+	static public function isActive(handle:Handle):Bool;
+
+	/**
+		Returns `true` if the handle is closing or closed, `false` otherwise.
+
+		Note: This function should only be used between the initialization of
+		the handle and the arrival of the close callback.
+	**/
+	static public function isClosing(handle:Handle):Bool;
+
+	/**
+		Reference the given handle.
+
+		@see https://aantron.github.io/luv/luv/Luv/Handle/#val-ref
+	**/
+	static public function ref(handle:Handle):Void;
+
+	/**
+		Un-reference the given handle.
+
+		@see https://aantron.github.io/luv/luv/Luv/Handle/#val-unref
+	**/
+	static public function unref(handle:Handle):Void;
+
+	/**
+		Returns `true` if the handle referenced, `false` otherwise.
+
+		@see https://aantron.github.io/luv/luv/Luv/Handle/#val-has_ref
+	**/
+	static public function hasRef(handle:Handle):Bool;
+
+	/**
+		Gets the size of the OS send buffer for a socket.
+
+		@see https://aantron.github.io/luv/luv/Luv/Handle/#val-send_buffer_size
+	**/
+	static public function sendBufferSize(handle:SocketHandle):Result<Int>;
+
+	/**
+		Sets the size of the OS send buffer for a socket.
+
+		@see https://aantron.github.io/luv/luv/Luv/Handle/#val-set_send_buffer_size
+	**/
+	static public function setSendBufferSize(handle:SocketHandle, size:Int):Result<Result.NoData>;
+
+	/**
+		Gets the size of the OS receive buffer for a socket.
+
+		@see https://aantron.github.io/luv/luv/Luv/Handle/#val-recv_buffer_size
+	**/
+	static public function recvBufferSize(handle:SocketHandle):Result<Int>;
+
+	/**
+		Sets the size of the OS receive buffer for a socket.
+
+		@see https://aantron.github.io/luv/luv/Luv/Handle/#val-set_recv_buffer_size
+	**/
+	static public function setRecvBufferSize(handle:SocketHandle, size:Int):Result<Result.NoData>;
+
+// TODO
+	// /**
+	// 	Retrieves the file descriptor associated with the handle.
+
+	// 	@see https://aantron.github.io/luv/luv/Luv/Handle/#val-fileno
+	// **/
+	// static public function fileno(handle:FileNo):Result<OsFd>;
+}

+ 26 - 0
std/eval/luv/Idle.hx

@@ -0,0 +1,26 @@
+package eval.luv;
+
+/**
+	Per-iteration callback.
+
+	@see https://aantron.github.io/luv/luv/Luv/Idle
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Idle to Handle {
+	/**
+		Allocate and initialize an idle handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop):Result<Idle>;
+
+	/**
+		Starts the handle with the given callback.
+	**/
+	public function start(callback:()->Void):Result<Result.NoData>;
+
+	/**
+		Stops the handle.
+	**/
+	public function stop():Result<Result.NoData>;
+}

+ 93 - 0
std/eval/luv/Loop.hx

@@ -0,0 +1,93 @@
+package eval.luv;
+
+enum abstract RunMode(Int) {
+	/** Runs the event loop until there are no more active and referenced handles or requests. */
+	var DEFAULT = 0;
+	/** Poll for i/o once. Note that this mode blocks if there are no pending callbacks. */
+	var ONCE = 1;
+	/** Poll for i/o once but don't block if there are no pending callbacks. */
+	var NOWAIT = 2;
+}
+
+/**
+	Configuration options.
+	@see http://docs.libuv.org/en/v1.x/loop.html#c.uv_loop_configure
+**/
+enum abstract LoopOption<T>(Int) {
+	extern static public final sigprof:Int;
+
+	var LOOP_BLOCK_SIGNAL:LoopOption<Int> = 0;
+	var METRICS_IDLE_TIME:LoopOption<Result.NoData> = 1;
+}
+
+/**
+	Event loops.
+
+	@see https://aantron.github.io/luv/luv/Luv/Loop
+
+	Haxe event loops define an implicit cast to libuv loops. That is, you can use
+	`sys.thread.Thread.current().events` in any place where `eval.luv.Loop` is
+	expected.
+**/
+@:coreType abstract Loop {
+	@:from
+	static inline function fromHaxeEventLoop(events:sys.thread.EventLoop):Loop {
+		return events.handle;
+	}
+
+	/**
+		Returns the default event loop.
+	**/
+	static public function defaultLoop():Loop;
+
+	/**
+		Allocates and initializes a new event loop.
+	**/
+	static public function init():Result<Loop>;
+
+	/**
+		Releases any state libuv is holding on to.
+
+		Normally there's no need to do this manually.
+
+		Warning! Only call `Loop.libraryShutdown()` once.
+		Warning! Don’t call `Loop.libraryShutdown()` when there are still event loops or I/O requests active.
+		Warning! Don’t call libuv functions after calling `Loop.libraryShutdown()`.
+	**/
+	static public function libraryShutdown():Void;
+
+	/**
+		Runs an event loop.
+	**/
+	public function run(mode:RunMode):Bool;
+
+	/**
+		Releases resources associated with an event loop.
+	**/
+	public function close():Result<Result.NoData>;
+
+	/**
+		Indicates whether the loop is monitoring any activity.
+	**/
+	public function alive():Bool;
+
+	/**
+		Stops an event loop as soon as possible.
+	**/
+	public function stop():Void;
+
+	/**
+		Returns the cached loop timestamp.
+	**/
+	public function now():eval.integers.UInt64;
+
+	/**
+		Updates the cached loop timestamp.
+	**/
+	public function updateTime():Void;
+
+	/**
+		Sets the loop option.
+	**/
+	public function configure<T>(option:LoopOption<T>, value:T):Result<Result.NoData>;
+}

+ 19 - 0
std/eval/luv/LuvException.hx

@@ -0,0 +1,19 @@
+package eval.luv;
+
+/**
+	Exceptions thrown by functions in `eval.luv` package.
+**/
+class LuvException extends haxe.Exception {
+	/**
+		The error.
+	**/
+	public final error:UVError;
+
+	/**
+		Instantiates an error with given message and position.
+	**/
+	public function new(error:UVError, ?message:String, ?previous:haxe.Exception) {
+		super(message == null ? error.toString() : message, previous);
+		this.error = error;
+	}
+}

+ 13 - 0
std/eval/luv/Metrics.hx

@@ -0,0 +1,13 @@
+package eval.luv;
+
+/**
+	Metrics.
+
+	@see https://aantron.github.io/luv/luv/Luv/Metrics
+**/
+extern class Metrics {
+	/**
+		Retrieves the amount of time the loop has been blocked waiting in the kernel.
+	**/
+	static function idleTime(loop:Loop):eval.integers.UInt64;
+}

+ 35 - 0
std/eval/luv/Mutex.hx

@@ -0,0 +1,35 @@
+package eval.luv;
+
+/**
+	Mutexes.
+
+	@see https://aantron.github.io/luv/luv/Luv/Mutex
+**/
+@:coreType abstract Mutex {
+	/**
+		Allocates and initializes a mutex.
+	**/
+	static public function init(?recursive:Bool):Result<Mutex>;
+
+	/**
+		Cleans up a mutex.
+	**/
+	public function destroy():Void;
+
+	/**
+		Takes the mutex.
+
+		The calling thread is blocked until it obtains the mutex.
+	**/
+	public function lock():Void;
+
+	/**
+		Tries to take the mutex without blocking.
+	**/
+	public function tryLock():Result<Result.NoData>;
+
+	/**
+		Releases the mutex.
+	**/
+	public function unlock():Void;
+}

+ 36 - 0
std/eval/luv/Network.hx

@@ -0,0 +1,36 @@
+package eval.luv;
+
+typedef InterfaceAddress = {
+	var name:String;
+	var isInternal:Bool;
+	var physical:NativeString;
+	var address:SockAddr;
+	var netmask:SockAddr;
+}
+
+/**
+	Network interfaces and hostname.
+
+	@see https://aantron.github.io/luv/luv/Luv/Network
+**/
+extern class Network {
+	/**
+		Lists network interface addresses.
+	**/
+	static function interfaceAddresses():Result<Array<InterfaceAddress>>;
+
+	/**
+		Retrieves a network interface name.
+	**/
+	static function ifIndexToName(index:Int):Result<String>;
+
+	/**
+		Retrieves a network interface identifier suitable for use in an IPv6 scoped address.
+	**/
+	static function ifIndexToIid(index:Int):Result<String>;
+
+	/**
+		Evaluates to the system's hostname.
+	**/
+	static function getHostName():Result<String>;
+}

+ 18 - 0
std/eval/luv/Once.hx

@@ -0,0 +1,18 @@
+package eval.luv;
+
+/**
+	Once-only initialization.
+
+	@see https://aantron.github.io/luv/luv/Luv/Once
+**/
+@:coreType abstract Once {
+	/**
+		Allocates and initializes a once-only barrier.
+	**/
+	static public function init():Result<Once>;
+
+	/**
+		Guards the given callback to be called only once.
+	**/
+	public function once(callback:()->Void):Void;
+}

+ 6 - 0
std/eval/luv/OsFd.hx

@@ -0,0 +1,6 @@
+package eval.luv;
+
+/**
+	@see https://aantron.github.io/luv/luv/Luv/Os_fd/Fd
+**/
+@:coreType abstract OsFd {}

+ 6 - 0
std/eval/luv/OsSocket.hx

@@ -0,0 +1,6 @@
+package eval.luv;
+
+/**
+	@see https://aantron.github.io/luv/luv/Luv/Os_fd/Socket
+**/
+@:coreType abstract OsSocket {}

+ 21 - 0
std/eval/luv/Passwd.hx

@@ -0,0 +1,21 @@
+package eval.luv;
+
+typedef PasswdData = {
+	var username:String;
+	var uid:Int;
+	var gid:Int;
+	var shell:Null<String>;
+	var homedir:NativeString;
+}
+
+/**
+	Current user information.
+
+	@see https://aantron.github.io/luv/luv/Luv/Passwd
+**/
+extern class Passwd {
+	/**
+		Gets passwd entry for the current user.
+	**/
+	static function getPasswd():Result<PasswdData>;
+}

+ 37 - 0
std/eval/luv/Path.hx

@@ -0,0 +1,37 @@
+package eval.luv;
+
+import eval.integers.Int64;
+import eval.integers.UInt64;
+
+/**
+	Relevant directories.
+
+	@see https://aantron.github.io/luv/luv/Luv/Path
+**/
+extern class Path {
+	/**
+		Evaluates to the executable's path.
+		It's always the path to the Haxe compiler.
+	**/
+	static function exePath():Result<NativeString>;
+
+	/**
+		Evaluates to the current working directory.
+	**/
+	static function cwd():Result<NativeString>;
+
+	/**
+		Changes the current working directory.
+	**/
+	static function chdir(dir:NativeString):Result<Result.NoData>;
+
+	/**
+		Evaluates to the path of the home directory.
+	**/
+	static function homedir():Result<NativeString>;
+
+	/**
+		Evaluates to the path of the temporary directory.
+	**/
+	static function tmpdir():Result<NativeString>;
+}

+ 18 - 0
std/eval/luv/Pid.hx

@@ -0,0 +1,18 @@
+package eval.luv;
+
+/**
+	Process ids.
+
+	@see https://aantron.github.io/luv/luv/Luv/Pid
+**/
+extern class Pid {
+	/**
+		Evaluates to the pid of the current process.
+	**/
+	static function getPid():Int;
+
+	/**
+		Evaluates to the pid of the parent process.
+	**/
+	static function getPPid():Int;
+}

+ 77 - 0
std/eval/luv/Pipe.hx

@@ -0,0 +1,77 @@
+package eval.luv;
+
+enum abstract PipeMode(Int) {
+	var READ = 0;
+	var WRITE = 1;
+	var READ_WRITE = 2;
+}
+
+enum ReceiveHandle {
+	NONE;
+	TCP(associate:(tcp:Tcp)->Result<Result.NoData>);
+	PIPE(associate:(pipe:Pipe)->Result<Result.NoData>);
+}
+
+/**
+	Pipes
+
+	@see https://aantron.github.io/luv/luv/Luv/Pipe
+**/
+@:using(eval.luv.Handle)
+@:using(eval.luv.Stream)
+@:coreType abstract Pipe to Handle to Stream to Stream.TStream<Pipe> to Handle.SocketHandle {
+	/**
+		Allocates and initializes a pipe.
+
+		The pipe is not yet connected to anything at this point.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop, forHandlePassing:Bool = false):Result<Pipe>;
+
+	/**
+		Assigns a pipe a name or an address.
+	**/
+	public function bind(nameOrAddress:NativeString):Result<Result.NoData>;
+
+	/**
+		Connects to the pipe at the given name or address.
+	**/
+	public function connect(target:NativeString, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Retrieves the name or address assigned to the pipe.
+	**/
+	public function getSockName():Result<NativeString>;
+
+	/**
+		Retrieves the name or address of the pipe's peer.
+	**/
+	public function getPeerName():Result<NativeString>;
+
+	/**
+		Set the number of pending pipe instance handles when the pipe server is
+		waiting for connections.
+	**/
+	public function pendingInstances(amount:Int):Void;
+
+	/**
+		Receives a file descriptor over the given pipe.
+
+		File descriptors are sent using the `sendHandle` argument of `eval.luv.Stream.write2`.
+		On the receiving end, call `eval.luv.Stream.readStart`. When that function
+		calls its callback, there may be file descriptors in the pipe, in addition
+		to the ordinary data provided to the callback.
+
+		To check, call this function `eval.luv.Pipe.recieveHandle` in a loop until
+		it returns `NONE`. Each time it returns `TCP(associate)` or `PIPE(associate)`,
+		create an appropriate handle using either `eval.luv.TCP.init` or `eval.uv.Pipe.init`,
+		and call `associate` to receive the file descriptor and associate it with handle.
+	**/
+	public function receiveHandle():ReceiveHandle;
+
+	/**
+		Sets pipe permissions.
+	**/
+	public function chmod(mode:PipeMode):Result<Result.NoData>;
+}

+ 26 - 0
std/eval/luv/Prepare.hx

@@ -0,0 +1,26 @@
+package eval.luv;
+
+/**
+	Pre-I/O callback.
+
+	@see https://aantron.github.io/luv/luv/Luv/Prepare
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Prepare to Handle {
+	/**
+		Allocate and initialize a prepare handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop):Result<Prepare>;
+
+	/**
+		Starts the handle with the given callback.
+	**/
+	public function start(callback:()->Void):Result<Result.NoData>;
+
+	/**
+		Stops the handle.
+	**/
+	public function stop():Result<Result.NoData>;
+}

+ 89 - 0
std/eval/luv/Process.hx

@@ -0,0 +1,89 @@
+package eval.luv;
+
+import eval.integers.Int64;
+
+/**
+	File descriptor redirections for use with `eval.luv.Process.spawn`
+**/
+@:coreType abstract Redirection {}
+
+/**
+	Options for spawning the process.
+**/
+typedef ProcessOptions = {
+	var ?onExit:(p:Process, exitStatus:Int64, termSignal:Int)->Void;
+	var ?environment:Map<String,NativeString>;
+	var ?workingDirectory:NativeString;
+	var ?redirect:Array<Redirection>;
+	var ?uid:Int;
+	var ?gid:Int;
+	var ?windowsVerbatimArguments:Bool;
+	var ?detached:Bool;
+	var ?windowsHide:Bool;
+	var ?windowsHideConsole:Bool;
+	var ?windowsHideGui:Bool;
+}
+
+/**
+	Subprocesses.
+
+	@see https://aantron.github.io/luv/luv/Luv/Process
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Process to Handle {
+	extern static public final stdin:Int;
+	extern static public final stdout:Int;
+	extern static public final stderr:Int;
+
+	/**
+		Causes `fd` in the child to be connected to `toParentPipe` in the parent.
+
+		Binds `UV_CREATE_PIPE`.
+
+		`readableInChild` sets `UV_READABLE_PIPE`, and `writableInChild` sets `UV_WRITABLE_PIPE`.
+
+		`overlapped` sets `UV_OVERLAPPED_PIPE`.
+	**/
+	static public function toParentPipe(fd:Int, parentPipe:Pipe, readableInChild:Bool, writableInChild:Bool, overlapped:Bool):Redirection;
+
+	/**
+		Causes `fd` in the child to be connected to the same device or peer as `fromParentFd` in the parent.
+
+		Binds `UV_INHERIT_FD`
+	**/
+	static public function inheritFd(fd:Int, fromParentFd:Int):Redirection;
+
+	/**
+		Same as `eval.luv.Process.inheritFd`, but takes an `eval.luv.Stream` for the parent file descriptor.
+
+		Binds `UV_INHERIT_STREAM`.
+	**/
+	static public function inheritStream(fd:Int, fromParentStream:Stream):Redirection;
+
+	/**
+		Starts a process.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function spawn(loop:Loop, cmd:NativeString, args:Array<NativeString>, ?options:ProcessOptions):Result<Process>;
+
+	/**
+		Disables (tries) file descriptor inheritance for inherited descriptors.
+	**/
+	static public function disableStdioInheritance():Void;
+
+	/**
+		Sends the given signal to the process with the given pid.
+	**/
+	static public function killPid(pid:Int, sigNum:Signal.SigNum):Result<Result.NoData>;
+
+	/**
+		Sends the given signal to the process.
+	**/
+	public function kill(sigNum:Signal.SigNum):Result<Result.NoData>;
+
+	/**
+		Evaluates to the pid of the process.
+	**/
+	public function pid():Int;
+}

+ 26 - 0
std/eval/luv/Random.hx

@@ -0,0 +1,26 @@
+package eval.luv;
+
+@:forward
+abstract RandomRequest(Request) to Request {}
+
+/**
+	System entropy source.
+
+	@see https://aantron.github.io/luv/luv/Luv/Random
+**/
+extern class Random {
+
+	static function createRequest():RandomRequest;
+
+	/**
+		Fills the given buffer with bits from the system entropy source.
+	**/
+	static function random(loop:Loop, buffer:Buffer, ?request:RandomRequest, callback:(result:Result<Result.NoData>)->Void):Void;
+}
+
+extern class RandomSync {
+	/**
+		Fills the given buffer with bits from the system entropy source.
+	**/
+	static function random(buffer:Buffer):Result<Result.NoData>;
+}

+ 13 - 0
std/eval/luv/Request.hx

@@ -0,0 +1,13 @@
+package eval.luv;
+
+/**
+	Requests.
+
+	@see https://aantron.github.io/luv/luv/Luv/Request
+**/
+@:coreType abstract Request {
+	/**
+		Tries to cancel a pending request.
+	**/
+	public function cancel():Result<Result.NoData>;
+}

+ 78 - 0
std/eval/luv/Resource.hx

@@ -0,0 +1,78 @@
+package eval.luv;
+
+import eval.integers.UInt64;
+import eval.integers.Int64;
+
+typedef RUsage = {
+	var utime:{sec:Int64, usec:Int64};
+	var stime:{sec:Int64, usec:Int64};
+	var maxrss:UInt64;
+	var ixrss:UInt64;
+	var idrss:UInt64;
+	var isrss:UInt64;
+	var minflt:UInt64;
+	var majflt:UInt64;
+	var nswap:UInt64;
+	var inblock:UInt64;
+	var oublock:UInt64;
+	var msgsnd:UInt64;
+	var msgrcv:UInt64;
+	var nsignals:UInt64;
+	var nvcsw:UInt64;
+	var nivcsw:UInt64;
+}
+
+/**
+	Resource usage.
+
+	@see https://aantron.github.io/luv/luv/Luv/Resource
+**/
+extern class Resource {
+	/**
+		Evaluates to the current uptime.
+	**/
+	static function uptime():Result<Float>;
+
+	/**
+		Evaluates to the load average.
+	**/
+	static function loadAvg():Array<Float>;
+
+	/**
+		Evaluates to the amount of free memory, in bytes.
+	**/
+	static function freeMemory():UInt64;
+
+	/**
+		Evaluates to the total amount of memory, in bytes.
+	**/
+	static function totalMemory():UInt64;
+
+	/**
+		Gets the amount of memory available to the process (in bytes) based on
+		limits imposed by the OS.
+		If there is no such constraint returns `null`
+	**/
+	static function constrainedMemory():Null<UInt64>;
+
+	/**
+		Evaluates to the priority of the process with the given pid.
+	**/
+	static function getPriority(pid:Int):Result<Int>;
+
+	/**
+		Sets the priority of the process with the given pid.
+	**/
+	static function setPriority(pid:Int, priority:Int):Result<Result.NoData>;
+
+	/**
+		Evaluates to the resident set size for the current process.
+	**/
+	static function residentSetMemory(pid:Int):Result<UInt64>;
+
+	/**
+		Gets the resource usage measures for the current process.
+	**/
+	static function getRUsage():Result<RUsage>;
+
+}

+ 40 - 0
std/eval/luv/Result.hx

@@ -0,0 +1,40 @@
+package eval.luv;
+
+/**
+	Outcome of an operation.
+**/
+@:using(eval.luv.Result.ResultTools)
+enum Result<T> {
+	/** Operation completed successfully. **/
+	Ok(value:T);
+	/** Operation failed. **/
+	Error(e:UVError);
+}
+
+enum abstract NoData(Dynamic) {
+	var NoData = null;
+}
+
+class ResultTools {
+	/**
+		Returns the result value on success or throws `eval.luv.LuvException`
+		on failure.
+	**/
+	static public inline function resolve<T>(result:Result<T>):T {
+		switch result {
+			case Ok(v): return v;
+			case Error(e): throw new LuvException(e);
+		}
+	}
+
+	/**
+		Returns `true` if the result is `Ok`.
+		Returns `false` if the result is `Error`.
+	**/
+	static public inline function isOk<T>(result:Result<T>):Bool {
+		return switch result {
+			case Ok(_): true;
+			case Error(_): false;
+		}
+	}
+}

+ 48 - 0
std/eval/luv/RwLock.hx

@@ -0,0 +1,48 @@
+package eval.luv;
+
+/**
+	Read-write locks.
+
+	@see https://aantron.github.io/luv/luv/Luv/Rwlock
+**/
+@:coreType abstract RwLock {
+	/**
+		Allocates and initializes a read-write lock.
+	**/
+	static public function init():Result<RwLock>;
+
+	/**
+		Cleans up a read-write lock.
+	**/
+	public function destroy():Void;
+
+	/**
+		Takes a read-write lock for reading (shared access).
+	**/
+	public function rdLock():Void;
+
+	/**
+		Tries to take a read-write lock for reading without blocking.
+	**/
+	public function rdTryLock():Result<Result.NoData>;
+
+	/**
+		Releases a read-write lock after it was taken for reading.
+	**/
+	public function rdUnlock():Void;
+
+	/**
+		Takes a read-write lock for writing (exclusive access).
+	**/
+	public function wrLock():Void;
+
+	/**
+		Tries to take a read-write lock for writing without blocking.
+	**/
+	public function wrTryLock():Result<Result.NoData>;
+
+	/**
+		Releases a read-write lock after it was taken for writing.
+	**/
+	public function wrUnlock():Void;
+}

+ 33 - 0
std/eval/luv/Semaphore.hx

@@ -0,0 +1,33 @@
+package eval.luv;
+
+/**
+	Semaphores.
+
+	@see https://aantron.github.io/luv/luv/Luv/Semaphore
+**/
+@:coreType abstract Semaphore {
+	/**
+		Allocates and initializes a read-write lock.
+	**/
+	static public function init(value:Int):Result<Semaphore>;
+
+	/**
+		Cleans up a semaphore.
+	**/
+	public function destroy():Void;
+
+	/**
+		Increments a semaphore.
+	**/
+	public function post():Void;
+
+	/**
+		Decrements a semaphore.
+	**/
+	public function wait():Void;
+
+	/**
+		Tries to decrement a semaphore without blocking.
+	**/
+	public function tryWait():Result<Result.NoData>;
+}

+ 57 - 0
std/eval/luv/Signal.hx

@@ -0,0 +1,57 @@
+package eval.luv;
+
+/**
+	For the moment, the signals exposed are those that are both present on Unix
+	and present or emulated by libuv on Windows.
+
+	You can also provide a plain integer signal code instead of the values of
+	this enum.
+
+	@see https://aantron.github.io/luv/luv/Luv/Signal#signals
+**/
+extern enum abstract SigNum(Int) from Int to Int {
+	var SIGABRT;
+	var SIGFPE;
+	var SIGHUP;
+	var SIGILL;
+	var SIGINT;
+	var SIGKILL;
+	var SIGSEGV;
+	var SIGTERM;
+	var SIGWINCH;
+}
+
+/**
+	Signals.
+
+	@see https://aantron.github.io/luv/luv/Luv/Signal
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Signal to Handle {
+	/**
+		Allocates and initializes a signal handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop):Result<Signal>;
+
+	/**
+		Starts the signal handle.
+	**/
+	public function start(sigNum:SigNum, callback:()->Void):Result<Result.NoData>;
+
+	/**
+		Like `eval.luv.Signal.start`, but the handle is stopped after one callback call.
+	**/
+	public function startOneshot(sigNum:SigNum, callback:()->Void):Result<Result.NoData>;
+
+	/**
+		Stops the signal handle.
+	**/
+	public function stop():Result<Result.NoData>;
+
+	/**
+		Evaluates to the signal number associated with the handle.
+	**/
+	public function signum():Int;
+}

+ 47 - 0
std/eval/luv/SockAddr.hx

@@ -0,0 +1,47 @@
+package eval.luv;
+
+/**
+	Network address families.
+**/
+enum AddressFamily {
+	UNSPEC;
+	INET;
+	INET6;
+	OTHER(i:Int);
+}
+
+/**
+	Socket types.
+**/
+enum SocketType {
+	STREAM;
+	DGRAM;
+	RAW;
+	OTHER(i:Int);
+}
+
+/**
+	Binds `struct sockaddr`.
+
+	@see https://aantron.github.io/luv/luv/Luv/Sockaddr
+**/
+@:coreType abstract SockAddr {
+	/** Extracts the port in a network address. */
+	public var port(get,never):Null<Int>;
+	function get_port():Null<Int>;
+
+	/**
+		Converts a string and port number to an IPv4 struct sockaddr.
+	**/
+	static public function ipv4(host:String, port:Int):Result<SockAddr>;
+
+	/**
+		Converts a string and port number to an IPv6 struct sockaddr.
+	**/
+	static public function ipv6(host:String, port:Int):Result<SockAddr>;
+
+	/**
+		Converts a network address to a string.
+	**/
+	public function toString():String;
+}

+ 111 - 0
std/eval/luv/Stream.hx

@@ -0,0 +1,111 @@
+package eval.luv;
+
+@:coreType abstract TStream<T> to Stream {}
+// typedef TStream<T> = Stream;
+
+enum SendHandle {
+	TCP(tcp:Tcp);
+	PIPE(pipe:Pipe);
+}
+
+/**
+	Streams.
+
+	@see https://aantron.github.io/luv/luv/Luv/Stream
+**/
+@:coreType abstract Stream to Handle {
+	/**
+		Shuts down the write side of the stream.
+	**/
+	extern static public function shutdown(stream:Stream, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Starts listening for incoming connections.
+
+		`backlog` indicates the number of connections the kernel might queue.
+		When a new incoming connection is received the `callback` is called.
+	**/
+	extern static public function listen(stream:Stream, callback:(result:Result<Result.NoData>)->Void, ?backlog:Int):Void;
+
+	/**
+		This call is used in conjunction with `Stream.listen()` to accept incoming
+		connections. Call this function after receiving a `callback` of `listen(callback)`
+		to accept the connection. Before calling this function the client handle
+		must be initialized.
+
+		When the `callback` of `listen(callback)` is called it is guaranteed that
+		this function will complete successfully the first time.
+
+		`client` should be a freshly-initialized stream.
+	**/
+	extern static public function accept<T>(server:TStream<T>, client:TStream<T>):Result<Result.NoData>;
+
+	/**
+		Calls the `callback` whenever data is available on the stream.
+
+		The amount of data read is equal to the length of the buffer passed to
+		the `callback`. `allocate` is called immediately before each call to the
+		main `callback`, to create buffer, into which the data will be read.
+
+		The end of the stream (typically, when the remote peer closes or shuts down
+		the connection) is indicated by `UVError.UV_EOF` being passed to the `callback`.
+		Note that this behavior is different from `eval.luv.File.read`.
+
+		Zero-length reads are possible, and do not indicate the end of stream. Instead,
+		they usually indicate `UVError.UV_EAGAIN` inside libuv; libuv still calls the
+		`callback` in order to give the C user a chance to deallocate the data buffer.
+		This is not usually an issue in OCaml (which is the backend for eval target of
+		Haxe), so a wrapper of this function can usually simply ignore zero-length reads.
+		It is then also safe to convert `UVError.UV_EOF` to zero-length reads in a
+		higher-level API, for consistency with reading files, and in accordance with OS
+		API convention.
+
+		To read only once, call `eval.luv.Stream.readStop` immediately, in the `callback`.
+		Otherwise, the main callback will be called repeatedly.
+	**/
+	extern static public function readStart(stream:Stream, callback:(result:Result<Buffer>)->Void, allocate:(size:Int)->Buffer):Void;
+
+	/**
+		Stops reading.
+	**/
+	extern static public function readStop(stream:Stream):Result<Result.NoData>;
+
+	/**
+		Writes the given buffer to the stream.
+
+		The second argument passed to the `callback` is the number of bytes written.
+		libuv has an internal queue of writes, in part to implement retry. This means
+		that writes can be partial at the libuv API level, so it is possible to receive
+		both an `UVError` result, and for some data to have been successfully written.
+	**/
+	extern static public function write(stream:Stream, data:Array<Buffer>, callback:(result:Result<Result.NoData>, bytesWritten:Int)->Void):Result<Result.NoData>;
+
+	/**
+		Like `eval.luv.Stream.write`, but allows sending a TCP socket or pipe over the
+		stream.
+	**/
+	extern static public function write2(stream:TStream<Pipe>, data:Array<Buffer>, sendHandle:SendHandle, callback:(result:Result<Result.NoData>, bytesWritten:Int)->Void):Result<Result.NoData>;
+
+	/**
+		Same as `eval.luv.Stream.write()`, but won’t queue a write request if it can’t
+		be completed immediately.
+
+		Returns the number of bytes written.
+	**/
+	extern static public function tryWrite(stream:Stream, data:Array<Buffer>):Result<Int>;
+
+	/**
+		Indicates whether the stream is readable (has data).
+	**/
+	extern static public function isReadable(stream:Stream):Bool;
+
+	/**
+		Indicates whether the stream is writable (has space in buffers).
+	**/
+	extern static public function isWritable(stream:Stream):Bool;
+
+	/**
+		Sets the blocking mode of the stream.
+	**/
+	extern static public function setBlocking(stream:Stream, block:Bool):Result<Result.NoData>;
+}

+ 39 - 0
std/eval/luv/SystemInfo.hx

@@ -0,0 +1,39 @@
+package eval.luv;
+
+import eval.integers.UInt64;
+
+typedef CpuInfo = {
+	var model:String;
+	var speed:Int;
+	var times:{
+		var user:UInt64;
+		var nice:UInt64;
+		var sys:UInt64;
+		var idle:UInt64;
+		var irq:UInt64;
+	};
+}
+
+typedef Uname = {
+	var sysname:String;
+	var release:String;
+	var version:String;
+	var machine:String;
+}
+
+/**
+	System information.
+
+	@see https://aantron.github.io/luv/luv/Luv/System_info
+**/
+extern class SystemInfo {
+	/**
+		Gets information about the CPUs on the system.
+	**/
+	static function cpuInfo():Result<Array<CpuInfo>>;
+
+	/**
+		Gets information about the CPUs on the system.
+	**/
+	static function uname():Result<Uname>;
+}

+ 62 - 0
std/eval/luv/Tcp.hx

@@ -0,0 +1,62 @@
+package eval.luv;
+
+import haxe.ds.Option;
+import eval.luv.SockAddr;
+
+/**
+	TCP sockets.
+
+	@see https://aantron.github.io/luv/luv/Luv/TCP
+**/
+@:using(eval.luv.Handle)
+@:using(eval.luv.Stream)
+@:coreType abstract Tcp to Handle to Stream to Stream.TStream<Tcp> to Handle.SocketHandle {
+	/**
+		Allocates and initializes a TCP stream.
+
+		The stream is not yet connected or listening.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop, ?domain:AddressFamily):Result<Tcp>;
+
+	/**
+		Sets TCP_NODELAY.
+	**/
+	public function noDelay():Result<Result.NoData>;
+
+	/**
+		Sets the TCP keepalive.
+	**/
+	public function keepAlive(value:Option<Int>):Result<Result.NoData>;
+
+	/**
+		Sets simultaneous accept.
+	**/
+	public function simultaneousAccepts(value:Bool):Result<Result.NoData>;
+
+	/**
+		Assigns an address to the TCP socket.
+	**/
+	public function bind(addr:SockAddr, ipv6Only:Bool = false):Result<Result.NoData>;
+
+	/**
+		Retrieves the address assigned to the TCP socket.
+	**/
+	public function getSockName():Result<SockAddr>;
+
+	/**
+		Retrieves the address of the TCP socket's peer.
+	**/
+	public function getPeerName():Result<SockAddr>;
+
+	/**
+		Connects to a host.
+	**/
+	public function connect(addr:SockAddr, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Resets the connection.
+	**/
+	public function closeReset(callback:(result:Result<Result.NoData>)->Void):Void;
+}

+ 34 - 0
std/eval/luv/Thread.hx

@@ -0,0 +1,34 @@
+package eval.luv;
+
+/**
+	Threads.
+
+	@see https://aantron.github.io/luv/luv/Luv/Thread
+
+	`eval.luv` integrates libuv with the OCaml runtime lock. This means that, as
+	in any other OCaml program, two threads cannot be running OCaml code at the
+	same time. Thus, two threads cannot be running Haxe code at the same time
+	because eval interpreter is written in OCaml.
+	However, `eval.luv` releases the lock when calling a potentially-blocking libuv API,
+	so that other threads can run while the calling thread is blocked. In particular,
+	the lock is released during calls to `eval.luv.Loop.run`, which means that other
+	threads can run in between when you make a call to a non-blocking API, and when
+	its callback is called by libuv.
+**/
+@:coreType abstract Thread {
+	/**
+		Returns the representation of the calling thread.
+	**/
+	static public function self():Thread;
+
+	/**
+		Starts a new thread, which will run the given function.
+	**/
+	static public function create(fn:()->Void, ?stackSize:Int):Result<Thread>;
+
+	/**
+		Waits for the thread to terminate.
+	**/
+	public function join():Result<Result.NoData>;
+
+}

+ 31 - 0
std/eval/luv/ThreadPool.hx

@@ -0,0 +1,31 @@
+package eval.luv;
+
+@:forward
+abstract ThreadPoolRequest(Request) to Request {}
+
+/**
+	Thread pool.
+
+	@see https://aantron.github.io/luv/luv/Luv/Thread_pool
+**/
+extern class ThreadPool {
+
+	static function createRequest():ThreadPoolRequest;
+
+	/**
+		Schedules a function to be called by a thread in the thread pool.
+
+		`work` is the function that will be called in the thread pool.
+		`callback` will be called by the `loop` after `work` completes, or
+		immediately, in case there is an error scheduling `work`.
+	**/
+	static function queueWork(loop:Loop, ?request:ThreadPoolRequest, work:()->Void, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Sets thread pool size.
+
+		This function should be called as soon during process startup as possible.
+	**/
+	static function setSize(size:Int, ?ifNotAlreadySet:Bool):Void;
+
+}

+ 27 - 0
std/eval/luv/Time.hx

@@ -0,0 +1,27 @@
+package eval.luv;
+
+import eval.integers.Int64;
+import eval.integers.UInt64;
+
+/**
+	Current time.
+
+	@see https://aantron.github.io/luv/luv/Luv/Time
+**/
+extern class Time {
+	/**
+		Get time.
+	**/
+	static function getTimeOfDay():Result<{sec:Int64, usec:Int}>;
+
+	/**
+		Samples the high-resolution timer.
+	**/
+	static function hrTime():UInt64;
+
+	/**
+		Suspends the calling thread for at least the given number of milliseconds.
+	**/
+	static function sleep(duration:Int):Void;
+
+}

+ 40 - 0
std/eval/luv/Timer.hx

@@ -0,0 +1,40 @@
+package eval.luv;
+
+/**
+	Timers.
+
+	@see https://aantron.github.io/luv/luv/Luv/Timer
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Timer to Handle {
+	/** The timer repeat interval. */
+	public var repeat(get,set):Int;
+	function get_repeat():Int;
+	function set_repeat(v:Int):Int;
+
+	/** Evaluates to the time until the timer expires, or zero if it has already expired. */
+	public var dueIn(get,never):Int;
+	function get_dueIn():Int;
+
+	/**
+		Allocate and initialize an idle handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop):Result<Timer>;
+
+	/**
+		Starts a timer.
+	**/
+	public function start(callback:()->Void, timeoutMs:Int, ?repeatMs:Int):Result<Result.NoData>;
+
+	/**
+		Stops a timer.
+	**/
+	public function stop():Result<Result.NoData>;
+
+	/**
+		Restarts a timer.
+	**/
+	public function again():Result<Result.NoData>;
+}

+ 63 - 0
std/eval/luv/Tty.hx

@@ -0,0 +1,63 @@
+package eval.luv;
+
+enum abstract TtyMode(Int) {
+	var NORMAL = 0;
+	var RAW = 1;
+	var IO = 2;
+}
+
+enum abstract VTermState(Int) {
+	var SUPPORTED = 0;
+	var UNSUPPORTED = 1;
+}
+
+/**
+	Consoles.
+
+	@see https://aantron.github.io/luv/luv/Luv/Tty
+**/
+@:using(eval.luv.Handle)
+@:using(eval.luv.Stream)
+@:coreType abstract Tty to Handle to Stream to Stream.TStream<Tty> {
+	/**
+		To be called when the program exits.
+		Resets TTY settings to default values for the next process to take over.
+	**/
+	static public function resetMode():Result<Result.NoData>;
+
+	/**
+		Controls whether console virtual terminal sequences are processed by libuv
+		or console. Useful in particular for enabling ConEmu support of ANSI X3.64
+		and Xterm 256 colors. Otherwise Windows10 consoles are usually detected
+		automatically.
+
+		This function is only meaningful on Windows systems. On Unix it is silently
+		ignored.
+	**/
+	static public function setVTermState(state:VTermState):Void;
+
+	/**
+		Get the current state of whether console virtual terminal sequences are
+		handled by libuv or the console.
+
+		This function is not implemented on Unix, where it returns `UVError.UV_ENOTSUP`.
+	**/
+	static public function getVTermState():Result<VTermState>;
+
+	/**
+		Allocates and initializes a TTY handle.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop, file:File):Result<Tty>;
+
+	/**
+		Sets the TTY's mode.
+	**/
+	public function setMode(mode:TtyMode):Result<Result.NoData>;
+
+	/**
+		Retrieves the current window size.
+	**/
+	public function getWinSize():Result<{width:Int, height:Int}>;
+}

+ 187 - 0
std/eval/luv/UVError.hx

@@ -0,0 +1,187 @@
+package eval.luv;
+
+/**
+	Error handling.
+
+	@see https://aantron.github.io/luv/luv/Luv/Error
+**/
+enum abstract UVError(Int) {
+	/** argument list too long */
+	var UV_E2BIG = 0;
+	/** permission denied */
+	var UV_EACCES = 1;
+	/** address already in use */
+	var UV_EADDRINUSE = 2;
+	/** address not available */
+	var UV_EADDRNOTAVAIL = 3;
+	/** address family not supported */
+	var UV_EAFNOSUPPORT = 4;
+	/** resource temporarily unavailable */
+	var UV_EAGAIN = 5;
+	/** address family not supported */
+	var UV_EAI_ADDRFAMILY = 6;
+	/** temporary failure */
+	var UV_EAI_AGAIN = 7;
+	/** bad ai_flags value */
+	var UV_EAI_BADFLAGS = 8;
+	/** invalid value for hints */
+	var UV_EAI_BADHINTS = 9;
+	/** request canceled */
+	var UV_EAI_CANCELED = 10;
+	/** permanent failure */
+	var UV_EAI_FAIL = 11;
+	/** ai_family not supported */
+	var UV_EAI_FAMILY = 12;
+	/** out of memory */
+	var UV_EAI_MEMORY = 13;
+	/** no address */
+	var UV_EAI_NODATA = 14;
+	/** unknown node or service */
+	var UV_EAI_NONAME = 15;
+	/** argument buffer overflow */
+	var UV_EAI_OVERFLOW = 16;
+	/** resolved protocol is unknown */
+	var UV_EAI_PROTOCOL = 17;
+	/** service not available for socket type */
+	var UV_EAI_SERVICE = 18;
+	/** socket type not supported */
+	var UV_EAI_SOCKTYPE = 19;
+	/** connection already in progress */
+	var UV_EALREADY = 20;
+	/** bad file descriptor */
+	var UV_EBADF = 21;
+	/** resource busy or locked */
+	var UV_EBUSY = 22;
+	/** operation canceled */
+	var UV_ECANCELED = 23;
+	/** invalid Unicode character */
+	// var UV_ECHARSET = 24; // not defined in Luv ocaml library
+	/** software caused connection abort */
+	var UV_ECONNABORTED = 25;
+	/** connection refused */
+	var UV_ECONNREFUSED = 26;
+	/** connection reset by peer */
+	var UV_ECONNRESET = 27;
+	/** destination address required */
+	var UV_EDESTADDRREQ = 28;
+	/** file already exists */
+	var UV_EEXIST = 29;
+	/** bad address in system call argument */
+	var UV_EFAULT = 30;
+	/** file too large */
+	var UV_EFBIG = 31;
+	/** host is unreachable */
+	var UV_EHOSTUNREACH = 32;
+	/** interrupted system call */
+	var UV_EINTR = 33;
+	/** invalid argument */
+	var UV_EINVAL = 34;
+	/** i/o error */
+	var UV_EIO = 35;
+	/** socket is already connected */
+	var UV_EISCONN = 36;
+	/** illegal operation on a directory */
+	var UV_EISDIR = 37;
+	/** too many symbolic links encountered */
+	var UV_ELOOP = 38;
+	/** too many open files */
+	var UV_EMFILE = 39;
+	/** message too long */
+	var UV_EMSGSIZE = 40;
+	/** name too long */
+	var UV_ENAMETOOLONG = 41;
+	/** network is down */
+	var UV_ENETDOWN = 42;
+	/** network is unreachable */
+	var UV_ENETUNREACH = 43;
+	/** file table overflow */
+	var UV_ENFILE = 44;
+	/** no buffer space available */
+	var UV_ENOBUFS = 45;
+	/** no such device */
+	var UV_ENODEV = 46;
+	/** no such file or directory */
+	var UV_ENOENT = 47;
+	/** not enough memory */
+	var UV_ENOMEM = 48;
+	/** machine is not on the network */
+	var UV_ENONET = 49;
+	/** protocol not available */
+	var UV_ENOPROTOOPT = 50;
+	/** no space left on device */
+	var UV_ENOSPC = 51;
+	/** function not implemented */
+	var UV_ENOSYS = 52;
+	/** socket is not connected */
+	var UV_ENOTCONN = 53;
+	/** not a directory */
+	var UV_ENOTDIR = 54;
+	/** directory not empty */
+	var UV_ENOTEMPTY = 55;
+	/** socket operation on non-socket */
+	var UV_ENOTSOCK = 56;
+	/** operation not supported on socket */
+	var UV_ENOTSUP = 57;
+	/** operation not permitted */
+	var UV_EPERM = 58;
+	/** broken pipe */
+	var UV_EPIPE = 59;
+	/** protocol error */
+	var UV_EPROTO = 60;
+	/** protocol not supported */
+	var UV_EPROTONOSUPPORT = 61;
+	/** protocol wrong type for socket */
+	var UV_EPROTOTYPE = 62;
+	/** result too large */
+	var UV_ERANGE = 63;
+	/** read-only file system */
+	var UV_EROFS = 64;
+	/** cannot send after transport endpoint shutdown */
+	var UV_ESHUTDOWN = 65;
+	/** invalid seek */
+	var UV_ESPIPE = 66;
+	/** no such process */
+	var UV_ESRCH = 67;
+	/** connection timed out */
+	var UV_ETIMEDOUT = 68;
+	/** text file is busy */
+	var UV_ETXTBSY = 69;
+	/** cross-device link not permitted */
+	var UV_EXDEV = 70;
+	/** unknown error */
+	var UV_UNKNOWN = 71;
+	/** end of file */
+	var UV_EOF = 72;
+	/** no such device or address */
+	var UV_ENXIO = 73;
+	/** too many links */
+	var UV_EMLINK = 74;
+	/** inappropriate ioctl for device */
+	var UV_ENOTTY = 75;
+	/** inappropriate file type or format */
+	var UV_EFTYPE = 76;
+	/** illegal byte sequence */
+	var UV_EILSEQ = 77;
+
+	/**
+		Converts a system error code to a libuv error.
+	**/
+	extern static public function translateSysError(code:Int):UVError;
+
+	/**
+		Setup a callback for unhandled exceptions.
+
+		@see https://aantron.github.io/luv/luv/Luv/Error#val-set_on_unhandled_exception
+	**/
+	extern static public function setOnUnhandledException(callback:(e:haxe.Exception)->Void):Void;
+
+	/**
+		Returns the name of the given error.
+	**/
+	extern public function errName():String;
+
+	/**
+		Returns the error message corresponding to the given error.
+	**/
+	extern public function toString():String;
+}

+ 121 - 0
std/eval/luv/Udp.hx

@@ -0,0 +1,121 @@
+package eval.luv;
+
+import haxe.ds.Option;
+import eval.luv.SockAddr;
+
+enum abstract UdpMembership(Int) {
+	var LEAVE_GROUP = 0;
+	var JOIN_GROUP = 1;
+}
+
+enum abstract RecvFlag(Int) {
+	var PARTIAL = 0;
+	var MMSG_CHUNK = 1;
+	var MMSG_FREE = 2;
+}
+
+/**
+	UDP sockets.
+
+	@see https://aantron.github.io/luv/luv/Luv/UDP
+**/
+@:using(eval.luv.Handle)
+@:coreType abstract Udp to Handle to Handle.SocketHandle {
+	/**
+		Allocates and initializes a UDP socket.
+
+		The handle should be cleaned up with `eval.luv.Handle.close` when no longer needed.
+	**/
+	static public function init(loop:Loop, ?domain:AddressFamily, recvmmsg:Bool = false):Result<Udp>;
+
+	/**
+		Assigns an address to the UDP socket.
+	**/
+	public function bind(addr:SockAddr, ipv6Only:Bool = false, reuseAddr:Bool = false):Result<Result.NoData>;
+
+	/**
+		Assigns a peer address to the socket.
+	**/
+	public function connect(addr:SockAddr):Result<ConnectedUdp>;
+
+	/**
+		Retrieves the address assigned to the UDP socket.
+	**/
+	public function getSockName():Result<SockAddr>;
+
+	/**
+		Sets multicast group membership.
+	**/
+	public function setMembership(group:String, interfaceName:String, membership:UdpMembership):Result<Result.NoData>;
+
+	/**
+		Sets source-specific multicast group membership.
+	**/
+	public function setSourceMembership(group:String, interfaceName:String, source:String, membership:UdpMembership):Result<Result.NoData>;
+
+	/**
+		Set multicast loopback.
+	**/
+	public function setMulticastLoop(value:Bool):Result<Result.NoData>;
+
+	/**
+		Set multicast TTL.
+	**/
+	public function setMulticastTtl(value:Int):Result<Result.NoData>;
+
+	/**
+		Sets the interface to be used for multicast.
+	**/
+	public function setMulticastInterface(value:Int):Result<Result.NoData>;
+
+	/**
+		Sets broadcast.
+	**/
+	public function setBroadcast(value:Bool):Result<Result.NoData>;
+
+	/**
+		Sets the TTL.
+	**/
+	public function setTtl(value:Int):Result<Result.NoData>;
+
+	/**
+		Sends a datagram.
+
+		For connected UDP sockets, see `eval.luv.UDP.Connected.send`.
+	**/
+	public function send(data:Array<Buffer>, addr:SockAddr, callback:(result:Result<Result.NoData>)->Void):Void;
+
+	/**
+		Like `eval.luv.UDP.send`, but only attempts to send the datagram immediately.
+	**/
+	public function trySend(data:Array<Buffer>, addr:SockAddr):Result<Result.NoData>;
+
+	/**
+		Calls `callback` whenever a datagram is received on the UDP socket.
+
+		@see https://aantron.github.io/luv/luv/Luv/UDP/index.html#val-recv_start
+	**/
+	public function recvStart(callback:(result:Result<{data:Buffer, addr:Option<SockAddr>, flags:Array<RecvFlag>}>, ?allocate:(size:Int)->Buffer)->Void):Void;
+
+	/**
+		Stops the callback provided to `eval.luv.UDP.recvStart`.
+	**/
+	public function recvStop():Result<Result.NoData>;
+
+	/**
+		Evaluates to true if and only if the UDP was created with `recvmmsg = true`
+		and the platform supports recvmmsg(2).
+	**/
+	public function usingRecvmmsg():Bool;
+
+	/**
+		Number of bytes queued for sending. This field strictly shows how much
+		information is currently queued.
+	**/
+	public function getSendQueueSize():Int;
+
+	/**
+		Number of send requests currently in the queue awaiting to be processed.
+	**/
+	public function getSendQueueCount():Int;
+}

+ 44 - 0
std/eval/luv/Version.hx

@@ -0,0 +1,44 @@
+package eval.luv;
+
+/**
+	Version information for the vendored libuv.
+
+	@see https://aantron.github.io/luv/luv/Luv/Version
+**/
+extern class Version {
+	/**
+		Returns the libuv version as a string.
+	**/
+	static function string():String;
+
+	/**
+		libuv major version number.
+	**/
+	static final major:Int;
+
+	/**
+		libuv minor version number.
+	**/
+	static final minor:Int;
+
+	/**
+		libuv patch version number.
+	**/
+	static final patch:Int;
+
+	/**
+		`true` if the libuv version is a release, and `false` if it is a development version.
+		This does not depend on Haxe compilation arguments and will almost always be `true`.
+	**/
+	static final isRelease:Bool;
+
+	/**
+		libuv version suffix for development releases.
+	**/
+	static final suffix:String;
+
+	/**
+		libuv version packed into a single integer.
+	**/
+	static final hex:Int;
+}

+ 22 - 3
std/sys/thread/EventLoop.hx

@@ -17,10 +17,15 @@ enum NextEventTime {
 	At(time:Float);
 }
 
+abstract EventLoopHandle(Int) {}
+
 /**
 	An event loop implementation used for `sys.thread.Thread`
 **/
+@:coreApi
 class EventLoop {
+	public final handle:EventLoopHandle = cast 0;
+
 	final mutex = new Mutex();
 	final oneTimeEvents = new Array<Null<()->Void>>();
 	var oneTimeEventsIdx = 0;
@@ -105,6 +110,9 @@ class EventLoop {
 		Executes all pending events.
 
 		The returned time stamps can be used with `Sys.time()` for calculations.
+
+		Depending on a target platform this method may be non-reentrant. It must
+		not be called from event callbacks.
 	**/
 	public function progress():NextEventTime {
 		return switch __progress(Sys.time(), []) {
@@ -117,10 +125,18 @@ class EventLoop {
 	}
 
 	/**
-		Waits for a new event to be added, or `timeout` (in seconds) to expire.
-		Returns `true` if an event was added and `false` if a timeout occurs.
+		Blocks until a new event is added or `timeout` (in seconds) to expires.
+
+		Depending on a target platform this method may also automatically execute arriving
+		events while waiting. However if any event is executed it will stop waiting.
+
+		Returns `true` if more events are expected.
+		Returns `false` if no more events expected.
+
+		Depending on a target platform this method may be non-reentrant. It must
+		not be called from event callbacks.
 	**/
-	public function wait(?timeout:Float) {
+	public function wait(?timeout:Float):Bool {
 		return waitLock.wait(timeout);
 	}
 
@@ -128,6 +144,9 @@ class EventLoop {
 		Execute all pending events.
 		Wait and execute as many events as many times `promiseEvent()` was called.
 		Runs until all repeating events are cancelled and no more events is expected.
+
+		Depending on a target platform this method may be non-reentrant. It must
+		not be called from event callbacks.
 	**/
 	public function loop():Void {
 		var events = [];