Skip to content

Commit e40f9de

Browse files
committed
feat(extension) Replace instance.exports['sum'](1, 2) by instance.exports.sum(1, 2).
1 parent 21ef8a7 commit e40f9de

File tree

6 files changed

+69
-47
lines changed

6 files changed

+69
-47
lines changed

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ from wasmer import Instance, Value
6060

6161
wasm_bytes = open('simple.wasm', 'rb').read()
6262
instance = Instance(wasm_bytes)
63-
result = instance.exports['sum'](5, 37)
63+
result = instance.exports.sum(5, 37)
6464

6565
print(result) # 42!
6666
```
@@ -88,18 +88,17 @@ wasm_bytes = open('my_program.wasm', 'rb').read()
8888
instance = Instance(wasm_bytes)
8989

9090
# Call a function on it.
91-
result = instance.exports['sum'](1, 2)
91+
result = instance.exports.sum(1, 2)
9292

9393
print(result) # 3
9494
```
9595

96-
All exported functions are accessible in the `exports`
97-
dictionnary. Each value of this dictionnary is a function. Arguments
98-
of these functions are automatically casted to WebAssembly value. If
99-
one wants to explicitely pass a value of a particular type, it is
100-
possible to use the `Value` class,
101-
e.g. `instance.exports['sum'](Value.i32(1), Value.i32(2))`. Note that
102-
for most usecases, this is not necessary.
96+
All exported functions are accessible on the `exports` getter.
97+
Arguments of these functions are automatically casted to WebAssembly
98+
values. If one wants to explicitely pass a value of a particular type,
99+
it is possible to use the `Value` class,
100+
e.g. `instance.exports.sum(Value.i32(1), Value.i32(2))`. Note that for
101+
most usecases, this is not necessary.
103102

104103
### The `Value` class
105104

@@ -169,7 +168,7 @@ wasm_bytes = open('my_program.wasm', 'rb').read()
169168
instance = Instance(wasm_bytes)
170169

171170
# Call a function that returns a pointer to a string for instance.
172-
pointer = instance.exports['return_string']()
171+
pointer = instance.exports.return_string()
173172

174173
# Get the memory view, with the offset set to `pointer` (default is 0).
175174
memory = instance.uint8_memory_view(pointer)

examples/memory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
wasm_bytes = open(__dir__ + '/memory.wasm', 'rb').read()
77
instance = Instance(wasm_bytes)
8-
pointer = instance.exports['return_hello']()
8+
pointer = instance.exports.return_hello()
99

1010
memory = instance.uint8_memory_view(pointer)
1111
nth = 0;

examples/simple.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
wasm_bytes = open(__dir__ + '/simple.wasm', 'rb').read()
77
instance = Instance(wasm_bytes)
8-
result = instance.exports['sum'](5, 37)
8+
9+
result = instance.exports.sum(1, 2)
910

1011
print(result) # 42!

src/instance.rs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
33
use crate::{memory_view, value::Value};
44
use pyo3::{
5-
exceptions::RuntimeError,
5+
class::basic::PyObjectProtocol,
6+
exceptions::{LookupError, RuntimeError},
67
prelude::*,
7-
types::{PyAny, PyBytes, PyDict, PyFloat, PyLong, PyTuple},
8-
PyNativeType, PyTryFrom,
8+
types::{PyAny, PyBytes, PyFloat, PyLong, PyTuple},
9+
PyNativeType, PyTryFrom, ToPyObject,
910
};
1011
use std::rc::Rc;
1112
use wasmer_runtime::{self as runtime, imports, instantiate, Export, Memory, Value as WasmValue};
@@ -91,10 +92,37 @@ impl ExportedFunction {
9192
}
9293
}
9394

95+
#[pyclass]
96+
pub struct ExportedFunctions {
97+
instance: Rc<runtime::Instance>,
98+
functions: Vec<String>,
99+
}
100+
101+
#[pyproto]
102+
impl PyObjectProtocol for ExportedFunctions {
103+
fn __getattr__(&self, key: String) -> PyResult<ExportedFunction> {
104+
if self.functions.contains(&key) {
105+
Ok(ExportedFunction {
106+
function_name: key,
107+
instance: self.instance.clone(),
108+
})
109+
} else {
110+
Err(LookupError::py_err(format!(
111+
"Function `{}` does not exist.",
112+
key
113+
)))
114+
}
115+
}
116+
117+
fn __repr__(&self) -> PyResult<String> {
118+
Ok(format!("{:?}", self.functions))
119+
}
120+
}
121+
94122
#[pyclass]
95123
pub struct Instance {
96124
instance: Rc<runtime::Instance>,
97-
exports: PyObject,
125+
exports: Py<ExportedFunctions>,
98126
}
99127

