Browse Source

Environment Records have been implemented (bar module), exec::scope has now been changed to Environment

pull/18/head
Jason Williams 6 years ago committed by GitHub
parent
commit
5664ea6d1b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 102
      Cargo.lock
  2. 2
      Cargo.toml
  3. 1
      src/bin/bin.rs
  4. 173
      src/lib/environment/declerative_environment_record.rs
  5. 79
      src/lib/environment/environment_record_trait.rs
  6. 246
      src/lib/environment/function_environment_record.rs
  7. 197
      src/lib/environment/global_environment_record.rs
  8. 214
      src/lib/environment/lexical_environment.rs
  9. 6
      src/lib/environment/mod.rs
  10. 124
      src/lib/environment/object_environment_record.rs
  11. 161
      src/lib/exec.rs
  12. 4
      src/lib/js/array.rs
  13. 2
      src/lib/js/boolean.rs
  14. 6
      src/lib/js/console.rs
  15. 8
      src/lib/js/error.rs
  16. 6
      src/lib/js/function.rs
  17. 6
      src/lib/js/json.rs
  18. 6
      src/lib/js/math.rs
  19. 6
      src/lib/js/object.rs
  20. 8
      src/lib/js/string.rs
  21. 73
      src/lib/js/value.rs
  22. 3
      src/lib/lib.rs
  23. 9
      tests/js/test.js

102
Cargo.lock generated

@ -2,18 +2,18 @@
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]] [[package]]
name = "Boa" name = "Boa"
version = "0.1.6" version = "0.1.7"
dependencies = [ dependencies = [
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gc 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "gc 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.0.4" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -23,7 +23,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -31,26 +31,17 @@ name = "cloudabi"
version = "0.0.3" version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "fuchsia-zircon" name = "fuchsia-cprng"
version = "0.3.3" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "gc" name = "gc"
version = "0.3.2" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -65,12 +56,12 @@ dependencies = [
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.3" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.43" version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
@ -93,52 +84,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.5.5" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.2.2" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.1.40" version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "0.2.6" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.80" version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.32" version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -170,12 +161,12 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.40" version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -185,7 +176,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.6" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -203,30 +194,29 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum bitflags 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd1fa8ad26490b0a5cfec99089952250301b6716cdeaa7c9ab229598fb82ab66"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum gc 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75656800ec248b3d0c33b685e442a67e7308009ae59b1f8eb60c4f09ebebb512"
"checksum gc 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "630b15fafc2270fc89de904da9ebb8b950642a5ed7bd99ec3f95558b0831ea9a"
"checksum gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2501c15cbaf28a0c2214617aa85351982a933161d7937fe6cd71c855364e0ea6" "checksum gc_derive 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2501c15cbaf28a0c2214617aa85351982a933161d7937fe6cd71c855364e0ea6"
"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)" = "c6785aa7dd976f5fbf3b71cfd9cd49d7f783c1ff565a858d71031c6c313aa5c6"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252"
"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" "checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f"
"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" "checksum serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)" = "aa5f7c20820475babd2c077c3ab5f8c77a31c15e16ea38687b4c02d3e48680f4"
"checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd"
"checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

2
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "Boa" name = "Boa"
version = "0.1.6" version = "0.1.7"
authors = ["Jason Williams <jase.williams@gmail.com>"] authors = ["Jason Williams <jase.williams@gmail.com>"]
description = "Boa is a Javascript lexer, parser and Just-in-Time compiler written in Rust. Currently, it has support for some of the language." description = "Boa is a Javascript lexer, parser and Just-in-Time compiler written in Rust. Currently, it has support for some of the language."
homepage = "https://github.com/jasonwilliams/boa" homepage = "https://github.com/jasonwilliams/boa"

1
src/bin/bin.rs

@ -12,7 +12,6 @@ pub fn main() {
// Setup executor // Setup executor
let expr = Parser::new(tokens).parse_all().unwrap(); let expr = Parser::new(tokens).parse_all().unwrap();
// print!("{:#?}", expr);
let mut engine: Interpreter = Executor::new(); let mut engine: Interpreter = Executor::new();
let result = engine.run(&expr); let result = engine.run(&expr);

173
src/lib/environment/declerative_environment_record.rs

@ -0,0 +1,173 @@
//! # Declerative Records
//!
//! Each declarative Environment Record is associated with an ECMAScript program scope containing variable,
//! `constant`, `let`, `class`, `module`, `import`, and/or function declarations.
//! A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope.
//! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.github.io/ecma262/#sec-declarative-environment-records)
use crate::environment::environment_record_trait::EnvironmentRecordTrait;
use crate::environment::lexical_environment::{Environment, EnvironmentType};
use crate::js::value::{Value, ValueData};
use gc::Gc;
use std::collections::hash_map::HashMap;
/// Declerative Bindings have a few properties for book keeping purposes, such as mutability (const vs let).
/// Can it be deleted? and strict mode.
///
/// So we need to create a struct to hold these values.
/// From this point onwards, a binding is referring to one of these structures.
#[derive(Trace, Finalize, Debug, Clone)]
pub struct DeclerativeEnvironmentRecordBinding {
pub value: Option<Value>,
pub can_delete: bool,
pub mutable: bool,
pub strict: bool,
}
/// A declarative Environment Record binds the set of identifiers defined by the
/// declarations contained within its scope.
#[derive(Trace, Finalize, Clone)]
pub struct DeclerativeEnvironmentRecord {
pub env_rec: HashMap<String, DeclerativeEnvironmentRecordBinding>,
pub outer_env: Option<Environment>,
}
impl EnvironmentRecordTrait for DeclerativeEnvironmentRecord {
fn has_binding(&self, name: &String) -> bool {
self.env_rec.contains_key(name)
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) {
if self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
self.env_rec.insert(
name,
DeclerativeEnvironmentRecordBinding {
value: None,
can_delete: deletion,
mutable: true,
strict: false,
},
);
}
fn create_immutable_binding(&mut self, name: String, strict: bool) {
if !self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
self.env_rec.insert(
name,
DeclerativeEnvironmentRecordBinding {
value: None,
can_delete: true,
mutable: false,
strict: strict,
},
);
}
fn initialize_binding(&mut self, name: String, value: Value) {
match self.env_rec.get_mut(&name) {
Some(ref mut record) => {
match record.value {
Some(_) => {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been defined", name);
}
None => record.value = Some(value),
}
}
None => {}
}
}
fn set_mutable_binding(&mut self, name: String, value: Value, mut strict: bool) {
if self.env_rec.get(&name).is_none() {
if strict == true {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
}
self.create_mutable_binding(name.clone(), true);
self.initialize_binding(name.clone(), value);
return;
}
let record: &mut DeclerativeEnvironmentRecordBinding = self.env_rec.get_mut(&name).unwrap();
if record.strict {
strict = true
}
if record.value.is_none() {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
}
if record.mutable {
record.value = Some(value);
} else {
if strict {
// TODO: change this when error handling comes into play
panic!("TypeError: Cannot mutate an immutable binding {}", name);
}
}
}
fn get_binding_value(&self, name: String, _strict: bool) -> Value {
if self.env_rec.get(&name).is_some() && self.env_rec.get(&name).unwrap().value.is_some() {
let record: &DeclerativeEnvironmentRecordBinding = self.env_rec.get(&name).unwrap();
record.value.as_ref().unwrap().clone()
} else {
// TODO: change this when error handling comes into play
panic!("ReferenceError: Cannot get binding value for {}", name);
}
}
fn delete_binding(&mut self, name: String) -> bool {
if self.env_rec.get(&name).is_some() {
if self.env_rec.get(&name).unwrap().can_delete {
self.env_rec.remove(&name);
true
} else {
false
}
} else {
false
}
}
fn has_this_binding(&self) -> bool {
false
}
fn has_super_binding(&self) -> bool {
false
}
fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined)
}
fn get_outer_environment(&self) -> Option<Environment> {
None
}
fn set_outer_environment(&mut self, env: Environment) {
self.outer_env = Some(env);
}
fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Declerative;
}
fn get_global_object(&self) -> Option<Value> {
match &self.outer_env {
Some(outer) => outer.borrow().get_global_object(),
None => None,
}
}
}

79
src/lib/environment/environment_record_trait.rs

@ -0,0 +1,79 @@
//! # Environment Records
//!
//! https://tc39.github.io/ecma262/#sec-environment-records
//! https://tc39.github.io/ecma262/#sec-lexical-environments
//!
//! Some environments are stored as JSObjects. This is for GC, i.e we want to keep an environment if a variable is closed-over (a closure is returned).
//! All of the logic to handle scope/environment records are stored in here.
//!
//! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait`
//!
use crate::environment::lexical_environment::{Environment, EnvironmentType};
use crate::js::value::Value;
use gc::{Finalize, Trace};
/// https://tc39.github.io/ecma262/#sec-environment-records
///
/// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods.
/// In this implementation we have a trait which represents the behaviour of all EnvironmentRecord types.
pub trait EnvironmentRecordTrait: Trace + Finalize {
/// Determine if an Environment Record has a binding for the String value N. Return true if it does and false if it does not.
fn has_binding(&self, name: &String) -> bool;
/// Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name.
/// If the Boolean argument deletion is true the binding may be subsequently deleted.
fn create_mutable_binding(&mut self, name: String, deletion: bool);
/// Create a new but uninitialized immutable binding in an Environment Record.
/// The String value N is the text of the bound name.
/// If strict is true then attempts to set it after it has been initialized will always throw an exception,
/// regardless of the strict mode setting of operations that reference that binding.
fn create_immutable_binding(&mut self, name: String, strict: bool);
/// Set the value of an already existing but uninitialized binding in an Environment Record.
/// The String value N is the text of the bound name.
/// V is the value for the binding and is a value of any ECMAScript language type.
fn initialize_binding(&mut self, name: String, value: Value);
/// Set the value of an already existing mutable binding in an Environment Record.
/// The String value `name` is the text of the bound name.
/// value is the `value` for the binding and may be a value of any ECMAScript language type. S is a Boolean flag.
/// If `strict` is true and the binding cannot be set throw a TypeError exception.
fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool);
/// Returns the value of an already existing binding from an Environment Record.
/// The String value N is the text of the bound name.
/// S is used to identify references originating in strict mode code or that
/// otherwise require strict mode reference semantics.
fn get_binding_value(&self, name: String, strict: bool) -> Value;
/// Delete a binding from an Environment Record.
/// The String value name is the text of the bound name.
/// If a binding for name exists, remove the binding and return true.
/// If the binding exists but cannot be removed return false. If the binding does not exist return true.
fn delete_binding(&mut self, name: String) -> bool;
/// Determine if an Environment Record establishes a this binding.
/// Return true if it does and false if it does not.
fn has_this_binding(&self) -> bool;
/// Determine if an Environment Record establishes a super method binding.
/// Return true if it does and false if it does not.
fn has_super_binding(&self) -> bool;
/// If this Environment Record is associated with a with statement, return the with object.
/// Otherwise, return undefined.
fn with_base_object(&self) -> Value;
/// Get the next environment up
fn get_outer_environment(&self) -> Option<Environment>;
/// Set the next environment up
fn set_outer_environment(&mut self, env: Environment);
/// Get the type of environment this is
fn get_environment_type(&self) -> EnvironmentType;
/// Fetch global variable
fn get_global_object(&self) -> Option<Value>;
}

