Skip to content

Commit 5ea92c8

Browse files
authored
Implement wasi.environ_{get,sizes_get}. (proxy-wasm#126)
Signed-off-by: Takeshi Yoneda <[email protected]>
1 parent d1a2a7d commit 5ea92c8

File tree

17 files changed

+373
-94
lines changed

17 files changed

+373
-94
lines changed

bazel/wasm.bzl

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ def _wasm_rust_transition_impl(settings, attr):
1919
"//command_line_option:platforms": "@rules_rust//rust/platform:wasm",
2020
}
2121

22+
def _wasi_rust_transition_impl(settings, attr):
23+
return {
24+
"//command_line_option:platforms": "@rules_rust//rust/platform:wasi",
25+
}
26+
2227
wasm_rust_transition = transition(
2328
implementation = _wasm_rust_transition_impl,
2429
inputs = [],
@@ -27,6 +32,14 @@ wasm_rust_transition = transition(
2732
],
2833
)
2934

35+
wasi_rust_transition = transition(
36+
implementation = _wasi_rust_transition_impl,
37+
inputs = [],
38+
outputs = [
39+
"//command_line_option:platforms",
40+
],
41+
)
42+
3043
def _wasm_binary_impl(ctx):
3144
out = ctx.actions.declare_file(ctx.label.name)
3245
ctx.actions.run(
@@ -49,7 +62,12 @@ wasm_rust_binary_rule = rule(
4962
attrs = _wasm_attrs(wasm_rust_transition),
5063
)
5164

52-
def wasm_rust_binary(name, tags = [], **kwargs):
65+
wasi_rust_binary_rule = rule(
66+
implementation = _wasm_binary_impl,
67+
attrs = _wasm_attrs(wasi_rust_transition),
68+
)
69+
70+
def wasm_rust_binary(name, tags = [], wasi = False, **kwargs):
5371
wasm_name = "_wasm_" + name.replace(".", "_")
5472
kwargs.setdefault("visibility", ["//visibility:public"])
5573

@@ -62,7 +80,11 @@ def wasm_rust_binary(name, tags = [], **kwargs):
6280
**kwargs
6381
)
6482

