Skip to content

Commit

Permalink
V2: use objects to define imports
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Aug 3, 2023
1 parent 5b9e8ed commit a139ffc
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 46 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ var_dump($instance->get_some());
php examples/global.php
```

Currently, this package supports `global` access and function `exports`.
Check out the [examples](examples) folder for more examples.

## Stubs

```
cargo php stubs
make stubs
```

## Roadmap
Expand Down
32 changes: 17 additions & 15 deletions examples/imports.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
<?php

$instance = Wasm\InstanceBuilder::fromWat(
<<<'EOWAT'
(module
(import "env" "global" (global $global (mut i32)))
(func (export "read_g") (result i32)
global.get $global)
(func (export "write_g") (param i32)
local.get 0
global.set $global))
EOWAT
)->withImports([
'env' => [
'global' => 33
]
])->build();
$instanceBuilder = Wasm\InstanceBuilder::fromWat(
<<<'EOWAT'
(module
(import "env" "global" (global $global (mut i32)))
(func (export "read_g") (result i32)
global.get $global)
(func (export "write_g") (param i32)
local.get 0
global.set $global))
EOWAT
);

$imports = Wasm\Imports::create();
$imports->define('env', 'global', \Wasm\Type\Global::mutable(32));
$instanceBuilder->import($imports);

$instance = $instanceBuilder->build();

var_dump($instance->read_g());
19 changes: 17 additions & 2 deletions ext-wasm.stubs.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,29 @@ public function __call(string $method, array $attributes): mixed {}

public function __get(string $accessor): mixed {}

public function __set(string $accessor, mixed $value): mixed {}
public function __set(string $accessor, mixed $value): void {}
}

class InstanceBuilder {
public static function fromWat(string $wat): \Wasm\InstanceBuilder {}

public function withImports(array $imports): \Wasm\InstanceBuilder {}
public function import(array $imports): void {}

public function build(): \Wasm\WasmInstance {}
}

class Imports {
public static function create(): self {}

public static function define(string $namespace, string $variable, \Wasm\Type\Global $value): void {}
}

}

namespace Wasm\Type {
class Global {
public static function mutable(mixed $value): self {}

public static function immutable(mixed $value): self {}
}
}
109 changes: 82 additions & 27 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
mod types;

use std::collections::HashMap;

use ext_php_rs::prelude::*;
use ext_php_rs::zend::ModuleEntry;
use ext_php_rs::types::{ZendClassObject};
use ext_php_rs::*;
use crate::types::Value;
use crate::types::value::Value;

#[php_class(name="Wasm\\WasmInstance")]
pub struct WasmInstance {
Expand Down Expand Up @@ -62,13 +61,11 @@ impl WasmInstance {
}
}

type ImportsType = HashMap<String, HashMap<String, Value>>;

#[php_class(name="Wasm\\InstanceBuilder")]
#[derive(Default)]
pub struct InstanceBuilder {
pub wat: Box<String>,
pub imports : Box<ImportsType>,
pub imports : Option<Box<WasmImports>>,
}

#[php_impl]
Expand All @@ -80,13 +77,12 @@ impl InstanceBuilder {
}.into()
}

pub fn with_imports(
// TODO : Change return type to be fluent (see #28)
pub fn import<'a>(
#[this] this: &mut ZendClassObject<InstanceBuilder>,
imports: ImportsType
) -> &mut ZendClassObject<InstanceBuilder> {
this.imports = imports.into();

this
imports: &mut ZendClassObject<WasmImports>
) -> () {
this.imports = Some((*imports).clone().into());
}

pub fn build(&mut self) -> PhpResult<WasmInstance> {
Expand All @@ -96,22 +92,8 @@ impl InstanceBuilder {

// Build imports
let mut import_object = wasmer::Imports::new();
for (namespace_name, namespace_dict) in (&*self.imports).into_iter() {
let namespace_name = namespace_name.to_string();
let namespace_dict_wasmer: Vec<(String, wasmer::Extern)> = namespace_dict
.into_iter()
.map(|(key, value)| (
key.clone(),
wasmer::Extern::Global(
wasmer::Global::new_mut(&mut store, value.clone().into()) // TODO : define mutability..
)
))
.collect();

import_object.register_namespace(
&namespace_name,
namespace_dict_wasmer
);
if self.imports.is_some() {
import_object = (self.imports.as_mut().unwrap()).into_wasmer_imports(&mut store)
}

let instance = wasmer::Instance::new(&mut store, &module, &import_object)
Expand All @@ -121,6 +103,79 @@ impl InstanceBuilder {
}
}


#[php_class(name="Wasm\\Imports")]
#[derive(Clone)]
pub struct WasmImports {
pub registry : HashMap<(String, String), WasmGlobal>,
}

#[php_impl]
impl WasmImports {
pub fn create() -> WasmImports {
WasmImports {
registry: HashMap::new().into()
}.into()
}

// TODO : Change return type to be fluent (see #28)
pub fn define(
//&mut self,
#[this] this: &mut ZendClassObject<WasmImports>,
namespace : String,
variable : String,
value : &mut ZendClassObject<WasmGlobal>,
) -> () {
this.registry.insert((namespace.clone(), variable.clone()), (*value).clone().into());
}
}

impl WasmImports {
pub fn into_wasmer_imports(&mut self, store : &mut wasmer::Store) -> wasmer::Imports {
let mut import_object = wasmer::Imports::new();

for ((namespace, name), value) in (&self.registry).into_iter() {
let converted = value.clone().into_wasmer_global(store);
import_object.define(&namespace, &name, converted);
}

import_object
}
}

#[php_class(name="Wasm\\Type\\Global")]
#[derive(Clone)]
pub struct WasmGlobal {
pub value : Box<Value>,
pub mutable : bool,
}

#[php_impl]
impl WasmGlobal {
pub fn mutable(value: Value) -> WasmGlobal {
WasmGlobal {value: value.into(), mutable: true}.into()
}

pub fn immutable(value: Value) -> WasmGlobal {
WasmGlobal {value: value.into(), mutable: false}.into()
}
}

impl WasmGlobal {
pub fn into_wasmer_global(&mut self, store : &mut wasmer::Store) -> wasmer::Global {
match self.mutable {
true => wasmer::Global::new_mut(store, (*self.value).into()),
false => wasmer::Global::new(store, (*self.value).into()),
}
}

pub fn into_wasmer_extern(&mut self, store : &mut wasmer::Store) -> wasmer::Extern {
wasmer::Extern::Global(
self.into_wasmer_global(store)
)
}
}

/// Used by the `phpinfo()` function and when you run `php -i`.
pub extern "C" fn php_module_info(_module: *mut ModuleEntry) {
info_table_start!();
Expand Down
1 change: 1 addition & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod value;
File renamed without changes.
85 changes: 85 additions & 0 deletions tests/ImportsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace Test;

use PHPUnit\Framework\TestCase;
use Wasm\InstanceBuilder;
use Wasm\Imports;
use Wasm\Type;

class ImportsTest extends TestCase
{
public function test_it_fails_on_missing_imports() {
$this->expectException(\Exception::class);
$this->createBuilderFromWat()->build();
}

public function test_it_fails_on_unkown_env() {
$imports = Imports::create();
$imports->define('unkown', 'global', Type\Global::mutable(32));

$builder = $this->createBuilderFromWat();
$builder->import($imports);

$this->expectException(\Exception::class);
$builder->build();
}

public function test_it_fails_on_unkown_import_key() {
$imports = Imports::create();
$imports->define('env', 'unkown', Type\Global::mutable(32));

$builder = $this->createBuilderFromWat();
$builder->import($imports);

$this->expectException(\Exception::class);
$builder->build();
}

public function test_it_fails_on_invalid_type() {
$imports = Imports::create();
$imports->define('env', 'global', Type\Global::mutable(1.1));

$builder = $this->createBuilderFromWat();
$builder->import($imports);

$this->expectException(\Exception::class);
$builder->build();
}

public function test_it_fails_on_invalid_mutability() {
$imports = Imports::create();
$imports->define('env', 'global', Type\Global::immutable(32));

$builder = $this->createBuilderFromWat();
$builder->import($imports);

$this->expectException(\Exception::class);
$builder->build();
}

public function test_it_imports_simple_globals() {
$imports = Imports::create();
$imports->define('env', 'global', Type\Global::mutable(32));

$builder = $this->createBuilderFromWat();
$builder->import($imports);

$instance = $builder->build();

self::assertSame(32, $instance->read_g());
}

private function createBuilderFromWat(?string $wat = null): InstanceBuilder
{
return InstanceBuilder::fromWat($wat ?? <<<'EOWAT'
(module
(import "env" "global" (global $global (mut i32)))
(func (export "read_g") (result i32)
global.get $global)
(func (export "write_g") (param i32)
local.get 0
global.set $global))
EOWAT);
}
}

0 comments on commit a139ffc

Please sign in to comment.