246
src/lib/environment/function_environment_record.rs

@ -0,0 +1,246 @@
//! # Function Environment Records
//!
//! A function Environment Record is a declarative Environment Record that is used to represent
//! the top-level scope of a function and, if the function is not an ArrowFunction,
//! provides a `this` binding.
//! If a function is not an ArrowFunction function and references super,
//! its function Environment Record also contains the state that is used to perform super method invocations
//! from within the function.
//! More info: https://tc39.github.io/ecma262/#sec-function-environment-records
use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecordBinding;
use crate::environment::environment_record_trait::EnvironmentRecordTrait;
use crate::environment::lexical_environment::{Environment, EnvironmentType};
use crate::js::value::{Value, ValueData};
use gc::Gc;
use std::collections::hash_map::HashMap;
/// Different binding status for `this`.
/// Usually set on a function environment record
#[derive(Trace, Finalize, Debug, Clone)]
pub enum BindingStatus {
/// If the value is "lexical", this is an ArrowFunction and does not have a local this value.
Lexical,
/// If initialized the function environment record has already been bound with a `this` value
Initialized,
/// If uninitialized the function environment record has not been bouned with a `this` value
Uninitialized,
}
/// https://tc39.github.io/ecma262/#table-16
#[derive(Trace, Finalize, Clone)]
pub struct FunctionEnvironmentRecord {
pub env_rec: HashMap<String, DeclerativeEnvironmentRecordBinding>,
/// This is the this value used for this invocation of the function.
pub this_value: Value,
/// If the value is "lexical", this is an ArrowFunction and does not have a local this value.
pub this_binding_status: BindingStatus,
/// The function object whose invocation caused this Environment Record to be created.
pub function_object: Value,
/// If the associated function has super property accesses and is not an ArrowFunction,
/// [[HomeObject]] is the object that the function is bound to as a method.
/// The default value for [[HomeObject]] is undefined.
pub home_object: Value,
/// If this Environment Record was created by the [[Construct]] internal method,
/// [[NewTarget]] is the value of the [[Construct]] newTarget parameter.
/// Otherwise, its value is undefined.
pub new_target: Value,
/// Reference to the outer environment to help with the scope chain
/// Option type is needed as some environments can be created before we know what the outer env is
pub outer_env: Option<Environment>,
}
impl FunctionEnvironmentRecord {
pub fn bind_this_value(&mut self, value: Value) {
match self.this_binding_status {
// You can not bind an arrow function, their `this` value comes from the lexical scope above
BindingStatus::Lexical => {
// TODO: change this when error handling comes into play
panic!("Cannot bind to an arrow function!");
}
// You can not bind a function twice
BindingStatus::Initialized => {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot bind to an initialised function!");
}
BindingStatus::Uninitialized => {
self.this_value = value;
self.this_binding_status = BindingStatus::Initialized;
}
}
}
pub fn get_this_binding(&self) -> Value {
match self.this_binding_status {
BindingStatus::Lexical => {
// TODO: change this when error handling comes into play
panic!("There is no this for a lexical function record");
}
BindingStatus::Uninitialized => {
// TODO: change this when error handling comes into play
panic!("Reference Error: Unitialised binding for this function");
}
BindingStatus::Initialized => self.this_value.clone(),
}
}
}
impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
// TODO: get_super_base can't implement until GetPrototypeof is implemented on object
fn has_binding(&self, name: &String) -> bool {
self.env_rec.contains_key(name)
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) {
if !self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
self.env_rec.insert(
name,
DeclerativeEnvironmentRecordBinding {
value: None,
can_delete: deletion,
mutable: true,
strict: false,
},
);
}
fn create_immutable_binding(&mut self, name: String, strict: bool) {
if !self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
self.env_rec.insert(
name,
DeclerativeEnvironmentRecordBinding {
value: None,
can_delete: true,
mutable: false,
strict: strict,
},
);
}
fn initialize_binding(&mut self, name: String, value: Value) {
match self.env_rec.get_mut(&name) {
Some(ref mut record) => {
match record.value {
Some(_) => {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been defined", name);
}
None => record.value = Some(value),
}
}
None => {}
}
}
fn set_mutable_binding(&mut self, name: String, value: Value, mut strict: bool) {
if self.env_rec.get(&name).is_none() {
if strict == true {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
}
self.create_mutable_binding(name.clone(), true);
self.initialize_binding(name.clone(), value);
return;
}
let record: &mut DeclerativeEnvironmentRecordBinding = self.env_rec.get_mut(&name).unwrap();
if record.strict {
strict = true
}
if record.value.is_none() {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
}
if record.mutable {
record.value = Some(value);
} else {
if strict {
// TODO: change this when error handling comes into play
panic!("TypeError: Cannot mutate an immutable binding {}", name);
}
}
}
fn get_binding_value(&self, name: String, _strict: bool) -> Value {
if self.env_rec.get(&name).is_some() && self.env_rec.get(&name).unwrap().value.is_some() {
let record: &DeclerativeEnvironmentRecordBinding = self.env_rec.get(&name).unwrap();
record.value.as_ref().unwrap().clone()
} else {
// TODO: change this when error handling comes into play
panic!("ReferenceError: Cannot get binding value for {}", name);
}
}
fn delete_binding(&mut self, name: String) -> bool {
if self.env_rec.get(&name).is_some() {
if self.env_rec.get(&name).unwrap().can_delete {
self.env_rec.remove(&name);
true
} else {
false
}
} else {
false
}
}
fn has_super_binding(&self) -> bool {
match self.this_binding_status {
BindingStatus::Lexical => false,
_ => {
if self.home_object.is_undefined() {
false
} else {
true
}
}
}
}
fn has_this_binding(&self) -> bool {
match self.this_binding_status {
BindingStatus::Lexical => false,
_ => true,
}
}
fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined)
}
fn get_outer_environment(&self) -> Option<Environment> {
match &self.outer_env {
Some(outer) => Some(outer.clone()),
None => None,
}
}
fn set_outer_environment(&mut self, env: Environment) {
self.outer_env = Some(env);
}
fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Function;
}
fn get_global_object(&self) -> Option<Value> {
match &self.outer_env {
Some(ref outer) => outer.borrow().get_global_object(),
None => None,
}
}
}

