Skip to content

Commit

Permalink
[WIP] Add first-class method calls; put in table
Browse files Browse the repository at this point in the history
  • Loading branch information
cosine authored and CosineP committed Apr 5, 2022
1 parent febdbbb commit 93d9d3e
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 84 deletions.
65 changes: 65 additions & 0 deletions libjankscripten/src/jankyscript/methods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::{shared::Type, typ};
use lazy_static::lazy_static;
use std::collections::HashMap;

macro_rules! entry {
($name:ident, $($args:tt -> $ret:ident),+) => {
(stringify!($name), vec![$(typ!(fun $args -> $ret)),+])
};
}

fn methods_table() -> HashMap<&'static str, Vec<Type>> {
[
entry!(slice, (string, int, int) -> string, (array, int, int) -> array),
entry!(at, (string, int) -> any, (string, int) -> string),
entry!(concat, (array, array) -> array, (string, string) -> string),
// Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
// Array.prototype[@@unscopables] // ??
// length is actually special and not really part of the prototype but
// for the purpose of type inference it should be here
//"length",
// Array.prototype[@@iterator]() // ??
// get Array[@@species] // ??
//"copyWithin",
//"entries",
//"every",
//"fill",
//"filter",
//"find",
//"findIndex",
//"flat",
//"flatMap",
//"forEach",
//"from", // Array.from, not prototype
//"includes",
//"indexOf",
//"isArray", // Array.isArray
//"join",
//"keys",
//"lastIndexOf",
//"map",
//"of", // Array.of
//"pop",
//"push",
//"reduce",
//"reduceRight",
//"reverse",
//"shift",
//"slice",
//"some",
//"sort",
//"splice",
//"toLocaleString",
//"toSource",
//"toString",
//"unshift",
//"values",
]
.iter()
.cloned()
.collect()
}

lazy_static! {
pub static ref METHODS_TABLE: HashMap<&'static str, Vec<Type>> = methods_table();
}
2 changes: 1 addition & 1 deletion libjankscripten/src/jankyscript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ pub mod constructors;
pub mod from_js;
mod fv;
mod insert_returns;
mod methods;
mod operators;
mod operators_z3;
mod pretty;
mod prototypes;
pub mod syntax;
mod type_checking;
mod typeinf;
Expand Down
57 changes: 0 additions & 57 deletions libjankscripten/src/jankyscript/prototypes.rs

This file was deleted.

