Repository: Geal/serverless-wasm
Branch: master
Commit: 1fab6e2fdb41
Files: 105
Total size: 80.3 KB
Directory structure:
gitextract_dnc2u41d/
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── rustfmt.toml
├── samples/
│ ├── config.toml
│ ├── testbackend/
│ │ ├── Cargo.toml
│ │ ├── build.sh
│ │ ├── config.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── testbackend.wasm
│ ├── testfunc/
│ │ ├── Cargo.toml
│ │ ├── build.sh
│ │ ├── config.toml
│ │ └── src/
│ │ └── lib.rs
│ └── testfunc.wasm
├── serverless-api/
│ ├── Cargo.toml
│ ├── src/
│ │ └── lib.rs
│ └── target/
│ ├── .rustc_info.json
│ └── debug/
│ ├── .cargo-lock
│ ├── .fingerprint/
│ │ ├── serverless-api-58a0200541263288/
│ │ │ ├── dep-lib-serverless_api-58a0200541263288
│ │ │ ├── lib-serverless_api-58a0200541263288
│ │ │ └── lib-serverless_api-58a0200541263288.json
│ │ └── serverless-api-81a9cad5d43bde63/
│ │ ├── dep-lib-serverless_api-81a9cad5d43bde63
│ │ ├── lib-serverless_api-81a9cad5d43bde63
│ │ └── lib-serverless_api-81a9cad5d43bde63.json
│ ├── deps/
│ │ ├── libserverless_api-58a0200541263288.rlib
│ │ ├── libserverless_api-81a9cad5d43bde63.rmeta
│ │ ├── serverless_api-58a0200541263288.d
│ │ └── serverless_api-81a9cad5d43bde63.d
│ ├── incremental/
│ │ └── serverless_api-101jg2fs4zen3/
│ │ └── s-f15by5smxq-136cfpy-1gc3dqcx796yx/
│ │ ├── 16u6js6g0l3k1ic6.bc.z
│ │ ├── 16u6js6g0l3k1ic6.o
│ │ ├── 181cuta0v63atwcm.bc.z
│ │ ├── 181cuta0v63atwcm.o
│ │ ├── 1im38lueib99jsk0.bc.z
│ │ ├── 1im38lueib99jsk0.o
│ │ ├── 1mvmz58owquyropc.bc.z
│ │ ├── 1mvmz58owquyropc.o
│ │ ├── 1w44dretk843l467.bc.z
│ │ ├── 1w44dretk843l467.o
│ │ ├── 1y16o1qfye96o7m0.bc.z
│ │ ├── 1y16o1qfye96o7m0.o
│ │ ├── 23tqyymcb18u96mb.bc.z
│ │ ├── 23tqyymcb18u96mb.o
│ │ ├── 2jqywn86b2gsqohu.bc.z
│ │ ├── 2jqywn86b2gsqohu.o
│ │ ├── 2lyh15q6cjwzy18c.bc.z
│ │ ├── 2lyh15q6cjwzy18c.o
│ │ ├── 2q5257pdh5222n7q.bc.z
│ │ ├── 2q5257pdh5222n7q.o
│ │ ├── 3ayaeypdcro9d6yk.bc.z
│ │ ├── 3ayaeypdcro9d6yk.o
│ │ ├── 3wq0rk2lqn1zrv77.bc.z
│ │ ├── 3wq0rk2lqn1zrv77.o
│ │ ├── 3wta9ctgdrpkmlpr.bc.z
│ │ ├── 3wta9ctgdrpkmlpr.o
│ │ ├── 40mv2bo8bxf7ur21.bc.z
│ │ ├── 40mv2bo8bxf7ur21.o
│ │ ├── 43v6g0y2xsxoggnt.bc.z
│ │ ├── 43v6g0y2xsxoggnt.o
│ │ ├── 48721dc4k5qxei0u.bc.z
│ │ ├── 48721dc4k5qxei0u.o
│ │ ├── 49a7n47po4ttqjl7.bc.z
│ │ ├── 49a7n47po4ttqjl7.o
│ │ ├── 49lx1q7cxvpykyv0.bc.z
│ │ ├── 49lx1q7cxvpykyv0.o
│ │ ├── 4ezmh1vbs95c5ack.bc.z
│ │ ├── 4ezmh1vbs95c5ack.o
│ │ ├── 4i7tkntav6hyd03k.bc.z
│ │ ├── 4i7tkntav6hyd03k.o
│ │ ├── 4xq48u46a1pwiqn7.bc.z
│ │ ├── 4xq48u46a1pwiqn7.o
│ │ ├── 4yh8x2b62dcih00t.bc.z
│ │ ├── 4yh8x2b62dcih00t.o
│ │ ├── 4ypvbwho0bu5tnww.bc.z
│ │ ├── 4ypvbwho0bu5tnww.o
│ │ ├── 56dly8q07ws8ucdq.bc.z
│ │ ├── 56dly8q07ws8ucdq.o
│ │ ├── 8xzrsc1ux72v29j.bc.z
│ │ ├── 8xzrsc1ux72v29j.o
│ │ ├── 98g0d9x8aw3akpe.bc.z
│ │ ├── 98g0d9x8aw3akpe.o
│ │ ├── 9elsx31vb4it187.bc.z
│ │ ├── 9elsx31vb4it187.o
│ │ ├── c6lbtaiefvx3wya.bc.z
│ │ ├── c6lbtaiefvx3wya.o
│ │ ├── mz7vgmcf23rofcc.bc.z
│ │ ├── mz7vgmcf23rofcc.o
│ │ ├── y08g5q2x813c4wx.bc.z
│ │ ├── y08g5q2x813c4wx.o
│ │ ├── z9ox7biyn1otfln.bc.z
│ │ └── z9ox7biyn1otfln.o
│ ├── libserverless_api.d
│ ├── libserverless_api.rlib
│ └── libserverless_api.rmeta
└── src/
├── async/
│ ├── host.rs
│ ├── mod.rs
│ └── session.rs
├── config.rs
├── interpreter.rs
├── jit/
│ ├── env.rs
│ └── mod.rs
├── main.rs
└── sync/
├── host.rs
└── mod.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/target
**/*.rs.bk
================================================
FILE: Cargo.toml
================================================
[package]
name = "serverless-wasm"
version = "0.1.0"
license = "MIT"
repository = "https://github.com/Geal/serverless-wasm"
readme = "README.md"
authors = ["Geoffroy Couprie <geo.couprie@gmail.com>"]
[dependencies]
parity-wasm = "^0.27"
rouille = "^2.1"
slab = "^0.3"
toml = "^0.4"
serde = "^1.0"
serde_derive = "^1.0"
mio = "^0.6"
httparse = "^1.2"
#wasmi = "^0.1"
wasmi = { git = "https://github.com/geal/wasmi" }
#wasmi = { path = "../wasmi" }
cretonne = "*"
cretonne-wasm = "^0.8"
cretonne-module = "*"
cretonne-simplejit = "^0.8"
================================================
FILE: LICENSE
================================================
Copyright (c) 2018 Geoffroy Couprie
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# Serverless Web Assembly framework

## Why?
For fun.
## But why?

This is a small demo of Web Assembly's potential outside of browsers.
It has been designed with client side execution in mind, but there's
nothing preventing it from running in other platforms.
There are people working on running WASM binaries from a shell, and
putting WASM code inside kernels.
Here are some benefits of Web Assembly's design:
- maps well to CPU assembly (with Just In Time compilation in mind)
- the virtual machine does not require garbage collection, only small memory areas
that will be handled by the guest code
- meant to be sandboxed
And here's the best part: it is meant to be a target language, a lot of other
languages will compile to WASM. You can already write C or C++ and compile
to WASM with emscripten. Rust's compiler natively supports it. There are demos
in Go, Haskell and Ruby.
The network effects are huge: the major browsers implement it and can run any
WASM app, and every language wants to run on the client side.
Now, what happens when you leverage these advantages to build a server platform?
You get a system that can run a lot of small, sandboxed, resource limited
applications, written in a lot of different languages.
You do not care about how to start it, you don't need to map it to filesystems
and common runtimes like containers do. You just have some bytecode that imports
a few functions, and runs well isolated.
This is a bit like the serverless promise, the ability to run arbitrarily small
functions, but without even caring about the startup time or the state size,
this will be the smallest you can think of.
## What works
Currently, the server is able to load a pre built web assembly binary, exports
some function that it can use for logging, to build a response and connect to
other servers, and handle requests using that wasm file (as long as it exports
a "handle" function).
## How to run it
### Requirements for WASM applications
the WASM application must export a `handle` function that takes no arguments and
returns no arguments.
The virtual machine currently exposes the following functions, that you can use
to build your response:
```rust
extern {
fn log(ptr: *const u8, size: u64);
fn response_set_status_line(status: u32, ptr: *const u8, size: u64);
fn response_set_header(name_ptr: *const u8, name_size: u64, value_ptr: *const u8, value_size: u64);
fn response_set_body(ptr: *const u8, size: u64);
fn tcp_connect(ptr: *const u8, size: u64) -> i32;
fn tcp_read(fd: i32, ptr: *mut u8, size: u64) -> i64;
fn tcp_write(fd: i32, ptr: *const u8, size: u64) -> i64;
}
```
### Configuration file
You define which WASM binary will handle which requests through a TOML configuration
file:
```toml
listen_address = "127.0.0.1:8080"
[[applications]]
file_path = "./samples/testfunc.wasm"
method = "GET"
url_path = "/hello"
[[applications]]
file_path = "./samples/testbackend.wasm"
method = "GET"
url_path = "/backend"
```
### Running it
You can build and launch the server as follows:
```rust
cargo build && ./target/debug/serverless-wasm ./samples/config.toml
```
## Current features
- [x] load web assembly file to handle requests
- [x] logging function available from WASM
- [x] API to build a response from WASM
- [x] (blocking) TCP connections to backend servers or databases
- [x] routing to mutiple apps depending on the request
- [x] set up initial state via "environment variables"
- [ ] proper error handling (the server will panic even if you give it the side eye)
- [ ] (in progress) asynchronous event loop to receive connections and handle backend TCP connections
- [ ] file system abstraction (loading files from S3 or other providers?)
- [ ] (in progress) "standard API" for functions exported by the VM
## Prior art
While I was building this, I heard of [IceCore](https://github.com/losfair/IceCore),
which looks quite cool, with JIT support, etc.
It's quite nice to see multiple platforms attempting this. Maybe we'll be able to
agree onthe "web assembly standard API" so WASM apps can run on any of those :)
================================================
FILE: rustfmt.toml
================================================
tab_spaces = 2
struct_field_align_threshold = 100
max_width = 140
================================================
FILE: samples/config.toml
================================================
listen_address = "127.0.0.1:8080"
[[applications]]
file_path = "./samples/testfunc.wasm"
method = "GET"
url_path = "/hello"
function = "hello"
[[applications]]
file_path = "./samples/testfunc.wasm"
method = "GET"
url_path = "/bonjour"
function = "bonjour"
[[applications]]
file_path = "./samples/testbackend.wasm"
method = "GET"
url_path = "/backend"
function = "handle"
env = { "/env/backend"= "127.0.0.1:8181"}
================================================
FILE: samples/testbackend/Cargo.toml
================================================
[package]
name = "testbackend"
version = "0.1.0"
authors = ["Geoffroy Couprie <geo.couprie@gmail.com>"]
[lib]
path = "src/lib.rs"
crate-type = ["cdylib"]
[dependencies]
serverless-api = { path = "../../serverless-api/" }
================================================
FILE: samples/testbackend/build.sh
================================================
#!/bin/sh
RUSTFLAGS="-Clink-args=--import-memory" cargo +nightly build -v --target wasm32-unknown-unknown --release
cp target/wasm32-unknown-unknown/release/testbackend.wasm ..
================================================
FILE: samples/testbackend/config.toml
================================================
rustflags = ["-Clink-args=--import-memory"]
================================================
FILE: samples/testbackend/src/lib.rs
================================================
use std::str;
extern crate serverless_api as api;
#[no_mangle]
pub extern "C" fn handle() {
api::log("Hello world with api!");
let body;
let key = "/env/backend";
match api::db::get(key) {
None => {
body = format!("could not get value for key {}", key);
},
Some(address) => {
api::log(&format!("connecting to backend at {}", address));
match api::TcpStream::connect(&address) {
None => {
body = "could not connect to backend".to_string();
},
Some(mut socket) => {
match socket.write(b"hello\n") {
None => {
body = "could not write to backend server".to_string();
},
Some(_) => {
let mut res: [u8; 100] = [0u8; 100];
match socket.read(&mut res) {
None => {
body = "could not read from backend server".to_string();
},
Some(sz) => {
api::log(&format!("read data from backend: \"{:?}\"", str::from_utf8(&res[..sz]).unwrap()));
body = format!("Hello world from wasm!\nanswer from backend:\n{}\n", str::from_utf8(&res[..sz]).unwrap());
api::response::set_status(200, "Ok");
api::response::set_header("Content-length", &body.len().to_string());
api::response::set_body(body.as_bytes());
}
}
}
}
}
}
}
}
api::log(&body);
api::response::set_status(500, "Server error");
api::response::set_header("Content-length", &body.len().to_string());
api::response::set_body(body.as_bytes());
}
================================================
FILE: samples/testfunc/Cargo.toml
================================================
[package]
name = "testfunc"
version = "0.1.0"
authors = ["Geoffroy Couprie <geo.couprie@gmail.com>"]
[lib]
path = "src/lib.rs"
crate-type = ["cdylib"]
[dependencies]
serverless-api = { path = "../../serverless-api/" }
================================================
FILE: samples/testfunc/build.sh
================================================
#!/bin/sh
RUSTFLAGS="-Clink-args=--import-memory" cargo +nightly build -v --target wasm32-unknown-unknown --release
cp target/wasm32-unknown-unknown/release/testfunc.wasm ..
================================================
FILE: samples/testfunc/config.toml
================================================
rustflags = ["-Clink-args=--import-memory"]
================================================
FILE: samples/testfunc/src/lib.rs
================================================
use std::ptr;
use std::str;
extern crate serverless_api as api;
#[no_mangle]
pub extern "C" fn hello() {
api::log("Hello world with api!");
api::response::set_status(200, "Ok");
let body = "Hello world from wasm!\n";
api::response::set_header("Content-length", &body.len().to_string());
api::response::set_body(body.as_bytes());
}
#[no_mangle]
pub extern "C" fn bonjour() {
api::log("Bonjour tout le monde!");
api::response::set_status(200, "Ok");
let body = "Bonjour tout le monde depuis le monde merveilleux de WASM!\n";
api::response::set_header("Content-length", &body.len().to_string());
api::response::set_body(body.as_bytes());
}
================================================
FILE: serverless-api/Cargo.toml
================================================
[package]
name = "serverless-api"
version = "0.1.0"
authors = ["Geoffroy Couprie <geo.couprie@gmail.com>"]
[dependencies]
================================================
FILE: serverless-api/src/lib.rs
================================================
use std::str;
mod sys {
extern {
pub fn log(ptr: *const u8, size: u64);
pub fn response_set_status_line(status: u32, ptr: *const u8, size: u64);
pub fn response_set_header(name_ptr: *const u8, name_size: u64, value_ptr: *const u8, value_size: u64);
pub fn response_set_body(ptr: *const u8, size: u64);
pub fn tcp_connect(ptr: *const u8, size: u64) -> i32;
pub fn tcp_read(fd: i32, ptr: *mut u8, size: u64) -> i64;
pub fn tcp_write(fd: i32, ptr: *const u8, size: u64) -> i64;
pub fn db_get(key_ptr: *const u8, key_size: u64, value_ptr: *const u8, value_size: u64) -> i64;
}
}
pub fn log(s: &str) {
unsafe { sys::log(s.as_ptr(), s.len() as u64) };
}
pub mod db {
use super::sys;
use std::iter::repeat;
pub fn get(key: &str) -> Option<String> {
let mut empty = vec![];
let read_sz = unsafe {
sys::db_get(key.as_ptr(), key.len() as u64, (&mut empty).as_mut_ptr(), empty.len() as u64)
};
if read_sz < 0 {
return None;
} else if read_sz == 0 {
return Some(String::new());
}
let mut v = Vec::with_capacity(read_sz as usize);
v.extend(repeat(0).take(read_sz as usize));
let sz = unsafe {
sys::db_get(key.as_ptr(), key.len() as u64, v.as_mut_ptr(), v.len() as u64)
};
if sz < 0 {
return None;
} else if sz == 0 {
return Some(String::new());
}
if sz as usize != v.len() {
None
} else {
String::from_utf8(v).ok()
}
}
}
pub mod response {
use super::sys;
pub fn set_status(status: u16, reason: &str) {
unsafe {
sys::response_set_status_line(status.into(), reason.as_ptr(), reason.len() as u64);
}
}
pub fn set_header(name: &str, value: &str) {
unsafe {
sys::response_set_header(name.as_ptr(), name.len() as u64, value.as_ptr(), value.len() as u64);
}
}
pub fn set_body(body: &[u8]) {
unsafe {
sys::response_set_body(body.as_ptr(), body.len() as u64);
}
}
}
pub struct TcpStream {
fd: i32
}
impl TcpStream {
pub fn connect(address: &str) -> Option<TcpStream> {
let fd = unsafe { sys::tcp_connect(address.as_ptr(), address.len() as u64) };
if fd < 0 {
None
} else {
Some(TcpStream { fd })
}
}
pub fn write(&mut self, data: &[u8]) -> Option<usize> {
let res = unsafe { sys::tcp_write(self.fd, data.as_ptr(), data.len() as u64) };
if res < 0 {
None
} else {
Some(res as usize)
}
}
pub fn read(&mut self, data: &mut [u8]) -> Option<usize> {
let res = unsafe { sys::tcp_read(self.fd, data.as_mut_ptr(), data.len() as u64) };
if res < 0 {
None
} else {
Some(res as usize)
}
}
}
================================================
FILE: serverless-api/target/.rustc_info.json
================================================
{"rustc_fingerprint":10175240907518990213,"outputs":{"1617349019360157463":["___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/geal/.rustup/toolchains/nightly-x86_64-apple-darwin\ndebug_assertions\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"mmx\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_feature=\"ssse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nunix\n",""],"1164083562126845933":["rustc 1.27.0-nightly (9fae15374 2018-05-13)\nbinary: rustc\ncommit-hash: 9fae1537462bb10fd17d07816efc17cfe4786806\ncommit-date: 2018-05-13\nhost: x86_64-apple-darwin\nrelease: 1.27.0-nightly\nLLVM version: 6.0\n",""],"3144802570395919623":["___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/geal/.rustup/toolchains/nightly-x86_64-apple-darwin\ndebug_assertions\nproc_macro\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"mmx\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_feature=\"ssse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nunix\n",""]}}
================================================
FILE: serverless-api/target/debug/.cargo-lock
================================================
================================================
FILE: serverless-api/target/debug/.fingerprint/serverless-api-58a0200541263288/lib-serverless_api-58a0200541263288
================================================
68e06764be0d3fe0
================================================
FILE: serverless-api/target/debug/.fingerprint/serverless-api-58a0200541263288/lib-serverless_api-58a0200541263288.json
================================================
{"rustc":2983502972076547984,"features":"[]","target":12616003758275913516,"profile":16965165444893579269,"path":10872709659218687626,"deps":[],"local":[{"MtimeBased":[[1526638291,222510886],".fingerprint/serverless-api-58a0200541263288/dep-lib-serverless_api-58a0200541263288"]}],"rustflags":[],"edition":"Edition2015"}
================================================
FILE: serverless-api/target/debug/.fingerprint/serverless-api-81a9cad5d43bde63/lib-serverless_api-81a9cad5d43bde63
================================================
d109624b85f6a4ab
================================================
FILE: serverless-api/target/debug/.fingerprint/serverless-api-81a9cad5d43bde63/lib-serverless_api-81a9cad5d43bde63.json
================================================
{"rustc":2983502972076547984,"features":"[]","target":12616003758275913516,"profile":4101608828254088483,"path":10872709659218687626,"deps":[],"local":[{"MtimeBased":[[1526637383,987549318],".fingerprint/serverless-api-81a9cad5d43bde63/dep-lib-serverless_api-81a9cad5d43bde63"]}],"rustflags":[],"edition":"Edition2015"}
================================================
FILE: serverless-api/target/debug/deps/serverless_api-58a0200541263288.d
================================================
/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/deps/libserverless_api-58a0200541263288.rlib: src/lib.rs
/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/deps/serverless_api-58a0200541263288.d: src/lib.rs
src/lib.rs:
================================================
FILE: serverless-api/target/debug/deps/serverless_api-81a9cad5d43bde63.d
================================================
/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/deps/serverless_api-81a9cad5d43bde63.rmeta: src/lib.rs
/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/deps/serverless_api-81a9cad5d43bde63.d: src/lib.rs
src/lib.rs:
================================================
FILE: serverless-api/target/debug/libserverless_api.d
================================================
/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/libserverless_api.rlib: /Users/geal/dev/rust/projects/serverless-wasm/serverless-api/src/lib.rs
================================================
FILE: src/async/host.rs
================================================
//! from https://github.com/paritytech/wasmi/blob/master/src/tests/host.rs
use slab::Slab;
use std::collections::HashMap;
use std::io::{Read, Write};
use std::iter::repeat;
use mio::net::TcpStream;
use std::net::SocketAddr;
use std::str;
use std::cmp;
use std::rc::Rc;
use std::cell::RefCell;
use wasmi::memory_units::Pages;
use wasmi::*;
use interpreter::Host;
#[derive(Debug)]
pub enum AsyncHostError {
Connecting(SocketAddr),
TcpRead(i32, u32, u64),
TcpWrite(i32, u32, u64, usize),
}
impl ::std::fmt::Display for AsyncHostError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
write!(f, "{:?}", self)
}
}
impl HostError for AsyncHostError {}
#[derive(Clone, Debug)]
pub struct PreparedResponse {
pub status_code: Option<u16>,
pub reason: Option<String>,
pub headers: Vec<(String, String)>,
pub body: Option<Vec<u8>>,
}
impl PreparedResponse {
pub fn new() -> PreparedResponse {
PreparedResponse {
status_code: None,
reason: None,
headers: Vec::new(),
body: None,
}
}
}
pub struct State {
pub memory: Option<MemoryRef>,
pub instance: Option<ModuleRef>,
pub prepared_response: PreparedResponse,
pub connections: Slab<TcpStream>,
pub db: HashMap<String, String>,
}
impl State {
pub fn new() -> State {
State {
memory: None,//Some(MemoryInstance::alloc(Pages(3), Some(Pages(100))).unwrap()),
instance: None,
prepared_response: PreparedResponse::new(),
connections: Slab::with_capacity(100),
db: HashMap::new(),
}
}
}
impl State {
pub fn get_buf(&mut self, ptr: u32, size: usize) -> Option<Vec<u8>> {
self.memory.as_ref().and_then(|mref| {
mref.get(ptr, size).map_err(|e| println!("get buf error: {:?}", e)).ok()
})
}
pub fn write_buf(&mut self, ptr: u32, data: &[u8]) {
self.memory.as_ref().map(|m| m.set(ptr, data));
}
}
pub struct AsyncHost {
pub inner: Rc<RefCell<State>>,
}
impl Host for AsyncHost {
type State = State;
fn build(s: Rc<RefCell<Self::State>>) -> Self {
AsyncHost { inner: s }
}
}
/// log(ptr: *mut u8, size: u64)
///
/// Returns value at the given address in memory. This function
/// requires attached memory.
const LOG_INDEX: usize = 0;
const RESPONSE_SET_STATUS_LINE: usize = 1;
const RESPONSE_SET_HEADER: usize = 2;
const RESPONSE_SET_BODY: usize = 3;
const TCP_CONNECT: usize = 4;
const TCP_READ: usize = 5;
const TCP_WRITE: usize = 6;
const DB_GET: usize = 7;
impl Externals for AsyncHost {
fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
match index {
LOG_INDEX => {
let ptr: u32 = args.nth(0);
let sz: u64 = args.nth(1);
let v = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
println!("log({} bytes): {}", v.len(), str::from_utf8(&v).unwrap());
Ok(None)
}
RESPONSE_SET_STATUS_LINE => {
let status: u32 = args.nth(0);
let ptr: u32 = args.nth(1);
let sz: u64 = args.nth(2);
let reason = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
self.inner.borrow_mut().prepared_response.status_code = Some(status as u16);
self.inner.borrow_mut().prepared_response.reason = Some(String::from_utf8(reason).unwrap());
Ok(None)
}
RESPONSE_SET_HEADER => {
let ptr1: u32 = args.nth(0);
let sz1: u64 = args.nth(1);
let ptr2: u32 = args.nth(2);
let sz2: u64 = args.nth(3);
let header_name = {
self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr1, sz1 as usize)
.unwrap()
};
let header_value = {
self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr2, sz2 as usize)
.unwrap()
};
self.inner.borrow_mut().prepared_response.headers.push((
String::from_utf8(header_name).unwrap(),
String::from_utf8(header_value).unwrap(),
));
Ok(None)
}
RESPONSE_SET_BODY => {
let ptr: u32 = args.nth(0);
let sz: u64 = args.nth(1);
let body = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
self.inner.borrow_mut().prepared_response.body = Some(body);
Ok(None)
}
TCP_CONNECT => {
let ptr: u32 = args.nth(0);
let sz: u64 = args.nth(1);
let v = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
let address = String::from_utf8(v).unwrap();
println!("received tcp_connect for {:?}", address);
let error = AsyncHostError::Connecting(address.parse().unwrap());
Err(Trap::new(TrapKind::Host(Box::new(error))))
}
TCP_READ => {
let fd: i32 = args.nth(0);
let ptr: u32 = args.nth(1);
let sz: u64 = args.nth(2);
let error = AsyncHostError::TcpRead(fd, ptr, sz);
Err(Trap::new(TrapKind::Host(Box::new(error))))
/*
let mut v = Vec::with_capacity(sz as usize);
v.extend(repeat(0).take(sz as usize));
let mut state = self.inner.borrow_mut();
if let Ok(sz) = state.connections[fd as usize].read(&mut v) {
state.memory.as_ref().map(|m| m.set(ptr, &v[..sz]));
Ok(Some(RuntimeValue::I64(sz as i64)))
} else {
Ok(Some(RuntimeValue::I64(-1)))
}
*/
}
TCP_WRITE => {
let fd: i32 = args.nth(0);
let ptr: u32 = args.nth(1);
let sz: u64 = args.nth(2);
let error = AsyncHostError::TcpWrite(fd, ptr, sz, 0);
Err(Trap::new(TrapKind::Host(Box::new(error))))
/*
let buf = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
if let Ok(sz) = self.inner.borrow_mut().connections[fd as usize].write(&buf) {
Ok(Some(RuntimeValue::I64(sz as i64)))
} else {
Ok(Some(RuntimeValue::I64(-1)))
}
*/
}
DB_GET => {
let key_ptr: u32 = args.nth(0);
let key_sz: u64 = args.nth(1);
let value_ptr: u32 = args.nth(2);
let value_sz: u64 = args.nth(3);
let v = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(key_ptr, key_sz as usize)
.unwrap();
let key = String::from_utf8(v).unwrap();
println!("requested value for key {}", key);
match self.inner.borrow().db.get(&key) {
None => Ok(Some(RuntimeValue::I64(-1))),
Some(value) => {
let to_write = cmp::min(value.len(), value_sz as usize);
self
.inner
.borrow()
.memory
.as_ref()
.map(|m| m.set(value_ptr, (&value[..to_write]).as_bytes()));
Ok(Some(RuntimeValue::I64(value.len() as i64)))
}
}
}
_ => panic!("env doesn't provide function at index {}", index),
}
}
}
impl State {
fn check_signature(&self, index: usize, signature: &Signature) -> bool {
let (params, ret_ty): (&[ValueType], Option<ValueType>) = match index {
LOG_INDEX => (&[ValueType::I32, ValueType::I64], None),
RESPONSE_SET_STATUS_LINE => (&[ValueType::I32, ValueType::I32, ValueType::I64], None),
RESPONSE_SET_HEADER => (
&[
ValueType::I32,
ValueType::I64,
ValueType::I32,
ValueType::I64,
],
None,
),
RESPONSE_SET_BODY => (&[ValueType::I32, ValueType::I64], None),
TCP_CONNECT => (&[ValueType::I32, ValueType::I64], Some(ValueType::I32)),
TCP_READ => (
&[ValueType::I32, ValueType::I32, ValueType::I64],
Some(ValueType::I64),
),
TCP_WRITE => (
&[ValueType::I32, ValueType::I32, ValueType::I64],
Some(ValueType::I64),
),
DB_GET => (
&[
ValueType::I32,
ValueType::I64,
ValueType::I32,
ValueType::I64,
],
Some(ValueType::I64),
),
_ => return false,
};
signature.params() == params && signature.return_type() == ret_ty
}
}
impl ModuleImportResolver for State {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
let index = match field_name {
"log" => LOG_INDEX,
"response_set_status_line" => RESPONSE_SET_STATUS_LINE,
"response_set_header" => RESPONSE_SET_HEADER,
"response_set_body" => RESPONSE_SET_BODY,
"tcp_connect" => TCP_CONNECT,
"tcp_read" => TCP_READ,
"tcp_write" => TCP_WRITE,
"db_get" => DB_GET,
_ => {
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
};
if !self.check_signature(index, signature) {
return Err(Error::Instantiation(format!(
"Export `{}` doesnt match expected type {:?}",
field_name, signature
)));
}
Ok(FuncInstance::alloc_host(signature.clone(), index))
}
fn resolve_memory(&self, _field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
let Pages(initial1) = self.memory.as_ref().map(|m| m.initial()).unwrap();
let initial2 = _memory_type.initial() as usize;
//println!("requested {} pages", initial2);
if initial2 > initial1 {
self.memory.as_ref().map(|_m| {
//println!("grow res: {:?}", m.grow(Pages(initial2 - initial1)).unwrap());
});
}
let Pages(_initial) = self.memory.as_ref().map(|m| m.current_size()).unwrap();
//println!("current number of pages: {}", initial);
//println!("resolving memory at name: {}", field_name);
let res = self.memory.as_ref().unwrap().clone();
Ok(res)
}
}
pub struct StateResolver {
pub inner: Rc<RefCell<State>>,
}
impl ModuleImportResolver for StateResolver {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
let index = match field_name {
"log" => LOG_INDEX,
"response_set_status_line" => RESPONSE_SET_STATUS_LINE,
"response_set_header" => RESPONSE_SET_HEADER,
"response_set_body" => RESPONSE_SET_BODY,
"tcp_connect" => TCP_CONNECT,
"tcp_read" => TCP_READ,
"tcp_write" => TCP_WRITE,
"db_get" => DB_GET,
_ => {
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
};
if !self.inner.borrow().check_signature(index, signature) {
return Err(Error::Instantiation(format!(
"Export `{}` doesnt match expected type {:?}",
field_name, signature
)));
}
Ok(FuncInstance::alloc_host(signature.clone(), index))
}
fn resolve_memory(&self, _field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
self.inner.borrow_mut().memory = Some(MemoryInstance::alloc(Pages(_memory_type.initial() as usize), Some(Pages(100))).unwrap());
Ok(self.inner.borrow().memory.as_ref().unwrap().clone())
}
}
================================================
FILE: src/async/mod.rs
================================================
use config::{ApplicationState, Config};
use mio::*;
use mio::net::{TcpListener, TcpStream};
use mio::unix::UnixReady;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;
use slab::Slab;
mod host;
mod session;
const SERVER: Token = Token(0);
pub fn server(config: Config) {
let state = ApplicationState::new(&config);
let addr = (&config.listen_address).parse().unwrap();
let server = TcpListener::bind(&addr).unwrap();
let mut poll = Poll::new().unwrap();
poll
.register(&server, SERVER, Ready::readable(), PollOpt::edge())
.unwrap();
let mut events = Events::with_capacity(1024);
let state = Rc::new(RefCell::new(state));
let mut connections = Slab::with_capacity(1024);
let mut ready = VecDeque::new();
loop {
poll.poll(&mut events, None).unwrap();
println!("got events: {:?}", events);
for event in events.iter() {
match event.token() {
SERVER => {
if let Ok((sock, addr)) = server.accept() {
match connections.vacant_entry() {
None => {
println!("error: no more room for new connections");
}
Some(entry) => {
let index = entry.index();
poll.register(
&sock,
Token(index + 1),
Ready::readable() | Ready::writable() | Ready::from(UnixReady::hup() | UnixReady::error()),
PollOpt::edge(),
);
let client = Rc::new(RefCell::new(session::Session::new(
state.clone(),
sock,
index,
)));
entry.insert(client);
}
}
}
}
Token(i) => {
let client_token = i - 1;
if let Some(ref mut client) = connections.get_mut(client_token) {
if client
.borrow_mut()
.process_events(client_token, event.readiness())
{
ready.push_back(client_token);
}
} else {
println!(
"non existing token {:?} got events {:?}",
client_token,
event.readiness()
);
}
}
_ => unreachable!(),
}
}
for client_token in ready.drain(..) {
let mut cont = session::ExecutionResult::Continue;
if let Some(ref mut client) = connections.get_mut(client_token) {
cont = client.borrow_mut().execute();
} else {
println!("non existing token {:?} was marked as ready", client_token);
}
match cont {
session::ExecutionResult::Close(tokens) => {
for t in tokens.iter() {
connections.remove(client_token);
}
},
session::ExecutionResult::ConnectBackend(address) => {
let client = connections.get(client_token).unwrap().clone();
match connections.vacant_entry() {
None => {
println!("error: no more room for new connections");
}
Some(entry) => {
let index = entry.index();
let stream = TcpStream::connect(&address).unwrap();
poll.register(
&stream,
Token(index + 1),
Ready::readable() | Ready::writable() | Ready::from(UnixReady::hup() | UnixReady::error()),
PollOpt::edge(),
);
client.borrow_mut().add_backend(stream, index);
entry.insert(client);
}
}
},
_ => {}
}
}
}
}
================================================
FILE: src/async/session.rs
================================================
use mio::unix::UnixReady;
use mio::net::TcpStream;
use mio::{Poll, Ready};
use std::collections::HashMap;
use std::iter::repeat;
use std::rc::Rc;
use std::io::{ErrorKind, Read, Write};
use std::cell::RefCell;
use std::net::{SocketAddr, Shutdown};
use slab::Slab;
use interpreter::WasmInstance;
use super::host;
use config::ApplicationState;
use httparse;
use wasmi::{ExternVal, ImportsBuilder, ModuleInstance, TrapKind, RuntimeValue};
#[derive(Debug, Clone, PartialEq)]
pub enum ExecutionResult {
WouldBlock,
Close(Vec<usize>),
Continue,
ConnectBackend(SocketAddr),
//Register(usize),
//Remove(Vec<usize>),
}
#[derive(Debug)]
pub struct Stream {
pub readiness: UnixReady,
pub interest: UnixReady,
pub stream: TcpStream,
pub index: usize,
}
pub struct Buf {
buf: Vec<u8>,
offset: usize,
len: usize,
}
#[derive(Debug,Clone,PartialEq)]
pub enum SessionState {
WaitingForRequest,
WaitingForBackendConnect(usize),
TcpRead(i32, u32, usize),
TcpWrite(i32, Vec<u8>, usize),
Executing,
Done,
}
pub struct Session {
client: Stream,
backends: HashMap<usize, Stream>,
instance: Option<WasmInstance<host::State, host::AsyncHost>>,
config: Rc<RefCell<ApplicationState>>,
buffer: Buf,
pub state: Option<SessionState>,
method: Option<String>,
path: Option<String>,
env: Option<Rc<RefCell<host::State>>>,
}
impl Session {
pub fn new(config: Rc<RefCell<ApplicationState>>, stream: TcpStream, index: usize) -> Session {
let client = Stream {
readiness: UnixReady::from(Ready::empty()),
interest: UnixReady::from(Ready::readable()) | UnixReady::hup() | UnixReady::error(),
stream,
index,
};
let capacity = 8192;
let mut v = Vec::with_capacity(capacity);
v.extend(repeat(0).take(capacity));
let buffer = Buf {
buf: v,
offset: 0,
len: 0,
};
Session {
client,
backends: HashMap::new(),
instance: None,
config,
buffer,
state: Some(SessionState::WaitingForRequest),
method: None,
path: None,
env: None,
}
}
pub fn add_backend(&mut self, stream: TcpStream, index: usize) {
let s = Stream {
readiness: UnixReady::from(Ready::empty()),
interest: UnixReady::from(Ready::writable()) | UnixReady::hup() | UnixReady::error(),
stream,
index,
};
self.backends.insert(index, s);
self.state = Some(SessionState::WaitingForBackendConnect(index));
}
pub fn resume(&mut self) -> ExecutionResult {
let res = self.instance.as_mut().map(|instance| instance.resume()).unwrap();
println!("resume result: {:?}", res);
match res {
Err(t) => match t.kind() {
TrapKind::Host(ref err) => {
match err.as_ref().downcast_ref() {
Some(host::AsyncHostError::Connecting(address)) => {
println!("returning connect to backend server: {}", address);
return ExecutionResult::ConnectBackend(address.clone());
},
Some(host::AsyncHostError::TcpWrite(fd, ptr, sz, written)) => {
self.backends.get_mut(&(*fd as usize)).map(|backend| backend.interest.insert(UnixReady::from(Ready::writable())));
let buf = self.env.as_mut().and_then(|env| env.borrow_mut().get_buf(*ptr, *sz as usize)).unwrap();
self.state = Some(SessionState::TcpWrite(*fd, buf, *written));
return ExecutionResult::Continue;
},
Some(host::AsyncHostError::TcpRead(fd, ptr, sz)) => {
self.backends.get_mut(&(*fd as usize)).map(|backend| backend.interest.insert(UnixReady::from(Ready::readable())));
self.state = Some(SessionState::TcpRead(*fd, *ptr, *sz as usize));
return ExecutionResult::Continue;
},
_ => { panic!("got host error: {:?}", err) }
}
},
_ => {
panic!("got trap: {:?}", t);
}
},
Ok(_) => if self
.instance
.as_mut()
.map(|instance| {
println!(
"set up response: {:?}",
instance.state.borrow().prepared_response
);
instance
.state
.borrow()
.prepared_response
.status_code
.is_some() && instance.state.borrow().prepared_response.body.is_some()
})
.unwrap_or(false)
{
self.client.interest.insert(Ready::writable());
return ExecutionResult::Continue
}
}
ExecutionResult::Continue
}
pub fn create_instance(&mut self) -> ExecutionResult {
let method = self.method.as_ref().unwrap();
let path = self.path.as_ref().unwrap();
if let Some((func_name, module, ref opt_env)) = self.config.borrow().route(method, path) {
let mut env = host::State::new();
if let Some(h) = opt_env {
env.db.extend(
h.iter()
.map(|(ref k, ref v)| (k.to_string(), v.to_string())),
);
}
let env = Rc::new(RefCell::new(env));
self.env = Some(env.clone());
let resolver = host::StateResolver { inner: env.clone() };
let main = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &resolver))
.expect("Failed to instantiate module")
.assert_no_start();
if let Some(ExternVal::Func(func_ref)) = main.export_by_name(func_name) {
let instance = WasmInstance::new(env, &func_ref, &[]);
self.instance = Some(instance);
ExecutionResult::Continue
} else {
println!("function not found");
self
.client
.stream
.write(b"HTTP/1.1 404 Not Found\r\nContent-length: 19\r\n\r\nFunction not found\n");
self.client.stream.shutdown(Shutdown::Both);
self.client.interest = UnixReady::from(Ready::empty());
ExecutionResult::Close(vec![self.client.index])
}
} else {
println!("route not found");
self
.client
.stream
.write(b"HTTP/1.1 404 Not Found\r\nContent-length: 16\r\n\r\nRoute not found\n");
self.client.stream.shutdown(Shutdown::Both);
self.client.interest = UnixReady::from(Ready::empty());
ExecutionResult::Close(vec![self.client.index])
}
}
pub fn process_events(&mut self, token: usize, events: Ready) -> bool {
println!("client[{}]: token {} got events {:?}", self.client.index, token, events);
if token == self.client.index {
self.client.readiness = self.client.readiness | UnixReady::from(events);
self.client.readiness & self.client.interest != UnixReady::from(Ready::empty())
} else {
if let Some(ref mut stream) = self.backends.get_mut(&token) {
println!("state: {:?}", self.state);
if self.state == Some(SessionState::WaitingForBackendConnect(token)) {
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I32(token as i32)));
self.state = Some(SessionState::Executing);
}
stream.readiness.insert(UnixReady::from(events));
stream.readiness & stream.interest != UnixReady::from(Ready::empty())
} else {
println!("non existing backend {} got events {:?}", token, events);
false
}
}
}
pub fn execute(&mut self) -> ExecutionResult {
loop {
let front_readiness = self.client.readiness & self.client.interest;
if front_readiness.is_readable() {
let res = self.front_readable();
if res != ExecutionResult::Continue {
return res;
}
}
if front_readiness.is_writable() {
let res = self.front_writable();
if res != ExecutionResult::Continue {
return res;
}
}
let res = self.process();
if res != ExecutionResult::Continue {
return res;
}
}
}
fn front_readable(&mut self) -> ExecutionResult {
if self.state == Some(SessionState::WaitingForRequest) {
loop {
if self.buffer.offset + self.buffer.len == self.buffer.buf.len() {
break;
}
match self
.client
.stream
.read(&mut self.buffer.buf[self.buffer.offset + self.buffer.len..])
{
Ok(0) => {
return ExecutionResult::Close(vec![self.client.index]);
}
Ok(sz) => {
self.buffer.len += sz;
}
Err(e) => {
if e.kind() == ErrorKind::WouldBlock {
self.client.readiness.remove(Ready::readable());
break;
}
}
}
}
ExecutionResult::Continue
} else {
ExecutionResult::Close(vec![self.client.index])
}
}
fn process(&mut self) -> ExecutionResult {
println!("[{}] process", self.client.index);
let state = self.state.take().unwrap();
match state {
SessionState::WaitingForRequest => {
let (method, path) = {
let mut headers = [httparse::Header {
name: "",
value: &[],
}; 16];
let mut req = httparse::Request::new(&mut headers);
match req.parse(&self.buffer.buf[self.buffer.offset..self.buffer.len]) {
Err(e) => {
println!("http parsing error: {:?}", e);
self.state = Some(SessionState::WaitingForRequest);
return ExecutionResult::Close(vec![self.client.index]);
}
Ok(httparse::Status::Partial) => {
self.state = Some(SessionState::WaitingForRequest);
return ExecutionResult::Continue;
}
Ok(httparse::Status::Complete(sz)) => {
self.buffer.offset += sz;
println!("got request: {:?}", req);
(
req.method.unwrap().to_string(),
req.path.unwrap().to_string(),
)
}
}
};
self.client.interest.remove(Ready::readable());
self.method = Some(method);
self.path = Some(path);
self.state = Some(SessionState::Executing);
ExecutionResult::Continue
},
SessionState::Executing => {
if self.instance.is_none() {
let res = self.create_instance();
if res != ExecutionResult::Continue {
self.state = Some(SessionState::Executing);
return res;
}
}
println!("resuming");
self.state = Some(SessionState::Executing);
self.resume()
},
SessionState::TcpRead(fd, ptr, sz) => {
let readiness = self.backends[&(fd as usize)].readiness & self.backends[&(fd as usize)].interest;
println!("tcpread({}): readiness: {:?}", fd, readiness);
if readiness.is_readable() {
let mut buffer = Vec::with_capacity(sz as usize);
buffer.extend(repeat(0).take(sz as usize));
let mut read = 0usize;
loop {
match self.backends.get_mut(&(fd as usize)).unwrap().stream.read(&mut buffer[read..]) {
Ok(0) => {
println!("read 0");
self.backends.get_mut(&(fd as usize)).map(|backend| backend.readiness.remove(Ready::readable()));
self.env.as_mut().map(|env| env.borrow_mut().write_buf(ptr, &buffer[..read]));
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I64(read as i64)));
self.state = Some(SessionState::Executing);
return ExecutionResult::Continue;
},
Ok(sz) => {
read += sz;
println!("read {} bytes", read);
if read == sz {
//FIXME: return result
self.env.as_mut().map(|env| env.borrow_mut().write_buf(ptr, &buffer[..read]));
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I64(read as i64)));
self.state = Some(SessionState::Executing);
return ExecutionResult::Continue;
}
},
Err(e) => match e.kind() {
ErrorKind::WouldBlock => {
println!("wouldblock");
self.backends.get_mut(&(fd as usize)).map(|backend| backend.readiness.remove(Ready::readable()));
self.env.as_mut().map(|env| env.borrow_mut().write_buf(ptr, &buffer[..read]));
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I64(read as i64)));
self.state = Some(SessionState::Executing);
return ExecutionResult::Continue;
},
e => {
println!("backend socket error: {:?}", e);
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I64(-1)));
self.state = Some(SessionState::Executing);
//FIXME
return ExecutionResult::Continue;
}
}
}
}
} else {
self.state = Some(SessionState::TcpRead(fd, ptr, sz));
ExecutionResult::WouldBlock
}
},
SessionState::TcpWrite(fd, buffer, mut written) => {
let readiness = self.backends[&(fd as usize)].readiness & self.backends[&(fd as usize)].interest;
if readiness.is_writable() {
loop {
match self.backends.get_mut(&(fd as usize)).unwrap().stream.write(&buffer[written..]) {
Ok(0) => {
self.backends.get_mut(&(fd as usize)).map(|backend| backend.readiness.remove(Ready::writable()));
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I64(written as i64)));
self.state = Some(SessionState::Executing);
return ExecutionResult::Continue;
},
Ok(sz) => {
written += sz;
println!("wrote {} bytes", sz);
if written == buffer.len() {
//FIXME: return result
self.state = Some(SessionState::Executing);
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I64(written as i64)));
return ExecutionResult::Continue;
}
},
Err(e) => match e.kind() {
ErrorKind::WouldBlock => {
println!("wouldblock");
self.backends.get_mut(&(fd as usize)).map(|backend| backend.readiness.remove(Ready::writable()));
self.state = Some(SessionState::TcpWrite(fd, buffer, written));
return ExecutionResult::Continue;
},
e => {
println!("backend socket error: {:?}", e);
self.instance.as_mut().map(|instance| instance.add_function_result(RuntimeValue::I64(-1)));
self.state = Some(SessionState::Executing);
//FIXME
return ExecutionResult::Continue;
}
}
}
}
} else {
self.state = Some(SessionState::TcpWrite(fd, buffer, written));
ExecutionResult::WouldBlock
}
//FIXME: handle error and hup
},
SessionState::WaitingForBackendConnect(_) => {
panic!("should not have called execute() in WaitingForBackendConnect");
},
SessionState::Done => {
panic!("done");
}
}
}
fn front_writable(&mut self) -> ExecutionResult {
println!("[{}] front writable", self.client.index);
let response = self
.instance
.as_mut()
.map(|instance| instance.state.borrow().prepared_response.clone())
.unwrap();
self
.client
.stream
.write_fmt(format_args!("HTTP/1.1 {} {}\r\n", response.status_code.unwrap(), response.reason.unwrap()));
for header in response.headers.iter() {
self
.client
.stream
.write_fmt(format_args!("{}: {}\r\n", header.0, header.1));
}
self.client.stream.write(b"\r\n");
self.client.stream.write(&response.body.unwrap()[..]);
ExecutionResult::Close(vec![self.client.index])
}
}
================================================
FILE: src/config.rs
================================================
use interpreter::load_module;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use toml;
use wasmi::Module;
#[derive(Deserialize, Debug)]
pub struct WasmApp {
pub file_path: String,
pub method: String,
pub url_path: String,
pub function: String,
pub env: Option<HashMap<String, String>>,
}
#[derive(Deserialize, Debug)]
pub struct Config {
pub listen_address: String,
pub applications: Vec<WasmApp>,
}
pub fn load(file: &str) -> Option<Config> {
if let Ok(mut file) = File::open(file) {
let mut contents = String::new();
if let Ok(_) = file.read_to_string(&mut contents) {
return toml::from_str(&contents)
.map_err(|e| {
println!("configuration deserialization error: {:?}", e);
e
})
.ok();
}
}
None
}
pub struct ApplicationState {
/// (method, url path) -> (function name, module path, env)
pub routes: HashMap<(String, String), (String, String, Option<HashMap<String, String>>)>,
/// module path -> Module
pub modules: HashMap<String, Module>,
}
impl ApplicationState {
pub fn new(config: &Config) -> ApplicationState {
let mut routes = HashMap::new();
let mut modules = HashMap::new();
for app in config.applications.iter() {
//FIXME: it might be good to not panic when we don't find the function in the module
let module = load_module(&app.file_path, &app.function);
if !modules.contains_key(&app.file_path) {
modules.insert(app.file_path.clone(), module);
}
routes.insert(
(app.method.clone(), app.url_path.clone()),
(app.function.clone(), app.file_path.clone(), app.env.clone()),
);
}
ApplicationState {
routes: routes,
modules: modules,
}
}
pub fn route(&self, method: &str, url: &str) -> Option<(&str, &Module, &Option<HashMap<String, String>>)> {
if let Some((func_name, module_path, ref opt_env)) = self.routes.get(&(method.to_string(), url.to_string())) {
if let Some(module) = self.modules.get(module_path) {
return Some((func_name, module, opt_env));
}
}
None
}
}
================================================
FILE: src/interpreter.rs
================================================
use parity_wasm;
use parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};
use std::collections::VecDeque;
use wasmi::{self, Module};
use wasmi::{BlockFrameType, Externals, FuncInstance, FuncRef, FunctionContext, Interpreter, RunResult, RuntimeValue, Trap, TrapKind};
use std::marker;
use std::rc::Rc;
use std::cell::RefCell;
pub const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
pub const DEFAULT_FRAME_STACK_LIMIT: usize = 16384;
pub trait HostBuilder<'a, S> {
fn build(s: &'a mut S) -> Self;
}
pub trait Host {
type State;
fn build(s: Rc<RefCell<Self::State>>) -> Self;
}
pub struct WasmInstance<S, E: Externals + Host<State = S>> {
pub state: Rc<RefCell<S>>,
pub stack: VecDeque<FunctionContext>,
_marker: marker::PhantomData<E>,
}
impl<S, E: Externals + Host<State = S>> WasmInstance<S, E> {
pub fn new(state: Rc<RefCell<S>>, func_ref: &FuncRef, args: &[RuntimeValue]) -> WasmInstance<S, E> {
let stack = create_stack(&func_ref, args);
WasmInstance {
state: state,
stack,
_marker: marker::PhantomData,
}
}
pub fn resume(&mut self) -> Result<Option<RuntimeValue>, Trap> {
let mut host = E::build(self.state.clone());
let mut interpreter = Interpreter::new(&mut host);
println!("WasmInstance::resume: stack\n{:?}", self.stack);
my_run_interpreter_loop(&mut interpreter, &mut self.stack)
}
pub fn add_function_result(&mut self, return_value: RuntimeValue) {
self.stack.back_mut().map(|function_context| {
function_context.value_stack_mut().push(return_value).expect("should have pushed the return value");
println!("adding return value to {:?} initialized: {}",
function_context.function, function_context.is_initialized);
});
println!("added function result {:?}, stack len:{}", return_value, self.stack.len());
}
}
pub fn create_stack(func: &FuncRef, args: &[RuntimeValue]) -> VecDeque<FunctionContext> {
let context = FunctionContext::new(
func.clone(),
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
func.signature(),
args.into_iter().cloned().collect(),
);
let mut function_stack = VecDeque::new();
function_stack.push_back(context);
function_stack
}
pub fn my_run_interpreter_loop<E>(
interpreter: &mut Interpreter<E>,
function_stack: &mut VecDeque<FunctionContext>,
) -> Result<Option<RuntimeValue>, Trap>
where
E: Externals,
{
loop {
let mut function_context = function_stack
.pop_back()
.expect("on loop entry - not empty; on loop continue - checking for emptiness; qed");
let function_ref = function_context.function.clone();
let function_body = function_ref
.body()
.expect("Host functions checked in function_return below; Internal functions always have a body; qed");
if !function_context.is_initialized() {
let return_type = function_context.return_type;
function_context.initialize(&function_body.locals);
function_context
.push_frame(&function_body.labels, BlockFrameType::Function, return_type)
.map_err(Trap::new)?;
}
let function_return = interpreter
.do_run_function(
&mut function_context,
function_body.opcodes.elements(),
&function_body.labels,
)
.map_err(Trap::new)?;
match function_return {
RunResult::Return(return_value) => match function_stack.back_mut() {
Some(caller_context) => if let Some(return_value) = return_value {
caller_context
.value_stack_mut()
.push(return_value)
.map_err(Trap::new)?;
},
None => return Ok(return_value),
},
RunResult::NestedCall(nested_func) => {
//println!("calling nested func, stack len={}", function_stack.len());
match FuncInstance::invoke_context(&nested_func, &mut function_context, interpreter.externals) {
Err(t) => {
if let TrapKind::Host(_) = t.kind() {
//function_context.value_stack_mut().push(RuntimeValue::I32(42)).expect("should have pushed the return value");
function_stack.push_back(function_context);
println!("got host trapkind");
return Err(t);
} else {
println!("resume got error: {:?}", t);
return Err(t);
}
},
Ok(None) => {
function_stack.push_back(function_context);
//println!("got ok(none) stack len={}", function_stack.len());
}
Ok(Some(nested_context)) => {
function_stack.push_back(function_context);
function_stack.push_back(nested_context);
//println!("got ok(some(nested_context)) stack len={}", function_stack.len());
}
}
}
}
}
}
pub fn load_module(file: &str, func_name: &str) -> Module {
let module = parity_wasm::deserialize_file(file).expect("File to be deserialized");
// Extracts call arguments from command-line arguments
let _args = {
// Export section has an entry with a func_name with an index inside a module
let export_section = module.export_section().expect("No export section found");
// It's a section with function declarations (which are references to the type section entries)
let function_section = module
.function_section()
.expect("No function section found");
// Type section stores function types which are referenced by function_section entries
let type_section = module.type_section().expect("No type section found");
// Given function name used to find export section entry which contains
// an `internal` field which points to the index in the function index space
let found_entry = export_section
.entries()
.iter()
.find(|entry| func_name == entry.field())
.expect(&format!("No export with name {} found", func_name));
// Function index in the function index space (internally-defined + imported)
let function_index: usize = match found_entry.internal() {
&Internal::Function(index) => index as usize,
_ => panic!("Founded export is not a function"),
};
// We need to count import section entries (functions only!) to subtract it from function_index
// and obtain the index within the function section
let import_section_len: usize = match module.import_section() {
Some(import) => import
.entries()
.iter()
.map(|entry| {
//println!("importing entry {:?}", entry);
entry
})
.filter(|entry| match entry.external() {
&External::Function(_) => true,
_ => false,
})
.count(),
None => 0,
};
// Calculates a function index within module's function section
let function_index_in_section = function_index - import_section_len;
// Getting a type reference from a function section entry
let func_type_ref: usize = function_section.entries()[function_index_in_section].type_ref() as usize;
// Use the reference to get an actual function type
let function_type: &FunctionType = match &type_section.types()[func_type_ref] {
&Type::Function(ref func_type) => func_type,
};
// Parses arguments and constructs runtime values in correspondence of their types
function_type
.params()
.iter()
.enumerate()
.map(|(_i, value)| match value {
&ValueType::I32 => RuntimeValue::I32(
0, /* program_args[i]
.parse::<i32>()
.expect(&format!("Can't parse arg #{} as i32", program_args[i])),*/
),
&ValueType::I64 => RuntimeValue::I64(
0, /* program_args[i]
.parse::<i64>()
.expect(&format!("Can't parse arg #{} as i64", program_args[i])),*/
),
&ValueType::F32 => RuntimeValue::F32(
0.0, /* program_args[i]
.parse::<f32>()
.expect(&format!("Can't parse arg #{} as f32", program_args[i])),*/
),
&ValueType::F64 => RuntimeValue::F64(
0.0, /* program_args[i]
.parse::<f64>()
.expect(&format!("Can't parse arg #{} as f64", program_args[i])),*/
),
})
.collect::<Vec<RuntimeValue>>()
};
wasmi::Module::from_parity_wasm_module(module).expect("Module to be valid")
}
================================================
FILE: src/jit/env.rs
================================================
use cretonne_wasm::{
ModuleEnvironment, GlobalIndex, MemoryIndex, TableIndex,
FunctionIndex, Table, Memory, Global, SignatureIndex,
FuncTranslator, FuncEnvironment, GlobalValue
};
use cretonne::prelude::{settings::{self, Flags}, types::*, InstBuilder, Signature};
use cretonne::codegen::{
ir::{self, ExternalName, Function},
cursor::FuncCursor
};
pub struct Exportable<T> {
/// A wasm entity.
pub entity: T,
/// Names under which the entity is exported.
pub export_names: Vec<String>,
}
impl<T> Exportable<T> {
pub fn new(entity: T) -> Self {
Self {
entity,
export_names: Vec::new(),
}
}
}
pub struct ModuleInfo {
pub flags: Flags,
pub signatures: Vec<Signature>,
pub imported_funcs: Vec<(String, String)>,
pub functions: Vec<Exportable<SignatureIndex>>,
pub function_bodies: Vec<Function>,
pub memories: Vec<Exportable<Memory>>,
pub tables: Vec<Exportable<Table>>,
pub globals: Vec<Exportable<Global>>,
pub start_func: Option<FunctionIndex>,
}
impl ModuleInfo {
pub fn new() -> ModuleInfo {
ModuleInfo {
flags: settings::Flags::new(settings::builder()),
signatures: Vec::new(),
imported_funcs: Vec::new(),
functions: Vec::new(),
function_bodies: Vec::new(),
memories: Vec::new(),
tables: Vec::new(),
globals: Vec::new(),
start_func: None,
}
}
}
pub struct Env {
pub info: ModuleInfo,
trans: FuncTranslator,
}
impl Env {
pub fn new() -> Env {
Env {
info: ModuleInfo::new(),
trans: FuncTranslator::new(),
}
}
}
fn get_func_name(func_index: FunctionIndex) -> ir::ExternalName {
ExternalName::user(0, func_index as u32)
}
impl<'data> ModuleEnvironment<'data> for Env {
fn flags(&self) -> &Flags {
&self.info.flags
}
fn get_func_name(&self, func_index: FunctionIndex) -> ExternalName {
get_func_name(func_index)
}
fn declare_signature(&mut self, sig: &Signature) {
self.info.signatures.push(sig.clone());
}
fn get_signature(&self, sig_index: SignatureIndex) -> &Signature {
&self.info.signatures[sig_index]
}
fn declare_func_import(
&mut self,
sig_index: SignatureIndex,
module: &'data str,
field: &'data str
) {
assert_eq!(
self.info.functions.len(),
self.info.imported_funcs.len(),
"Imported functions must be declared first"
);
self.info.functions.push(Exportable::new(sig_index));
self.info.imported_funcs.push((
String::from(module),
String::from(field),
));
println!("declared function import {}:{}", module, field);
}
fn get_num_func_imports(&self) -> usize {
self.info.imported_funcs.len()
}
fn declare_func_type(&mut self, sig_index: SignatureIndex) {
self.info.functions.push(Exportable::new(sig_index));
}
fn get_func_type(&self, func_index: FunctionIndex) -> SignatureIndex {
self.info.functions[func_index].entity
}
fn declare_global(&mut self, global: Global) {
self.info.globals.push(Exportable::new(global));
}
fn get_global(&self, global_index: GlobalIndex) -> &Global {
&self.info.globals[global_index].entity
}
fn declare_table(&mut self, table: Table) {
self.info.tables.push(Exportable::new(table));
}
fn declare_table_elements(
&mut self,
table_index: TableIndex,
base: Option<GlobalIndex>,
offset: usize,
elements: Vec<FunctionIndex>
) {
//println!("declaring table elements at table n°{} base {:?} offset {}:{:?}", table_index, base, offset, elements);
}
fn declare_memory(&mut self, memory: Memory) {
println!("declaring new memory zone, min: {}, max: {:?}, shared: {}", memory.pages_count, memory.maximum,
memory.shared);
self.info.memories.push(Exportable::new(memory));
}
fn declare_data_initialization(
&mut self,
memory_index: MemoryIndex,
base: Option<GlobalIndex>,
offset: usize,
data: &'data [u8]
) {
println!("declaring data init for memory n°{}, base {:?}, offset {}, data: {:?}",
memory_index, base, offset, data.len());
}
fn declare_func_export(
&mut self,
func_index: FunctionIndex,
name: &'data str
) {
println!("exporting function n°{} at '{}'", func_index, name);
self.info.functions[func_index].export_names.push(
String::from(name)
)
}
fn declare_table_export(
&mut self,
table_index: TableIndex,
name: &'data str
) { unimplemented!() }
fn declare_memory_export(
&mut self,
memory_index: MemoryIndex,
name: &'data str
) { unimplemented!() }
fn declare_global_export(
&mut self,
global_index: GlobalIndex,
name: &'data str
) { unimplemented!() }
fn declare_start_func(&mut self, index: FunctionIndex) {
debug_assert!(self.info.start_func.is_none());
self.info.start_func = Some(index);
}
fn define_function_body(
&mut self,
body_bytes: &'data [u8]
) -> Result<(), String> {
let func = {
let mut func_environ = FuncEnv::new(&self.info);
let function_index = self.get_num_func_imports() + self.info.function_bodies.len();
let name = get_func_name(function_index);
let sig = func_environ.vmctx_sig(self.get_func_type(function_index));
let mut func = Function::with_name_signature(name, sig);
self.trans
.translate(body_bytes, &mut func, &mut func_environ)
.map_err(|e| format!("{}", e))?;
func
};
self.info.function_bodies.push(func);
Ok(())
}
}
pub struct FuncEnv<'env> {
pub mod_info: &'env ModuleInfo,
}
impl<'env> FuncEnv<'env> {
pub fn new(mod_info: &'env ModuleInfo) -> Self {
Self { mod_info }
}
// Create a signature for `sigidx` amended with a `vmctx` argument after the standard wasm
// arguments.
fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature {
let mut sig = self.mod_info.signatures[sigidx].clone();
sig.params.push(ir::AbiParam::special(
self.native_pointer(),
ir::ArgumentPurpose::VMContext,
));
sig
}
}
impl<'env> FuncEnvironment for FuncEnv<'env> {
fn flags(&self) -> &settings::Flags {
&self.mod_info.flags
}
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalValue {
// Just create a dummy `vmctx` global.
let offset = ((index * 8) as i32 + 8).into();
let gv = func.create_global_var(ir::GlobalVarData::VMContext { offset });
GlobalValue::Memory {
gv,
ty: self.mod_info.globals[index].entity.ty,
}
}
fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
// Create a static heap whose base address is stored at `vmctx+0`.
let gv = func.create_global_var(ir::GlobalVarData::VMContext { offset: 0.into() });
func.create_heap(ir::HeapData {
base: ir::HeapBase::GlobalVar(gv),
min_size: 0.into(),
guard_size: 0x8000_0000.into(),
style: ir::HeapStyle::Static { bound: 0x1_0000_0000.into() },
})
}
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
// A real implementation would probably change the calling convention and add `vmctx` and
// signature index arguments.
func.import_signature(self.vmctx_sig(index))
}
fn make_direct_func(&mut self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef {
let sigidx = self.mod_info.functions[index].entity;
// A real implementation would probably add a `vmctx` argument.
// And maybe attempt some signature de-duplication.
let signature = func.import_signature(self.vmctx_sig(sigidx));
let name = get_func_name(index);
func.import_function(ir::ExtFuncData {
name,
signature,
colocated: false,
})
}
fn translate_call_indirect(
&mut self,
mut pos: FuncCursor,
_table_index: TableIndex,
_sig_index: SignatureIndex,
sig_ref: ir::SigRef,
callee: ir::Value,
call_args: &[ir::Value],
) -> ir::Inst {
// Pass the current function's vmctx parameter on to the callee.
let vmctx = pos.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("Missing vmctx parameter");
// The `callee` value is an index into a table of function pointers.
// Apparently, that table is stored at absolute address 0 in this dummy environment.
// TODO: Generate bounds checking code.
let ptr = self.native_pointer();
let callee_offset = if ptr == I32 {
pos.ins().imul_imm(callee, 4)
} else {
let ext = pos.ins().uextend(I64, callee);
pos.ins().imul_imm(ext, 4)
};
let mut mflags = ir::MemFlags::new();
mflags.set_notrap();
mflags.set_aligned();
let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0);
// Build a value list for the indirect call instruction containing the callee, call_args,
// and the vmctx parameter.
let mut args = ir::ValueList::default();
args.push(func_ptr, &mut pos.func.dfg.value_lists);
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
args.push(vmctx, &mut pos.func.dfg.value_lists);
pos.ins()
.CallIndirect(ir::Opcode::CallIndirect, VOID, sig_ref, args)
.0
}
fn translate_call(
&mut self,
mut pos: FuncCursor,
_callee_index: FunctionIndex,
callee: ir::FuncRef,
call_args: &[ir::Value],
) -> ir::Inst {
// Pass the current function's vmctx parameter on to the callee.
let vmctx = pos.func
.special_param(ir::ArgumentPurpose::VMContext)
.expect("Missing vmctx parameter");
// Build a value list for the call instruction containing the call_args and the vmctx
// parameter.
let mut args = ir::ValueList::default();
args.extend(call_args.iter().cloned(), &mut pos.func.dfg.value_lists);
args.push(vmctx, &mut pos.func.dfg.value_lists);
pos.ins().Call(ir::Opcode::Call, VOID, callee, args).0
}
fn translate_grow_memory(
&mut self,
mut pos: FuncCursor,
_index: MemoryIndex,
_heap: ir::Heap,
_val: ir::Value,
) -> ir::Value {
pos.ins().iconst(I32, -1)
}
fn translate_current_memory(
&mut self,
mut pos: FuncCursor,
_index: MemoryIndex,
_heap: ir::Heap,
) -> ir::Value {
pos.ins().iconst(I32, -1)
}
}
================================================
FILE: src/jit/mod.rs
================================================
use config::Config;
use cretonne_wasm::{translate_module, DummyEnvironment};
use std::fs::File;
use std::io::Read;
mod env;
pub fn server(config: Config) {
for app in config.applications.iter() {
println!("loading {}:{} at '{} {}'", app.file_path, app.function, app.method, app.url_path);
if let Ok(mut file) = File::open(&app.file_path) {
let mut data = Vec::new();
file.read_to_end(&mut data);
//let mut env = DummyEnvironment::default();
let mut env = env::Env::new();
translate_module(&data, &mut env).unwrap();
//let func_env = env.func_env();
//println!("bytecode:\n{:?}", env.func_bytecode_sizes);
}
}
}
================================================
FILE: src/main.rs
================================================
extern crate httparse;
extern crate mio;
extern crate parity_wasm;
extern crate rouille;
extern crate slab;
extern crate toml;
extern crate wasmi;
extern crate cretonne;
extern crate cretonne_wasm;
extern crate cretonne_module;
extern crate cretonne_simplejit;
#[macro_use]
extern crate serde_derive;
use std::env::args;
mod async;
mod config;
mod interpreter;
mod sync;
mod jit;
fn main() {
let args: Vec<_> = args().collect();
if args.len() != 2 {
println!("Usage: {} <config_file>", args[0]);
return;
}
if let Some(config) = config::load(&args[1]) {
async::server(config);
} else {
println!("invalid configuration");
}
}
================================================
FILE: src/sync/host.rs
================================================
//! from https://github.com/paritytech/wasmi/blob/master/src/tests/host.rs
use slab::Slab;
use std::collections::HashMap;
use std::io::{Read, Write};
use std::iter::repeat;
use std::net::TcpStream;
use std::str;
use std::cmp;
use std::rc::Rc;
use std::cell::RefCell;
use wasmi::memory_units::Pages;
use wasmi::*;
use interpreter::Host;
#[derive(Debug, Clone, PartialEq)]
struct HostErrorWithCode {
error_code: u32,
}
impl ::std::fmt::Display for HostErrorWithCode {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
write!(f, "{}", self.error_code)
}
}
impl HostError for HostErrorWithCode {}
#[derive(Clone)]
pub struct PreparedResponse {
pub status_code: Option<u16>,
pub headers: Vec<(String, String)>,
pub body: Option<Vec<u8>>,
}
impl PreparedResponse {
pub fn new() -> PreparedResponse {
PreparedResponse {
status_code: None,
headers: Vec::new(),
body: None,
}
}
}
pub struct State {
memory: Option<MemoryRef>,
instance: Option<ModuleRef>,
pub prepared_response: PreparedResponse,
connections: Slab<TcpStream>,
pub db: HashMap<String, String>,
}
impl State {
pub fn new() -> State {
State {
memory: Some(MemoryInstance::alloc(Pages(3), Some(Pages(10))).unwrap()),
instance: None,
prepared_response: PreparedResponse::new(),
connections: Slab::with_capacity(100),
db: HashMap::new(),
}
}
}
pub struct SyncHost {
pub inner: Rc<RefCell<State>>,
}
impl Host for SyncHost {
type State = State;
fn build(s: Rc<RefCell<Self::State>>) -> Self {
SyncHost { inner: s }
}
}
/// log(ptr: *mut u8, size: u64)
///
/// Returns value at the given address in memory. This function
/// requires attached memory.
const LOG_INDEX: usize = 0;
const RESPONSE_SET_STATUS_LINE: usize = 1;
const RESPONSE_SET_HEADER: usize = 2;
const RESPONSE_SET_BODY: usize = 3;
const TCP_CONNECT: usize = 4;
const TCP_READ: usize = 5;
const TCP_WRITE: usize = 6;
const DB_GET: usize = 7;
impl Externals for SyncHost {
fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Option<RuntimeValue>, Trap> {
match index {
LOG_INDEX => {
let ptr: u32 = args.nth(0);
let sz: u64 = args.nth(1);
let v = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
println!("log({} bytes): {}", v.len(), str::from_utf8(&v).unwrap());
Ok(None)
}
RESPONSE_SET_STATUS_LINE => {
let status: u32 = args.nth(0);
let ptr: u32 = args.nth(1);
let sz: u64 = args.nth(2);
let _reason = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
self.inner.borrow_mut().prepared_response.status_code = Some(status as u16);
Ok(None)
}
RESPONSE_SET_HEADER => {
let ptr1: u32 = args.nth(0);
let sz1: u64 = args.nth(1);
let ptr2: u32 = args.nth(2);
let sz2: u64 = args.nth(3);
let header_name = {
self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr1, sz1 as usize)
.unwrap()
};
let header_value = {
self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr2, sz2 as usize)
.unwrap()
};
self.inner.borrow_mut().prepared_response.headers.push((
String::from_utf8(header_name).unwrap(),
String::from_utf8(header_value).unwrap(),
));
Ok(None)
}
RESPONSE_SET_BODY => {
let ptr: u32 = args.nth(0);
let sz: u64 = args.nth(1);
let body = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
self.inner.borrow_mut().prepared_response.body = Some(body);
Ok(None)
}
TCP_CONNECT => {
let ptr: u32 = args.nth(0);
let sz: u64 = args.nth(1);
let v = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
let address = String::from_utf8(v).unwrap();
if let Ok(socket) = TcpStream::connect(&address) {
if let Ok(fd) = self.inner.borrow_mut().connections.insert(socket) {
Ok(Some(RuntimeValue::I32(fd as i32)))
} else {
Ok(Some(RuntimeValue::I32(-2)))
}
} else {
Ok(Some(RuntimeValue::I32(-1)))
}
}
TCP_READ => {
let fd: i32 = args.nth(0);
let ptr: u32 = args.nth(1);
let sz: u64 = args.nth(2);
let mut v = Vec::with_capacity(sz as usize);
v.extend(repeat(0).take(sz as usize));
let mut state = self.inner.borrow_mut();
if let Ok(sz) = state.connections[fd as usize].read(&mut v) {
state.memory.as_ref().map(|m| m.set(ptr, &v[..sz]));
Ok(Some(RuntimeValue::I64(sz as i64)))
} else {
Ok(Some(RuntimeValue::I64(-1)))
}
}
TCP_WRITE => {
let fd: i32 = args.nth(0);
let ptr: u32 = args.nth(1);
let sz: u64 = args.nth(2);
let buf = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(ptr, sz as usize)
.unwrap();
if let Ok(sz) = self.inner.borrow_mut().connections[fd as usize].write(&buf) {
Ok(Some(RuntimeValue::I64(sz as i64)))
} else {
Ok(Some(RuntimeValue::I64(-1)))
}
}
DB_GET => {
let key_ptr: u32 = args.nth(0);
let key_sz: u64 = args.nth(1);
let value_ptr: u32 = args.nth(2);
let value_sz: u64 = args.nth(3);
let v = self
.inner
.borrow()
.memory
.as_ref()
.expect("Function 'inc_mem' expects attached memory")
.get(key_ptr, key_sz as usize)
.unwrap();
let key = String::from_utf8(v).unwrap();
println!("requested value for key {}", key);
match self.inner.borrow().db.get(&key) {
None => Ok(Some(RuntimeValue::I64(-1))),
Some(value) => {
let to_write = cmp::min(value.len(), value_sz as usize);
self
.inner
.borrow()
.memory
.as_ref()
.map(|m| m.set(value_ptr, (&value[..to_write]).as_bytes()));
Ok(Some(RuntimeValue::I64(value.len() as i64)))
}
}
}
_ => panic!("env doesn't provide function at index {}", index),
}
}
}
impl State {
fn check_signature(&self, index: usize, signature: &Signature) -> bool {
let (params, ret_ty): (&[ValueType], Option<ValueType>) = match index {
LOG_INDEX => (&[ValueType::I32, ValueType::I64], None),
RESPONSE_SET_STATUS_LINE => (&[ValueType::I32, ValueType::I32, ValueType::I64], None),
RESPONSE_SET_HEADER => (
&[
ValueType::I32,
ValueType::I64,
ValueType::I32,
ValueType::I64,
],
None,
),
RESPONSE_SET_BODY => (&[ValueType::I32, ValueType::I64], None),
TCP_CONNECT => (&[ValueType::I32, ValueType::I64], Some(ValueType::I32)),
TCP_READ => (
&[ValueType::I32, ValueType::I32, ValueType::I64],
Some(ValueType::I64),
),
TCP_WRITE => (
&[ValueType::I32, ValueType::I32, ValueType::I64],
Some(ValueType::I64),
),
DB_GET => (
&[
ValueType::I32,
ValueType::I64,
ValueType::I32,
ValueType::I64,
],
Some(ValueType::I64),
),
_ => return false,
};
signature.params() == params && signature.return_type() == ret_ty
}
}
impl ModuleImportResolver for State {
fn resolve_func(&self, field_name: &str, signature: &Signature) -> Result<FuncRef, Error> {
let index = match field_name {
"log" => LOG_INDEX,
"response_set_status_line" => RESPONSE_SET_STATUS_LINE,
"response_set_header" => RESPONSE_SET_HEADER,
"response_set_body" => RESPONSE_SET_BODY,
"tcp_connect" => TCP_CONNECT,
"tcp_read" => TCP_READ,
"tcp_write" => TCP_WRITE,
"db_get" => DB_GET,
_ => {
return Err(Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
};
if !self.check_signature(index, signature) {
return Err(Error::Instantiation(format!(
"Export `{}` doesnt match expected type {:?}",
field_name, signature
)));
}
Ok(FuncInstance::alloc_host(signature.clone(), index))
}
fn resolve_memory(&self, _field_name: &str, _memory_type: &MemoryDescriptor) -> Result<MemoryRef, Error> {
let Pages(initial1) = self.memory.as_ref().map(|m| m.initial()).unwrap();
let initial2 = _memory_type.initial() as usize;
//println!("requested {} pages", initial2);
if initial2 > initial1 {
self.memory.as_ref().map(|_m| {
//println!("grow res: {:?}", m.grow(Pages(initial2 - initial1)).unwrap());
});
}
let Pages(_initial) = self.memory.as_ref().map(|m| m.current_size()).unwrap();
//println!("current number of pages: {}", initial);
//println!("resolving memory at name: {}", field_name);
let res = self.memory.as_ref().unwrap().clone();
Ok(res)
}
}
================================================
FILE: src/sync/mod.rs
================================================
use rouille;
use wasmi::{Error, ExternVal, ImportsBuilder, ModuleInstance};
use std::rc::Rc;
use std::cell::RefCell;
use config::{ApplicationState, Config};
use interpreter::WasmInstance;
mod host;
pub fn server(config: Config) {
let state = ApplicationState::new(&config);
rouille::start_server(&config.listen_address, move |request| {
if let Some((func_name, module, ref opt_env)) = state.route(request.method(), &request.url()) {
let mut env = host::State::new();
if let Some(h) = opt_env {
env.db.extend(
h.iter()
.map(|(ref k, ref v)| (k.to_string(), v.to_string())),
);
}
let main = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
.expect("Failed to instantiate module")
.assert_no_start();
let mut response = env.prepared_response.clone();
if let Some(ExternVal::Func(func_ref)) = main.export_by_name(func_name) {
let mut instance: WasmInstance<host::State, host::SyncHost> = WasmInstance::new(Rc::new(RefCell::new(env)), &func_ref, &[]);
let res = instance.resume().map_err(|t| Error::Trap(t));
println!("invocation result: {:?}", res);
response = instance.state.borrow().prepared_response.clone();
} else {
panic!("handle error here");
};
if let host::PreparedResponse {
status_code: Some(status),
headers,
body: Some(body),
} = response
{
rouille::Response {
status_code: status,
headers: Vec::new(),
data: rouille::ResponseBody::from_data(body),
upgrade: None,
}
} else {
rouille::Response::text("wasm failed").with_status_code(500)
}
} else {
rouille::Response::empty_404()
}
});
}
/*
pub fn start(file: &str) {
let module = load_module(file, "handle");
let mut env = host::SyncHost::new();
let main = ModuleInstance::new(&module, &ImportsBuilder::new().with_resolver("env", &env))
.expect("Failed to instantiate module")
.assert_no_start();
println!(
"Result: {:?}",
main.invoke_export("handle", &[], &mut env)
);
}
*/
gitextract_dnc2u41d/
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── rustfmt.toml
├── samples/
│ ├── config.toml
│ ├── testbackend/
│ │ ├── Cargo.toml
│ │ ├── build.sh
│ │ ├── config.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── testbackend.wasm
│ ├── testfunc/
│ │ ├── Cargo.toml
│ │ ├── build.sh
│ │ ├── config.toml
│ │ └── src/
│ │ └── lib.rs
│ └── testfunc.wasm
├── serverless-api/
│ ├── Cargo.toml
│ ├── src/
│ │ └── lib.rs
│ └── target/
│ ├── .rustc_info.json
│ └── debug/
│ ├── .cargo-lock
│ ├── .fingerprint/
│ │ ├── serverless-api-58a0200541263288/
│ │ │ ├── dep-lib-serverless_api-58a0200541263288
│ │ │ ├── lib-serverless_api-58a0200541263288
│ │ │ └── lib-serverless_api-58a0200541263288.json
│ │ └── serverless-api-81a9cad5d43bde63/
│ │ ├── dep-lib-serverless_api-81a9cad5d43bde63
│ │ ├── lib-serverless_api-81a9cad5d43bde63
│ │ └── lib-serverless_api-81a9cad5d43bde63.json
│ ├── deps/
│ │ ├── libserverless_api-58a0200541263288.rlib
│ │ ├── libserverless_api-81a9cad5d43bde63.rmeta
│ │ ├── serverless_api-58a0200541263288.d
│ │ └── serverless_api-81a9cad5d43bde63.d
│ ├── incremental/
│ │ └── serverless_api-101jg2fs4zen3/
│ │ └── s-f15by5smxq-136cfpy-1gc3dqcx796yx/
│ │ ├── 16u6js6g0l3k1ic6.bc.z
│ │ ├── 16u6js6g0l3k1ic6.o
│ │ ├── 181cuta0v63atwcm.bc.z
│ │ ├── 181cuta0v63atwcm.o
│ │ ├── 1im38lueib99jsk0.bc.z
│ │ ├── 1im38lueib99jsk0.o
│ │ ├── 1mvmz58owquyropc.bc.z
│ │ ├── 1mvmz58owquyropc.o
│ │ ├── 1w44dretk843l467.bc.z
│ │ ├── 1w44dretk843l467.o
│ │ ├── 1y16o1qfye96o7m0.bc.z
│ │ ├── 1y16o1qfye96o7m0.o
│ │ ├── 23tqyymcb18u96mb.bc.z
│ │ ├── 23tqyymcb18u96mb.o
│ │ ├── 2jqywn86b2gsqohu.bc.z
│ │ ├── 2jqywn86b2gsqohu.o
│ │ ├── 2lyh15q6cjwzy18c.bc.z
│ │ ├── 2lyh15q6cjwzy18c.o
│ │ ├── 2q5257pdh5222n7q.bc.z
│ │ ├── 2q5257pdh5222n7q.o
│ │ ├── 3ayaeypdcro9d6yk.bc.z
│ │ ├── 3ayaeypdcro9d6yk.o
│ │ ├── 3wq0rk2lqn1zrv77.bc.z
│ │ ├── 3wq0rk2lqn1zrv77.o
│ │ ├── 3wta9ctgdrpkmlpr.bc.z
│ │ ├── 3wta9ctgdrpkmlpr.o
│ │ ├── 40mv2bo8bxf7ur21.bc.z
│ │ ├── 40mv2bo8bxf7ur21.o
│ │ ├── 43v6g0y2xsxoggnt.bc.z
│ │ ├── 43v6g0y2xsxoggnt.o
│ │ ├── 48721dc4k5qxei0u.bc.z
│ │ ├── 48721dc4k5qxei0u.o
│ │ ├── 49a7n47po4ttqjl7.bc.z
│ │ ├── 49a7n47po4ttqjl7.o
│ │ ├── 49lx1q7cxvpykyv0.bc.z
│ │ ├── 49lx1q7cxvpykyv0.o
│ │ ├── 4ezmh1vbs95c5ack.bc.z
│ │ ├── 4ezmh1vbs95c5ack.o
│ │ ├── 4i7tkntav6hyd03k.bc.z
│ │ ├── 4i7tkntav6hyd03k.o
│ │ ├── 4xq48u46a1pwiqn7.bc.z
│ │ ├── 4xq48u46a1pwiqn7.o
│ │ ├── 4yh8x2b62dcih00t.bc.z
│ │ ├── 4yh8x2b62dcih00t.o
│ │ ├── 4ypvbwho0bu5tnww.bc.z
│ │ ├── 4ypvbwho0bu5tnww.o
│ │ ├── 56dly8q07ws8ucdq.bc.z
│ │ ├── 56dly8q07ws8ucdq.o
│ │ ├── 8xzrsc1ux72v29j.bc.z
│ │ ├── 8xzrsc1ux72v29j.o
│ │ ├── 98g0d9x8aw3akpe.bc.z
│ │ ├── 98g0d9x8aw3akpe.o
│ │ ├── 9elsx31vb4it187.bc.z
│ │ ├── 9elsx31vb4it187.o
│ │ ├── c6lbtaiefvx3wya.bc.z
│ │ ├── c6lbtaiefvx3wya.o
│ │ ├── mz7vgmcf23rofcc.bc.z
│ │ ├── mz7vgmcf23rofcc.o
│ │ ├── y08g5q2x813c4wx.bc.z
│ │ ├── y08g5q2x813c4wx.o
│ │ ├── z9ox7biyn1otfln.bc.z
│ │ └── z9ox7biyn1otfln.o
│ ├── libserverless_api.d
│ ├── libserverless_api.rlib
│ └── libserverless_api.rmeta
└── src/
├── async/
│ ├── host.rs
│ ├── mod.rs
│ └── session.rs
├── config.rs
├── interpreter.rs
├── jit/
│ ├── env.rs
│ └── mod.rs
├── main.rs
└── sync/
├── host.rs
└── mod.rs
SYMBOL INDEX (144 symbols across 13 files)
FILE: samples/testbackend/src/lib.rs
function handle (line 5) | pub extern "C" fn handle() {
FILE: samples/testfunc/src/lib.rs
function hello (line 6) | pub extern "C" fn hello() {
function bonjour (line 17) | pub extern "C" fn bonjour() {
FILE: serverless-api/src/lib.rs
function log (line 5) | pub fn log(ptr: *const u8, size: u64);
function response_set_status_line (line 6) | pub fn response_set_status_line(status: u32, ptr: *const u8, size: u64);
function response_set_header (line 7) | pub fn response_set_header(name_ptr: *const u8, name_size: u64, value_pt...
function response_set_body (line 8) | pub fn response_set_body(ptr: *const u8, size: u64);
function tcp_connect (line 9) | pub fn tcp_connect(ptr: *const u8, size: u64) -> i32;
function tcp_read (line 10) | pub fn tcp_read(fd: i32, ptr: *mut u8, size: u64) -> i64;
function tcp_write (line 11) | pub fn tcp_write(fd: i32, ptr: *const u8, size: u64) -> i64;
function db_get (line 12) | pub fn db_get(key_ptr: *const u8, key_size: u64, value_ptr: *const u8, v...
function log (line 16) | pub fn log(s: &str) {
function get (line 24) | pub fn get(key: &str) -> Option<String> {
function set_status (line 60) | pub fn set_status(status: u16, reason: &str) {
function set_header (line 66) | pub fn set_header(name: &str, value: &str) {
function set_body (line 72) | pub fn set_body(body: &[u8]) {
type TcpStream (line 79) | pub struct TcpStream {
method connect (line 84) | pub fn connect(address: &str) -> Option<TcpStream> {
method write (line 93) | pub fn write(&mut self, data: &[u8]) -> Option<usize> {
method read (line 102) | pub fn read(&mut self, data: &mut [u8]) -> Option<usize> {
FILE: src/async/host.rs
type AsyncHostError (line 18) | pub enum AsyncHostError {
method fmt (line 25) | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt:...
type PreparedResponse (line 33) | pub struct PreparedResponse {
method new (line 41) | pub fn new() -> PreparedResponse {
type State (line 51) | pub struct State {
method new (line 60) | pub fn new() -> State {
method get_buf (line 71) | pub fn get_buf(&mut self, ptr: u32, size: usize) -> Option<Vec<u8>> {
method write_buf (line 77) | pub fn write_buf(&mut self, ptr: u32, data: &[u8]) {
method check_signature (line 295) | fn check_signature(&self, index: usize, signature: &Signature) -> bool {
type AsyncHost (line 83) | pub struct AsyncHost {
type State (line 88) | type State = State;
method build (line 90) | fn build(s: Rc<RefCell<Self::State>>) -> Self {
constant LOG_INDEX (line 99) | const LOG_INDEX: usize = 0;
constant RESPONSE_SET_STATUS_LINE (line 101) | const RESPONSE_SET_STATUS_LINE: usize = 1;
constant RESPONSE_SET_HEADER (line 102) | const RESPONSE_SET_HEADER: usize = 2;
constant RESPONSE_SET_BODY (line 103) | const RESPONSE_SET_BODY: usize = 3;
constant TCP_CONNECT (line 104) | const TCP_CONNECT: usize = 4;
constant TCP_READ (line 105) | const TCP_READ: usize = 5;
constant TCP_WRITE (line 106) | const TCP_WRITE: usize = 6;
constant DB_GET (line 107) | const DB_GET: usize = 7;
method invoke_index (line 110) | fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Op...
method resolve_func (line 335) | fn resolve_func(&self, field_name: &str, signature: &Signature) -> Resul...
method resolve_memory (line 363) | fn resolve_memory(&self, _field_name: &str, _memory_type: &MemoryDescrip...
type StateResolver (line 381) | pub struct StateResolver {
method resolve_func (line 386) | fn resolve_func(&self, field_name: &str, signature: &Signature) -> Resul...
method resolve_memory (line 414) | fn resolve_memory(&self, _field_name: &str, _memory_type: &MemoryDescrip...
FILE: src/async/mod.rs
constant SERVER (line 14) | const SERVER: Token = Token(0);
function server (line 16) | pub fn server(config: Config) {
FILE: src/async/session.rs
type ExecutionResult (line 19) | pub enum ExecutionResult {
type Stream (line 29) | pub struct Stream {
type Buf (line 36) | pub struct Buf {
type SessionState (line 43) | pub enum SessionState {
type Session (line 52) | pub struct Session {
method new (line 65) | pub fn new(config: Rc<RefCell<ApplicationState>>, stream: TcpStream, i...
method add_backend (line 95) | pub fn add_backend(&mut self, stream: TcpStream, index: usize) {
method resume (line 108) | pub fn resume(&mut self) -> ExecutionResult {
method create_instance (line 162) | pub fn create_instance(&mut self) -> ExecutionResult {
method process_events (line 208) | pub fn process_events(&mut self, token: usize, events: Ready) -> bool {
method execute (line 231) | pub fn execute(&mut self) -> ExecutionResult {
method front_readable (line 257) | fn front_readable(&mut self) -> ExecutionResult {
method process (line 290) | fn process(&mut self) -> ExecutionResult {
method front_writable (line 455) | fn front_writable(&mut self) -> ExecutionResult {
FILE: src/config.rs
type WasmApp (line 9) | pub struct WasmApp {
type Config (line 18) | pub struct Config {
function load (line 23) | pub fn load(file: &str) -> Option<Config> {
type ApplicationState (line 38) | pub struct ApplicationState {
method new (line 46) | pub fn new(config: &Config) -> ApplicationState {
method route (line 70) | pub fn route(&self, method: &str, url: &str) -> Option<(&str, &Module,...
FILE: src/interpreter.rs
constant DEFAULT_VALUE_STACK_LIMIT (line 10) | pub const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
constant DEFAULT_FRAME_STACK_LIMIT (line 11) | pub const DEFAULT_FRAME_STACK_LIMIT: usize = 16384;
type HostBuilder (line 13) | pub trait HostBuilder<'a, S> {
method build (line 14) | fn build(s: &'a mut S) -> Self;
type Host (line 17) | pub trait Host {
method build (line 20) | fn build(s: Rc<RefCell<Self::State>>) -> Self;
type WasmInstance (line 23) | pub struct WasmInstance<S, E: Externals + Host<State = S>> {
function new (line 30) | pub fn new(state: Rc<RefCell<S>>, func_ref: &FuncRef, args: &[RuntimeVal...
function resume (line 40) | pub fn resume(&mut self) -> Result<Option<RuntimeValue>, Trap> {
function add_function_result (line 48) | pub fn add_function_result(&mut self, return_value: RuntimeValue) {
function create_stack (line 58) | pub fn create_stack(func: &FuncRef, args: &[RuntimeValue]) -> VecDeque<F...
function my_run_interpreter_loop (line 73) | pub fn my_run_interpreter_loop<E>(
function load_module (line 143) | pub fn load_module(file: &str, func_name: &str) -> Module {
FILE: src/jit/env.rs
type Exportable (line 12) | pub struct Exportable<T> {
function new (line 21) | pub fn new(entity: T) -> Self {
type ModuleInfo (line 29) | pub struct ModuleInfo {
method new (line 42) | pub fn new() -> ModuleInfo {
type Env (line 57) | pub struct Env {
method new (line 63) | pub fn new() -> Env {
method flags (line 76) | fn flags(&self) -> &Flags {
method get_func_name (line 80) | fn get_func_name(&self, func_index: FunctionIndex) -> ExternalName {
method declare_signature (line 84) | fn declare_signature(&mut self, sig: &Signature) {
method get_signature (line 88) | fn get_signature(&self, sig_index: SignatureIndex) -> &Signature {
method declare_func_import (line 92) | fn declare_func_import(
method get_num_func_imports (line 111) | fn get_num_func_imports(&self) -> usize {
method declare_func_type (line 115) | fn declare_func_type(&mut self, sig_index: SignatureIndex) {
method get_func_type (line 119) | fn get_func_type(&self, func_index: FunctionIndex) -> SignatureIndex {
method declare_global (line 123) | fn declare_global(&mut self, global: Global) {
method get_global (line 127) | fn get_global(&self, global_index: GlobalIndex) -> &Global {
method declare_table (line 131) | fn declare_table(&mut self, table: Table) {
method declare_table_elements (line 135) | fn declare_table_elements(
method declare_memory (line 145) | fn declare_memory(&mut self, memory: Memory) {
method declare_data_initialization (line 151) | fn declare_data_initialization(
method declare_func_export (line 162) | fn declare_func_export(
method declare_table_export (line 173) | fn declare_table_export(
method declare_memory_export (line 178) | fn declare_memory_export(
method declare_global_export (line 183) | fn declare_global_export(
method declare_start_func (line 189) | fn declare_start_func(&mut self, index: FunctionIndex) {
method define_function_body (line 194) | fn define_function_body(
function get_func_name (line 71) | fn get_func_name(func_index: FunctionIndex) -> ir::ExternalName {
type FuncEnv (line 215) | pub struct FuncEnv<'env> {
function new (line 220) | pub fn new(mod_info: &'env ModuleInfo) -> Self {
function vmctx_sig (line 226) | fn vmctx_sig(&self, sigidx: SignatureIndex) -> ir::Signature {
method flags (line 237) | fn flags(&self) -> &settings::Flags {
method make_global (line 241) | fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -...
method make_heap (line 251) | fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) ->...
method make_indirect_sig (line 263) | fn make_indirect_sig(&mut self, func: &mut ir::Function, index: Signatur...
method make_direct_func (line 269) | fn make_direct_func(&mut self, func: &mut ir::Function, index: FunctionI...
method translate_call_indirect (line 282) | fn translate_call_indirect(
method translate_call (line 323) | fn translate_call(
method translate_grow_memory (line 344) | fn translate_grow_memory(
method translate_current_memory (line 354) | fn translate_current_memory(
FILE: src/jit/mod.rs
function server (line 9) | pub fn server(config: Config) {
FILE: src/main.rs
function main (line 24) | fn main() {
FILE: src/sync/host.rs
type HostErrorWithCode (line 17) | struct HostErrorWithCode {
method fmt (line 22) | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt:...
type PreparedResponse (line 30) | pub struct PreparedResponse {
method new (line 37) | pub fn new() -> PreparedResponse {
type State (line 46) | pub struct State {
method new (line 55) | pub fn new() -> State {
method check_signature (line 271) | fn check_signature(&self, index: usize, signature: &Signature) -> bool {
type SyncHost (line 66) | pub struct SyncHost {
type State (line 71) | type State = State;
method build (line 73) | fn build(s: Rc<RefCell<Self::State>>) -> Self {
constant LOG_INDEX (line 82) | const LOG_INDEX: usize = 0;
constant RESPONSE_SET_STATUS_LINE (line 84) | const RESPONSE_SET_STATUS_LINE: usize = 1;
constant RESPONSE_SET_HEADER (line 85) | const RESPONSE_SET_HEADER: usize = 2;
constant RESPONSE_SET_BODY (line 86) | const RESPONSE_SET_BODY: usize = 3;
constant TCP_CONNECT (line 87) | const TCP_CONNECT: usize = 4;
constant TCP_READ (line 88) | const TCP_READ: usize = 5;
constant TCP_WRITE (line 89) | const TCP_WRITE: usize = 6;
constant DB_GET (line 90) | const DB_GET: usize = 7;
method invoke_index (line 93) | fn invoke_index(&mut self, index: usize, args: RuntimeArgs) -> Result<Op...
method resolve_func (line 311) | fn resolve_func(&self, field_name: &str, signature: &Signature) -> Resul...
method resolve_memory (line 339) | fn resolve_memory(&self, _field_name: &str, _memory_type: &MemoryDescrip...
FILE: src/sync/mod.rs
function server (line 11) | pub fn server(config: Config) {
Condensed preview — 105 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (88K chars).
[
{
"path": ".gitignore",
"chars": 20,
"preview": "\n/target\n**/*.rs.bk\n"
},
{
"path": "Cargo.toml",
"chars": 536,
"preview": "[package]\nname = \"serverless-wasm\"\nversion = \"0.1.0\"\nlicense = \"MIT\"\nrepository = \"https://github.com/Geal/serverless-wa"
},
{
"path": "LICENSE",
"chars": 1060,
"preview": "Copyright (c) 2018 Geoffroy Couprie\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of thi"
},
{
"path": "README.md",
"chars": 4305,
"preview": "# Serverless Web Assembly framework\n\n {\n api::log(\"Hello world wit"
},
{
"path": "samples/testfunc/Cargo.toml",
"chars": 220,
"preview": "[package]\nname = \"testfunc\"\nversion = \"0.1.0\"\nauthors = [\"Geoffroy Couprie <geo.couprie@gmail.com>\"]\n\n[lib]\npath = \"src/"
},
{
"path": "samples/testfunc/build.sh",
"chars": 175,
"preview": "#!/bin/sh\nRUSTFLAGS=\"-Clink-args=--import-memory\" cargo +nightly build -v --target wasm32-unknown-unknown --release\ncp t"
},
{
"path": "samples/testfunc/config.toml",
"chars": 44,
"preview": "rustflags = [\"-Clink-args=--import-memory\"]\n"
},
{
"path": "samples/testfunc/src/lib.rs",
"chars": 663,
"preview": "use std::ptr;\nuse std::str;\nextern crate serverless_api as api;\n\n#[no_mangle]\npub extern \"C\" fn hello() {\n api::log(\"He"
},
{
"path": "serverless-api/Cargo.toml",
"chars": 123,
"preview": "[package]\nname = \"serverless-api\"\nversion = \"0.1.0\"\nauthors = [\"Geoffroy Couprie <geo.couprie@gmail.com>\"]\n\n[dependencie"
},
{
"path": "serverless-api/src/lib.rs",
"chars": 2689,
"preview": "use std::str;\n\nmod sys {\n extern {\n pub fn log(ptr: *const u8, size: u64);\n pub fn response_set_status_line(statu"
},
{
"path": "serverless-api/target/.rustc_info.json",
"chars": 1673,
"preview": "{\"rustc_fingerprint\":10175240907518990213,\"outputs\":{\"1617349019360157463\":[\"___\\nlib___.rlib\\nlib___.dylib\\nlib___.dyli"
},
{
"path": "serverless-api/target/debug/.cargo-lock",
"chars": 0,
"preview": ""
},
{
"path": "serverless-api/target/debug/.fingerprint/serverless-api-58a0200541263288/lib-serverless_api-58a0200541263288",
"chars": 16,
"preview": "68e06764be0d3fe0"
},
{
"path": "serverless-api/target/debug/.fingerprint/serverless-api-58a0200541263288/lib-serverless_api-58a0200541263288.json",
"chars": 320,
"preview": "{\"rustc\":2983502972076547984,\"features\":\"[]\",\"target\":12616003758275913516,\"profile\":16965165444893579269,\"path\":1087270"
},
{
"path": "serverless-api/target/debug/.fingerprint/serverless-api-81a9cad5d43bde63/lib-serverless_api-81a9cad5d43bde63",
"chars": 16,
"preview": "d109624b85f6a4ab"
},
{
"path": "serverless-api/target/debug/.fingerprint/serverless-api-81a9cad5d43bde63/lib-serverless_api-81a9cad5d43bde63.json",
"chars": 319,
"preview": "{\"rustc\":2983502972076547984,\"features\":\"[]\",\"target\":12616003758275913516,\"profile\":4101608828254088483,\"path\":10872709"
},
{
"path": "serverless-api/target/debug/deps/serverless_api-58a0200541263288.d",
"chars": 270,
"preview": "/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/deps/libserverless_api-58a0200541263288.rlib: "
},
{
"path": "serverless-api/target/debug/deps/serverless_api-81a9cad5d43bde63.d",
"chars": 268,
"preview": "/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/deps/serverless_api-81a9cad5d43bde63.rmeta: sr"
},
{
"path": "serverless-api/target/debug/libserverless_api.d",
"chars": 170,
"preview": "/Users/geal/dev/rust/projects/serverless-wasm/serverless-api/target/debug/libserverless_api.rlib: /Users/geal/dev/rust/p"
},
{
"path": "src/async/host.rs",
"chars": 12003,
"preview": "//! from https://github.com/paritytech/wasmi/blob/master/src/tests/host.rs\n\nuse slab::Slab;\nuse std::collections::HashMa"
},
{
"path": "src/async/mod.rs",
"chars": 3639,
"preview": "use config::{ApplicationState, Config};\n\nuse mio::*;\nuse mio::net::{TcpListener, TcpStream};\nuse mio::unix::UnixReady;\nu"
},
{
"path": "src/async/session.rs",
"chars": 16304,
"preview": "use mio::unix::UnixReady;\nuse mio::net::TcpStream;\nuse mio::{Poll, Ready};\nuse std::collections::HashMap;\nuse std::iter:"
},
{
"path": "src/config.rs",
"chars": 2134,
"preview": "use interpreter::load_module;\nuse std::collections::HashMap;\nuse std::fs::File;\nuse std::io::Read;\nuse toml;\nuse wasmi::"
},
{
"path": "src/interpreter.rs",
"chars": 8335,
"preview": "use parity_wasm;\nuse parity_wasm::elements::{External, FunctionType, Internal, Type, ValueType};\nuse std::collections::V"
},
{
"path": "src/jit/env.rs",
"chars": 10810,
"preview": "use cretonne_wasm::{\n ModuleEnvironment, GlobalIndex, MemoryIndex, TableIndex,\n FunctionIndex, Table, Memory, Global, "
},
{
"path": "src/jit/mod.rs",
"chars": 676,
"preview": "use config::Config;\n\nuse cretonne_wasm::{translate_module, DummyEnvironment};\nuse std::fs::File;\nuse std::io::Read;\n\nmod"
},
{
"path": "src/main.rs",
"chars": 657,
"preview": "extern crate httparse;\nextern crate mio;\nextern crate parity_wasm;\nextern crate rouille;\nextern crate slab;\nextern crate"
},
{
"path": "src/sync/host.rs",
"chars": 10011,
"preview": "//! from https://github.com/paritytech/wasmi/blob/master/src/tests/host.rs\n\nuse slab::Slab;\nuse std::collections::HashMa"
},
{
"path": "src/sync/mod.rs",
"chars": 2204,
"preview": "use rouille;\nuse wasmi::{Error, ExternVal, ImportsBuilder, ModuleInstance};\nuse std::rc::Rc;\nuse std::cell::RefCell;\n\nus"
}
]
// ... and 70 more files (download for full content)
About this extraction
This page contains the full source code of the Geal/serverless-wasm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 105 files (80.3 KB), approximately 23.2k tokens, and a symbol index with 144 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.