197
src/lib/environment/global_environment_record.rs

@ -0,0 +1,197 @@
//! # Global Environment Records
//!
//! A global Environment Record is used to represent the outer most scope that is shared by all
//! of the ECMAScript Script elements that are processed in a common realm.
//! A global Environment Record provides the bindings for built-in globals (clause 18),
//! properties of the global object, and for all top-level declarations (13.2.8, 13.2.10)
//! that occur within a Script.
//! More info: https://tc39.github.io/ecma262/#sec-global-environment-records
use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecord;
use crate::environment::environment_record_trait::EnvironmentRecordTrait;
use crate::environment::lexical_environment::{Environment, EnvironmentType};
use crate::environment::object_environment_record::ObjectEnvironmentRecord;
use crate::js::value::{Value, ValueData};
use gc::Gc;
use std::collections::HashSet;
#[derive(Trace, Finalize, Clone)]
pub struct GlobalEnvironmentRecord {
pub object_record: Box<ObjectEnvironmentRecord>,
pub global_this_binding: Value,
pub declerative_record: Box<DeclerativeEnvironmentRecord>,
pub var_names: HashSet<String>,
}
impl GlobalEnvironmentRecord {
pub fn get_this_binding(&self) -> Value {
return self.global_this_binding.clone();
}
pub fn has_var_decleration(&self, name: &String) -> bool {
return self.var_names.contains(name);
}
pub fn has_lexical_decleration(&self, name: &String) -> bool {
self.declerative_record.has_binding(name)
}
pub fn has_restricted_global_property(&self, name: &String) -> bool {
let global_object = &self.object_record.bindings;
let existing_prop = global_object.get_prop(name.clone());
match existing_prop {
Some(prop) => {
if prop.value.is_undefined() || prop.configurable == true {
return false;
}
true
}
None => false,
}
}
pub fn create_global_var_binding(&mut self, name: String, deletion: bool) {
let obj_rec = &mut self.object_record;
let global_object = &obj_rec.bindings;
let has_property = global_object.has_field(name.clone());
let extensible = global_object.is_extensible();
if !has_property && extensible {
obj_rec.create_mutable_binding(name.clone(), deletion);
obj_rec.initialize_binding(name.clone(), Gc::new(ValueData::Undefined));
}
let var_declared_names = &mut self.var_names;
if !var_declared_names.contains(&name) {
var_declared_names.insert(name.clone());
}
}
pub fn create_global_function_binding(&mut self, name: String, value: Value, deletion: bool) {
let global_object = &mut self.object_record.bindings;
let existing_prop = global_object.get_prop(name.clone());
match existing_prop {
Some(prop) => {
if prop.value.is_undefined() || prop.configurable {
global_object.update_prop(
name,
Some(value),
Some(true),
Some(true),
Some(deletion),
);
}
}
None => {
global_object.update_prop(
name,
Some(value),
Some(true),
Some(true),
Some(deletion),
);
}
}
}
}
impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
fn has_binding(&self, name: &String) -> bool {
if self.declerative_record.has_binding(name) {
return true;
}
self.object_record.has_binding(name)
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) {
if self.declerative_record.has_binding(&name) {
// TODO: change to exception
panic!("Binding already exists!");
}
self.declerative_record
.create_mutable_binding(name.clone(), deletion)
}
fn create_immutable_binding(&mut self, name: String, strict: bool) {
if self.declerative_record.has_binding(&name) {
// TODO: change to exception
panic!("Binding already exists!");
}
self.declerative_record
.create_immutable_binding(name.clone(), strict)
}
fn initialize_binding(&mut self, name: String, value: Value) {
if self.declerative_record.has_binding(&name) {
// TODO: assert binding is in the object environment record
return self
.declerative_record
.initialize_binding(name.clone(), value);
}
panic!("Should not initialized binding without creating first.");
}
fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool) {
if self.declerative_record.has_binding(&name) {
return self
.declerative_record
.set_mutable_binding(name, value, strict);
}
self.object_record.set_mutable_binding(name, value, strict)
}
fn get_binding_value(&self, name: String, strict: bool) -> Value {
if self.declerative_record.has_binding(&name) {
return self.declerative_record.get_binding_value(name, strict);
}
return self.object_record.get_binding_value(name, strict);
}
fn delete_binding(&mut self, name: String) -> bool {
if self.declerative_record.has_binding(&name) {
return self.declerative_record.delete_binding(name.clone());
}
let global: &Value = &self.object_record.bindings;
if global.has_field(name.clone()) {
let status = self.object_record.delete_binding(name.clone());
if status {
let var_names = &mut self.var_names;
if var_names.contains(&name) {
var_names.remove(&name);
return status;
}
}
}
true
}
fn has_this_binding(&self) -> bool {
true
}
fn has_super_binding(&self) -> bool {
false
}
fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined)
}
fn get_outer_environment(&self) -> Option<Environment> {
None
}
fn set_outer_environment(&mut self, _env: Environment) {
unimplemented!()
}
fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Global;
}
fn get_global_object(&self) -> Option<Value> {
Some(self.global_this_binding.clone())
}
}