65-
wasm_rust_binary_rule(
83+
bin_rule = wasm_rust_binary_rule
84+
if wasi:
85+
bin_rule = wasi_rust_binary_rule
86+
87+
bin_rule(
6688
name = name,
6789
binary = ":" + wasm_name,
6890
tags = tags + ["manual"],

include/proxy-wasm/null_vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct NullVm : public WasmVm {
4343
bool setMemory(uint64_t pointer, uint64_t size, const void *data) override;
4444
bool setWord(uint64_t pointer, Word data) override;
4545
bool getWord(uint64_t pointer, Word *data) override;
46+
size_t getWordSize() override;
4647
std::string_view getCustomSection(std::string_view name) override;
4748
std::string_view getPrecompiledSectionName() override;
4849

include/proxy-wasm/wasm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
5353
public:
5454
WasmBase(std::unique_ptr<WasmVm> wasm_vm, std::string_view vm_id,
5555
std::string_view vm_configuration, std::string_view vm_key,
56+
std::unordered_map<std::string, std::string> envs,
5657
AllowedCapabilitiesMap allowed_capabilities);
5758
WasmBase(const std::shared_ptr<WasmHandleBase> &other, WasmVmFactory factory);
5859
virtual ~WasmBase();
@@ -136,6 +137,8 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
136137

137138
AbiVersion abiVersion() { return abi_version_; }
138139

140+
const std::unordered_map<std::string, std::string> &envs() { return envs_; }
141+
139142
// Called to raise the flag which indicates that the context should stop iteration regardless of
140143
// returned filter status from Proxy-Wasm extensions. For example, we ignore
141144
// FilterHeadersStatus::Continue after a local reponse is sent by the host.
@@ -190,6 +193,8 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
190193
std::unordered_map<uint32_t, ContextBase *> contexts_; // Contains all contexts.
191194
std::unordered_map<uint32_t, std::chrono::milliseconds> timer_period_; // per root_id.
192195
std::unique_ptr<ShutdownHandle> shutdown_handle_;
196+
std::unordered_map<std::string, std::string>
197+
envs_; // environment variables passed through wasi.environ_get
193198

194199
WasmCallVoid<0> _initialize_; /* Emscripten v1.39.17+ */
195200
WasmCallVoid<0> _start_; /* Emscripten v1.39.0+ */

include/proxy-wasm/wasm_vm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ class WasmVm {
243243
*/
244244
virtual bool setWord(uint64_t pointer, Word data) = 0;
245245

246+
/**
247+
* @return the Word size in this VM.
248+
*/
249+
virtual size_t getWordSize() = 0;
250+
246251
/**
247252
* Get the contents of the custom section with the given name or "" if it does not exist.
248253
* @param name the name of the custom section to get.

src/exports.cc

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -767,18 +767,46 @@ Word wasi_unstable_fd_fdstat_get(void *raw_context, Word fd, Word statOut) {
767767
}
768768

769769
// __wasi_errno_t __wasi_environ_get(char **environ, char *environ_buf);
770-
Word wasi_unstable_environ_get(void *, Word, Word) {
770+
Word wasi_unstable_environ_get(void *raw_context, Word environ_array_ptr, Word environ_buf) {
771+
auto context = WASM_CONTEXT(raw_context);
772+
auto word_size = context->wasmVm()->getWordSize();
773+
auto &envs = context->wasm()->envs();
774+
for (auto e : envs) {
775+
if (!context->wasmVm()->setWord(environ_array_ptr, environ_buf)) {
776+
return 21; // __WASI_EFAULT
777+
}
778+
779+
std::string data;
780+
data.reserve(e.first.size() + e.second.size() + 2);
781+
data.append(e.first);
782+
data.append("=");
783+
data.append(e.second);
784+
data.append("\x0");
785+
if (!context->wasmVm()->setMemory(environ_buf, data.size(), data.c_str())) {
786+
return 21; // __WASI_EFAULT
787+
}
788+
environ_buf = environ_buf.u64_ + data.size();
789+
environ_array_ptr = environ_array_ptr.u64_ + word_size;
790+
}
791+
771792
return 0; // __WASI_ESUCCESS
772793
}
773794

774795
// __wasi_errno_t __wasi_environ_sizes_get(size_t *environ_count, size_t
775796
// *environ_buf_size);
776797
Word wasi_unstable_environ_sizes_get(void *raw_context, Word count_ptr, Word buf_size_ptr) {
777798
auto context = WASM_CONTEXT(raw_context);
778-
if (!context->wasmVm()->setWord(count_ptr, Word(0))) {
799+
auto &envs = context->wasm()->envs();
800+
if (!context->wasmVm()->setWord(count_ptr, Word(envs.size()))) {
779801
return 21; // __WASI_EFAULT
780802
}
781-
if (!context->wasmVm()->setWord(buf_size_ptr, Word(0))) {
803+
804+
size_t size = 0;
805+
for (auto e : envs) {
806+
// len(key) + len(value) + 1('=') + 1(null terminator)
807+
size += e.first.size() + e.second.size() + 2;
808+
}
809+
if (!context->wasmVm()->setWord(buf_size_ptr, Word(size))) {
782810
return 21; // __WASI_EFAULT
783811
}
784812
return 0; // __WASI_ESUCCESS

src/null/null_vm.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ bool NullVm::getWord(uint64_t pointer, Word *data) {
102102
return true;
103103
}
104104

105+
size_t NullVm::getWordSize() { return sizeof(uint64_t); }
106+
105107
std::string_view NullVm::getCustomSection(std::string_view /* name */) {
106108
// Return nothing: there is no WASM file.
107109
return {};

src/v8/v8.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class V8 : public WasmVm {
6767
bool setMemory(uint64_t pointer, uint64_t size, const void *data) override;
6868
bool getWord(uint64_t pointer, Word *word) override;
6969
bool setWord(uint64_t pointer, Word word) override;
70+
size_t getWordSize() override { return sizeof(uint32_t); };
7071

7172
#define _REGISTER_HOST_FUNCTION(T) \
7273
void registerCallback(std::string_view module_name, std::string_view function_name, T, \

src/wasm.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ WasmBase::WasmBase(const std::shared_ptr<WasmHandleBase> &base_wasm_handle, Wasm
193193
: std::enable_shared_from_this<WasmBase>(*base_wasm_handle->wasm()),
194194
vm_id_(base_wasm_handle->wasm()->vm_id_), vm_key_(base_wasm_handle->wasm()->vm_key_),
195195
started_from_(base_wasm_handle->wasm()->wasm_vm()->cloneable()),
196+
envs_(base_wasm_handle->wasm()->envs()),
196197
allowed_capabilities_(base_wasm_handle->wasm()->allowed_capabilities_),
197198
base_wasm_handle_(base_wasm_handle) {
198199
if (started_from_ != Cloneable::NotCloneable) {
@@ -209,9 +210,10 @@ WasmBase::WasmBase(const std::shared_ptr<WasmHandleBase> &base_wasm_handle, Wasm
209210

210211
WasmBase::WasmBase(std::unique_ptr<WasmVm> wasm_vm, std::string_view vm_id,
211212
std::string_view vm_configuration, std::string_view vm_key,
213+
std::unordered_map<std::string, std::string> envs,
212214
AllowedCapabilitiesMap allowed_capabilities)
213215
: vm_id_(std::string(vm_id)), vm_key_(std::string(vm_key)), wasm_vm_(std::move(wasm_vm)),
214-
allowed_capabilities_(std::move(allowed_capabilities)),
216+
envs_(envs), allowed_capabilities_(std::move(allowed_capabilities)),
215217
vm_configuration_(std::string(vm_configuration)), vm_id_handle_(getVmIdHandle(vm_id)) {
216218
if (!wasm_vm_) {
217219
failed_ = FailState::UnableToCreateVM;

src/wasmtime/wasmtime.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class Wasmtime : public WasmVm {
6565
bool setMemory(uint64_t pointer, uint64_t size, const void *data) override;
6666
bool getWord(uint64_t pointer, Word *word) override;
6767
bool setWord(uint64_t pointer, Word word) override;
68+
size_t getWordSize() override { return sizeof(uint32_t); };
6869

6970
#define _REGISTER_HOST_FUNCTION(T) \
7071
void registerCallback(std::string_view module_name, std::string_view function_name, T, \

src/wavm/wavm.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ struct Wavm : public WasmVm {
229229
bool setMemory(uint64_t pointer, uint64_t size, const void *data) override;
230230
bool getWord(uint64_t pointer, Word *data) override;
231231
bool setWord(uint64_t pointer, Word data) override;
232+
size_t getWordSize() override { return sizeof(uint32_t); };
232233
std::string_view getCustomSection(std::string_view name) override;
233234
std::string_view getPrecompiledSectionName() override;
234235
AbiVersion getAbiVersion() override;

test/BUILD

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@rules_cc//cc:defs.bzl", "cc_test")
1+
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
22
load("@proxy_wasm_cpp_host//bazel:variables.bzl", "COPTS", "LINKOPTS")
33

44
cc_test(
@@ -23,6 +23,23 @@ cc_test(
2323
],
2424
linkopts = LINKOPTS,
2525
deps = [
26+
":utility_lib",
27+
"//:lib",
28+
"@com_google_googletest//:gtest",
29+
"@com_google_googletest//:gtest_main",
30+
],
31+
)
32+
33+
cc_test(
34+
name = "exports_test",
35+
srcs = ["exports_test.cc"],
36+
copts = COPTS,
37+
data = [
38+
"//test/test_data:env.wasm",
39+
],
40+
linkopts = LINKOPTS,
41+
deps = [
42+
":utility_lib",
2643
"//:lib",
2744
"@com_google_googletest//:gtest",
2845
"@com_google_googletest//:gtest_main",
@@ -72,3 +89,17 @@ cc_test(
7289
"@com_google_googletest//:gtest_main",
7390
],
7491
)
92+
93+
cc_library(
94+
name = "utility_lib",
95+
srcs = [
96+
"utility.cc",
97+
"utility.h",
98+
],
99+
hdrs = ["utility.h"],
100+
copts = COPTS,
101+
deps = [
102+
"//:lib",
103+
"@com_google_googletest//:gtest",
104+
],
105+
)

test/exports_test.cc

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "gtest/gtest.h"
16+
17+
#include <fstream>
18+
#include <iostream>
19+
#include <memory>
20+
#include <sstream>
21+
#include <string>
22+
#include <vector>
23+
24+
#include "include/proxy-wasm/context.h"
25+
#include "include/proxy-wasm/exports.h"
26+
#include "include/proxy-wasm/wasm.h"
27+
28+
#include "test/utility.h"
29+
30+
namespace proxy_wasm {
31+
namespace {
32+
33+
auto test_values = testing::ValuesIn(getRuntimes());
34+
35+
INSTANTIATE_TEST_SUITE_P(Runtimes, TestVM, test_values);
36+
37+
class TestContext : public ContextBase {
38+
public:
39+
TestContext(WasmBase *base) : ContextBase(base){};
40+
WasmResult log(uint32_t, std::string_view msg) override {
41+
log_ += std::string(msg) + "\n";
42+
return WasmResult::Ok;
43+
}
44+
std::string &log_msg() { return log_; }
45+
46+
private:
47+
std::string log_;
48+
};
49+
50+
TEST_P(TestVM, Environment) {
51+
std::unordered_map<std::string, std::string> envs = {{"KEY1", "VALUE1"}, {"KEY2", "VALUE2"}};
52+
initialize("env.wasm");
53+
54+
auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", envs, {});
55+
ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, false));
56+
57+
TestContext context(&wasm_base);
58+
current_context_ = &context;
59+
60+
wasm_base.registerCallbacks();
61+
62+
ASSERT_TRUE(wasm_base.wasm_vm()->link(""));
63+
64+
WasmCallVoid<0> run;
65+
wasm_base.wasm_vm()->getFunction("run", &run);
66+
67+
run(current_context_);
68+
69+
auto msg = context.log_msg();
70+
EXPECT_NE(std::string::npos, msg.find("KEY1: VALUE1")) << msg;
71+
EXPECT_NE(std::string::npos, msg.find("KEY2: VALUE2")) << msg;
72+
}
73+
74+
TEST_P(TestVM, WithoutEnvironment) {
75+
initialize("env.wasm");
76+
auto wasm_base = WasmBase(std::move(vm_), "vm_id", "", "", {}, {});
77+
ASSERT_TRUE(wasm_base.wasm_vm()->load(source_, false));
78+
79+
TestContext context(&wasm_base);
80+
current_context_ = &context;
81+
82+
wasm_base.registerCallbacks();
83+
84+
ASSERT_TRUE(wasm_base.wasm_vm()->link(""));
85+
86+
WasmCallVoid<0> run;
87+
wasm_base.wasm_vm()->getFunction("run", &run);
88+
89+
run(current_context_);
90+
91+
EXPECT_EQ(context.log_msg(), "");
92+
}
93+
94+
} // namespace
95+
} // namespace proxy_wasm

0 commit comments

Comments
 (0)