mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Genericize the wasm lib to not be specifically for the demo
This included changing RustObjectKind::RustFunction.function to be a `Box<Fn()>` instead of a `fn()` to support closures.
This commit is contained in:
22 files changed
+10523
-91
No files matched your search
+1
-1
@@ -4,7 +4,7 @@ version = "0.0.1"
|
||||
authors = ["Windel Bouwman", "Shing Lyu <shing.lyu@gmail.com>"]
|
||||
|
||||
[workspace]
|
||||
members = [".", "vm", "wasm", "parser"]
|
||||
members = [".", "vm", "wasm/lib", "parser"]
|
||||
|
||||
[dependencies]
|
||||
log="0.4.1"
|
||||
|
||||
+1
-1
@@ -559,7 +559,7 @@ impl Frame {
|
||||
bytecode::Instruction::LoadBuildClass => {
|
||||
let rustfunc = PyObject::new(
|
||||
PyObjectKind::RustFunction {
|
||||
function: builtins::builtin_build_class_,
|
||||
function: Box::new(builtins::builtin_build_class_),
|
||||
},
|
||||
vm.ctx.type_type(),
|
||||
);
|
||||
|
||||
+26
-7
@@ -452,9 +452,24 @@ impl PyContext {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_rustfunc(&self, function: RustPyFunc) -> PyObjectRef {
|
||||
pub fn new_rustfunc<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
||||
&self,
|
||||
function: F,
|
||||
) -> PyObjectRef {
|
||||
PyObject::new(
|
||||
PyObjectKind::RustFunction { function: function },
|
||||
PyObjectKind::RustFunction {
|
||||
function: Box::new(function),
|
||||
},
|
||||
self.function_type(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_rustfunc_from_box(
|
||||
&self,
|
||||
function: Box<(Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult)>,
|
||||
) -> PyObjectRef {
|
||||
PyObject::new(
|
||||
PyObjectKind::RustFunction { function },
|
||||
self.function_type(),
|
||||
)
|
||||
}
|
||||
@@ -463,7 +478,10 @@ impl PyContext {
|
||||
PyObject::new(PyObjectKind::Frame { frame: frame }, self.frame_type())
|
||||
}
|
||||
|
||||
pub fn new_property(&self, function: RustPyFunc) -> PyObjectRef {
|
||||
pub fn new_property<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
||||
&self,
|
||||
function: F,
|
||||
) -> PyObjectRef {
|
||||
let fget = self.new_rustfunc(function);
|
||||
let py_obj = PyObject::new(
|
||||
PyObjectKind::Instance {
|
||||
@@ -505,7 +523,10 @@ impl PyContext {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_member_descriptor(&self, function: RustPyFunc) -> PyObjectRef {
|
||||
pub fn new_member_descriptor<F: 'static + Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult>(
|
||||
&self,
|
||||
function: F,
|
||||
) -> PyObjectRef {
|
||||
let dict = self.new_dict();
|
||||
self.set_item(&dict, "function", self.new_rustfunc(function));
|
||||
self.new_instance(dict, self.member_descriptor_type())
|
||||
@@ -762,8 +783,6 @@ impl PyFuncArgs {
|
||||
}
|
||||
}
|
||||
|
||||
type RustPyFunc = fn(vm: &mut VirtualMachine, PyFuncArgs) -> PyResult;
|
||||
|
||||
/// Rather than determining the type of a python object, this enum is more
|
||||
/// a holder for the rust payload of a python object. It is more a carrier
|
||||
/// of rust data for a particular python object. Determine the python type
|
||||
@@ -840,7 +859,7 @@ pub enum PyObjectKind {
|
||||
dict: PyObjectRef,
|
||||
},
|
||||
RustFunction {
|
||||
function: RustPyFunc,
|
||||
function: Box<(Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult)>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -229,7 +229,7 @@ impl VirtualMachine {
|
||||
pub fn invoke(&mut self, func_ref: PyObjectRef, args: PyFuncArgs) -> PyResult {
|
||||
trace!("Invoke: {:?} {:?}", func_ref, args);
|
||||
match func_ref.borrow().kind {
|
||||
PyObjectKind::RustFunction { function } => function(self, args),
|
||||
PyObjectKind::RustFunction { ref function } => function(self, args),
|
||||
PyObjectKind::Function {
|
||||
ref code,
|
||||
ref scope,
|
||||
|
||||
+3
-7
@@ -1,7 +1,3 @@
|
||||
wasm-pack build --debug && \
|
||||
cd pkg && \
|
||||
npm link && \
|
||||
cd ../app && \
|
||||
npm install && \
|
||||
npm link rustpython_wasm && \
|
||||
node_modules/.bin/webpack-dev-server
|
||||
set -e
|
||||
(cd lib; wasm-pack build --debug)
|
||||
(cd demo; npm install && node_modules/.bin/webpack-dev-server)
|
||||
File renamed without changes.
File renamed without changes.
Generated
+6032
File diff suppressed because it is too large.
Load diff
File renamed without changes.
File renamed without changes.
@@ -34,6 +34,21 @@ while count < until:
|
||||
<h3>Standard Output</h3>
|
||||
<textarea id="console">Loading...</textarea>
|
||||
|
||||
<p>Here's some info regarding the <code>rp.eval_py()</code> function</p>
|
||||
<ul>
|
||||
<li>
|
||||
You can return variables from python and get them returned to
|
||||
JS, with the only requirement being that they're serializable
|
||||
with <code>json.dumps</code>.
|
||||
</li>
|
||||
<li>
|
||||
You can pass an object as the second argument to the function,
|
||||
and that will be available in python as the variable
|
||||
<code>js_vars</code>. Again, only values that can be serialized
|
||||
with <code>JSON.stringify()</code> will go through.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a href="https://github.com/RustPython/RustPython">
|
||||
<img
|
||||
style="position: absolute; top: 0; right: 0; border: 0;"
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as rp from '../../pkg';
|
||||
import * as rp from '../../lib/pkg';
|
||||
import CodeMirror from 'codemirror';
|
||||
import 'codemirror/mode/python/python';
|
||||
import 'codemirror/addon/comment/comment';
|
||||
@@ -14,6 +14,7 @@ const editor = CodeMirror.fromTextArea(document.getElementById('code'), {
|
||||
'Ctrl-/': 'toggleComment',
|
||||
'Cmd-/': 'toggleComment'
|
||||
},
|
||||
lineNumbers: true,
|
||||
mode: 'text/x-python',
|
||||
indentUnit: 4,
|
||||
autofocus: true
|
||||
@@ -29,7 +30,9 @@ function runCodeFromTextarea() {
|
||||
|
||||
const code = editor.getValue();
|
||||
try {
|
||||
rp.run_from_textbox(code);
|
||||
rp.eval_py(code, {
|
||||
stdout: '#console'
|
||||
});
|
||||
} catch (e) {
|
||||
errorElement.textContent = e;
|
||||
console.error(e);
|
||||
File renamed without changes.
File renamed without changes.
+4345
File diff suppressed because it is too large.
Load diff
File renamed without changes.
@@ -7,8 +7,8 @@ authors = ["Ryan Liddle <ryan@rmliddle.com>"]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
rustpython_parser = {path = "../parser"}
|
||||
rustpython_vm = {path = "../vm"}
|
||||
rustpython_parser = {path = "../../parser"}
|
||||
rustpython_vm = {path = "../../vm"}
|
||||
cfg-if = "0.1.2"
|
||||
wasm-bindgen = "0.2"
|
||||
js-sys = "0.3"
|
||||
@@ -5,12 +5,11 @@ extern crate rustpython_vm;
|
||||
extern crate wasm_bindgen;
|
||||
extern crate web_sys;
|
||||
|
||||
use js_sys::Reflect;
|
||||
use js_sys::{Array, Object, Reflect, TypeError};
|
||||
use rustpython_vm::compile;
|
||||
use rustpython_vm::pyobject::{self, PyObjectRef, PyResult};
|
||||
use rustpython_vm::pyobject::{self, PyFuncArgs, PyObjectRef, PyResult};
|
||||
use rustpython_vm::VirtualMachine;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::console;
|
||||
|
||||
fn py_str_err(vm: &mut VirtualMachine, py_err: &PyObjectRef) -> String {
|
||||
vm.to_pystr(&py_err)
|
||||
@@ -76,21 +75,84 @@ where
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn eval_py(source: &str, js_injections: Option<js_sys::Object>) -> Result<JsValue, JsValue> {
|
||||
pub fn eval_py(source: &str, options: Option<Object>) -> Result<JsValue, JsValue> {
|
||||
let options = options.unwrap_or_else(|| Object::new());
|
||||
let js_vars = {
|
||||
let prop = Reflect::get(&options, &"vars".into())?;
|
||||
let prop = Object::from(prop);
|
||||
if prop.is_undefined() {
|
||||
None
|
||||
} else if prop.is_object() {
|
||||
Some(prop)
|
||||
} else {
|
||||
return Err(TypeError::new("vars must be an object").into());
|
||||
}
|
||||
};
|
||||
let stdout = {
|
||||
let prop = Reflect::get(&options, &"stdout".into())?;
|
||||
if prop.is_undefined() {
|
||||
None
|
||||
} else {
|
||||
Some(prop)
|
||||
}
|
||||
};
|
||||
let string_stdout = {
|
||||
let prop = Reflect::get(&options, &"stdoutString".into())?;
|
||||
let prop = Object::from(prop);
|
||||
if prop.is_undefined() {
|
||||
true
|
||||
} else if let Some(prop) = prop.as_bool() {
|
||||
prop
|
||||
} else {
|
||||
return Err(TypeError::new("stdoutString must be a boolean").into());
|
||||
}
|
||||
};
|
||||
let mut vm = VirtualMachine::new();
|
||||
|
||||
let print_fn: Box<(Fn(&mut VirtualMachine, PyFuncArgs) -> PyResult)> = match stdout {
|
||||
Some(val) => {
|
||||
if let Some(selector) = val.as_string() {
|
||||
Box::new(
|
||||
move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult {
|
||||
wasm_builtins::builtin_print_html(vm, args, &selector)
|
||||
},
|
||||
)
|
||||
} else if val.is_function() {
|
||||
let func = js_sys::Function::from(val);
|
||||
Box::new(
|
||||
move |vm: &mut VirtualMachine, args: PyFuncArgs| -> PyResult {
|
||||
let arr = Array::new();
|
||||
for arg in args.args {
|
||||
let arg = if string_stdout {
|
||||
vm.to_pystr(&arg)?.into()
|
||||
} else {
|
||||
py_to_js(vm, arg)
|
||||
};
|
||||
arr.push(&arg);
|
||||
}
|
||||
func.apply(&JsValue::UNDEFINED, &arr)
|
||||
.map_err(|err| js_to_py(vm, err))?;
|
||||
Ok(vm.get_none())
|
||||
},
|
||||
)
|
||||
} else {
|
||||
return Err(TypeError::new("stdout must be a function or a css selector").into());
|
||||
}
|
||||
}
|
||||
None => Box::new(wasm_builtins::builtin_print_console),
|
||||
};
|
||||
|
||||
vm.ctx.set_attr(
|
||||
&vm.builtins,
|
||||
"print",
|
||||
vm.context()
|
||||
.new_rustfunc(wasm_builtins::builtin_print_console),
|
||||
vm.ctx.new_rustfunc_from_box(print_fn),
|
||||
);
|
||||
|
||||
let res = eval(&mut vm, source, |vm, vars| {
|
||||
let injections = vm.new_dict();
|
||||
|
||||
if let Some(js_injections) = js_injections.clone() {
|
||||
for pair in js_sys::try_iter(&js_sys::Object::entries(&js_injections))
|
||||
if let Some(js_vars) = js_vars.clone() {
|
||||
for pair in js_sys::try_iter(&Object::entries(&js_vars))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
{
|
||||
@@ -112,40 +174,3 @@ pub fn eval_py(source: &str, js_injections: Option<js_sys::Object>) -> Result<Js
|
||||
res.map(|value| py_to_js(&mut vm, value))
|
||||
.map_err(|err| py_str_err(&mut vm, &err).into())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run_from_textbox(source: &str) -> Result<JsValue, JsValue> {
|
||||
//add hash in here
|
||||
console::log_1(&"Running RustPython".into());
|
||||
console::log_1(&"Running code:".into());
|
||||
console::log_1(&source.to_string().into());
|
||||
|
||||
let mut vm = VirtualMachine::new();
|
||||
|
||||
// We are monkey-patching the builtin print to use console.log
|
||||
// TODO: monkey-patch sys.stdout instead, after print actually uses sys.stdout
|
||||
vm.ctx.set_attr(
|
||||
&vm.builtins,
|
||||
"print",
|
||||
vm.context().new_rustfunc(wasm_builtins::builtin_print_html),
|
||||
);
|
||||
|
||||
match eval(&mut vm, source, |_, _| {}) {
|
||||
Ok(value) => {
|
||||
console::log_1(&"Execution successful".into());
|
||||
match value.borrow().kind {
|
||||
pyobject::PyObjectKind::None => {}
|
||||
_ => {
|
||||
if let Ok(text) = vm.to_pystr(&value) {
|
||||
wasm_builtins::print_to_html(&text);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(JsValue::UNDEFINED)
|
||||
}
|
||||
Err(err) => {
|
||||
console::log_1(&"Execution failed".into());
|
||||
Err(py_str_err(&mut vm, &err).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,47 +8,48 @@ extern crate js_sys;
|
||||
extern crate wasm_bindgen;
|
||||
extern crate web_sys;
|
||||
|
||||
use crate::js_to_py;
|
||||
use js_sys::Array;
|
||||
use rustpython_vm::obj::objstr;
|
||||
use rustpython_vm::pyobject::{PyFuncArgs, PyResult};
|
||||
use rustpython_vm::VirtualMachine;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{console, window, HtmlTextAreaElement};
|
||||
|
||||
// The HTML id of the textarea element that act as our STDOUT
|
||||
const CONSOLE_ELEMENT_ID: &str = "console";
|
||||
|
||||
pub fn print_to_html(text: &str) {
|
||||
pub fn print_to_html(text: &str, selector: &str) -> Result<(), JsValue> {
|
||||
let document = window().unwrap().document().unwrap();
|
||||
let element = document
|
||||
.get_element_by_id(CONSOLE_ELEMENT_ID)
|
||||
.expect("Can't find the console textarea");
|
||||
let textarea = element.dyn_ref::<HtmlTextAreaElement>().unwrap();
|
||||
.query_selector(selector)?
|
||||
.ok_or_else(|| js_sys::TypeError::new("Couldn't get element"))?;
|
||||
let textarea = element
|
||||
.dyn_ref::<HtmlTextAreaElement>()
|
||||
.ok_or_else(|| js_sys::TypeError::new("Element must be a textarea"))?;
|
||||
let value = textarea.value();
|
||||
textarea.set_value(&format!("{}{}", value, text));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn builtin_print_html(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
pub fn builtin_print_html(vm: &mut VirtualMachine, args: PyFuncArgs, selector: &str) -> PyResult {
|
||||
let mut output = String::new();
|
||||
let mut first = true;
|
||||
for a in args.args {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
print_to_html(&" ")
|
||||
output.push_str(" ");
|
||||
}
|
||||
let v = vm.to_str(&a)?;
|
||||
let s = objstr::get_value(&v);
|
||||
print_to_html(&format!("{}\n", s))
|
||||
output.push_str(&vm.to_pystr(&a)?);
|
||||
output.push('\n');
|
||||
}
|
||||
print_to_html(&output, selector).map_err(|err| js_to_py(vm, err))?;
|
||||
Ok(vm.get_none())
|
||||
}
|
||||
|
||||
pub fn builtin_print_console(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
let arr = Array::new();
|
||||
for a in args.args {
|
||||
let v = vm.to_str(&a)?;
|
||||
let s = objstr::get_value(&v);
|
||||
arr.push(&s.into());
|
||||
for arg in args.args {
|
||||
arr.push(&vm.to_pystr(&arg)?.into());
|
||||
}
|
||||
console::log(&arr);
|
||||
Ok(vm.get_none())
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../target
|
||||
+4
-8
@@ -1,8 +1,4 @@
|
||||
wasm-pack build && \
|
||||
cd pkg && \
|
||||
npm link && \
|
||||
cd ../app && \
|
||||
npm install && \
|
||||
npm link rustpython_wasm && \
|
||||
node_modules/.bin/webpack --mode production && \
|
||||
echo "Output saved to app/dist"
|
||||
set -e
|
||||
(cd lib; wasm-pack build)
|
||||
(cd demo; npm install && node_modules/.bin/webpack --mode production)
|
||||
echo "Output saved to demo/dist"
|
||||
@@ -1 +0,0 @@
|
||||
../target/
|
||||
Reference in new issue
Block a user