214
src/lib/environment/lexical_environment.rs

@ -0,0 +1,214 @@
//! # Lexical Environment
//!
//! https://tc39.github.io/ecma262/#sec-lexical-environment-operations
//!
//! The following operations are used to operate upon lexical environments
//! This is the entrypoint to lexical environments.
//!
use crate::environment::declerative_environment_record::DeclerativeEnvironmentRecord;
use crate::environment::environment_record_trait::EnvironmentRecordTrait;
use crate::environment::function_environment_record::{BindingStatus, FunctionEnvironmentRecord};
use crate::environment::global_environment_record::GlobalEnvironmentRecord;
use crate::environment::object_environment_record::ObjectEnvironmentRecord;
use crate::js::value::{Value, ValueData};
use gc::{Gc, GcCell};
use std::collections::hash_map::HashMap;
use std::collections::{HashSet, VecDeque};
use std::debug_assert;
use std::error;
use std::fmt;
/// Environments are wrapped in a Box and then in a GC wrapper
pub type Environment = Gc<GcCell<Box<EnvironmentRecordTrait>>>;
/// Give each environment an easy way to declare its own type
/// This helps with comparisons
#[derive(Debug)]
pub enum EnvironmentType {
Declerative,
Function,
Global,
Object,
}
pub struct LexicalEnvironment {
environment_stack: VecDeque<Environment>,
}
/// An error that occurred during lexing or compiling of the source input.
#[derive(Debug, Clone)]
pub struct EnvironmentError {
details: String,
}
impl EnvironmentError {
pub fn new(msg: &str) -> EnvironmentError {
EnvironmentError {
details: msg.to_string(),
}
}
}
impl fmt::Display for EnvironmentError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.details)
}
}
impl error::Error for EnvironmentError {
fn description(&self) -> &str {
&self.details
}
fn cause(&self) -> Option<&error::Error> {
// Generic error, underlying cause isn't tracked.
None
}
}
impl LexicalEnvironment {
pub fn new(global: Value) -> LexicalEnvironment {
let global_env = new_global_environment(global.clone(), global.clone());
let mut lexical_env = LexicalEnvironment {
environment_stack: VecDeque::new(),
};
// lexical_env.push(global_env);
lexical_env.environment_stack.push_back(global_env);
lexical_env
}
pub fn push(&mut self, env: Environment) {
let current_env: Environment = self.get_current_environment().clone();
env.borrow_mut().set_outer_environment(current_env);
self.environment_stack.push_back(env);
}
pub fn pop(&mut self) {
self.environment_stack.pop_back();
}
pub fn get_global_object(&self) -> Option<Value> {
let global = &self.environment_stack[0];
global.borrow().get_global_object()
}
pub fn create_mutable_binding(&mut self, name: String, deletion: bool) {
self.get_current_environment()
.borrow_mut()
.create_mutable_binding(name, deletion)
}
pub fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool) {
let env = self.get_current_environment();
env.borrow_mut().set_mutable_binding(name, value, strict);
}
pub fn initialize_binding(&mut self, name: String, value: Value) {
let env = self.get_current_environment();
env.borrow_mut().initialize_binding(name, value);
}
/// get_current_environment_ref is used when you only need to borrow the environment
/// (you only need to add a new variable binding, or you want to fetch a value)
pub fn get_current_environment_ref(&self) -> &Environment {
&self
.environment_stack
.get(self.environment_stack.len() - 1)
.unwrap()
}
/// When neededing to clone an environment (linking it with another environnment)
/// cloning is more suited. The GC will remove the env once nothing is linking to it anymore
pub fn get_current_environment(&mut self) -> &mut Environment {
self.environment_stack.back_mut().unwrap()
}
pub fn get_binding_value(&mut self, name: String) -> Value {
let env: Environment = self.get_current_environment().clone();
let borrowed_env = env.borrow();
let result = borrowed_env.has_binding(&name);
if result {
return borrowed_env.get_binding_value(name, false);
}
// Check outer scope
if borrowed_env.get_outer_environment().is_some() {
let mut outer: Option<Environment> = borrowed_env.get_outer_environment();
while outer.is_some() {
if outer.as_ref().unwrap().borrow().has_binding(&name) {
return outer.unwrap().borrow().get_binding_value(name, false);
}
outer = outer.unwrap().borrow().get_outer_environment();
}
}
Gc::new(ValueData::Undefined)
}
}
pub fn new_declerative_environment(env: Option<Environment>) -> Environment {
let boxed_env = Box::new(DeclerativeEnvironmentRecord {
env_rec: HashMap::new(),
outer_env: env,
});
Gc::new(GcCell::new(boxed_env))
}
pub fn new_function_environment(
f: Value,
new_target: Value,
outer: Option<Environment>,
) -> Environment {
debug_assert!(f.is_function());
debug_assert!(new_target.is_object() || new_target.is_undefined());
Gc::new(GcCell::new(Box::new(FunctionEnvironmentRecord {
env_rec: HashMap::new(),
function_object: f.clone(),
this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported
home_object: Gc::new(ValueData::Undefined),
new_target: new_target,
outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.github.io/ecma262/#sec-ecmascript-function-objects
this_value: Gc::new(ValueData::Undefined), // TODO: this_value should start as an Option as its not always there to begin with
})))
}
pub fn new_object_environment(object: Value, environment: Option<Environment>) -> Environment {
Gc::new(GcCell::new(Box::new(ObjectEnvironmentRecord {
bindings: object,
outer_env: environment,
/// Object Environment Records created for with statements (13.11)
/// can provide their binding object as an implicit this value for use in function calls.
/// The capability is controlled by a withEnvironment Boolean value that is associated
/// with each object Environment Record. By default, the value of withEnvironment is false
/// for any object Environment Record.
with_environment: false,
})))
}
pub fn new_global_environment(global: Value, this_value: Value) -> Environment {
let obj_rec = Box::new(ObjectEnvironmentRecord {
bindings: global,
outer_env: None,
/// Object Environment Records created for with statements (13.11)
/// can provide their binding object as an implicit this value for use in function calls.
/// The capability is controlled by a withEnvironment Boolean value that is associated
/// with each object Environment Record. By default, the value of withEnvironment is false
/// for any object Environment Record.
with_environment: false,
});
let dcl_rec = Box::new(DeclerativeEnvironmentRecord {
env_rec: HashMap::new(),
outer_env: None,
});
Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord {
object_record: obj_rec,
global_this_binding: this_value,
declerative_record: dcl_rec,
var_names: HashSet::new(),
})))
}

6
src/lib/environment/mod.rs

@ -0,0 +1,6 @@
pub mod declerative_environment_record;
pub mod environment_record_trait;
pub mod function_environment_record;
pub mod global_environment_record;
pub mod lexical_environment;
pub mod object_environment_record;

124
src/lib/environment/object_environment_record.rs