1 change: 1 addition & 0 deletions libjankscripten/src/jankyscript/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub enum Expr {
Binary(BinaryOp, Box<Expr>, Box<Expr>, Pos),
Assign(Box<LValue>, Box<Expr>, Pos),
Call(Box<Expr>, Vec<Expr>, Pos),
MethodCall(Id, Vec<Expr>, Type, Pos),
PrimCall(RTSFunction, Vec<Expr>, Pos),
Func(Func, Pos),
Closure(Func, Vec<(Expr, Type)>, Pos),
Expand Down
17 changes: 4 additions & 13 deletions libjankscripten/src/jankyscript/type_checking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
//! This occurs after type inference to ensure that type inference succeeded
//! correctly.
use super::prototypes;
use super::syntax::*;
use crate::pos::Pos;
use crate::shared::std_lib::get_global_object;
Expand Down Expand Up @@ -357,6 +356,10 @@ fn type_check_expr(expr: &Expr, env: Env) -> TypeCheckingResult<Type> {
// type check this call
type_check_fun_call(fun_type, args, env, s.clone())
}
Expr::MethodCall(obj, args, typ, s) => {
let obj_type = lookup(&env, &obj, &s)?;
type_check_fun_call(typ.clone(), args, env, s.clone())
}
Expr::Coercion(coercion, e, s) => {
// type the expression. regardless of the coercion, the expression
// needs to be well-typed.
Expand Down Expand Up @@ -397,18 +400,6 @@ fn type_check_expr(expr: &Expr, env: Env) -> TypeCheckingResult<Type> {
Expr::Dot(obj, prop, s) => {
let obj_type = type_check_expr(obj, env)?;

// TODO(luna): , Date, String, ...
if !(prototypes::ARRAY_PROTOTYPE.contains(prop.name())
&& (obj_type == Type::Array || obj_type == Type::Any))
{
ensure(
"property lookup expects a DynObject",
Type::DynObject,
obj_type,
&s,
)?;
}

// we don't know anything about the type we're returning.
// even if this property doesn't exist on the given object,
// it'll return undefined (in non-strict mode).
Expand Down
16 changes: 3 additions & 13 deletions libjankscripten/src/jankyscript/typeinf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
use super::super::shared::coercions::Coercion;
use super::operators::OVERLOADS;
use super::operators_z3::Z3Operators;
use super::prototypes;
use super::syntax::*;
use super::typeinf_env::Env;
use super::walk::{Loc, Visitor};
Expand Down Expand Up @@ -365,21 +364,12 @@ impl<'a> Typeinf<'a> {
fn cgen_dot(&mut self, obj_e: &mut Expr, x: &mut Id, p: &mut Pos) -> ast::Bool<'a> {
let w = self.fresh_weight();
let (phi_1, t) = self.cgen_expr(obj_e);
let phi_array = if prototypes::ARRAY_PROTOTYPE.contains(x.name()) {
z3f!(self, (and (= (tid t) (typ array))))
} else {
// Since the field is part of no known prototype (we assume
// no platypus objects) (still TODO Date, String, etc),
// we know it's a DynObject, so can safely perform the
// coercion
let e = obj_e.take();
*obj_e = coerce(t.clone(), Type::DynObject, e, p.clone());
z3f!(self, false)
};
let e = obj_e.take();
*obj_e = coerce(t.clone(), Type::DynObject, e, p.clone());
let phi_2 = z3f!(self,
(or
(and (= (tid t) (typ dynobject)) (id w.clone()))
(and (id phi_array) (id w.clone()))
(id w.clone())
(and (= (tid t) (typ any)) (not (id &w)))));
phi_1 & phi_2
}
Expand Down
1 change: 1 addition & 0 deletions libjankscripten/src/javascript/desugar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub fn desugar(stmt: &mut Stmt, ng: &mut NameGen) {
// dep: desugar_function_applications, add_blocks
desugar_updates::desugar_updates(stmt, ng);
desugar_bracket_str::desugar_bracket_str(stmt);
resugar_method_call::resugar_method_call(stmt);
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions libjankscripten/src/javascript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod desugar_vardecls;
mod lift_vars;
mod normalize_std_lib_calls;
mod parser;
mod resugar_method_call;
pub mod syntax;
pub mod walk;

Expand Down
34 changes: 34 additions & 0 deletions libjankscripten/src/javascript/resugar_method_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use super::syntax::*;
use super::*;

struct ResugarMethodCall;

impl Visitor for ResugarMethodCall {
fn exit_expr(&mut self, expr: &mut Expr, _: &Loc) {
match expr {
Expr::Call(f, args, _) => match &mut **f {
Expr::Dot(obj, name, s) => {
let id = if let Expr::Id(obj_name, _) = &**obj {
obj_name.clone()
} else {
panic!("Desugar should have named method objects")
};
let name = if let Id::Named(field) = name {
std::mem::replace(field, Default::default())
} else {
panic!("Dot should't access special ids")
};
let args = std::mem::replace(args, vec![]);
*expr = Expr::MethodCall(id, name, args, s.clone());
}
_ => (),
},
_ => (),
}
}
}

pub fn resugar_method_call(program: &mut Stmt) {
let mut v = ResugarMethodCall;
program.walk(&mut v);
}
1 change: 1 addition & 0 deletions libjankscripten/src/javascript/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ pub enum Expr {
If(Box<Expr>, Box<Expr>, Box<Expr>, Pos),
Assign(AssignOp, Box<LValue>, Box<Expr>, Pos),
Call(Box<Expr>, Vec<Expr>, Pos),
MethodCall(Id, String, Vec<Expr>, Pos),
Func(Option<Id>, Vec<Id>, Box<Stmt>, Pos),
Seq(Vec<Expr>, Pos),
}
Expand Down
1 change: 1 addition & 0 deletions libjankscripten/src/notwasm/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ pub enum Expr {
/// right now, never constructed from jankyscript, only in tests
Call(Id, Vec<Id>, Pos),
ClosureCall(Id, Vec<Id>, Pos),
AnyMethodCall(Id, Vec<Id>, Vec<Type>, Pos),
PrimCall(RTSFunction, Vec<Atom>, Pos),
ObjectEmpty,
/// `ObjectSet(obj, field, value, _)` is `obj.field = value;`. The translator generates code
Expand Down
13 changes: 13 additions & 0 deletions libjankscripten/src/rts_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ use strum_macros::EnumIter;
#[derive(Debug, Clone, PartialEq, EnumIter, Eq, Hash)]
pub enum RTSFunction {
Todo(&'static str),
// Type-specialized methods. They are always implemented in rust, with a
// name given algorithmically based on the type and method name. The full type is
// also provided, because it's generated in the methods table
Method(&'static str, Type),
// unary ops
Typeof,
Delete,
Expand Down Expand Up @@ -52,6 +56,7 @@ impl RTSFunction {
use RTSFunctionImpl::*;
match self {
Todo(name) => todo!("unimplemented operator: {}", name),
Method(..) => Rust(self.to_string()),
Typeof => Rust("janky_typeof".into()),
Delete => Rust("janky_delete".into()),
Void => Rust("janky_void".into()),
Expand Down Expand Up @@ -101,6 +106,7 @@ impl RTSFunction {
use RTSFunction::*;
match self {
Todo(name) => todo!("unimplemented operator: {}", name),
Method(_, ty) => ty.clone(),
Typeof => Function(vec![Any], Box::new(String)),
// the second operand of InstanceOf is really "a function" but we don't have a type for that
Delete | InstanceOf => Function(vec![Any, Any], Box::new(Bool)),
Expand All @@ -127,6 +133,13 @@ impl std::fmt::Display for RTSFunction {
"{}",
match self {
Todo(s) => s,
Method(name, ty) => {
if let Type::Function(ts, _) = ty {
return write!(f, "{}_{}", ts[0], name);
} else {
panic!("non-function function type")
}
}
Typeof => "typeof",
Delete => "delete",
Void => "void",
Expand Down

0 comments on commit 93d9d3e

Please sign in to comment.