diff --git a/integration_tests/test_data/any_method.notwasm b/integration_tests/test_data/any_method.notwasm new file mode 100644 index 0000000..40ff3d8 --- /dev/null +++ b/integration_tests/test_data/any_method.notwasm @@ -0,0 +1,6 @@ +function main(): i32 { + var a = any(@array_new()); + var _ = a?.push<(Array) -> i32, (str) -> i32>(undefined); + var r = 5; + return r; +} diff --git a/integration_tests/test_data/any_method.wasm.wat b/integration_tests/test_data/any_method.wasm.wat new file mode 100644 index 0000000..e69de29 diff --git a/libjankscripten/src/jankyscript/mod.rs b/libjankscripten/src/jankyscript/mod.rs index bf3193a..9b6ab66 100644 --- a/libjankscripten/src/jankyscript/mod.rs +++ b/libjankscripten/src/jankyscript/mod.rs @@ -7,7 +7,6 @@ pub mod constructors; pub mod from_js; mod fv; mod insert_returns; -mod methods; mod operators; mod operators_z3; mod pretty; diff --git a/libjankscripten/src/jankyscript/select_method_call.rs b/libjankscripten/src/jankyscript/select_method_call.rs index 7a74075..e090139 100644 --- a/libjankscripten/src/jankyscript/select_method_call.rs +++ b/libjankscripten/src/jankyscript/select_method_call.rs @@ -1,10 +1,10 @@ //! Turn (typed) method calls into relevant typed calls use super::constructors::*; -use super::methods::get_type_by_prefix; use super::syntax::*; use super::walk::*; use crate::rts_function::RTSFunction; +use crate::shared::methods::get_type_by_prefix; use crate::typ; /// Turn (typed) method calls into relevant typed calls diff --git a/libjankscripten/src/jankyscript/type_checking.rs b/libjankscripten/src/jankyscript/type_checking.rs index e9eed23..a1b4376 100644 --- a/libjankscripten/src/jankyscript/type_checking.rs +++ b/libjankscripten/src/jankyscript/type_checking.rs @@ -2,9 +2,9 @@ //! This occurs after type inference to ensure that type inference succeeded //! correctly. -use super::methods::get_type_by_prefix; use super::syntax::*; use crate::pos::Pos; +use crate::shared::methods::get_type_by_prefix; use crate::shared::std_lib::get_global_object; use im_rc::HashMap; use thiserror::Error; diff --git a/libjankscripten/src/jankyscript/typeinf.rs b/libjankscripten/src/jankyscript/typeinf.rs index 0e96984..e614489 100644 --- a/libjankscripten/src/jankyscript/typeinf.rs +++ b/libjankscripten/src/jankyscript/typeinf.rs @@ -17,13 +17,13 @@ //! list of types. use super::super::shared::coercions::Coercion; -use super::methods::METHODS_TABLE; use super::operators::OVERLOADS; use super::operators_z3::Z3Operators; use super::syntax::*; use super::typeinf_env::Env; use super::walk::{Loc, Visitor}; use crate::pos::Pos; +use crate::shared::methods::METHODS_TABLE; use crate::typ; use crate::z3ez::Z3EZ; use z3::ast::{self, Ast, Dynamic}; diff --git a/libjankscripten/src/notwasm/from_jankyscript.rs b/libjankscripten/src/notwasm/from_jankyscript.rs index 725e679..fadecf6 100644 --- a/libjankscripten/src/notwasm/from_jankyscript.rs +++ b/libjankscripten/src/notwasm/from_jankyscript.rs @@ -97,7 +97,7 @@ use super::super::rope::Rope; use super::constructors::*; use super::syntax::*; use crate::pos::Pos; -use crate::rts_function::RTSFunction; +use crate::shared::methods::METHODS_TABLE; use crate::shared::NameGen; use std::collections::HashMap; @@ -424,10 +424,19 @@ fn compile_expr<'a>(state: &'a mut S, expr: J::Expr, cxt: C<'a>) -> Rope { state, *obj, C::id(move |state, fun_id| { + // borrow checker + let args_len = args.len(); compile_exprs(state, args, move |state, arg_ids| { + let possible_typs = METHODS_TABLE + .get(&(method.as_str(), args_len)) + .unwrap() + .iter() + // never a function type + .map(|t| t.notwasm_typ(false)) + .collect(); cxt.recv_e( state, - Expr::AnyMethodCall(fun_id, method, arg_ids, todo!(), p), + Expr::AnyMethodCall(fun_id, method, arg_ids, possible_typs, p), ) }) }), diff --git a/libjankscripten/src/notwasm/lexer.l b/libjankscripten/src/notwasm/lexer.l index 3c8ea75..5481e64 100644 --- a/libjankscripten/src/notwasm/lexer.l +++ b/libjankscripten/src/notwasm/lexer.l @@ -66,4 +66,5 @@ while "while" \) ")" \[ "[" \] "]" +\? "?" . "UNMATCHED" diff --git a/libjankscripten/src/notwasm/parser.y b/libjankscripten/src/notwasm/parser.y index 477cd7b..396bb01 100644 --- a/libjankscripten/src/notwasm/parser.y +++ b/libjankscripten/src/notwasm/parser.y @@ -23,7 +23,7 @@ IdString -> String : ; Id -> Id : - 'ID' { Id::Named($lexer.span_str($1.unwrap().span()).to_string()) } + IdString { Id::Named($1) } | 'bogus' '(' 'env' ')' { Id::Bogus("env") } ; @@ -80,7 +80,7 @@ AtomSeq -> Vec : ; Atom -> Atom : - '$' Id '(' AtomSeq ')' { Atom::PrimApp($2, $4, pos($1)) } + '$' Id '(' AtomSeq ')' { Atom::PrimApp($2, $4, pos($1)) } | 'any' '(' Atom ')' { Atom::ToAny(ToAny::new($3), pos($1)) } | 'env' '.' U32 ':' Type { Atom::EnvGet($3, $5, pos($4)) } | 'rt' '(' Id ')' { Atom::GetPrimFunc($3, pos($1)) } @@ -138,6 +138,7 @@ Expr -> Expr : | 'newRef' '(' Atom ',' Type ')' { Expr::NewRef($3, $5, pos($1)) } | Id '!' '(' IdSeq ')' { Expr::ClosureCall($1, $4, pos($2)) } | Id '(' IdSeq ')' { Expr::Call($1, $3, pos($2)) } + | Id '?' '.' IdString '<' TypeSeq '>' '(' IdSeq ')' { Expr::AnyMethodCall($1, $4, $9, $6, pos($3)) } | AtomAdd { let p = $1.pos().clone(); Expr::Atom($1, p) } ; diff --git a/libjankscripten/src/notwasm/rt_bindings.rs b/libjankscripten/src/notwasm/rt_bindings.rs index c18a023..183042d 100644 --- a/libjankscripten/src/notwasm/rt_bindings.rs +++ b/libjankscripten/src/notwasm/rt_bindings.rs @@ -1,6 +1,7 @@ -use super::constructors::*; use super::syntax::Type; +use super::{constructors::*, syntax::FnType}; use crate::rts_function::{RTSFunction, RTSFunctionImpl}; +use crate::shared::methods::METHODS_TABLE; use std::collections::HashMap; use strum::IntoEnumIterator; use Type::*; @@ -38,21 +39,30 @@ pub fn get_rt_bindings() -> BindMap { &mono, vec![I32, Bool, a_clos.clone()], ); + let mut insert_rts_fn = |rts: &RTSFunction| { + if let RTSFunctionImpl::Rust(name) = rts.name() { + // Automatically generate the name and notwasm type + m.insert(name.into(), rts.janky_typ().notwasm_typ(false)); + } + }; // Step 2: automatically insert runtime functions from RTSFunction. for rts in RTSFunction::iter() { match rts { RTSFunction::Todo(..) | RTSFunction::Import(..) | RTSFunction::Method(..) => (), - _ => { - if let RTSFunctionImpl::Rust(name) = rts.name() { - // Automatically generate the name and notwasm type - m.insert(name.into(), rts.janky_typ().notwasm_typ(false)); - } - } + _ => insert_rts_fn(&rts), } // NotWasm runtime functions do not need to be added because // NotWasm already knows about them. } + // Step 3: RTSFunction::Method is infinitely large, import only the ones + // created by METHODS_TABLE + for ((name, _), typs) in METHODS_TABLE.iter() { + for typ in typs { + let rts = RTSFunction::Method(name.to_string(), typ.clone()); + insert_rts_fn(&rts); + } + } map } diff --git a/libjankscripten/src/notwasm/translation.rs b/libjankscripten/src/notwasm/translation.rs index 2cef15c..98d1cf1 100644 --- a/libjankscripten/src/notwasm/translation.rs +++ b/libjankscripten/src/notwasm/translation.rs @@ -1,6 +1,10 @@ //! translate NotWasm to wasm, using the rust runtime whenever possible //! //! preconditions: [super::compile] +//! +//! Much of this module relies on constants duplicated in the runtime, of +//! course. Check the constants at the top of the file, and any comments that refer +//! to other definitions use super::super::rts_function::*; use super::rt_bindings::get_rt_bindings; @@ -21,6 +25,9 @@ const ANY_SIZE: u32 = 8; const TAG_SIZE: u32 = 4; const LENGTH_SIZE: u32 = 4; const FN_OBJ_SIZE: u32 = 4; +// Check runtime::any_value::test::abi_any_discriminants_stable. For now, (my version of) rust seems to have stable and sensible discriminants for our any representation, which is defined by rust. Then we USE these assumptions in translation for: +// Expr::AnyMethodCall +// Expr::AnyLength (TODO) type FuncTypeMap = HashMap<(Vec, Option), u32>; @@ -709,35 +716,42 @@ impl<'a> Translate<'a> { _ => panic!("expected Func ID ({})", f), }; } + // This is using assumptions from the runtime. See + // runtime::any_value::test::abi_any_discriminants_stable N::Expr::AnyMethodCall(obj, method, args, typs, s) => { - todo!(); - // Unconditional exit 8 (acting 5) - self.out.push(Block(BlockType::Value(ValueType::I64))); - // Error 7 (acting 4) - self.out.push(Block(BlockType::Value(ValueType::I64))); - // Skip null 6 - // Skip undefined 5 - // TODO(closure) 4 - // Ptr 3 - self.out.push(Block(BlockType::Value(ValueType::I64))); - // Bool 2 - self.out.push(Block(BlockType::Value(ValueType::I64))); - // F64 1 - self.out.push(Block(BlockType::Value(ValueType::I64))); - // I32 0 - self.out.push(Block(BlockType::Value(ValueType::I64))); - // Get our object and look at the descriminant + // Does this get the discriminant?? Idk! self.get_id(obj); self.out.push(I32Const(0xf000)); self.out.push(I32And); self.out.push(I32Const(3 * 8)); self.out.push(I32ShrU); - // See runtime::any_value::tests::abi_any_discriminants_stable - BrTable(Box::new(BrTableData { - table: Box::new([0, 1, 2, 3, 4, 5, 6]), - default: 7, - })); - todo!() + self.to_any(&N::Type::I32); + self.rt_call("dbg_log"); + //// Unconditional exit 8 (acting 5) + //self.out.push(Block(BlockType::Value(ValueType::I64))); + //// Error 7 (acting 4) + //self.out.push(Block(BlockType::Value(ValueType::I64))); + //// Skip null 6 + //// Skip undefined 5 + //// TODO(closure) 4 + //// Ptr 3 + //self.out.push(Block(BlockType::Value(ValueType::I64))); + //// Bool 2 + //self.out.push(Block(BlockType::Value(ValueType::I64))); + //// F64 1 + //self.out.push(Block(BlockType::Value(ValueType::I64))); + //// I32 0 + //self.out.push(Block(BlockType::Value(ValueType::I64))); + //// Get our object and look at the discriminant + //self.get_id(obj); + //self.out.push(I32Const(0xf000)); + //self.out.push(I32And); + //self.out.push(I32Const(3 * 8)); + //self.out.push(I32ShrU); + //BrTable(Box::new(BrTableData { + // table: Box::new([0, 1, 2, 3, 4, 5, 6]), + // default: 7, + //})); } N::Expr::ClosureCall(f, args, s) => { match self.id_env.get(f).cloned() { diff --git a/libjankscripten/src/jankyscript/methods.rs b/libjankscripten/src/shared/methods.rs similarity index 100% rename from libjankscripten/src/jankyscript/methods.rs rename to libjankscripten/src/shared/methods.rs diff --git a/libjankscripten/src/shared/mod.rs b/libjankscripten/src/shared/mod.rs index eb71af6..e53e38a 100644 --- a/libjankscripten/src/shared/mod.rs +++ b/libjankscripten/src/shared/mod.rs @@ -1,5 +1,6 @@ pub mod coercions; mod id; +pub mod methods; pub mod std_lib; mod types; diff --git a/runtime/src/wasm32.rs b/runtime/src/wasm32.rs index 83539d0..028c7ff 100644 --- a/runtime/src/wasm32.rs +++ b/runtime/src/wasm32.rs @@ -112,6 +112,17 @@ pub fn log_any_raw(_this: AnyValue, any: AnyValue) -> AnyValue { AnyEnum::I32(42).into() } +/// *This cannot be called by notwasm*. However it is extremely useful for +/// debugging / notwasm compilation because it doesn't require extra arguments +/// dbg_log returns its input so as long as the top of the stack is an any, it +/// can be inserted *anywhere* +#[no_mangle] +pub fn dbg_log(any: AnyValue) -> AnyValue { + let real: AnyEnum = *any; + log!("{:?}", real); + any +} + pub fn heap() -> &'static Heap { unsafe { &HEAP }.as_ref().unwrap() } diff --git a/stdlib.notwasm b/stdlib.notwasm index 1ab10be..b007863 100644 --- a/stdlib.notwasm +++ b/stdlib.notwasm @@ -64,6 +64,7 @@ import math_max : (env, any, any, any) -> any; // __JNKS import heap_dump : (env, any) -> any; import log_any_raw : (any, any) -> any; +//import dbg_log : (any) -> any; import janky_primitive_plus : (any, any) -> any; import any_is_object : (any) -> bool;