diff --git a/Cargo.lock b/Cargo.lock index 818d0af89e..6193772c40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -760,6 +760,8 @@ dependencies = [ "num-bigint 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1043,6 +1045,42 @@ name = "ucd-util" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-normalization" version = "0.1.7" @@ -1363,6 +1401,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" +"checksum unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +"checksum unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" +"checksum unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +"checksum unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +"checksum unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index e57d421406..f2f8e5a6b4 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -14,4 +14,5 @@ log="0.4.1" regex="0.2.2" num-bigint = "0.2" num-traits = "0.2" - +unicode-xid = "0.1.0" +unic-emoji-char = "0.9.0" diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 113f7afb1a..defa5e2322 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1,12 +1,17 @@ //! This module takes care of lexing python source text. This means source //! code is translated into separate tokens. +extern crate unic_emoji_char; +extern crate unicode_xid; + pub use super::token::Tok; use num_bigint::BigInt; use num_traits::Num; use std::cmp::Ordering; use std::collections::HashMap; use std::str::FromStr; +use unic_emoji_char::is_emoji_presentation; +use unicode_xid::UnicodeXID; #[derive(Clone, Copy, PartialEq, Debug)] struct IndentationLevel { @@ -300,7 +305,7 @@ where } } - while self.is_char() { + while self.is_identifier_continuation() { name.push(self.next_char().unwrap()); } let end_pos = self.get_pos(); @@ -540,10 +545,21 @@ where Ok((start_pos, tok, end_pos)) } - fn is_char(&self) -> bool { - match self.chr0 { - Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9') => true, - _ => false, + fn is_identifier_start(&self, c: char) -> bool { + match c { + '_' => true, + c => UnicodeXID::is_xid_start(c), + } + } + + fn is_identifier_continuation(&self) -> bool { + if let Some(c) = self.chr0 { + match c { + '_' | '0'..='9' => true, + c => UnicodeXID::is_xid_continue(c), + } + } else { + false } } @@ -686,347 +702,380 @@ where } } - match self.chr0 { - Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => { + // Check if we have some character: + if let Some(c) = self.chr0 { + // First check identifier: + if self.is_identifier_start(c) { return Some(self.lex_identifier()); - } - Some('#') => { - self.lex_comment(); - continue; - } - Some('"') => { - return Some(self.lex_string(false, false, false, false)); - } - Some('\'') => { - return Some(self.lex_string(false, false, false, false)); - } - Some('=') => { + } else if is_emoji_presentation(c) { let tok_start = self.get_pos(); self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::EqEqual, tok_end))); + let tok_end = self.get_pos(); + println!("Emoji: {}", c); + return Some(Ok(( + tok_start, + Tok::Name { + name: c.to_string(), + }, + tok_end, + ))); + } else { + match c { + '0'..='9' => return Some(self.lex_number()), + '#' => { + self.lex_comment(); + continue; } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Equal, tok_end))); + '"' => { + return Some(self.lex_string(false, false, false, false)); } - } - } - Some('+') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::PlusEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Plus, tok_end))); - } - } - Some('*') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::StarEqual, tok_end))); + '\'' => { + return Some(self.lex_string(false, false, false, false)); } - Some('*') => { + '=' => { + let tok_start = self.get_pos(); self.next_char(); match self.chr0 { Some('=') => { self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleStarEqual, tok_end))); + return Some(Ok((tok_start, Tok::EqEqual, tok_end))); } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleStar, tok_end))); + return Some(Ok((tok_start, Tok::Equal, tok_end))); } } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Star, tok_end))); - } - } - } - Some('/') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { + '+' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::SlashEqual, tok_end))); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::PlusEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Plus, tok_end))); + } } - Some('/') => { + '*' => { + let tok_start = self.get_pos(); self.next_char(); match self.chr0 { Some('=') => { self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleSlashEqual, tok_end))); + return Some(Ok((tok_start, Tok::StarEqual, tok_end))); + } + Some('*') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::DoubleStarEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::DoubleStar, tok_end))); + } + } } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleSlash, tok_end))); + return Some(Ok((tok_start, Tok::Star, tok_end))); } } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Slash, tok_end))); + '/' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::SlashEqual, tok_end))); + } + Some('/') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::DoubleSlashEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::DoubleSlash, tok_end))); + } + } + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Slash, tok_end))); + } + } } - } - } - Some('%') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::PercentEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Percent, tok_end))); - } - } - Some('|') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::VbarEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Vbar, tok_end))); - } - } - Some('^') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::CircumFlex, tok_end))); - } - } - Some('&') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::AmperEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Amper, tok_end))); - } - } - Some('-') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { + '%' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::MinusEqual, tok_end))); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::PercentEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Percent, tok_end))); + } } - Some('>') => { + '|' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Rarrow, tok_end))); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::VbarEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Vbar, tok_end))); + } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Minus, tok_end))); + '^' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::CircumFlex, tok_end))); + } } - } - } - Some('@') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::AtEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::At, tok_end))); - } - } - Some('!') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { + '&' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::NotEqual, tok_end))); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::AmperEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Amper, tok_end))); + } } - _ => panic!("Invalid token '!'"), - } - } - Some('~') => { - return Some(self.eat_single_char(Tok::Tilde)); - } - Some('(') => { - let result = self.eat_single_char(Tok::Lpar); - self.nesting += 1; - return Some(result); - } - Some(')') => { - let result = self.eat_single_char(Tok::Rpar); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some('[') => { - let result = self.eat_single_char(Tok::Lsqb); - self.nesting += 1; - return Some(result); - } - Some(']') => { - let result = self.eat_single_char(Tok::Rsqb); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some('{') => { - let result = self.eat_single_char(Tok::Lbrace); - self.nesting += 1; - return Some(result); - } - Some('}') => { - let result = self.eat_single_char(Tok::Rbrace); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some(':') => { - return Some(self.eat_single_char(Tok::Colon)); - } - Some(';') => { - return Some(self.eat_single_char(Tok::Semi)); - } - Some('<') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('<') => { + '-' => { + let tok_start = self.get_pos(); self.next_char(); match self.chr0 { Some('=') => { self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LeftShiftEqual, tok_end))); + return Some(Ok((tok_start, Tok::MinusEqual, tok_end))); + } + Some('>') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Rarrow, tok_end))); } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LeftShift, tok_end))); + return Some(Ok((tok_start, Tok::Minus, tok_end))); } } } - Some('=') => { + '@' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LessEqual, tok_end))); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::AtEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::At, tok_end))); + } } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Less, tok_end))); + '!' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::NotEqual, tok_end))); + } else { + return Some(Err(LexicalError::UnrecognizedToken { tok: '!' })); + } } - } - } - Some('>') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('>') => { + '~' => { + return Some(self.eat_single_char(Tok::Tilde)); + } + '(' => { + let result = self.eat_single_char(Tok::Lpar); + self.nesting += 1; + return Some(result); + } + ')' => { + let result = self.eat_single_char(Tok::Rpar); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + '[' => { + let result = self.eat_single_char(Tok::Lsqb); + self.nesting += 1; + return Some(result); + } + ']' => { + let result = self.eat_single_char(Tok::Rsqb); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + '{' => { + let result = self.eat_single_char(Tok::Lbrace); + self.nesting += 1; + return Some(result); + } + '}' => { + let result = self.eat_single_char(Tok::Rbrace); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + ':' => { + return Some(self.eat_single_char(Tok::Colon)); + } + ';' => { + return Some(self.eat_single_char(Tok::Semi)); + } + '<' => { + let tok_start = self.get_pos(); self.next_char(); match self.chr0 { + Some('<') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::LeftShiftEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::LeftShift, tok_end))); + } + } + } Some('=') => { self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::RightShiftEqual, tok_end))); + return Some(Ok((tok_start, Tok::LessEqual, tok_end))); } _ => { let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::RightShift, tok_end))); + return Some(Ok((tok_start, Tok::Less, tok_end))); } } } - Some('=') => { + '>' => { + let tok_start = self.get_pos(); self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::GreaterEqual, tok_end))); + match self.chr0 { + Some('>') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::RightShiftEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::RightShift, tok_end))); + } + } + } + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::GreaterEqual, tok_end))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Greater, tok_end))); + } + } } - _ => { + ',' => { + let tok_start = self.get_pos(); + self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Greater, tok_end))); + return Some(Ok((tok_start, Tok::Comma, tok_end))); } - } - } - Some(',') => { - let tok_start = self.get_pos(); - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Comma, tok_end))); - } - Some('.') => { - let tok_start = self.get_pos(); - self.next_char(); - if let (Some('.'), Some('.')) = (&self.chr0, &self.chr1) { - self.next_char(); - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Ellipsis, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Dot, tok_end))); - } - } - Some('\n') => { - let tok_start = self.get_pos(); - self.next_char(); - let tok_end = self.get_pos(); - self.new_line(); + '.' => { + let tok_start = self.get_pos(); + self.next_char(); + if let (Some('.'), Some('.')) = (&self.chr0, &self.chr1) { + self.next_char(); + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Ellipsis, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Dot, tok_end))); + } + } + '\n' => { + let tok_start = self.get_pos(); + self.next_char(); + let tok_end = self.get_pos(); + self.new_line(); - // Depending on the nesting level, we emit newline or not: - if self.nesting == 0 { - self.at_begin_of_line = true; - return Some(Ok((tok_start, Tok::Newline, tok_end))); - } else { - continue; + // Depending on the nesting level, we emit newline or not: + if self.nesting == 0 { + self.at_begin_of_line = true; + return Some(Ok((tok_start, Tok::Newline, tok_end))); + } else { + continue; + } + } + ' ' => { + // Skip whitespaces + self.next_char(); + continue; + } + _ => { + let c = self.next_char(); + return Some(Err(LexicalError::UnrecognizedToken { tok: c.unwrap() })); + } // Ignore all the rest.. } } - Some(' ') => { - // Skip whitespaces - self.next_char(); - continue; - } - None => return None, - _ => { - let c = self.next_char(); - return Some(Err(LexicalError::UnrecognizedToken { tok: c.unwrap() })); - } // Ignore all the rest.. + } else { + return None; } } } diff --git a/src/main.rs b/src/main.rs index a0683ec3ed..750faf98a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,16 +65,11 @@ fn main() { } fn _run_string(vm: &VirtualMachine, source: &str, source_path: String) -> PyResult { - let code_obj = compile::compile( - source, - &compile::Mode::Exec, - source_path, - vm.ctx.code_type(), - ) - .map_err(|err| { - let syntax_error = vm.context().exceptions.syntax_error.clone(); - vm.new_exception(syntax_error, err.to_string()) - })?; + let code_obj = + compile::compile(vm, source, &compile::Mode::Exec, source_path).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.to_string()) + })?; // trace!("Code object: {:?}", code_obj.borrow()); let vars = vm.ctx.new_scope(); // Keep track of local variables vm.run_code_obj(code_obj, vars) @@ -115,12 +110,7 @@ fn run_script(vm: &VirtualMachine, script_file: &str) -> PyResult { } fn shell_exec(vm: &VirtualMachine, source: &str, scope: Scope) -> Result<(), CompileError> { - match compile::compile( - source, - &compile::Mode::Single, - "".to_string(), - vm.ctx.code_type(), - ) { + match compile::compile(vm, source, &compile::Mode::Single, "".to_string()) { Ok(code) => { if let Err(err) = vm.run_code_obj(code, scope) { print_exception(vm, &err); @@ -163,7 +153,7 @@ fn get_prompt(vm: &VirtualMachine, prompt_name: &str) -> String { fn run_shell(vm: &VirtualMachine) -> PyResult { println!( - "Welcome to the magnificent Rust Python {} interpreter", + "Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}", crate_version!() ); let vars = vm.ctx.new_scope(); diff --git a/tests/.travis-runner.sh b/tests/.travis-runner.sh index 8a004d3b19..f555265898 100755 --- a/tests/.travis-runner.sh +++ b/tests/.travis-runner.sh @@ -13,6 +13,7 @@ pip install pipenv if [ $CODE_COVERAGE = "true" ] then find . -name '*.gcda' -delete + find . -name '*.gcno' -delete export CARGO_INCREMENTAL=0 export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads" @@ -31,7 +32,7 @@ then zip -0 ccov.zip `find . \( -name "rustpython*.gc*" \) -print` # Install grcov - curl -L https://github.com/mozilla/grcov/releases/download/v0.4.1/grcov-linux-x86_64.tar.bz2 | tar jxf - + curl -L https://github.com/mozilla/grcov/releases/download/v0.4.2/grcov-linux-x86_64.tar.bz2 | tar jxf - ./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir "/*" -p "x" > lcov.info diff --git a/tests/snippets/append.py b/tests/snippets/append.py deleted file mode 100644 index a0490cb6d3..0000000000 --- a/tests/snippets/append.py +++ /dev/null @@ -1,2 +0,0 @@ -x = [] -x.append(1) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 89014880ce..2ea71f7a7f 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -50,3 +50,7 @@ assert list(reversed(range(5))) == [4, 3, 2, 1, 0] assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5] assert list(reversed(range(1,10,5))) == [6, 1] + +# range retains the original int refs +i = 2**64 +assert range(i).stop is i diff --git a/tests/snippets/bytes.py b/tests/snippets/bytes.py new file mode 100644 index 0000000000..2473f44fb3 --- /dev/null +++ b/tests/snippets/bytes.py @@ -0,0 +1,46 @@ +# repr +assert repr(bytes([0, 1, 2])) == repr(b'\x00\x01\x02') +assert ( +repr(bytes([0, 9, 10, 11, 13, 31, 32, 33, 89, 120, 255]) +== "b'\\x00\\t\\n\\x0b\\r\\x1f !Yx\\xff'") +) + +# comp +a = b"abcd" +b = b"ab" +c = b"abcd" + +assert a > b +assert a >= b +assert b < a +assert b <= a +assert a == c + +# hash not implemented for iterator +# assert hash(iter(a)) == hash(iter(b"abcd")) + +assert repr(a) == "b'abcd'" +assert len(a) == 4 + +assert a + b == b"abcdab" + +# contains +assert b"ab" in b"abcd" +assert b"cd" in b"abcd" +assert b"abcd" in b"abcd" +assert b"a" in b"abcd" +assert b"d" in b"abcd" +assert b"dc" not in b"abcd" +assert 97 in b"abcd" +assert 150 not in b"abcd" + +# getitem +d = b"abcdefghij" + +assert d[1] == 98 +assert d[-1] == 106 +assert d[2:6] == b"cdef" +assert d[-6:] == b"efghij" +assert d[1:8:2] == b"bdfh" +# assert d[8:1:-2] == b"igec" +# assert d[-1:-8:-2] == b"jhfd" diff --git a/tests/snippets/set.py b/tests/snippets/set.py index a7a20b0efb..9e49dff89e 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -144,3 +144,88 @@ def __hash__(self): assert a == set([1,2,4,5]) with assertRaises(TypeError): a ^= 1 + +# frozen set + +assert frozenset([1,2]) == frozenset([1,2]) +assert not frozenset([1,2,3]) == frozenset([1,2]) + +assert frozenset([1,2,3]) >= frozenset([1,2]) +assert frozenset([1,2]) >= frozenset([1,2]) +assert not frozenset([1,3]) >= frozenset([1,2]) + +assert frozenset([1,2,3]).issuperset(frozenset([1,2])) +assert frozenset([1,2]).issuperset(frozenset([1,2])) +assert not frozenset([1,3]).issuperset(frozenset([1,2])) + +assert frozenset([1,2,3]) > frozenset([1,2]) +assert not frozenset([1,2]) > frozenset([1,2]) +assert not frozenset([1,3]) > frozenset([1,2]) + +assert frozenset([1,2]) <= frozenset([1,2,3]) +assert frozenset([1,2]) <= frozenset([1,2]) +assert not frozenset([1,3]) <= frozenset([1,2]) + +assert frozenset([1,2]).issubset(frozenset([1,2,3])) +assert frozenset([1,2]).issubset(frozenset([1,2])) +assert not frozenset([1,3]).issubset(frozenset([1,2])) + +assert frozenset([1,2]) < frozenset([1,2,3]) +assert not frozenset([1,2]) < frozenset([1,2]) +assert not frozenset([1,3]) < frozenset([1,2]) + +a = frozenset([1, 2, 3]) +assert len(a) == 3 +b = a.copy() +assert b == a + +assert frozenset([1,2,3]).union(frozenset([4,5])) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]).union(frozenset([1,2,3,4,5])) == frozenset([1,2,3,4,5]) + +assert frozenset([1,2,3]) | frozenset([4,5]) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]) | frozenset([1,2,3,4,5]) == frozenset([1,2,3,4,5]) + +assert frozenset([1,2,3]).intersection(frozenset([1,2])) == frozenset([1,2]) +assert frozenset([1,2,3]).intersection(frozenset([5,6])) == frozenset([]) + +assert frozenset([1,2,3]) & frozenset([4,5]) == frozenset([]) +assert frozenset([1,2,3]) & frozenset([1,2,3,4,5]) == frozenset([1,2,3]) + +assert frozenset([1,2,3]).difference(frozenset([1,2])) == frozenset([3]) +assert frozenset([1,2,3]).difference(frozenset([5,6])) == frozenset([1,2,3]) + +assert frozenset([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3]) +assert frozenset([1,2,3]) - frozenset([1,2,3,4,5]) == frozenset([]) + +assert frozenset([1,2,3]).symmetric_difference(frozenset([1,2])) == frozenset([3]) +assert frozenset([1,2,3]).symmetric_difference(frozenset([5,6])) == frozenset([1,2,3,5,6]) + +assert frozenset([1,2,3]) ^ frozenset([4,5]) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]) ^ frozenset([1,2,3,4,5]) == frozenset([4,5]) + +assert_raises(TypeError, lambda: frozenset([[]])) + +# set and frozen set +assert frozenset([1,2,3]).union(set([4,5])) == frozenset([1,2,3,4,5]) +assert set([1,2,3]).union(frozenset([4,5])) == set([1,2,3,4,5]) + +assert frozenset([1,2,3]) | set([4,5]) == frozenset([1,2,3,4,5]) +assert set([1,2,3]) | frozenset([4,5]) == set([1,2,3,4,5]) + +assert frozenset([1,2,3]).intersection(set([5,6])) == frozenset([]) +assert set([1,2,3]).intersection(frozenset([5,6])) == set([]) + +assert frozenset([1,2,3]) & set([1,2,3,4,5]) == frozenset([1,2,3]) +assert set([1,2,3]) & frozenset([1,2,3,4,5]) == set([1,2,3]) + +assert frozenset([1,2,3]).difference(set([5,6])) == frozenset([1,2,3]) +assert set([1,2,3]).difference(frozenset([5,6])) == set([1,2,3]) + +assert frozenset([1,2,3]) - set([4,5]) == frozenset([1,2,3]) +assert set([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3]) + +assert frozenset([1,2,3]).symmetric_difference(set([1,2])) == frozenset([3]) +assert set([1,2,3]).symmetric_difference(frozenset([1,2])) == set([3]) + +assert frozenset([1,2,3]) ^ set([4,5]) == frozenset([1,2,3,4,5]) +assert set([1,2,3]) ^ frozenset([4,5]) == set([1,2,3,4,5]) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index 4483b37f62..31511db11e 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -1,3 +1,5 @@ +from testutils import assert_raises + assert "a" == 'a' assert """a""" == "a" assert len(""" " "" " "" """) == 11 @@ -124,3 +126,9 @@ assert 'z' > 'b' assert 'z' >= 'b' assert 'a' >= 'a' + +def try_mutate_str(): + word = "word" + word[0] = 'x' + +assert_raises(TypeError, try_mutate_str) diff --git a/tests/snippets/types_snippet.py b/tests/snippets/types_snippet.py index ac715751ef..f441552d11 100644 --- a/tests/snippets/types_snippet.py +++ b/tests/snippets/types_snippet.py @@ -26,3 +26,26 @@ assert isinstance(type, type) assert issubclass(type, type) + +class A: pass +class B(A): pass +class C(A): pass +class D(B, C): pass + +assert A.__subclasses__() == [B, C] +assert B.__subclasses__() == [D] +assert C.__subclasses__() == [D] +assert D.__subclasses__() == [] + +del D + +try: # gc sweep is needed here for CPython... + import gc; gc.collect() +except: # ...while RustPython doesn't have `gc` yet. + pass + +assert B.__subclasses__() == [] +assert C.__subclasses__() == [] + +assert type in object.__subclasses__() + diff --git a/tests/snippets/unicode_fu.py b/tests/snippets/unicode_fu.py new file mode 100644 index 0000000000..96d5bf9770 --- /dev/null +++ b/tests/snippets/unicode_fu.py @@ -0,0 +1,13 @@ + +# Test the unicode support! 👋 + + +áš´=2 + +assert áš´*8 == 16 + +áš´="👋" + +c = áš´*3 + +assert c == '👋👋👋' diff --git a/tests/snippets/xfail_3.1.2.17.py b/tests/snippets/xfail_3.1.2.17.py deleted file mode 100644 index fc956bef43..0000000000 --- a/tests/snippets/xfail_3.1.2.17.py +++ /dev/null @@ -1,7 +0,0 @@ -word = "Python" - -word[0] = "J" # Should raise a error, immutable -word[2:] = "Jy" # Should raise a error, immutable - - - diff --git a/tests/test_snippets.py b/tests/test_snippets.py index 4be73b7bde..7edbd7b7e5 100644 --- a/tests/test_snippets.py +++ b/tests/test_snippets.py @@ -17,7 +17,6 @@ class _TestType(enum.Enum): functional = 1 - benchmark = 2 logger = logging.getLogger('tests') @@ -25,7 +24,6 @@ class _TestType(enum.Enum): TEST_ROOT = os.path.abspath(os.path.join(ROOT_DIR, 'tests')) TEST_DIRS = { _TestType.functional: os.path.join(TEST_ROOT, 'snippets'), - _TestType.benchmark: os.path.join(TEST_ROOT, 'benchmarks'), } CPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR, 'py_code_object')) RUSTPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR)) @@ -65,8 +63,7 @@ def run_via_cpython_bytecode(filename, test_type): # Step2: run cpython bytecode: env = os.environ.copy() - log_level = 'info' if test_type == _TestType.benchmark else 'debug' - env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level) + env['RUST_LOG'] = 'info,cargo=error,jobserver=error' env['RUST_BACKTRACE'] = '1' with pushd(CPYTHON_RUNNER_DIR): subprocess.check_call(['cargo', 'run', bytecode_filename], env=env) @@ -74,15 +71,15 @@ def run_via_cpython_bytecode(filename, test_type): def run_via_rustpython(filename, test_type): env = os.environ.copy() - log_level = 'info' if test_type == _TestType.benchmark else 'trace' - env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level) + env['RUST_LOG'] = 'info,cargo=error,jobserver=error' env['RUST_BACKTRACE'] = '1' + + target = 'release' if env.get('CODE_COVERAGE', 'false') == 'true': - subprocess.check_call( - ['cargo', 'run', filename], env=env) - else: - subprocess.check_call( - ['cargo', 'run', '--release', filename], env=env) + target = 'debug' + binary = os.path.abspath(os.path.join(ROOT_DIR, 'target', target, 'rustpython')) + + subprocess.check_call([binary, filename], env=env) def create_test_function(cls, filename, method, test_type): @@ -124,4 +121,7 @@ def get_test_files(): # @populate('cpython_bytecode') @populate('rustpython') class SampleTestCase(unittest.TestCase): - pass + @classmethod + def setUpClass(cls): + subprocess.check_call(['cargo', 'build']) + subprocess.check_call(['cargo', 'build', '--release']) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 5ac00561c6..1de24b86ab 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -11,7 +11,7 @@ use num_traits::{Signed, ToPrimitive}; use crate::compile; use crate::import::import_module; use crate::obj::objbool; -use crate::obj::objdict; +use crate::obj::objdict::PyDictRef; use crate::obj::objint; use crate::obj::objiter; use crate::obj::objstr::{self, PyStringRef}; @@ -24,24 +24,10 @@ use crate::pyobject::{ }; use crate::vm::VirtualMachine; +use crate::obj::objcode::PyCodeRef; #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::io::io_open; -fn get_locals(vm: &VirtualMachine) -> PyObjectRef { - let d = vm.new_dict(); - // TODO: implement dict_iter_items? - let locals = vm.get_locals(); - let key_value_pairs = objdict::get_key_value_pairs(&locals); - for (key, value) in key_value_pairs { - objdict::set_item(&d, vm, &key, &value); - } - d -} - -fn dir_locals(vm: &VirtualMachine) -> PyObjectRef { - get_locals(vm) -} - fn builtin_abs(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(x, None)]); match vm.get_method(x.clone(), "__abs__") { @@ -95,7 +81,7 @@ fn builtin_bin(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_callable(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, None)]); - let is_callable = objtype::class_has_attr(&obj.type_pyref(), "__call__"); + let is_callable = objtype::class_has_attr(&obj.class(), "__call__"); Ok(vm.new_bool(is_callable)) } @@ -112,22 +98,17 @@ fn builtin_chr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.new_str(txt)) } -fn builtin_compile(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [ - (source, None), - (filename, Some(vm.ctx.str_type())), - (mode, Some(vm.ctx.str_type())) - ] - ); - let source = objstr::get_value(source); +fn builtin_compile( + source: PyStringRef, + filename: PyStringRef, + mode: PyStringRef, + vm: &VirtualMachine, +) -> PyResult { // TODO: fix this newline bug: - let source = format!("{}\n", source); + let source = format!("{}\n", &source.value); let mode = { - let mode = objstr::get_value(mode); + let mode = &mode.value; if mode == "exec" { compile::Mode::Exec } else if mode == "eval" { @@ -141,9 +122,7 @@ fn builtin_compile(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } }; - let filename = objstr::get_value(filename); - - compile::compile(&source, &mode, filename, vm.ctx.code_type()).map_err(|err| { + compile::compile(vm, &source, &mode, filename.value.to_string()).map_err(|err| { let syntax_error = vm.context().exceptions.syntax_error.clone(); vm.new_exception(syntax_error, err.to_string()) }) @@ -160,7 +139,7 @@ fn builtin_delattr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { fn builtin_dir(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { if args.args.is_empty() { - Ok(dir_locals(vm)) + Ok(vm.get_locals().into_object()) } else { let obj = args.args.into_iter().next().unwrap(); let seq = vm.call_method(&obj, "__dir__", vec![])?; @@ -190,25 +169,23 @@ fn builtin_eval(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let scope = make_scope(vm, globals, locals)?; // Determine code object: - let code_obj = if objtype::isinstance(source, &vm.ctx.code_type()) { - source.clone() + let code_obj = if let Ok(code_obj) = PyCodeRef::try_from_object(vm, source.clone()) { + code_obj } else if objtype::isinstance(source, &vm.ctx.str_type()) { let mode = compile::Mode::Eval; let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err( - |err| { - let syntax_error = vm.context().exceptions.syntax_error.clone(); - vm.new_exception(syntax_error, err.to_string()) - }, - )? + compile::compile(vm, &source, &mode, "".to_string()).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.to_string()) + })? } else { return Err(vm.new_type_error("code argument must be str or code object".to_string())); }; // Run the source: - vm.run_code_obj(code_obj.clone(), scope) + vm.run_code_obj(code_obj, scope) } /// Implements `exec` @@ -229,14 +206,12 @@ fn builtin_exec(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let source = objstr::get_value(source); // TODO: fix this newline bug: let source = format!("{}\n", source); - compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()).map_err( - |err| { - let syntax_error = vm.context().exceptions.syntax_error.clone(); - vm.new_exception(syntax_error, err.to_string()) - }, - )? - } else if objtype::isinstance(source, &vm.ctx.code_type()) { - source.clone() + compile::compile(vm, &source, &mode, "".to_string()).map_err(|err| { + let syntax_error = vm.context().exceptions.syntax_error.clone(); + vm.new_exception(syntax_error, err.to_string()) + })? + } else if let Ok(code_obj) = PyCodeRef::try_from_object(vm, source.clone()) { + code_obj } else { return Err(vm.new_type_error("source argument must be str or code object".to_string())); }; @@ -258,7 +233,7 @@ fn make_scope( } else if vm.isinstance(arg, &dict_type)? { Some(arg) } else { - let arg_typ = arg.typ(); + let arg_typ = arg.class(); let actual_type = vm.to_pystr(&arg_typ)?; let expected_type_name = vm.to_pystr(&dict_type)?; return Err(vm.new_type_error(format!( @@ -272,11 +247,11 @@ fn make_scope( let current_scope = vm.current_scope(); let globals = match globals { - Some(dict) => dict.clone(), + Some(dict) => dict.clone().downcast().unwrap(), None => current_scope.globals.clone(), }; let locals = match locals { - Some(dict) => Some(dict.clone()), + Some(dict) => dict.clone().downcast().ok(), None => current_scope.get_only_locals(), }; @@ -318,7 +293,7 @@ fn builtin_getattr( } } -fn builtin_globals(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { +fn builtin_globals(vm: &VirtualMachine) -> PyResult { Ok(vm.current_scope().globals.clone()) } @@ -386,15 +361,14 @@ fn builtin_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(value) => vm.invoke(value, PyFuncArgs::default()), Err(..) => Err(vm.new_type_error(format!( "object of type '{}' has no method {:?}", - objtype::get_type_name(&obj.typ()), + obj.class().name, len_method_name ))), } } -fn builtin_locals(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args); - Ok(vm.get_locals()) +fn builtin_locals(vm: &VirtualMachine) -> PyDictRef { + vm.get_locals() } fn builtin_max(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -623,10 +597,9 @@ fn builtin_reversed(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { match vm.get_method(obj.clone(), "__reversed__") { Ok(value) => vm.invoke(value, PyFuncArgs::default()), // TODO: fallback to using __len__ and __getitem__, if object supports sequence protocol - Err(..) => Err(vm.new_type_error(format!( - "'{}' object is not reversible", - objtype::get_type_name(&obj.typ()), - ))), + Err(..) => { + Err(vm.new_type_error(format!("'{}' object is not reversible", obj.class().name))) + } } } // builtin_reversed @@ -695,9 +668,14 @@ fn builtin_import(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); let current_path = { - let mut source_pathbuf = PathBuf::from(&vm.current_frame().code.source_path); - source_pathbuf.pop(); - source_pathbuf + match vm.current_frame() { + Some(frame) => { + let mut source_pathbuf = PathBuf::from(&frame.code.source_path); + source_pathbuf.pop(); + source_pathbuf + } + None => PathBuf::new(), + } }; import_module(vm, current_path, &objstr::get_value(name)) @@ -815,9 +793,9 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu }; for base in bases.clone() { - if objtype::issubclass(&base.type_pyref(), &metaclass) { - metaclass = base.type_pyref(); - } else if !objtype::issubclass(&metaclass, &base.type_pyref()) { + if objtype::issubclass(&base.class(), &metaclass) { + metaclass = base.class(); + } else if !objtype::issubclass(&metaclass, &base.class()) { return Err(vm.new_type_error("metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases".to_string())); } } @@ -828,13 +806,15 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu let prepare = vm.get_attribute(metaclass.clone().into_object(), "__prepare__")?; let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?; - let cells = vm.new_dict(); + let namespace: PyDictRef = TryFromObject::try_from_object(vm, namespace)?; + + let cells = vm.ctx.new_dict(); vm.invoke_with_locals(function, cells.clone(), namespace.clone())?; let class = vm.call_method( metaclass.as_object(), "__call__", - vec![name_arg, bases, namespace], + vec![name_arg, bases, namespace.into_object()], )?; cells.set_item(&vm.ctx, "__class__", class.clone()); Ok(class) diff --git a/vm/src/compile.rs b/vm/src/compile.rs index 46101cdf46..00bafe7278 100644 --- a/vm/src/compile.rs +++ b/vm/src/compile.rs @@ -8,8 +8,9 @@ use crate::bytecode::{self, CallType, CodeObject, Instruction, Varargs}; use crate::error::CompileError; use crate::obj::objcode; -use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyObject, PyObjectRef}; +use crate::obj::objcode::PyCodeRef; +use crate::pyobject::PyValue; +use crate::VirtualMachine; use num_complex::Complex64; use rustpython_parser::{ast, parser}; @@ -24,11 +25,11 @@ struct Compiler { /// Compile a given sourcecode into a bytecode object. pub fn compile( + vm: &VirtualMachine, source: &str, mode: &Mode, source_path: String, - code_type: PyClassRef, -) -> Result { +) -> Result { let mut compiler = Compiler::new(); compiler.source_path = Some(source_path); compiler.push_new_code_object("".to_string()); @@ -50,10 +51,7 @@ pub fn compile( let code = compiler.pop_code_object(); trace!("Compilation completed: {:?}", code); - Ok(PyObject::new( - objcode::PyCode::new(code), - code_type.into_object(), - )) + Ok(objcode::PyCode::new(code).into_ref(vm)) } pub enum Mode { diff --git a/vm/src/eval.rs b/vm/src/eval.rs index 202286eb13..6c7b3ca549 100644 --- a/vm/src/eval.rs +++ b/vm/src/eval.rs @@ -8,12 +8,7 @@ use crate::pyobject::PyResult; use crate::vm::VirtualMachine; pub fn eval(vm: &VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult { - match compile::compile( - source, - &compile::Mode::Eval, - source_path.to_string(), - vm.ctx.code_type(), - ) { + match compile::compile(vm, source, &compile::Mode::Eval, source_path.to_string()) { Ok(bytecode) => { debug!("Code object: {:?}", bytecode); vm.run_code_obj(bytecode, scope) diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 3eb962edfe..fb757cd0f3 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -68,7 +68,6 @@ fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { args, required = [(exc, Some(vm.ctx.exceptions.exception_type.clone()))] ); - let type_name = objtype::get_type_name(&exc.typ()); let msg = if let Ok(m) = vm.get_attribute(exc.clone(), "msg") { match vm.to_pystr(&m) { Ok(msg) => msg, @@ -77,7 +76,7 @@ fn exception_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } else { panic!("Error message must be set"); }; - let s = format!("{}: {}", type_name, msg); + let s = format!("{}: {}", exc.class().name, msg); Ok(vm.new_str(s)) } diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 79fdfc1f57..88a619eb36 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -11,9 +11,8 @@ use crate::bytecode; use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; -use crate::obj::objcode; -use crate::obj::objdict; -use crate::obj::objdict::PyDict; +use crate::obj::objcode::PyCodeRef; +use crate::obj::objdict::{self, PyDictRef}; use crate::obj::objint::PyInt; use crate::obj::objiter; use crate::obj::objlist; @@ -22,7 +21,7 @@ use crate::obj::objstr; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, + DictProtocol, IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; @@ -80,8 +79,8 @@ impl<'a, T> Iterator for Iter<'a, T> { #[derive(Clone)] pub struct Scope { - locals: RcList, - pub globals: PyObjectRef, + locals: RcList, + pub globals: PyDictRef, } impl fmt::Debug for Scope { @@ -92,7 +91,7 @@ impl fmt::Debug for Scope { } impl Scope { - pub fn new(locals: Option, globals: PyObjectRef) -> Scope { + pub fn new(locals: Option, globals: PyDictRef) -> Scope { let locals = match locals { Some(dict) => RcList::new().insert(dict), None => RcList::new(), @@ -100,18 +99,18 @@ impl Scope { Scope { locals, globals } } - pub fn get_locals(&self) -> PyObjectRef { + pub fn get_locals(&self) -> PyDictRef { match self.locals.iter().next() { Some(dict) => dict.clone(), None => self.globals.clone(), } } - pub fn get_only_locals(&self) -> Option { + pub fn get_only_locals(&self) -> Option { self.locals.iter().next().cloned() } - pub fn child_scope_with_locals(&self, locals: PyObjectRef) -> Scope { + pub fn child_scope_with_locals(&self, locals: PyDictRef) -> Scope { Scope { locals: self.locals.clone().insert(locals), globals: self.globals.clone(), @@ -142,7 +141,7 @@ impl NameProtocol for Scope { return Some(value); } - vm.builtins.get_item(name) + vm.get_attribute(vm.builtins.clone(), name).ok() } fn load_cell(&self, _vm: &VirtualMachine, name: &str) -> Option { @@ -186,6 +185,8 @@ enum BlockType { }, } +pub type FrameRef = PyRef; + pub struct Frame { pub code: bytecode::CodeObject, // We need 1 stack per frame @@ -211,7 +212,7 @@ pub enum ExecutionResult { pub type FrameResult = Result, PyObjectRef>; impl Frame { - pub fn new(code: PyObjectRef, scope: Scope) -> Frame { + pub fn new(code: PyCodeRef, scope: Scope) -> Frame { //populate the globals and locals //TODO: This is wrong, check https://github.com/nedbat/byterun/blob/31e6c4a8212c35b5157919abff43a7daa0f377c6/byterun/pyvm2.py#L95 /* @@ -224,7 +225,7 @@ impl Frame { // locals.extend(callargs); Frame { - code: objcode::get_value(&code), + code: code.code.clone(), stack: RefCell::new(vec![]), blocks: RefCell::new(vec![]), // save the callargs as locals @@ -384,7 +385,7 @@ impl Frame { Ok(None) } bytecode::Instruction::BuildMap { size, unpack } => { - let map_obj = vm.ctx.new_dict(); + let map_obj = vm.ctx.new_dict().into_object(); for _x in 0..*size { let obj = self.pop_value(); if *unpack { @@ -411,7 +412,7 @@ impl Frame { if x.is(&vm.ctx.none()) { None } else if let Some(i) = x.payload::() { - Some(i.value.clone()) + Some(i.as_bigint().clone()) } else { panic!("Expect Int or None as BUILD_SLICE arguments") } @@ -562,12 +563,15 @@ impl Frame { } bytecode::Instruction::MakeFunction { flags } => { let _qualified_name = self.pop_value(); - let code_obj = self.pop_value(); + let code_obj = self + .pop_value() + .downcast() + .expect("Second to top value on the stack must be a code object"); let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { self.pop_value() } else { - vm.new_dict() + vm.ctx.new_dict().into_object() }; let defaults = if flags.contains(bytecode::FunctionOpArg::HAS_DEFAULTS) { @@ -834,8 +838,10 @@ impl Frame { let module = vm.import(module)?; // Grab all the names from the module and put them in the context - for (k, v) in module.get_key_value_pairs().iter() { - self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); + if let Some(dict) = &module.dict { + for (k, v) in dict.get_key_value_pairs().iter() { + self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); + } } Ok(None) } @@ -942,7 +948,7 @@ impl Frame { // let args = PyFuncArgs::default(); // TODO: what happens when we got an error during handling exception? let args = if let Some(exc) = exc { - let exc_type = exc.typ(); + let exc_type = exc.class().into_object(); let exc_val = exc.clone(); let exc_tb = vm.ctx.none(); // TODO: retrieve traceback? vec![exc_type, exc_val, exc_tb] @@ -1086,7 +1092,7 @@ impl Frame { Ok(found) => Ok(found), Err(_) => Err(vm.new_type_error(format!( "{} has no __contains__ method", - objtype::get_type_name(&haystack.typ()) + haystack.class().name ))), } } @@ -1096,7 +1102,7 @@ impl Frame { Ok(found) => Ok(vm.ctx.new_bool(!objbool::get_value(&found))), Err(_) => Err(vm.new_type_error(format!( "{} has no __contains__ method", - objtype::get_type_name(&haystack.typ()) + haystack.class().name ))), } } @@ -1225,13 +1231,11 @@ impl fmt::Debug for Frame { .iter() .map(|elem| format!("\n > {:?}", elem)) .collect::(); - let local_str = match self.scope.get_locals().payload::() { - Some(dict) => objdict::get_key_value_pairs_from_content(&dict.entries.borrow()) - .iter() - .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) - .collect::(), - None => panic!("locals unexpectedly not wrapping a dict!",), - }; + let dict = self.scope.get_locals(); + let local_str = objdict::get_key_value_pairs_from_content(&dict.entries.borrow()) + .iter() + .map(|elem| format!("\n {:?} = {:?}", elem.0, elem.1)) + .collect::(); write!( f, "Frame Object {{ \n Stack:{}\n Blocks:{}\n Locals:{}\n}}", diff --git a/vm/src/function.rs b/vm/src/function.rs index a7581d3f25..1c61ffc87c 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -99,7 +99,7 @@ impl PyFuncArgs { Ok(Some(kwarg)) } else { let expected_ty_name = vm.to_pystr(&ty)?; - let actual_ty_name = vm.to_pystr(&kwarg.typ())?; + let actual_ty_name = vm.to_pystr(&kwarg.class())?; Err(vm.new_type_error(format!( "argument of type {} is required for named parameter `{}` (got: {})", expected_ty_name, key, actual_ty_name @@ -247,6 +247,15 @@ where } } +impl IntoIterator for KwArgs { + type Item = (String, T); + type IntoIter = std::collections::hash_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + /// A list of positional argument values. /// /// A built-in function with a `Args` parameter is analagous to a Python @@ -305,6 +314,15 @@ pub enum OptionalArg { } impl OptionalArg { + #[inline] + pub fn is_present(&self) -> bool { + match self { + Present(_) => true, + Missing => false, + } + } + + #[inline] pub fn into_option(self) -> Option { match self { Present(value) => Some(value), diff --git a/vm/src/import.rs b/vm/src/import.rs index ed5a6d094b..936b896805 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -27,10 +27,10 @@ fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &s let source = util::read_file(file_path.as_path()) .map_err(|e| vm.new_exception(import_error.clone(), e.description().to_string()))?; let code_obj = compile::compile( + vm, &source, &compile::Mode::Exec, file_path.to_str().unwrap().to_string(), - vm.ctx.code_type(), ) .map_err(|err| { let syntax_error = vm.context().exceptions.syntax_error.clone(); diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 0c205ffc80..a631867976 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -20,7 +20,7 @@ macro_rules! type_check { let arg = &$args.args[$arg_count]; if !$crate::obj::objtype::isinstance(arg, &expected_type) { - let arg_typ = arg.typ(); + let arg_typ = arg.class(); let expected_type_name = $vm.to_pystr(&expected_type)?; let actual_type = $vm.to_pystr(&arg_typ)?; return Err($vm.new_type_error(format!( diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index e21a3a3296..d4ef4b0fbc 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -25,7 +25,7 @@ pub fn boolval(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { Ok(if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { let bool_res = vm.invoke(f, PyFuncArgs::default())?; match bool_res.payload::() { - Some(i) => !i.value.is_zero(), + Some(i) => !i.as_bigint().is_zero(), None => return Err(vm.new_type_error(String::from("TypeError"))), } } else { @@ -59,7 +59,7 @@ pub fn not(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult { // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> bool { - !obj.payload::().unwrap().value.is_zero() + !obj.payload::().unwrap().as_bigint().is_zero() } fn bool_repr(vm: &VirtualMachine, args: PyFuncArgs) -> Result { diff --git a/vm/src/obj/objbytes.rs b/vm/src/obj/objbytes.rs index 8d64cc5076..70378cc470 100644 --- a/vm/src/obj/objbytes.rs +++ b/vm/src/obj/objbytes.rs @@ -11,6 +11,7 @@ use crate::pyobject::{ use crate::vm::VirtualMachine; use super::objint; +use super::objsequence::PySliceableSequence; use super::objtype::{self, PyClassRef}; #[derive(Debug)] @@ -67,7 +68,10 @@ pub fn init(context: &PyContext) { "__repr__" => context.new_rustfunc(bytes_repr), "__len__" => context.new_rustfunc(bytes_len), "__iter__" => context.new_rustfunc(bytes_iter), - "__doc__" => context.new_str(bytes_doc.to_string()) + "__doc__" => context.new_str(bytes_doc.to_string()), + "__add__" => context.new_rustfunc(PyBytesRef::add), + "__contains__" => context.new_rustfunc(PyBytesRef::contains), + "__getitem__" => context.new_rustfunc(PyBytesRef::getitem), }); } @@ -191,7 +195,7 @@ pub fn get_value<'a>(obj: &'a PyObjectRef) -> impl Deref> + 'a fn bytes_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.bytes_type()))]); let value = get_value(obj); - let data = String::from_utf8(value.to_vec()).unwrap(); + let data = format_bytes(&value); Ok(vm.new_str(format!("b'{}'", data))) } @@ -201,3 +205,101 @@ fn bytes_iter(obj: PyBytesRef, _vm: &VirtualMachine) -> PyIteratorValue { iterated_obj: obj.into_object(), } } + +/// return true if b is a subset of a. +fn vec_contains(a: &Vec, b: &Vec) -> bool { + let a_len = a.len(); + let b_len = b.len(); + for (n, i) in a.iter().enumerate() { + if n + b_len <= a_len && *i == b[0] { + if &a[n..n + b_len] == b.as_slice() { + return true; + } + } + } + false +} + +fn format_bytes(chars: &Vec) -> String { + let mut res = String::new(); + for i in chars { + res.push_str(BYTES_REPR[*i as usize]) + } + res +} + +const BYTES_REPR: &'static [&'static str] = &[ + "\\x00", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\x07", "\\x08", "\\t", "\\n", + "\\x0b", "\\x0c", "\\r", "\\x0e", "\\x0f", "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", + "\\x15", "\\x16", "\\x17", "\\x18", "\\x19", "\\x1a", "\\x1b", "\\x1c", "\\x1d", "\\x1e", + "\\x1f", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", + "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", + "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", + "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", + "}", "~", "\\x7f", "\\x80", "\\x81", "\\x82", "\\x83", "\\x84", "\\x85", "\\x86", "\\x87", + "\\x88", "\\x89", "\\x8a", "\\x8b", "\\x8c", "\\x8d", "\\x8e", "\\x8f", "\\x90", "\\x91", + "\\x92", "\\x93", "\\x94", "\\x95", "\\x96", "\\x97", "\\x98", "\\x99", "\\x9a", "\\x9b", + "\\x9c", "\\x9d", "\\x9e", "\\x9f", "\\xa0", "\\xa1", "\\xa2", "\\xa3", "\\xa4", "\\xa5", + "\\xa6", "\\xa7", "\\xa8", "\\xa9", "\\xaa", "\\xab", "\\xac", "\\xad", "\\xae", "\\xaf", + "\\xb0", "\\xb1", "\\xb2", "\\xb3", "\\xb4", "\\xb5", "\\xb6", "\\xb7", "\\xb8", "\\xb9", + "\\xba", "\\xbb", "\\xbc", "\\xbd", "\\xbe", "\\xbf", "\\xc0", "\\xc1", "\\xc2", "\\xc3", + "\\xc4", "\\xc5", "\\xc6", "\\xc7", "\\xc8", "\\xc9", "\\xca", "\\xcb", "\\xcc", "\\xcd", + "\\xce", "\\xcf", "\\xd0", "\\xd1", "\\xd2", "\\xd3", "\\xd4", "\\xd5", "\\xd6", "\\xd7", + "\\xd8", "\\xd9", "\\xda", "\\xdb", "\\xdc", "\\xdd", "\\xde", "\\xdf", "\\xe0", "\\xe1", + "\\xe2", "\\xe3", "\\xe4", "\\xe5", "\\xe6", "\\xe7", "\\xe8", "\\xe9", "\\xea", "\\xeb", + "\\xec", "\\xed", "\\xee", "\\xef", "\\xf0", "\\xf1", "\\xf2", "\\xf3", "\\xf4", "\\xf5", + "\\xf6", "\\xf7", "\\xf8", "\\xf9", "\\xfa", "\\xfb", "\\xfc", "\\xfd", "\\xfe", "\\xff", +]; + +impl PyBytesRef { + fn add(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&other, &vm.ctx.bytes_type()) { + let rhs = get_value(&other); + let elements: Vec = self.value.iter().chain(rhs.iter()).cloned().collect(); + Ok(vm.ctx.new_bytes(elements)) + } else { + Err(vm.new_not_implemented_error("".to_string())) + } + } + + fn contains(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&needle, &vm.ctx.bytes_type()) { + let result = vec_contains(&self.value, &get_value(&needle)); + Ok(result) + } else if objtype::isinstance(&needle, &vm.ctx.int_type()) { + let result = self + .value + .contains(&objint::get_value(&needle).to_u8().unwrap()); + Ok(result) + } else { + Err(vm.new_type_error(format!("Cannot add {:?} and {:?}", self, needle))) + } + } + + fn getitem(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if objtype::isinstance(&needle, &vm.ctx.int_type()) { + match objint::get_value(&needle).to_i32() { + Some(pos) => { + if let Some(idx) = self.value.get_pos(pos) { + Ok(vm.ctx.new_int(self.value[idx])) + } else { + Err(vm.new_index_error("index out of range".to_string())) + } + } + None => Err( + vm.new_index_error("cannot fit 'int' into an index-sized integer".to_string()) + ), + } + } else if objtype::isinstance(&needle, &vm.ctx.slice_type()) { + Ok(vm + .ctx + .new_bytes((self.value.get_slice_items(&vm, &needle)).unwrap())) + } else { + Err(vm.new_type_error(format!( + "byte indices must be integers or slices, not {}", + needle.class() + ))) + } + } +} diff --git a/vm/src/obj/objcode.rs b/vm/src/obj/objcode.rs index fda5603f00..a26645b913 100644 --- a/vm/src/obj/objcode.rs +++ b/vm/src/obj/objcode.rs @@ -5,13 +5,14 @@ use std::fmt; use crate::bytecode; -use crate::function::PyFuncArgs; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; +pub type PyCodeRef = PyRef; + pub struct PyCode { - code: bytecode::CodeObject, + pub code: bytecode::CodeObject, } impl PyCode { @@ -32,91 +33,63 @@ impl PyValue for PyCode { } } -pub fn init(context: &PyContext) { - let code_type = context.code_type.as_object(); - extend_class!(context, code_type, { - "__new__" => context.new_rustfunc(code_new), - "__repr__" => context.new_rustfunc(code_repr) - }); - - for (name, f) in &[ - ( - "co_argcount", - code_co_argcount as fn(&VirtualMachine, PyFuncArgs) -> PyResult, - ), - ("co_consts", code_co_consts), - ("co_filename", code_co_filename), - ("co_firstlineno", code_co_firstlineno), - ("co_kwonlyargcount", code_co_kwonlyargcount), - ("co_name", code_co_name), - ] { - context.set_attr(code_type, name, context.new_property(f)) +impl PyCodeRef { + fn new(_cls: PyClassRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("Cannot directly create code object".to_string())) } -} -pub fn get_value(obj: &PyObjectRef) -> bytecode::CodeObject { - if let Some(code) = obj.payload::() { - code.code.clone() - } else { - panic!("Inner error getting code {:?}", obj) + fn repr(self, _vm: &VirtualMachine) -> String { + let code = &self.code; + format!( + "", + code.obj_name, + self.get_id(), + code.source_path, + code.first_line_number + ) } -} - -fn code_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(_cls, None)]); - Err(vm.new_type_error("Cannot directly create code object".to_string())) -} -fn code_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(o, Some(vm.ctx.code_type()))]); - - let code = get_value(o); - let repr = format!( - "", - code.obj_name, - o.get_id(), - code.source_path, - code.first_line_number - ); - Ok(vm.new_str(repr)) -} - -fn member_code_obj(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.code_type()))]); - Ok(get_value(zelf)) -} + fn co_argcount(self, _vm: &VirtualMachine) -> usize { + self.code.arg_names.len() + } -fn code_co_argcount(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let code_obj = member_code_obj(vm, args)?; - Ok(vm.ctx.new_int(code_obj.arg_names.len())) -} + fn co_filename(self, _vm: &VirtualMachine) -> String { + self.code.source_path.clone() + } -fn code_co_filename(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let code_obj = member_code_obj(vm, args)?; - let source_path = code_obj.source_path; - Ok(vm.new_str(source_path)) -} + fn co_firstlineno(self, _vm: &VirtualMachine) -> usize { + self.code.first_line_number + } -fn code_co_firstlineno(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let code_obj = member_code_obj(vm, args)?; - Ok(vm.ctx.new_int(code_obj.first_line_number)) -} + fn co_kwonlyargcount(self, _vm: &VirtualMachine) -> usize { + self.code.kwonlyarg_names.len() + } -fn code_co_kwonlyargcount(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let code_obj = member_code_obj(vm, args)?; - Ok(vm.ctx.new_int(code_obj.kwonlyarg_names.len())) -} + fn co_consts(self, vm: &VirtualMachine) -> PyObjectRef { + let consts = self + .code + .get_constants() + .map(|x| vm.ctx.unwrap_constant(x)) + .collect(); + vm.ctx.new_tuple(consts) + } -fn code_co_consts(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let code_obj = member_code_obj(vm, args)?; - let consts = code_obj - .get_constants() - .map(|x| vm.ctx.unwrap_constant(x)) - .collect(); - Ok(vm.ctx.new_tuple(consts)) + fn co_name(self, _vm: &VirtualMachine) -> String { + self.code.obj_name.clone() + } } -fn code_co_name(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let code_obj = member_code_obj(vm, args)?; - Ok(vm.new_str(code_obj.obj_name)) +pub fn init(context: &PyContext) { + let code_type = context.code_type.as_object(); + extend_class!(context, code_type, { + "__new__" => context.new_rustfunc(PyCodeRef::new), + "__repr__" => context.new_rustfunc(PyCodeRef::repr), + + "co_argcount" => context.new_property(PyCodeRef::co_argcount), + "co_consts" => context.new_property(PyCodeRef::co_consts), + "co_filename" => context.new_property(PyCodeRef::co_filename), + "co_firstlineno" => context.new_property(PyCodeRef::co_firstlineno), + "co_kwonlyargcount" => context.new_property(PyCodeRef::co_kwonlyargcount), + "co_name" => context.new_property(PyCodeRef::co_name), + }); } diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 0c8d5fe5ec..061c3477e7 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; use std::fmt; use std::ops::{Deref, DerefMut}; -use crate::function::{OptionalArg, PyFuncArgs}; +use crate::function::{KwArgs, OptionalArg}; use crate::pyobject::{ - PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + DictProtocol, PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -124,57 +124,48 @@ pub fn py_dict_to_attributes(dict: &PyObjectRef) -> PyAttributes { attrs } -pub fn attributes_to_py_dict(vm: &VirtualMachine, attributes: PyAttributes) -> PyResult { - let dict = vm.ctx.new_dict(); - for (key, value) in attributes { - let key = vm.ctx.new_str(key); - set_item(&dict, vm, &key, &value); - } - Ok(dict) -} - // Python dict methods: -fn dict_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(_ty, Some(vm.ctx.type_type()))], - optional = [(dict_obj, None)] - ); - let dict = vm.ctx.new_dict(); - if let Some(dict_obj) = dict_obj { - if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { - for (needle, value) in get_key_value_pairs(&dict_obj) { - set_item(&dict, vm, &needle, &value); - } - } else { - let iter = objiter::get_iter(vm, dict_obj)?; - loop { - fn err(vm: &VirtualMachine) -> PyObjectRef { - vm.new_type_error("Iterator must have exactly two elements".to_string()) +impl PyDictRef { + fn new( + _class: PyClassRef, // TODO Support subclasses of int. + dict_obj: OptionalArg, + kwargs: KwArgs, + vm: &VirtualMachine, + ) -> PyResult { + let dict = vm.ctx.new_dict(); + if let OptionalArg::Present(dict_obj) = dict_obj { + if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { + for (needle, value) in get_key_value_pairs(&dict_obj) { + set_item(dict.as_object(), vm, &needle, &value); } - let element = match objiter::get_next_object(vm, &iter)? { - Some(obj) => obj, - None => break, - }; - let elem_iter = objiter::get_iter(vm, &element)?; - let needle = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - if objiter::get_next_object(vm, &elem_iter)?.is_some() { - return Err(err(vm)); + } else { + let iter = objiter::get_iter(vm, &dict_obj)?; + loop { + fn err(vm: &VirtualMachine) -> PyObjectRef { + vm.new_type_error("Iterator must have exactly two elements".to_string()) + } + let element = match objiter::get_next_object(vm, &iter)? { + Some(obj) => obj, + None => break, + }; + let elem_iter = objiter::get_iter(vm, &element)?; + let needle = + objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; + let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; + if objiter::get_next_object(vm, &elem_iter)?.is_some() { + return Err(err(vm)); + } + set_item(dict.as_object(), vm, &needle, &value); } - set_item(&dict, vm, &needle, &value); } } + for (needle, value) in kwargs.into_iter() { + let py_needle = vm.new_str(needle); + set_item(&dict.as_object(), vm, &py_needle, &value); + } + Ok(dict) } - for (needle, value) in args.kwargs { - let py_needle = vm.new_str(needle); - set_item(&dict, vm, &py_needle, &value); - } - Ok(dict) -} -impl PyDictRef { fn bool(self, _vm: &VirtualMachine) -> bool { !self.entries.borrow().is_empty() } @@ -302,6 +293,31 @@ impl PyDictRef { } } +impl DictProtocol for PyDictRef { + fn contains_key(&self, k: &str) -> bool { + content_contains_key_str(&self.entries.borrow(), k) + } + + fn get_item(&self, k: &str) -> Option { + content_get_key_str(&self.entries.borrow(), k) + } + + fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { + get_key_value_pairs(self.as_object()) + } + + // Item set/get: + fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef) { + let key = ctx.new_str(key.to_string()); + set_item_in_content(&mut self.entries.borrow_mut(), &key, &v); + } + + fn del_item(&self, key: &str) { + let mut elements = get_mut_elements(self.as_object()); + elements.remove(key).unwrap(); + } +} + pub fn init(context: &PyContext) { extend_class!(context, &context.dict_type, { "__bool__" => context.new_rustfunc(PyDictRef::bool), @@ -310,7 +326,7 @@ pub fn init(context: &PyContext) { "__delitem__" => context.new_rustfunc(PyDictRef::delitem), "__getitem__" => context.new_rustfunc(PyDictRef::getitem), "__iter__" => context.new_rustfunc(PyDictRef::iter), - "__new__" => context.new_rustfunc(dict_new), + "__new__" => context.new_rustfunc(PyDictRef::new), "__repr__" => context.new_rustfunc(PyDictRef::repr), "__setitem__" => context.new_rustfunc(PyDictRef::setitem), "clear" => context.new_rustfunc(PyDictRef::clear), diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 0b0e5d4488..68d1e49f6b 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -32,7 +32,7 @@ fn enumerate_new( vm: &VirtualMachine, ) -> PyResult { let counter = match start { - OptionalArg::Present(start) => start.value.clone(), + OptionalArg::Present(start) => start.as_bigint().clone(), OptionalArg::Missing => BigInt::zero(), }; diff --git a/vm/src/obj/objfilter.rs b/vm/src/obj/objfilter.rs index 6fcf84fb4d..e547d2c43e 100644 --- a/vm/src/obj/objfilter.rs +++ b/vm/src/obj/objfilter.rs @@ -1,13 +1,13 @@ use crate::function::PyFuncArgs; -use crate::pyobject::{ - IdProtocol, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; // Required for arg_check! to use isinstance use super::objbool; use super::objiter; use crate::obj::objtype::PyClassRef; +pub type PyFilterRef = PyRef; + #[derive(Debug)] pub struct PyFilter { predicate: PyObjectRef, @@ -20,20 +20,19 @@ impl PyValue for PyFilter { } } -fn filter_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(cls, None), (function, None), (iterable, None)] - ); - let iterator = objiter::get_iter(vm, iterable)?; - Ok(PyObject::new( - PyFilter { - predicate: function.clone(), - iterator, - }, - cls.clone(), - )) +fn filter_new( + cls: PyClassRef, + function: PyObjectRef, + iterable: PyObjectRef, + vm: &VirtualMachine, +) -> PyResult { + let iterator = objiter::get_iter(vm, &iterable)?; + + PyFilter { + predicate: function.clone(), + iterator, + } + .into_ref_with_type(vm, cls) } fn filter_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index 1abeabba2a..ca6bad9d22 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -4,7 +4,7 @@ use super::objstr; use super::objtype; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - IntoPyObject, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + IntoPyObject, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::ToBigInt; @@ -155,7 +155,7 @@ impl PyFloatRef { } } - fn new_float(cls: PyObjectRef, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn new_float(cls: PyClassRef, arg: PyObjectRef, vm: &VirtualMachine) -> PyResult { let value = if objtype::isinstance(&arg, &vm.ctx.float_type()) { get_value(&arg) } else if objtype::isinstance(&arg, &vm.ctx.int_type()) { @@ -190,10 +190,9 @@ impl PyFloatRef { } } } else { - let type_name = objtype::get_type_name(&arg.typ()); - return Err(vm.new_type_error(format!("can't convert {} to float", type_name))); + return Err(vm.new_type_error(format!("can't convert {} to float", arg.class().name))); }; - Ok(PyObject::new(PyFloat { value }, cls.clone())) + PyFloat { value }.into_ref_with_type(vm, cls) } fn mod_(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/obj/objframe.rs b/vm/src/obj/objframe.rs index 8657dc2800..b4ed15f74a 100644 --- a/vm/src/obj/objframe.rs +++ b/vm/src/obj/objframe.rs @@ -2,43 +2,35 @@ */ -use crate::frame::Frame; -use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use super::objcode::PyCodeRef; +use super::objdict::PyDictRef; +use crate::frame::FrameRef; +use crate::pyobject::{PyContext, PyResult}; use crate::vm::VirtualMachine; pub fn init(context: &PyContext) { - let frame_type = &context.frame_type; - extend_class!(context, frame_type, { - "__new__" => context.new_rustfunc(frame_new), - "__repr__" => context.new_rustfunc(frame_repr), - "f_locals" => context.new_property(frame_flocals), - "f_code" => context.new_property(frame_fcode) + extend_class!(context, &context.frame_type, { + "__new__" => context.new_rustfunc(FrameRef::new), + "__repr__" => context.new_rustfunc(FrameRef::repr), + "f_locals" => context.new_property(FrameRef::flocals), + "f_code" => context.new_property(FrameRef::fcode), }); } -fn frame_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(_cls, None)]); - Err(vm.new_type_error("Cannot directly create frame object".to_string())) -} +impl FrameRef { + fn new(_class: FrameRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_type_error("Cannot directly create frame object".to_string())) + } -fn frame_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(_frame, Some(vm.ctx.frame_type()))]); - let repr = "".to_string(); - Ok(vm.new_str(repr)) -} + fn repr(self, _vm: &VirtualMachine) -> String { + "".to_string() + } -fn frame_flocals(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); - let frame = get_value(frame); - Ok(frame.scope.get_locals().clone()) -} - -fn frame_fcode(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(frame, Some(vm.ctx.frame_type()))]); - Ok(vm.ctx.new_code_object(get_value(frame).code.clone())) -} + fn flocals(self, _vm: &VirtualMachine) -> PyDictRef { + self.scope.get_locals() + } -pub fn get_value(obj: &PyObjectRef) -> &Frame { - &obj.payload::().unwrap() + fn fcode(self, vm: &VirtualMachine) -> PyCodeRef { + vm.ctx.new_code_object(self.code.clone()) + } } diff --git a/vm/src/obj/objfunction.rs b/vm/src/obj/objfunction.rs index 96fed8eec2..507d7697a9 100644 --- a/vm/src/obj/objfunction.rs +++ b/vm/src/obj/objfunction.rs @@ -1,19 +1,22 @@ use crate::frame::Scope; use crate::function::PyFuncArgs; +use crate::obj::objcode::PyCodeRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; +pub type PyFunctionRef = PyRef; + #[derive(Debug)] pub struct PyFunction { // TODO: these shouldn't be public - pub code: PyObjectRef, + pub code: PyCodeRef, pub scope: Scope, pub defaults: PyObjectRef, } impl PyFunction { - pub fn new(code: PyObjectRef, scope: Scope, defaults: PyObjectRef) -> Self { + pub fn new(code: PyCodeRef, scope: Scope, defaults: PyObjectRef) -> Self { PyFunction { code, scope, @@ -28,6 +31,12 @@ impl PyValue for PyFunction { } } +impl PyFunctionRef { + fn code(self, _vm: &VirtualMachine) -> PyCodeRef { + self.code.clone() + } +} + #[derive(Debug)] pub struct PyMethod { // TODO: these shouldn't be public @@ -51,7 +60,7 @@ pub fn init(context: &PyContext) { let function_type = &context.function_type; extend_class!(context, function_type, { "__get__" => context.new_rustfunc(bind_method), - "__code__" => context.new_property(function_code) + "__code__" => context.new_property(PyFunctionRef::code) }); let builtin_function_or_method_type = &context.builtin_function_or_method_type; @@ -67,16 +76,9 @@ fn bind_method(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { required = [(function, None), (obj, None), (cls, None)] ); - if obj.is(&vm.get_none()) && !cls.is(&obj.typ()) { + if obj.is(&vm.get_none()) && !cls.is(&obj.class()) { Ok(function.clone()) } else { Ok(vm.ctx.new_bound_method(function.clone(), obj.clone())) } } - -fn function_code(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - match args.args[0].payload() { - Some(PyFunction { ref code, .. }) => Ok(code.clone()), - None => Err(vm.new_type_error("no code".to_string())), - } -} diff --git a/vm/src/obj/objgenerator.rs b/vm/src/obj/objgenerator.rs index b766aafd4d..9718d29a44 100644 --- a/vm/src/obj/objgenerator.rs +++ b/vm/src/obj/objgenerator.rs @@ -2,17 +2,17 @@ * The mythical generator. */ -use crate::frame::{ExecutionResult, Frame}; -use crate::function::PyFuncArgs; +use crate::frame::{ExecutionResult, FrameRef}; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; +pub type PyGeneratorRef = PyRef; + #[derive(Debug)] pub struct PyGenerator { - frame: PyObjectRef, + frame: FrameRef, } -type PyGeneratorRef = PyRef; impl PyValue for PyGenerator { fn class(vm: &VirtualMachine) -> PyClassRef { @@ -20,48 +20,23 @@ impl PyValue for PyGenerator { } } -pub fn init(context: &PyContext) { - let generator_type = &context.generator_type; - extend_class!(context, generator_type, { - "__iter__" => context.new_rustfunc(generator_iter), - "__next__" => context.new_rustfunc(generator_next), - "send" => context.new_rustfunc(generator_send) - }); -} - -pub fn new_generator(frame: PyObjectRef, vm: &VirtualMachine) -> PyGeneratorRef { - PyGenerator { frame }.into_ref(vm) -} - -fn generator_iter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(o, Some(vm.ctx.generator_type()))]); - Ok(o.clone()) -} +impl PyGeneratorRef { + pub fn new(frame: FrameRef, vm: &VirtualMachine) -> PyGeneratorRef { + PyGenerator { frame }.into_ref(vm) + } -fn generator_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(o, Some(vm.ctx.generator_type()))]); - let value = vm.get_none(); - send(vm, o, &value) -} + fn iter(self, _vm: &VirtualMachine) -> PyGeneratorRef { + self + } -fn generator_send(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(o, Some(vm.ctx.generator_type())), (value, None)] - ); - send(vm, o, value) -} + fn next(self, vm: &VirtualMachine) -> PyResult { + self.send(vm.get_none(), vm) + } -fn send(vm: &VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult { - if let Some(PyGenerator { ref frame }) = gen.payload() { - if let Some(frame) = frame.payload::() { - frame.push_value(value.clone()); - } else { - panic!("Generator frame isn't a frame."); - } + fn send(self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { + self.frame.push_value(value.clone()); - match vm.run_frame(frame.clone())? { + match vm.run_frame(self.frame.clone())? { ExecutionResult::Yield(value) => Ok(value), ExecutionResult::Return(_value) => { // Stop iteration! @@ -69,7 +44,14 @@ fn send(vm: &VirtualMachine, gen: &PyObjectRef, value: &PyObjectRef) -> PyResult Err(vm.new_exception(stop_iteration, "End of generator".to_string())) } } - } else { - panic!("Cannot extract frame from non-generator"); } } + +pub fn init(context: &PyContext) { + let generator_type = &context.generator_type; + extend_class!(context, generator_type, { + "__iter__" => context.new_rustfunc(PyGeneratorRef::iter), + "__next__" => context.new_rustfunc(PyGeneratorRef::next), + "send" => context.new_rustfunc(PyGeneratorRef::send) + }); +} diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7c19968da3..c7f0ff5af4 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::hash::{Hash, Hasher}; use num_bigint::{BigInt, ToBigInt}; @@ -18,8 +19,13 @@ use crate::obj::objtype::PyClassRef; #[derive(Debug)] pub struct PyInt { - // TODO: shouldn't be public - pub value: BigInt, + value: BigInt, +} + +impl fmt::Display for PyInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + BigInt::fmt(&self.value, f) + } } pub type PyIntRef = PyRef; @@ -28,6 +34,10 @@ impl PyInt { pub fn new>(i: T) -> Self { PyInt { value: i.into() } } + + pub fn as_bigint(&self) -> &BigInt { + &self.value + } } impl IntoPyObject for BigInt { @@ -201,11 +211,7 @@ impl PyIntRef { fn lshift(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if !objtype::isinstance(&other, &vm.ctx.int_type()) { - return Err(vm.new_type_error(format!( - "unsupported operand type(s) for << '{}' and '{}'", - objtype::get_type_name(&self.as_object().typ()), - objtype::get_type_name(&other.typ()) - ))); + return Ok(vm.ctx.not_implemented()); } if let Some(n_bits) = get_value(&other).to_usize() { @@ -224,11 +230,7 @@ impl PyIntRef { fn rshift(self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { if !objtype::isinstance(&other, &vm.ctx.int_type()) { - return Err(vm.new_type_error(format!( - "unsupported operand type(s) for >> '{}' and '{}'", - objtype::get_type_name(&self.as_object().typ()), - objtype::get_type_name(&other.typ()) - ))); + return Ok(vm.ctx.not_implemented()); } if let Some(n_bits) = get_value(&other).to_usize() { @@ -410,10 +412,9 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult PyResult { vm.call_method(iter_target, "__iter__", vec![]) - // let type_str = objstr::get_value(&vm.to_str(iter_target.typ()).unwrap()); + // let type_str = objstr::get_value(&vm.to_str(iter_target.class()).unwrap()); // let type_error = vm.new_type_error(format!("Cannot iterate over {}", type_str)); // return Err(type_error); } diff --git a/vm/src/obj/objmemory.rs b/vm/src/obj/objmemory.rs index c5926c4abd..7f53fb5ae6 100644 --- a/vm/src/obj/objmemory.rs +++ b/vm/src/obj/objmemory.rs @@ -1,8 +1,9 @@ -use crate::function::PyFuncArgs; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; +pub type PyMemoryViewRef = PyRef; + #[derive(Debug)] pub struct PyMemoryView { obj: PyObjectRef, @@ -14,15 +15,13 @@ impl PyValue for PyMemoryView { } } -pub fn new_memory_view(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(cls, None), (bytes_object, None)]); - vm.ctx.set_attr(cls, "obj", bytes_object.clone()); - Ok(PyObject::new( - PyMemoryView { - obj: bytes_object.clone(), - }, - cls.clone(), - )) +pub fn new_memory_view( + cls: PyClassRef, + bytes_object: PyObjectRef, + vm: &VirtualMachine, +) -> PyResult { + vm.ctx.set_attr(&cls, "obj", bytes_object.clone()); + PyMemoryView { obj: bytes_object }.into_ref_with_type(vm, cls) } pub fn init(ctx: &PyContext) { diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 2d1d8921e1..b641f04268 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,12 +1,10 @@ -use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{DictProtocol, PyContext, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyModule { pub name: String, - pub dict: PyObjectRef, } pub type PyModuleRef = PyRef; @@ -18,23 +16,21 @@ impl PyValue for PyModule { impl PyModuleRef { fn dir(self: PyModuleRef, vm: &VirtualMachine) -> PyResult { - let keys = self - .dict - .get_key_value_pairs() - .iter() - .map(|(k, _v)| k.clone()) - .collect(); - Ok(vm.ctx.new_list(keys)) - } - - fn set_attr(self, attr: PyStringRef, value: PyObjectRef, vm: &VirtualMachine) { - self.dict.set_item(&vm.ctx, &attr.value, value) + if let Some(dict) = &self.into_object().dict { + let keys = dict + .get_key_value_pairs() + .iter() + .map(|(k, _v)| k.clone()) + .collect(); + Ok(vm.ctx.new_list(keys)) + } else { + panic!("Modules should definitely have a dict."); + } } } pub fn init(context: &PyContext) { extend_class!(&context, &context.module_type, { "__dir__" => context.new_rustfunc(PyModuleRef::dir), - "__setattr__" => context.new_rustfunc(PyModuleRef::set_attr) }); } diff --git a/vm/src/obj/objnone.rs b/vm/src/obj/objnone.rs index 7b0eefec21..aff20f465f 100644 --- a/vm/src/obj/objnone.rs +++ b/vm/src/obj/objnone.rs @@ -6,13 +6,13 @@ use crate::pyobject::{ }; use crate::vm::VirtualMachine; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyNone; pub type PyNoneRef = PyRef; impl PyValue for PyNone { fn class(vm: &VirtualMachine) -> PyClassRef { - vm.ctx.none().type_pyref() + vm.ctx.none().class() } } @@ -44,7 +44,7 @@ impl PyNoneRef { fn get_attribute(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult { trace!("None.__getattribute__({:?}, {:?})", self, name); - let cls = self.typ(); + let cls = self.class(); // Properties use a comparision with None to determine if they are either invoked by am // instance binding or a class binding. But if the object itself is None then this detection @@ -69,7 +69,7 @@ impl PyNoneRef { } if let Some(attr) = class_get_attr(&cls, &name.value) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if class_has_attr(&attr_class, "__set__") { if let Some(get_func) = class_get_attr(&attr_class, "__get__") { return call_descriptor( @@ -88,7 +88,7 @@ impl PyNoneRef { // Ok(obj_attr) // } else if let Some(attr) = class_get_attr(&cls, &name.value) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if let Some(get_func) = class_get_attr(&attr_class, "__get__") { call_descriptor(attr, get_func, self.into_object(), cls.into_object(), vm) } else { @@ -107,7 +107,7 @@ fn none_new(_: PyClassRef, vm: &VirtualMachine) -> PyNoneRef { } pub fn init(context: &PyContext) { - extend_class!(context, &context.none.typ(), { + extend_class!(context, &context.none.class(), { "__new__" => context.new_rustfunc(none_new), "__repr__" => context.new_rustfunc(PyNoneRef::repr), "__bool__" => context.new_rustfunc(PyNoneRef::bool), diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index e0ff9b20cd..74ba2dc3d4 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,5 +1,5 @@ +use super::objdict::{self, PyDictRef}; use super::objlist::PyList; -use super::objmodule::PyModule; use super::objstr::{self, PyStringRef}; use super::objtype; use crate::function::PyFuncArgs; @@ -7,11 +7,11 @@ use crate::obj::objproperty::PropertyBuilder; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ DictProtocol, IdProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, PyResult, PyValue, - TypeProtocol, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyInstance; impl PyValue for PyInstance { @@ -22,12 +22,13 @@ impl PyValue for PyInstance { pub fn new_instance(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator - let cls = args.shift(); - Ok(if cls.is(&vm.ctx.object) { - PyObject::new_without_dict(PyInstance, cls) + let cls = PyClassRef::try_from_object(vm, args.shift())?; + let dict = if cls.is(&vm.ctx.object) { + None } else { - PyObject::new(PyInstance, cls) - }) + Some(vm.ctx.new_dict()) + }; + Ok(PyObject::new(PyInstance, cls, dict)) } fn object_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -103,10 +104,10 @@ fn object_setattr( vm: &VirtualMachine, ) -> PyResult<()> { trace!("object.__setattr__({:?}, {}, {:?})", obj, attr_name, value); - let cls = obj.type_pyref(); + let cls = obj.class(); if let Some(attr) = objtype::class_get_attr(&cls, &attr_name.value) { - if let Some(descriptor) = objtype::class_get_attr(&attr.type_pyref(), "__set__") { + if let Some(descriptor) = objtype::class_get_attr(&attr.class(), "__set__") { return vm .invoke(descriptor, vec![attr, obj.clone(), value]) .map(|_| ()); @@ -114,34 +115,34 @@ fn object_setattr( } if let Some(ref dict) = obj.clone().dict { - dict.borrow_mut().insert(attr_name.value.clone(), value); + dict.set_item(&vm.ctx, &attr_name.value, value); Ok(()) } else { - let type_name = objtype::get_type_name(obj.type_ref()); Err(vm.new_attribute_error(format!( "'{}' object has no attribute '{}'", - type_name, &attr_name.value + obj.class().name, + &attr_name.value ))) } } fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) -> PyResult<()> { - let cls = obj.type_pyref(); + let cls = obj.class(); if let Some(attr) = objtype::class_get_attr(&cls, &attr_name.value) { - if let Some(descriptor) = objtype::class_get_attr(&attr.type_pyref(), "__delete__") { + if let Some(descriptor) = objtype::class_get_attr(&attr.class(), "__delete__") { return vm.invoke(descriptor, vec![attr, obj.clone()]).map(|_| ()); } } if let Some(ref dict) = obj.dict { - dict.borrow_mut().remove(&attr_name.value); + dict.del_item(&attr_name.value); Ok(()) } else { - let type_name = objtype::get_type_name(obj.type_ref()); Err(vm.new_attribute_error(format!( "'{}' object has no attribute '{}'", - type_name, &attr_name.value + obj.class().name, + &attr_name.value ))) } } @@ -153,9 +154,8 @@ fn object_str(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { fn object_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.object()))]); - let type_name = objtype::get_type_name(&obj.typ()); let address = obj.get_id(); - Ok(vm.new_str(format!("<{} object at 0x{:x}>", type_name, address))) + Ok(vm.new_str(format!("<{} object at 0x{:x}>", obj.class().name, address))) } pub fn object_dir(obj: PyObjectRef, vm: &VirtualMachine) -> PyList { @@ -215,7 +215,7 @@ fn object_init(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult { } fn object_class(obj: PyObjectRef, _vm: &VirtualMachine) -> PyObjectRef { - obj.typ() + obj.class().into_object() } fn object_class_setter( @@ -223,17 +223,13 @@ fn object_class_setter( _value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult { - let type_repr = vm.to_pystr(&instance.typ())?; + let type_repr = vm.to_pystr(&instance.class())?; Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) } -fn object_dict(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - if let Some(ref dict) = args.args[0].dict { - let new_dict = vm.new_dict(); - for (attr, value) in dict.borrow().iter() { - new_dict.set_item(&vm.ctx, &attr, value.clone()); - } - Ok(new_dict) +fn object_dict(object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Some(ref dict) = object.dict { + Ok(dict.clone()) } else { Err(vm.new_type_error("TypeError: no dictionary.".to_string())) } @@ -250,10 +246,10 @@ fn object_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ); let name = objstr::get_value(&name_str); trace!("object.__getattribute__({:?}, {:?})", obj, name); - let cls = obj.type_pyref(); + let cls = obj.class(); if let Some(attr) = objtype::class_get_attr(&cls, &name) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if objtype::class_has_attr(&attr_class, "__set__") { if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") { return vm.invoke(descriptor, vec![attr, obj.clone(), cls.into_object()]); @@ -273,15 +269,8 @@ fn object_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } fn object_getattr(obj: &PyObjectRef, attr_name: &str) -> Option { - // TODO: - // This is an all kinds of wrong work-around for the temporary difference in - // shape between modules and object. It will disappear once that is fixed. - if let Some(PyModule { ref dict, .. }) = obj.payload::() { - return dict.get_item(attr_name); - } - if let Some(ref dict) = obj.dict { - dict.borrow().get(attr_name).cloned() + dict.get_item(attr_name) } else { None } @@ -289,12 +278,12 @@ fn object_getattr(obj: &PyObjectRef, attr_name: &str) -> Option { pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Get class attributes: - let mut attributes = objtype::get_attributes(obj.type_pyref()); + let mut attributes = objtype::get_attributes(obj.class()); // Get instance attributes: if let Some(dict) = &obj.dict { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); + for (key, value) in objdict::get_key_value_pairs(dict.as_object()) { + attributes.insert(key.to_string(), value.clone()); } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index 7a9dd8447e..9d72171106 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -6,7 +6,9 @@ use crate::function::IntoPyNativeFunc; use crate::function::OptionalArg; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{IdProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{ + IdProtocol, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, +}; use crate::vm::VirtualMachine; /// Read-only property, doesn't have __set__ or __delete__ @@ -137,7 +139,7 @@ impl PyPropertyRef { setter: self.setter.clone(), deleter: self.deleter.clone(), } - .into_ref_with_type(vm, self.typ()) + .into_ref_with_type(vm, TypeProtocol::class(&self)) } fn setter(self, setter: Option, vm: &VirtualMachine) -> PyResult { @@ -146,7 +148,7 @@ impl PyPropertyRef { setter: setter.or_else(|| self.setter.clone()), deleter: self.deleter.clone(), } - .into_ref_with_type(vm, self.typ()) + .into_ref_with_type(vm, TypeProtocol::class(&self)) } fn deleter(self, deleter: Option, vm: &VirtualMachine) -> PyResult { @@ -155,7 +157,7 @@ impl PyPropertyRef { setter: self.setter.clone(), deleter: deleter.or_else(|| self.deleter.clone()), } - .into_ref_with_type(vm, self.typ()) + .into_ref_with_type(vm, TypeProtocol::class(&self)) } } @@ -200,7 +202,7 @@ impl<'a> PropertyBuilder<'a> { deleter: None, }; - PyObject::new(payload, self.ctx.property_type().into_object()) + PyObject::new(payload, self.ctx.property_type(), None) } else { let payload = PyReadOnlyProperty { getter: self.getter.expect( @@ -208,7 +210,7 @@ impl<'a> PropertyBuilder<'a> { ), }; - PyObject::new(payload, self.ctx.readonly_property_type().into_object()) + PyObject::new(payload, self.ctx.readonly_property_type(), None) } } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index db4ded48dd..04c9c273b1 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -3,26 +3,21 @@ use std::ops::Mul; use num_bigint::{BigInt, Sign}; use num_integer::Integer; -use num_traits::{One, Signed, ToPrimitive, Zero}; +use num_traits::{One, Signed, Zero}; use crate::function::{OptionalArg, PyFuncArgs}; -use crate::pyobject::{ - PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{Either, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; -use super::objint::{self, PyInt, PyIntRef}; -use super::objslice::PySlice; -use super::objtype; +use super::objint::{PyInt, PyIntRef}; +use super::objslice::PySliceRef; use super::objtype::PyClassRef; #[derive(Debug, Clone)] pub struct PyRange { - // Unfortunately Rust's built in range type doesn't support things like indexing - // or ranges where start > end so we need to roll our own. - pub start: BigInt, - pub stop: BigInt, - pub step: BigInt, + pub start: PyIntRef, + pub stop: PyIntRef, + pub step: PyIntRef, } impl PyValue for PyRange { @@ -32,71 +27,38 @@ impl PyValue for PyRange { } impl PyRange { - #[inline] - pub fn try_len(&self) -> Option { - match self.step.sign() { - Sign::Plus if self.start < self.stop => ((&self.stop - &self.start - 1usize) - / &self.step) - .to_usize() - .map(|sz| sz + 1), - Sign::Minus if self.start > self.stop => ((&self.start - &self.stop - 1usize) - / (-&self.step)) - .to_usize() - .map(|sz| sz + 1), - _ => Some(0), - } - } - - #[inline] - pub fn len(&self) -> usize { - self.try_len().unwrap() - } - #[inline] fn offset(&self, value: &BigInt) -> Option { - match self.step.sign() { - Sign::Plus if *value >= self.start && *value < self.stop => Some(value - &self.start), - Sign::Minus if *value <= self.start && *value > self.stop => Some(&self.start - value), + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + match step.sign() { + Sign::Plus if value >= start && value < stop => Some(value - start), + Sign::Minus if value <= self.start.as_bigint() && value > stop => Some(start - value), _ => None, } } - #[inline] - pub fn contains(&self, value: &BigInt) -> bool { - match self.offset(value) { - Some(ref offset) => offset.is_multiple_of(&self.step), - None => false, - } - } - #[inline] pub fn index_of(&self, value: &BigInt) -> Option { + let step = self.step.as_bigint(); match self.offset(value) { - Some(ref offset) if offset.is_multiple_of(&self.step) => { - Some((offset / &self.step).abs()) - } + Some(ref offset) if offset.is_multiple_of(step) => Some((offset / step).abs()), Some(_) | None => None, } } - #[inline] - pub fn count(&self, value: &BigInt) -> usize { - if self.index_of(value).is_some() { - 1 - } else { - 0 - } - } - #[inline] pub fn is_empty(&self) -> bool { - (self.start <= self.stop && self.step.is_negative()) - || (self.start >= self.stop && self.step.is_positive()) + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + (start <= stop && step.is_negative()) || (start >= stop && step.is_positive()) } #[inline] pub fn forward(&self) -> bool { - self.start < self.stop + self.start.as_bigint() < self.stop.as_bigint() } #[inline] @@ -104,50 +66,20 @@ impl PyRange { where &'a BigInt: Mul, { - let result = &self.start + &self.step * index; + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); - if (self.forward() && !self.is_empty() && result < self.stop) - || (!self.forward() && !self.is_empty() && result > self.stop) + let result = start + step * index; + + if (self.forward() && !self.is_empty() && &result < stop) + || (!self.forward() && !self.is_empty() && &result > stop) { Some(result) } else { None } } - - #[inline] - pub fn reversed(&self) -> Self { - // compute the last element that is actually contained within the range - // this is the new start - let remainder = ((&self.stop - &self.start) % &self.step).abs(); - let start = if remainder.is_zero() { - &self.stop - &self.step - } else { - &self.stop - &remainder - }; - - match self.step.sign() { - Sign::Plus => PyRange { - start, - stop: &self.start - 1, - step: -&self.step, - }, - Sign::Minus => PyRange { - start, - stop: &self.start + 1, - step: -&self.step, - }, - Sign::NoSign => unreachable!(), - } - } - - pub fn repr(&self) -> String { - if self.step == BigInt::one() { - format!("range({}, {})", self.start, self.stop) - } else { - format!("range({}, {}, {})", self.start, self.stop, self.step) - } - } } pub fn get_value(obj: &PyObjectRef) -> PyRange { @@ -166,20 +98,20 @@ pub fn init(context: &PyContext) { When step is given, it specifies the increment (or decrement)."; extend_class!(context, range_type, { - "__bool__" => context.new_rustfunc(range_bool), - "__contains__" => context.new_rustfunc(range_contains), + "__bool__" => context.new_rustfunc(PyRangeRef::bool), + "__contains__" => context.new_rustfunc(PyRangeRef::contains), "__doc__" => context.new_str(range_doc.to_string()), - "__getitem__" => context.new_rustfunc(range_getitem), - "__iter__" => context.new_rustfunc(range_iter), - "__len__" => context.new_rustfunc(range_len), + "__getitem__" => context.new_rustfunc(PyRangeRef::getitem), + "__iter__" => context.new_rustfunc(PyRangeRef::iter), + "__len__" => context.new_rustfunc(PyRangeRef::len), "__new__" => context.new_rustfunc(range_new), - "__repr__" => context.new_rustfunc(range_repr), - "__reversed__" => context.new_rustfunc(range_reversed), - "count" => context.new_rustfunc(range_count), - "index" => context.new_rustfunc(range_index), - "start" => context.new_property(range_start), - "step" => context.new_property(range_step), - "stop" => context.new_property(range_stop) + "__repr__" => context.new_rustfunc(PyRangeRef::repr), + "__reversed__" => context.new_rustfunc(PyRangeRef::reversed), + "count" => context.new_rustfunc(PyRangeRef::count), + "index" => context.new_rustfunc(PyRangeRef::index), + "start" => context.new_property(PyRangeRef::start), + "stop" => context.new_property(PyRangeRef::stop), + "step" => context.new_property(PyRangeRef::step), }); } @@ -188,9 +120,9 @@ type PyRangeRef = PyRef; impl PyRangeRef { fn new(cls: PyClassRef, stop: PyIntRef, vm: &VirtualMachine) -> PyResult { PyRange { - start: Zero::zero(), - stop: stop.value.clone(), - step: One::one(), + start: PyInt::new(BigInt::zero()).into_ref(vm), + stop: stop.clone(), + step: PyInt::new(BigInt::one()).into_ref(vm), } .into_ref_with_type(vm, cls) } @@ -203,196 +135,181 @@ impl PyRangeRef { vm: &VirtualMachine, ) -> PyResult { PyRange { - start: start.value.clone(), - stop: stop.value.clone(), + start: start, + stop: stop, step: step .into_option() - .map(|i| i.value.clone()) - .unwrap_or_else(One::one), + .unwrap_or_else(|| PyInt::new(BigInt::one()).into_ref(vm)), } .into_ref_with_type(vm, cls) } -} - -fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let range = if args.args.len() <= 2 { - let (cls, stop) = args.bind(vm)?; - PyRangeRef::new(cls, stop, vm) - } else { - let (cls, start, stop, step) = args.bind(vm)?; - PyRangeRef::new_from(cls, start, stop, step, vm) - }?; - - Ok(range.into_object()) -} -fn range_iter(range: PyRangeRef, _vm: &VirtualMachine) -> PyIteratorValue { - PyIteratorValue { - position: Cell::new(0), - iterated_obj: range.into_object(), + fn start(self, _vm: &VirtualMachine) -> PyIntRef { + self.start.clone() } -} - -fn range_reversed(zelf: PyRangeRef, vm: &VirtualMachine) -> PyIteratorValue { - let range = zelf.reversed(); - PyIteratorValue { - position: Cell::new(0), - iterated_obj: range.into_ref(vm).into_object(), + fn stop(self, _vm: &VirtualMachine) -> PyIntRef { + self.stop.clone() } -} - -fn range_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - if let Some(len) = get_value(zelf).try_len() { - Ok(vm.ctx.new_int(len)) - } else { - Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())) + fn step(self, _vm: &VirtualMachine) -> PyIntRef { + self.step.clone() } -} -fn range_getitem(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (subscript, None)] - ); + fn iter(self: PyRangeRef, _vm: &VirtualMachine) -> PyIteratorValue { + PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + } + } - let range = get_value(zelf); + fn reversed(self: PyRangeRef, vm: &VirtualMachine) -> PyIteratorValue { + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); - if let Some(i) = subscript.payload::() { - if let Some(int) = range.get(i.value.clone()) { - Ok(vm.ctx.new_int(int)) - } else { - Err(vm.new_index_error("range object index out of range".to_string())) - } - } else if let Some(PySlice { - ref start, - ref stop, - ref step, - }) = subscript.payload() - { - let new_start = if let Some(int) = start { - if let Some(i) = range.get(int) { - i - } else { - range.start.clone() - } + // compute the last element that is actually contained within the range + // this is the new start + let remainder = ((stop - start) % step).abs(); + let new_start = if remainder.is_zero() { + stop - step } else { - range.start.clone() + stop - &remainder }; - let new_end = if let Some(int) = stop { - if let Some(i) = range.get(int) { - i - } else { - range.stop - } - } else { - range.stop + let new_stop: BigInt = match step.sign() { + Sign::Plus => start - 1, + Sign::Minus => start + 1, + Sign::NoSign => unreachable!(), }; - let new_step = if let Some(int) = step { - int * range.step - } else { - range.step + let reversed = PyRange { + start: PyInt::new(new_start).into_ref(vm), + stop: PyInt::new(new_stop).into_ref(vm), + step: PyInt::new(-step).into_ref(vm), }; - Ok(PyRange { - start: new_start, - stop: new_end, - step: new_step, + PyIteratorValue { + position: Cell::new(0), + iterated_obj: reversed.into_ref(vm).into_object(), } - .into_ref(vm) - .into_object()) - } else { - Err(vm.new_type_error("range indices must be integer or slice".to_string())) } -} - -fn range_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - - let repr = get_value(zelf).repr(); - - Ok(vm.ctx.new_str(repr)) -} - -fn range_bool(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - - let len = get_value(zelf).len(); - Ok(vm.ctx.new_bool(len > 0)) -} - -fn range_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] - ); - - let range = get_value(zelf); - - let result = if objtype::isinstance(needle, &vm.ctx.int_type()) { - range.contains(&objint::get_value(needle)) - } else { - false - }; - - Ok(vm.ctx.new_bool(result)) -} + fn len(self, _vm: &VirtualMachine) -> PyInt { + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); -fn range_index(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] - ); + match step.sign() { + Sign::Plus if start < stop => PyInt::new((stop - start - 1usize) / step + 1), + Sign::Minus if start > stop => PyInt::new((start - stop - 1usize) / (-step) + 1), + Sign::Plus | Sign::Minus => PyInt::new(0), + Sign::NoSign => unreachable!(), + } + } - let range = get_value(zelf); + fn repr(self, _vm: &VirtualMachine) -> String { + if self.step.as_bigint().is_one() { + format!("range({}, {})", self.start, self.stop) + } else { + format!("range({}, {}, {})", self.start, self.stop, self.step) + } + } - if objtype::isinstance(needle, &vm.ctx.int_type()) { - let needle = objint::get_value(needle); + fn bool(self, _vm: &VirtualMachine) -> bool { + !self.is_empty() + } - match range.index_of(&needle) { - Some(idx) => Ok(vm.ctx.new_int(idx)), - None => Err(vm.new_value_error(format!("{} is not in range", needle))), + fn contains(self, needle: PyObjectRef, _vm: &VirtualMachine) -> bool { + if let Ok(int) = needle.downcast::() { + match self.offset(int.as_bigint()) { + Some(ref offset) => offset.is_multiple_of(self.step.as_bigint()), + None => false, + } + } else { + false } - } else { - Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())) } -} - -fn range_count(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (item, None)] - ); - let range = get_value(zelf); + fn index(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Ok(int) = needle.downcast::() { + match self.index_of(int.as_bigint()) { + Some(idx) => Ok(PyInt::new(idx)), + None => Err(vm.new_value_error(format!("{} is not in range", int))), + } + } else { + Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())) + } + } - if objtype::isinstance(item, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(range.count(&objint::get_value(item)))) - } else { - Ok(vm.ctx.new_int(0)) + fn count(self, item: PyObjectRef, _vm: &VirtualMachine) -> PyInt { + if let Ok(int) = item.downcast::() { + if self.index_of(int.as_bigint()).is_some() { + PyInt::new(1) + } else { + PyInt::new(0) + } + } else { + PyInt::new(0) + } } -} -fn range_start(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - Ok(vm.ctx.new_int(get_value(zelf).start)) + fn getitem(self, subscript: Either, vm: &VirtualMachine) -> PyResult { + match subscript { + Either::A(index) => { + if let Some(value) = self.get(index.as_bigint()) { + Ok(PyInt::new(value).into_ref(vm).into_object()) + } else { + Err(vm.new_index_error("range object index out of range".to_string())) + } + } + Either::B(slice) => { + let new_start = if let Some(int) = slice.start.as_ref() { + if let Some(i) = self.get(int) { + PyInt::new(i).into_ref(vm) + } else { + self.start.clone() + } + } else { + self.start.clone() + }; + + let new_end = if let Some(int) = slice.stop.as_ref() { + if let Some(i) = self.get(int) { + PyInt::new(i).into_ref(vm) + } else { + self.stop.clone() + } + } else { + self.stop.clone() + }; + + let new_step = if let Some(int) = slice.step.as_ref() { + PyInt::new(int * self.step.as_bigint()).into_ref(vm) + } else { + self.step.clone() + }; + + Ok(PyRange { + start: new_start, + stop: new_end, + step: new_step, + } + .into_ref(vm) + .into_object()) + } + } + } } -fn range_stop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - Ok(vm.ctx.new_int(get_value(zelf).stop)) -} +fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { + let range = if args.args.len() <= 2 { + let (cls, stop) = args.bind(vm)?; + PyRangeRef::new(cls, stop, vm) + } else { + let (cls, start, stop, step) = args.bind(vm)?; + PyRangeRef::new_from(cls, start, stop, step, vm) + }?; -fn range_step(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - Ok(vm.ctx.new_int(get_value(zelf).step)) + Ok(range.into_object()) } diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index ad9c7bf361..a07b2454de 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -144,7 +144,7 @@ pub fn get_item( subscript: PyObjectRef, ) -> PyResult { if let Some(i) = subscript.payload::() { - return match i.value.to_i32() { + return match i.as_bigint().to_i32() { Some(value) => { if let Some(pos_index) = elements.to_vec().get_pos(value) { let obj = elements[pos_index].clone(); @@ -163,12 +163,14 @@ pub fn get_item( if sequence.payload::().is_some() { Ok(PyObject::new( PyList::from(elements.to_vec().get_slice_items(vm, &subscript)?), - sequence.typ(), + sequence.class(), + None, )) } else if sequence.payload::().is_some() { Ok(PyObject::new( PyTuple::from(elements.to_vec().get_slice_items(vm, &subscript)?), - sequence.typ(), + sequence.class(), + None, )) } else { panic!("sequence get_item called for non-sequence") diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 33a4455318..ad82c34de0 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -9,13 +9,14 @@ use std::hash::{Hash, Hasher}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use super::objbool; use super::objint; use super::objiter; +use super::objtype; use super::objtype::PyClassRef; #[derive(Default)] @@ -24,6 +25,12 @@ pub struct PySet { } pub type PySetRef = PyRef; +#[derive(Default)] +pub struct PyFrozenSet { + elements: HashMap, +} +pub type PyFrozenSetRef = PyRef; + impl fmt::Debug for PySet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // TODO: implement more detailed, non-recursive Debug formatter @@ -31,14 +38,65 @@ impl fmt::Debug for PySet { } } +impl fmt::Debug for PyFrozenSet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more detailed, non-recursive Debug formatter + f.write_str("frozenset") + } +} + impl PyValue for PySet { fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.set_type() } } +impl PyValue for PyFrozenSet { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.ctx.frozenset_type() + } +} + pub fn get_elements(obj: &PyObjectRef) -> HashMap { - obj.payload::().unwrap().elements.borrow().clone() + if let Some(set) = obj.payload::() { + return set.elements.borrow().clone(); + } else if let Some(frozenset) = obj.payload::() { + return frozenset.elements.clone(); + } + panic!("Not frozenset or set"); +} + +fn validate_set_or_frozenset(vm: &VirtualMachine, cls: PyClassRef) -> PyResult<()> { + if !(objtype::issubclass(&cls, &vm.ctx.set_type()) + || objtype::issubclass(&cls, &vm.ctx.frozenset_type())) + { + return Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))); + } + Ok(()) +} + +fn create_set( + vm: &VirtualMachine, + elements: HashMap, + cls: PyClassRef, +) -> PyResult { + if objtype::issubclass(&cls, &vm.ctx.set_type()) { + Ok(PyObject::new( + PySet { + elements: RefCell::new(elements), + }, + PySet::class(vm), + None, + )) + } else if objtype::issubclass(&cls, &vm.ctx.frozenset_type()) { + Ok(PyObject::new( + PyFrozenSet { elements: elements }, + PyFrozenSet::class(vm), + None, + )) + } else { + Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))) + } } fn perform_action_with_hash( @@ -152,11 +210,9 @@ fn set_clear(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } /* Create a new object of sub-type of set */ -fn set_new( - cls: PyClassRef, - iterable: OptionalArg, - vm: &VirtualMachine, -) -> PyResult { +fn set_new(cls: PyClassRef, iterable: OptionalArg, vm: &VirtualMachine) -> PyResult { + validate_set_or_frozenset(vm, cls.clone())?; + let elements: HashMap = match iterable { OptionalArg::Missing => HashMap::new(), OptionalArg::Present(iterable) => { @@ -169,25 +225,22 @@ fn set_new( } }; - PySet { - elements: RefCell::new(elements), - } - .into_ref_with_type(vm, cls) + create_set(vm, elements, cls.clone()) } fn set_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.len called with: {:?}", args); - arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); + arg_check!(vm, args, required = [(s, None)]); + validate_set_or_frozenset(vm, s.class())?; let elements = get_elements(s); Ok(vm.context().new_int(elements.len())) } -fn set_copy(obj: PySetRef, _vm: &VirtualMachine) -> PySet { +fn set_copy(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { trace!("set.copy called with: {:?}", obj); - let elements = obj.elements.borrow().clone(); - PySet { - elements: RefCell::new(elements), - } + validate_set_or_frozenset(vm, obj.class())?; + let elements = get_elements(&obj).clone(); + create_set(vm, elements, obj.class()) } fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -211,11 +264,8 @@ fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn set_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(set, Some(vm.ctx.set_type())), (needle, None)] - ); + arg_check!(vm, args, required = [(set, None), (needle, None)]); + validate_set_or_frozenset(vm, set.class())?; for element in get_elements(set).iter() { match vm._eq(needle.clone(), element.1.clone()) { Ok(value) => { @@ -281,14 +331,10 @@ fn set_compare_inner( size_func: &Fn(usize, usize) -> bool, swap: bool, ) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); + arg_check!(vm, args, required = [(zelf, None), (other, None)]); + + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; let get_zelf = |swap: bool| -> &PyObjectRef { if swap { @@ -323,47 +369,48 @@ fn set_compare_inner( Ok(vm.new_bool(true)) } -fn set_union(zelf: PySetRef, other: PySetRef, _vm: &VirtualMachine) -> PySet { - let mut elements = zelf.elements.borrow().clone(); - elements.extend(other.elements.borrow().clone()); +fn set_union(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; - PySet { - elements: RefCell::new(elements), - } + let mut elements = get_elements(&zelf).clone(); + elements.extend(get_elements(&other).clone()); + + create_set(vm, elements, zelf.class()) } -fn set_intersection(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { +fn set_intersection(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Intersection) } -fn set_difference(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { +fn set_difference(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Difference) } fn set_symmetric_difference( - zelf: PySetRef, - other: PySetRef, + zelf: PyObjectRef, + other: PyObjectRef, vm: &VirtualMachine, -) -> PyResult { +) -> PyResult { + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; let mut elements = HashMap::new(); - for element in zelf.elements.borrow().iter() { - let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&zelf).iter() { + let value = vm.call_method(&other, "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - for element in other.elements.borrow().iter() { - let value = vm.call_method(zelf.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&other).iter() { + let value = vm.call_method(&zelf, "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - Ok(PySet { - elements: RefCell::new(elements), - }) + create_set(vm, elements, zelf.class()) } enum SetCombineOperation { @@ -372,15 +419,17 @@ enum SetCombineOperation { } fn set_combine_inner( - zelf: PySetRef, - other: PySetRef, + zelf: PyObjectRef, + other: PyObjectRef, vm: &VirtualMachine, op: SetCombineOperation, -) -> PyResult { +) -> PyResult { + validate_set_or_frozenset(vm, zelf.class())?; + validate_set_or_frozenset(vm, other.class())?; let mut elements = HashMap::new(); - for element in zelf.elements.borrow().iter() { - let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&zelf).iter() { + let value = vm.call_method(&other, "__contains__", vec![element.1.clone()])?; let should_add = match op { SetCombineOperation::Intersection => objbool::get_value(&value), SetCombineOperation::Difference => !objbool::get_value(&value), @@ -390,9 +439,7 @@ fn set_combine_inner( } } - Ok(PySet { - elements: RefCell::new(elements), - }) + create_set(vm, elements, zelf.class()) } fn set_pop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -548,65 +595,43 @@ pub fn init(context: &PyContext) { set(iterable) -> new set object\n\n\ Build an unordered collection of unique elements."; - context.set_attr(set_type, "__contains__", context.new_rustfunc(set_contains)); - context.set_attr(set_type, "__len__", context.new_rustfunc(set_len)); - context.set_attr(set_type, "__new__", context.new_rustfunc(set_new)); - context.set_attr(set_type, "__repr__", context.new_rustfunc(set_repr)); - context.set_attr(set_type, "__eq__", context.new_rustfunc(set_eq)); - context.set_attr(set_type, "__ge__", context.new_rustfunc(set_ge)); - context.set_attr(set_type, "__gt__", context.new_rustfunc(set_gt)); - context.set_attr(set_type, "__le__", context.new_rustfunc(set_le)); - context.set_attr(set_type, "__lt__", context.new_rustfunc(set_lt)); - context.set_attr(set_type, "issubset", context.new_rustfunc(set_le)); - context.set_attr(set_type, "issuperset", context.new_rustfunc(set_ge)); - context.set_attr(set_type, "union", context.new_rustfunc(set_union)); - context.set_attr(set_type, "__or__", context.new_rustfunc(set_union)); - context.set_attr( - set_type, - "intersection", - context.new_rustfunc(set_intersection), - ); - context.set_attr(set_type, "__and__", context.new_rustfunc(set_intersection)); - context.set_attr(set_type, "difference", context.new_rustfunc(set_difference)); - context.set_attr(set_type, "__sub__", context.new_rustfunc(set_difference)); - context.set_attr( - set_type, - "symmetric_difference", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr( - set_type, - "__xor__", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr(set_type, "__doc__", context.new_str(set_doc.to_string())); - context.set_attr(set_type, "add", context.new_rustfunc(set_add)); - context.set_attr(set_type, "remove", context.new_rustfunc(set_remove)); - context.set_attr(set_type, "discard", context.new_rustfunc(set_discard)); - context.set_attr(set_type, "clear", context.new_rustfunc(set_clear)); - context.set_attr(set_type, "copy", context.new_rustfunc(set_copy)); - context.set_attr(set_type, "pop", context.new_rustfunc(set_pop)); - context.set_attr(set_type, "update", context.new_rustfunc(set_update)); - context.set_attr(set_type, "__ior__", context.new_rustfunc(set_ior)); - context.set_attr( - set_type, - "intersection_update", - context.new_rustfunc(set_intersection_update), - ); - context.set_attr(set_type, "__iand__", context.new_rustfunc(set_iand)); - context.set_attr( - set_type, - "difference_update", - context.new_rustfunc(set_difference_update), - ); - context.set_attr(set_type, "__isub__", context.new_rustfunc(set_isub)); - context.set_attr( - set_type, - "symmetric_difference_update", - context.new_rustfunc(set_symmetric_difference_update), - ); - context.set_attr(set_type, "__ixor__", context.new_rustfunc(set_ixor)); - context.set_attr(set_type, "__iter__", context.new_rustfunc(set_iter)); + extend_class!(context, set_type, { + "__contains__" => context.new_rustfunc(set_contains), + "__len__" => context.new_rustfunc(set_len), + "__new__" => context.new_rustfunc(set_new), + "__repr__" => context.new_rustfunc(set_repr), + "__eq__" => context.new_rustfunc(set_eq), + "__ge__" => context.new_rustfunc(set_ge), + "__gt__" => context.new_rustfunc(set_gt), + "__le__" => context.new_rustfunc(set_le), + "__lt__" => context.new_rustfunc(set_lt), + "issubset" => context.new_rustfunc(set_le), + "issuperset" => context.new_rustfunc(set_ge), + "union" => context.new_rustfunc(set_union), + "__or__" => context.new_rustfunc(set_union), + "intersection" => context.new_rustfunc(set_intersection), + "__and__" => context.new_rustfunc(set_intersection), + "difference" => context.new_rustfunc(set_difference), + "__sub__" => context.new_rustfunc(set_difference), + "symmetric_difference" => context.new_rustfunc(set_symmetric_difference), + "__xor__" => context.new_rustfunc(set_symmetric_difference), + "__doc__" => context.new_str(set_doc.to_string()), + "add" => context.new_rustfunc(set_add), + "remove" => context.new_rustfunc(set_remove), + "discard" => context.new_rustfunc(set_discard), + "clear" => context.new_rustfunc(set_clear), + "copy" => context.new_rustfunc(set_copy), + "pop" => context.new_rustfunc(set_pop), + "update" => context.new_rustfunc(set_update), + "__ior__" => context.new_rustfunc(set_ior), + "intersection_update" => context.new_rustfunc(set_intersection_update), + "__iand__" => context.new_rustfunc(set_iand), + "difference_update" => context.new_rustfunc(set_difference_update), + "__isub__" => context.new_rustfunc(set_isub), + "symmetric_difference_update" => context.new_rustfunc(set_symmetric_difference_update), + "__ixor__" => context.new_rustfunc(set_ixor), + "__iter__" => context.new_rustfunc(set_iter) + }); let frozenset_type = &context.frozenset_type; @@ -614,20 +639,27 @@ pub fn init(context: &PyContext) { frozenset(iterable) -> frozenset object\n\n\ Build an immutable unordered collection of unique elements."; - context.set_attr( - frozenset_type, - "__contains__", - context.new_rustfunc(set_contains), - ); - context.set_attr(frozenset_type, "__len__", context.new_rustfunc(set_len)); - context.set_attr( - frozenset_type, - "__doc__", - context.new_str(frozenset_doc.to_string()), - ); - context.set_attr( - frozenset_type, - "__repr__", - context.new_rustfunc(frozenset_repr), - ); + extend_class!(context, frozenset_type, { + "__new__" => context.new_rustfunc(set_new), + "__eq__" => context.new_rustfunc(set_eq), + "__ge__" => context.new_rustfunc(set_ge), + "__gt__" => context.new_rustfunc(set_gt), + "__le__" => context.new_rustfunc(set_le), + "__lt__" => context.new_rustfunc(set_lt), + "issubset" => context.new_rustfunc(set_le), + "issuperset" => context.new_rustfunc(set_ge), + "union" => context.new_rustfunc(set_union), + "__or__" => context.new_rustfunc(set_union), + "intersection" => context.new_rustfunc(set_intersection), + "__and__" => context.new_rustfunc(set_intersection), + "difference" => context.new_rustfunc(set_difference), + "__sub__" => context.new_rustfunc(set_difference), + "symmetric_difference" => context.new_rustfunc(set_symmetric_difference), + "__xor__" => context.new_rustfunc(set_symmetric_difference), + "__contains__" => context.new_rustfunc(set_contains), + "__len__" => context.new_rustfunc(set_len), + "__doc__" => context.new_str(frozenset_doc.to_string()), + "__repr__" => context.new_rustfunc(frozenset_repr), + "copy" => context.new_rustfunc(set_copy) + }); } diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 4a7124fdfd..3d955b3f2c 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -1,7 +1,7 @@ use num_bigint::BigInt; use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use super::objint; @@ -21,6 +21,8 @@ impl PyValue for PySlice { } } +pub type PySliceRef = PyRef; + fn slice_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { no_kwargs!(vm, args); let (cls, start, stop, step): ( @@ -55,14 +57,13 @@ fn slice_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok((cls, Some(start), Some(stop), step)) } }?; - Ok(PyObject::new( - PySlice { - start: start.map(|x| objint::get_value(x).clone()), - stop: stop.map(|x| objint::get_value(x).clone()), - step: step.map(|x| objint::get_value(x).clone()), - }, - cls.clone(), - )) + PySlice { + start: start.map(|x| objint::get_value(x).clone()), + stop: stop.map(|x| objint::get_value(x).clone()), + step: step.map(|x| objint::get_value(x).clone()), + } + .into_ref_with_type(vm, cls.clone().downcast().unwrap()) + .map(|x| x.into_object()) } fn get_property_value(vm: &VirtualMachine, value: &Option) -> PyResult { diff --git a/vm/src/obj/objstr.rs b/vm/src/obj/objstr.rs index 67ee8e5e7e..aa62812a24 100644 --- a/vm/src/obj/objstr.rs +++ b/vm/src/obj/objstr.rs @@ -715,7 +715,7 @@ fn str_format(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let zelf = &args.args[0]; if !objtype::isinstance(&zelf, &vm.ctx.str_type()) { - let zelf_typ = zelf.typ(); + let zelf_typ = zelf.class(); let actual_type = vm.to_pystr(&zelf_typ)?; return Err(vm.new_type_error(format!( "descriptor 'format' requires a 'str' object but received a '{}'", @@ -738,7 +738,7 @@ fn call_object_format(vm: &VirtualMachine, argument: PyObjectRef, format_spec: & let returned_type = vm.ctx.new_str(format_spec.to_string()); let result = vm.call_method(&argument, "__format__", vec![returned_type])?; if !objtype::isinstance(&result, &vm.ctx.str_type()) { - let result_type = result.typ(); + let result_type = result.class(); let actual_type = vm.to_pystr(&result_type)?; return Err(vm.new_type_error(format!("__format__ must return a str, not {}", actual_type))); } @@ -809,7 +809,7 @@ fn str_new( OptionalArg::Present(ref input) => vm.to_str(input)?.into_object(), OptionalArg::Missing => vm.new_str("".to_string()), }; - if string.typ().is(&cls) { + if string.class().is(&cls) { TryFromObject::try_from_object(vm, string) } else { let payload = string.payload::().unwrap(); diff --git a/vm/src/obj/objsuper.rs b/vm/src/obj/objsuper.rs index 195b57d236..24424d265f 100644 --- a/vm/src/obj/objsuper.rs +++ b/vm/src/obj/objsuper.rs @@ -112,10 +112,9 @@ fn super_new( // Check type argument: if !objtype::isinstance(py_type.as_object(), &vm.get_type()) { - let type_name = objtype::get_type_name(py_type.as_object().type_ref()); return Err(vm.new_type_error(format!( "super() argument 1 must be type, not {}", - type_name + py_type.class().name ))); } @@ -123,7 +122,7 @@ fn super_new( let py_obj = if let OptionalArg::Present(obj) = py_obj { obj.clone() } else { - let frame = vm.current_frame(); + let frame = vm.current_frame().expect("no current frame for super()"); if let Some(first_arg) = frame.code.arg_names.get(0) { match vm.get_locals().get_item(first_arg) { Some(obj) => obj.clone(), diff --git a/vm/src/obj/objtuple.rs b/vm/src/obj/objtuple.rs index e5bf5c1eec..a199e34b18 100644 --- a/vm/src/obj/objtuple.rs +++ b/vm/src/obj/objtuple.rs @@ -15,7 +15,6 @@ use super::objsequence::{ }; use super::objtype::{self, PyClassRef}; -#[derive(Default)] pub struct PyTuple { // TODO: shouldn't be public // TODO: tuples are immutable, remove this RefCell diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 81f04603bb..14958300bf 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -4,21 +4,25 @@ use std::fmt; use crate::function::{Args, KwArgs, PyFuncArgs}; use crate::pyobject::{ - IdProtocol, PyAttributes, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, - TypeProtocol, + IdProtocol, PyAttributes, PyContext, PyIterable, PyObject, PyObjectRef, PyRef, PyResult, + PyValue, TypeProtocol, }; use crate::vm::VirtualMachine; use super::objdict; +use super::objdict::PyDictRef; use super::objlist::PyList; use super::objproperty::PropertyBuilder; -use super::objstr::{self, PyStringRef}; +use super::objstr::PyStringRef; use super::objtuple::PyTuple; +use super::objweakref::PyWeak; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct PyClass { pub name: String, pub mro: Vec, + pub subclasses: RefCell>, + pub attributes: RefCell, } impl fmt::Display for PyClass { @@ -35,12 +39,6 @@ impl PyValue for PyClass { } } -impl TypeProtocol for PyClassRef { - fn type_ref(&self) -> &PyObjectRef { - &self.as_object().type_ref() - } -} - struct IterMro<'a> { cls: &'a PyClassRef, offset: Option, @@ -106,17 +104,17 @@ impl PyClassRef { format!("", self.name) } - fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - vm.new_dict() + fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyDictRef { + vm.ctx.new_dict() } fn getattribute(self, name_ref: PyStringRef, vm: &VirtualMachine) -> PyResult { let name = &name_ref.value; trace!("type.__getattribute__({:?}, {:?})", self, name); - let mcl = self.type_pyref(); + let mcl = self.class(); if let Some(attr) = class_get_attr(&mcl, &name) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if class_has_attr(&attr_class, "__set__") { if let Some(descriptor) = class_get_attr(&attr_class, "__get__") { return vm.invoke( @@ -128,7 +126,7 @@ impl PyClassRef { } if let Some(attr) = class_get_attr(&self, &name) { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if let Some(descriptor) = class_get_attr(&attr_class, "__get__") { let none = vm.get_none(); return vm.invoke(descriptor, vec![attr, none, self.into_object()]); @@ -145,6 +143,36 @@ impl PyClassRef { Err(vm.new_attribute_error(format!("{} has no attribute '{}'", self, name))) } } + + fn set_attr( + self, + attr_name: PyStringRef, + value: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult<()> { + if let Some(attr) = class_get_attr(&self.class(), &attr_name.value) { + if let Some(descriptor) = class_get_attr(&attr.class(), "__set__") { + vm.invoke(descriptor, vec![attr, self.into_object(), value])?; + return Ok(()); + } + } + + self.attributes + .borrow_mut() + .insert(attr_name.to_string(), value); + Ok(()) + } + + fn subclasses(self, _vm: &VirtualMachine) -> PyList { + let mut subclasses = self.subclasses.borrow_mut(); + subclasses.retain(|x| x.upgrade().is_some()); + PyList::from( + subclasses + .iter() + .map(|x| x.upgrade().unwrap()) + .collect::>(), + ) + } } /* @@ -167,6 +195,9 @@ pub fn init(ctx: &PyContext) { "__repr__" => ctx.new_rustfunc(PyClassRef::repr), "__prepare__" => ctx.new_rustfunc(PyClassRef::prepare), "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), + "__setattr__" => ctx.new_rustfunc(PyClassRef::set_attr), + "__subclasses__" => ctx.new_rustfunc(PyClassRef::subclasses), + "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), "__instancecheck__" => ctx.new_rustfunc(PyClassRef::instance_check), "__subclasscheck__" => ctx.new_rustfunc(PyClassRef::subclass_check), "__doc__" => ctx.new_str(type_doc.to_string()), @@ -181,7 +212,7 @@ fn _mro(cls: &PyClassRef) -> Vec { /// Determines if `obj` actually an instance of `cls`, this doesn't call __instancecheck__, so only /// use this if `cls` is known to have not overridden the base __instancecheck__ magic method. pub fn isinstance(obj: &PyObjectRef, cls: &PyClassRef) -> bool { - issubclass(&obj.type_pyref(), &cls) + issubclass(&obj.class(), &cls) } /// Determines if `subclass` is actually a subclass of `cls`, this doesn't call __subclasscheck__, @@ -192,35 +223,13 @@ pub fn issubclass(subclass: &PyClassRef, cls: &PyClassRef) -> bool { subclass.is(cls) || mro.iter().any(|c| c.is(cls.as_object())) } -pub fn get_type_name(typ: &PyObjectRef) -> String { - if let Some(PyClass { name, .. }) = &typ.payload::() { - name.clone() - } else { - panic!("Cannot get type_name of non-type type {:?}", typ); - } -} - pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { debug!("type.__new__ {:?}", args); if args.args.len() == 2 { - arg_check!( - vm, - args, - required = [(_typ, Some(vm.ctx.type_type())), (obj, None)] - ); - Ok(obj.typ()) + Ok(args.args[1].class().into_object()) } else if args.args.len() == 4 { - arg_check!( - vm, - args, - required = [ - (typ, Some(vm.ctx.type_type())), - (name, Some(vm.ctx.str_type())), - (bases, None), - (dict, Some(vm.ctx.dict_type())) - ] - ); - type_new_class(vm, typ, name, bases, dict) + let (typ, name, bases, dict) = args.bind(vm)?; + type_new_class(vm, typ, name, bases, dict).map(|x| x.into_object()) } else { Err(vm.new_type_error(format!(": type_new: {:?}", args))) } @@ -228,23 +237,18 @@ pub fn type_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { pub fn type_new_class( vm: &VirtualMachine, - typ: &PyObjectRef, - name: &PyObjectRef, - bases: &PyObjectRef, - dict: &PyObjectRef, -) -> PyResult { - let mut bases: Vec = vm - .extract_elements(bases)? - .iter() - .map(|x| x.clone().downcast().unwrap()) - .collect(); + typ: PyClassRef, + name: PyStringRef, + bases: PyIterable, + dict: PyDictRef, +) -> PyResult { + let mut bases: Vec = bases.iter(vm)?.collect::, _>>()?; bases.push(vm.ctx.object()); - let name = objstr::get_value(name); new( typ.clone(), - &name, + &name.value, bases, - objdict::py_dict_to_attributes(dict), + objdict::py_dict_to_attributes(dict.as_object()), ) } @@ -263,32 +267,13 @@ pub fn type_call(class: PyClassRef, args: Args, kwargs: KwArgs, vm: &VirtualMach Ok(obj) } -// Very private helper function for class_get_attr -fn class_get_attr_in_dict(class: &PyClassRef, attr_name: &str) -> Option { - if let Some(ref dict) = class.as_object().dict { - dict.borrow().get(attr_name).cloned() - } else { - panic!("Only classes should be in MRO!"); - } -} - -// Very private helper function for class_has_attr -fn class_has_attr_in_dict(class: &PyClassRef, attr_name: &str) -> bool { - if let Some(ref dict) = class.as_object().dict { - dict.borrow().contains_key(attr_name) - } else { - panic!("All classes are expected to have dicts!"); - } -} - // This is the internal get_attr implementation for fast lookup on a class. -pub fn class_get_attr(zelf: &PyClassRef, attr_name: &str) -> Option { - let mro = &zelf.mro; - if let Some(item) = class_get_attr_in_dict(zelf, attr_name) { +pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option { + if let Some(item) = class.attributes.borrow().get(attr_name).cloned() { return Some(item); } - for class in mro { - if let Some(item) = class_get_attr_in_dict(class, attr_name) { + for class in &class.mro { + if let Some(item) = class.attributes.borrow().get(attr_name).cloned() { return Some(item); } } @@ -296,12 +281,12 @@ pub fn class_get_attr(zelf: &PyClassRef, attr_name: &str) -> Option } // This is the internal has_attr implementation for fast lookup on a class. -pub fn class_has_attr(zelf: &PyClassRef, attr_name: &str) -> bool { - class_has_attr_in_dict(zelf, attr_name) - || zelf +pub fn class_has_attr(class: &PyClassRef, attr_name: &str) -> bool { + class.attributes.borrow().contains_key(attr_name) + || class .mro .iter() - .any(|d| class_has_attr_in_dict(d, attr_name)) + .any(|c| c.attributes.borrow().contains_key(attr_name)) } pub fn get_attributes(cls: PyClassRef) -> PyAttributes { @@ -312,10 +297,8 @@ pub fn get_attributes(cls: PyClassRef) -> PyAttributes { base_classes.reverse(); for bc in base_classes { - if let Some(ref dict) = &bc.as_object().dict { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); - } + for (name, value) in bc.attributes.borrow().iter() { + attributes.insert(name.to_string(), value.clone()); } } @@ -365,22 +348,31 @@ fn linearise_mro(mut bases: Vec>) -> Option> { } pub fn new( - typ: PyObjectRef, + typ: PyClassRef, name: &str, bases: Vec, dict: HashMap, -) -> PyResult { - let mros = bases.into_iter().map(|x| _mro(&x)).collect(); +) -> PyResult { + let mros = bases.iter().map(|x| _mro(&x)).collect(); let mro = linearise_mro(mros).unwrap(); - Ok(PyObject { + let new_type = PyObject { payload: PyClass { name: String::from(name), mro, + subclasses: RefCell::new(vec![]), + attributes: RefCell::new(dict), }, - dict: Some(RefCell::new(dict)), + dict: None, typ, } - .into_ref()) + .into_ref(); + for base in bases { + base.subclasses + .borrow_mut() + .push(PyWeak::downgrade(&new_type)); + } + + Ok(new_type.downcast().unwrap()) } #[cfg(test)] @@ -401,23 +393,8 @@ mod tests { let object: PyClassRef = context.object.clone(); let type_type = &context.type_type; - let a = new( - type_type.clone().into_object(), - "A", - vec![object.clone()], - HashMap::new(), - ) - .unwrap(); - let b = new( - type_type.clone().into_object(), - "B", - vec![object.clone()], - HashMap::new(), - ) - .unwrap(); - - let a: PyClassRef = a.downcast().unwrap(); - let b: PyClassRef = b.downcast().unwrap(); + let a = new(type_type.clone(), "A", vec![object.clone()], HashMap::new()).unwrap(); + let b = new(type_type.clone(), "B", vec![object.clone()], HashMap::new()).unwrap(); assert_eq!( map_ids(linearise_mro(vec![ diff --git a/vm/src/obj/objweakref.rs b/vm/src/obj/objweakref.rs index 3a71e3b630..685e32b704 100644 --- a/vm/src/obj/objweakref.rs +++ b/vm/src/obj/objweakref.rs @@ -11,9 +11,9 @@ pub struct PyWeak { } impl PyWeak { - pub fn downgrade(obj: PyObjectRef) -> PyWeak { + pub fn downgrade(obj: &PyObjectRef) -> PyWeak { PyWeak { - referent: Rc::downgrade(&obj), + referent: Rc::downgrade(obj), } } @@ -33,7 +33,7 @@ pub type PyWeakRef = PyRef; impl PyWeakRef { // TODO callbacks fn create(cls: PyClassRef, referent: PyObjectRef, vm: &VirtualMachine) -> PyResult { - PyWeak::downgrade(referent).into_ref_with_type(vm, cls) + PyWeak::downgrade(&referent).into_ref_with_type(vm, cls) } fn call(self, vm: &VirtualMachine) -> PyObjectRef { diff --git a/vm/src/obj/objzip.rs b/vm/src/obj/objzip.rs index 63df96f62f..e628cb218a 100644 --- a/vm/src/obj/objzip.rs +++ b/vm/src/obj/objzip.rs @@ -1,10 +1,12 @@ -use crate::function::PyFuncArgs; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::function::{Args, PyFuncArgs}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; use super::objiter; use crate::obj::objtype::PyClassRef; +pub type PyZipRef = PyRef; + #[derive(Debug)] pub struct PyZip { iterators: Vec, @@ -16,15 +18,12 @@ impl PyValue for PyZip { } } -fn zip_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - no_kwargs!(vm, args); - let cls = &args.args[0]; - let iterables = &args.args[1..]; +fn zip_new(cls: PyClassRef, iterables: Args, vm: &VirtualMachine) -> PyResult { let iterators = iterables - .iter() - .map(|iterable| objiter::get_iter(vm, iterable)) + .into_iter() + .map(|iterable| objiter::get_iter(vm, &iterable)) .collect::, _>>()?; - Ok(PyObject::new(PyZip { iterators }, cls.clone())) + PyZip { iterators }.into_ref_with_type(vm, cls) } fn zip_next(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 41a1ca6f34..aa3348be59 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -14,7 +14,7 @@ use num_traits::{One, Zero}; use crate::bytecode; use crate::exceptions; -use crate::frame::{Frame, Scope}; +use crate::frame::Scope; use crate::function::{IntoPyNativeFunc, PyFuncArgs}; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; @@ -22,8 +22,9 @@ use crate::obj::objbytearray; use crate::obj::objbytes; use crate::obj::objclassmethod; use crate::obj::objcode; +use crate::obj::objcode::PyCodeRef; use crate::obj::objcomplex::{self, PyComplex}; -use crate::obj::objdict::{self, PyDict}; +use crate::obj::objdict::{self, PyDict, PyDictRef}; use crate::obj::objellipsis; use crate::obj::objenumerate; use crate::obj::objfilter; @@ -87,7 +88,7 @@ impl fmt::Display for PyObject { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::TypeProtocol; if let Some(PyClass { ref name, .. }) = self.payload::() { - let type_name = objtype::get_type_name(&self.typ()); + let type_name = self.class().name.clone(); // We don't have access to a vm, so just assume that if its parent's name // is type, it's a type if type_name == "type" { @@ -100,7 +101,7 @@ impl fmt::Display for PyObject { if let Some(PyModule { ref name, .. }) = self.payload::() { return write!(f, "module '{}'", name); } - write!(f, "'{}' object", objtype::get_type_name(&self.typ())) + write!(f, "'{}' object", self.class().name) } } @@ -152,14 +153,7 @@ pub struct PyContext { pub fn create_type(name: &str, type_type: &PyClassRef, base: &PyClassRef) -> PyClassRef { let dict = PyAttributes::new(); - let new_type = objtype::new( - type_type.clone().into_object(), - name, - vec![base.clone()], - dict, - ) - .unwrap(); - new_type.downcast().unwrap() + objtype::new(type_type.clone(), name, vec![base.clone()], dict).unwrap() } pub type PyNotImplementedRef = PyRef; @@ -169,7 +163,7 @@ pub struct PyNotImplemented; impl PyValue for PyNotImplemented { fn class(vm: &VirtualMachine) -> PyClassRef { - vm.ctx.not_implemented().type_pyref() + vm.ctx.not_implemented().class() } } @@ -189,37 +183,49 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { // and both `type` and `object are instances of `type`. // to produce this circular dependency, we need an unsafe block. // (and yes, this will never get dropped. TODO?) - unsafe { + let (type_type, object_type) = unsafe { let object_type = PyObject { typ: mem::uninitialized(), // ! - dict: Some(RefCell::new(PyAttributes::new())), + dict: None, payload: PyClass { name: String::from("object"), mro: vec![], + subclasses: RefCell::new(vec![]), + attributes: RefCell::new(PyAttributes::new()), }, } .into_ref(); let type_type = PyObject { typ: mem::uninitialized(), // ! - dict: Some(RefCell::new(PyAttributes::new())), + dict: None, payload: PyClass { name: String::from("type"), mro: vec![object_type.clone().downcast().unwrap()], + subclasses: RefCell::new(vec![]), + attributes: RefCell::new(PyAttributes::new()), }, } .into_ref(); let object_type_ptr = PyObjectRef::into_raw(object_type.clone()) as *mut PyObject; let type_type_ptr = PyObjectRef::into_raw(type_type.clone()) as *mut PyObject; + + let type_type: PyClassRef = type_type.downcast().unwrap(); + let object_type: PyClassRef = object_type.downcast().unwrap(); + ptr::write(&mut (*object_type_ptr).typ, type_type.clone()); ptr::write(&mut (*type_type_ptr).typ, type_type.clone()); - ( - type_type.downcast().unwrap(), - object_type.downcast().unwrap(), - ) - } + (type_type, object_type) + }; + + object_type + .subclasses + .borrow_mut() + .push(objweakref::PyWeak::downgrade(&type_type.as_object())); + + (type_type, object_type) } // Basic objects: @@ -265,7 +271,7 @@ impl PyContext { fn create_object(payload: T, cls: &PyClassRef) -> PyRef { PyRef { - obj: PyObject::new(payload, cls.clone().into_object()), + obj: PyObject::new(payload, cls.clone(), None), _payload: PhantomData, } } @@ -515,37 +521,31 @@ impl PyContext { self.object.clone() } - pub fn new_object(&self) -> PyObjectRef { - self.new_instance(self.object.clone(), None) - } - pub fn new_int>(&self, i: T) -> PyObjectRef { - PyObject::new(PyInt::new(i), self.int_type().into_object()) + PyObject::new(PyInt::new(i), self.int_type(), None) } pub fn new_float(&self, value: f64) -> PyObjectRef { - PyObject::new(PyFloat::from(value), self.float_type().into_object()) + PyObject::new(PyFloat::from(value), self.float_type(), None) } pub fn new_complex(&self, value: Complex64) -> PyObjectRef { - PyObject::new(PyComplex::from(value), self.complex_type().into_object()) + PyObject::new(PyComplex::from(value), self.complex_type(), None) } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new(objstr::PyString { value: s }, self.str_type().into_object()) + PyObject::new(objstr::PyString { value: s }, self.str_type(), None) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new( - objbytes::PyBytes::new(data), - self.bytes_type().into_object(), - ) + PyObject::new(objbytes::PyBytes::new(data), self.bytes_type(), None) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { PyObject::new( objbytearray::PyByteArray::new(data), - self.bytearray_type().into_object(), + self.bytearray_type(), + None, ) } @@ -558,45 +558,40 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyTuple::from(elements), self.tuple_type().into_object()) + PyObject::new(PyTuple::from(elements), self.tuple_type(), None) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyList::from(elements), self.list_type().into_object()) + PyObject::new(PyList::from(elements), self.list_type(), None) } pub fn new_set(&self) -> PyObjectRef { // Initialized empty, as calling __hash__ is required for adding each object to the set // which requires a VM context - this is done in the objset code itself. - PyObject::new(PySet::default(), self.set_type().into_object()) + PyObject::new(PySet::default(), self.set_type(), None) } - pub fn new_dict(&self) -> PyObjectRef { - PyObject::new(PyDict::default(), self.dict_type().into_object()) + pub fn new_dict(&self) -> PyDictRef { + PyObject::new(PyDict::default(), self.dict_type(), None) + .downcast() + .unwrap() } pub fn new_class(&self, name: &str, base: PyClassRef) -> PyClassRef { - let typ = objtype::new( - self.type_type().into_object(), - name, - vec![base], - PyAttributes::new(), - ) - .unwrap(); - typ.downcast().unwrap() + objtype::new(self.type_type(), name, vec![base], PyAttributes::new()).unwrap() } pub fn new_scope(&self) -> Scope { Scope::new(None, self.new_dict()) } - pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { + pub fn new_module(&self, name: &str, dict: PyDictRef) -> PyObjectRef { PyObject::new( PyModule { name: name.to_string(), - dict, }, - self.module_type.clone().into_object(), + self.module_type.clone(), + Some(dict), ) } @@ -606,14 +601,11 @@ impl PyContext { { PyObject::new( PyBuiltinFunction::new(f.into_func()), - self.builtin_function_or_method_type().into_object(), + self.builtin_function_or_method_type(), + None, ) } - pub fn new_frame(&self, code: PyObjectRef, scope: Scope) -> PyObjectRef { - PyObject::new(Frame::new(code, scope), self.frame_type().into_object()) - } - pub fn new_property(&self, f: F) -> PyObjectRef where F: IntoPyNativeFunc, @@ -621,34 +613,37 @@ impl PyContext { PropertyBuilder::new(self).add_getter(f).create() } - pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new(objcode::PyCode::new(code), self.code_type().into_object()) + pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyCodeRef { + PyObject::new(objcode::PyCode::new(code), self.code_type(), None) + .downcast() + .unwrap() } pub fn new_function( &self, - code_obj: PyObjectRef, + code_obj: PyCodeRef, scope: Scope, defaults: PyObjectRef, ) -> PyObjectRef { PyObject::new( PyFunction::new(code_obj, scope, defaults), - self.function_type().into_object(), + self.function_type(), + Some(self.new_dict()), ) } pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { PyObject::new( PyMethod::new(object, function), - self.bound_method_type().into_object(), + self.bound_method_type(), + None, ) } - pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { - let dict = dict.unwrap_or_default(); + pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { PyObject { - typ: class.into_object(), - dict: Some(RefCell::new(dict)), + typ: class, + dict: dict, payload: objobject::PyInstance, } .into_ref() @@ -671,11 +666,12 @@ impl PyContext { value: V, ) { let obj = obj.into(); - if let Some(PyModule { ref dict, .. }) = obj.payload::() { - dict.set_item(self, attr_name, value.into()) - } else if let Some(ref dict) = obj.dict { - dict.borrow_mut() + if let Some(PyClass { ref attributes, .. }) = obj.payload::() { + attributes + .borrow_mut() .insert(attr_name.to_string(), value.into()); + } else if let Some(ref dict) = obj.dict { + dict.set_item(self, attr_name, value.into()); } else { unimplemented!("set_attr unimplemented for: {:?}", obj); }; @@ -689,7 +685,9 @@ impl PyContext { bytecode::Constant::String { ref value } => self.new_str(value.clone()), bytecode::Constant::Bytes { ref value } => self.new_bytes(value.clone()), bytecode::Constant::Boolean { ref value } => self.new_bool(value.clone()), - bytecode::Constant::Code { ref code } => self.new_code_object(*code.clone()), + bytecode::Constant::Code { ref code } => { + self.new_code_object(*code.clone()).into_object() + } bytecode::Constant::Tuple { ref elements } => { let elements = elements .iter() @@ -716,22 +714,29 @@ pub struct PyObject where T: ?Sized + PyObjectPayload, { - pub typ: PyObjectRef, - pub dict: Option>, // __dict__ member + pub typ: PyClassRef, + pub dict: Option, // __dict__ member pub payload: T, } impl PyObject { - pub fn downcast(self: Rc) -> Option> { + /// Attempt to downcast this reference to a subclass. + /// + /// If the downcast fails, the original ref is returned in as `Err` so + /// another downcast can be attempted without unnecessary cloning. + /// + /// Note: The returned `Result` is _not_ a `PyResult`, even though the + /// types are compatible. + pub fn downcast(self: Rc) -> Result, PyObjectRef> { if self.payload_is::() { - Some({ + Ok({ PyRef { obj: self, _payload: PhantomData, } }) } else { - None + Err(self) } } } @@ -771,7 +776,7 @@ impl PyRef { pub fn typ(&self) -> PyClassRef { PyRef { - obj: self.obj.typ(), + obj: self.obj.class().into_object(), _payload: PhantomData, } } @@ -801,7 +806,7 @@ where } else { let class = T::class(vm); let expected_type = vm.to_pystr(&class)?; - let actual_type = vm.to_pystr(&obj.typ())?; + let actual_type = vm.to_pystr(&obj.class())?; Err(vm.new_type_error(format!( "Expected type {}, not {}", expected_type, actual_type, @@ -876,18 +881,12 @@ impl IdProtocol for PyRef { } pub trait TypeProtocol { - fn typ(&self) -> PyObjectRef { - self.type_ref().clone() - } - fn type_pyref(&self) -> PyClassRef { - self.typ().downcast().unwrap() - } - fn type_ref(&self) -> &PyObjectRef; + fn class(&self) -> PyClassRef; } impl TypeProtocol for PyObjectRef { - fn type_ref(&self) -> &PyObjectRef { - (**self).type_ref() + fn class(&self) -> PyClassRef { + (**self).class() } } @@ -895,8 +894,14 @@ impl TypeProtocol for PyObject where T: ?Sized + PyObjectPayload, { - fn type_ref(&self) -> &PyObjectRef { - &self.typ + fn class(&self) -> PyClassRef { + self.typ.clone() + } +} + +impl TypeProtocol for PyRef { + fn class(&self) -> PyClassRef { + self.obj.typ.clone() } } @@ -920,8 +925,6 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { if let Some(dict) = self.payload::() { objdict::content_get_key_str(&dict.entries.borrow(), k) - } else if let Some(PyModule { ref dict, .. }) = self.payload::() { - dict.get_item(k) } else { panic!("TODO {:?}", k) } @@ -930,8 +933,6 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { if self.payload_is::() { objdict::get_key_value_pairs(self) - } else if let Some(PyModule { ref dict, .. }) = self.payload::() { - dict.get_key_value_pairs() } else { panic!("TODO") } @@ -942,8 +943,6 @@ impl DictProtocol for PyObjectRef { if let Some(dict) = self.payload::() { let key = ctx.new_str(key.to_string()); objdict::set_item_in_content(&mut dict.entries.borrow_mut(), &key, &v); - } else if let Some(PyModule { ref dict, .. }) = self.payload::() { - dict.set_item(ctx, key, v); } else { panic!("TODO {:?}", self); } @@ -961,7 +960,7 @@ pub trait BufferProtocol { impl BufferProtocol for PyObjectRef { fn readonly(&self) -> bool { - match objtype::get_type_name(&self.typ()).as_ref() { + match self.class().name.as_str() { "bytes" => false, "bytearray" | "memoryview" => true, _ => panic!("Bytes-Like type expected not {:?}", self), @@ -1124,7 +1123,7 @@ where T: PyValue + Sized, { fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { - Ok(PyObject::new(self, T::class(vm).into_object())) + Ok(PyObject::new(self, T::class(vm), None)) } } @@ -1146,19 +1145,10 @@ impl PyObject where T: Sized + PyObjectPayload, { - pub fn new(payload: T, typ: PyObjectRef) -> PyObjectRef { - PyObject { - typ, - dict: Some(RefCell::new(PyAttributes::new())), - payload, - } - .into_ref() - } - - pub fn new_without_dict(payload: T, typ: PyObjectRef) -> PyObjectRef { + pub fn new(payload: T, typ: PyClassRef, dict: Option) -> PyObjectRef { PyObject { typ, - dict: None, + dict: dict, payload, } .into_ref() @@ -1187,7 +1177,7 @@ pub trait PyValue: fmt::Debug + Sized + 'static { fn into_ref(self, vm: &VirtualMachine) -> PyRef { PyRef { - obj: PyObject::new(self, Self::class(vm).into_object()), + obj: PyObject::new(self, Self::class(vm), None), _payload: PhantomData, } } @@ -1195,8 +1185,13 @@ pub trait PyValue: fmt::Debug + Sized + 'static { fn into_ref_with_type(self, vm: &VirtualMachine, cls: PyClassRef) -> PyResult> { let class = Self::class(vm); if objtype::issubclass(&cls, &class) { + let dict = if cls.is(&class) { + None + } else { + Some(vm.ctx.new_dict()) + }; Ok(PyRef { - obj: PyObject::new(self, cls.obj), + obj: PyObject::new(self, cls, dict), _payload: PhantomData, }) } else { @@ -1218,6 +1213,52 @@ impl PyObjectPayload for T { } } +pub enum Either { + A(A), + B(B), +} + +/// This allows a builtin method to accept arguments that may be one of two +/// types, raising a `TypeError` if it is neither. +/// +/// # Example +/// +/// ``` +/// use rustpython_vm::VirtualMachine; +/// use rustpython_vm::obj::{objstr::PyStringRef, objint::PyIntRef}; +/// use rustpython_vm::pyobject::Either; +/// +/// fn do_something(arg: Either, vm: &VirtualMachine) { +/// match arg { +/// Either::A(int)=> { +/// // do something with int +/// } +/// Either::B(string) => { +/// // do something with string +/// } +/// } +/// } +/// ``` +impl TryFromObject for Either, PyRef> +where + A: PyValue, + B: PyValue, +{ + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + obj.downcast::() + .map(Either::A) + .or_else(|obj| obj.clone().downcast::().map(Either::B)) + .map_err(|obj| { + vm.new_type_error(format!( + "must be {} or {}, not {}", + A::class(vm), + B::class(vm), + obj.class() + )) + }) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index a3010a5199..0011407aba 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -11,9 +11,20 @@ use rustpython_parser::{ast, parser}; use crate::function::PyFuncArgs; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objtype::PyClassRef; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; +#[derive(Debug)] +struct AstNode; +// type AstNodeRef = PyRef; + +impl PyValue for AstNode { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.class("ast", "AST") + } +} + /* * Idea: maybe we can create a sort of struct with some helper functions? struct AstToPyAst { @@ -30,35 +41,35 @@ impl AstToPyAst { } */ -fn program_to_ast(ctx: &PyContext, program: &ast::Program) -> PyObjectRef { +fn program_to_ast(vm: &VirtualMachine, program: &ast::Program) -> PyObjectRef { let mut body = vec![]; for statement in &program.statements { - body.push(statement_to_ast(ctx, statement)); + body.push(statement_to_ast(&vm, statement)); } // TODO: create Module node: // let ast_node = ctx.new_instance(this.Module); - let ast_node = ctx.new_object(); - let py_body = ctx.new_list(body); - ctx.set_attr(&ast_node, "body", py_body); + let ast_node = create_node(vm, "program"); + let py_body = vm.ctx.new_list(body); + vm.ctx.set_attr(&ast_node, "body", py_body); ast_node } // Create a node class instance -fn create_node(ctx: &PyContext, _name: &str) -> PyObjectRef { +fn create_node(vm: &VirtualMachine, _name: &str) -> PyObjectRef { // TODO: instantiate a class of type given by name // TODO: lookup in the current module? - ctx.new_object() + PyObject::new(AstNode, AstNode::class(vm), Some(vm.ctx.new_dict())) } -fn statements_to_ast(ctx: &PyContext, statements: &[ast::LocatedStatement]) -> PyObjectRef { +fn statements_to_ast(vm: &VirtualMachine, statements: &[ast::LocatedStatement]) -> PyObjectRef { let mut py_statements = vec![]; for statement in statements { - py_statements.push(statement_to_ast(ctx, statement)); + py_statements.push(statement_to_ast(vm, statement)); } - ctx.new_list(py_statements) + vm.ctx.new_list(py_statements) } -fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObjectRef { +fn statement_to_ast(vm: &VirtualMachine, statement: &ast::LocatedStatement) -> PyObjectRef { let node = match &statement.node { ast::Statement::ClassDef { name, @@ -66,17 +77,18 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj decorator_list, .. } => { - let node = create_node(ctx, "ClassDef"); + let node = create_node(vm, "ClassDef"); // Set name: - ctx.set_attr(&node, "name", ctx.new_str(name.to_string())); + vm.ctx + .set_attr(&node, "name", vm.ctx.new_str(name.to_string())); // Set body: - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_decorator_list = expressions_to_ast(ctx, decorator_list); - ctx.set_attr(&node, "decorator_list", py_decorator_list); + let py_decorator_list = expressions_to_ast(vm, decorator_list); + vm.ctx.set_attr(&node, "decorator_list", py_decorator_list); node } ast::Statement::FunctionDef { @@ -86,80 +98,83 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj decorator_list, returns, } => { - let node = create_node(ctx, "FunctionDef"); + let node = create_node(vm, "FunctionDef"); // Set name: - ctx.set_attr(&node, "name", ctx.new_str(name.to_string())); + vm.ctx + .set_attr(&node, "name", vm.ctx.new_str(name.to_string())); - ctx.set_attr(&node, "args", parameters_to_ast(ctx, args)); + vm.ctx.set_attr(&node, "args", parameters_to_ast(vm, args)); // Set body: - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_decorator_list = expressions_to_ast(ctx, decorator_list); - ctx.set_attr(&node, "decorator_list", py_decorator_list); + let py_decorator_list = expressions_to_ast(vm, decorator_list); + vm.ctx.set_attr(&node, "decorator_list", py_decorator_list); let py_returns = if let Some(hint) = returns { - expression_to_ast(ctx, hint) + expression_to_ast(vm, hint) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "returns", py_returns); + vm.ctx.set_attr(&node, "returns", py_returns); node } - ast::Statement::Continue => create_node(ctx, "Continue"), - ast::Statement::Break => create_node(ctx, "Break"), - ast::Statement::Pass => create_node(ctx, "Pass"), + ast::Statement::Continue => create_node(vm, "Continue"), + ast::Statement::Break => create_node(vm, "Break"), + ast::Statement::Pass => create_node(vm, "Pass"), ast::Statement::Assert { test, msg } => { - let node = create_node(ctx, "Pass"); + let node = create_node(vm, "Pass"); - ctx.set_attr(&node, "test", expression_to_ast(ctx, test)); + vm.ctx.set_attr(&node, "test", expression_to_ast(vm, test)); let py_msg = match msg { - Some(msg) => expression_to_ast(ctx, msg), - None => ctx.none(), + Some(msg) => expression_to_ast(vm, msg), + None => vm.ctx.none(), }; - ctx.set_attr(&node, "msg", py_msg); + vm.ctx.set_attr(&node, "msg", py_msg); node } ast::Statement::Delete { targets } => { - let node = create_node(ctx, "Delete"); + let node = create_node(vm, "Delete"); - let py_targets = - ctx.new_tuple(targets.iter().map(|v| expression_to_ast(ctx, v)).collect()); - ctx.set_attr(&node, "targets", py_targets); + let py_targets = vm + .ctx + .new_tuple(targets.iter().map(|v| expression_to_ast(vm, v)).collect()); + vm.ctx.set_attr(&node, "targets", py_targets); node } ast::Statement::Return { value } => { - let node = create_node(ctx, "Return"); + let node = create_node(vm, "Return"); let py_value = if let Some(value) = value { - ctx.new_tuple(value.iter().map(|v| expression_to_ast(ctx, v)).collect()) + vm.ctx + .new_tuple(value.iter().map(|v| expression_to_ast(vm, v)).collect()) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "value", py_value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Statement::If { test, body, orelse } => { - let node = create_node(ctx, "If"); + let node = create_node(vm, "If"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } @@ -169,49 +184,49 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj body, orelse, } => { - let node = create_node(ctx, "For"); + let node = create_node(vm, "For"); - let py_target = expression_to_ast(ctx, target); - ctx.set_attr(&node, "target", py_target); + let py_target = expression_to_ast(vm, target); + vm.ctx.set_attr(&node, "target", py_target); - let py_iter = expressions_to_ast(ctx, iter); - ctx.set_attr(&node, "iter", py_iter); + let py_iter = expressions_to_ast(vm, iter); + vm.ctx.set_attr(&node, "iter", py_iter); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Statement::While { test, body, orelse } => { - let node = create_node(ctx, "While"); + let node = create_node(vm, "While"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Statement::Expression { expression } => { - let node = create_node(ctx, "Expr"); + let node = create_node(vm, "Expr"); - let value = expression_to_ast(ctx, expression); - ctx.set_attr(&node, "value", value); + let value = expression_to_ast(vm, expression); + vm.ctx.set_attr(&node, "value", value); node } @@ -221,38 +236,38 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj }; // set lineno on node: - let lineno = ctx.new_int(statement.location.get_row()); - ctx.set_attr(&node, "lineno", lineno); + let lineno = vm.ctx.new_int(statement.location.get_row()); + vm.ctx.set_attr(&node, "lineno", lineno); node } -fn expressions_to_ast(ctx: &PyContext, expressions: &[ast::Expression]) -> PyObjectRef { +fn expressions_to_ast(vm: &VirtualMachine, expressions: &[ast::Expression]) -> PyObjectRef { let mut py_expression_nodes = vec![]; for expression in expressions { - py_expression_nodes.push(expression_to_ast(ctx, expression)); + py_expression_nodes.push(expression_to_ast(vm, expression)); } - ctx.new_list(py_expression_nodes) + vm.ctx.new_list(py_expression_nodes) } -fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectRef { +fn expression_to_ast(vm: &VirtualMachine, expression: &ast::Expression) -> PyObjectRef { let node = match &expression { ast::Expression::Call { function, args, .. } => { - let node = create_node(ctx, "Call"); + let node = create_node(vm, "Call"); - let py_func_ast = expression_to_ast(ctx, function); - ctx.set_attr(&node, "func", py_func_ast); + let py_func_ast = expression_to_ast(vm, function); + vm.ctx.set_attr(&node, "func", py_func_ast); - let py_args = expressions_to_ast(ctx, args); - ctx.set_attr(&node, "args", py_args); + let py_args = expressions_to_ast(vm, args); + vm.ctx.set_attr(&node, "args", py_args); node } ast::Expression::Binop { a, op, b } => { - let node = create_node(ctx, "BinOp"); + let node = create_node(vm, "BinOp"); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "left", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "left", py_a); // Operator: let str_op = match op { @@ -270,15 +285,15 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::Operator::BitAnd => "BitAnd", ast::Operator::FloorDiv => "FloorDiv", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); - let py_b = expression_to_ast(ctx, b); - ctx.set_attr(&node, "right", py_b); + let py_b = expression_to_ast(vm, b); + vm.ctx.set_attr(&node, "right", py_b); node } ast::Expression::Unop { op, a } => { - let node = create_node(ctx, "UnaryOp"); + let node = create_node(vm, "UnaryOp"); let str_op = match op { ast::UnaryOperator::Not => "Not", @@ -286,37 +301,37 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::UnaryOperator::Neg => "USub", ast::UnaryOperator::Pos => "UAdd", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "operand", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "operand", py_a); node } ast::Expression::BoolOp { a, op, b } => { - let node = create_node(ctx, "BoolOp"); + let node = create_node(vm, "BoolOp"); // Attach values: - let py_a = expression_to_ast(ctx, a); - let py_b = expression_to_ast(ctx, b); - let py_values = ctx.new_tuple(vec![py_a, py_b]); - ctx.set_attr(&node, "values", py_values); + let py_a = expression_to_ast(vm, a); + let py_b = expression_to_ast(vm, b); + let py_values = vm.ctx.new_tuple(vec![py_a, py_b]); + vm.ctx.set_attr(&node, "values", py_values); let str_op = match op { ast::BooleanOperator::And => "And", ast::BooleanOperator::Or => "Or", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); node } ast::Expression::Compare { a, op, b } => { - let node = create_node(ctx, "Compare"); + let node = create_node(vm, "Compare"); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "left", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "left", py_a); // Operator: let str_op = match op { @@ -331,282 +346,283 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::Comparison::Is => "Is", ast::Comparison::IsNot => "IsNot", }; - let py_ops = ctx.new_list(vec![ctx.new_str(str_op.to_string())]); - ctx.set_attr(&node, "ops", py_ops); + let py_ops = vm.ctx.new_list(vec![vm.ctx.new_str(str_op.to_string())]); + vm.ctx.set_attr(&node, "ops", py_ops); - let py_b = ctx.new_list(vec![expression_to_ast(ctx, b)]); - ctx.set_attr(&node, "comparators", py_b); + let py_b = vm.ctx.new_list(vec![expression_to_ast(vm, b)]); + vm.ctx.set_attr(&node, "comparators", py_b); node } ast::Expression::Identifier { name } => { - let node = create_node(ctx, "Identifier"); + let node = create_node(vm, "Identifier"); // Id: - let py_name = ctx.new_str(name.clone()); - ctx.set_attr(&node, "id", py_name); + let py_name = vm.ctx.new_str(name.clone()); + vm.ctx.set_attr(&node, "id", py_name); node } ast::Expression::Lambda { args, body } => { - let node = create_node(ctx, "Lambda"); + let node = create_node(vm, "Lambda"); - ctx.set_attr(&node, "args", parameters_to_ast(ctx, args)); + vm.ctx.set_attr(&node, "args", parameters_to_ast(vm, args)); - let py_body = expression_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = expression_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); node } ast::Expression::IfExpression { test, body, orelse } => { - let node = create_node(ctx, "IfExp"); + let node = create_node(vm, "IfExp"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = expression_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = expression_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_orelse = expression_to_ast(ctx, orelse); - ctx.set_attr(&node, "orelse", py_orelse); + let py_orelse = expression_to_ast(vm, orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Expression::Number { value } => { - let node = create_node(ctx, "Num"); + let node = create_node(vm, "Num"); let py_n = match value { - ast::Number::Integer { value } => ctx.new_int(value.clone()), - ast::Number::Float { value } => ctx.new_float(*value), + ast::Number::Integer { value } => vm.ctx.new_int(value.clone()), + ast::Number::Float { value } => vm.ctx.new_float(*value), ast::Number::Complex { real, imag } => { - ctx.new_complex(Complex64::new(*real, *imag)) + vm.ctx.new_complex(Complex64::new(*real, *imag)) } }; - ctx.set_attr(&node, "n", py_n); + vm.ctx.set_attr(&node, "n", py_n); node } ast::Expression::True => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.new_bool(true)); + vm.ctx.set_attr(&node, "value", vm.ctx.new_bool(true)); node } ast::Expression::False => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.new_bool(false)); + vm.ctx.set_attr(&node, "value", vm.ctx.new_bool(false)); node } ast::Expression::None => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.none()); + vm.ctx.set_attr(&node, "value", vm.ctx.none()); node } - ast::Expression::Ellipsis => create_node(ctx, "Ellipsis"), + ast::Expression::Ellipsis => create_node(vm, "Ellipsis"), ast::Expression::List { elements } => { - let node = create_node(ctx, "List"); + let node = create_node(vm, "List"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Tuple { elements } => { - let node = create_node(ctx, "Tuple"); + let node = create_node(vm, "Tuple"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Set { elements } => { - let node = create_node(ctx, "Set"); + let node = create_node(vm, "Set"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Dict { elements } => { - let node = create_node(ctx, "Dict"); + let node = create_node(vm, "Dict"); let mut keys = Vec::new(); let mut values = Vec::new(); for (k, v) in elements { - keys.push(expression_to_ast(ctx, k)); - values.push(expression_to_ast(ctx, v)); + keys.push(expression_to_ast(vm, k)); + values.push(expression_to_ast(vm, v)); } - let py_keys = ctx.new_list(keys); - ctx.set_attr(&node, "keys", py_keys); + let py_keys = vm.ctx.new_list(keys); + vm.ctx.set_attr(&node, "keys", py_keys); - let py_values = ctx.new_list(values); - ctx.set_attr(&node, "values", py_values); + let py_values = vm.ctx.new_list(values); + vm.ctx.set_attr(&node, "values", py_values); node } ast::Expression::Comprehension { kind, generators } => { let node = match kind.deref() { ast::ComprehensionKind::GeneratorExpression { .. } => { - create_node(ctx, "GeneratorExp") + create_node(vm, "GeneratorExp") } - ast::ComprehensionKind::List { .. } => create_node(ctx, "ListComp"), - ast::ComprehensionKind::Set { .. } => create_node(ctx, "SetComp"), - ast::ComprehensionKind::Dict { .. } => create_node(ctx, "DictComp"), + ast::ComprehensionKind::List { .. } => create_node(vm, "ListComp"), + ast::ComprehensionKind::Set { .. } => create_node(vm, "SetComp"), + ast::ComprehensionKind::Dict { .. } => create_node(vm, "DictComp"), }; let g = generators .iter() - .map(|g| comprehension_to_ast(ctx, g)) + .map(|g| comprehension_to_ast(vm, g)) .collect(); - let py_generators = ctx.new_list(g); - ctx.set_attr(&node, "generators", py_generators); + let py_generators = vm.ctx.new_list(g); + vm.ctx.set_attr(&node, "generators", py_generators); node } ast::Expression::Yield { value } => { - let node = create_node(ctx, "Yield"); + let node = create_node(vm, "Yield"); let py_value = match value { - Some(value) => expression_to_ast(ctx, value), - None => ctx.none(), + Some(value) => expression_to_ast(vm, value), + None => vm.ctx.none(), }; - ctx.set_attr(&node, "value", py_value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::YieldFrom { value } => { - let node = create_node(ctx, "YieldFrom"); + let node = create_node(vm, "YieldFrom"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::Subscript { a, b } => { - let node = create_node(ctx, "Subscript"); + let node = create_node(vm, "Subscript"); - let py_value = expression_to_ast(ctx, a); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "value", py_value); - let py_slice = expression_to_ast(ctx, b); - ctx.set_attr(&node, "slice", py_slice); + let py_slice = expression_to_ast(vm, b); + vm.ctx.set_attr(&node, "slice", py_slice); node } ast::Expression::Attribute { value, name } => { - let node = create_node(ctx, "Attribute"); + let node = create_node(vm, "Attribute"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); - let py_attr = ctx.new_str(name.to_string()); - ctx.set_attr(&node, "attr", py_attr); + let py_attr = vm.ctx.new_str(name.to_string()); + vm.ctx.set_attr(&node, "attr", py_attr); node } ast::Expression::Starred { value } => { - let node = create_node(ctx, "Starred"); + let node = create_node(vm, "Starred"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::Slice { elements } => { - let node = create_node(ctx, "Slice"); + let node = create_node(vm, "Slice"); - let py_value = expressions_to_ast(ctx, elements); - ctx.set_attr(&node, "bounds", py_value); + let py_value = expressions_to_ast(vm, elements); + vm.ctx.set_attr(&node, "bounds", py_value); node } - ast::Expression::String { value } => string_to_ast(ctx, value), + ast::Expression::String { value } => string_to_ast(vm, value), ast::Expression::Bytes { value } => { - let node = create_node(ctx, "Bytes"); - ctx.set_attr(&node, "s", ctx.new_bytes(value.clone())); + let node = create_node(vm, "Bytes"); + vm.ctx.set_attr(&node, "s", vm.ctx.new_bytes(value.clone())); node } }; // TODO: retrieve correct lineno: - let lineno = ctx.new_int(1); - ctx.set_attr(&node, "lineno", lineno); + let lineno = vm.ctx.new_int(1); + vm.ctx.set_attr(&node, "lineno", lineno); node } -fn parameters_to_ast(ctx: &PyContext, args: &ast::Parameters) -> PyObjectRef { - let node = create_node(ctx, "arguments"); +fn parameters_to_ast(vm: &VirtualMachine, args: &ast::Parameters) -> PyObjectRef { + let node = create_node(vm, "arguments"); - ctx.set_attr( + vm.ctx.set_attr( &node, "args", - ctx.new_list(args.args.iter().map(|a| parameter_to_ast(ctx, a)).collect()), + vm.ctx + .new_list(args.args.iter().map(|a| parameter_to_ast(vm, a)).collect()), ); node } -fn parameter_to_ast(ctx: &PyContext, parameter: &ast::Parameter) -> PyObjectRef { - let node = create_node(ctx, "arg"); +fn parameter_to_ast(vm: &VirtualMachine, parameter: &ast::Parameter) -> PyObjectRef { + let node = create_node(vm, "arg"); - let py_arg = ctx.new_str(parameter.arg.to_string()); - ctx.set_attr(&node, "arg", py_arg); + let py_arg = vm.ctx.new_str(parameter.arg.to_string()); + vm.ctx.set_attr(&node, "arg", py_arg); let py_annotation = if let Some(annotation) = ¶meter.annotation { - expression_to_ast(ctx, annotation) + expression_to_ast(vm, annotation) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "annotation", py_annotation); + vm.ctx.set_attr(&node, "annotation", py_annotation); node } -fn comprehension_to_ast(ctx: &PyContext, comprehension: &ast::Comprehension) -> PyObjectRef { - let node = create_node(ctx, "comprehension"); +fn comprehension_to_ast(vm: &VirtualMachine, comprehension: &ast::Comprehension) -> PyObjectRef { + let node = create_node(vm, "comprehension"); - let py_target = expression_to_ast(ctx, &comprehension.target); - ctx.set_attr(&node, "target", py_target); + let py_target = expression_to_ast(vm, &comprehension.target); + vm.ctx.set_attr(&node, "target", py_target); - let py_iter = expression_to_ast(ctx, &comprehension.iter); - ctx.set_attr(&node, "iter", py_iter); + let py_iter = expression_to_ast(vm, &comprehension.iter); + vm.ctx.set_attr(&node, "iter", py_iter); - let py_ifs = expressions_to_ast(ctx, &comprehension.ifs); - ctx.set_attr(&node, "ifs", py_ifs); + let py_ifs = expressions_to_ast(vm, &comprehension.ifs); + vm.ctx.set_attr(&node, "ifs", py_ifs); node } -fn string_to_ast(ctx: &PyContext, string: &ast::StringGroup) -> PyObjectRef { +fn string_to_ast(vm: &VirtualMachine, string: &ast::StringGroup) -> PyObjectRef { match string { ast::StringGroup::Constant { value } => { - let node = create_node(ctx, "Str"); - ctx.set_attr(&node, "s", ctx.new_str(value.clone())); + let node = create_node(vm, "Str"); + vm.ctx.set_attr(&node, "s", vm.ctx.new_str(value.clone())); node } ast::StringGroup::FormattedValue { value, .. } => { - let node = create_node(ctx, "FormattedValue"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let node = create_node(vm, "FormattedValue"); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::StringGroup::Joined { values } => { - let node = create_node(ctx, "JoinedStr"); - let py_values = ctx.new_list( + let node = create_node(vm, "JoinedStr"); + let py_values = vm.ctx.new_list( values .iter() - .map(|value| string_to_ast(ctx, value)) + .map(|value| string_to_ast(vm, value)) .collect(), ); - ctx.set_attr(&node, "values", py_values); + vm.ctx.set_attr(&node, "values", py_values); node } } @@ -619,7 +635,7 @@ fn ast_parse(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let internal_ast = parser::parse_program(&source_string) .map_err(|err| vm.new_value_error(format!("{}", err)))?; // source.clone(); - let ast_node = program_to_ast(&vm.ctx, &internal_ast); + let ast_node = program_to_ast(&vm, &internal_ast); Ok(ast_node) } @@ -628,6 +644,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "parse" => ctx.new_rustfunc(ast_parse), "Module" => py_class!(ctx, "_ast.Module", ctx.object(), {}), "FunctionDef" => py_class!(ctx, "_ast.FunctionDef", ctx.object(), {}), - "Call" => py_class!(ctx, "_ast.Call", ctx.object(), {}) + "Call" => py_class!(ctx, "_ast.Call", ctx.object(), {}), + "AST" => py_class!(ctx, "_ast.AST", ctx.object(), {}), }) } diff --git a/vm/src/stdlib/dis.rs b/vm/src/stdlib/dis.rs index 04209d8b3b..222f23383d 100644 --- a/vm/src/stdlib/dis.rs +++ b/vm/src/stdlib/dis.rs @@ -1,23 +1,18 @@ -use crate::function::PyFuncArgs; -use crate::obj::objcode; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objcode::PyCodeRef; +use crate::pyobject::{PyContext, PyObjectRef, PyResult, TryFromObject}; use crate::vm::VirtualMachine; -fn dis_dis(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(obj, None)]); - +fn dis_dis(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { // Method or function: if let Ok(co) = vm.get_attribute(obj.clone(), "__code__") { - return dis_disassemble(vm, PyFuncArgs::new(vec![co], vec![])); + return dis_disassemble(co, vm); } - dis_disassemble(vm, PyFuncArgs::new(vec![obj.clone()], vec![])) + dis_disassemble(obj, vm) } -fn dis_disassemble(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(co, Some(vm.ctx.code_type()))]); - - let code = objcode::get_value(co); +fn dis_disassemble(co: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let code = &PyCodeRef::try_from_object(vm, co)?.code; print!("{}", code); Ok(vm.get_none()) } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index e1cfbd93b5..b881b7a724 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -74,7 +74,7 @@ impl<'s> serde::Serialize for PyObjectSerializer<'s> { } else { Err(serde::ser::Error::custom(format!( "Object of type '{:?}' is not serializable", - self.pyobject.typ() + self.pyobject.class() ))) } } @@ -177,14 +177,14 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { }; dict.set_item(&self.vm.ctx, &key, value); } - Ok(dict) + Ok(dict.into_object()) } fn visit_unit(self) -> Result where E: serde::de::Error, { - Ok(self.vm.ctx.none.clone().into_object()) + Ok(self.vm.get_none()) } } diff --git a/vm/src/stdlib/re.rs b/vm/src/stdlib/re.rs index 57db3f0a80..4dab1b0c3f 100644 --- a/vm/src/stdlib/re.rs +++ b/vm/src/stdlib/re.rs @@ -4,16 +4,13 @@ * This module fits the python re interface onto the rust regular expression * system. */ - -use std::path::PathBuf; - use regex::{Match, Regex}; use crate::function::PyFuncArgs; -use crate::import; use crate::obj::objstr; +use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{PyContext, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; impl PyValue for Regex { @@ -55,7 +52,8 @@ fn re_match(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { (string, Some(vm.ctx.str_type())) ] ); - let regex = make_regex(vm, pattern)?; + let pattern_str = objstr::get_value(&pattern); + let regex = make_regex(vm, &pattern_str)?; let search_text = objstr::get_value(string); do_match(vm, ®ex, search_text) @@ -74,8 +72,8 @@ fn re_search(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { ] ); - // let pattern_str = objstr::get_value(&pattern); - let regex = make_regex(vm, pattern)?; + let pattern_str = objstr::get_value(&pattern); + let regex = make_regex(vm, &pattern_str)?; let search_text = objstr::get_value(string); do_search(vm, ®ex, search_text) @@ -93,10 +91,8 @@ fn do_search(vm: &VirtualMachine, regex: &Regex, search_text: String) -> PyResul } } -fn make_regex(vm: &VirtualMachine, pattern: &PyObjectRef) -> PyResult { - let pattern_str = objstr::get_value(pattern); - - match Regex::new(&pattern_str) { +fn make_regex(vm: &VirtualMachine, pattern: &str) -> PyResult { + match Regex::new(pattern) { Ok(regex) => Ok(regex), Err(err) => Err(vm.new_value_error(format!("Error in regex: {:?}", err))), } @@ -117,39 +113,24 @@ impl PyValue for PyMatch { /// Take a found regular expression and convert it to proper match object. fn create_match(vm: &VirtualMachine, match_value: &Match) -> PyResult { - // Return match object: - // TODO: implement match object - // TODO: how to refer to match object defined in this - let module = import::import_module(vm, PathBuf::default(), "re").unwrap(); - let match_class = vm.get_attribute(module, "Match").unwrap(); - // let mo = vm.invoke(match_class, PyFuncArgs::default())?; // let txt = vm.ctx.new_str(result.as_str().to_string()); // vm.ctx.set_attr(&mo, "str", txt); - let match_value = PyMatch { + Ok(PyMatch { start: match_value.start(), end: match_value.end(), - }; - - Ok(PyObject::new(match_value, match_class.clone())) + } + .into_ref(vm) + .into_object()) } /// Compile a regular expression into a Pattern object. /// See also: /// https://docs.python.org/3/library/re.html#re.compile -fn re_compile(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(pattern, Some(vm.ctx.str_type()))] // TODO: flags=0 - ); - - let regex = make_regex(vm, pattern)?; - // TODO: retrieval of this module is akward: - let module = import::import_module(vm, PathBuf::default(), "re").unwrap(); - let pattern_class = vm.get_attribute(module, "Pattern").unwrap(); +fn re_compile(pattern: PyStringRef, vm: &VirtualMachine) -> PyResult> { + let regex = make_regex(vm, &pattern.value)?; - Ok(PyObject::new(regex, pattern_class.clone())) + Ok(regex.into_ref(vm)) } fn pattern_match(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { diff --git a/vm/src/stdlib/types.rs b/vm/src/stdlib/types.rs index 09aeb0dacc..8a5ab2939c 100644 --- a/vm/src/stdlib/types.rs +++ b/vm/src/stdlib/types.rs @@ -2,25 +2,27 @@ * Dynamic type creation and names for built in types. */ -use crate::function::PyFuncArgs; +use crate::function::OptionalArg; +use crate::obj::objdict::PyDict; +use crate::obj::objstr::PyStringRef; use crate::obj::objtype; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objtype::PyClassRef; +use crate::pyobject::{PyContext, PyIterable, PyObjectRef, PyResult, PyValue, TryFromObject}; use crate::VirtualMachine; -fn types_new_class(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(name, Some(vm.ctx.str_type()))], - optional = [(bases, None), (_kwds, None), (_exec_body, None)] - ); +fn types_new_class( + name: PyStringRef, + bases: OptionalArg>, + vm: &VirtualMachine, +) -> PyResult { + // TODO kwds and exec_body parameter - let bases: PyObjectRef = match bases { - Some(bases) => bases.clone(), - None => vm.ctx.new_tuple(vec![]), + let bases = match bases { + OptionalArg::Present(bases) => bases, + OptionalArg::Missing => PyIterable::try_from_object(vm, vm.ctx.new_tuple(vec![]))?, }; - let dict = vm.ctx.new_dict(); - objtype::type_new_class(vm, &vm.ctx.type_type().into_object(), name, &bases, &dict) + let dict = PyDict::default().into_ref(vm); + objtype::type_new_class(vm, vm.ctx.type_type(), name, bases, dict) } pub fn make_module(ctx: &PyContext) -> PyObjectRef { diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 8a45bc1c7e..a4a37b350c 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -1,10 +1,8 @@ use std::rc::Rc; use std::{env, mem}; -use num_traits::ToPrimitive; - -use crate::function::PyFuncArgs; -use crate::obj::objint; +use crate::frame::FrameRef; +use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol}; use crate::vm::VirtualMachine; @@ -18,28 +16,12 @@ fn argv(ctx: &PyContext) -> PyObjectRef { ctx.new_list(argv) } -fn frame_idx(vm: &VirtualMachine, offset: Option<&PyObjectRef>) -> Result { - if let Some(int) = offset { - if let Some(offset) = objint::get_value(&int).to_usize() { - if offset > vm.frames.borrow().len() - 1 { - return Err(vm.new_value_error("call stack is not deep enough".to_string())); - } - return Ok(offset); - } +fn getframe(offset: OptionalArg, vm: &VirtualMachine) -> PyResult { + let offset = offset.into_option().unwrap_or(0); + if offset > vm.frames.borrow().len() - 1 { + return Err(vm.new_value_error("call stack is not deep enough".to_string())); } - Ok(0) -} - -fn getframe(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [], - optional = [(offset, Some(vm.ctx.int_type()))] - ); - - let idx = frame_idx(vm, offset)?; - let idx = vm.frames.borrow().len() - idx - 1; + let idx = vm.frames.borrow().len() - offset - 1; let frame = &vm.frames.borrow()[idx]; Ok(frame.clone()) } diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 4a3cc3abc0..321d659b8f 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -14,14 +14,14 @@ use std::sync::{Mutex, MutexGuard}; use crate::builtins; use crate::bytecode; -use crate::frame::{ExecutionResult, Frame, Scope}; +use crate::frame::{ExecutionResult, Frame, FrameRef, Scope}; use crate::function::PyFuncArgs; use crate::obj::objbool; use crate::obj::objbuiltinfunc::PyBuiltinFunction; -use crate::obj::objcode; -use crate::obj::objframe; +use crate::obj::objcode::PyCodeRef; +use crate::obj::objdict::PyDictRef; use crate::obj::objfunction::{PyFunction, PyMethod}; -use crate::obj::objgenerator; +use crate::obj::objgenerator::PyGeneratorRef; use crate::obj::objiter; use crate::obj::objlist::PyList; use crate::obj::objsequence; @@ -30,7 +30,7 @@ use crate::obj::objtuple::PyTuple; use crate::obj::objtype; use crate::obj::objtype::PyClassRef; use crate::pyobject::{ - DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TryFromObject, TryIntoRef, + DictProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, PyValue, TryFromObject, TryIntoRef, TypeProtocol, }; use crate::stdlib; @@ -48,7 +48,7 @@ pub struct VirtualMachine { pub sys_module: PyObjectRef, pub stdlib_inits: RefCell>, pub ctx: PyContext, - pub frames: RefCell>, + pub frames: RefCell>, pub wasm_id: Option, } @@ -72,39 +72,51 @@ impl VirtualMachine { } } - pub fn run_code_obj(&self, code: PyObjectRef, scope: Scope) -> PyResult { - let frame = self.ctx.new_frame(code, scope); + pub fn run_code_obj(&self, code: PyCodeRef, scope: Scope) -> PyResult { + let frame = Frame::new(code, scope).into_ref(self); self.run_frame_full(frame) } - pub fn run_frame_full(&self, frame: PyObjectRef) -> PyResult { + pub fn run_frame_full(&self, frame: FrameRef) -> PyResult { match self.run_frame(frame)? { ExecutionResult::Return(value) => Ok(value), _ => panic!("Got unexpected result from function"), } } - pub fn run_frame(&self, frame: PyObjectRef) -> PyResult { + pub fn run_frame(&self, frame: FrameRef) -> PyResult { self.frames.borrow_mut().push(frame.clone()); - let frame = objframe::get_value(&frame); let result = frame.run(self); self.frames.borrow_mut().pop(); result } - pub fn current_frame(&self) -> Ref { - Ref::map(self.frames.borrow(), |frames| { - let index = frames.len() - 1; - let current_frame = &frames[index]; - objframe::get_value(current_frame) - }) + pub fn current_frame(&self) -> Option> { + let frames = self.frames.borrow(); + if frames.is_empty() { + None + } else { + Some(Ref::map(self.frames.borrow(), |frames| { + frames.last().unwrap() + })) + } } pub fn current_scope(&self) -> Ref { - let frame = self.current_frame(); + let frame = self + .current_frame() + .expect("called current_scope but no frames on the stack"); Ref::map(frame, |f| &f.scope) } + pub fn try_class(&self, module: &str, class: &str) -> PyResult { + let class = self + .get_attribute(self.import(module)?, class)? + .downcast() + .expect("not a class"); + Ok(class) + } + pub fn class(&self, module: &str, class: &str) -> PyClassRef { let module = self .import(module) @@ -112,7 +124,7 @@ impl VirtualMachine { let class = self .get_attribute(module.clone(), class) .unwrap_or_else(|_| panic!("module {} has no class {}", module, class)); - class.downcast().unwrap() + class.downcast().expect("not a class") } /// Create a new python string object. @@ -130,10 +142,6 @@ impl VirtualMachine { self.ctx.new_bool(b) } - pub fn new_dict(&self) -> PyObjectRef { - self.ctx.new_dict() - } - pub fn new_empty_exception(&self, exc_type: PyClassRef) -> PyResult { info!("New exception created: no msg"); let args = PyFuncArgs::default(); @@ -167,11 +175,11 @@ impl VirtualMachine { b: PyObjectRef, op: &str, ) -> PyObjectRef { - let a_type_name = objtype::get_type_name(&a.typ()); - let b_type_name = objtype::get_type_name(&b.typ()); self.new_type_error(format!( "Unsupported operand types for '{}': '{}' and '{}'", - op, a_type_name, b_type_name + op, + a.class().name, + b.class().name )) } @@ -224,7 +232,7 @@ impl VirtualMachine { self.ctx.object() } - pub fn get_locals(&self) -> PyObjectRef { + pub fn get_locals(&self) -> PyDictRef { self.current_scope().get_locals().clone() } @@ -249,10 +257,9 @@ impl VirtualMachine { } pub fn import(&self, module: &str) -> PyResult { - let builtins_import = self.builtins.get_item("__import__"); - match builtins_import { - Some(func) => self.invoke(func, vec![self.ctx.new_str(module.to_string())]), - None => Err(self.new_exception( + match self.get_attribute(self.builtins.clone(), "__import__") { + Ok(func) => self.invoke(func, vec![self.ctx.new_str(module.to_string())]), + Err(_) => Err(self.new_exception( self.ctx.exceptions.import_error.clone(), "__import__ not found".to_string(), )), @@ -264,7 +271,7 @@ impl VirtualMachine { pub fn isinstance(&self, obj: &PyObjectRef, cls: &PyClassRef) -> PyResult { // cpython first does an exact check on the type, although documentation doesn't state that // https://github.com/python/cpython/blob/a24107b04c1277e3c1105f98aff5bfa3a98b33a0/Objects/abstract.c#L2408 - if Rc::ptr_eq(&obj.typ(), cls.as_object()) { + if Rc::ptr_eq(&obj.class().into_object(), cls.as_object()) { Ok(true) } else { let ret = self.call_method(cls.as_object(), "__instancecheck__", vec![obj.clone()])?; @@ -280,10 +287,10 @@ impl VirtualMachine { } pub fn call_get_descriptor(&self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult { - let attr_class = attr.type_pyref(); + let attr_class = attr.class(); if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") { - let cls = obj.typ(); - self.invoke(descriptor, vec![attr, obj.clone(), cls]) + let cls = obj.class(); + self.invoke(descriptor, vec![attr, obj.clone(), cls.into_object()]) } else { Ok(attr) } @@ -294,7 +301,7 @@ impl VirtualMachine { T: Into, { // This is only used in the vm for magic methods, which use a greatly simplified attribute lookup. - let cls = obj.type_pyref(); + let cls = obj.class(); match objtype::class_get_attr(&cls, method_name) { Some(func) => { trace!( @@ -343,21 +350,20 @@ impl VirtualMachine { fn invoke_python_function( &self, - code: &PyObjectRef, + code: &PyCodeRef, scope: &Scope, defaults: &PyObjectRef, args: PyFuncArgs, ) -> PyResult { - let code_object = objcode::get_value(code); let scope = scope.child_scope(&self.ctx); - self.fill_locals_from_args(&code_object, &scope.get_locals(), args, defaults)?; + self.fill_locals_from_args(&code.code, &scope.get_locals(), args, defaults)?; // Construct frame: - let frame = self.ctx.new_frame(code.clone(), scope); + let frame = Frame::new(code.clone(), scope).into_ref(self); // If we have a generator, create a new generator - if code_object.is_generator { - Ok(objgenerator::new_generator(frame, self).into_object()) + if code.code.is_generator { + Ok(PyGeneratorRef::new(frame, self).into_object()) } else { self.run_frame_full(frame) } @@ -366,8 +372,8 @@ impl VirtualMachine { pub fn invoke_with_locals( &self, function: PyObjectRef, - cells: PyObjectRef, - locals: PyObjectRef, + cells: PyDictRef, + locals: PyDictRef, ) -> PyResult { if let Some(PyFunction { code, @@ -378,7 +384,7 @@ impl VirtualMachine { let scope = scope .child_scope_with_locals(cells) .child_scope_with_locals(locals); - let frame = self.ctx.new_frame(code.clone(), scope); + let frame = Frame::new(code.clone(), scope).into_ref(self); return self.run_frame_full(frame); } panic!( @@ -390,7 +396,7 @@ impl VirtualMachine { fn fill_locals_from_args( &self, code_object: &bytecode::CodeObject, - locals: &PyObjectRef, + locals: &PyDictRef, args: PyFuncArgs, defaults: &PyObjectRef, ) -> PyResult<()> { @@ -445,11 +451,11 @@ impl VirtualMachine { // Do we support `**kwargs` ? let kwargs = match code_object.varkeywords { bytecode::Varargs::Named(ref kwargs_name) => { - let d = self.new_dict(); + let d = self.ctx.new_dict().into_object(); locals.set_item(&self.ctx, kwargs_name, d.clone()); Some(d) } - bytecode::Varargs::Unnamed => Some(self.new_dict()), + bytecode::Varargs::Unnamed => Some(self.ctx.new_dict().into_object()), bytecode::Varargs::None => None, }; @@ -578,7 +584,7 @@ impl VirtualMachine { // get_method should be used for internal access to magic methods (by-passing // the full getattribute look-up. pub fn get_method(&self, obj: PyObjectRef, method_name: &str) -> PyResult { - let cls = obj.type_pyref(); + let cls = obj.class(); match objtype::class_get_attr(&cls, method_name) { Some(method) => self.call_get_descriptor(method, obj.clone()), None => Err(self.new_type_error(format!("{} has no method {:?}", obj, method_name))), diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 710b18e639..0e13aeb3ab 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use futures::Future; use js_sys::Promise; use num_traits::cast::ToPrimitive; @@ -7,12 +5,15 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::{future_to_promise, JsFuture}; -use rustpython_vm::function::PyFuncArgs; -use rustpython_vm::import::import_module; -use rustpython_vm::obj::objtype::PyClassRef; -use rustpython_vm::obj::{objint, objstr}; +use rustpython_vm::function::{OptionalArg, PyFuncArgs}; +use rustpython_vm::obj::{ + objfunction::PyFunction, + objint, + objstr::{self, PyStringRef}, + objtype::PyClassRef, +}; use rustpython_vm::pyobject::{ - PyContext, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol, + PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use rustpython_vm::VirtualMachine; @@ -45,8 +46,6 @@ impl FetchResponseFormat { fn browser_fetch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(url, Some(vm.ctx.str_type()))]); - let promise_type = import_promise_type(vm)?; - let response_format = args.get_optional_kwarg_with_type("response_format", vm.ctx.str_type(), vm)?; let method = args.get_optional_kwarg_with_type("method", vm.ctx.str_type(), vm)?; @@ -102,7 +101,7 @@ fn browser_fetch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { }) .and_then(JsFuture::from); - Ok(PyPromise::new_obj(promise_type, future_to_promise(future))) + Ok(PyPromise::from_future(future).into_ref(vm).into_object()) } fn browser_request_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -163,115 +162,145 @@ fn browser_cancel_animation_frame(vm: &VirtualMachine, args: PyFuncArgs) -> PyRe pub struct PyPromise { value: Promise, } +pub type PyPromiseRef = PyRef; impl PyValue for PyPromise { fn class(vm: &VirtualMachine) -> PyClassRef { - vm.class(BROWSER_NAME, "Promise") + vm.class("browser", "Promise") } } impl PyPromise { - pub fn new_obj(promise_type: PyClassRef, value: Promise) -> PyObjectRef { - PyObject::new(PyPromise { value }, promise_type.into_object()) + pub fn new(value: Promise) -> PyPromise { + PyPromise { value } } -} - -pub fn get_promise_value(obj: &PyObjectRef) -> Promise { - if let Some(promise) = obj.payload::() { - return promise.value.clone(); + pub fn from_future(future: F) -> PyPromise + where + F: Future + 'static, + { + PyPromise::new(future_to_promise(future)) } - panic!("Inner error getting promise") -} - -pub fn import_promise_type(vm: &VirtualMachine) -> PyResult { - match vm.get_attribute( - import_module(vm, PathBuf::default(), BROWSER_NAME)?, - "Promise", - ) { - Ok(promise) => PyClassRef::try_from_object(vm, promise), - Err(_) => Err(vm.new_not_implemented_error("No Promise".to_string())), + pub fn value(&self) -> Promise { + self.value.clone() } -} - -fn promise_then(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let promise_type = import_promise_type(vm)?; - arg_check!( - vm, - args, - required = [ - (zelf, Some(promise_type.clone())), - (on_fulfill, Some(vm.ctx.function_type())) - ], - optional = [(on_reject, Some(vm.ctx.function_type()))] - ); - - let on_fulfill = on_fulfill.clone(); - let on_reject = on_reject.cloned(); - - let acc_vm = AccessibleVM::from(vm); - let promise = get_promise_value(zelf); + fn then( + zelf: PyPromiseRef, + on_fulfill: PyRef, + on_reject: OptionalArg>, + vm: &VirtualMachine, + ) -> PyPromiseRef { + let acc_vm = AccessibleVM::from(vm); - let ret_future = JsFuture::from(promise).then(move |res| { - let stored_vm = &acc_vm - .upgrade() - .expect("that the vm is valid when the promise resolves"); - let vm = &stored_vm.vm; - let ret = match res { - Ok(val) => { - let val = convert::js_to_py(vm, val); - vm.invoke(on_fulfill, PyFuncArgs::new(vec![val], vec![])) - } - Err(err) => { - if let Some(on_reject) = on_reject { - let err = convert::js_to_py(vm, err); - vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![])) - } else { - return Err(err); + let ret_future = JsFuture::from(zelf.value.clone()).then(move |res| { + let stored_vm = &acc_vm + .upgrade() + .expect("that the vm is valid when the promise resolves"); + let vm = &stored_vm.vm; + let ret = match res { + Ok(val) => { + let val = convert::js_to_py(vm, val); + vm.invoke(on_fulfill.into_object(), PyFuncArgs::new(vec![val], vec![])) } - } - }; - convert::pyresult_to_jsresult(vm, ret) - }); + Err(err) => { + if let OptionalArg::Present(on_reject) = on_reject { + let err = convert::js_to_py(vm, err); + vm.invoke(on_reject.into_object(), PyFuncArgs::new(vec![err], vec![])) + } else { + return Err(err); + } + } + }; + convert::pyresult_to_jsresult(vm, ret) + }); - let ret_promise = future_to_promise(ret_future); + PyPromise::from_future(ret_future).into_ref(vm) + } - Ok(PyPromise::new_obj(promise_type, ret_promise)) + fn catch( + zelf: PyPromiseRef, + on_reject: PyRef, + vm: &VirtualMachine, + ) -> PyPromiseRef { + let acc_vm = AccessibleVM::from(vm); + + let ret_future = JsFuture::from(zelf.value.clone()).then(move |res| { + res.or_else(|err| { + let stored_vm = acc_vm + .upgrade() + .expect("that the vm is valid when the promise resolves"); + let vm = &stored_vm.vm; + let err = convert::js_to_py(vm, err); + let res = vm.invoke(on_reject.into_object(), PyFuncArgs::new(vec![err], vec![])); + convert::pyresult_to_jsresult(vm, res) + }) + }); + + PyPromise::from_future(ret_future).into_ref(vm) + } } -fn promise_catch(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - let promise_type = import_promise_type(vm)?; - arg_check!( - vm, - args, - required = [ - (zelf, Some(promise_type.clone())), - (on_reject, Some(vm.ctx.function_type())) - ] - ); +#[derive(Debug)] +struct Document { + doc: web_sys::Document, +} +type DocumentRef = PyRef; - let on_reject = on_reject.clone(); +impl PyValue for Document { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.class("browser", "Document") + } +} - let acc_vm = AccessibleVM::from(vm); +impl Document { + fn query(zelf: DocumentRef, query: PyStringRef, vm: &VirtualMachine) -> PyResult { + let elem = zelf + .doc + .query_selector(&query.value) + .map_err(|err| convert::js_py_typeerror(vm, err))?; + let elem = match elem { + Some(elem) => Element { elem }.into_ref(vm).into_object(), + None => vm.get_none(), + }; + Ok(elem) + } +} - let promise = get_promise_value(zelf); +#[derive(Debug)] +struct Element { + elem: web_sys::Element, +} +type ElementRef = PyRef; - let ret_future = JsFuture::from(promise).then(move |res| match res { - Ok(val) => Ok(val), - Err(err) => { - let stored_vm = acc_vm - .upgrade() - .expect("that the vm is valid when the promise resolves"); - let vm = &stored_vm.vm; - let err = convert::js_to_py(vm, err); - let res = vm.invoke(on_reject, PyFuncArgs::new(vec![err], vec![])); - convert::pyresult_to_jsresult(vm, res) - } - }); +impl PyValue for Element { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.class("browser", "Element") + } +} - let ret_promise = future_to_promise(ret_future); +impl Element { + fn get_attr( + zelf: ElementRef, + attr: PyStringRef, + default: OptionalArg, + vm: &VirtualMachine, + ) -> PyObjectRef { + match zelf.elem.get_attribute(&attr.value) { + Some(s) => vm.new_str(s), + None => default.into_option().unwrap_or_else(|| vm.get_none()), + } + } - Ok(PyPromise::new_obj(promise_type, ret_promise)) + fn set_attr( + zelf: ElementRef, + attr: PyStringRef, + value: PyStringRef, + vm: &VirtualMachine, + ) -> PyResult<()> { + zelf.elem + .set_attribute(&attr.value, &value.value) + .map_err(|err| convert::js_to_py(vm, err)) + } } fn browser_alert(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -319,19 +348,37 @@ fn browser_prompt(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(result) } -const BROWSER_NAME: &str = "browser"; - pub fn make_module(ctx: &PyContext) -> PyObjectRef { let promise = py_class!(ctx, "Promise", ctx.object(), { - "then" => ctx.new_rustfunc(promise_then), - "catch" => ctx.new_rustfunc(promise_catch) + "then" => ctx.new_rustfunc(PyPromise::then), + "catch" => ctx.new_rustfunc(PyPromise::catch), + }); + + let document_class = py_class!(ctx, "Document", ctx.object(), { + "query" => ctx.new_rustfunc(Document::query), + }); + + let document = PyObject::new( + Document { + doc: window().document().expect("Document missing from window"), + }, + document_class.clone(), + None, + ); + + let element = py_class!(ctx, "Element", ctx.object(), { + "get_attr" => ctx.new_rustfunc(Element::get_attr), + "set_attr" => ctx.new_rustfunc(Element::set_attr), }); - py_module!(ctx, BROWSER_NAME, { + py_module!(ctx, "browser", { "fetch" => ctx.new_rustfunc(browser_fetch), "request_animation_frame" => ctx.new_rustfunc(browser_request_animation_frame), "cancel_animation_frame" => ctx.new_rustfunc(browser_cancel_animation_frame), "Promise" => promise, + "Document" => document_class, + "document" => document, + "Element" => element, "alert" => ctx.new_rustfunc(browser_alert), "confirm" => ctx.new_rustfunc(browser_confirm), "prompt" => ctx.new_rustfunc(browser_prompt), @@ -341,5 +388,5 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { pub fn setup_browser_module(vm: &VirtualMachine) { vm.stdlib_inits .borrow_mut() - .insert(BROWSER_NAME.to_string(), Box::new(make_module)); + .insert("browser".to_string(), Box::new(make_module)); } diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index c7ba1c6161..588c7571cd 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -4,7 +4,7 @@ use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; use rustpython_vm::function::PyFuncArgs; use rustpython_vm::obj::{objbytes, objint, objsequence, objtype}; -use rustpython_vm::pyobject::{DictProtocol, PyObjectRef, PyResult}; +use rustpython_vm::pyobject::{DictProtocol, PyObjectRef, PyResult, PyValue}; use rustpython_vm::VirtualMachine; use crate::browser_module; @@ -115,9 +115,9 @@ pub fn py_to_js(vm: &VirtualMachine, py_obj: PyObjectRef) -> JsValue { } } // the browser module might not be injected - if let Ok(promise_type) = browser_module::import_promise_type(vm) { - if objtype::isinstance(&py_obj, &promise_type) { - return browser_module::get_promise_value(&py_obj).into(); + if vm.try_class("browser", "Promise").is_ok() { + if let Some(py_prom) = py_obj.payload::() { + return py_prom.value().into(); } } @@ -159,8 +159,10 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { if js_val.is_object() { if let Some(promise) = js_val.dyn_ref::() { // the browser module might not be injected - if let Ok(promise_type) = browser_module::import_promise_type(vm) { - return browser_module::PyPromise::new_obj(promise_type, promise.clone()); + if vm.try_class("browser", "Promise").is_ok() { + return browser_module::PyPromise::new(promise.clone()) + .into_ref(vm) + .into_object(); } } if Array::is_array(&js_val) { @@ -185,13 +187,13 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { u8_array.for_each(&mut |byte, _, _| vec.push(byte)); vm.ctx.new_bytes(vec) } else { - let dict = vm.new_dict(); + let dict = vm.ctx.new_dict(); for pair in object_entries(&Object::from(js_val)) { let (key, val) = pair.expect("iteration over object to not fail"); let py_val = js_to_py(vm, val); dict.set_item(&vm.ctx, &String::from(js_sys::JsString::from(key)), py_val); } - dict + dict.into_object() } } else if js_val.is_function() { let func = js_sys::Function::from(js_val); diff --git a/wasm/lib/src/vm_class.rs b/wasm/lib/src/vm_class.rs index fd2181182f..c7e637a9f5 100644 --- a/wasm/lib/src/vm_class.rs +++ b/wasm/lib/src/vm_class.rs @@ -45,7 +45,6 @@ impl StoredVirtualMachine { thread_local! { static STORED_VMS: RefCell>> = RefCell::default(); - static ACTIVE_VMS: RefCell> = RefCell::default(); } #[wasm_bindgen(js_name = vmStore)] @@ -285,8 +284,7 @@ impl WASMVirtualMachine { ref vm, ref scope, .. }| { source.push('\n'); - let code = - compile::compile(&source, &mode, "".to_string(), vm.ctx.code_type()); + let code = compile::compile(vm, &source, &mode, "".to_string()); let code = code.map_err(|err| { let js_err = SyntaxError::new(&format!("Error parsing Python code: {}", err)); if let rustpython_vm::error::CompileError::Parse(ref parse_error) = err { diff --git a/wasm/lib/src/wasm_builtins.rs b/wasm/lib/src/wasm_builtins.rs index d5bc830dfe..ada0c83f53 100644 --- a/wasm/lib/src/wasm_builtins.rs +++ b/wasm/lib/src/wasm_builtins.rs @@ -25,7 +25,7 @@ pub fn format_print_args(vm: &VirtualMachine, args: PyFuncArgs) -> Result Result