100128
#[pymethods]
@@ -114,39 +142,33 @@ impl Instance {
114142
};
115143

116144
let py = object.py();
117-
let dict = PyDict::new(py);
145+
let mut exported_functions = Vec::new();
118146

119147
for (export_name, export) in instance.exports() {
120148
if let Export::Function { .. } = export {
121-
dict.set_item(
122-
export_name.clone(),
123-
Py::new(
124-
py,
125-
ExportedFunction {
126-
function_name: export_name,
127-
instance: instance.clone(),
128-
},
129-
)?,
130-
)?;
149+
exported_functions.push(export_name);
131150
}
132151
}
133152

134153
object.init({
135154
Self {
136-
instance,
137-
exports: dict.to_object(py),
155+
instance: instance.clone(),
156+
exports: Py::new(
157+
py,
158+
ExportedFunctions {
159+
instance: instance.clone(),
160+
functions: exported_functions,
161+
},
162+
)?,
138163
}
139164
});
140165

141166
Ok(())
142167
}
143168

144169
#[getter]
145-
fn exports(&self) -> PyResult<&PyDict> {
146-
let gil = Python::acquire_gil();
147-
let py = gil.python();
148-
149-
Ok(self.exports.cast_as::<PyDict>(py)?)
170+
fn exports(&self) -> PyResult<&Py<ExportedFunctions>> {
171+
Ok(&self.exports)
150172
}
151173

152174
#[args(offset = 0)]

tests/instance.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,66 +25,66 @@ def test_failed_to_instantiate(self):
2525
)
2626

2727
def test_function_does_not_exist(self):
28-
with self.assertRaises(KeyError) as context_manager:
29-
Instance(TEST_BYTES).exports['foo']
28+
with self.assertRaises(LookupError) as context_manager:
29+
Instance(TEST_BYTES).exports.foo
3030

3131
exception = context_manager.exception
3232
self.assertEqual(
3333
str(exception),
34-
"'foo'"
34+
"Function `foo` does not exist."
3535
)
3636

3737
def test_basic_sum(self):
3838
self.assertEqual(
39-
Instance(TEST_BYTES).exports['sum'](1, 2),
39+
Instance(TEST_BYTES).exports.sum(1, 2),
4040
3
4141
)
4242

4343
def test_call_arity_0(self):
4444
self.assertEqual(
45-
Instance(TEST_BYTES).exports['arity_0'](),
45+
Instance(TEST_BYTES).exports.arity_0(),
4646
42
4747
)
4848

4949
def test_call_i32_i32(self):
5050
self.assertEqual(
51-
Instance(TEST_BYTES).exports['i32_i32'](7),
51+
Instance(TEST_BYTES).exports.i32_i32(7),
5252
7
5353
)
5454

5555
def test_call_i64_i64(self):
5656
self.assertEqual(
57-
Instance(TEST_BYTES).exports['i64_i64'](7),
57+
Instance(TEST_BYTES).exports.i64_i64(7),
5858
7
5959
)
6060

6161
def test_call_f32_f32(self):
6262
self.assertEqual(
63-
Instance(TEST_BYTES).exports['f32_f32'](7.),
63+
Instance(TEST_BYTES).exports.f32_f32(7.),
6464
7.
6565
)
6666

6767
def test_call_f64_f64(self):
6868
self.assertEqual(
69-
Instance(TEST_BYTES).exports['f64_f64'](7.),
69+
Instance(TEST_BYTES).exports.f64_f64(7.),
7070
7.
7171
)
7272

7373
def test_call_i32_i64_f32_f64_f64(self):
7474
self.assertEqual(
75-
round(Instance(TEST_BYTES).exports['i32_i64_f32_f64_f64'](1, 2, 3.4, 5.6), 6),
75+
round(Instance(TEST_BYTES).exports.i32_i64_f32_f64_f64(1, 2, 3.4, 5.6), 6),
7676
1 + 2 + 3.4 + 5.6
7777
)
7878

7979
def test_call_bool_casted_to_i32(self):
8080
self.assertEqual(
81-
Instance(TEST_BYTES).exports['bool_casted_to_i32'](),
81+
Instance(TEST_BYTES).exports.bool_casted_to_i32(),
8282
1
8383
)
8484

8585
def test_call_string(self):
8686
self.assertEqual(
87-
Instance(TEST_BYTES).exports['string'](),
87+
Instance(TEST_BYTES).exports.string(),
8888
1048576
8989
)
9090

tests/memory_view.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def test_set_out_of_range(self):
6565

6666
def test_hello_world(self):
6767
instance = Instance(TEST_BYTES)
68-
pointer = instance.exports['string']()
68+
pointer = instance.exports.string()
6969
memory = instance.uint8_memory_view(pointer)
7070
nth = 0
7171
string = ''

0 commit comments

Comments
 (0)