├── .gitignore
├── .gitmodules
├── .project
├── Cargo.lock
├── Cargo.toml
├── README.md
├── ecma-262 5.1.txt
├── src
├── contrib
│ ├── mod.rs
│ └── test262.rs
├── debug.rs
├── errors.rs
├── gc
│ ├── handles
│ │ ├── array.rs
│ │ ├── array_local.rs
│ │ ├── array_root.rs
│ │ ├── local.rs
│ │ ├── mod.rs
│ │ ├── ptr.rs
│ │ └── root.rs
│ ├── mod.rs
│ ├── os.rs
│ └── strategy
│ │ ├── copying.rs
│ │ └── mod.rs
├── ir
│ ├── builder.rs
│ └── mod.rs
├── lib.rs
├── main.rs
├── rt
│ ├── allocators.rs
│ ├── boolean.rs
│ ├── env
│ │ ├── array.rs
│ │ ├── boolean.rs
│ │ ├── console.rs
│ │ ├── date.rs
│ │ ├── error.rs
│ │ ├── function.rs
│ │ ├── global.rs
│ │ ├── json
│ │ │ ├── lexer.rs
│ │ │ ├── mod.rs
│ │ │ ├── parser.rs
│ │ │ └── writer.rs
│ │ ├── math.rs
│ │ ├── mod.rs
│ │ ├── number.rs
│ │ ├── object.rs
│ │ ├── regexp.rs
│ │ └── string
│ │ │ ├── mod.rs
│ │ │ └── replacer.rs
│ ├── fmt
│ │ ├── bigint.rs
│ │ └── mod.rs
│ ├── interpreter.rs
│ ├── iterator.rs
│ ├── mod.rs
│ ├── null.rs
│ ├── number.rs
│ ├── object
│ │ ├── array_store.rs
│ │ ├── hash_store.rs
│ │ ├── mod.rs
│ │ └── sparse_array.rs
│ ├── regexp.rs
│ ├── runtime.rs
│ ├── scope.rs
│ ├── stack.rs
│ ├── string.rs
│ ├── undefined.rs
│ ├── utf.rs
│ ├── value.rs
│ └── walker.rs
├── syntax
│ ├── ast
│ │ ├── mod.rs
│ │ └── visitor.rs
│ ├── lexer.rs
│ ├── mod.rs
│ ├── parser
│ │ ├── locals
│ │ │ ├── block_walker.rs
│ │ │ └── mod.rs
│ │ └── mod.rs
│ ├── reader.rs
│ └── token.rs
├── trace.rs
└── util
│ ├── interner.rs
│ ├── iter.rs
│ ├── manualbox.rs
│ ├── matchers.rs
│ └── mod.rs
├── test.bat
├── test262-ignore.json
└── tests
└── tc39.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /gdbscript.txt
3 | /succeeded
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "tests/tc39"]
2 | path = tests/tc39
3 | url = https://github.com/tc39/test262
4 | branch = master
5 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | rjs
4 |
5 |
6 |
7 |
8 |
9 | com.github.rustdt.ide.core.Builder
10 |
11 |
12 |
13 |
14 |
15 | com.github.rustdt.ide.core.nature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | [root]
2 | name = "rjs"
3 | version = "0.0.1"
4 | dependencies = [
5 | "chrono 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
6 | "lazy_static 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
7 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
8 | "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
9 | "regex 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
10 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
11 | "strtod 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
12 | "time 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
13 | ]
14 |
15 | [[package]]
16 | name = "aho-corasick"
17 | version = "0.2.1"
18 | source = "registry+https://github.com/rust-lang/crates.io-index"
19 | dependencies = [
20 | "memchr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
21 | ]
22 |
23 | [[package]]
24 | name = "chrono"
25 | version = "0.2.14"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | dependencies = [
28 | "num 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
29 | "time 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
30 | ]
31 |
32 | [[package]]
33 | name = "gcc"
34 | version = "0.3.8"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 |
37 | [[package]]
38 | name = "lazy_static"
39 | version = "0.1.11"
40 | source = "registry+https://github.com/rust-lang/crates.io-index"
41 |
42 | [[package]]
43 | name = "libc"
44 | version = "0.1.8"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 |
47 | [[package]]
48 | name = "memchr"
49 | version = "0.1.3"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | dependencies = [
52 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
53 | ]
54 |
55 | [[package]]
56 | name = "num"
57 | version = "0.1.25"
58 | source = "registry+https://github.com/rust-lang/crates.io-index"
59 | dependencies = [
60 | "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
61 | "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
62 | ]
63 |
64 | [[package]]
65 | name = "rand"
66 | version = "0.3.8"
67 | source = "registry+https://github.com/rust-lang/crates.io-index"
68 | dependencies = [
69 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
70 | ]
71 |
72 | [[package]]
73 | name = "regex"
74 | version = "0.1.38"
75 | source = "registry+https://github.com/rust-lang/crates.io-index"
76 | dependencies = [
77 | "aho-corasick 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
78 | "memchr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
79 | "regex-syntax 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
80 | ]
81 |
82 | [[package]]
83 | name = "regex-syntax"
84 | version = "0.1.2"
85 | source = "registry+https://github.com/rust-lang/crates.io-index"
86 |
87 | [[package]]
88 | name = "rustc-serialize"
89 | version = "0.3.15"
90 | source = "registry+https://github.com/rust-lang/crates.io-index"
91 |
92 | [[package]]
93 | name = "strtod"
94 | version = "0.0.1"
95 | source = "registry+https://github.com/rust-lang/crates.io-index"
96 |
97 | [[package]]
98 | name = "time"
99 | version = "0.1.26"
100 | source = "registry+https://github.com/rust-lang/crates.io-index"
101 | dependencies = [
102 | "gcc 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
103 | "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
104 | ]
105 |
106 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 |
3 | name = "rjs"
4 | version = "0.0.1"
5 | authors = [ "Pieter van Ginkel " ]
6 |
7 | [features]
8 |
9 | trace = []
10 | tracegc = []
11 |
12 | [[bin]]
13 |
14 | name = "rjs"
15 | path = "src/main.rs"
16 | doc = false
17 |
18 | [lib]
19 |
20 | name = "rjs"
21 | path = "src/lib.rs"
22 |
23 | [dependencies]
24 |
25 | libc = "0.1"
26 | lazy_static = "0.1.10"
27 | rustc-serialize = "0.3"
28 | chrono = "0.2.14"
29 | time = "0.1.26"
30 | rand = "0.3.8"
31 | strtod = "0.0.1"
32 | regex = "0.1.38"
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript on Rust
2 |
3 | `rjs` is a native JavaScript implementation in Rust.
4 |
5 | ## The project
6 |
7 | The goal of `rjs` is to provide a fast, native JavaScript implementation in Rust.
8 | Currently `rjs` is in alpha which means that the performance and stability is
9 | not representative of where we want to end up, and that the public API may still
10 | change. See the [roadmap](https://github.com/rust-js/rjs/wiki/Roadmap) for a detailed
11 | description of the current state of the project and the end goals.
12 |
13 | ## Contributing
14 |
15 | Contribution can be done by creating a pull request. Pull requests for small issues
16 | are always welcome. If you want to pick up a larger feature, please contact
17 | the maintainer to get started.
18 |
19 | ## Documentation
20 |
21 | The documentation can be found at http://rust-js.github.io/rjs/rjs/. This is still
22 | work in progress, so a lot of information is still missing.
23 |
24 | ## License
25 |
26 | `rjs` is licensed under the Apache 2.0 license.
27 |
--------------------------------------------------------------------------------
/src/contrib/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod test262;
2 |
--------------------------------------------------------------------------------
/src/debug.rs:
--------------------------------------------------------------------------------
1 | use std::cell::RefCell;
2 | use std::sync::Mutex;
3 |
4 | static mut DEBUG_ENABLED : bool = false;
5 |
6 | lazy_static! {
7 | static ref DEBUG_OUT : Mutex> = Mutex::new(RefCell::new(String::new()));
8 | }
9 |
10 | macro_rules! debugln {
11 | ($fmt:expr) => (debug!(concat!($fmt, "\n")));
12 | ($fmt:expr, $($arg:tt)*) => (debug!(concat!($fmt, "\n"), $($arg)*));
13 | }
14 |
15 | macro_rules! debug {
16 | ($fmt:expr) => ( if cfg!(not(ndebug)) { $crate::debug::debug_write($fmt); });
17 | ($fmt:expr, $($arg:tt)*) => ( if cfg!(not(ndebug)) { $crate::debug::debug_write(&format!($fmt, $($arg)*)); })
18 | }
19 |
20 | pub fn debug_write(string: &str) {
21 | if !unsafe { DEBUG_ENABLED } {
22 | return;
23 | }
24 |
25 | let guard = DEBUG_OUT.lock();
26 | let lock = guard.unwrap();
27 | lock.borrow_mut().push_str(string);
28 | }
29 |
30 | pub fn debug_enable(enabled: bool) {
31 | unsafe { DEBUG_ENABLED = enabled };
32 | }
33 |
34 | pub fn reset() -> String {
35 | let guard = DEBUG_OUT.lock();
36 | let lock = guard.unwrap();
37 |
38 | let mut string = lock.borrow_mut();
39 | let result = string.clone();
40 | *string = String::new();
41 |
42 | result
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/src/errors.rs:
--------------------------------------------------------------------------------
1 | pub static TYPE_NOT_A_FUNCTION : &'static str = "Value is not a function";
2 | pub static TYPE_MISSING_ARGUMENT : &'static str = "Missing argument";
3 | pub static TYPE_EXPECTED_ARRAY_ITEM : &'static str = "Expected at least one array item";
4 | pub static TYPE_INVALID : &'static str = "Unexpected type";
5 | pub static TYPE_CANNOT_PUT : &'static str = "Cannot set property";
6 | pub static TYPE_CANNOT_HAS_INSTANCE : &'static str = "Instance of parameter must be an object";
7 | pub static TYPE_ACCESSOR_NOT_CALLABLE : &'static str = "Accessor is not callable";
8 | pub static TYPE_WRITABLE_VALUE_INVALID_ON_ACCESSOR : &'static str = "Writable and value invalid for accessors";
9 | pub static TYPE_NOT_EXTENSIBLE : &'static str = "Object is not extensible";
10 | pub static TYPE_CANNOT_WRITE : &'static str = "Property is not writable";
11 | pub static TYPE_CANNOT_DELETE : &'static str = "Property cannot be deleted";
12 | pub static TYPE_NOT_A_CONSTRUCTOR : &'static str = "Not a constructor";
13 | pub static TYPE_UNDEFINED : &'static str = "Invalid operation on undefined";
14 | pub static TYPE_NULL : &'static str = "Invalid operation on null";
15 | pub static TYPE_IN_RHS_NOT_OBJECT : &'static str = "Target of in must be an object";
16 | pub static TYPE_CANNOT_ACCESS_ARGUMENTS_PROPERTY : &'static str = "Cannot access caller or callee of arguments";
17 | pub static TYPE_CANNOT_ACCESS_FUNCTION_PROPERTY : &'static str = "Cannot access caller or arguments of function";
18 | pub static TYPE_CANNOT_CALL_TO_STRING : &'static str = "Cannot call toString";
19 | pub static SYNTAX_CANNOT_RESOLVE_PROPERTY : &'static str = "Cannot resolve property";
20 | pub static TYPE_NOT_CALLABLE : &'static str = "Target must be callable";
21 | pub static TYPE_PROPERTY_ONLY_HAS_GETTER : &'static str = "Cannot set property because it only has a getter";
22 | pub static TYPE_NOT_DATE : &'static str = "Object is not a Date";
23 | pub static TYPE_NOT_COERCIBLE : &'static str = "Value is null or undefined";
24 | pub static TYPE_INVALID_ARGUMENTS_ARRAY : &'static str = "Arguments argument is not of a valid type";
25 | pub static TYPE_CYCLICAL_REFERENCE : &'static str = "Cannot stringify object structure with cyclical references";
26 | pub static TYPE_INVALID_REGEXP_ARGS : &'static str = "Flags cannot be combined with a RegExp object";
27 | pub static SYNTAX_INVALID_REGEXP_FLAGS : &'static str = "Invalid flags";
28 | pub static SYNTAX_INVALID_REGEX : &'static str = "Invalid regular expression";
29 |
--------------------------------------------------------------------------------
/src/gc/handles/array.rs:
--------------------------------------------------------------------------------
1 | use gc::{ArrayLocal, GcAllocator, ptr_t};
2 | use std::ops::{Deref, DerefMut, Index, IndexMut};
3 | use std::marker::PhantomData;
4 | use std::ptr;
5 | use std::mem::{size_of, transmute};
6 | use std::slice;
7 | use std::fmt;
8 |
9 | pub struct Array {
10 | ptr: ptr_t,
11 | _type: PhantomData
12 | }
13 |
14 | impl Array {
15 | pub fn ptr(&self) -> ptr_t {
16 | self.ptr
17 | }
18 |
19 | pub fn from_ptr(ptr: ptr_t) -> Array {
20 | Array {
21 | ptr: ptr,
22 | _type: PhantomData
23 | }
24 | }
25 |
26 | pub fn null() -> Array {
27 | Self::from_ptr(ptr::null())
28 | }
29 |
30 | pub fn is_null(&self) -> bool {
31 | self.ptr.is_null()
32 | }
33 |
34 | pub fn len(&self) -> usize {
35 | unsafe { *transmute::<_, *const usize>(self.ptr) }
36 | }
37 |
38 | pub fn as_local(&self, allocator: &U) -> ArrayLocal {
39 | allocator.alloc_array_local_from_array(*self)
40 | }
41 | }
42 |
43 | impl Copy for Array {}
44 |
45 | impl Clone for Array {
46 | fn clone(&self) -> Array {
47 | Self::from_ptr(self.ptr)
48 | }
49 | }
50 |
51 | impl fmt::Debug for Array {
52 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
53 | write!(fmt, "Ptr {{ ptr: {:?} }}", self.ptr)
54 | }
55 | }
56 |
57 | impl Array {
58 | pub fn copy(from: Array, from_offset: usize, to: Array, to_offset: usize, count: usize) {
59 | unsafe {
60 | assert!(from_offset + count <= from.len() && to_offset + count <= to.len());
61 |
62 | ptr::copy(
63 | from.ptr.offset((size_of::() + from_offset * size_of::()) as isize),
64 | transmute(to.ptr.offset((size_of::() + to_offset * size_of::()) as isize)),
65 | count * size_of::()
66 | );
67 | }
68 | }
69 | }
70 |
71 | impl Deref for Array {
72 | type Target = [T];
73 |
74 | fn deref(&self) -> &[T] {
75 | unsafe {
76 | let size = *transmute::<_, *const usize>(self.ptr);
77 | let ptr = self.ptr.offset(size_of::() as isize);
78 |
79 | slice::from_raw_parts(
80 | transmute(ptr),
81 | size
82 | )
83 | }
84 | }
85 | }
86 |
87 | impl DerefMut for Array {
88 | fn deref_mut(&mut self) -> &mut [T] {
89 | unsafe {
90 | let size = *transmute::<_, *const usize>(self.ptr);
91 | let ptr = self.ptr.offset(size_of::() as isize);
92 |
93 | slice::from_raw_parts_mut(
94 | transmute(ptr),
95 | size
96 | )
97 | }
98 | }
99 | }
100 |
101 | impl Index for Array {
102 | type Output = T;
103 |
104 | fn index(&self, index: usize) -> &>::Output {
105 | assert!(index < self.len());
106 |
107 | unsafe {
108 | let ptr = self.ptr.offset(size_of::() as isize);
109 | let ptr = transmute::<_, *const T>(ptr);
110 | let ptr = ptr.offset(index as isize);
111 |
112 | transmute::<_, &T>(ptr)
113 | }
114 | }
115 | }
116 |
117 | impl IndexMut for Array {
118 | fn index_mut(&mut self, index: usize) -> &mut >::Output {
119 | assert!(index < self.len());
120 |
121 | unsafe {
122 | let ptr = self.ptr.offset(size_of::() as isize);
123 | let ptr = transmute::<_, *const T>(ptr);
124 | let ptr = ptr.offset(index as isize);
125 |
126 | transmute::<_, &mut T>(ptr)
127 | }
128 | }
129 | }
130 |
131 | pub trait AsArray {
132 | fn as_ptr(&self) -> Array;
133 | }
134 |
135 | impl AsArray for Array {
136 | fn as_ptr(&self) -> Array {
137 | *self
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/gc/handles/array_local.rs:
--------------------------------------------------------------------------------
1 | use gc::{Array, AsArray, ArrayRoot, GcAllocator};
2 | use std::ops::{Deref, DerefMut};
3 |
4 | pub struct ArrayLocal {
5 | handle: *const Array
6 | }
7 |
8 | impl ArrayLocal {
9 | pub unsafe fn new(handle: *const Array) -> ArrayLocal {
10 | ArrayLocal {
11 | handle: handle
12 | }
13 | }
14 |
15 | pub fn as_root(&self, allocator: &U) -> ArrayRoot {
16 | allocator.alloc_array_root_from_local(*self)
17 | }
18 | }
19 |
20 | impl Copy for ArrayLocal { }
21 |
22 | impl Clone for ArrayLocal {
23 | fn clone(&self) -> ArrayLocal {
24 | ArrayLocal {
25 | handle: self.handle
26 | }
27 | }
28 | }
29 |
30 | impl Deref for ArrayLocal {
31 | type Target = [T];
32 |
33 | fn deref(&self) -> &[T] {
34 | unsafe { &**self.handle }
35 | }
36 | }
37 |
38 | impl DerefMut for ArrayLocal {
39 | fn deref_mut(&mut self) -> &mut [T] {
40 | unsafe { &mut **(self.handle as *mut Array) }
41 | }
42 | }
43 |
44 | impl AsArray for ArrayLocal {
45 | fn as_ptr(&self) -> Array {
46 | unsafe { *self.handle }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/gc/handles/array_root.rs:
--------------------------------------------------------------------------------
1 | use gc::{Array, ArrayLocal, RootHandles, GcHeap, AsArray, AsPtr, GcAllocator};
2 | use std::ops::{Deref, DerefMut};
3 | use std::marker::PhantomData;
4 | use std::mem::{size_of, transmute};
5 | use std::slice;
6 | use std::rc::Rc;
7 |
8 | pub struct ArrayRoot {
9 | handles: Rc,
10 | handle: u32,
11 | _type: PhantomData
12 | }
13 |
14 | impl<'a, T> ArrayRoot {
15 | pub unsafe fn new>(heap: &'a GcHeap, ptr: U) -> ArrayRoot {
16 | ArrayRoot {
17 | handles: heap.handles.clone(),
18 | handle: heap.handles.add(ptr.as_ptr().ptr()),
19 | _type: PhantomData
20 | }
21 | }
22 |
23 | pub fn as_local(&self, allocator: &U) -> ArrayLocal {
24 | allocator.alloc_array_local_from_root(self)
25 | }
26 | }
27 |
28 | impl Deref for ArrayRoot {
29 | type Target = [T];
30 |
31 | fn deref(&self) -> &[T] {
32 | unsafe {
33 | let ptr = self.handles.get_target(self.handle);
34 | let size = *transmute::<_, *const usize>(ptr);
35 | let ptr = ptr.offset(size_of::() as isize);
36 |
37 | slice::from_raw_parts(
38 | transmute(ptr),
39 | size
40 | )
41 | }
42 | }
43 | }
44 |
45 | impl DerefMut for ArrayRoot {
46 | fn deref_mut(&mut self) -> &mut [T] {
47 | unsafe {
48 | let ptr = self.handles.get_target(self.handle);
49 | let size = *transmute::<_, *const usize>(ptr);
50 | let ptr = ptr.offset(size_of::() as isize);
51 |
52 | slice::from_raw_parts_mut(
53 | transmute(ptr),
54 | size
55 | )
56 | }
57 | }
58 | }
59 |
60 | impl Clone for ArrayRoot {
61 | fn clone(&self) -> ArrayRoot {
62 | ArrayRoot {
63 | handles: self.handles.clone(),
64 | handle: self.handles.clone_root(self.handle),
65 | _type: PhantomData
66 | }
67 | }
68 | }
69 |
70 | impl Drop for ArrayRoot {
71 | fn drop(&mut self) {
72 | self.handles.remove(self.handle);
73 | }
74 | }
75 |
76 | impl AsArray for ArrayRoot {
77 | fn as_ptr(&self) -> Array {
78 | unsafe { Array::from_ptr(self.handles.get_target(self.handle)) }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/gc/handles/local.rs:
--------------------------------------------------------------------------------
1 | use gc::{Ptr, Root, AsPtr, GcAllocator};
2 | use std::ops::{Deref, DerefMut};
3 |
4 | pub struct Local {
5 | handle: *const Ptr
6 | }
7 |
8 | impl Local {
9 | pub unsafe fn new(handle: *const Ptr) -> Local {
10 | Local {
11 | handle: handle
12 | }
13 | }
14 |
15 | pub fn as_root(&self, allocator: &U) -> Root {
16 | allocator.alloc_root_from_local(*self)
17 | }
18 | }
19 |
20 | impl Copy for Local {}
21 |
22 | impl Clone for Local {
23 | fn clone(&self) -> Local {
24 | Local {
25 | handle: self.handle
26 | }
27 | }
28 | }
29 |
30 | impl Deref for Local {
31 | type Target = T;
32 |
33 | fn deref(&self) -> &T {
34 | unsafe { &**self.handle }
35 | }
36 | }
37 |
38 | impl DerefMut for Local {
39 | fn deref_mut(&mut self) -> &mut T {
40 | unsafe { &mut **(self.handle as *mut Ptr) }
41 | }
42 | }
43 |
44 | impl AsPtr for Local {
45 | fn as_ptr(&self) -> Ptr {
46 | unsafe { *self.handle }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/gc/handles/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod array_local;
2 | pub mod array_root;
3 | pub mod array;
4 | pub mod local;
5 | pub mod ptr;
6 | pub mod root;
7 |
8 | pub use self::array_local::ArrayLocal;
9 | pub use self::array_root::ArrayRoot;
10 | pub use self::array::{Array, AsArray};
11 | pub use self::local::Local;
12 | pub use self::ptr::{Ptr, AsPtr};
13 | pub use self::root::Root;
14 |
--------------------------------------------------------------------------------
/src/gc/handles/ptr.rs:
--------------------------------------------------------------------------------
1 | use gc::{GcAllocator, Local, ptr_t};
2 | use std::ops::{Deref, DerefMut};
3 | use std::marker::PhantomData;
4 | use std::ptr;
5 | use std::mem::transmute;
6 | use std::fmt;
7 |
8 | pub struct Ptr {
9 | ptr: ptr_t,
10 | _type: PhantomData
11 | }
12 |
13 | impl Ptr {
14 | pub fn ptr(&self) -> ptr_t {
15 | self.ptr
16 | }
17 |
18 | pub fn from_ptr(ptr: ptr_t) -> Ptr {
19 | Ptr {
20 | ptr: ptr,
21 | _type: PhantomData
22 | }
23 | }
24 |
25 | pub fn null() -> Ptr {
26 | Self::from_ptr(ptr::null())
27 | }
28 |
29 | pub fn is_null(&self) -> bool {
30 | self.ptr.is_null()
31 | }
32 |
33 | pub fn as_local(&self, allocator: &U) -> Local {
34 | allocator.alloc_local_from_ptr(*self)
35 | }
36 | }
37 |
38 | impl PartialEq for Ptr {
39 | fn eq(&self, other: &Ptr) -> bool {
40 | self.ptr == other.ptr
41 | }
42 | }
43 |
44 | impl Copy for Ptr { }
45 |
46 | impl Clone for Ptr {
47 | fn clone(&self) -> Ptr {
48 | Self::from_ptr(self.ptr)
49 | }
50 | }
51 |
52 | impl fmt::Debug for Ptr {
53 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
54 | write!(fmt, "Ptr {{ ptr: {:?} }}", self.ptr)
55 | }
56 | }
57 |
58 | impl Deref for Ptr {
59 | type Target = T;
60 |
61 | fn deref(&self) -> &T {
62 | unsafe { transmute(self.ptr) }
63 | }
64 | }
65 |
66 | impl DerefMut for Ptr {
67 | fn deref_mut(&mut self) -> &mut T {
68 | unsafe { transmute(self.ptr) }
69 | }
70 | }
71 |
72 | pub trait AsPtr {
73 | fn as_ptr(&self) -> Ptr;
74 | }
75 |
76 | impl AsPtr for Ptr {
77 | fn as_ptr(&self) -> Ptr {
78 | *self
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/gc/handles/root.rs:
--------------------------------------------------------------------------------
1 | use gc::{Ptr, Local, RootHandles, GcHeap, GcAllocator, AsPtr};
2 | use std::ops::{Deref, DerefMut};
3 | use std::marker::PhantomData;
4 | use std::mem::transmute;
5 | use std::rc::Rc;
6 |
7 | pub struct Root {
8 | handles: Rc,
9 | handle: u32,
10 | _type: PhantomData
11 | }
12 |
13 | impl Root {
14 | pub unsafe fn new>(heap: &GcHeap, ptr: U) -> Root {
15 | Root {
16 | handles: heap.handles.clone(),
17 | handle: heap.handles.add(ptr.as_ptr().ptr()),
18 | _type: PhantomData
19 | }
20 | }
21 |
22 | pub fn as_local(&self, allocator: &U) -> Local {
23 | allocator.alloc_local_from_root(self)
24 | }
25 | }
26 |
27 | impl Deref for Root {
28 | type Target = T;
29 |
30 | fn deref(&self) -> &T {
31 | unsafe {
32 | let ptr = self.handles.get_target(self.handle);
33 | transmute(ptr)
34 | }
35 | }
36 | }
37 |
38 | impl DerefMut for Root {
39 | fn deref_mut(&mut self) -> &mut T {
40 | unsafe {
41 | let ptr = self.handles.get_target(self.handle);
42 | transmute(ptr)
43 | }
44 | }
45 | }
46 |
47 | impl Clone for Root {
48 | fn clone(&self) -> Root {
49 | Root {
50 | handles: self.handles.clone(),
51 | handle: self.handles.clone_root(self.handle),
52 | _type: PhantomData
53 | }
54 | }
55 | }
56 |
57 | impl Drop for Root {
58 | fn drop(&mut self) {
59 | self.handles.remove(self.handle);
60 | }
61 | }
62 |
63 | impl AsPtr for Root {
64 | fn as_ptr(&self) -> Ptr {
65 | unsafe { Ptr::from_ptr(self.handles.get_target(self.handle)) }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/gc/os.rs:
--------------------------------------------------------------------------------
1 | // See https://github.com/jemalloc/jemalloc/blob/dev/src/chunk_mmap.c for how jemalloc allocates memory.
2 |
3 | extern crate libc;
4 |
5 | use self::libc::*;
6 | use std::ptr;
7 | use std::mem;
8 | use gc::ptr_t;
9 |
10 | #[cfg(target_os = "windows")]
11 | unsafe fn map(addr: ptr_t, size: usize) -> ptr_t {
12 | assert!(size != 0);
13 |
14 | /*
15 | * If VirtualAlloc can't allocate at the given address when one is
16 | * given, it fails and returns NULL.
17 | */
18 | let ret = VirtualAlloc(ptr::null_mut(), size as size_t, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
19 |
20 | assert!(
21 | ret == ptr::null_mut() ||
22 | (addr == ptr::null() && ret as ptr_t != addr) ||
23 | (addr != ptr::null() && ret as ptr_t == addr)
24 | );
25 |
26 | mem::transmute(ret)
27 | }
28 |
29 | /* LINUX VERSION
30 | static void *
31 | pages_map(void *addr, size_t size)
32 | {
33 | void *ret;
34 |
35 | assert(size != 0);
36 |
37 | / *
38 | * We don't use MAP_FIXED here, because it can cause the *replacement*
39 | * of existing mappings, and we only want to create new mappings.
40 | * /
41 | ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
42 | -1, 0);
43 | assert(ret != NULL);
44 |
45 | if (ret == MAP_FAILED)
46 | ret = NULL;
47 | else if (addr != NULL && ret != addr) {
48 | / *
49 | * We succeeded in mapping memory, but not in the right place.
50 | * /
51 | pages_unmap(ret, size);
52 | ret = NULL;
53 | }
54 |
55 | assert(ret == NULL || (addr == NULL && ret != addr)
56 | || (addr != NULL && ret == addr));
57 | return (ret);
58 | }
59 | */
60 |
61 | #[cfg(target_os = "windows")]
62 | unsafe fn unmap(addr: ptr_t, _: usize) {
63 | if VirtualFree(mem::transmute(addr), 0, MEM_RELEASE) == 0 {
64 | panic!("error in VirtualFree");
65 | }
66 | }
67 |
68 | /* LINUX VERSION
69 | static void
70 | pages_unmap(void *addr, size_t size)
71 | {
72 |
73 | if (munmap(addr, size) == -1)
74 | {
75 | char buf[BUFERROR_BUF];
76 |
77 | buferror(get_errno(), buf, sizeof(buf));
78 | malloc_printf(": Error in "
79 | "munmap"
80 | "(): %s\n", buf);
81 | if (opt_abort)
82 | abort();
83 | }
84 | }
85 | */
86 |
87 | pub struct Memory {
88 | ptr: ptr_t,
89 | size: usize
90 | }
91 |
92 | impl Memory {
93 | pub fn empty() -> Memory {
94 | Memory {
95 | ptr: ptr::null_mut(),
96 | size: 0
97 | }
98 | }
99 |
100 | pub fn alloc(size: usize) -> Option {
101 | let ptr = unsafe { map(ptr::null(), size) };
102 | if ptr.is_null() {
103 | None
104 | } else {
105 | Some(Memory {
106 | ptr: ptr,
107 | size: size
108 | })
109 | }
110 | }
111 |
112 | pub unsafe fn ptr(&self) -> ptr_t {
113 | self.ptr
114 | }
115 |
116 | pub fn size(&self) -> usize {
117 | self.size
118 | }
119 | }
120 |
121 | impl Drop for Memory {
122 | fn drop(&mut self) {
123 | if self.size > 0 {
124 | unsafe { unmap(self.ptr, self.size) };
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/gc/strategy/copying.rs:
--------------------------------------------------------------------------------
1 | extern crate libc;
2 | extern crate time;
3 |
4 | use gc::strategy::Strategy;
5 | use gc::os::Memory;
6 | use gc::{GcRootWalker, GcOpts, GcMemHeader, GcWalker, GcWalk, GcFinalize, ptr_t};
7 | use std::ptr;
8 | use std::mem::{size_of, transmute, swap};
9 | use std::cmp::max;
10 |
11 | const PAGE_SIZE : usize = 4 * 1024;
12 |
13 | struct Header {
14 | forward: ptr_t,
15 | size: usize
16 | }
17 |
18 | impl Header {
19 | fn new(size: usize) -> Header {
20 | Header {
21 | forward: ptr::null(),
22 | size: size
23 | }
24 | }
25 |
26 | unsafe fn from_ptr<'a>(ptr: ptr_t) -> &'a mut Header {
27 | transmute(ptr.offset(-((size_of::() + size_of::()) as isize)))
28 | }
29 |
30 | unsafe fn offset_from_user(ptr: ptr_t) -> ptr_t {
31 | ptr.offset(-((size_of::() + size_of::()) as isize))
32 | }
33 |
34 | unsafe fn offset_to_user(ptr: ptr_t) -> ptr_t {
35 | ptr.offset((size_of::() + size_of::()) as isize)
36 | }
37 | }
38 |
39 | struct Block {
40 | memory: Memory,
41 | offset: usize,
42 | high_mark: usize
43 | }
44 |
45 | impl Block {
46 | unsafe fn alloc(&mut self, size: usize) -> ptr_t {
47 | let size = size + size_of::();
48 |
49 | if self.offset + size > self.high_mark {
50 | return ptr::null_mut();
51 | }
52 |
53 | let memory = self.memory.ptr().offset(self.offset as isize);
54 |
55 | (*(memory as *mut Header)) = Header::new(size);
56 |
57 | self.offset += size;
58 |
59 | memory.offset(size_of::() as isize)
60 | }
61 | }
62 |
63 | pub struct Copying {
64 | opts: GcOpts,
65 | from: Block,
66 | to: Memory,
67 | last_used: f64,
68 | last_failed: usize
69 | }
70 |
71 | impl Copying {
72 | pub fn new(opts: GcOpts) -> Copying {
73 | let memory = Memory::alloc(opts.initial_heap).unwrap();
74 | let high_mark = (opts.initial_heap * (opts.init_gc * 100.0) as usize) / 100;
75 |
76 | Copying {
77 | opts: opts,
78 | from: Block {
79 | memory: memory,
80 | offset: 0,
81 | high_mark: high_mark
82 | },
83 | to: Memory::empty(),
84 | last_used: 0.0,
85 | last_failed: 0
86 | }
87 | }
88 |
89 | unsafe fn copy(&mut self, mut walkers: Vec>, walker: &GcWalker) {
90 | // Calculate the new size of the heap. We use the fill factor of the previous
91 | // run as a basis and ensure that we have at least enough room to accept the
92 | // allocation that failed last (were we not able to reclaim any memory).
93 | //
94 | // The room we allocate comes down to the current allocated memory times the
95 | // fill factor times the growth factor. The growth factor is taken from
96 | // the configuration.
97 |
98 | // We need at least enough room to fit the last allocation. If we're not
99 | // able to reclaim any memory, the current offset plus the last allocation
100 | // need to fit.
101 | let mut target_size = max(self.from.memory.size(), self.from.offset + self.last_failed);
102 |
103 | tracegc!("last offset {} last failed {} target size {}", self.from.offset, self.last_failed, target_size);
104 |
105 | // If we had a fill of more than 50% last time, adjust with the growth factor.
106 | // If the growth factor is over 85%, we choose the fast growth factor. Otherwise
107 | // the slow one.
108 | if self.last_used > 0.5 {
109 | let growth_factor = if self.last_used > 0.85 {
110 | self.opts.fast_growth_factor
111 | } else {
112 | self.opts.slow_growth_factor
113 | };
114 |
115 | target_size = (target_size * (growth_factor * 100.0) as usize) / 100;
116 | tracegc!("last used {} over 50% target size {} growth factor {}", self.last_used, target_size, growth_factor);
117 | }
118 |
119 | // The minimum is set to the last used size.
120 | target_size = max(target_size, (target_size * (self.last_used * 100.0) as usize) / 100);
121 | tracegc!("last used {} target size {}", self.last_used, target_size);
122 |
123 | // We don't shrink beyond the initial heap size.
124 | target_size = max(target_size, self.opts.initial_heap);
125 | tracegc!("initial heap {} target_size {}", self.opts.initial_heap, target_size);
126 |
127 | self.last_failed = 0;
128 |
129 | target_size = (target_size + (PAGE_SIZE - 1)) & !(PAGE_SIZE - 1);
130 |
131 | // Ensure that the target heap is large enough.
132 |
133 | if self.to.size() < target_size {
134 | // First set to empty to first release our allocated memory.
135 | self.to = Memory::empty();
136 | self.to = Memory::alloc(target_size).unwrap();
137 | }
138 |
139 | let mut forwarder = Forwarder {
140 | target: self.to.ptr()
141 | };
142 |
143 | // Walk all GC roots.
144 |
145 | for walker in &mut walkers {
146 | loop {
147 | let ptr = walker.next();
148 | if ptr.is_null() {
149 | break;
150 | }
151 |
152 | let from = *ptr;
153 | *ptr = forwarder.forward(*ptr);
154 | tracegc!("forwarded {:?} to {:?}", from, *ptr);
155 | }
156 | }
157 |
158 | // Walk the to space.
159 |
160 | let mut ptr = Header::offset_to_user(self.to.ptr());
161 |
162 | while ptr < forwarder.target {
163 | let header = Header::from_ptr(ptr);
164 | let gc_header = GcMemHeader::from_ptr(ptr);
165 | let ty = gc_header.get_type_id();
166 | let size = gc_header.get_size();
167 | let ptrs = size / size_of::();
168 |
169 | if gc_header.is_array() {
170 | let count = *transmute::<_, *const usize>(ptr);
171 |
172 | let mut child = ptr.offset(size_of::() as isize);
173 | let end = child.offset((count * size) as isize);
174 |
175 | let mut index = 0;
176 |
177 | while child < end {
178 | tracegc!("processing index {}", index);
179 | index += 1;
180 |
181 | // Stop processing arrays when we receive a GcWalk::EndArray. This is
182 | // to signlify that none of the elements of the array will ever
183 | // contain a pointer.
184 | if !process_block(child, ty, ptrs, &mut forwarder, walker) {
185 | break;
186 | }
187 |
188 | child = child.offset(size as isize);
189 | }
190 |
191 | } else {
192 | process_block(ptr, ty, ptrs, &mut forwarder, walker);
193 | }
194 |
195 | ptr = ptr.offset(header.size as isize);
196 | }
197 |
198 | // Walk the from space and find all not-forwarded blocks to
199 | // allow them to be finalized.
200 |
201 | let mut ptr = Header::offset_to_user(self.from.memory.ptr());
202 | let end = ptr.offset(self.from.offset as isize);
203 |
204 | while ptr < end {
205 | let header = Header::from_ptr(ptr);
206 |
207 | if header.forward.is_null() {
208 | let gc_header = GcMemHeader::from_ptr(ptr);
209 | let ty = gc_header.get_type_id();
210 | let size = gc_header.get_size();
211 |
212 | if gc_header.is_array() {
213 | let count = *transmute::<_, *const usize>(ptr);
214 |
215 | let mut child = ptr.offset(size_of::() as isize);
216 | let end = child.offset((count * size) as isize);
217 |
218 | let mut index = 0;
219 |
220 | while child < end {
221 | tracegc!("finalizing index {}", index);
222 | index += 1;
223 |
224 | // Stop processing arrays when we receive a GcFinalize::NotFinalizable.
225 | // This signifies that none of the array elements will ever
226 | // be finalizable.
227 | match walker.finalize(ty, child) {
228 | GcFinalize::NotFinalizable => break,
229 | GcFinalize::Finalized => {}
230 | }
231 |
232 | child = child.offset(size as isize);
233 | }
234 |
235 | } else {
236 | walker.finalize(ty, ptr);
237 | }
238 | }
239 |
240 | ptr = ptr.offset(header.size as isize);
241 | }
242 |
243 | // Invalidate the from space in debug mode.
244 |
245 | if cfg!(not(ndebug)) {
246 | let mut ptr = transmute::<_, *mut u32>(self.from.memory.ptr());
247 | let end = ptr.offset((self.from.memory.size() / size_of::()) as isize);
248 |
249 | while ptr < end {
250 | *ptr = 0xdeadbeef;
251 | ptr = ptr.offset(1);
252 | }
253 | }
254 |
255 | // Swap the from and to space.
256 |
257 | self.from.offset = forwarder.target as usize - self.to.ptr() as usize;
258 | swap(&mut self.from.memory, &mut self.to);
259 | self.from.high_mark = (self.from.memory.size() * (self.opts.init_gc * 100.0) as usize) / 100;
260 |
261 | // Calculate the current fill rate.
262 |
263 | self.last_used = self.from.offset as f64 / self.from.memory.size() as f64;
264 | tracegc!("from offset {} from size {} last used {}", self.from.offset, self.from.memory.size(), self.last_used);
265 | }
266 | }
267 |
268 | struct Forwarder {
269 | target: ptr_t
270 | }
271 |
272 | impl Forwarder {
273 | unsafe fn forward(&mut self, ptr: ptr_t) -> ptr_t {
274 | let header = Header::from_ptr(ptr);
275 |
276 | if header.forward.is_null() {
277 | header.forward = self.target;
278 |
279 | *(self.target as *mut Header) = Header::new(header.size);
280 |
281 | let from = Header::offset_from_user(ptr).offset(size_of::() as isize);
282 | let to = transmute(self.target.offset(size_of::() as isize));
283 | let size = header.size - size_of::();
284 |
285 | ptr::copy(from, to, size);
286 |
287 | self.target = self.target.offset(header.size as isize);
288 | }
289 |
290 | Header::offset_to_user(header.forward)
291 | }
292 | }
293 |
294 | unsafe fn process_block(ptr: ptr_t, ty: u32, ptrs: usize, forwarder: &mut Forwarder, walker: &GcWalker) -> bool {
295 | for i in 0..ptrs {
296 | match walker.walk(ty, ptr, i as u32) {
297 | GcWalk::End => return true,
298 | GcWalk::EndArray => return false,
299 | GcWalk::Skip => {},
300 | GcWalk::Pointer => {
301 | let offset = (ptr as *mut ptr_t).offset(i as isize);
302 | let child = *offset;
303 | tracegc!("forwarding {:?} of type {:?} at {:?} index {}", child, ty, ptr, i);
304 | if !child.is_null() {
305 | *offset = forwarder.forward(child);
306 | }
307 | }
308 | }
309 | }
310 |
311 | true
312 | }
313 |
314 | impl Strategy for Copying {
315 | unsafe fn alloc_raw(&mut self, size: usize) -> ptr_t {
316 | // Round the size to the next pointer.
317 | let size = (size + (size_of::() - 1)) & !(size_of::() - 1);
318 |
319 | let result = self.from.alloc(size);
320 |
321 | if result.is_null() {
322 | self.last_failed = size;
323 | } else {
324 | ptr::write_bytes(transmute::<_, *mut u8>(result), 0, size);
325 | }
326 |
327 | result
328 | }
329 |
330 | fn mem_allocated(&self) -> usize {
331 | self.from.memory.size() + self.to.size()
332 | }
333 |
334 | fn mem_used(&self) -> usize {
335 | self.from.offset
336 | }
337 |
338 | fn gc(&mut self, walkers: Vec>, walker: &GcWalker) {
339 | let start = time::precise_time_ns();
340 |
341 | tracegc!("=== GC === start");
342 |
343 | unsafe {
344 | self.copy(walkers, walker);
345 | }
346 |
347 | let elapsed = (time::precise_time_ns() - start) / 1_000_000;
348 |
349 | tracegc!("=== GC === allocated {} ({}) used {} ms {}", nice_size(self.from.memory.size()), nice_size(self.mem_allocated()), nice_size(self.mem_used()), elapsed);
350 | }
351 | }
352 |
353 | fn nice_size(size: usize) -> String {
354 | if size < 1024 {
355 | format!("{} B", size)
356 | } else {
357 | let mut size = size as f64 / 1024.0;
358 | if size < 1024.0 {
359 | format!("{:.1} KB", size)
360 | } else {
361 | size /= 1024.0;
362 | if size < 1024.0 {
363 | format!("{:.1} MB", size)
364 | } else {
365 | size /= 1024.0;
366 | format!("{:.1} GB", size)
367 | }
368 | }
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/src/gc/strategy/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod copying;
2 |
3 | extern crate libc;
4 |
5 | use gc::{GcRootWalker, GcWalker, ptr_t};
6 |
7 | pub trait Strategy {
8 | unsafe fn alloc_raw(&mut self, size: usize) -> ptr_t;
9 |
10 | fn mem_allocated(&self) -> usize;
11 |
12 | fn mem_used(&self) -> usize;
13 |
14 | fn gc(&mut self, walkers: Vec>, walker: &GcWalker);
15 | }
16 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![feature(test)]
2 |
3 | #[macro_use]
4 | extern crate lazy_static;
5 |
6 | pub use rt::{JsResult, JsError};
7 |
8 | #[macro_use]
9 | mod debug;
10 | #[macro_use]
11 | mod trace;
12 | mod syntax;
13 | mod ir;
14 | mod util;
15 | pub mod gc;
16 | pub mod rt;
17 | mod errors;
18 | pub mod contrib;
19 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | extern crate rjs;
2 |
3 | fn main() {
4 | rjs::contrib::test262::Test262Runner::run();
5 | }
6 |
--------------------------------------------------------------------------------
/src/rt/allocators.rs:
--------------------------------------------------------------------------------
1 | use gc::{GcAllocator, Local, Root, Ptr, ArrayLocal, ArrayRoot, Array};
2 | use rt::JsEnv;
3 |
4 | impl GcAllocator for JsEnv {
5 | fn alloc_array_local_from_root(&self, root: &ArrayRoot) -> ArrayLocal {
6 | self.heap.alloc_array_local_from_root(root)
7 | }
8 |
9 | fn alloc_array_root_from_local(&self, local: ArrayLocal) -> ArrayRoot {
10 | self.heap.alloc_array_root_from_local(local)
11 | }
12 |
13 | fn alloc_array_local_from_array(&self, array: Array) -> ArrayLocal {
14 | self.heap.alloc_array_local_from_array(array)
15 | }
16 |
17 | fn alloc_root_from_local(&self, local: Local) -> Root {
18 | self.heap.alloc_root_from_local(local)
19 | }
20 |
21 | fn alloc_local_from_ptr(&self, ptr: Ptr) -> Local {
22 | self.heap.alloc_local_from_ptr(ptr)
23 | }
24 |
25 | fn alloc_local_from_root(&self, root: &Root) -> Local {
26 | self.heap.alloc_local_from_root(root)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/rt/boolean.rs:
--------------------------------------------------------------------------------
1 | use rt::{JsItem, JsEnv, JsValue, JsHandle};
2 |
3 | pub struct JsBoolean {
4 | value: bool
5 | }
6 |
7 | impl JsBoolean {
8 | pub fn new(value: bool) -> JsBoolean {
9 | JsBoolean {
10 | value: value
11 | }
12 | }
13 | }
14 |
15 | impl JsItem for JsBoolean {
16 | fn as_value(&self) -> JsValue {
17 | JsValue::new_bool(self.value)
18 | }
19 |
20 | fn has_prototype(&self) -> bool {
21 | true
22 | }
23 |
24 | fn prototype(&self, env: &JsEnv) -> Option {
25 | Some(env.handle(JsHandle::Boolean).as_value())
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/rt/env/boolean.rs:
--------------------------------------------------------------------------------
1 | use ::{JsResult, JsError};
2 | use rt::{JsEnv, JsArgs, JsValue, JsFnMode, JsItem, JsType, JsString};
3 | use syntax::token::name;
4 |
5 | // 15.6.1 The Boolean Constructor Called as a Function
6 | // 15.6.2 The Boolean Constructor
7 | pub fn Boolean_constructor(env: &mut JsEnv, mode: JsFnMode, args: JsArgs) -> JsResult {
8 | let arg = if args.argc > 0 {
9 | args.arg(env, 0).to_boolean()
10 | } else {
11 | false
12 | };
13 |
14 | let arg = JsValue::new_bool(arg);
15 |
16 | if mode.construct() {
17 | let this_arg = args.this(env);
18 | let mut this = this_arg.unwrap_object();
19 |
20 | this.set_class(Some(name::BOOLEAN_CLASS));
21 | this.set_value(arg);
22 |
23 | Ok(this_arg)
24 | } else {
25 | Ok(arg)
26 | }
27 | }
28 |
29 | fn get_bool_value(env: &mut JsEnv, this: JsValue) -> JsResult {
30 | match this.ty() {
31 | JsType::Boolean => Ok(this.unwrap_bool()),
32 | JsType::Object if this.class() == Some(name::BOOLEAN_CLASS) => {
33 | let this = this.unwrap_object();
34 |
35 | Ok(this.value(env).unwrap_bool())
36 | }
37 | _ => Err(JsError::new_type(env, ::errors::TYPE_INVALID))
38 | }
39 | }
40 |
41 | // 15.6.4.3 Boolean.prototype.valueOf ( )
42 | pub fn Boolean_valueOf(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
43 | let this_arg = args.this(env);
44 | let value = try!(get_bool_value(env, this_arg));
45 |
46 | Ok(JsValue::new_bool(value))
47 | }
48 |
49 | // 15.6.4.2 Boolean.prototype.toString ( )
50 | pub fn Boolean_toString(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
51 | let this_arg = args.this(env);
52 | let value = try!(get_bool_value(env, this_arg));
53 |
54 | let result = if value { "true" } else { "false" };
55 |
56 | Ok(JsString::from_str(env, result).as_value())
57 | }
58 |
--------------------------------------------------------------------------------
/src/rt/env/console.rs:
--------------------------------------------------------------------------------
1 | use ::JsResult;
2 | use rt::{JsEnv, JsArgs, JsValue, JsFnMode};
3 |
4 | // TODO #65
5 | pub fn console_log(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
6 | let string = try!(args.arg(env, 0).to_string(env)).to_string();
7 |
8 | println!("{}", string);
9 |
10 | Ok(JsValue::new_undefined())
11 | }
12 |
--------------------------------------------------------------------------------
/src/rt/env/error.rs:
--------------------------------------------------------------------------------
1 | use ::{JsResult, JsError};
2 | use rt::{JsEnv, JsArgs, JsValue, JsFnMode, JsItem, JsType, JsString};
3 | use syntax::token::name;
4 |
5 | pub fn Error_constructor(env: &mut JsEnv, mode: JsFnMode, args: JsArgs) -> JsResult {
6 | if !mode.construct() {
7 | let target_args = args.args(env);
8 | return args.function(env).construct(env, target_args);
9 | }
10 |
11 | let this = try!(args.this(env).to_object(env));
12 | let mut this_obj = this.unwrap_object();
13 | this_obj.set_class(Some(name::ERROR_CLASS));
14 |
15 | let message = args.arg(env, 0);
16 | if !message.is_undefined() {
17 | let message = try!(message.to_string(env)).as_value();
18 | try!(this_obj.put(env, name::MESSAGE, message, true));
19 | }
20 |
21 | Ok(this)
22 | }
23 |
24 | pub fn Error_toString(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
25 | let this = args.this(env);
26 | if this.ty() != JsType::Object {
27 | Err(JsError::new_type(env, ::errors::TYPE_INVALID))
28 | } else {
29 | let name = try!(this.get(env, name::NAME));
30 | let name = if name.is_undefined() {
31 | JsString::from_str(env, "Error")
32 | } else {
33 | try!(name.to_string(env))
34 | };
35 |
36 | let message = try!(this.get(env, name::MESSAGE));
37 | let message = if message.is_undefined() {
38 | JsString::from_str(env, "")
39 | } else {
40 | try!(message.to_string(env))
41 | };
42 |
43 | let result = if name.chars().len() == 0 {
44 | message
45 | } else if message.chars().len() == 0 {
46 | name
47 | } else {
48 | let result = format!("{}: {}", name.to_string(), message.to_string());
49 | JsString::from_str(env, &result)
50 | };
51 |
52 | Ok(result.as_value())
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/rt/env/function.rs:
--------------------------------------------------------------------------------
1 | use ::{JsResult, JsError};
2 | use rt::{JsEnv, JsString, JsFnMode, JsArgs, JsValue, JsItem, JsFunction, JsType};
3 | use rt::{JsScope, JsDescriptor, JsObject, JsHandle};
4 | use syntax::ast::FunctionRef;
5 | use syntax::parser::ParseMode;
6 | use gc::*;
7 | use std::fmt::Write;
8 | use std::cmp::max;
9 | use syntax::Name;
10 | use syntax::token::name;
11 |
12 | pub fn Function_baseConstructor(_env: &mut JsEnv, _mode: JsFnMode, _args: JsArgs) -> JsResult {
13 | Ok(JsValue::new_undefined())
14 | }
15 |
16 | pub fn Function_constructor(env: &mut JsEnv, mode: JsFnMode, args: JsArgs) -> JsResult {
17 | if !mode.construct() {
18 | let target_args = args.args(env);
19 | return args.function(env).construct(env, target_args);
20 | }
21 |
22 | let body;
23 |
24 | let mut source = String::new();
25 |
26 | source.push_str("function (");
27 |
28 | if args.argc == 0 {
29 | body = JsString::from_str(env, "");
30 | } else {
31 | for i in 0..args.argc - 1 {
32 | if i > 0 {
33 | source.push_str(", ");
34 | }
35 | let arg = try!(args.arg(env, i).to_string(env));
36 | source.push_str(&arg.to_string());
37 | }
38 |
39 | body = try!(args.arg(env, args.argc - 1).to_string(env));
40 | }
41 |
42 | source.push_str(") { ");
43 | source.push_str(&body.to_string());
44 | source.push_str(" }");
45 |
46 | let function_ref = try!(env.ir.parse_string(&source, mode.strict(), ParseMode::Normal, false));
47 | // The function returned is the program, but we need the function. The program
48 | // function is created last so we need the last but one.
49 |
50 | let function_ref = FunctionRef(function_ref.0 - 1);
51 |
52 | env.new_function(function_ref, None, mode.strict())
53 | }
54 |
55 | // 15.3.4.4 Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
56 | pub fn Function_call(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
57 | let func = args.this(env);
58 | if !func.is_callable() {
59 | Err(JsError::new_type(env, ::errors::TYPE_NOT_A_FUNCTION))
60 | } else {
61 | let this_arg = args.arg(env, 0);
62 |
63 | let mut call_args = Vec::new();
64 | for i in 1..args.argc {
65 | call_args.push(args.arg(env, i));
66 | }
67 |
68 | func.call(env, this_arg, call_args, false)
69 | }
70 | }
71 |
72 | // 15.3.4.3 Function.prototype.apply (thisArg, argArray)
73 | pub fn Function_apply(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
74 | let func = args.this(env);
75 | if !func.is_callable() {
76 | Err(JsError::new_type(env, ::errors::TYPE_NOT_A_FUNCTION))
77 | } else {
78 | let this_arg = args.arg(env, 0);
79 |
80 | let call_args = args.arg(env, 1);
81 | let call_args = if call_args.is_null_or_undefined() {
82 | Vec::new()
83 | } else {
84 | match call_args.class() {
85 | Some(name::ARRAY_CLASS) | Some(name::ARGUMENTS_CLASS) => {},
86 | _ => {
87 | // "Array" is a valid parameter to the apply method.
88 | let prototype = try!(call_args.get(env, name::PROTOTYPE));
89 | let array_prototype = env.handle(JsHandle::Array);
90 | if prototype.ty() != JsType::Object || prototype.unwrap_object().as_ptr() != array_prototype.as_ptr() {
91 | return Err(JsError::new_type(env, ::errors::TYPE_INVALID_ARGUMENTS_ARRAY))
92 | }
93 | }
94 | }
95 |
96 | let len = try!(call_args.get(env, name::LENGTH));
97 | let len = try!(len.to_uint32(env));
98 | let mut result = Vec::new();
99 |
100 | for i in 0..len {
101 | result.push(try!(call_args.get(env, Name::from_index(i as usize))));
102 | }
103 |
104 | result
105 | };
106 |
107 | func.call(env, this_arg, call_args, false)
108 | }
109 | }
110 |
111 | // 15.3.4.2 Function.prototype.toString ( )
112 | // TODO #66: This can be greatly improved, e.g. by retaining/getting the real code.
113 | pub fn Function_toString(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
114 | let this_arg = args.this(env);
115 |
116 | if this_arg.ty() == JsType::Object {
117 | if let Some(function) = this_arg.unwrap_object().function() {
118 | fn get_function_details(env: &mut JsEnv, this: JsValue, function: &JsFunction) -> JsResult<(Option, Option)> {
119 | match *function {
120 | JsFunction::Ir(function_ref) => {
121 | let description = env.ir.get_function(function_ref);
122 | Ok((description.name, None))
123 | }
124 | JsFunction::Native(name, args, _, _) => {
125 | Ok((name, Some(args)))
126 | }
127 | JsFunction::Bound => {
128 | let scope = this.scope(env).unwrap();
129 | let target = scope.get(env, 0);
130 | if let Some(function) = target.unwrap_object().function() {
131 | get_function_details(env, target, &function)
132 | } else {
133 | Err(JsError::new_type(env, ::errors::TYPE_INVALID))
134 | }
135 | }
136 | }
137 | }
138 |
139 | let (name, args) = try!(get_function_details(env, this_arg, &function));
140 |
141 | let mut code = String::new();
142 |
143 | code.push_str("function ");
144 |
145 | if let Some(name) = name {
146 | code.push_str(&*env.ir.interner().get(name));
147 | code.push_str(" ");
148 | }
149 |
150 | code.push_str("(");
151 |
152 | if let Some(args) = args {
153 | for i in 0..args {
154 | if i > 0 {
155 | code.push_str(", ");
156 | }
157 |
158 | write!(code, "arg{}", i).ok();
159 | }
160 | } else {
161 | code.push_str(" /* ... */ ");
162 | }
163 |
164 | code.push_str(") {\n");
165 | code.push_str(" /* ... */\n");
166 | code.push_str("}\n");
167 |
168 | return Ok(JsString::from_str(env, &code).as_value());
169 | }
170 | }
171 |
172 | Err(JsError::new_type(env, ::errors::TYPE_INVALID))
173 | }
174 |
175 | // 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, …]])
176 | pub fn Function_bind(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
177 | let this_arg = args.this(env);
178 |
179 | if !this_arg.is_callable() {
180 | Err(JsError::new_type(env, ::errors::TYPE_NOT_CALLABLE))
181 | } else {
182 | let mut scope = JsScope::new_local_thin(env, max(args.argc + 1, 2), None);
183 |
184 | scope.set(0, this_arg);
185 | scope.set(1, args.arg(env, 0));
186 |
187 | if args.argc > 1 {
188 | for i in 0..args.argc - 1 {
189 | scope.set(i + 2, args.arg(env, i + 1));
190 | }
191 | }
192 |
193 | let mut result = JsObject::new_function(env, JsFunction::Bound, true);
194 |
195 | let length = if this_arg.class() == Some(name::FUNCTION_CLASS) {
196 | let length = try!(this_arg.get(env, name::LENGTH)).unwrap_number() as usize;
197 | let argc = if args.argc > 1 { args.argc - 1 } else { 0 };
198 | if argc > length { 0 } else { length - argc }
199 | } else {
200 | 0
201 | };
202 |
203 | let length = JsValue::new_number(length as f64);
204 | result.define_own_property(env, name::LENGTH, JsDescriptor::new_value(length, false, false, true), false).ok();
205 |
206 | result.set_scope(Some(scope));
207 |
208 | Ok(result.as_value())
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/src/rt/env/global.rs:
--------------------------------------------------------------------------------
1 | use ::{JsResult, JsError};
2 | use rt::{JsEnv, JsArgs, JsValue, JsType, JsFnMode, JsString, JsHandle, JsItem};
3 | use syntax::parser::ParseMode;
4 | use ::util::matchers::DecimalMatcher;
5 | use std::{char, f64};
6 | use ::syntax::lexer::{is_line_terminator, is_whitespace};
7 |
8 | // 15.1.2.2 parseInt (string , radix)
9 | pub fn Global_parseInt(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
10 | // TODO #67: Use DecimalMatcher.
11 |
12 | let string = try!(args.arg(env, 0).to_string(env));
13 | let radix = try!(args.arg(env, 1).to_int32(env)) as i64;
14 |
15 | let mut offset = 0;
16 | let chars = string.to_string().chars().collect::>();
17 |
18 | while offset < chars.len() {
19 | let c = chars[offset];
20 | if !is_whitespace(c) && !is_line_terminator(c) {
21 | break;
22 | }
23 |
24 | offset += 1;
25 | }
26 |
27 | let sign = if offset < chars.len() {
28 | match chars[offset] {
29 | '-' => {
30 | offset += 1;
31 | -1
32 | }
33 | '+' => {
34 | offset += 1;
35 | 1
36 | }
37 | _ => {
38 | 1
39 | }
40 | }
41 | } else {
42 | 1
43 | };
44 |
45 | let (mut radix, strip_prefix) = if radix != 0 {
46 | if radix < 2 || radix > 36 {
47 | return Ok(JsValue::new_number(f64::NAN));
48 | }
49 | (radix, radix == 16)
50 | } else {
51 | (10, true)
52 | };
53 |
54 | if strip_prefix && offset + 1 < chars.len() {
55 | if chars[offset] == '0' && (chars[offset + 1] == 'x' || chars[offset + 1] == 'X') {
56 | offset += 2;
57 | radix = 16;
58 | }
59 | }
60 |
61 | let start = offset;
62 | let mut result = 0.0;
63 |
64 | while offset < chars.len() {
65 | let c = chars[offset];
66 |
67 | let digit = if c >= '0' && c <= '9' {
68 | c as i64 - '0' as i64
69 | } else if c >= 'a' && c <= 'z' {
70 | c as i64 - 'a' as i64 + 10
71 | } else if c >= 'A' && c <= 'Z' {
72 | c as i64 - 'A' as i64 + 10
73 | } else {
74 | break;
75 | };
76 |
77 | if digit >= radix {
78 | break;
79 | }
80 |
81 | offset += 1;
82 | result *= radix as f64;
83 | result += digit as f64;
84 | }
85 |
86 | if offset == start {
87 | Ok(JsValue::new_number(f64::NAN))
88 | } else {
89 | Ok(JsValue::new_number(result * sign as f64))
90 | }
91 | }
92 |
93 | pub fn Global_parseFloat(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
94 | let input = try!(args.arg(env, 0).to_string(env)).to_string();
95 |
96 | let result = DecimalMatcher::from_str(&input, false, false).as_f64().unwrap_or(f64::NAN);
97 |
98 | Ok(JsValue::new_number(result))
99 | }
100 |
101 | // 15.1.2.4 isNaN (number)
102 | pub fn Global_isNaN(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
103 | let result = try!(args.arg(env, 0).to_number(env)).is_nan();
104 |
105 | Ok(JsValue::new_bool(result))
106 | }
107 |
108 | // 15.1.2.5 isFinite (number)
109 | pub fn Global_isFinite(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs) -> JsResult {
110 | let result = try!(args.arg(env, 0).to_number(env)).is_finite();
111 |
112 | Ok(JsValue::new_bool(result))
113 | }
114 |
115 | // 15.1.2.1 eval (x)
116 | pub fn Global_eval(env: &mut JsEnv, mode: JsFnMode, args: JsArgs) -> JsResult {
117 | let arg = args.arg(env, 0);
118 |
119 | if arg.ty() != JsType::String {
120 | Ok(arg)
121 | } else {
122 | let arg = arg.unwrap_string().to_string();
123 | let global = env.handle(JsHandle::Global).as_value();
124 | let global_scope = env.global_scope.as_local(env);
125 |
126 | env.eval_scoped(&arg, mode.strict(), global, global_scope, ParseMode::Eval)
127 | .map(|result| result.as_value(env))
128 | }
129 | }
130 |
131 | fn is_uri_unescape(c: char) -> bool {
132 | match c {
133 | 'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' | '.' | '!' | '~' | '*' | '\'' | '(' | ')' => true,
134 | _ => false
135 | }
136 | }
137 |
138 | fn is_uri_reserved(c: char) -> bool {
139 | match c {
140 | ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ',' => true,
141 | _ => false
142 | }
143 | }
144 |
145 | fn decode bool>(env: &mut JsEnv, _mode: JsFnMode, args: JsArgs, is_reserved: F) -> JsResult {
146 | fn parse_hex(env: &mut JsEnv, c: char) -> JsResult {
147 | if c >= '0' && c <= '9' {
148 | Ok(((c as u32) - ('0' as u32)) as u8)
149 | } else if c >= 'a' && c <= 'f' {
150 | Ok(((c as u32) - ('a' as u32) + 10u32) as u8)
151 | } else if c >= 'A' && c <= 'F' {
152 | Ok(((c as u32) - ('A' as u32) + 10u32) as u8)
153 | } else {
154 | Err(JsError::new_uri(env))
155 | }
156 | }
157 |
158 | fn decode_part(env: &mut JsEnv, chars: &[u16], offset: usize) -> JsResult