@ -0,0 +1,124 @@
//! # Object Records
//!
//! Each object Environment Record is associated with an object called its binding object.
//! An object Environment Record binds the set of string identifier names that directly
//! correspond to the property names of its binding object.
//! Property keys that are not strings in the form of an IdentifierName are not included in the set of bound identifiers.
//! More info: [Object Records](https://tc39.github.io/ecma262/#sec-object-environment-records)
use crate::environment::environment_record_trait::EnvironmentRecordTrait;
use crate::environment::lexical_environment::{Environment, EnvironmentType};
use crate::js::object::Property;
use crate::js::value::{Value, ValueData};
use gc::Gc;
#[derive(Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord {
pub bindings: Value,
pub with_environment: bool,
pub outer_env: Option<Environment>,
}
impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn has_binding(&self, name: &String) -> bool {
if !self.bindings.has_field(name.to_string()) {
return false;
}
if !self.with_environment {
return true;
}
// TODO: implement unscopables
true
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) {
// TODO: could save time here and not bother generating a new undefined object,
// only for it to be replace with the real value later. We could just add the name to a Vector instead
let bindings = &mut self.bindings;
let uninitialized = Gc::new(ValueData::Undefined);
let mut prop = Property::new(uninitialized);
prop.enumerable = true;
prop.writable = true;
prop.configurable = deletion;
bindings.set_prop(name, prop);
}
fn create_immutable_binding(&mut self, _name: String, _strict: bool) {
unimplemented!()
}
fn initialize_binding(&mut self, name: String, value: Value) {
// We should never need to check if a binding has been created,
// As all calls to create_mutable_binding are followed by initialized binding
// The below is just a check.
debug_assert!(self.has_binding(&name));
return self.set_mutable_binding(name, value, false);
}
fn set_mutable_binding(&mut self, name: String, value: Value, strict: bool) {
debug_assert!(value.is_object() || value.is_function());
let bindings = &mut self.bindings;
bindings.update_prop(name, Some(value.clone()), None, None, Some(strict));
}
fn get_binding_value(&self, name: String, strict: bool) -> Value {
if self.bindings.has_field(name.clone()) {
return self.bindings.get_field(name);
}
if !strict {
return Gc::new(ValueData::Undefined);
}
// TODO: throw error here
// Error handling not implemented yet
Gc::new(ValueData::Undefined)
}
fn delete_binding(&mut self, name: String) -> bool {
self.bindings.remove_prop(&name);
true
}
fn has_this_binding(&self) -> bool {
false
}
fn has_super_binding(&self) -> bool {
false
}
fn with_base_object(&self) -> Value {
// Object Environment Records return undefined as their
// WithBaseObject unless their withEnvironment flag is true.
if self.with_environment {
return self.bindings.clone();
}
Gc::new(ValueData::Undefined)
}
fn get_outer_environment(&self) -> Option<Environment> {
match &self.outer_env {
Some(outer) => Some(outer.clone()),
None => None,
}
}
fn set_outer_environment(&mut self, env: Environment) {
self.outer_env = Some(env);
}
fn get_environment_type(&self) -> EnvironmentType {
return EnvironmentType::Function;
}
fn get_global_object(&self) -> Option<Value> {
match &self.outer_env {
Some(outer) => outer.borrow().get_global_object(),
None => None,
}
}
}

161
src/lib/exec.rs

