Skip to content

Commit

Permalink
Add js_eval/2 for wasm32
Browse files Browse the repository at this point in the history
  • Loading branch information
aarroyoc committed Nov 20, 2023
1 parent f32b035 commit 1fdefdb
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 13 deletions.
12 changes: 1 addition & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ tokio = { version = "1.28.2", features = [

[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
console_error_panic_hook = "0.1"
console_log = "1.0"
wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4"
serde-wasm-bindgen = "0.5"
web-sys = { version = "0.3", features = ["Document", "Window", "Element"] }
js-sys = "0.3"

[target.'cfg(target_os = "wasi")'.dependencies]
ring-wasi = { version = "0.16.25" }
Expand Down
4 changes: 4 additions & 0 deletions build/instructions_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,8 @@ enum SystemClauseType {
ForeignCall,
#[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))]
DefineForeignStruct,
#[strum_discriminants(strum(props(Arity = "2", Name = "$js_eval")))]
JsEval,
#[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))]
PredicateDefined,
#[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))]
Expand Down Expand Up @@ -1774,6 +1776,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallLoadForeignLib |
&Instruction::CallForeignCall |
&Instruction::CallDefineForeignStruct |
&Instruction::CallJsEval |
&Instruction::CallPredicateDefined |
&Instruction::CallStripModule |
&Instruction::CallCurrentTime |
Expand Down Expand Up @@ -2008,6 +2011,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteLoadForeignLib |
&Instruction::ExecuteForeignCall |
&Instruction::ExecuteDefineForeignStruct |
&Instruction::ExecuteJsEval |
&Instruction::ExecutePredicateDefined |
&Instruction::ExecuteStripModule |
&Instruction::ExecuteCurrentTime |
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn eval_code(s: &str) -> String {
use machine::mock_wam::*;
use web_sys::console;

console_error_panic_hook::set_once();

let mut wam = Machine::with_test_streams();
let bytes = wam.test_load_string(s);
Expand Down
29 changes: 29 additions & 0 deletions src/lib/wasm.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/** Predicates for the WebAssembly platform
This module contains predicates that are only available in
the WASM (WebAssembly) version of Scryer Prolog.
*/

:- module(wasm, [js_eval/2]).

:- use_module(library(error)).

%% js_eval(+JsCode, -Result).
%
% Executes a JavaScript snippet `JsCode` using the platform
% `eval` function. `Result` takes the return value of that code.
% Strings, booleans, numbers, null and undefined are directly mapped to Prolog.
% Arrays, objects, bigints, symbols and functions are not mapped.
% Instead, a `js_{type}` atom will be returned.
%
% Example (on a browser):
%
% ```
% ?- js_eval("prompt('What is your name?')", Name).
% % A prompt is showed, with a textbox.
% Name = "Whatever was written on the textbox".
% ```
js_eval(JsCode, Result) :-
must_be(chars, JsCode),
can_be(chars, Result),
'$js_eval'(JsCode, Result).
8 changes: 8 additions & 0 deletions src/machine/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4128,6 +4128,14 @@ impl Machine {
try_or_throw!(self.machine_st, self.define_foreign_struct());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallJsEval => {
try_or_throw!(self.machine_st, self.js_eval());
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteJsEval => {
try_or_throw!(self.machine_st, self.js_eval());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallCurrentTime => {
self.current_time();
step_or_fail!(self, self.machine_st.p += 1);
Expand Down
63 changes: 63 additions & 0 deletions src/machine/system_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4879,6 +4879,69 @@ impl Machine {
Ok(())
}

#[cfg(not(target_arch = "wasm32"))]
#[inline(always)]
pub(crate) fn js_eval(&mut self) -> CallResult {
unimplemented!()
}

#[cfg(target_arch = "wasm32")]
#[inline(always)]
pub(crate) fn js_eval(&mut self) -> CallResult {

let code = self.deref_register(1);
let result_reg = self.deref_register(2);
if let Some(code) = self.machine_st.value_to_str_like(code) {
let result = match js_sys::eval(&code.as_str()) {
Ok(result) => self.unify_js_value(result, result_reg),
Err(result) => self.unify_js_value(result, result_reg),
};
return Ok(());
}
self.machine_st.fail = true;
Ok(())
}

fn unify_js_value(&mut self, result: wasm_bindgen::JsValue, result_reg: HeapCellValue) {
match result.as_bool() {
Some(result) => {
match result {
true => self.machine_st.unify_atom(atom!("true"), result_reg),
false => self.machine_st.unify_atom(atom!("false"), result_reg),
}
}
None => match result.as_f64() {
Some(result) => {
let n = float_alloc!(result, self.machine_st.arena);
self.machine_st.unify_f64(n, result_reg);
}
None => match result.as_string() {
Some(result) => {
let result = AtomTable::build_with(&self.machine_st.atom_tbl, &result);
self.machine_st.unify_complete_string(result, result_reg);
}
None => if result.is_null() {
self.machine_st.unify_atom(atom!("null"), result_reg);
} else if result.is_undefined() {
self.machine_st.unify_atom(atom!("undefined"), result_reg);
} else if result.is_symbol() {
self.machine_st.unify_atom(atom!("js_symbol"), result_reg);
} else if result.is_object() {
self.machine_st.unify_atom(atom!("js_object"), result_reg);
} else if result.is_array() {
self.machine_st.unify_atom(atom!("js_array"), result_reg);
} else if result.is_function() {
self.machine_st.unify_atom(atom!("js_function"), result_reg);
} else if result.is_bigint() {
self.machine_st.unify_atom(atom!("js_bigint"), result_reg);
} else {
self.machine_st.unify_atom(atom!("js_unknown_type"), result_reg);
}
}
}
}
}

#[inline(always)]
pub(crate) fn current_time(&mut self) {
let timestamp = self.systemtime_to_timestamp(SystemTime::now());
Expand Down

0 comments on commit 1fdefdb

Please sign in to comment.