@ -1,3 +1,4 @@
use crate::environment::lexical_environment::{new_function_environment, LexicalEnvironment};
use crate::js::function::{Function, RegularFunction}; use crate::js::function::{Function, RegularFunction};
use crate::js::object::{INSTANCE_PROTOTYPE, PROTOTYPE}; use crate::js::object::{INSTANCE_PROTOTYPE, PROTOTYPE};
use crate::js::value::{from_value, to_value, ResultValue, Value, ValueData}; use crate::js::value::{from_value, to_value, ResultValue, Value, ValueData};
@ -8,27 +9,11 @@ use crate::syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp};
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
/// A variable scope
#[derive(Trace, Finalize, Clone, Debug)]
pub struct Scope {
/// The value of `this` in the scope
pub this: Value,
/// The variables declared in the scope
pub vars: Value,
}
/// An execution engine /// An execution engine
pub trait Executor { pub trait Executor {
/// Make a new execution engine /// Make a new execution engine
fn new() -> Self; fn new() -> Self;
/// Set a global variable called `name` with the value `val`
fn set_global(&mut self, name: String, val: Value) -> Value;
/// Resolve the global variable `name`
fn get_global(&self, name: String) -> Value;
/// Create a new scope and return it
fn make_scope(&mut self, this: Value) -> Scope;
/// Destroy the current scope
fn destroy_scope(&mut self) -> Scope;
/// Run an expression /// Run an expression
fn run(&mut self, expr: &Expr) -> ResultValue; fn run(&mut self, expr: &Expr) -> ResultValue;
} }
@ -36,59 +21,25 @@ pub trait Executor {
/// A Javascript intepreter /// A Javascript intepreter
pub struct Interpreter { pub struct Interpreter {
/// An object representing the global object /// An object representing the global object
global: Value, environment: LexicalEnvironment,
/// The scopes
pub scopes: Vec<Scope>,
}
impl Interpreter {
#[inline(always)]
/// Get the current scope
pub fn scope(&self) -> &Scope {
self.scopes.get(self.scopes.len() - 1).unwrap()
}
} }
impl Executor for Interpreter { impl Executor for Interpreter {
fn new() -> Interpreter { fn new() -> Interpreter {
let global = ValueData::new_obj(None); let global = ValueData::new_obj(None);
object::init(global.clone()); let test = ValueData::new_obj(None);
console::init(global.clone()); object::init(&global);
math::init(global.clone()); console::init(&global);
array::init(global.clone()); math::init(&global);
function::init(global.clone()); array::init(&global);
json::init(global.clone()); function::init(&global);
string::init(global.clone()); json::init(&global);
string::init(&global);
Interpreter { Interpreter {
global: global.clone(), environment: LexicalEnvironment::new(global.clone()),
scopes: vec![Scope {
this: global.clone(),
vars: global.clone(),
}],
} }
} }
fn set_global(&mut self, name: String, val: Value) -> Value {
self.global.borrow().set_field(name, val)
}
fn get_global(&self, name: String) -> Value {
self.global.borrow().get_field(name)
}
fn make_scope(&mut self, this: Value) -> Scope {
let scope = Scope {
this: this,
vars: ValueData::new_obj(None),
};
self.scopes.push(scope.clone());
scope
}
fn destroy_scope(&mut self) -> Scope {
self.scopes.pop().unwrap()
}
fn run(&mut self, expr: &Expr) -> ResultValue { fn run(&mut self, expr: &Expr) -> ResultValue {
match expr.def { match expr.def {
ExprDef::ConstExpr(Const::Null) => Ok(to_value(None::<()>)), ExprDef::ConstExpr(Const::Null) => Ok(to_value(None::<()>)),
@ -112,21 +63,7 @@ impl Executor for Interpreter {
Ok(obj) Ok(obj)
} }
ExprDef::LocalExpr(ref name) => { ExprDef::LocalExpr(ref name) => {
let mut val = Gc::new(ValueData::Undefined); let val = self.environment.get_binding_value(name.to_string());
for scope in self.scopes.iter().rev() {
let vars = scope.vars.clone();
let vars_ptr = vars.borrow();
match *vars_ptr.clone() {
ValueData::Object(ref obj, _) => match obj.borrow().get(name) {
Some(v) => {
val = v.value.clone();
break;
}
None => (),
},
_ => unreachable!(),
}
}
Ok(val) Ok(val)
} }
ExprDef::GetConstFieldExpr(ref obj, ref field) => { ExprDef::GetConstFieldExpr(ref obj, ref field) => {
@ -152,28 +89,39 @@ impl Executor for Interpreter {
obj.borrow().get_field(field.borrow().to_string()), obj.borrow().get_field(field.borrow().to_string()),
) )
} }
_ => (self.global.clone(), self.run(&callee.clone())?), _ => (
self.environment.get_global_object().unwrap(),
self.run(&callee.clone())?,
), // 'this' binding should come from the function's self-contained environment
}; };
let mut v_args = Vec::with_capacity(args.len()); let mut v_args = Vec::with_capacity(args.len());
for arg in args.iter() { for arg in args.iter() {
v_args.push(self.run(arg)?); v_args.push(self.run(arg)?);
} }
match *func { match *func {
ValueData::Function(ref func) => match *func.borrow() { ValueData::Function(ref inner_func) => match *inner_func.borrow() {
Function::NativeFunc(ref ntv) => { Function::NativeFunc(ref ntv) => {
let func = ntv.data; let func = ntv.data;
func(this, self.run(callee)?, v_args) func(this, self.run(callee)?, v_args)
} }
Function::RegularFunc(ref data) => { Function::RegularFunc(ref data) => {
let scope = self.make_scope(this); let env = &mut self.environment;
let scope_vars_ptr = scope.vars.borrow(); // New target (second argument) is only needed for constructors, just pass undefined
let undefined = Gc::new(ValueData::Undefined);
env.push(new_function_environment(
func.clone(),
undefined,
Some(env.get_current_environment_ref().clone()),
));
for i in 0..data.args.len() { for i in 0..data.args.len() {
let name = data.args.get(i).unwrap(); let name = data.args.get(i).unwrap();
let expr = v_args.get(i).unwrap(); let expr = v_args.get(i).unwrap();
scope_vars_ptr.set_field(name.clone(), expr.clone()); self.environment.create_mutable_binding(name.clone(), false);
self.environment
.initialize_binding(name.clone(), expr.to_owned());
} }
let result = self.run(&data.expr); let result = self.run(&data.expr);
self.destroy_scope(); self.environment.pop();
result result
} }
}, },
@ -226,23 +174,26 @@ impl Executor for Interpreter {
Ok(result) Ok(result)
} }
ExprDef::ObjectDeclExpr(ref map) => { ExprDef::ObjectDeclExpr(ref map) => {
let obj = ValueData::new_obj(Some(self.global.clone())); let global_val = &self.environment.get_global_object().unwrap();
let obj = ValueData::new_obj(Some(global_val));
for (key, val) in map.iter() { for (key, val) in map.iter() {
obj.borrow().set_field(key.clone(), r#try!(self.run(val))); obj.borrow().set_field(key.clone(), self.run(val)?);
} }
Ok(obj) Ok(obj)
} }
ExprDef::ArrayDeclExpr(ref arr) => { ExprDef::ArrayDeclExpr(ref arr) => {
let arr_map = ValueData::new_obj(Some(self.global.clone())); let global_val = &self.environment.get_global_object().unwrap();
let arr_map = ValueData::new_obj(Some(global_val));
let mut index: i32 = 0; let mut index: i32 = 0;
for val in arr.iter() { for val in arr.iter() {
let val = r#try!(self.run(val)); let val = self.run(val)?;
arr_map.borrow().set_field(index.to_string(), val); arr_map.borrow().set_field(index.to_string(), val);
index += 1; index += 1;
} }
arr_map.borrow().set_field_slice( arr_map.borrow().set_field_slice(
INSTANCE_PROTOTYPE, INSTANCE_PROTOTYPE,
self.get_global("Array".to_string()) self.environment
.get_binding_value("Array".to_string())
.borrow() .borrow()
.get_field_slice(PROTOTYPE), .get_field_slice(PROTOTYPE),
); );
@ -254,9 +205,10 @@ impl Executor for Interpreter {
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
let val = Gc::new(ValueData::Function(GcCell::new(function))); let val = Gc::new(ValueData::Function(GcCell::new(function)));
if name.is_some() { if name.is_some() {
self.global self.environment
.borrow() .create_mutable_binding(name.clone().unwrap(), false);
.set_field(name.clone().unwrap(), val.clone()); self.environment
.initialize_binding(name.clone().unwrap(), val.clone())
} }
Ok(val) Ok(val)
} }
@ -343,21 +295,28 @@ impl Executor for Interpreter {
this.borrow() this.borrow()
.set_field_slice(INSTANCE_PROTOTYPE, func.borrow().get_field_slice(PROTOTYPE)); .set_field_slice(INSTANCE_PROTOTYPE, func.borrow().get_field_slice(PROTOTYPE));
match *func { match *func {
ValueData::Function(ref func) => match func.clone().into_inner() { ValueData::Function(ref inner_func) => match inner_func.clone().into_inner() {
Function::NativeFunc(ref ntv) => { Function::NativeFunc(ref ntv) => {
let func = ntv.data; let func = ntv.data;
func(this, self.run(callee)?, v_args) func(this, self.run(callee)?, v_args)
} }
Function::RegularFunc(ref data) => { Function::RegularFunc(ref data) => {
let scope = self.make_scope(this); // Create new scope
let scope_vars_ptr = scope.vars.borrow(); let env = &mut self.environment;
env.push(new_function_environment(
func.clone(),
this.clone(),
Some(env.get_current_environment_ref().clone()),
));
for i in 0..data.args.len() { for i in 0..data.args.len() {
let name = data.args.get(i).unwrap(); let name = data.args.get(i).unwrap();
let expr = v_args.get(i).unwrap(); let expr = v_args.get(i).unwrap();
scope_vars_ptr.set_field(name.clone(), (*expr).clone()); env.create_mutable_binding(name.clone(), false);
env.initialize_binding(name.clone(), expr.to_owned());
} }
let result = self.run(&data.expr); let result = self.run(&data.expr);
self.destroy_scope(); self.environment.pop();
result result
} }
}, },
@ -370,13 +329,12 @@ impl Executor for Interpreter {
}, },
ExprDef::ThrowExpr(ref ex) => Err(r#try!(self.run(ex))), ExprDef::ThrowExpr(ref ex) => Err(r#try!(self.run(ex))),
ExprDef::AssignExpr(ref ref_e, ref val_e) => { ExprDef::AssignExpr(ref ref_e, ref val_e) => {
let val = r#try!(self.run(val_e)); let val = self.run(val_e)?;
match ref_e.def { match ref_e.def {
ExprDef::LocalExpr(ref name) => { ExprDef::LocalExpr(ref name) => {
self.scope() self.environment.create_mutable_binding(name.clone(), false);
.vars self.environment
.borrow() .initialize_binding(name.clone(), val.clone());
.set_field(name.clone(), val.clone());
} }
ExprDef::GetConstFieldExpr(ref obj, ref field) => { ExprDef::GetConstFieldExpr(ref obj, ref field) => {
let val_obj = r#try!(self.run(obj)); let val_obj = r#try!(self.run(obj));
@ -387,15 +345,14 @@ impl Executor for Interpreter {
Ok(val) Ok(val)
} }
ExprDef::VarDeclExpr(ref vars) => { ExprDef::VarDeclExpr(ref vars) => {
let scope_vars = self.scope().vars.clone();
let scope_vars_ptr = scope_vars.borrow();
for var in vars.iter() { for var in vars.iter() {
let (name, value) = var.clone(); let (name, value) = var.clone();
let val = match value { let val = match value {
Some(v) => r#try!(self.run(&v)), Some(v) => r#try!(self.run(&v)),
None => Gc::new(ValueData::Null), None => Gc::new(ValueData::Null),
}; };
scope_vars_ptr.set_field(name.clone(), val); self.environment.create_mutable_binding(name.clone(), false);
self.environment.initialize_binding(name, val);
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }

4
src/lib/js/array.rs

@ -1,6 +1,6 @@
use gc::Gc;
use crate::js::function::NativeFunctionData; use crate::js::function::NativeFunctionData;
use crate::js::value::{to_value, ResultValue, Value, ValueData}; use crate::js::value::{to_value, ResultValue, Value, ValueData};
use gc::Gc;
/// Create a new array /// Create a new array
pub fn make_array(this: Value, _: Value, _: Vec<Value>) -> ResultValue { pub fn make_array(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
@ -14,6 +14,6 @@ pub fn _create() -> Value {
array array
} }
/// Initialise the global object with the `Array` object /// Initialise the global object with the `Array` object
pub fn init(global: Value) { pub fn init(global: &Value) {
global.set_field_slice("Array", _create()); global.set_field_slice("Array", _create());
} }

2
src/lib/js/boolean.rs

@ -11,7 +11,7 @@ pub fn _create(global: Value) -> Value {
boolean boolean
} }
/// Initialise the global object with the `Error` object /// Initialise the global object with the `Error` object
pub fn init(global: Value) { pub fn init(global: &Value) {
let global_ptr = global.borrow(); let global_ptr = global.borrow();
global_ptr.set_field_slice("Boolean", _create(global)); global_ptr.set_field_slice("Boolean", _create(global));
} }

6
src/lib/js/console.rs

@ -65,7 +65,7 @@ pub fn error(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
/// Create a new `console` object /// Create a new `console` object
pub fn _create(global: Value) -> Value { pub fn _create(global: &Value) -> Value {
let console = ValueData::new_obj(Some(global)); let console = ValueData::new_obj(Some(global));
console.set_field_slice("log", to_value(log as NativeFunctionData)); console.set_field_slice("log", to_value(log as NativeFunctionData));
console.set_field_slice("error", to_value(error as NativeFunctionData)); console.set_field_slice("error", to_value(error as NativeFunctionData));
@ -73,6 +73,6 @@ pub fn _create(global: Value) -> Value {
console console
} }
/// Initialise the global object with the `console` object /// Initialise the global object with the `console` object
pub fn init(global: Value) { pub fn init(global: &Value) {
global.set_field_slice("console", _create(global.clone())); global.set_field_slice("console", _create(global));
} }

8
src/lib/js/error.rs

@ -1,7 +1,7 @@
use gc::Gc;
use crate::js::function::NativeFunctionData; use crate::js::function::NativeFunctionData;
use crate::js::object::PROTOTYPE; use crate::js::object::PROTOTYPE;
use crate::js::value::{to_value, ResultValue, Value, ValueData}; use crate::js::value::{to_value, ResultValue, Value, ValueData};
use gc::Gc;
/// Create a new error /// Create a new error
pub fn make_error(this: Value, _: Value, args: Vec<Value>) -> ResultValue { pub fn make_error(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
@ -17,7 +17,7 @@ pub fn to_string(this: Value, _: Value, _: Vec<Value>) -> ResultValue {
Ok(to_value(format!("{}: {}", name, message).to_string())) Ok(to_value(format!("{}: {}", name, message).to_string()))
} }
/// Create a new `Error` object /// Create a new `Error` object
pub fn _create(global: Value) -> Value { pub fn _create(global: &Value) -> Value {
let prototype = ValueData::new_obj(Some(global)); let prototype = ValueData::new_obj(Some(global));
prototype.set_field_slice("message", to_value("")); prototype.set_field_slice("message", to_value(""));
prototype.set_field_slice("name", to_value("Error")); prototype.set_field_slice("name", to_value("Error"));
@ -27,6 +27,6 @@ pub fn _create(global: Value) -> Value {
error error
} }
/// Initialise the global object with the `Error` object /// Initialise the global object with the `Error` object
pub fn init(global: Value) { pub fn init(global: &Value) {
global.set_field_slice("Error", _create(global.clone())); global.set_field_slice("Error", _create(global));
} }

6
src/lib/js/function.rs

@ -1,8 +1,8 @@
use gc::Gc;
use crate::js::object::{ObjectData, Property}; use crate::js::object::{ObjectData, Property};
use crate::js::value::{to_value, ResultValue, Value, ValueData}; use crate::js::value::{to_value, ResultValue, Value, ValueData};
use std::collections::HashMap;
use crate::syntax::ast::expr::Expr; use crate::syntax::ast::expr::Expr;
use gc::Gc;
use std::collections::HashMap;
/// fn(this, callee, arguments) /// fn(this, callee, arguments)
pub type NativeFunctionData = fn(Value, Value, Vec<Value>) -> ResultValue; pub type NativeFunctionData = fn(Value, Value, Vec<Value>) -> ResultValue;
@ -73,7 +73,7 @@ pub fn _create() -> Value {
to_value(function) to_value(function)
} }
/// Initialise the global object with the `Function` object /// Initialise the global object with the `Function` object
pub fn init(global: Value) { pub fn init(global: &Value) {
let global_ptr = global; let global_ptr = global;
global_ptr.set_field_slice("Function", _create()); global_ptr.set_field_slice("Function", _create());
} }

6
src/lib/js/json.rs

@ -20,7 +20,7 @@ pub fn stringify(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
} }
/// Create a new `JSON` object /// Create a new `JSON` object
pub fn _create(global: Value) -> Value { pub fn _create(global: &Value) -> Value {
let object = ValueData::new_obj(Some(global)); let object = ValueData::new_obj(Some(global));
object.set_field_slice("stringify", to_value(stringify as NativeFunctionData)); object.set_field_slice("stringify", to_value(stringify as NativeFunctionData));
object.set_field_slice("parse", to_value(parse as NativeFunctionData)); object.set_field_slice("parse", to_value(parse as NativeFunctionData));
@ -28,6 +28,6 @@ pub fn _create(global: Value) -> Value {
} }
/// Initialise the global object with the `JSON` object /// Initialise the global object with the `JSON` object
pub fn init(global: Value) { pub fn init(global: &Value) {
global.set_field_slice("JSON", _create(global.clone())); global.set_field_slice("JSON", _create(global));
} }

6
src/lib/js/math.rs

@ -186,7 +186,7 @@ pub fn tan(_: Value, _: Value, args: Vec<Value>) -> ResultValue {
})) }))
} }
/// Create a new `Math` object /// Create a new `Math` object
pub fn _create(global: Value) -> Value { pub fn _create(global: &Value) -> Value {
let math = ValueData::new_obj(Some(global)); let math = ValueData::new_obj(Some(global));
math.set_field_slice("E", to_value(f64::consts::E)); math.set_field_slice("E", to_value(f64::consts::E));
math.set_field_slice("LN2", to_value(f64::consts::LN_2)); math.set_field_slice("LN2", to_value(f64::consts::LN_2));
@ -218,6 +218,6 @@ pub fn _create(global: Value) -> Value {
math math
} }
/// Initialise the `Math` object on the global object /// Initialise the `Math` object on the global object
pub fn init(global: Value) { pub fn init(global: &Value) {
global.set_field_slice("Math", _create(global.clone())); global.set_field_slice("Math", _create(global));
} }

6
src/lib/js/object.rs

@ -118,7 +118,7 @@ pub fn has_own_prop(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
} }
/// Create a new `Object` object /// Create a new `Object` object
pub fn _create(global: Value) -> Value { pub fn _create(global: &Value) -> Value {
let object = to_value(make_object as NativeFunctionData); let object = to_value(make_object as NativeFunctionData);
let prototype = ValueData::new_obj(Some(global)); let prototype = ValueData::new_obj(Some(global));
prototype.set_field_slice( prototype.set_field_slice(
@ -144,6 +144,6 @@ pub fn _create(global: Value) -> Value {
} }
/// Initialise the `Object` object on the global object /// Initialise the `Object` object on the global object
pub fn init(global: Value) { pub fn init(global: &Value) {
global.set_field_slice("Object", _create(global.clone())); global.set_field_slice("Object", _create(global));
} }

8
src/lib/js/string.rs

@ -78,7 +78,7 @@ pub fn char_code_at(this: Value, _: Value, args: Vec<Value>) -> ResultValue {
} }
/// Create a new `String` object /// Create a new `String` object
pub fn _create(global: Value) -> Value { pub fn _create(global: &Value) -> Value {
let string = to_value(make_string as NativeFunctionData); let string = to_value(make_string as NativeFunctionData);
let proto = ValueData::new_obj(Some(global)); let proto = ValueData::new_obj(Some(global));
let prop = Property { let prop = Property {
@ -97,8 +97,8 @@ pub fn _create(global: Value) -> Value {
string string
} }
/// Initialise the `String` object on the global object /// Initialise the `String` object on the global object
pub fn init(global: Value) { pub fn init(global: &Value) {
global.set_field_slice("String", _create(global.clone())); global.set_field_slice("String", _create(global));
} }
#[cfg(test)] #[cfg(test)]
@ -107,7 +107,7 @@ mod tests {
#[test] #[test]
fn check_string_constructor_is_function() { fn check_string_constructor_is_function() {
let global = ValueData::new_obj(None); let global = ValueData::new_obj(None);
let string_constructor = _create(global); let string_constructor = _create(&global);
assert_eq!(string_constructor.is_function(), true); assert_eq!(string_constructor.is_function(), true);
} }
} }

73
src/lib/js/value.rs

@ -45,7 +45,7 @@ pub enum ValueData {
impl ValueData { impl ValueData {
/// Returns a new empty object /// Returns a new empty object
pub fn new_obj(global: Option<Value>) -> Value { pub fn new_obj(global: Option<&Value>) -> Value {
let mut obj: ObjectData = HashMap::new(); let mut obj: ObjectData = HashMap::new();
let private_obj: ObjectData = HashMap::new(); let private_obj: ObjectData = HashMap::new();
if global.is_some() { if global.is_some() {
@ -72,6 +72,14 @@ impl ValueData {
)) ))
} }
/// This will tell us if we can exten an object or not, not properly implemented yet, for now always returns true
/// For scalar types it should be false, for objects check the private field for extensibilaty. By default true
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal would turn extensible to false
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze would also turn extensible to false
pub fn is_extensible(&self) -> bool {
true
}
/// Returns true if the value is an object /// Returns true if the value is an object
pub fn is_object(&self) -> bool { pub fn is_object(&self) -> bool {
match *self { match *self {
@ -174,6 +182,20 @@ impl ValueData {
} }
} }
/// remove_prop removes a property from a Value object.
/// It will return a boolean based on if the value was removed, if there was no value to remove false is returned
pub fn remove_prop(&self, field: &String) {
match *self {
ValueData::Object(ref obj, _) => obj.borrow_mut().deref_mut().remove(field),
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
ValueData::Function(ref func) => match func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut func) => func.object.remove(field),
Function::RegularFunc(ref mut func) => func.object.remove(field),
},
_ => None,
};
}
/// Resolve the property in the object /// Resolve the property in the object
/// Returns a copy of the Property /// Returns a copy of the Property
pub fn get_prop(&self, field: String) -> Option<Property> { pub fn get_prop(&self, field: String) -> Option<Property> {
@ -188,6 +210,8 @@ impl ValueData {
let obj: ObjectData = match *self { let obj: ObjectData = match *self {
ValueData::Object(ref obj, _) => { ValueData::Object(ref obj, _) => {
let hash = obj.clone(); let hash = obj.clone();
// TODO: This will break, we should return a GcCellRefMut instead
// into_inner will consume the wrapped value and remove it from the hashmap
hash.into_inner() hash.into_inner()
} }
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the * // Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
@ -207,6 +231,45 @@ impl ValueData {
} }
} }
/// update_prop will overwrite individual [Property] fields, unlike
/// Set_prop, which will overwrite prop with a new Property
/// Mostly used internally for now
pub fn update_prop(
&self,
field: String,
value: Option<Value>,
enumerable: Option<bool>,
writable: Option<bool>,
configurable: Option<bool>,
) {
let obj: Option<ObjectData> = match self {
ValueData::Object(ref obj, _) => Some(obj.borrow_mut().deref_mut().clone()),
// Accesing .object on borrow() seems to automatically dereference it, so we don't need the *
ValueData::Function(ref func) => match func.borrow_mut().deref_mut() {
Function::NativeFunc(ref mut func) => Some(func.object.clone()),
Function::RegularFunc(ref mut func) => Some(func.object.clone()),
},
_ => None,
};
if obj.is_none() {
return ();
}
let mut hashmap = obj.unwrap();
// Use value, or walk up the prototype chain
match hashmap.get_mut(&field) {
Some(ref mut prop) => {
prop.value = value.unwrap_or(prop.value.clone());
prop.enumerable = enumerable.unwrap_or(prop.enumerable);
prop.writable = writable.unwrap_or(prop.writable);
prop.configurable = configurable.unwrap_or(prop.configurable);
}
// Try again with next prop up the chain
None => (),
}
}
/// Resolve the property in the object /// Resolve the property in the object
/// Returns a copy of the Property /// Returns a copy of the Property
pub fn get_private_prop(&self, field: String) -> Option<Property> { pub fn get_private_prop(&self, field: String) -> Option<Property> {
@ -270,6 +333,14 @@ impl ValueData {
} }
} }
/// Check to see if the Value has the field, mainly used by environment records
pub fn has_field(&self, field: String) -> bool {
match self.get_prop(field) {
Some(_) => true,
None => false,
}
}
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
pub fn get_field_slice<'a>(&self, field: &'a str) -> Value { pub fn get_field_slice<'a>(&self, field: &'a str) -> Value {
self.get_field(field.to_string()) self.get_field(field.to_string())

3
src/lib/lib.rs

@ -1,11 +1,12 @@
extern crate chrono;
extern crate gc; extern crate gc;
extern crate rand; extern crate rand;
extern crate serde_json; extern crate serde_json;
extern crate chrono;
#[macro_use] #[macro_use]
extern crate gc_derive; extern crate gc_derive;
pub mod environment;
pub mod exec; pub mod exec;
pub mod js; pub mod js;
pub mod syntax; pub mod syntax;

9
tests/js/test.js

@ -1,2 +1,7 @@
var a = new String("ABC"); var a = "World";
a.charCodeAt(-1); function jason() {
console.log(a);
return true;
}
jason();

Loading…
Cancel
Save