├── src ├── utils.rs ├── builtins │ ├── mod.rs │ ├── console.rs │ ├── error.rs │ ├── boolean.rs │ ├── number.rs │ ├── string.rs │ ├── global.rs │ ├── function.rs │ ├── array.rs │ ├── object.rs │ └── promise.rs ├── ast_utils.rs ├── constants.rs ├── lib.rs ├── error.rs ├── scope.rs ├── bytecode.rs ├── ast_token.rs ├── ast_node.rs └── value.rs ├── .gitignore ├── .gitmodules ├── .gitpod.yml ├── Cargo.toml ├── tests ├── class_test.rs ├── boolean_test.rs ├── string_test.rs ├── array_test.rs ├── promise_test.rs ├── binary_test.rs ├── asi_test.rs ├── confitional_test.rs ├── function_test.rs ├── error_test.rs ├── object_test.rs ├── test262_test.rs └── ast_lexer_test.rs ├── docs └── global.md ├── readme.md └── Cargo.lock /src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn combine_string() { 2 | 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .test 3 | 262_result.json 4 | 262_result_diff.json 5 | Cargo.lock -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test262"] 2 | path = test262 3 | url = https://github.com/tc39/test262.git 4 | -------------------------------------------------------------------------------- /src/builtins/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod object; 2 | pub mod function; 3 | pub mod array; 4 | pub mod string; 5 | pub mod number; 6 | pub mod boolean; 7 | pub mod promise; 8 | pub mod error; 9 | pub mod global; 10 | pub mod console; -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | tasks: 6 | - init: cargo build 7 | command: cargo watch -x run 8 | 9 | 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "jsi" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [dev-dependencies] 11 | serde = { version = "1.0.126", features = ["derive"]} 12 | serde_json = "1.0.64" 13 | yaml-rust = "0.4" -------------------------------------------------------------------------------- /tests/class_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::JSI; 2 | 3 | 4 | #[test] 5 | fn ast_class() { 6 | let mut jsi_vm = JSI::new(); 7 | let program= jsi_vm.parse(String::from("class A {\n 8 | a 9 | b; 10 | xxx = 213;\n 11 | constructor(arg) {\n 12 | this.a = arg; 13 | } 14 | 15 | async func() { 16 | return new Promise(resolve => { 17 | resolve(this.a); 18 | }) 19 | } 20 | }")); 21 | println!("program {:?}", program); 22 | // assert_eq!(value,Value::Number(20f64)); 23 | } -------------------------------------------------------------------------------- /docs/global.md: -------------------------------------------------------------------------------- 1 | 2 | ## 全局对象 3 | 4 | ### 如何创建一个全局对象 5 | 6 | 1. constants 中创建一个全局对象的名称,例如 Promise,并添加到 GLOBAL_OBJECT_NAME_LIST 列表中 7 | a. GLOBAL_OBJECT_NAME_LIST 列表在 context 初始化的时候,会被遍历并创建全局对象添加到全局作用域中 8 | 2. 实现 create_promise 方法,也就是用来创建 Promise 对象的方法 9 | a. 绑定 constructor 属性 10 | c. 关联原型,通过 PROTO_PROPERTY_NAME 关联 Promise.prototype 11 | 3. 实现全局构造方法 bind_global_promise 12 | a. 主要是设置 INSTANTIATE_OBJECT_METHOD_NAME 方法,此方法在 context 执行 new 的时候会被调用,用来实例化内置的全局对象 13 | b. 绑定 Promise 的静态方法 14 | c. 绑定 Promise.prototype 的方法(原型方法) 15 | -------------------------------------------------------------------------------- /src/ast_utils.rs: -------------------------------------------------------------------------------- 1 | pub fn get_hex_number_value(chr: char) -> i32 { 2 | match chr { 3 | '0' => 0, 4 | '1' => 1, 5 | '2' => 2, 6 | '3' => 3, 7 | '4' => 4, 8 | '5' => 5, 9 | '6' => 6, 10 | '7' => 7, 11 | '8' => 8, 12 | '9' => 9, 13 | 'a' | 'A' => 10, 14 | 'b' | 'B' => 11, 15 | 'c' | 'C' => 12, 16 | 'd' | 'D' => 13, 17 | 'e' | 'E' => 14, 18 | 'f' | 'F' => 15, 19 | _ => 16, 20 | } 21 | } 22 | 23 | pub fn chars_to_string(chars: &Vec, start: usize, end: usize) -> String { 24 | return chars[start..end].iter().collect() 25 | } 26 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | // base 2 | pub const GLOBAL_NUMBER_NAME: &str = "Number"; 3 | pub const GLOBAL_STRING_NAME: &str = "String"; 4 | pub const GLOBAL_BOOLEAN_NAME: &str = "Boolean"; 5 | pub const GLOBAL_OBJECT_NAME: &str = "Object"; 6 | pub const GLOBAL_ARRAY_NAME: &str = "Array"; 7 | pub const GLOBAL_FUNCTION_NAME: &str = "Function"; 8 | 9 | // more 10 | pub const GLOBAL_PROMISE_NAME: &str = "Promise"; 11 | 12 | pub const GLOBAL_ERROR_NAME: &str = "Error"; 13 | pub const GLOBAL_TYPE_ERROR_NAME: &str = "TypeError"; 14 | 15 | pub const GLOBAL_OBJECT_NAME_LIST: [&str;9] = [ 16 | GLOBAL_NUMBER_NAME, 17 | GLOBAL_STRING_NAME, 18 | GLOBAL_BOOLEAN_NAME, 19 | // Object 20 | GLOBAL_OBJECT_NAME, 21 | GLOBAL_ARRAY_NAME, 22 | GLOBAL_FUNCTION_NAME, 23 | GLOBAL_PROMISE_NAME, 24 | // Error 25 | GLOBAL_ERROR_NAME, 26 | GLOBAL_TYPE_ERROR_NAME, 27 | ]; 28 | 29 | pub const PROTO_PROPERTY_NAME: &str = "[[Property]]"; -------------------------------------------------------------------------------- /src/builtins/console.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, cell::RefCell}; 2 | 3 | use crate::{context::{Context}, ast_node::{ClassType, CallContext}, error::JSIResult}; 4 | use super::{object::{create_object, Property, Object},function::builtin_function}; 5 | use crate::{value::{Value}}; 6 | pub fn create_console(ctx: &mut Context) -> Rc> { 7 | let console_obj = create_object(ctx, ClassType::Object, None); 8 | let console_rc = Rc::clone(&console_obj); 9 | let mut console = console_rc.borrow_mut(); 10 | // console.log 11 | let name = String::from("log"); 12 | console.property.insert(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 0f64, console_log) }); 13 | console_obj 14 | } 15 | 16 | 17 | // console.log 18 | fn console_log(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 19 | let mut strs: Vec = vec![]; 20 | for arg in args.iter() { 21 | strs.push(arg.to_string(call_ctx.ctx)); 22 | } 23 | println!("{}", strs.join(" ")); 24 | Ok(Value::Undefined) 25 | } -------------------------------------------------------------------------------- /tests/boolean_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, value::Value}; 2 | 3 | #[test] 4 | fn run_boolean_to_string() { 5 | let mut jsi = JSI::new(); 6 | let result = jsi.run_with_bytecode(String::from("\ 7 | let bool1 = false, bool2 = true; 8 | bool1.toString() + bool2.toString()")).unwrap(); 9 | assert_eq!(result , Value::String(String::from("falsetrue"))); 10 | } 11 | 12 | #[test] 13 | fn run_boolean_object() { 14 | let mut jsi = JSI::new(); 15 | let result = jsi.run(String::from("\ 16 | let bool1 = Boolean(), bool2 = new Boolean(1), bool3 = new Boolean(new Boolean(new String('123'))) , bool4 = new Boolean(new Boolean(new String(''))); 17 | bool1.toString() + bool2.toString() + bool3.toString() + bool4.toString()")).unwrap(); 18 | assert_eq!(result , Value::String(String::from("falsetruetruefalse"))); 19 | } 20 | 21 | #[test] 22 | fn run_boolean_typeof() { 23 | let mut jsi = JSI::new(); 24 | let result = jsi.run_with_bytecode(String::from("\ 25 | typeof false")).unwrap(); 26 | assert_eq!(result , Value::String(String::from("boolean"))); 27 | } -------------------------------------------------------------------------------- /tests/string_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, value::Value}; 2 | 3 | #[test] 4 | fn run_string() { 5 | let mut jsi = JSI::new(); 6 | let result = jsi.run_with_bytecode(String::from("\ 7 | let a = '123'; 8 | let b = 'abc'; 9 | a + b 10 | ")).unwrap(); 11 | assert_eq!(result , Value::String(String::from("123abc"))); 12 | } 13 | 14 | #[test] 15 | fn run_string_object() { 16 | let mut jsi = JSI::new(); 17 | let result = jsi.run(String::from("\ 18 | let a = String(123), b = new String('abc'); 19 | a + b")).unwrap(); 20 | assert_eq!(result , Value::String(String::from("123abc"))); 21 | } 22 | 23 | 24 | #[test] 25 | fn run_string_typeof() { 26 | let mut jsi = JSI::new(); 27 | let result = jsi.run_with_bytecode(String::from("\ 28 | typeof 'abc'")).unwrap(); 29 | assert_eq!(result , Value::String(String::from("string"))); 30 | } 31 | 32 | #[test] 33 | fn run_string_xxx() { 34 | let mut jsi = JSI::new(); 35 | let result = jsi.run_with_bytecode(String::from("\ 36 | !('')")).unwrap(); 37 | assert_eq!(result , Value::Boolean(true)); 38 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod context; 2 | pub mod ast; 3 | pub mod ast_token; 4 | pub mod ast_node; 5 | pub mod ast_utils; 6 | pub mod value; 7 | pub mod scope; 8 | pub mod error; 9 | pub mod builtins; 10 | pub mod constants; 11 | pub mod bytecode; 12 | 13 | use ast::Program; 14 | use context::Context; 15 | use error::JSIResult; 16 | use value::Value; 17 | pub struct JSI { 18 | context: Context, 19 | } 20 | 21 | impl JSI { 22 | pub fn new() -> JSI { 23 | let context = Context::new(); 24 | JSI { 25 | context, 26 | } 27 | } 28 | 29 | pub fn set_strict(&mut self,strict: bool) { 30 | self.context.set_strict(strict); 31 | } 32 | 33 | pub fn run(&mut self, code: String) -> JSIResult { 34 | return self.context.run(code) 35 | } 36 | 37 | pub fn run_with_bytecode(&mut self, code: String) -> JSIResult { 38 | return self.context.run_with_bytecode(code) 39 | } 40 | 41 | pub fn parse(&mut self, code: String) -> JSIResult { 42 | return self.context.parse(code) 43 | } 44 | 45 | pub fn dump_byte_code(&mut self, code: String) -> JSIResult { 46 | return self.context.dump_byte_code(code) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/array_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, value::Value}; 2 | 3 | 4 | #[test] 5 | fn run_array_to_string() { 6 | let mut jsi = JSI::new(); 7 | let result = jsi.run_with_bytecode(String::from("\ 8 | let arr = [1,2,3]\n 9 | arr.push(4); 10 | arr.toString()")).unwrap(); 11 | assert_eq!(result , Value::String(String::from("1,2,3,4"))); 12 | } 13 | 14 | #[test] 15 | fn run_array_join() { 16 | let mut jsi = JSI::new(); 17 | let result = jsi.run_with_bytecode(String::from("\ 18 | let arr = [1,2,3]\n 19 | arr.join(':')")).unwrap(); 20 | assert_eq!(result , Value::String(String::from("1:2:3"))); 21 | } 22 | 23 | #[test] 24 | // https://github.com/tc39/test262/blob/main/test/built-ins/Array/15.4.5-1.js 25 | fn run_array_instances_has_class() { 26 | let mut jsi = JSI::new(); 27 | let result = jsi.run_with_bytecode(String::from("\ 28 | let arr = []\n 29 | Object.prototype.toString.call(arr)")).unwrap(); 30 | assert_eq!(result , Value::String(String::from("[object Array]"))); 31 | } 32 | 33 | #[test] 34 | fn run_array_typeof() { 35 | let mut jsi = JSI::new(); 36 | let result = jsi.run_with_bytecode(String::from("\ 37 | let num = [1,2]; 38 | num.concat([2,3], 4).join(',') + typeof num")).unwrap(); 39 | assert_eq!(result , Value::String(String::from("1,2,2,3,4object"))); 40 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## JSI 2 | 3 | JSI is a JavaScript interpreter written in Rust that supports bytecode similar to quickjs. 4 | 5 | 6 | test262 passed 7 | test262 total 8 | 9 | --- 10 | 11 | ### Usage 12 | ```rust 13 | use jsi::JSI; 14 | 15 | let mut jsi = JSI::new(); 16 | 17 | // Run with ast 18 | let result = jsi.run(String::from("\ 19 | let a = []; 20 | let i = 0; 21 | outer: 22 | while(i < 3) { 23 | i ++; 24 | let j = 0; 25 | while(j < 5) { 26 | j ++; 27 | if (j == 1 && i == 1) { 28 | continue outer 29 | } 30 | if (j == 4) break 31 | if (j == 3 && i == 2) { 32 | break outer 33 | } 34 | a.push(i * j); 35 | } 36 | } 37 | a.join(':')") 38 | ).unwrap(); 39 | assert_eq!(result , Value::String(String::from("2:4"))); 40 | 41 | // Run with bytecode 42 | let result2 = jsi.run_with_bytecode(String::from("\ 43 | let a = '123'; 44 | let b = 'abc'; 45 | a + b 46 | ")).unwrap(); 47 | assert_eq!(result , Value::String(String::from("123abc"))); 48 | ``` 49 | 50 | ### Development 51 | 52 | + git submodule `git submodule update --init --recursive` 53 | + test262 `RUST_MIN_STACK=8388608 cargo test --package jsi --test test262_test -- test_all_262 --exact --nocapture` 54 | 55 | ### Refs 56 | 57 | + Ecma Standard: https://tc39.es/ecma262/multipage/#sec-intro 58 | + Test262: https://github.com/tc39/test262 59 | 60 | ### License 61 | MIT 62 | 63 | --- 64 | by [echosoar](https://github.com/echosoar) -------------------------------------------------------------------------------- /tests/promise_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, value::Value}; 2 | 3 | #[test] 4 | fn run_promise_base() { 5 | let mut jsi = JSI::new(); 6 | let result = jsi.run(String::from("\ 7 | let resolveCache; 8 | let promise = new Promise(resolve => { 9 | resolveCache = resolve; 10 | }); 11 | 12 | let res = promise.then(value => value + 'xyz'); 13 | resolveCache('123abc'); 14 | res 15 | ")).unwrap(); 16 | if let Value::Promise(promise_rc) = &result { 17 | let promise_mut = promise_rc.borrow_mut(); 18 | let state = promise_mut.get_inner_property_value(String::from("[[PromiseState]]")).unwrap(); 19 | assert_eq!(state , Value::String(String::from("fulfilled"))); 20 | let value = promise_mut.get_inner_property_value(String::from("[[PromiseFulfilledValue]]")).unwrap(); 21 | assert_eq!(value , Value::String(String::from("123abcxyz"))); 22 | } else { 23 | panic!("Expected a Promise"); 24 | } 25 | } 26 | 27 | 28 | #[test] 29 | fn run_promise_then() { 30 | let mut jsi = JSI::new(); 31 | let result = jsi.run(String::from("\ 32 | let resolveCache; 33 | let promise = new Promise(resolve => { 34 | resolveCache = resolve; 35 | }); 36 | 37 | let res = promise.then(value => { 38 | return Promise.reject(value + ':reject1'); 39 | }).then(value => { 40 | return Promise.reject(value + ':resolve2'); 41 | }, rejValue => { 42 | return Promise.resolve(rejValue + ':reject2'); 43 | }).then(value => { 44 | // TODO: 返回一个 new Promise,但是先缓存 resolve 方法,后面再执行 45 | return Promise.reject(value + ':resolve3'); 46 | }, rejValue => { 47 | return Promise.resolve(rejValue + ':reject3'); 48 | }); 49 | resolveCache('123abc'); 50 | res 51 | ")).unwrap(); 52 | 53 | if let Value::Promise(promise_rc) = &result { 54 | let promise_mut = promise_rc.borrow_mut(); 55 | let state = promise_mut.get_inner_property_value(String::from("[[PromiseState]]")).unwrap(); 56 | assert_eq!(state , Value::String(String::from("rejected"))); 57 | let reason = promise_mut.get_inner_property_value(String::from("[[PromiseRejectedReason]]")).unwrap(); 58 | assert_eq!(reason , Value::String(String::from("123abc:reject1:reject2:resolve3"))); 59 | } else { 60 | panic!("Expected a Promise"); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/binary_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, value::Value}; 2 | 3 | 4 | #[test] 5 | fn run_binary_equal() { 6 | let check_list = vec![ 7 | // same type 8 | ("1 == 1", true), 9 | ("1 != 1", false), 10 | ("0 == 1", false), 11 | ("0 != 1", true), 12 | ("true == true", true), 13 | ("true != true", false), 14 | ("false == true", false), 15 | ("false != true", true), 16 | ("'123' == '123'", true), 17 | ("'123' != '123'", false), 18 | ("'124' == '123'", false), 19 | ("'124' != '123'", true), 20 | ("null == null", true), 21 | ("null != null", false), 22 | ("undefined == undefined", true), 23 | ("undefined != undefined", false), 24 | // diff type 25 | ("1 == true", true), 26 | ("1 != true", false), 27 | ("1 === true", false), // strict equal 28 | ("1 !== true", true), // strict not equal 29 | ("0 == false", true), 30 | ("0 != false", false), 31 | ("0 === false", false), // strict equal 32 | ("0 !== false", true), // strict not equal 33 | ("2 == true", false), 34 | ("2 != true", true), 35 | ("0 == 1", false), 36 | ("'0' == 0", true), 37 | ("'0' != 0", false), 38 | ("'0' === 0", false), // strict equal 39 | ("'0' !== 0", true), // strict equal 40 | ("'1' == 0", false), 41 | ("123 == '123'", true), 42 | ("123 === '123'", false), // strict equal 43 | ("123 == '124'", false), 44 | ("null == undefined", true), 45 | ("null != undefined", false), 46 | ("null === undefined", false), // strict equal 47 | ("null !== undefined", true), // strict equal 48 | ("'1' == true", true), 49 | ("'1' === true", false), // strict equal 50 | ("'true' == true", false), 51 | ("'0' == false", true), 52 | ("'0' === false", false), // strict equal 53 | ("'false' == false", false), 54 | ("!0", true), 55 | ("!1", false), 56 | ("!''", true), 57 | ("!'0'", false), 58 | ("!false", true), 59 | ("!true", false), 60 | ("++1 === 2", true), 61 | ("--2 === 1", true), 62 | // TODO: Object/Array/Function 63 | 64 | ]; 65 | let mut jsi = JSI::new(); 66 | for check_item in check_list { 67 | assert_eq!(jsi.run_with_bytecode(String::from(check_item.0)).unwrap(), Value::Boolean(check_item.1), "expr: {:?}", check_item.0); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{result, rc::Rc, cell::RefCell}; 2 | use crate::constants::{GLOBAL_ERROR_NAME, GLOBAL_TYPE_ERROR_NAME}; 3 | use crate::context::{Context}; 4 | use crate::{builtins::{object::Object, error::create_error}, value::Value}; 5 | 6 | pub type JSIResult = result::Result; 7 | 8 | #[derive(Debug,PartialEq,Clone)] 9 | pub enum JSIErrorType { 10 | // 语法错误,遇到了不符合语法规范的标记(token)或标记顺序 11 | SyntaxError, 12 | // 类型错误 13 | TypeError, 14 | // 引用错误,不存在的变量 15 | ReferenceError, 16 | // 范围错误,如设置 array 的length为非数字 17 | RangeError, 18 | Unknown, 19 | } 20 | 21 | impl JSIErrorType { 22 | pub fn to_string(&self) -> String { 23 | match self { 24 | JSIErrorType::SyntaxError => String::from("SyntaxError"), 25 | JSIErrorType::TypeError => String::from("TypeError"), 26 | JSIErrorType::ReferenceError => String::from("ReferenceError"), 27 | JSIErrorType::RangeError => String::from("RangeError"), 28 | JSIErrorType::Unknown => String::from("Unknown"), 29 | } 30 | } 31 | // 转换为全局错误对象名称 32 | pub fn to_global_error_type(&self) -> &str { 33 | match self { 34 | JSIErrorType::TypeError => GLOBAL_TYPE_ERROR_NAME, 35 | _ => GLOBAL_ERROR_NAME, 36 | } 37 | } 38 | } 39 | 40 | #[derive(Debug, Clone)] 41 | pub struct JSIError { 42 | pub error_type: JSIErrorType, 43 | pub message: String, 44 | pub line: i32, 45 | pub column: i32, 46 | pub value: Option 47 | } 48 | 49 | impl JSIError { 50 | pub fn new(error_type: JSIErrorType, message: String, line: i32, column: i32) -> JSIError { 51 | return JSIError { 52 | error_type, 53 | message, 54 | line, 55 | column, 56 | value: None 57 | } 58 | } 59 | 60 | pub fn to_error_object(&self, ctx: &mut Context) -> Rc> { 61 | if let Some(value) = &self.value { 62 | return value.to_object(ctx); 63 | } 64 | let new_error = create_error(ctx, Value::String(self.message.clone()), self.error_type.to_global_error_type()); 65 | // TODO: set error line/stack 66 | let obj = if let Value::Object(obj) = new_error { 67 | Some(obj) 68 | } else { 69 | None 70 | }.unwrap(); 71 | return obj; 72 | } 73 | 74 | pub fn set_value(&mut self, value: Value) { 75 | self.value = Some(value); 76 | } 77 | } -------------------------------------------------------------------------------- /src/scope.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, cell::RefCell, rc::Rc}; 2 | 3 | use crate::{bytecode::ByteCode, value::{Value, ValueInfo}}; 4 | // 上下文环境 5 | #[derive(Debug, Clone)] 6 | pub struct Scope { 7 | pub id: i32, 8 | pub parent: Option>>, 9 | pub from: Option>>, 10 | pub childs: Vec>>, 11 | pub labels: Vec, 12 | // 当前上下文的 this 13 | pub this: Option, 14 | variables: HashMap, 15 | pub function_call_args: Vec, 16 | } 17 | 18 | 19 | #[derive(Debug, Clone)] 20 | pub struct VariableInfo { 21 | pub value: Value, 22 | pub is_const: bool, 23 | pub bytecode: Vec, 24 | } 25 | 26 | impl Scope { 27 | pub fn new() -> Scope { 28 | Scope { 29 | id: 0, 30 | childs: vec![], 31 | parent: None, 32 | from: None, 33 | this: None, 34 | labels: vec![], 35 | variables: HashMap::new(), 36 | function_call_args: vec![], 37 | } 38 | } 39 | 40 | pub fn set_value(&mut self, name: String, value: Value, is_const: bool) { 41 | self.variables.insert(name, VariableInfo{value, is_const, bytecode: vec![]}); 42 | } 43 | 44 | pub fn set_bytecode(&mut self, name: String, value: Value, is_const: bool, bytecode: Vec) { 45 | self.variables.insert(name, VariableInfo{value, is_const, bytecode}); 46 | } 47 | } 48 | 49 | pub fn get_value_info_and_scope(scope: Rc>, identifier: String) -> (Option, Rc>, bool) { 50 | // println!("get_value_and_scope: {:?} {:?}", identifier, scope); 51 | let s = scope.borrow(); 52 | let value = s.variables.get(&identifier); 53 | if let Some(val) = value { 54 | let value_info = ValueInfo { 55 | name: Some(identifier.clone()), 56 | value: val.value.clone(), 57 | is_const: val.is_const, 58 | reference: None, 59 | access_path: identifier.clone(), 60 | }; 61 | return (Some(value_info), Rc::clone(&scope), val.is_const) 62 | } else { 63 | if let Some(parent) = &scope.borrow().parent { 64 | get_value_info_and_scope(Rc::clone(parent), identifier) 65 | } else { 66 | (None, Rc::clone(&scope), false) 67 | } 68 | } 69 | } 70 | 71 | pub fn get_value_and_scope(scope: Rc>, identifier: String) -> (Option, Rc>, bool) { 72 | let (value_info, scope, is_const) = get_value_info_and_scope(scope, identifier); 73 | if let Some(value_info) = value_info { 74 | return (Some(value_info.value), scope, is_const); 75 | } 76 | // 如果没有找到变量,返回 None 77 | (None, scope, false) 78 | } -------------------------------------------------------------------------------- /src/builtins/error.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc}; 2 | use crate::constants::{PROTO_PROPERTY_NAME, GLOBAL_TYPE_ERROR_NAME}; 3 | use crate::context::{Context}; 4 | use crate::{value::{Value, INSTANTIATE_OBJECT_METHOD_NAME}, ast_node::{ClassType, CallContext}, constants::GLOBAL_ERROR_NAME, error::JSIResult}; 5 | 6 | use super::global::{get_global_object_prototype_by_name, get_global_object_by_name}; 7 | use super::{object::{create_object, Property},function::builtin_function}; 8 | 9 | // ref:https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-error-objects 10 | // 实例化 Error 对象 11 | pub fn create_error(ctx: &mut Context, init: Value, error_type: &str) -> Value { 12 | let global_error = get_global_object_by_name(ctx, error_type); 13 | let error = create_object(ctx, ClassType::Error, None); 14 | let error_clone = Rc::clone(&error); 15 | let mut error_mut = (*error_clone).borrow_mut(); 16 | 17 | // 将实例化的 error 对象的 __proto__ 指向全局的 Error.prototype 18 | let global_prototype = get_global_object_prototype_by_name(ctx, error_type); 19 | error_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 20 | 21 | error_mut.constructor = Some(Rc::downgrade(&global_error)); 22 | 23 | let msg = init.to_string(ctx); 24 | 25 | error_mut.define_property(String::from("message"), Property { enumerable: true, value: Value::String(msg)}); 26 | Value::Object(error) 27 | } 28 | 29 | pub fn bind_global_error(ctx: &mut Context, error_type: &str) { 30 | // Error 31 | let create_function = builtin_function(ctx, error_type.to_string(), 1f64, create); 32 | 33 | let error_rc = get_global_object_by_name(ctx, error_type); 34 | let mut error = (*error_rc).borrow_mut(); 35 | error.set_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string(), create_function); 36 | if error_type != GLOBAL_ERROR_NAME { 37 | return 38 | } 39 | if let Some(prop)= &error.prototype { 40 | let prototype_rc = Rc::clone(prop); 41 | let mut prototype = prototype_rc.borrow_mut(); 42 | let name = String::from("toString"); 43 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 0f64, to_string) }); 44 | } 45 | } 46 | 47 | // 创建实例化对象 48 | fn create(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 49 | let mut param = Value::Undefined; 50 | if args.len() > 0 { 51 | param = args[0].clone(); 52 | } 53 | Ok(create_error(call_ctx.ctx, param, call_ctx.func_name.as_str())) 54 | } 55 | 56 | // Error.prototype.toString 57 | fn to_string(_: &mut CallContext, _: Vec) -> JSIResult { 58 | // let this = call_ctx.this; 59 | // TODO: 60 | Ok(Value::String(String::from("err"))) 61 | } -------------------------------------------------------------------------------- /tests/asi_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI,ast_node::{Statement, ExpressionStatement, Expression, IdentifierLiteral, PrefixUnaryExpression, AssignExpression, BinaryExpression, FunctionDeclaration, BlockStatement, ReturnStatement, Keywords}, ast_token::Token, value::Value}; 2 | 3 | #[test] 4 | // test262: https://github.com/tc39/test262/blob/main/test/language/asi/S7.9.2_A1_T5.js 5 | fn asi_increment_after_identifier() { 6 | let mut jsi_vm = JSI::new(); 7 | let program = jsi_vm.parse(String::from("a = b\n 8 | ++c")).unwrap(); 9 | assert_eq!(program.body, vec![ 10 | Statement::Expression(ExpressionStatement { // a + b 11 | expression: Expression::Assign(AssignExpression { 12 | left: Box::new(Expression::Identifier(IdentifierLiteral { literal: String::from("a") })), 13 | operator: Token::Assign, 14 | right: Box::new(Expression::Identifier(IdentifierLiteral { literal: String::from("b") })), 15 | }), 16 | }), 17 | Statement::Expression(ExpressionStatement { // ++c 18 | expression: Expression::PrefixUnary(PrefixUnaryExpression { 19 | operand: Box::new(Expression::Identifier(IdentifierLiteral { literal: String::from("c") })), 20 | operator: Token::Increment, 21 | }) 22 | }) 23 | ]); 24 | } 25 | 26 | #[test] 27 | // test262: https://github.com/tc39/test262/blob/main/test/language/asi/S7.9.2_A1_T4.js 28 | fn asi_after_keyword_return() { 29 | let mut jsi_vm = JSI::new(); 30 | let program = jsi_vm.parse(String::from("function test(){\n 31 | return\n 32 | a+b\n 33 | }")).unwrap(); 34 | assert_eq!(program.body, vec![ 35 | Statement::Function(FunctionDeclaration { // a + b 36 | is_anonymous: false, 37 | is_arrow: false, 38 | name: IdentifierLiteral { literal: String::from("test") }, 39 | parameters: vec![], 40 | body: BlockStatement { 41 | statements: vec![ 42 | Statement::Return(ReturnStatement { 43 | expression: Expression::Keyword(Keywords::Undefined) 44 | }), 45 | Statement::Expression(ExpressionStatement { // ++c 46 | expression: Expression::Binary(BinaryExpression { 47 | left: Box::new(Expression::Identifier(IdentifierLiteral { literal: String::from("a") })), 48 | operator: Token::Plus, 49 | right: Box::new(Expression::Identifier(IdentifierLiteral { literal: String::from("b") })), 50 | }) 51 | }) 52 | ] 53 | }, 54 | declarations: vec![], 55 | bytecode: vec![], 56 | }), 57 | ]); 58 | } 59 | 60 | 61 | 62 | #[test] 63 | // test262: https://github.com/tc39/test262/blob/main/test/language/asi/S7.9.2_A1_T7.js 64 | fn asi_not_insert_after_identifier() { 65 | let mut jsi_vm = JSI::new(); 66 | let value = jsi_vm.run(String::from("function c (a){ 67 | return 2*a; 68 | } 69 | 70 | var a=1,b=2,d=4,e=5; 71 | 72 | a=b+c 73 | (d+e)")).unwrap(); 74 | assert_eq!(value,Value::Number(20f64)); 75 | } -------------------------------------------------------------------------------- /src/bytecode.rs: -------------------------------------------------------------------------------- 1 | // 定义 bytecode operator 列表 2 | 3 | use core::fmt; 4 | 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub enum EByteCodeop { 7 | // 将 undefined 推入栈 8 | OpUndefined, 9 | OpNull, 10 | OpTrue, 11 | OpFalse, 12 | OpString, 13 | OpNumber, 14 | // 从栈中弹出 n 个值初始化数组,然后把数组推入栈 15 | OpArray, 16 | // 向当前作用于推入一个变量,并弹出一个值作为变量的初始值 17 | OpScopePutVarInit, 18 | OpScopePutVar, 19 | OpScopeGetVar, 20 | OpPushConst, 21 | // 从栈中弹出一个值,获取对象属性后入栈 22 | OpGetProperty, 23 | // 从栈中弹出2个值,把第一个值赋值给第二个值 24 | OpAssign, 25 | // 从栈中弹出一个值,进行一元运算后推入栈 26 | OpPrefixUnary, 27 | OpPostfixUnary, 28 | // 从栈中弹出 2 值,进行运算后的值推入栈 29 | OpAdd, 30 | OpSub, 31 | OpMul, 32 | OpDiv, 33 | // function xxx 函数定义,匿名函数的名称为 `""` 34 | OpFuncStart, 35 | OpFuncEnd, 36 | // 获取函数参数,弹出一个值作为参数名,推入栈 37 | OpGetArg, 38 | // 将函数入栈,传入的是函数名,如果是匿名函数, 函数名是 `""` 39 | OpGetFunc, 40 | // 函数调用,弹出 n 个值作为参数、弹出一个值作为 function,进行执行,结果入栈 41 | OpCall, 42 | OpReturn, 43 | // 标签 44 | OpLabel, 45 | OpGoto, 46 | // 跳转到指定标签 47 | OpIfFalse, 48 | // 逻辑运算 49 | OpEqual, 50 | OpNotEqual, 51 | OpStrictEqual, 52 | OpStrictNotEqual, 53 | // < 54 | OpLessThan, 55 | // <= 56 | OpLessThanOrEqual, 57 | // > 58 | OpGreaterThan, 59 | // >= 60 | OpGreaterThanOrEqual, 61 | OpInstanceOf, 62 | } 63 | 64 | impl fmt::Display for EByteCodeop { 65 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 66 | match self { 67 | EByteCodeop::OpUndefined => write!(f, "Undefined"), 68 | EByteCodeop::OpNull => write!(f, "Null"), 69 | EByteCodeop::OpTrue => write!(f, "True"), 70 | EByteCodeop::OpFalse => write!(f, "False"), 71 | EByteCodeop::OpString => write!(f, "String"), 72 | EByteCodeop::OpNumber => write!(f, "Number"), 73 | EByteCodeop::OpArray => write!(f, "Array"), 74 | EByteCodeop::OpScopePutVarInit => write!(f, "ScopePutVarInit"), 75 | EByteCodeop::OpScopePutVar => write!(f, "ScopePutVar"), 76 | EByteCodeop::OpScopeGetVar => write!(f, "ScopeGetVar"), 77 | EByteCodeop::OpPushConst => write!(f, "PushConst"), 78 | EByteCodeop::OpAdd => write!(f, "Add"), 79 | EByteCodeop::OpSub => write!(f, "Sub"), 80 | EByteCodeop::OpMul => write!(f, "Mul"), 81 | EByteCodeop::OpDiv => write!(f, "Div"), 82 | EByteCodeop::OpCall => write!(f, "Call"), 83 | _ => write!(f, "Unknown"), 84 | } 85 | } 86 | } 87 | 88 | // ByteCode 结构体定义 89 | #[derive(Debug, Clone)] 90 | pub struct ByteCode { 91 | // 指令 92 | pub op: EByteCodeop, 93 | // 操作数 94 | pub args: Vec, 95 | // 行号 96 | pub line: usize, 97 | } 98 | 99 | impl PartialEq for ByteCode { 100 | fn eq(&self, other: &ByteCode) -> bool { 101 | match (self, other) { 102 | _ => self.op == other.op, 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /tests/confitional_test.rs: -------------------------------------------------------------------------------- 1 | 2 | use jsi::{JSI, value::Value}; 3 | 4 | #[test] 5 | fn run_if_else() { 6 | let mut jsi = JSI::new(); 7 | let result = jsi.run_with_bytecode(String::from("\ 8 | let a = false; 9 | let b = 1; 10 | if (a) { 11 | b = 1; 12 | } else if (b) { 13 | if (b == 1) { 14 | b = 2; 15 | } else { 16 | b = 3; 17 | } 18 | } else { 19 | b = 4; 20 | }\n 21 | b")).unwrap(); 22 | assert_eq!(result , Value::Number(2f64)); 23 | } 24 | 25 | #[test] 26 | fn run_switch_case() { 27 | let mut jsi = JSI::new(); 28 | let result = jsi.run(String::from("\ 29 | let res; 30 | switch('a') { 31 | case 'a': 32 | res = 1; 33 | break; 34 | case 'b': 35 | res = 2; 36 | break; 37 | default: 38 | res = 3; 39 | break; 40 | } 41 | res")).unwrap(); 42 | assert_eq!(result , Value::Number(1f64)); 43 | } 44 | 45 | #[test] 46 | fn run_for() { 47 | let mut jsi = JSI::new(); 48 | let result = jsi.run(String::from("\ 49 | let a = []; 50 | let i; 51 | for(i = 0; i < 3; i++) { 52 | a.push(++i); 53 | } 54 | a.join(':')")).unwrap(); 55 | assert_eq!(result , Value::String(String::from("1:3"))); 56 | } 57 | 58 | 59 | #[test] 60 | fn run_for_break_continue_label() { 61 | let mut jsi = JSI::new(); 62 | let result = jsi.run(String::from("\ 63 | let a = []; 64 | outer: 65 | for(let i = 0; i < 3; i++) { 66 | for(let j = 0; j < 5; j++) { 67 | if (j == 1 && i == 1) { 68 | continue outer 69 | } 70 | if (j == 4) break 71 | if (j == 3 && i == 2) { 72 | break outer 73 | } 74 | a.push(i * j); 75 | }\n 76 | }\n 77 | a.join(':')")).unwrap(); 78 | assert_eq!(result , Value::String(String::from("0:0:0:0:0:0:2:4"))); 79 | } 80 | 81 | #[test] 82 | fn run_while_break_continue_label() { 83 | let mut jsi = JSI::new(); 84 | let result = jsi.run(String::from("\ 85 | let a = []; 86 | let i = 0; 87 | outer: 88 | while(i < 3) { 89 | i ++; 90 | let j = 0; 91 | while(j < 5) { 92 | j ++; 93 | if (j == 1 && i == 1) { 94 | continue outer 95 | } 96 | if (j == 4) break 97 | if (j == 3 && i == 2) { 98 | break outer 99 | } 100 | a.push(i * j); 101 | } 102 | } 103 | a.join(':')")).unwrap(); 104 | assert_eq!(result , Value::String(String::from("2:4"))); 105 | } 106 | 107 | #[test] 108 | fn run_dowhile_break_continue_label() { 109 | let mut jsi = JSI::new(); 110 | let result = jsi.run(String::from("\ 111 | let a = []; 112 | let i = 0; 113 | outer: 114 | do { 115 | i ++; 116 | let j = 0 117 | do { 118 | j ++; 119 | if (j == 1 && i == 1) { 120 | continue outer 121 | } 122 | if (j == 4) break 123 | if (j == 3 && i == 2) { 124 | break outer 125 | } 126 | a.push(i * j); 127 | } while (j < 5); 128 | } while (i < 3); 129 | a.join(':')")).unwrap(); 130 | assert_eq!(result , Value::String(String::from("2:4"))); 131 | } -------------------------------------------------------------------------------- /tests/function_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, value::Value, error::JSIErrorType}; 2 | 3 | 4 | #[test] 5 | fn run_function_base() { 6 | let mut jsi_vm = JSI::new(); 7 | let value = jsi_vm.run_with_bytecode(String::from("\n 8 | function add(x, y) { 9 | return x * 2 + y; 10 | }; 11 | add(1, 'a')")).unwrap(); 12 | assert_eq!(value , Value::String(String::from("2a"))); 13 | } 14 | 15 | 16 | #[test] 17 | fn run_function_scope1() { 18 | let mut jsi_vm = JSI::new(); 19 | let value = jsi_vm.run(String::from("\n 20 | let fun1 = function(x, y) { 21 | let a = 123; 22 | return fun2(); 23 | };\n 24 | let fun2 = function() { 25 | return a; 26 | };\n 27 | fun1()")); 28 | if let Err(jsi_error) = value { 29 | assert_eq!(jsi_error.error_type, JSIErrorType::ReferenceError); 30 | assert_eq!(jsi_error.message , String::from("a is not defined")); 31 | } else { 32 | assert!(false , "need TypeError"); 33 | } 34 | } 35 | 36 | #[test] 37 | fn run_function_scope2() { 38 | let mut jsi_vm = JSI::new(); 39 | let value = jsi_vm.run(String::from("\n 40 | let a = 123; 41 | let fun = function() { 42 | return a; 43 | };\n 44 | fun()")).unwrap(); 45 | assert_eq!(value , Value::Number(123f64)); 46 | } 47 | 48 | #[test] 49 | fn run_function_instances_has_class() { 50 | let mut jsi = JSI::new(); 51 | let result = jsi.run(String::from("\ 52 | function func() {}\n 53 | Object.prototype.toString.call(func)")).unwrap(); 54 | assert_eq!(result , Value::String(String::from("[object Function]"))); 55 | } 56 | 57 | #[test] 58 | fn run_function_typeof() { 59 | let mut jsi = JSI::new(); 60 | let result = jsi.run(String::from("\ 61 | var check=0; 62 | while(function f(){}){ 63 | if(typeof(f) === 'function') { 64 | check = -1; 65 | break; 66 | } else { 67 | check = 1; 68 | break; 69 | } 70 | }check.toString() + typeof function() {}")).unwrap(); 71 | assert_eq!(result , Value::String(String::from("1function"))); 72 | } 73 | 74 | #[test] 75 | fn run_arrow_function() { 76 | let mut jsi = JSI::new(); 77 | jsi.set_strict(false); 78 | let result = jsi.run(String::from("\ 79 | let a = (a, b ,c) => { 80 | return '1' + a + b + c; 81 | } 82 | let b = b => { 83 | return '2' + b; 84 | }; 85 | let c = c => c + '3'; 86 | let d = (d,d) => [arguments.length, arguments[0], arguments[1], d, '4'].join(); 87 | a(1, 'a', false) + a.name + b(2) + b.name + c(3) + c.name + d(4,5);")).unwrap(); 88 | assert_eq!(result , Value::String(String::from("11afalsea22b33c2,4,5,5,4"))); 89 | } 90 | 91 | #[test] 92 | fn run_new_function() { 93 | let mut jsi = JSI::new(); 94 | jsi.set_strict(false); 95 | let result = jsi.run(String::from("\ 96 | let a = function(a, b ,c) { 97 | this.name = a + b + c; 98 | } 99 | a.prototype.age = 456; 100 | let b = new a(1,'2', false); 101 | let c = a; 102 | let d = a.bind(123); 103 | b.age + b.name + (a === c) + (a === d); 104 | ")).unwrap(); 105 | assert_eq!(result , Value::String(String::from("45612falsetruefalse"))); 106 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "itoa" 7 | version = "1.0.15" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 10 | 11 | [[package]] 12 | name = "jsi" 13 | version = "0.1.0" 14 | dependencies = [ 15 | "serde", 16 | "serde_json", 17 | "yaml-rust", 18 | ] 19 | 20 | [[package]] 21 | name = "linked-hash-map" 22 | version = "0.5.6" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" 25 | 26 | [[package]] 27 | name = "memchr" 28 | version = "2.7.4" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 31 | 32 | [[package]] 33 | name = "proc-macro2" 34 | version = "1.0.95" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 37 | dependencies = [ 38 | "unicode-ident", 39 | ] 40 | 41 | [[package]] 42 | name = "quote" 43 | version = "1.0.40" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 46 | dependencies = [ 47 | "proc-macro2", 48 | ] 49 | 50 | [[package]] 51 | name = "ryu" 52 | version = "1.0.20" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 55 | 56 | [[package]] 57 | name = "serde" 58 | version = "1.0.219" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 61 | dependencies = [ 62 | "serde_derive", 63 | ] 64 | 65 | [[package]] 66 | name = "serde_derive" 67 | version = "1.0.219" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 70 | dependencies = [ 71 | "proc-macro2", 72 | "quote", 73 | "syn", 74 | ] 75 | 76 | [[package]] 77 | name = "serde_json" 78 | version = "1.0.140" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 81 | dependencies = [ 82 | "itoa", 83 | "memchr", 84 | "ryu", 85 | "serde", 86 | ] 87 | 88 | [[package]] 89 | name = "syn" 90 | version = "2.0.101" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 93 | dependencies = [ 94 | "proc-macro2", 95 | "quote", 96 | "unicode-ident", 97 | ] 98 | 99 | [[package]] 100 | name = "unicode-ident" 101 | version = "1.0.18" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 104 | 105 | [[package]] 106 | name = "yaml-rust" 107 | version = "0.4.5" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 110 | dependencies = [ 111 | "linked-hash-map", 112 | ] 113 | -------------------------------------------------------------------------------- /src/builtins/boolean.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc}; 2 | use crate::constants::PROTO_PROPERTY_NAME; 3 | use crate::context::{Context}; 4 | use crate::{value::{Value, INSTANTIATE_OBJECT_METHOD_NAME}, ast_node::{ClassType, CallContext}, constants::GLOBAL_BOOLEAN_NAME, error::JSIResult}; 5 | 6 | use super::global::{get_global_object_prototype_by_name, get_global_object_by_name}; 7 | use super::{object::{create_object, Property}, function::builtin_function}; 8 | 9 | // ref:https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-boolean-objects 10 | pub fn create_boolean(ctx: &mut Context, init: Value) -> Value { 11 | let global_boolean = get_global_object_by_name(ctx, GLOBAL_BOOLEAN_NAME); 12 | let boolean = create_object(ctx, ClassType::Boolean, None); 13 | let boolean_clone = Rc::clone(&boolean); 14 | let mut boolean_mut = (*boolean_clone).borrow_mut(); 15 | let global_prototype = get_global_object_prototype_by_name(ctx, GLOBAL_BOOLEAN_NAME); 16 | boolean_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 17 | 18 | boolean_mut.constructor = Some(Rc::downgrade(&global_boolean)); 19 | boolean_mut.set_inner_property_value(String::from("value"), init); 20 | Value::BooleanObj(boolean) 21 | } 22 | 23 | pub fn bind_global_boolean(ctx: &mut Context) { 24 | let bool_rc = get_global_object_by_name(ctx, GLOBAL_BOOLEAN_NAME); 25 | let mut bool = (*bool_rc).borrow_mut(); 26 | let create_function = builtin_function(ctx, INSTANTIATE_OBJECT_METHOD_NAME.to_string(), 1f64, create); 27 | bool.set_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string(), create_function); 28 | if let Some(prop)= &bool.prototype { 29 | let prototype_rc = Rc::clone(prop); 30 | let mut prototype = prototype_rc.borrow_mut(); 31 | let name = String::from("toString"); 32 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 0f64, boolean_to_string) }); 33 | let name = String::from("valueOf"); 34 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 1f64, value_of) }); 35 | } 36 | } 37 | 38 | // Boolean.prototype.toString 39 | fn boolean_to_string(call_ctx: &mut CallContext, _: Vec) -> JSIResult { 40 | let value = value_of(call_ctx, vec![])?; 41 | Ok(Value::String(value.to_string(call_ctx.ctx))) 42 | } 43 | 44 | // Boolean.prototype.valueOf 45 | fn value_of(call_ctx: &mut CallContext, _: Vec) -> JSIResult { 46 | if let Value::Boolean(_) = &call_ctx.this { 47 | return Ok(call_ctx.this.clone()) 48 | } 49 | 50 | let bool_obj = match &call_ctx.this { 51 | Value::Object(bool) => { 52 | if let ClassType::Boolean = bool.borrow().class_type { 53 | Some(bool) 54 | } else { 55 | None 56 | } 57 | }, 58 | Value::BooleanObj(boolobj) => Some(boolobj), 59 | _ => None, 60 | }; 61 | if let Some(bool) = bool_obj { 62 | let init = bool.borrow().get_inner_property_value(String::from("value")); 63 | if let Some(value) = init { 64 | let res: bool = value.to_boolean(call_ctx.ctx); 65 | return Ok(Value::Boolean(res)) 66 | } 67 | } 68 | Ok(Value::Boolean(false)) 69 | } 70 | 71 | fn create(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 72 | let mut param = Value::Undefined; 73 | if args.len() > 0 { 74 | param = args[0].clone(); 75 | } 76 | Ok(create_boolean(call_ctx.ctx, param)) 77 | } -------------------------------------------------------------------------------- /src/builtins/number.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc}; 2 | use crate::constants::{GLOBAL_NUMBER_NAME, PROTO_PROPERTY_NAME}; 3 | use crate::context::{Context}; 4 | use crate::error::{JSIError, JSIErrorType}; 5 | use crate::{value::{Value, INSTANTIATE_OBJECT_METHOD_NAME}, ast_node::{ClassType, CallContext}, error::JSIResult}; 6 | 7 | use super::global::{get_global_object_prototype_by_name, get_global_object_by_name}; 8 | use super::{object::{create_object, Property}, function::builtin_function}; 9 | 10 | pub fn create_number(ctx: &mut Context, init: Value) -> Value { 11 | let global_number = get_global_object_by_name(ctx, GLOBAL_NUMBER_NAME); 12 | let number = create_object(ctx, ClassType::Number, None); 13 | let number_clone = Rc::clone(&number); 14 | let mut number_mut = (*number_clone).borrow_mut(); 15 | number_mut.constructor = Some(Rc::downgrade(&global_number)); 16 | number_mut.set_inner_property_value(String::from("value"), init); 17 | 18 | let global_prototype = get_global_object_prototype_by_name(ctx, GLOBAL_NUMBER_NAME); 19 | number_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 20 | Value::NumberObj(number) 21 | } 22 | 23 | pub fn bind_global_number(ctx: &mut Context) { 24 | let number_rc = get_global_object_by_name(ctx, GLOBAL_NUMBER_NAME); 25 | let mut number = (*number_rc).borrow_mut(); 26 | let create_function = builtin_function(ctx, INSTANTIATE_OBJECT_METHOD_NAME.to_string(), 1f64, create); 27 | number.set_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string(), create_function); 28 | if let Some(prop)= &number.prototype { 29 | let prototype_rc = Rc::clone(prop); 30 | let mut prototype = (*prototype_rc).borrow_mut(); 31 | let name = String::from("toString"); 32 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 0f64, to_string) }); 33 | let name = String::from("valueOf"); 34 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 1f64, value_of) }); 35 | } 36 | } 37 | 38 | // Number.prototype.toString 39 | fn to_string(call_ctx: &mut CallContext, _: Vec) -> JSIResult { 40 | let value = value_of(call_ctx, vec![])?; 41 | Ok(Value::String(value.to_string(call_ctx.ctx))) 42 | } 43 | 44 | 45 | // Number.prototype.valueOf 46 | fn value_of(call_ctx: &mut CallContext, _: Vec) -> JSIResult { 47 | if let Value::Number(_) = &call_ctx.this { 48 | return Ok(call_ctx.this.clone()) 49 | } 50 | 51 | let number_obj = match &call_ctx.this { 52 | Value::Object(number) => { 53 | if let ClassType::Number = number.borrow().class_type { 54 | Some(number) 55 | } else { 56 | None 57 | } 58 | }, 59 | Value::NumberObj(number) => Some(number), 60 | _ => None, 61 | }; 62 | 63 | if let Some(number) = number_obj { 64 | let init = number.borrow().get_inner_property_value(String::from("value")); 65 | if let Some(value) = init { 66 | let res = value.to_number(call_ctx.ctx); 67 | if let Some(num) = res { 68 | return Ok(Value::Number(num)) 69 | } 70 | } 71 | } 72 | Err(JSIError::new(JSIErrorType::TypeError, format!("Number.prototype.valueOf requires that 'this' be a Number"), 0, 0)) 73 | } 74 | 75 | fn create(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 76 | let mut param = Value::Undefined; 77 | if args.len() > 0 { 78 | param = args[0].clone(); 79 | } 80 | Ok(create_number(call_ctx.ctx, param)) 81 | } -------------------------------------------------------------------------------- /src/builtins/string.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::ops::Index; 3 | use std::{rc::Rc}; 4 | use crate::constants::{PROTO_PROPERTY_NAME, GLOBAL_STRING_NAME}; 5 | use crate::context::{Context}; 6 | use crate::error::{JSIError, JSIErrorType}; 7 | use crate::{value::{Value, INSTANTIATE_OBJECT_METHOD_NAME}, ast_node::{ClassType, CallContext}, error::JSIResult}; 8 | 9 | use super::global::{get_global_object_prototype_by_name, get_global_object_by_name}; 10 | use super::{object::{create_object, Property}, function::builtin_function}; 11 | 12 | pub fn create_string(ctx: &mut Context, init: Value) -> Value { 13 | let global_string = get_global_object_by_name(ctx, GLOBAL_STRING_NAME); 14 | let string = create_object(ctx, ClassType::String, None); 15 | let string_clone = Rc::clone(&string); 16 | let mut string_mut = (*string_clone).borrow_mut(); 17 | string_mut.constructor = Some(Rc::downgrade(&global_string)); 18 | if init.is_string() { 19 | string_mut.set_inner_property_value(String::from("value"), init); 20 | } else { 21 | string_mut.set_inner_property_value(String::from("value"), Value::String(init.to_string(ctx))); 22 | } 23 | 24 | 25 | let global_prototype = get_global_object_prototype_by_name(ctx, GLOBAL_STRING_NAME); 26 | string_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 27 | Value::StringObj(string) 28 | } 29 | 30 | pub fn bind_global_string(ctx: &mut Context) { 31 | let string_rc = get_global_object_by_name(ctx, GLOBAL_STRING_NAME); 32 | let mut string = (*string_rc).borrow_mut(); 33 | let create_function = builtin_function(ctx, INSTANTIATE_OBJECT_METHOD_NAME.to_string(), 1f64, create); 34 | string.set_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string(), create_function); 35 | if let Some(prop)= &string.prototype { 36 | let prototype_rc = Rc::clone(prop); 37 | let mut prototype = (*prototype_rc).borrow_mut(); 38 | let name = String::from("charAt"); 39 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 1f64, char_at) }); 40 | let name = String::from("toString"); 41 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 0f64, to_string) }); 42 | let name = String::from("valueOf"); 43 | prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 1f64, to_string) }); 44 | } 45 | } 46 | 47 | 48 | fn create(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 49 | let mut param = Value::Undefined; 50 | if args.len() > 0 { 51 | param = args[0].clone(); 52 | } 53 | Ok(create_string(call_ctx.ctx, param)) 54 | } 55 | 56 | // String.prototype.toString 57 | fn to_string(call_ctx: &mut CallContext, _: Vec) -> JSIResult { 58 | let string = get_string(call_ctx); 59 | if let Ok(str) = string { 60 | return Ok(Value::String(str)) 61 | } 62 | Err(JSIError::new(JSIErrorType::TypeError, format!("String.prototype.toString requires that 'this' be a String"), 0, 0)) 63 | } 64 | 65 | // String.prototype.charAt 66 | fn char_at(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 67 | if call_ctx.this.is_not_strict_null() { 68 | return Err(JSIError::new(JSIErrorType::TypeError, format!("String.prototype.charAt called on null or undefined"), 0, 0)) 69 | } 70 | let string = get_string(call_ctx); 71 | if let Err(_) = string { 72 | return Err(JSIError::new(JSIErrorType::TypeError, format!("unknown error"), 0, 0)) 73 | } 74 | let str = match string { 75 | Ok(str) => str, 76 | _ => String::from(""), 77 | }; 78 | let mut index: usize = 0; 79 | if args.len() > 0 { 80 | let arg = &args[0]; 81 | let index_number = arg.to_number(call_ctx.ctx); 82 | if let Some(index_f64) = index_number { 83 | index = index_f64 as usize; 84 | } else { 85 | let arg_type = arg.type_of(); 86 | return Err(JSIError::new(JSIErrorType::TypeError, format!("Cannot convert {:?} to primitive value at String.charAt", arg_type), 0, 0)) 87 | } 88 | } 89 | let utf16: Vec = str.as_str().encode_utf16().collect(); 90 | if index < utf16.len() { 91 | let char = utf16[index]; 92 | return Ok(Value::String(char.to_string())) 93 | } 94 | 95 | return Ok(Value::String("".to_string())) 96 | } 97 | 98 | fn get_string(call_ctx: &mut CallContext) -> Result> { 99 | if let Value::String(str) = &call_ctx.this { 100 | return Ok(str.clone()) 101 | } 102 | let string_obj = match &call_ctx.this { 103 | Value::Object(string) => { 104 | if let ClassType::String = string.borrow().class_type { 105 | Some(string) 106 | } else { 107 | None 108 | } 109 | }, 110 | Value::StringObj(string) => Some(string), 111 | _ => None, 112 | }; 113 | if let Some(str) = string_obj { 114 | let init = str.borrow().get_inner_property_value(String::from("value")); 115 | if let Some(value) = init { 116 | return Ok(value.to_string(call_ctx.ctx)) 117 | } 118 | } 119 | Err("error".into()) 120 | } -------------------------------------------------------------------------------- /src/ast_token.rs: -------------------------------------------------------------------------------- 1 | 2 | // 关键字 3 | // ref: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Lexical_grammar#%E5%85%B3%E9%94%AE%E5%AD%97 4 | #[derive(Debug, PartialEq, Clone)] 5 | pub enum Token { 6 | // es6 keywords 7 | Break, 8 | Case, 9 | Catch, 10 | Class, 11 | Const, 12 | Continue, 13 | Debugger, 14 | Default, 15 | Delete, 16 | Do, 17 | Else, 18 | Export, 19 | Extends, 20 | Finally, 21 | For, 22 | Function, 23 | If, 24 | Import, 25 | In, 26 | Instanceof, 27 | New, 28 | Return, 29 | Super, 30 | Switch, 31 | This, 32 | Throw, 33 | Try, 34 | Typeof, 35 | Var, 36 | Void, 37 | While, 38 | With, 39 | Yield, 40 | // 仅在严格模式下作为关键字 41 | Implements, 42 | Interface, 43 | Let, 44 | Package, 45 | Private, 46 | Protected, 47 | Public, 48 | Static, 49 | Async, // ES2015 50 | Await, // ES2015 51 | 52 | // 字面量 literal 53 | Undefined, 54 | Null, 55 | True, 56 | False, 57 | // 类型标识符 58 | Number, 59 | String, 60 | // 普通标识符 61 | Identifier, 62 | // 运算符 operators 63 | // 标点符号 64 | Plus, // "+" 65 | Subtract, // "-" 66 | Multiply, // "*" 67 | Slash, // "/" 68 | Remainder, // "%" 69 | And, // "&" 70 | Or, // "|" 71 | ExclusiveOr, // "^" 72 | ShiftLeft, // "<<" 73 | ShiftRight, // ">>" 74 | UnsignedShiftRight, // ">>>" 75 | AndNot, // "&^" 76 | AddAssign, // "+=" 77 | SubtractAssign, // "-=" 78 | MultiplyAssign, // "*=" 79 | SlashAssign, // "/=" 80 | RemainderAssign, // "%=" 81 | AndAssign, // "&=" 82 | OrAssign, // "|=" 83 | ExclusiveOrAssign, // "^=" 84 | ShiftLeftAssign, // "<<=" 85 | ShiftRightAssign, // ">>=" 86 | UnsignedShiftRightAssign, // ">>>=" 87 | AndNotAssign, // "&^=" 88 | LogicalAnd, // "&&" 89 | LogicalAndAssign, // "&&=" 90 | LogicalOr, // "||" 91 | LogicalOrAssign, // "||=" 92 | Increment, // "++" 93 | Decrement, // "--" 94 | Equal, // "==" 95 | StrictEqual, // "===" 96 | Less, // "<" 97 | Greater, // ">" 98 | Assign, // "=" 99 | Not, // "!" 100 | BitwiseNot, // "~" 101 | NotEqual, // "!=" 102 | StrictNotEqual, // "!==" 103 | LessOrEqual, // "<=" 104 | GreaterOrEqual, // ">=" 105 | LeftParenthesis, // "(" 106 | LeftBracket, // "[" 107 | LeftBrace, // "{" 108 | Comma, // ",", 109 | Period, // "." 110 | RightParenthesis, // ")" 111 | RightBracket, // "]" 112 | RightBrace, // "}" 113 | Semicolon, // ";" 114 | Colon, // ":" 115 | QuestionMark, // "?" 116 | Backtick, // ` ES2015 117 | Exponentiation, // "**" ES2017 118 | ExponentiationAssign, // "**=" ES2017 119 | NullishCoalescing, // "??" ES2020 120 | NullishCoalescingAssign, // "??=" ES2020 121 | OptionalChaining, // "?." ES2020 122 | 123 | // not keyword 124 | ILLEGAL, 125 | // 结尾 126 | EOF, 127 | } 128 | 129 | // to_string 130 | impl ToString for Token { 131 | fn to_string(&self) -> String { 132 | match self { 133 | Token::Await => String::from("await"), 134 | Token::BitwiseNot => String::from("~"), 135 | Token::Decrement => String::from("--"), 136 | Token::Delete => String::from("delete"), 137 | Token::Increment => String::from("++"), 138 | Token::Not => String::from("!"), 139 | Token::Plus => String::from("+"), 140 | Token::Subtract => String::from("-"), 141 | Token::Typeof => String::from("typeof"), 142 | Token::Void => String::from("void"), 143 | _ => String::from("unsupported to_string token"), 144 | } 145 | } 146 | } 147 | 148 | 149 | pub fn get_token_keyword(literal: &String, is_strict: bool) -> Token { 150 | let str = literal.as_str(); 151 | match str { 152 | "break" => Token::Break, 153 | "case" => Token::Case, 154 | "catch" => Token::Catch, 155 | "class" => Token::Class, 156 | "const" => Token::Const, 157 | "continue" => Token::Continue, 158 | "debugger" => Token::Debugger, 159 | "default" => Token::Default, 160 | "delete" => Token::Delete, 161 | "do" => Token::Do, 162 | "else" => Token::Else, 163 | "export" => Token::Export, 164 | "extends" => Token::Extends, 165 | "finally" => Token::Finally, 166 | "for" => Token::For, 167 | "function" => Token::Function, 168 | "if" => Token::If, 169 | "import" => Token::Import, 170 | "in" => Token::In, 171 | "instanceof" => Token::Instanceof, 172 | "new" => Token::New, 173 | "return" => Token::Return, 174 | "super" => Token::Super, 175 | "switch" => Token::Switch, 176 | "this" => Token::This, 177 | "throw" => Token::Throw, 178 | "try" => Token::Try, 179 | "typeof" => Token::Typeof, 180 | "var" => Token::Var, 181 | "void" => Token::Void, 182 | "while" => Token::While, 183 | "with" => Token::With, 184 | "yield" => Token::Yield, 185 | "async" => Token::Async, 186 | "await" => Token::Await, 187 | _ => { 188 | if is_strict { 189 | match str { 190 | "implements" => Token::Implements, 191 | "interface" => Token::Interface, 192 | "let" => Token::Let, 193 | "package" => Token::Package, 194 | "private" => Token::Private, 195 | "protected" => Token::Protected, 196 | "public" => Token::Public, 197 | "static" => Token::Static, 198 | _ => Token::ILLEGAL, 199 | } 200 | } else { 201 | Token::ILLEGAL 202 | } 203 | } 204 | } 205 | } 206 | 207 | pub fn get_token_literal(literal: &String) -> Token { 208 | match literal.as_str() { 209 | "null" => Token::Null, 210 | "undefined" => Token::Undefined, 211 | "true" => Token::True, 212 | "false" => Token::False, 213 | _ => Token::ILLEGAL 214 | } 215 | } -------------------------------------------------------------------------------- /tests/error_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, value::Value, error::JSIErrorType}; 2 | 3 | #[test] 4 | fn run_throw_new_error() { 5 | let mut jsi = JSI::new(); 6 | let result = jsi.run(String::from("\ 7 | let errA = new Error; 8 | let errB = new Error(123); 9 | let errC = new Error('abc'); 10 | let errD = Error('def'); 11 | let result = { 12 | errA: errA.message, 13 | errB: errB.message, 14 | errC: errC.message, 15 | errD: errD.message 16 | } 17 | ")); 18 | println!("result: {:?}", result); 19 | // assert_eq!(result , Value::String(String::from("1,2"))); 20 | } 21 | 22 | #[test] 23 | fn run_reference_error() { 24 | let mut jsi = JSI::new(); 25 | let result = jsi.run(String::from("\ 26 | let a = b + 1")); 27 | if let Err(jsi_error) = result { 28 | assert_eq!(jsi_error.message , String::from("b is not defined")); 29 | } else { 30 | assert!(false , "need reference error"); 31 | } 32 | } 33 | 34 | #[test] 35 | fn run_new_number_error() { 36 | let mut jsi = JSI::new(); 37 | let result = jsi.run(String::from("\ 38 | let num = new 123;\n 39 | ")); 40 | if let Err(jsi_error) = result { 41 | assert_eq!(jsi_error.error_type, JSIErrorType::TypeError); 42 | assert_eq!(jsi_error.message , String::from("123 is not a constructor")); 43 | } else { 44 | assert!(false , "need TypeError"); 45 | } 46 | } 47 | 48 | #[test] 49 | fn run_new_false_error() { 50 | let mut jsi = JSI::new(); 51 | let result = jsi.run(String::from("\ 52 | let num = new false;\n 53 | ")); 54 | if let Err(jsi_error) = result { 55 | assert_eq!(jsi_error.error_type, JSIErrorType::TypeError); 56 | assert_eq!(jsi_error.message , String::from("false is not a constructor")); 57 | } else { 58 | assert!(false , "need TypeError"); 59 | } 60 | } 61 | 62 | #[test] 63 | fn run_read_properties_of_null_error() { 64 | let mut jsi = JSI::new(); 65 | let result = jsi.run(String::from("\ 66 | let num = null.a;\n 67 | ")); 68 | if let Err(jsi_error) = result { 69 | assert_eq!(jsi_error.error_type, JSIErrorType::TypeError); 70 | assert_eq!(jsi_error.message , String::from("Cannot read properties of null (reading 'a')")); 71 | } else { 72 | assert!(false , "need TypeError"); 73 | } 74 | } 75 | 76 | #[test] 77 | fn run_read_properties_of_number_property() { 78 | let mut jsi = JSI::new(); 79 | let result = jsi.run(String::from("\ 80 | let num = null.1;\n 81 | ")); 82 | if let Err(jsi_error) = result { 83 | assert_eq!(jsi_error.error_type, JSIErrorType::SyntaxError); 84 | assert_eq!(jsi_error.message , String::from("Unexpected number")); 85 | } else { 86 | assert!(false , "need SyntaxError"); 87 | } 88 | } 89 | 90 | #[test] 91 | fn run_read_properties_of_null_error_catch() { 92 | let mut jsi = JSI::new(); 93 | let result = jsi.run(String::from("\ 94 | try { 95 | let num = null.a; 96 | } catch (e) { 97 | e.message 98 | } 99 | ")).unwrap(); 100 | assert_eq!(result , Value::String(String::from("Cannot read properties of null (reading 'a')"))); 101 | } 102 | 103 | #[test] 104 | fn run_throw_error_catch() { 105 | let mut jsi = JSI::new(); 106 | let result = jsi.run(String::from("\ 107 | try { 108 | throw {a: 'abc'} 109 | } catch (obj) { 110 | obj.a 111 | } 112 | ")).unwrap(); 113 | assert_eq!(result , Value::String(String::from("abc"))); 114 | } 115 | 116 | 117 | #[test] 118 | fn run_const_error() { 119 | let mut jsi = JSI::new(); 120 | let result = jsi.run(String::from("\ 121 | const a = 123; 122 | a = 456; 123 | ")); 124 | if let Err(jsi_error) = result { 125 | assert_eq!(jsi_error.error_type, JSIErrorType::TypeError); 126 | assert_eq!(jsi_error.message , String::from("Assignment to constant variable")); 127 | } else { 128 | assert!(false , "need SyntaxError"); 129 | } 130 | } 131 | 132 | 133 | #[test] 134 | fn run_arrow_function_ast_error() { 135 | let mut jsi = JSI::new(); 136 | let result = jsi.run(String::from("\ 137 | var af = x; 138 | => {}; ")); 139 | if let Err(jsi_error) = result { 140 | assert_eq!(jsi_error.error_type, JSIErrorType::SyntaxError); 141 | assert_eq!(jsi_error.message , String::from("Unexpected token '=>'")); 142 | } else { 143 | assert!(false , "need SyntaxError"); 144 | } 145 | } 146 | 147 | #[test] 148 | fn run_arrow_function_duplicates_error_error() { 149 | let mut jsi = JSI::new(); 150 | let result = jsi.run(String::from("\ 151 | var af = (x, x) => {} ")); 152 | if let Err(jsi_error) = result { 153 | assert_eq!(jsi_error.error_type, JSIErrorType::SyntaxError); 154 | assert_eq!(jsi_error.message , String::from("Duplicate parameter name not allowed in this context")); 155 | } else { 156 | assert!(false , "need SyntaxError"); 157 | } 158 | } 159 | 160 | #[test] 161 | fn run_arrow_function_error() { 162 | let mut jsi = JSI::new(); 163 | let result = jsi.run(String::from("\ 164 | (x => x) = 1")); 165 | if let Err(jsi_error) = result { 166 | assert_eq!(jsi_error.error_type, JSIErrorType::SyntaxError); 167 | assert_eq!(jsi_error.message , String::from("Invalid left-hand side in assignment")); 168 | } else { 169 | assert!(false , "need SyntaxError"); 170 | } 171 | } 172 | 173 | #[test] 174 | fn run_error_test() { 175 | let mut jsi = JSI::new(); 176 | let result = jsi.run(String::from("\ 177 | var e = new Error('123'); 178 | console.log('e', e.constructor); 179 | ")); 180 | if let Err(jsi_error) = result { 181 | assert_eq!(jsi_error.error_type, JSIErrorType::SyntaxError); 182 | assert_eq!(jsi_error.message , String::from("Duplicate parameter name not allowed in this context")); 183 | } 184 | } -------------------------------------------------------------------------------- /src/builtins/global.rs: -------------------------------------------------------------------------------- 1 | use std::cell::{RefCell}; 2 | use std::rc::{Rc}; 3 | 4 | use crate::ast_node::ClassType; 5 | use crate::builtins::promise::bind_global_promise; 6 | use crate::constants::{GLOBAL_OBJECT_NAME_LIST, GLOBAL_OBJECT_NAME, PROTO_PROPERTY_NAME, GLOBAL_ERROR_NAME, GLOBAL_TYPE_ERROR_NAME}; 7 | use crate::value::Value; 8 | use crate::context::{Context}; 9 | use super::array::bind_global_array; 10 | use super::boolean::{bind_global_boolean}; 11 | use super::error::{bind_global_error}; 12 | use super::function::{bind_global_function}; 13 | use super::number::bind_global_number; 14 | use super::object::{Object, Property, bind_global_object}; 15 | use super::string::bind_global_string; 16 | 17 | pub const IS_GLOABL_OBJECT: &str = "isGlobal"; 18 | 19 | pub fn new_global_object() -> Rc> { 20 | let object = Rc::new(RefCell::new(Object::new(ClassType::Object, None))); 21 | let object_clone = Rc::clone(&object); 22 | let mut object_mut = (*object_clone).borrow_mut(); 23 | 24 | // 创建原型对象 prototype 25 | // Object.prototype 是所有对象的原型 26 | // 原型上面的方法,通过 bind_global_object 挂载 27 | let prototype = Rc::new(RefCell::new(Object::new(ClassType::Object, None))); 28 | let prototype_clone = Rc::clone(&prototype); 29 | let mut prototype_mut = prototype_clone.borrow_mut(); 30 | prototype_mut.define_property(String::from("constructor"), Property { 31 | enumerable: false, 32 | value: Value::RefObject(Rc::downgrade(&object)), 33 | }); 34 | object_mut.prototype = Some(prototype); 35 | object 36 | } 37 | 38 | // 全局对象 39 | pub fn new_global_this() -> Rc> { 40 | // 先创建全局 Object,以及 Object.prototype 41 | let first_obj = new_global_object(); 42 | let first_obj_clone = Rc::clone(&first_obj); 43 | let mut first_obj_borrow = (*first_obj_clone).borrow_mut(); 44 | first_obj_borrow.set_inner_property_value(IS_GLOABL_OBJECT.to_string(), Value::Boolean(true)); 45 | first_obj_borrow.set_inner_property_value(String::from("name"), Value::String(GLOBAL_OBJECT_NAME.to_string())); 46 | // native function 47 | let native_function = new_global_object(); 48 | { 49 | let native_function_rc = Rc::clone(&native_function); 50 | let mut native_borrow = native_function_rc.borrow_mut(); 51 | // 绑定 native_function的原型到全局 Object.prototype 52 | // Object['__proto__'] === native_function 53 | first_obj_borrow.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&native_function))); 54 | // native_function.__proto__ === Object['__proto__'].__proto__ === Object.prototype 55 | if let Some(prop) = &first_obj_borrow.prototype { 56 | native_borrow.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(prop))); 57 | } 58 | } 59 | 60 | // Global 61 | let global = new_global_object(); 62 | let global_clone = Rc::clone(&global); 63 | { 64 | let mut global_obj = global_clone.borrow_mut(); 65 | global_obj.property.insert(GLOBAL_OBJECT_NAME.to_string(), Property { enumerable: true, value: Value::Object(Rc::clone(&first_obj))}); 66 | // 创建并绑定全局对象 67 | for name in GLOBAL_OBJECT_NAME_LIST.iter() { 68 | if name == &GLOBAL_OBJECT_NAME { 69 | continue; 70 | } 71 | let object = new_global_object(); 72 | let object_rc = Rc::clone(&object); 73 | let mut object_borrow = object_rc.borrow_mut(); 74 | // 绑定当前对象的原型 75 | object_borrow.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::Object(Rc::clone(&native_function))); 76 | 77 | // 标记是全局对象 78 | object_borrow.set_inner_property_value(IS_GLOABL_OBJECT.to_string(), Value::Boolean(true)); 79 | // 添加对象 name 80 | object_borrow.set_inner_property_value(String::from("name"), Value::String(name.to_string())); 81 | global_obj.property.insert(name.to_string(), Property { enumerable: true, value: Value::Object(Rc::clone(&object))}); 82 | } 83 | } 84 | 85 | return global; 86 | } 87 | 88 | pub fn bind_global(ctx: &mut Context) { 89 | // 绑定 Object 的 静态方法 和 原型链方法 90 | bind_global_object(ctx); 91 | // 绑定 Function 的 静态方法 和 原型链方法 92 | bind_global_function(ctx); 93 | // 绑定 Array 的 静态方法 和 原型链方法 94 | bind_global_array(ctx); 95 | // 绑定 String 的 静态方法 和 原型链方法 96 | bind_global_string(ctx); 97 | // 绑定 Boolean 的 静态方法 和 原型链方法 98 | bind_global_boolean(ctx); 99 | // 绑定 Number 的 静态方法 和 原型链方法 100 | bind_global_number(ctx); 101 | 102 | // 绑定 Promise 的 静态方法 和 原型链方法 103 | bind_global_promise(ctx); 104 | // 绑定 Error 的 静态方法 和 原型链方法 105 | bind_global_error(ctx, GLOBAL_ERROR_NAME); 106 | bind_global_error(ctx, GLOBAL_TYPE_ERROR_NAME); 107 | 108 | let obj_rc = get_global_object(ctx, GLOBAL_OBJECT_NAME.to_string()); 109 | let obj_rc = obj_rc.borrow(); 110 | let obj_prototype_rc = &obj_rc.prototype; 111 | if let Some(obj_prototype) = obj_prototype_rc { 112 | // 绑定 prototype.[[Property]] 113 | for name in GLOBAL_OBJECT_NAME_LIST.iter() { 114 | if name == &GLOBAL_OBJECT_NAME { 115 | continue; 116 | } 117 | let global_item_rc = get_global_object(ctx, name.to_string()); 118 | let global_item_ref = global_item_rc.borrow(); 119 | if let Some(prop)= &global_item_ref.prototype { 120 | 121 | let prototype_rc = Rc::clone(prop); 122 | let mut prototype = (*prototype_rc).borrow_mut(); 123 | 124 | // 除 Object 外,其他的原型对象的原型 [[Property]] 都是 Object 的原型对象 125 | prototype.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&obj_prototype))); 126 | 127 | } 128 | } 129 | } 130 | } 131 | 132 | pub fn get_global_object(ctx: &mut Context, name: String) -> Rc> { 133 | 134 | let value = { 135 | let clone_global_mut = ctx.global.borrow_mut(); 136 | clone_global_mut.get_value(name.clone()) 137 | }; 138 | 139 | let obj = value.to_object(ctx); 140 | return obj; 141 | } 142 | 143 | pub fn get_global_object_by_name(ctx: &mut Context, name: &str) -> Rc> { 144 | let value = { 145 | let clone_global_mut = ctx.global.borrow_mut(); 146 | clone_global_mut.get_value(name.to_string().clone()) 147 | }; 148 | let obj = value.to_object(ctx); 149 | return obj; 150 | } 151 | // 获取全局对象的 prototype 152 | pub fn get_global_object_prototype_by_name(ctx: &mut Context, name: &str) -> Rc> { 153 | let obj = get_global_object_by_name(ctx, name); 154 | let obj_clone = Rc::clone(&obj); 155 | let obj_borrow = obj_clone.borrow_mut(); 156 | let proto = (obj_borrow.prototype.as_ref()).unwrap(); 157 | return Rc::clone(&proto); 158 | } -------------------------------------------------------------------------------- /tests/object_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI, ast_node::{Expression, Statement, ObjectLiteral, PropertyAssignment, NumberLiteral, StringLiteral, Keywords, BinaryExpression, ComputedPropertyName}, ast_token::Token, value::Value}; 2 | 3 | #[test] 4 | fn ast_base() { 5 | let mut jsi = JSI::new(); 6 | let program = jsi.parse(String::from("let a = {a: 123, b: '123', [1 + 'a']: false}")).unwrap(); 7 | let expr = match &program.body[0] { 8 | Statement::Var(expr_statement) => { 9 | if let Expression::Var(v) = &expr_statement.list[0] { 10 | *v.initializer.clone() 11 | } else { 12 | Expression::Unknown 13 | } 14 | }, 15 | _ => Expression::Unknown, 16 | }; 17 | assert_eq!(expr, Expression::Object(ObjectLiteral { 18 | properties: vec![ 19 | PropertyAssignment{ 20 | name: Box::new(Expression::String(StringLiteral { literal: String::from("a"), value: String::from("a")})), 21 | initializer: Box::new(Expression::Number(NumberLiteral { literal: String::from("123"), value: 123f64 })), 22 | }, 23 | PropertyAssignment{ 24 | name: Box::new(Expression::String(StringLiteral { literal: String::from("b"), value: String::from("b")})), 25 | initializer: Box::new(Expression::String(StringLiteral { literal: String::from("'123'"), value: String::from("123") })), 26 | }, 27 | PropertyAssignment{ 28 | name: Box::new(Expression::ComputedPropertyName(ComputedPropertyName { 29 | expression: Box::new(Expression::Binary(BinaryExpression { 30 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("1"), value: 1f64 })), 31 | operator: Token::Plus, 32 | right: Box::new(Expression::String(StringLiteral { literal: String::from("'a'"), value: String::from("a") })), 33 | })) 34 | })), 35 | initializer: Box::new(Expression::Keyword(Keywords::False)), 36 | } 37 | ] 38 | })); 39 | } 40 | 41 | #[test] 42 | fn ast_with_child_object() { 43 | let mut jsi = JSI::new(); 44 | let program = jsi.parse(String::from("let a = {obj: { x: false}}")).unwrap(); 45 | let expr = match &program.body[0] { 46 | Statement::Var(expr_statement) => { 47 | if let Expression::Var(v) = &expr_statement.list[0] { 48 | *v.initializer.clone() 49 | } else { 50 | Expression::Unknown 51 | } 52 | }, 53 | _ => Expression::Unknown, 54 | }; 55 | assert_eq!(expr, Expression::Object(ObjectLiteral { 56 | properties: vec![ 57 | PropertyAssignment{ 58 | name: Box::new(Expression::String(StringLiteral { literal: String::from("obj"), value: String::from("obj")})), 59 | initializer: Box::new(Expression::Object(ObjectLiteral { 60 | properties: vec![ 61 | PropertyAssignment{ 62 | name: Box::new(Expression::String(StringLiteral { literal: String::from("x"), value: String::from("x")})), 63 | initializer: Box::new(Expression::Keyword(Keywords::False)), 64 | }, 65 | ] 66 | })), 67 | }, 68 | ] 69 | })); 70 | } 71 | 72 | #[test] 73 | fn run_object() { 74 | let mut jsi = JSI::new(); 75 | let result = jsi.run(String::from("\ 76 | let a = 'foo', b = 42, c = {};\ 77 | let obj = {a, b, c};\ 78 | return obj.b;")).unwrap(); 79 | assert_eq!(result , Value::Number(42f64)); 80 | } 81 | 82 | #[test] 83 | fn run_object_duplicate_naming() { 84 | let mut jsi = JSI::new(); 85 | let result = jsi.run(String::from("let obj = { x: 1, x: 2}; obj.x;")).unwrap(); 86 | assert_eq!(result , Value::Number(2f64)); 87 | } 88 | 89 | #[test] 90 | fn run_object_property_access() { 91 | let mut jsi = JSI::new(); 92 | let result = jsi.run(String::from("let obj = { a: 'foo', b: 123}; obj['a'] + obj.b;")).unwrap(); 93 | assert_eq!(result , Value::String(String::from("foo123"))); 94 | } 95 | 96 | 97 | #[test] 98 | fn run_object_with_function_property() { 99 | let mut jsi = JSI::new(); 100 | let result = jsi.run(String::from("let obj = { fun: function(a) {return a + 123;}}; obj.fun('abc') + 456;")).unwrap(); 101 | assert_eq!(result , Value::String(String::from("abc123456"))); 102 | } 103 | 104 | 105 | #[test] 106 | fn run_object_as_param_ref() { 107 | let mut jsi = JSI::new(); 108 | let result = jsi.run(String::from("\ 109 | let obj = { a: 123};\ 110 | let fun = function(obj) {\ 111 | let x = 123;\ 112 | x = 456;\ 113 | obj.a = x;};\ 114 | fun(obj);\ 115 | obj.a;\ 116 | ")).unwrap(); 117 | assert_eq!(result , Value::Number(456f64)); 118 | } 119 | 120 | #[test] 121 | fn run_object_with_array_key() { 122 | let mut jsi = JSI::new(); 123 | let result = jsi.run(String::from("\ 124 | let a = [1,2]\n 125 | let b = {[a]: 3}\n 126 | Object.keys(b).toString()")).unwrap(); 127 | assert_eq!(result , Value::String(String::from("1,2"))); 128 | } 129 | 130 | #[test] 131 | fn run_new_object() { 132 | let mut jsi = JSI::new(); 133 | let result = jsi.run(String::from("\ 134 | let num = new Object(1);\n 135 | let num2 = Object(2);\n 136 | let numRes = num + num2 + 2; 137 | numRes.toString()")).unwrap(); 138 | assert_eq!(result , Value::String(String::from("5"))); 139 | } 140 | 141 | #[test] 142 | fn run_object_typeof() { 143 | let mut jsi = JSI::new(); 144 | let result = jsi.run(String::from("\ 145 | let num = new Object(1); 146 | typeof num")).unwrap(); 147 | assert_eq!(result , Value::String(String::from("object"))); 148 | } 149 | 150 | 151 | #[test] 152 | fn run_object_value() { 153 | let mut jsi = JSI::new(); 154 | let result = jsi.run(String::from("\ 155 | var counter = 0; 156 | var key1 = { 157 | valueOf: function() { 158 | counter++; 159 | return 1; 160 | }, 161 | toString: null 162 | }; 163 | var key2 = { 164 | valueOf: function() { 165 | counter++; 166 | return 2; 167 | }, 168 | toString: null 169 | }; 170 | 171 | var object = { 172 | a: 'A', 173 | [key1]: 'B', 174 | c: 'C', 175 | [key2]: 'D', 176 | }; 177 | Object.getOwnPropertyNames(object).join() 178 | ")).unwrap(); 179 | assert_eq!(result , Value::String(String::from("1,2,a,c"))); 180 | } 181 | 182 | #[test] 183 | fn run_object_test() { 184 | let mut jsi = JSI::new(); 185 | let result = jsi.run(String::from("\ 186 | const person = { 187 | isHuman: false, 188 | printIntroduction: function () { 189 | return `My name is ${this.name}. Am I human? ${this.isHuman}`; 190 | }, 191 | }; 192 | const me = Object.create(person); 193 | me.name = 'Matthew'; 194 | me.isHuman = true; 195 | me.printIntroduction(); 196 | ")).unwrap(); 197 | assert_eq!(result , Value::String(String::from("My name is Matthew. Am I human? true"))); 198 | } -------------------------------------------------------------------------------- /src/builtins/function.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::{Rc, Weak}, cell::RefCell}; 2 | use crate::{ast_node::{BlockStatement, IdentifierLiteral, Parameter}, bytecode::ByteCode, constants::{GLOBAL_FUNCTION_NAME, PROTO_PROPERTY_NAME}, context::Context, error::{JSIError, JSIErrorType}}; 3 | use crate::{ast_node::{Statement, FunctionDeclaration, BuiltinFunction, ClassType, CallContext}, value::{Value}, scope::Scope, error::JSIResult}; 4 | 5 | use super::{object::{create_object, Property, Object}, global::{get_global_object_prototype_by_name, get_global_object_by_name}, array::create_list_from_array_list}; 6 | 7 | // 初始化一个方法 8 | // ref: https://tc39.es/ecma262/multipage/ecmascript-language-functions-and-classes.html#prod-FunctionDeclaration 9 | pub fn create_function(ctx: &mut Context, function_declaration: &FunctionDeclaration, define_scope: Weak>) -> Value { 10 | let global_function = get_global_object_by_name(ctx, GLOBAL_FUNCTION_NAME); 11 | let function = create_object(ctx,ClassType::Function, Some(Box::new(Statement::Function((*function_declaration).clone())))); 12 | 13 | let function_clone = Rc::clone(&function); 14 | let mut function_mut = (*function_clone).borrow_mut(); 15 | // 绑定 fun.constructor = global.Function 16 | function_mut.constructor = Some(Rc::downgrade(&global_function)); 17 | // fun.name 18 | function_mut.define_property(String::from("name"), Property { 19 | enumerable: false, 20 | value: Value::String(function_declaration.name.literal.clone()), 21 | }); 22 | // fun.length 23 | function_mut.define_property(String::from("length"), Property { 24 | enumerable: false, 25 | value: Value::Number(function_declaration.parameters.len() as f64) 26 | }); 27 | 28 | let global_prototype = get_global_object_prototype_by_name(ctx, GLOBAL_FUNCTION_NAME); 29 | function_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 30 | 31 | // define_scope 32 | function_mut.set_inner_property_value(String::from("define_scope"), Value::Scope(define_scope)); 33 | function_mut.set_inner_property_value(String::from("bytecode"), Value::ByteCode(function_declaration.bytecode.clone())); 34 | 35 | // function prototype 36 | let prototype = Rc::new(RefCell::new(Object::new(ClassType::Object, None))); 37 | let prototype_clone = Rc::clone(&prototype); 38 | let mut prototype_mut = prototype_clone.borrow_mut(); 39 | // function.prototype.constructor 指向自己 40 | prototype_mut.define_property(String::from("constructor"), Property { 41 | enumerable: false, 42 | value: Value::RefObject(Rc::downgrade(&function)), 43 | }); 44 | function_mut.prototype = Some(prototype); 45 | Value::Function(function) 46 | } 47 | 48 | 49 | pub fn create_function_with_bytecode(ctx: &mut Context, name: String, parameters: Vec, bytecode: Vec, define_scope: Weak>) -> Value { 50 | let function_declaration = FunctionDeclaration { 51 | is_anonymous: false, 52 | is_arrow: false, 53 | name: IdentifierLiteral { 54 | literal: name, 55 | }, 56 | parameters, 57 | body: BlockStatement { 58 | statements: vec![], 59 | }, 60 | declarations: vec![], 61 | bytecode 62 | }; 63 | create_function(ctx, &function_declaration, define_scope) 64 | } 65 | 66 | // 构建内置方法 67 | pub fn builtin_function(ctx: &mut Context, name: String, length: f64, fun: BuiltinFunction) -> Value { 68 | let global_function = get_global_object_by_name(ctx, GLOBAL_FUNCTION_NAME); 69 | let function = create_object(ctx, ClassType::Function, Some(Box::new(Statement::BuiltinFunction(fun)))); 70 | let function_clone = Rc::clone(&function); 71 | let mut function_mut = (*function_clone).borrow_mut(); 72 | // 绑定 fun.constructor = global.Function 73 | function_mut.constructor = Some(Rc::downgrade(&global_function)); 74 | // fun.name 75 | function_mut.define_property(String::from("name"), Property { 76 | enumerable: false, 77 | value: Value::String(name), 78 | }); 79 | // fun.length 80 | function_mut.define_property(String::from("length"), Property { 81 | enumerable: false, 82 | value: Value::Number(length) 83 | }); 84 | 85 | let global_prototype = get_global_object_prototype_by_name(ctx, GLOBAL_FUNCTION_NAME); 86 | function_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 87 | Value::Function(function) 88 | } 89 | 90 | 91 | pub fn bind_global_function(ctx: &mut Context) { 92 | 93 | let apply_fun = builtin_function(ctx, String::from("apply"), 1f64, function_apply); 94 | let bind_fun = builtin_function(ctx, String::from("bind"), 1f64, function_bind); 95 | let call_fun = builtin_function(ctx, String::from("call"), 1f64, function_call); 96 | let fun_rc = get_global_object_by_name(ctx, GLOBAL_FUNCTION_NAME); 97 | let fun = (*fun_rc).borrow_mut(); 98 | if let Some(prop)= &fun.prototype { 99 | let prototype_rc = Rc::clone(prop); 100 | let mut prototype = prototype_rc.borrow_mut(); 101 | // let name = String::from("toString"); 102 | // prototype.define_property(name.clone(), Property { enumerable: true, value: builtin_function(global, name, 0f64, function_to_string) }); 103 | let name = String::from("apply"); 104 | prototype.define_property(name.clone(), Property { enumerable: true, value: apply_fun}); 105 | let name = String::from("call"); 106 | prototype.define_property(name.clone(), Property { enumerable: true, value: call_fun}); 107 | let name = String::from("bind"); 108 | prototype.define_property(name.clone(), Property { enumerable: true, value: bind_fun}); 109 | } 110 | } 111 | 112 | // Function.prototype.apply 113 | fn function_apply(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 114 | let mut this = Value::Undefined; 115 | let mut new_args: Vec = vec![]; 116 | if args.len() > 0 { 117 | this = args[0].clone(); 118 | if args.len() > 1 { 119 | new_args = create_list_from_array_list(call_ctx, &args[1])?; 120 | } 121 | } 122 | let new_fun = function_bind(call_ctx, vec![this])?; 123 | let call_function_define = match new_fun { 124 | Value::Function(function) => Some(function), 125 | _ => None, 126 | }.unwrap(); 127 | return call_ctx.call_function(call_function_define, None, None, new_args); 128 | } 129 | 130 | // Function.prototype.call 131 | fn function_call(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 132 | let mut this = Value::Undefined; 133 | let mut new_args: Vec = vec![]; 134 | if args.len() > 0 { 135 | this = args[0].clone(); 136 | new_args = args[1..].to_vec(); 137 | } 138 | let new_fun = function_bind(call_ctx, vec![this])?; 139 | let call_function_define = match new_fun { 140 | Value::Function(function) => Some(function), 141 | _ => None, 142 | }.unwrap(); 143 | return call_ctx.call_function(call_function_define, None, None, new_args); 144 | } 145 | 146 | // Function.prototype.bind 147 | fn function_bind(ctx: &mut CallContext, args: Vec) -> JSIResult { 148 | let mut this = Value::Undefined; 149 | if args.len() > 0 { 150 | this = args[0].clone(); 151 | } 152 | if let Value::Function(function_object) = &ctx.this { 153 | let fun_obj = function_object.borrow(); 154 | let mut new_fun = fun_obj.force_copy(); 155 | new_fun.set_inner_property_value(String::from("this"), this); 156 | Ok(Value::Function(Rc::new(RefCell::new(new_fun)))) 157 | } else { 158 | Err(JSIError::new(JSIErrorType::TypeError, format!("Bind must be called on a function 159 | "), 0, 0)) 160 | } 161 | } 162 | 163 | pub fn get_function_this(ctx: &mut Context, func: Rc>)-> Rc> { 164 | let bind_this = func.borrow().get_inner_property_value(String::from("this")); 165 | if let Some(this) = bind_this { 166 | return this.to_object(ctx) 167 | } 168 | return func 169 | } 170 | 171 | pub fn get_builtin_function_name(ctx: &mut Context, function_define: &Rc>) -> String { 172 | let fun_clone = Rc::clone(function_define); 173 | let fun_obj = (*fun_clone).borrow_mut(); 174 | let name = fun_obj.get_property_value(String::from("name")); 175 | if let Value::String(name) = name { 176 | return name; 177 | } 178 | return String::from("") 179 | } -------------------------------------------------------------------------------- /src/builtins/array.rs: -------------------------------------------------------------------------------- 1 | use std::cell::{RefCell}; 2 | use std::{rc::Rc}; 3 | use crate::constants::{PROTO_PROPERTY_NAME, GLOBAL_ARRAY_NAME}; 4 | use crate::context::{Context}; 5 | use crate::{value::Value, ast_node::{CallContext, ClassType}, error::{JSIResult, JSIError, JSIErrorType}}; 6 | 7 | use super::function::builtin_function; 8 | use super::global::{get_global_object_prototype_by_name, get_global_object_by_name}; 9 | use super::object::Object; 10 | use super::{object::{create_object, Property}}; 11 | 12 | // ref: https://tc39.es/ecma262/multipage/ecmascript-language-functions-and-classes.html#prod-FunctionDeclaration 13 | pub fn create_array(ctx: &mut Context, length: usize) -> Value { 14 | let global_array = get_global_object_by_name(ctx, GLOBAL_ARRAY_NAME); 15 | let array = create_object(ctx, ClassType::Array, None); 16 | let array_clone = Rc::clone(&array); 17 | let mut array_mut = (*array_clone).borrow_mut(); 18 | array_mut.define_property(String::from("length"), Property { enumerable: true, value: Value::Number(length as f64) }); 19 | 20 | let global_prototype = get_global_object_prototype_by_name(ctx, GLOBAL_ARRAY_NAME); 21 | array_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 22 | // 绑定 fun.constructor = global.Array 23 | array_mut.constructor = Some(Rc::downgrade(&global_array)); 24 | Value::Array(array) 25 | } 26 | 27 | pub fn create_array_from_values(ctx: &mut Context, values: Vec) -> Value { 28 | let new_array = create_array(ctx, 0); 29 | if let Value::Array(arr_obj) = &new_array { 30 | 31 | let mut arr = arr_obj.borrow_mut(); 32 | arr.define_property(String::from("length"), Property { enumerable: false, value: Value::Number(values.len() as f64) }); 33 | let mut index = 0; 34 | for value in values.iter() { 35 | arr.define_property(index.to_string(), Property { enumerable: true, value: value.clone() }); 36 | index += 1 37 | } 38 | } 39 | new_array 40 | } 41 | 42 | pub fn bind_global_array(ctx: &mut Context) { 43 | let arr_rc = get_global_object_by_name(ctx, GLOBAL_ARRAY_NAME); 44 | let mut arr = (*arr_rc).borrow_mut(); 45 | let name = String::from("isArray"); 46 | arr.property.insert(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, 1f64, array_static_is_array) }); 47 | 48 | if let Some(prop)= &arr.prototype { 49 | let prototype_rc = Rc::clone(prop); 50 | let mut prototype = prototype_rc.borrow_mut(); 51 | prototype.define_builtin_function_property(ctx, String::from("concat"), 0, array_concat); 52 | prototype.define_builtin_function_property(ctx, String::from("join"), 1, array_join); 53 | prototype.define_builtin_function_property(ctx, String::from("push"), 1, array_push); 54 | prototype.define_builtin_function_property(ctx, String::from("toString"), 0, array_to_string); 55 | } 56 | } 57 | 58 | // Array.isArray 59 | fn array_static_is_array(call_ctx: &mut CallContext, _: Vec) -> JSIResult { 60 | match &call_ctx.this { 61 | Value::Array(array) => { 62 | let arrborrowed = array.borrow(); 63 | if let ClassType::Array = arrborrowed.class_type { 64 | Ok(Value::Boolean(true)) 65 | } else { 66 | Ok(Value::Boolean(false)) 67 | } 68 | }, 69 | _ => Ok(Value::Boolean(false)), 70 | } 71 | } 72 | 73 | // Array.prototype.concat 74 | fn array_concat(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 75 | let new_arr_info = clone_array_object(call_ctx)?; 76 | let mut len = new_arr_info.1; 77 | let new_array = new_arr_info.0; 78 | let new_array_clone = Rc::clone(&new_array); 79 | let mut new_array_borrowed = new_array_clone.borrow_mut(); 80 | for arg in args.iter() { 81 | match arg { 82 | Value::Array(arr) => { 83 | let iter = |_: i32, value: &Value, _: &mut Context| { 84 | new_array_borrowed.define_property(format!("{}", len), Property { enumerable: true, value: value.clone() }); 85 | len += 1; 86 | }; 87 | array_iter_mut(call_ctx, &arr, iter); 88 | }, 89 | _ => { 90 | new_array_borrowed.define_property(format!("{}", len), Property { enumerable: true, value: arg.clone() }); 91 | len += 1; 92 | } 93 | } 94 | } 95 | new_array_borrowed.define_property(String::from("length"), Property { enumerable: false, value: Value::Number(len.clone() as f64) }); 96 | Ok(Value::Array(new_array)) 97 | } 98 | 99 | 100 | 101 | 102 | // Array.prototype.join 103 | fn array_join(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 104 | let mut join = ","; 105 | if args.len() > 0 { 106 | if let Value::String(join_param) = &args[0] { 107 | join = join_param; 108 | } 109 | } 110 | let mut string_list: Vec = vec![]; 111 | let iter = |_: i32, value: &Value, ctx: &mut Context| { 112 | string_list.push(value.to_string(ctx)); 113 | }; 114 | let this_array_obj = match &call_ctx.this { 115 | Value::Array(array) => { 116 | Some(Rc::clone(array)) 117 | }, 118 | _ => None, 119 | }; 120 | if let Some(this_ref) = this_array_obj { 121 | array_iter_mut(call_ctx, &this_ref, iter); 122 | } 123 | 124 | Ok(Value::String(string_list.join(join))) 125 | } 126 | 127 | 128 | 129 | // Array.prototype.push 130 | fn array_push(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 131 | 132 | let this_array_obj = get_array_object_from_this(&call_ctx.this); 133 | 134 | if let Some(this_ref) = this_array_obj { 135 | let mut this = this_ref.borrow_mut(); 136 | let len_opt = this.get_property_value(String::from("length")).to_number(call_ctx.ctx); 137 | if let Some(len) = len_opt { 138 | let mut len = len as usize; 139 | for value in args.iter() { 140 | this.define_property(len.to_string(), Property { enumerable: true, value: value.clone() }); 141 | len += 1 142 | } 143 | let new_length = Value::Number(len as f64); 144 | this.define_property(String::from("length"), Property { enumerable: false, value: new_length.clone() }); 145 | return Ok(new_length) 146 | } 147 | return Err(JSIError::new(JSIErrorType::RangeError, format!("Invalid array length"), 0, 0)) 148 | } 149 | return Ok(Value::Number(args.len() as f64)); 150 | } 151 | 152 | 153 | 154 | // Array.prototype.toString 155 | fn array_to_string(ctx: &mut CallContext, _: Vec) -> JSIResult { 156 | array_join(ctx, vec![]) 157 | } 158 | 159 | fn array_iter_mut(call_ctx: &mut CallContext, arr_rc: &Rc>, mut callback: F) { 160 | let arr = arr_rc.borrow_mut(); 161 | let len = arr.get_property_value(String::from("length")); 162 | if let Value::Number(len) = len { 163 | for index in 0..(len as i32) { 164 | (callback)(index, &arr.get_property_value(index.to_string()), call_ctx.ctx); 165 | } 166 | } 167 | } 168 | 169 | // 复制数组对象 170 | fn clone_array_object(call_ctx: &mut CallContext) -> JSIResult<(Rc>, i32)> { 171 | let new_arr = match create_array(call_ctx.ctx, 0) { 172 | Value::Array(arr) => Some(arr), 173 | _ => None 174 | }.unwrap(); 175 | let new_arr_rc = Rc::clone(&new_arr); 176 | let mut new_arr_borrowed = new_arr_rc.borrow_mut(); 177 | 178 | let mut length: i32 = 0; 179 | let this_array_obj = get_array_object_from_this(&call_ctx.this); 180 | if let Some(this_ref) = this_array_obj { 181 | let this = this_ref.borrow_mut(); 182 | let length_value = this.get_property_value(String::from("length")); 183 | match length_value { 184 | Value::Number(len) => { 185 | length = len as i32; 186 | }, 187 | _ => {}, 188 | }; 189 | for index in 0..(length as i32) { 190 | let value = this.get_property_value(index.to_string()); 191 | new_arr_borrowed.define_property(index.to_string(), Property { enumerable: true, value: value.clone() }); 192 | } 193 | } else { 194 | length = 1; 195 | new_arr_borrowed.define_property(String::from("0"), Property { enumerable: true, value: call_ctx.this.clone() }); 196 | } 197 | 198 | 199 | new_arr_borrowed.define_property(String::from("length"), Property { enumerable: false, value: Value::Number(length.clone() as f64) }); 200 | Ok((new_arr, length)) 201 | } 202 | 203 | pub fn create_list_from_array_list(call_ctx: &mut CallContext,value: &Value) -> JSIResult> { 204 | let mut list: Vec = vec![]; 205 | match value { 206 | Value::Boolean(_) | Value::Number(_) | Value::String(_) | Value::NAN => Err(JSIError::new(JSIErrorType::TypeError, format!("eateListFromArrayLike called on non-object 207 | "), 0, 0)), 208 | Value::Null | Value::Undefined => Ok(list), 209 | _ => { 210 | let obj = value.to_object(call_ctx.ctx); 211 | let obj_borrow = obj.borrow(); 212 | let len = obj_borrow.get_property_value(String::from("length")); 213 | if let Value::Number(len) = len { 214 | for index in 0..(len as i32) { 215 | let value = obj_borrow.get_property_value(index.to_string()); 216 | list.push(value); 217 | } 218 | } 219 | Ok(list) 220 | }, 221 | } 222 | } 223 | 224 | fn get_array_object_from_this<'a>(this_value: &'a Value) -> Option<&'a Rc>>{ 225 | match &this_value { 226 | Value::Array(array) | Value::Object(array) => { 227 | Some(array) 228 | }, 229 | _ => None, 230 | } 231 | } -------------------------------------------------------------------------------- /src/ast_node.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::{fmt, cell::RefCell, rc::Weak}; 3 | use crate::bytecode::ByteCode; 4 | use crate::context::{Context}; 5 | use crate::{ast_token::Token, value::Value, builtins::{object::Object}, error::JSIResult}; 6 | 7 | #[derive(Clone)] 8 | pub enum Statement { 9 | // A -> Z 10 | Block(BlockStatement), 11 | Break(BreakStatement), 12 | BuiltinFunction(BuiltinFunction), 13 | Class(ClassDeclaration), 14 | Continue(ContinueStatement), 15 | Expression(ExpressionStatement), 16 | For(ForStatement), 17 | Function(FunctionDeclaration), 18 | If(IfStatement), 19 | Label(LabeledStatement), 20 | Return(ReturnStatement), 21 | Switch(SwitchStatement), 22 | Throw(ThrowStatement), 23 | Try(TryCatchStatement), 24 | Unknown, // 未知 25 | Var(VariableDeclarationStatement), 26 | While(ForStatement), 27 | } 28 | 29 | impl PartialEq for Statement { 30 | fn eq(&self, other: &Statement) -> bool { 31 | match (self, other) { 32 | (Statement::Var(a), Statement::Var(b)) => *a == *b, 33 | (Statement::Function(a), Statement::Function(b)) => *a == *b, 34 | (Statement::Class(a), Statement::Class(b)) => *a == *b, 35 | (Statement::Block(a), Statement::Block(b)) => *a == *b, 36 | (Statement::Return(a), Statement::Return(b)) => *a == *b, 37 | (Statement::Expression(a), Statement::Expression(b)) => *a == *b, 38 | _ => false, 39 | } 40 | } 41 | } 42 | 43 | impl fmt::Debug for Statement { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | let stype = match self { 46 | Statement::Block(_) => { "block"}, 47 | Statement::Break(_) => { "break"}, 48 | Statement::BuiltinFunction(_) => { "builtin function"}, 49 | Statement::Class(_) => { "class"}, 50 | Statement::Continue(_) => { "continue"}, 51 | Statement::Expression(_) => { "expression"}, 52 | Statement::For(_) => { "for"}, 53 | Statement::Function(_) => { "function"}, 54 | Statement::If(_) => { "if"}, 55 | Statement::Label(_) => { "label"}, 56 | Statement::Return(_) => { "return"}, 57 | Statement::Switch(_) => { "switch"}, 58 | Statement::Try(_) => { "try"}, 59 | Statement::Var(_) => { "var"}, 60 | Statement::While(_) => { "while"}, 61 | _ => { 62 | "other" 63 | }, 64 | }; 65 | write!(f, "{}", stype) 66 | } 67 | } 68 | 69 | #[derive(Debug,Clone, PartialEq)] 70 | pub enum Expression { 71 | Var(VariableDeclaration), 72 | Assign(AssignExpression), 73 | Binary(BinaryExpression), 74 | Conditional(ConditionalExpression), 75 | PropertyAccess(PropertyAccessExpression), 76 | ElementAccess(ElementAccessExpression), 77 | Call(CallExpression), 78 | PrefixUnary(PrefixUnaryExpression), 79 | PostfixUnary(PostfixUnaryExpression), 80 | Group(GroupExpression), 81 | Identifier(IdentifierLiteral), 82 | Number(NumberLiteral), 83 | String(StringLiteral), 84 | Keyword(Keywords), 85 | Object(ObjectLiteral), 86 | Array(ArrayLiteral), 87 | Function(FunctionDeclaration), 88 | New(NewExpression), 89 | Sequence(SequenceExpression), 90 | TemplateLiteral(TemplateLiteralExpression), 91 | // {[a]: 12} 92 | ComputedPropertyName(ComputedPropertyName), 93 | // for class 94 | Class(ClassDeclaration), 95 | Constructor(FunctionDeclaration), 96 | ClassMethod(ClassMethodDeclaration), 97 | // Expression::Value 98 | Value(Box), 99 | Unknown, 100 | } 101 | impl Expression { 102 | // https://tc39.es/ecma262/multipage/syntax-directed-operations.html#sec-static-semantics-assignmenttargettype 103 | pub fn is_assignment_target_type(&self) -> bool { 104 | match self { 105 | Expression::Identifier(_) => true, 106 | Expression::PropertyAccess(_) => true, 107 | Expression::ElementAccess(_) => true, 108 | Expression::Group(group) => { 109 | return group.expression.is_assignment_target_type(); 110 | }, 111 | _ => false 112 | } 113 | } 114 | } 115 | 116 | #[derive(Debug,Clone, PartialEq)] 117 | pub enum Keywords { 118 | False, 119 | True, 120 | Null, 121 | Undefined, 122 | This, 123 | } 124 | 125 | impl Keywords { 126 | pub fn to_string(&self) -> String { 127 | match &self { 128 | Keywords::False => String::from("false"), 129 | Keywords::True => String::from("true"), 130 | Keywords::Null => String::from("null"), 131 | Keywords::Undefined => String::from("undefined"), 132 | Keywords::This => String::from("this"), 133 | } 134 | } 135 | } 136 | 137 | #[derive(Debug,Clone, PartialEq)] 138 | pub enum Declaration { 139 | Function(FunctionDeclaration) 140 | } 141 | 142 | #[derive(Debug, Clone, PartialEq)] 143 | pub enum VariableFlag { 144 | Var, 145 | Let, 146 | Const, 147 | } 148 | 149 | #[derive(Debug, Clone, PartialEq)] 150 | pub struct VariableDeclarationStatement { 151 | pub list: Vec, 152 | pub flag: VariableFlag, 153 | } 154 | 155 | #[derive(Debug, Clone, PartialEq)] 156 | pub struct IfStatement { 157 | pub condition: Expression, 158 | pub then_statement: Box, 159 | pub else_statement: Box, 160 | } 161 | 162 | 163 | #[derive(Debug, Clone, PartialEq)] 164 | pub struct SwitchStatement { 165 | pub condition: Expression, 166 | pub clauses: Vec, 167 | pub default_index: i32 168 | } 169 | 170 | #[derive(Debug, Clone, PartialEq)] 171 | pub struct CaseClause { 172 | pub condition: Option, 173 | pub statements: Vec 174 | } 175 | 176 | 177 | #[derive(Debug, Clone, PartialEq)] 178 | pub struct LabeledStatement { 179 | pub label: IdentifierLiteral, 180 | pub statement: Box, 181 | } 182 | 183 | 184 | #[derive(Debug, Clone, PartialEq)] 185 | pub struct ForStatement { 186 | pub initializer: Box, 187 | pub condition: Expression, 188 | pub incrementor: Expression, 189 | pub statement: Box, 190 | pub post_judgment: bool, 191 | } 192 | 193 | 194 | #[derive(Debug, Clone, PartialEq)] 195 | pub struct ThrowStatement { 196 | pub expression: Expression 197 | } 198 | 199 | #[derive(Debug, Clone, PartialEq)] 200 | pub struct TryCatchStatement { 201 | pub body: BlockStatement, 202 | pub catch: Option, 203 | pub finally: Option, 204 | } 205 | 206 | #[derive(Debug, Clone, PartialEq)] 207 | pub struct CatchClause { 208 | pub declaration: Option, 209 | pub body: BlockStatement 210 | } 211 | 212 | 213 | 214 | #[derive(Debug, Clone, PartialEq)] 215 | pub struct FunctionDeclaration { 216 | pub is_anonymous: bool, 217 | pub is_arrow: bool, 218 | pub name: IdentifierLiteral, 219 | pub parameters: Vec, 220 | pub body: BlockStatement, 221 | pub declarations: Vec, 222 | pub bytecode: Vec, 223 | } 224 | 225 | // ES2015 Computed Property Name 226 | #[derive(Debug, Clone, PartialEq)] 227 | pub struct ComputedPropertyName { 228 | pub expression: Box 229 | } 230 | 231 | #[derive(Debug, Clone, PartialEq)] 232 | pub struct NewExpression { 233 | pub expression: Box, 234 | pub arguments: Vec, 235 | } 236 | 237 | #[derive(Debug, Clone, PartialEq)] 238 | pub struct ClassDeclaration { 239 | pub name: IdentifierLiteral, 240 | pub members: Vec, 241 | // 继承 242 | pub heritage: Option> 243 | } 244 | 245 | #[derive(Debug, Clone, PartialEq)] 246 | pub struct ClassMethodDeclaration { 247 | pub name: IdentifierLiteral, 248 | pub modifiers: Vec, 249 | pub method: Box, 250 | } 251 | 252 | #[derive(Debug, Clone, PartialEq)] 253 | pub struct BlockStatement { 254 | pub statements: Vec 255 | } 256 | 257 | #[derive(Debug, Clone, PartialEq)] 258 | pub struct ReturnStatement { 259 | pub expression: Expression 260 | } 261 | 262 | #[derive(Debug, Clone, PartialEq)] 263 | pub struct BreakStatement { 264 | pub label: Option 265 | } 266 | 267 | #[derive(Debug, Clone, PartialEq)] 268 | pub struct ContinueStatement { 269 | pub label: Option 270 | } 271 | 272 | #[derive(Debug, Clone, PartialEq)] 273 | pub struct Parameter { 274 | pub name: IdentifierLiteral, 275 | pub initializer: Box 276 | } 277 | 278 | #[derive(Debug, Clone, PartialEq)] 279 | pub struct ExpressionStatement { 280 | pub expression: Expression 281 | } 282 | #[derive(Debug, Clone, PartialEq)] 283 | pub struct AssignExpression { 284 | pub left: Box, 285 | pub operator: Token, 286 | pub right: Box, 287 | } 288 | 289 | // 条件表达式 290 | #[derive(Debug, Clone, PartialEq)] 291 | pub struct ConditionalExpression { 292 | pub condition: Box, 293 | pub when_true: Box, 294 | pub when_false: Box, 295 | } 296 | 297 | // . 表达式 298 | #[derive(Debug, Clone, PartialEq)] 299 | pub struct BinaryExpression { 300 | pub left: Box, 301 | pub operator: Token, 302 | pub right: Box, 303 | } 304 | // 方法调用表达式 305 | #[derive(Debug, Clone, PartialEq)] 306 | pub struct CallExpression { 307 | pub expression: Box, 308 | pub arguments: Vec, 309 | } 310 | 311 | // . 属性访问表达式 312 | #[derive(Debug, Clone, PartialEq)] 313 | pub struct PropertyAccessExpression { 314 | pub expression: Box, 315 | pub name: IdentifierLiteral, 316 | } 317 | 318 | 319 | // [] 属性访问表达式 320 | #[derive(Debug, Clone, PartialEq)] 321 | pub struct ElementAccessExpression { 322 | pub expression: Box, 323 | pub argument: Box, 324 | } 325 | 326 | 327 | // 前置一元运算符表达式 328 | #[derive(Debug, Clone, PartialEq)] 329 | pub struct PrefixUnaryExpression { 330 | pub operand: Box, 331 | pub operator: Token, 332 | } 333 | 334 | // 后置一元运算符表达式 ++ -- 335 | #[derive(Debug, Clone, PartialEq)] 336 | pub struct PostfixUnaryExpression { 337 | pub operand: Box, 338 | pub operator: Token, 339 | } 340 | 341 | // 字符串模板表达式 342 | #[derive(Debug, Clone, PartialEq)] 343 | pub struct SequenceExpression { 344 | pub expressions: Vec 345 | } 346 | 347 | // 字符串模板表达式 348 | #[derive(Debug, Clone, PartialEq)] 349 | pub struct TemplateLiteralExpression { 350 | pub spans: Vec 351 | } 352 | 353 | #[derive(Debug, Clone, PartialEq)] 354 | pub struct GroupExpression { 355 | pub expression: Box 356 | } 357 | 358 | #[derive(Debug, Clone, PartialEq)] 359 | pub struct IdentifierLiteral { 360 | pub literal: String, 361 | } 362 | 363 | #[derive(Debug, Clone, PartialEq)] 364 | pub struct NumberLiteral { 365 | pub literal: String, 366 | pub value: f64, 367 | } 368 | 369 | 370 | #[derive(Debug, Clone, PartialEq)] 371 | pub struct StringLiteral { 372 | pub literal: String, 373 | pub value: String 374 | } 375 | 376 | #[derive(Debug, Clone, PartialEq)] 377 | pub struct ObjectLiteral { 378 | pub properties: Vec 379 | } 380 | 381 | #[derive(Debug, Clone, PartialEq)] 382 | pub struct ArrayLiteral { 383 | pub elements: Vec 384 | } 385 | 386 | 387 | #[derive(Debug, Clone, PartialEq)] 388 | pub struct PropertyAssignment { 389 | pub name: Box, 390 | pub initializer: Box 391 | } 392 | 393 | #[derive(Debug, Clone, PartialEq)] 394 | pub struct VariableDeclaration { 395 | pub name: String, 396 | pub initializer: Box 397 | } 398 | 399 | 400 | 401 | pub type BuiltinFunction = fn(&mut CallContext, Vec) -> JSIResult; 402 | // 方法调用的上下文 403 | pub struct CallContext<'a> { 404 | // 全局对象,globalThis 405 | pub ctx: &'a mut Context, 406 | // 调用时的 this 407 | pub this: Value, 408 | // 引用,调用的发起方,比如 a.call(),reference 就是 a 409 | // 当直接调用 call() 的时候,refererce 是 None 410 | pub reference: Option>>, 411 | pub func_name: String, 412 | } 413 | 414 | impl <'a>CallContext<'a> { 415 | pub fn call_function(&mut self, function_define: Rc>, call_this: Option, reference: Option>>, arguments: Vec) -> JSIResult { 416 | self.ctx.call_function_object(function_define, call_this,reference, arguments) 417 | } 418 | } 419 | 420 | #[derive(Debug, Clone)] 421 | pub enum ClassType { 422 | Object, 423 | Array, 424 | Function, 425 | String, 426 | Boolean, 427 | Number, 428 | Null, 429 | // 430 | Promise, 431 | Error, 432 | } 433 | 434 | impl ClassType { 435 | pub fn to_string(&self) -> String { 436 | match self { 437 | Self::Object => String::from("Object"), 438 | Self::Array => String::from("Array"), 439 | Self::Function => String::from("Function"), 440 | Self::String => String::from("String"), 441 | Self::Boolean => String::from("Boolean"), 442 | Self::Number => String::from("Number"), 443 | Self::Null => String::from("Null"), 444 | Self::Promise => String::from("Promise"), 445 | Self::Error => String::from("Error"), 446 | } 447 | } 448 | } -------------------------------------------------------------------------------- /tests/test262_test.rs: -------------------------------------------------------------------------------- 1 | use std::{path::{Path, PathBuf}, env, fs::{self, File}, io::{Write, Read}, panic}; 2 | use std::fs::metadata; 3 | use std::collections::HashMap; 4 | use jsi::JSI; 5 | use serde::{Serialize, Deserialize}; 6 | use yaml_rust::{YamlLoader, Yaml}; 7 | 8 | #[derive(Clone)] 9 | struct Test262Dir { 10 | pub name: String, 11 | pub dir: String, 12 | pub cases: usize, 13 | pub passed: usize, 14 | pub result: Test262DirResult, 15 | } 16 | 17 | impl Test262Dir { 18 | pub fn new(name: String, dir: String) -> Test262Dir { 19 | return Test262Dir { 20 | name, 21 | dir, 22 | cases: 0, 23 | passed: 0, 24 | result: Test262DirResult::new(), 25 | } 26 | } 27 | pub fn run(&mut self, preload_code: &str, ignore_list: &Vec, only_list: &Vec) { 28 | let (dirs, files) = self.get_childs(ignore_list, only_list); 29 | self.cases += files.len(); 30 | for file in files.iter() { 31 | let mut passed = false; 32 | let result = panic::catch_unwind(|| { 33 | let mut jsi = JSI::new(); 34 | jsi.set_strict(!file.no_strict); 35 | // println!("run: {:?}", code); 36 | let result = jsi.run(format!("{}\n{}", preload_code, file.code)); 37 | if only_list.len() > 0 { 38 | println!("result: {:?}", result); 39 | } 40 | return result; 41 | }); 42 | if result.is_err() { 43 | println!("panic: {:?} {:?}", file.name, file.code); 44 | passed = false; 45 | } else { 46 | if let Ok(inner_result) = result { 47 | // println!("inner_result {:?}", inner_result); 48 | if let Err(jsi_error) = inner_result { 49 | if file.negative { 50 | let error = jsi_error.error_type.to_string(); 51 | if file.negative_type.len() > 0 && error != file.negative_type { 52 | println!("negative error type: {:?} {:?} {:?}", file.name, file.negative_type, error); 53 | passed = false; 54 | } else { 55 | passed = true; 56 | } 57 | } else { 58 | passed = false; 59 | } 60 | } else { 61 | passed = !file.negative; 62 | } 63 | } 64 | } 65 | if passed { 66 | self.passed += 1; 67 | } 68 | self.result.files.insert(file.name.clone(), passed); 69 | } 70 | for dirs in dirs.iter() { 71 | let mut dirs_info = dirs.clone(); 72 | dirs_info.run(preload_code, ignore_list, only_list); 73 | self.cases += dirs_info.cases; 74 | self.passed += dirs_info.passed; 75 | self.result.dirs.insert(dirs_info.name.clone(), dirs_info.result); 76 | } 77 | self.result.cases = self.cases; 78 | self.result.passed = self.passed; 79 | } 80 | 81 | fn get_childs(&self, ignore_list: &Vec, only_list: &Vec) -> (Vec, Vec) { 82 | let dir = make_dir(&self.dir); 83 | let paths = fs::read_dir(&dir).unwrap(); 84 | let names = paths.filter_map(|entry| { 85 | entry.ok().and_then(|e| 86 | e.path().file_name() 87 | .and_then(|n| n.to_str().map(|s| String::from(s))) 88 | ) 89 | }).collect::>(); 90 | let mut dirs: Vec = vec![]; 91 | let mut files: Vec = vec![]; 92 | for name in names.iter() { 93 | let abso_name = dir.join(&name); 94 | if ignore_list.contains(&abso_name) { 95 | continue; 96 | } 97 | let md = metadata(&abso_name).unwrap(); 98 | if md.is_dir() { 99 | dirs.push(Test262Dir::new(name.clone(), String::from(abso_name.to_str().unwrap()))) 100 | } else { 101 | if only_list.len() > 0 { 102 | let mut is_ignore = true; 103 | for only in only_list.iter() { 104 | if abso_name.starts_with(&only) { 105 | is_ignore = false 106 | } 107 | } 108 | if is_ignore { 109 | continue; 110 | } 111 | } 112 | if name.ends_with(".js") { 113 | files.push(Test262File::new(name.clone(), String::from(abso_name.to_str().unwrap()))) 114 | } 115 | } 116 | } 117 | return (dirs, files) 118 | } 119 | } 120 | 121 | 122 | 123 | #[derive(Clone)] 124 | struct Test262File { 125 | pub name: String, 126 | pub code: String, 127 | pub no_strict: bool, 128 | pub negative: bool, 129 | pub negative_type: String, 130 | 131 | } 132 | impl Test262File { 133 | pub fn new(name: String, path: String) -> Test262File { 134 | let mut file = File::open(&path).unwrap(); 135 | let mut code = String::new(); 136 | file.read_to_string(&mut code).unwrap(); 137 | let config = Test262File::parse(&code); 138 | return Test262File { 139 | name, 140 | code, 141 | no_strict: config.0, 142 | negative: config.1, 143 | negative_type: config.2 144 | } 145 | } 146 | 147 | pub fn parse(code: &String) -> (bool, bool, String) { 148 | let mut no_strict = false; 149 | let mut negative = false; 150 | let mut negative_type = String::from(""); 151 | let start = code.find("/*---"); 152 | if let Some(start) = start { 153 | let end = code.find("---*/"); 154 | if let Some(end) = end { 155 | let config = &code[start + 5..end]; 156 | let docs = YamlLoader::load_from_str(config); 157 | if let Ok(docs) = docs { 158 | 159 | if let Yaml::Array(arr) = &docs[0]["flags"] { 160 | for flag in arr.iter() { 161 | if let Some(str) = flag.as_str() { 162 | match str { 163 | "noStrict" => { 164 | no_strict = true; 165 | }, 166 | _ => {} 167 | } 168 | } 169 | } 170 | } 171 | 172 | if let Yaml::BadValue = docs[0]["negative"] { 173 | 174 | } else { 175 | negative = true; 176 | let negative_type_value = docs[0]["negative"]["type"].as_str(); 177 | if let Some(negative_type_item) = negative_type_value { 178 | negative_type = String::from(negative_type_item); 179 | } 180 | } 181 | } 182 | } 183 | } 184 | return (no_strict, negative, negative_type); 185 | } 186 | } 187 | 188 | #[derive(Clone,Serialize, Deserialize, Debug)] 189 | struct Test262DirResult { 190 | pub cases: usize, 191 | pub passed: usize, 192 | pub dirs: HashMap, 193 | pub files: HashMap, 194 | } 195 | impl Test262DirResult { 196 | pub fn new() -> Test262DirResult { 197 | return Test262DirResult { 198 | cases: 0, 199 | passed: 0, 200 | dirs: HashMap::new(), 201 | files: HashMap::new(), 202 | } 203 | } 204 | } 205 | 206 | 207 | fn load_harness(path: &str) -> String { 208 | let mut file = File::open(format!("test262/{}", path)).unwrap(); 209 | let mut code = String::new(); 210 | file.read_to_string(&mut code).unwrap(); 211 | return code; 212 | } 213 | 214 | fn make_dir(dir: &String) -> PathBuf { 215 | Path::new( 216 | &env::current_dir().unwrap() 217 | ).join(dir) 218 | } 219 | 220 | #[test] 221 | fn test_all_262() { 222 | let preload_list = vec![ 223 | load_harness("harness/assert.js"), 224 | load_harness("harness/sta.js"), 225 | load_harness("harness/compareArray.js"), 226 | ]; 227 | let prelaod = preload_list.join("\n"); 228 | let ignore_list: Vec =vec![ 229 | make_dir(&String::from("test262/test/annexB")), 230 | make_dir(&String::from("test262/test/intl402")), 231 | ]; 232 | let only_list: Vec =vec![ 233 | // make_dir(&String::from("test262/test/built-ins/String/prototype/charAt/pos-coerce-err.js")), 234 | ]; 235 | let mut test262 = Test262Dir::new(String::from("base"), String::from("test262/test")); 236 | test262.run(prelaod.as_str(), &ignore_list, &only_list); 237 | let serialized_result = serde_json::to_string(&test262.result).unwrap(); 238 | let file_name = "./tests/262_result.json"; 239 | let mut file = File::create(file_name).unwrap(); 240 | file.write_all(serialized_result.as_bytes()).unwrap(); 241 | println!("result: passed {:?} / total {:?} at {}", test262.passed, test262.cases, file_name); 242 | if only_list.len() > 0 { 243 | return; 244 | } 245 | let old_file_name = "./tests/262_result_data.json"; 246 | let old_exists = Path::new(old_file_name).exists(); 247 | if old_exists { 248 | let mut diff = ResultDiff { 249 | passed: vec![], 250 | failed: vec![], 251 | add: vec![], 252 | remove: vec![], 253 | }; 254 | let json_old = read_json(old_file_name); 255 | let json_new = read_json(file_name); 256 | check_diff(&mut diff, String::from("test262/test"), &json_old, &json_new); 257 | let serialized_result = serde_json::to_string_pretty(&diff).unwrap(); 258 | let diff_result_file_name = "./tests/262_result_diff.json"; 259 | let mut file = File::create(diff_result_file_name).unwrap(); 260 | file.write_all(serialized_result.as_bytes()).unwrap(); 261 | println!("result diff: passed {:?} / failed {:?} / add {:?} / remove {:?} at {}", diff.passed.len(), diff.failed.len(), diff.add.len(), diff.remove.len(), diff_result_file_name); 262 | } 263 | 264 | } 265 | 266 | fn read_json(file_path: &str) -> serde_json::Value { 267 | let file = File::open(file_path).unwrap(); 268 | serde_json::from_reader(file).unwrap() 269 | } 270 | 271 | #[derive(Debug, Serialize)] 272 | struct ResultDiff { 273 | passed: Vec, 274 | failed: Vec, 275 | add: Vec, 276 | remove: Vec, 277 | } 278 | 279 | fn check_diff(diff: &mut ResultDiff, path: String, old: &serde_json::Value, new: &serde_json::Value) { 280 | let old_dirs = old.get("dirs").unwrap(); 281 | let new_dirs = new.get("dirs").unwrap(); 282 | let old_dir_list = old_dirs.as_object().unwrap(); 283 | let new_dir_list = new_dirs.as_object().unwrap(); 284 | for old_dir in old_dir_list.iter() { 285 | let dir_name = old_dir.0; 286 | let new_dir = new_dir_list.get(dir_name); 287 | let dir_path = format!("{}/{}", path, dir_name); 288 | if let Some(new_dir_value) = new_dir { 289 | check_diff(diff, dir_path, old_dir.1, new_dir_value); 290 | } else { 291 | // old exists but new not exists 292 | diff.remove.push(dir_path); 293 | } 294 | } 295 | 296 | for new_dir in new_dir_list.iter() { 297 | let dir_name = new_dir.0; 298 | let old_dir = old_dir_list.get(dir_name); 299 | let dir_path = format!("{}/{}", path, dir_name); 300 | if let None = old_dir { 301 | diff.add.push(dir_path); 302 | } 303 | } 304 | 305 | let old_files = old.get("files").unwrap(); 306 | let new_files = new.get("files").unwrap(); 307 | let old_files_list = old_files.as_object().unwrap(); 308 | let new_files_list = new_files.as_object().unwrap(); 309 | 310 | for old_file in old_files_list.iter() { 311 | let file_name = old_file.0; 312 | let new_file = new_files_list.get(file_name); 313 | let file_path = format!("{}/{}", path, file_name); 314 | if let Some(new_file_value) = new_file { 315 | let old_value_bool = old_file.1.as_bool().unwrap(); 316 | let new_value_bool = new_file_value.as_bool().unwrap(); 317 | if old_value_bool && !new_value_bool { 318 | diff.failed.push(file_path); 319 | } else if !old_value_bool && new_value_bool { 320 | diff.passed.push(file_path); 321 | } 322 | } else { 323 | // old exists but new not exists 324 | diff.remove.push(file_path); 325 | } 326 | } 327 | 328 | for new_file in new_files_list.iter() { 329 | let file_name = new_file.0; 330 | let old_file = old_files_list.get(file_name); 331 | let file_path = format!("{}/{}.js", path, file_name); 332 | if let None = old_file { 333 | diff.add.push(file_path); 334 | } 335 | } 336 | 337 | } -------------------------------------------------------------------------------- /src/builtins/object.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::BorrowMut; 2 | use std::cell::{RefCell}; 3 | use std::collections::HashMap; 4 | use std::rc::{Rc, Weak}; 5 | use crate::context::{Context}; 6 | use super::array::create_array; 7 | use std::sync::atomic::{AtomicUsize, Ordering}; 8 | // use super::array::new_array; 9 | use super::function::builtin_function; 10 | use super::global::{get_global_object, get_global_object_prototype_by_name, get_global_object_by_name}; 11 | use crate::ast_node::{Statement, CallContext, ClassType, BuiltinFunction}; 12 | use crate::constants::{GLOBAL_OBJECT_NAME, PROTO_PROPERTY_NAME}; 13 | use crate::error::{JSIResult, JSIError}; 14 | use crate::value::{Value, INSTANTIATE_OBJECT_METHOD_NAME}; 15 | 16 | static OBJECT_ID_COUNTER: AtomicUsize = AtomicUsize::new(0); 17 | 18 | #[derive(Debug,Clone)] 19 | // 对象 20 | pub struct Object { 21 | // 值类型,用作类型检测,如 Array.isArray 22 | pub class_type: ClassType, 23 | // 静态属性,比如 Object.keys 24 | pub property: HashMap, 25 | // 属性列表,对象的属性列表需要次序 26 | pub property_list: Vec, 27 | // 内置属性 28 | pub inner_property: HashMap, 29 | // 原型对象,用于查找原型链 30 | // 如果是构造方法对象,如 Object,则指向一个真实存在的 Object 31 | // 如:Array.prototype[key] = value 32 | // 如:Array.prototype.constructor = Array 33 | pub prototype: Option>>, 34 | // 如果是实例,则存在 constructor 值,指向构造方法 35 | // 如: arr.constructor = Array 36 | pub constructor: Option>>, 37 | // 对象的值 38 | value: Option>, 39 | // 对象 id 40 | id: usize, 41 | } 42 | 43 | impl Object { 44 | pub fn new(obj_type: ClassType, value: Option>) -> Object { 45 | OBJECT_ID_COUNTER.fetch_add(1, Ordering::SeqCst); 46 | let id = OBJECT_ID_COUNTER.load(Ordering::SeqCst); 47 | Object { 48 | class_type: obj_type, 49 | // 设置或添加的属性 50 | property: HashMap::new(), 51 | // 内置属性 52 | inner_property: HashMap::new(), 53 | property_list: vec![], 54 | prototype: None, 55 | constructor: None, 56 | value, 57 | id, 58 | } 59 | } 60 | 61 | // 强制拷贝 62 | // 用于 function.bind,但是不能拷贝 id 63 | pub fn force_copy(&self) -> Object { 64 | OBJECT_ID_COUNTER.fetch_add(1, Ordering::SeqCst); 65 | let id = OBJECT_ID_COUNTER.load(Ordering::SeqCst); 66 | Object { 67 | class_type: self.class_type.clone(), 68 | property: self.property.clone(), 69 | inner_property: self.inner_property.clone(), 70 | property_list: self.property_list.clone(), 71 | prototype: self.prototype.clone(), 72 | constructor: self.constructor.clone(), 73 | value: self.value.clone(), 74 | id 75 | } 76 | } 77 | 78 | pub fn set_value(&mut self, value: Option>) -> bool { 79 | self.value = value; 80 | return true; 81 | } 82 | 83 | pub fn get_initializer(&self) -> Option> { 84 | self.value.clone() 85 | } 86 | 87 | // // TODO: descriptor 88 | // pub fn define_property_by_value(&mut self, name: String, value: Value) -> bool { 89 | // self.define_property(name, Property { value, enumerable: false }); 90 | // return true; 91 | // } 92 | 93 | // TODO: descriptor 94 | pub fn define_property(&mut self, name: String, property: Property) -> bool { 95 | // 需要实现 descriptpor 96 | if !self.property_list.contains(&name) { 97 | self.property_list.push(name.clone()); 98 | } 99 | self.property.insert(name, property); 100 | return true; 101 | } 102 | 103 | pub fn get_id(&self) -> usize { 104 | return self.id; 105 | } 106 | 107 | // 定义内置方法属性 108 | pub fn define_builtin_function_property(&mut self, ctx: &mut Context, name: String, length: i32, fun: BuiltinFunction) -> bool { 109 | return self.define_property(name.clone(), Property { enumerable: true, value: builtin_function(ctx, name, length as f64, fun) }); 110 | } 111 | 112 | pub fn get_property_value(&self, name: String) -> Value { 113 | let prop = self.property.get(&name); 114 | if let Some(prop) = prop { 115 | prop.value.clone() 116 | } else { 117 | Value::Undefined 118 | } 119 | } 120 | 121 | pub fn get_inner_property_value(&self, name: String) -> Option { 122 | let prop = self.inner_property.get(&name); 123 | if let Some(prop) = prop { 124 | Some(prop.value.clone()) 125 | } else { 126 | None 127 | } 128 | } 129 | 130 | pub fn set_inner_property_value(&mut self, name: String, value: Value) { 131 | self.inner_property.insert(name, Property { enumerable: false, value }); 132 | } 133 | 134 | // 获取属性:从当前属性;从构造器的原型链上面寻找值 135 | pub fn get_value(&self, name: String) -> Value { 136 | if name == String::from("prototype") { 137 | if let Some(proto) = &self.prototype { 138 | return Value::Object(Rc::clone(proto)); 139 | } else { 140 | return Value::Undefined 141 | } 142 | } 143 | let prop = self.property.get(&name); 144 | if let Some(prop) = prop { 145 | return prop.value.clone() 146 | } else { 147 | // 先获取内置属性 148 | let prop = self.get_inner_property_value(name.clone()); 149 | if let Some(prop_value) = prop { 150 | return prop_value; 151 | } 152 | // 从 [[Prpperty]] 上获取原型链,从原型链上获取 153 | let proto = self.get_inner_property_value(PROTO_PROPERTY_NAME.to_string()); 154 | if let Some(proto_value) = &proto{ 155 | let obj = match proto_value { 156 | Value::RefObject(proto_obj)=> { 157 | proto_obj.upgrade() 158 | }, 159 | Value::Object(obj)=> { 160 | Some(Rc::clone(obj)) 161 | }, 162 | _ => None, 163 | }; 164 | 165 | if let Some(proto) = obj { 166 | let mut proto_rc = proto; 167 | loop { 168 | let proto_rc_clone = Rc::clone(&proto_rc); 169 | let proto = proto_rc_clone.borrow(); 170 | let prop = proto.property.get(&name); 171 | if let Some(property) = prop { 172 | return property.value.clone() 173 | } else { 174 | // 从 [[Prpperty]] 上获取 175 | let new_proto = proto.get_inner_property_value(PROTO_PROPERTY_NAME.to_string()); 176 | if let Some(new_proto) = new_proto { 177 | if let Value::RefObject(new_proto_obj) = new_proto { 178 | let new_proto_op = new_proto_obj.upgrade(); 179 | if let Some(proto) = new_proto_op { 180 | proto_rc = Rc::clone(&proto); 181 | continue; 182 | } 183 | } 184 | } 185 | } 186 | break; 187 | } 188 | } 189 | } 190 | } 191 | Value::Undefined 192 | } 193 | 194 | pub fn call(call_ctx: &mut CallContext, name: String, arguments:Vec) -> JSIResult { 195 | let fun = { 196 | let obj = call_ctx.this.to_object(call_ctx.ctx); 197 | let this_mut = (*obj).borrow_mut(); 198 | this_mut.get_value(name.clone()) 199 | }; 200 | if let Value::Function(function_define) = &fun { 201 | // 获取 function 定义 202 | let function_define_value = { 203 | let fun_clone = Rc::clone(function_define); 204 | let fun_obj = (*fun_clone).borrow_mut(); 205 | fun_obj.get_initializer().unwrap() 206 | }; 207 | 208 | // 内置方法 209 | if let Statement::BuiltinFunction(builtin_function) = function_define_value.as_ref() { 210 | // let mut ctx = CallContext{ global: ctx.global, this: Rc::downgrade(&function_define) }; 211 | return (builtin_function)(call_ctx, arguments); 212 | } 213 | if let Statement::Function(_) = function_define_value.as_ref() { 214 | let call_function_define = Rc::clone(function_define); 215 | return call_ctx.call_function(call_function_define, Some(Value::Function(Rc::clone(function_define))), None, arguments); 216 | } 217 | } 218 | Err(JSIError::new(crate::error::JSIErrorType::ReferenceError, format!("{:?} method not exists", name), 0, 0)) 219 | } 220 | } 221 | 222 | #[derive(Debug,Clone)] 223 | pub struct Property { 224 | // 是否可枚举 225 | pub enumerable: bool, 226 | pub value: Value, 227 | // TODO: 属性的描述符 descriptor writable ,是否可枚举等 228 | } 229 | 230 | // 实例化对象 231 | pub fn create_object(ctx: &mut Context, obj_type: ClassType, value: Option>) -> Rc> { 232 | let object = Rc::new(RefCell::new(Object::new(obj_type, value))); 233 | let object_clone = Rc::clone(&object); 234 | let mut object_mut = (*object_clone).borrow_mut(); 235 | // 绑定 obj.constructor = global.Object 236 | let global_object = get_global_object_by_name(ctx, GLOBAL_OBJECT_NAME); 237 | let global_prototype = get_global_object_prototype_by_name(ctx, GLOBAL_OBJECT_NAME); 238 | object_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&global_prototype))); 239 | let weak_rc = Rc::downgrade(&global_object); 240 | object_mut.constructor = Some(weak_rc); 241 | 242 | object 243 | } 244 | 245 | pub fn bind_global_object(ctx: &mut Context) { 246 | let obj_rc = get_global_object(ctx, GLOBAL_OBJECT_NAME.to_string()); 247 | 248 | // 绑定实例化方法 249 | let create_function = builtin_function(ctx, INSTANTIATE_OBJECT_METHOD_NAME.to_string(), 1f64, create); 250 | 251 | // prototype 252 | let object_create_fun = builtin_function(ctx, String::from("create"), 2f64, object_create); 253 | let object_has_own_fun = builtin_function(ctx, String::from("hasOwn"), 2f64, object_has_own); 254 | let object_keys_fun = builtin_function(ctx, String::from("keys"), 1f64, object_keys); 255 | let object_get_own_property_names_fun = builtin_function(ctx, String::from("getOwnPropertyNames"), 1f64, object_get_own_property_names); 256 | let object_get_prototype_of_fun = builtin_function(ctx, String::from("getPrototypeOf"), 1f64, object_get_prototype_of); 257 | let has_own_property_fun = builtin_function(ctx, String::from("hasOwnProperty"), 0f64, has_own_property); 258 | let object_to_string_fun = builtin_function(ctx, String::from("toString"), 0f64, to_string); 259 | 260 | 261 | let mut obj = (*obj_rc).borrow_mut(); 262 | obj.set_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string(), create_function); 263 | let property = obj.property.borrow_mut(); 264 | 265 | // Object.create 266 | let name = String::from("create"); 267 | property.insert(name.clone(), Property { enumerable: true, value: object_create_fun }); 268 | 269 | 270 | // Object.hasOwn 271 | let name = String::from("hasOwn"); 272 | property.insert(name.clone(), Property { enumerable: true, value: object_has_own_fun }); 273 | 274 | // Object.keys 275 | let name = String::from("keys"); 276 | property.insert(name.clone(), Property { enumerable: true, value: object_keys_fun }); 277 | 278 | // Object.getOwnPropertyNames 279 | let name = String::from("getOwnPropertyNames"); 280 | property.insert(name.clone(), Property { enumerable: true, value: object_get_own_property_names_fun }); 281 | 282 | // Object.getPrototypeOf 283 | let name = String::from("getPrototypeOf"); 284 | property.insert(name.clone(), Property { enumerable: true, value: object_get_prototype_of_fun }); 285 | 286 | if let Some(prop)= &obj.prototype { 287 | 288 | let prototype_rc = Rc::clone(prop); 289 | let mut prototype = (*prototype_rc).borrow_mut(); 290 | 291 | // 原型对象的原型 [[Property]] 为 null // Object.prototype.__proto__ == null 292 | prototype.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::Null); 293 | 294 | // Object.prototype.hasOwnProperty 295 | let name = String::from("hasOwnProperty"); 296 | prototype.define_property(name.clone(), Property { enumerable: true, value: has_own_property_fun }); 297 | 298 | // Object.prototype.toString 299 | let name = String::from("toString"); 300 | prototype.define_property(name.clone(), Property { enumerable: true, value: object_to_string_fun }); 301 | } 302 | 303 | } 304 | 305 | 306 | // Object.create() 307 | fn object_create(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 308 | if args.len() == 0 { 309 | // undefined 310 | return Err(JSIError::new(crate::error::JSIErrorType::TypeError, format!("Object prototype may only be an Object or null: undefined"), 0, 0)) 311 | } 312 | 313 | let new_object = create_object(call_ctx.ctx, ClassType::Object, None); 314 | 315 | let proto_arg = &args[0]; 316 | let object_clone = Rc::clone(&new_object); 317 | let mut object_mut = (*object_clone).borrow_mut(); 318 | if proto_arg.is_object() { 319 | // 绑定 [[Prototype]] = arg[0] 320 | let proto = proto_arg.to_object(call_ctx.ctx); 321 | object_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&proto))); 322 | } else if proto_arg.is_not_strict_null() { 323 | // null 324 | object_mut.inner_property.clear(); 325 | } 326 | // TODO: propertiesObject 327 | return Ok(Value::Object(new_object)); 328 | } 329 | 330 | // Object.hasOwn() 331 | fn object_has_own(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 332 | let mut obj = &Value::Undefined; 333 | if args.len() >= 1 { 334 | obj = &args[0] 335 | } 336 | let obj_rc= obj.to_object(call_ctx.ctx); 337 | if args.len() > 1 { 338 | let property_name = args[1].to_string(call_ctx.ctx); 339 | let obj = obj_rc.borrow(); 340 | return Ok(Value::Boolean(obj.property.contains_key(&property_name))); 341 | } 342 | 343 | return Ok(Value::Boolean(false)); 344 | } 345 | 346 | // Object.keys() 347 | fn object_keys(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 348 | let array = create_array(call_ctx.ctx, 0); 349 | let array_obj = match array { 350 | Value::Array(arr) => Some(arr), 351 | _ => None 352 | }.unwrap(); 353 | 354 | if args.len() < 1 { 355 | return Ok(Value::Array(array_obj)); 356 | } 357 | let obj_rc= args[0].to_object(call_ctx.ctx); 358 | let new_call_ctx = &mut CallContext { 359 | ctx: call_ctx.ctx, 360 | this: Value::Array(Rc::clone(&array_obj)), 361 | reference: None, 362 | func_name: String::from("push"), 363 | }; 364 | let obj = obj_rc.borrow(); 365 | for key in obj.property_list.iter() { 366 | let prop = obj.property.get(key); 367 | if let Some(property) = prop { 368 | if property.enumerable { 369 | Object::call(new_call_ctx, String::from("push"), vec![Value::String(key.clone())])?; 370 | } 371 | } 372 | } 373 | return Ok(Value::Array(array_obj)); 374 | } 375 | 376 | // Object.getOwnPropertyNames 377 | fn object_get_own_property_names(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 378 | let array = create_array(call_ctx.ctx, 0); 379 | let array_obj = match array { 380 | Value::Array(arr) => Some(arr), 381 | _ => None 382 | }.unwrap(); 383 | 384 | if args.len() < 1 { 385 | return Ok(Value::Array(array_obj)); 386 | } 387 | let obj_rc= args[0].to_object(call_ctx.ctx); 388 | let new_call_ctx = &mut CallContext { 389 | ctx: call_ctx.ctx, 390 | this: Value::Array(Rc::clone(&array_obj)), 391 | reference: None, 392 | func_name: String::from("push"), 393 | }; 394 | let obj = obj_rc.borrow(); 395 | for key in obj.property_list.iter() { 396 | Object::call(new_call_ctx, String::from("push"), vec![Value::String(key.clone())])?; 397 | } 398 | return Ok(Value::Array(array_obj)); 399 | } 400 | 401 | // Object.getPrototypeOf 402 | fn object_get_prototype_of(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 403 | let mut value = Value::Undefined; 404 | // item.creater 405 | if args.len() > 0 { 406 | value = args[0].clone(); 407 | } 408 | let obj = value.to_object(call_ctx.ctx); 409 | let create_method = obj.borrow().get_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string()); 410 | if let Some(_) = create_method{ 411 | // TODO: function return undefined 412 | return Ok(Value::Undefined); 413 | } 414 | 415 | let obj_rc = obj.borrow(); 416 | let inner_proto = obj_rc.get_inner_property_value(PROTO_PROPERTY_NAME.to_string()); 417 | if let Some(proto) = inner_proto { 418 | return Ok(proto); 419 | } 420 | // item.prototype 421 | Ok(obj_rc.get_value(String::from("prototype"))) 422 | } 423 | 424 | // Object.prototype.hasOwnProperty 425 | fn has_own_property(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 426 | let obj_rc = call_ctx.this.to_object(call_ctx.ctx); 427 | if args.len() > 0 { 428 | let property_name = args[0].to_string(call_ctx.ctx); 429 | let obj = obj_rc.borrow(); 430 | return Ok(Value::Boolean(obj.property.contains_key(&property_name))); 431 | } 432 | 433 | return Ok(Value::Boolean(false)); 434 | } 435 | 436 | // Object.prototype.toString 437 | fn to_string(call_ctx: &mut CallContext, _: Vec) -> JSIResult { 438 | let this_origin = call_ctx.this.to_object(call_ctx.ctx); 439 | let this = this_origin.borrow(); 440 | let mut obj_type : String = "[object ".to_owned(); 441 | obj_type.push_str(this.class_type.to_string().as_str()); 442 | obj_type.push(']'); 443 | Ok( Value::String(obj_type)) 444 | } 445 | 446 | // 实例化方法 new Object() 447 | fn create(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 448 | if args.len() > 0 { 449 | let obj = args[0].to_object_value(call_ctx.ctx); 450 | return Ok(obj) 451 | } 452 | Ok(Value::Object(create_object(call_ctx.ctx, ClassType::Object, None))) 453 | } -------------------------------------------------------------------------------- /tests/ast_lexer_test.rs: -------------------------------------------------------------------------------- 1 | use jsi::{JSI,ast_node::{Statement, ExpressionStatement, Expression, BinaryExpression, NumberLiteral, IdentifierLiteral, PostfixUnaryExpression, PrefixUnaryExpression, AssignExpression, GroupExpression, ConditionalExpression}, ast_token::Token}; 2 | 3 | struct TokenCheck { 4 | pub oper: String, 5 | pub token: Token 6 | } 7 | 8 | #[test] 9 | fn ast_lexer_binary_token() { 10 | let token_list = vec![ 11 | TokenCheck { oper: String::from("+"), token: Token::Plus }, 12 | TokenCheck { oper: String::from("-"), token: Token::Subtract }, 13 | TokenCheck { oper: String::from("*"), token: Token::Multiply }, 14 | TokenCheck { oper: String::from("/"), token: Token::Slash }, 15 | TokenCheck { oper: String::from("%"), token: Token::Remainder }, 16 | TokenCheck { oper: String::from("**"), token: Token::Exponentiation }, 17 | TokenCheck { oper: String::from("??"), token: Token::NullishCoalescing }, 18 | TokenCheck { oper: String::from("||"), token: Token::LogicalOr }, 19 | TokenCheck { oper: String::from("&&"), token: Token::LogicalAnd }, 20 | TokenCheck { oper: String::from("&"), token: Token::And }, 21 | TokenCheck { oper: String::from("|"), token: Token::Or }, 22 | TokenCheck { oper: String::from("^"), token: Token::ExclusiveOr }, 23 | TokenCheck { oper: String::from("=="), token: Token::Equal }, 24 | TokenCheck { oper: String::from("==="), token: Token::StrictEqual }, 25 | TokenCheck { oper: String::from("!="), token: Token::NotEqual }, 26 | TokenCheck { oper: String::from("!=="), token: Token::StrictNotEqual }, 27 | TokenCheck { oper: String::from("<"), token: Token::Less }, 28 | TokenCheck { oper: String::from("<="), token: Token::LessOrEqual }, 29 | TokenCheck { oper: String::from(">"), token: Token::Greater }, 30 | TokenCheck { oper: String::from(">="), token: Token::GreaterOrEqual }, 31 | TokenCheck { oper: String::from("in"), token: Token::In }, 32 | TokenCheck { oper: String::from("instanceof"), token: Token::Instanceof }, 33 | TokenCheck { oper: String::from("<<"), token: Token::ShiftLeft }, 34 | TokenCheck { oper: String::from(">>"), token: Token::ShiftRight }, 35 | TokenCheck { oper: String::from(">>>"), token: Token::UnsignedShiftRight }, 36 | ]; 37 | let mut jsi_vm = JSI::new(); 38 | for token in token_list.iter() { 39 | let mut code = String::from("1 "); 40 | code.push_str(token.oper.as_str()); 41 | code.push_str(" 1;"); 42 | let program = jsi_vm.parse(code).unwrap(); 43 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 44 | expression: Expression::Binary(BinaryExpression { 45 | left: Box::new(Expression::Number(NumberLiteral{ literal: String::from("1"), value: 1f64 })), 46 | operator: token.token.clone(), 47 | right: Box::new(Expression::Number(NumberLiteral{ literal: String::from("1"), value: 1f64 })), 48 | }) 49 | })]); 50 | } 51 | } 52 | 53 | 54 | #[test] 55 | fn ast_lexer_assign_token() { 56 | let token_list = vec![ 57 | TokenCheck { oper: String::from("="), token: Token::Assign }, 58 | TokenCheck { oper: String::from("+="), token: Token::AddAssign }, 59 | TokenCheck { oper: String::from("-="), token: Token::SubtractAssign }, 60 | TokenCheck { oper: String::from("**="), token: Token::ExponentiationAssign }, 61 | TokenCheck { oper: String::from("*="), token: Token::MultiplyAssign }, 62 | TokenCheck { oper: String::from("/="), token: Token::SlashAssign }, 63 | TokenCheck { oper: String::from("%="), token: Token::RemainderAssign }, 64 | TokenCheck { oper: String::from("<<="), token: Token::ShiftLeftAssign }, 65 | TokenCheck { oper: String::from(">>="), token: Token::ShiftRightAssign }, 66 | TokenCheck { oper: String::from(">>>="), token: Token::UnsignedShiftRightAssign }, 67 | TokenCheck { oper: String::from("&="), token: Token::AndAssign }, 68 | TokenCheck { oper: String::from("^="), token: Token::ExclusiveOrAssign }, 69 | TokenCheck { oper: String::from("|="), token: Token::OrAssign }, 70 | TokenCheck { oper: String::from("&&="), token: Token::LogicalAndAssign }, 71 | TokenCheck { oper: String::from("||="), token: Token::LogicalOrAssign }, 72 | TokenCheck { oper: String::from("??="), token: Token::NullishCoalescingAssign }, 73 | ]; 74 | let mut jsi_vm = JSI::new(); 75 | for token in token_list.iter() { 76 | let mut code = String::from("1"); 77 | code.push_str(token.oper.as_str()); 78 | code.push_str("1;"); 79 | let program = jsi_vm.parse(code).unwrap(); 80 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 81 | expression: Expression::Assign(AssignExpression { 82 | left: Box::new(Expression::Number(NumberLiteral{ literal: String::from("1"), value: 1f64 })), 83 | operator: token.token.clone(), 84 | right: Box::new(Expression::Number(NumberLiteral{ literal: String::from("1"), value: 1f64 })), 85 | }) 86 | })]); 87 | } 88 | } 89 | 90 | #[test] 91 | fn ast_lexer_prefix_unary_token() { 92 | let token_list = vec![ 93 | TokenCheck { oper: String::from("!"), token: Token::Not }, 94 | TokenCheck { oper: String::from("~"), token: Token::BitwiseNot }, 95 | TokenCheck { oper: String::from("+"), token: Token::Plus }, 96 | TokenCheck { oper: String::from("-"), token: Token::Subtract }, 97 | TokenCheck { oper: String::from("++"), token: Token::Increment }, 98 | TokenCheck { oper: String::from("--"), token: Token::Decrement }, 99 | TokenCheck { oper: String::from("typeof"), token: Token::Typeof }, 100 | TokenCheck { oper: String::from("void"), token: Token::Void }, 101 | TokenCheck { oper: String::from("delete"), token: Token::Delete }, 102 | TokenCheck { oper: String::from("await"), token: Token::Await }, 103 | ]; 104 | let mut jsi_vm = JSI::new(); 105 | for token in token_list.iter() { 106 | let mut code = String::from(""); 107 | code.push_str(token.oper.as_str()); 108 | code.push_str(" i;"); 109 | let program = jsi_vm.parse(code).unwrap(); 110 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 111 | expression: Expression::PrefixUnary(PrefixUnaryExpression { 112 | operand: Box::new(Expression::Identifier(IdentifierLiteral{ literal: String::from("i") })), 113 | operator: token.token.clone(), 114 | }) 115 | })]); 116 | } 117 | } 118 | 119 | 120 | #[test] 121 | fn ast_lexer_postfix_unary_token() { 122 | let token_list = vec![ 123 | TokenCheck { oper: String::from("++"), token: Token::Increment }, 124 | TokenCheck { oper: String::from("--"), token: Token::Decrement }, 125 | ]; 126 | let mut jsi_vm = JSI::new(); 127 | for token in token_list.iter() { 128 | let mut code = String::from("i"); 129 | code.push_str(token.oper.as_str()); 130 | code.push_str(";"); 131 | let program = jsi_vm.parse(code).unwrap(); 132 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 133 | expression: Expression::PostfixUnary(PostfixUnaryExpression { 134 | operand: Box::new(Expression::Identifier(IdentifierLiteral{ literal: String::from("i") })), 135 | operator: token.token.clone(), 136 | }) 137 | })]); 138 | } 139 | } 140 | 141 | #[test] 142 | // 右结合性 a [op] b [op] c == a [op] (b [op] c) 143 | fn ast_lexer_associativity_right_exponentiation() { 144 | let token_list = vec![ 145 | TokenCheck { oper: String::from("**"), token: Token::Exponentiation }, 146 | ]; 147 | let mut jsi_vm = JSI::new(); 148 | for token in token_list.iter() { 149 | let mut code = String::from("2 "); 150 | code.push_str(token.oper.as_str()); 151 | code.push_str(" 3 "); 152 | code.push_str(token.oper.as_str()); 153 | code.push_str(" 2;"); 154 | let program = jsi_vm.parse(code).unwrap(); 155 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 156 | expression: Expression::Binary(BinaryExpression { // 向右结合 2 op (3 op 2) 157 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2, 158 | operator: token.token.clone(), 159 | right: Box::new(Expression::Binary(BinaryExpression { // 3 op 2 160 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("3"), value: 3f64 })), // 3 161 | operator: token.token.clone(), 162 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 163 | })), 164 | }) 165 | })]); 166 | } 167 | 168 | let token_list_assign = vec![ 169 | TokenCheck { oper: String::from("="), token: Token::Assign }, 170 | TokenCheck { oper: String::from("+="), token: Token::AddAssign }, 171 | TokenCheck { oper: String::from("-="), token: Token::SubtractAssign }, 172 | TokenCheck { oper: String::from("**="), token: Token::ExponentiationAssign }, 173 | TokenCheck { oper: String::from("*="), token: Token::MultiplyAssign }, 174 | TokenCheck { oper: String::from("/="), token: Token::SlashAssign }, 175 | TokenCheck { oper: String::from("%="), token: Token::RemainderAssign }, 176 | TokenCheck { oper: String::from("<<="), token: Token::ShiftLeftAssign }, 177 | TokenCheck { oper: String::from(">>="), token: Token::ShiftRightAssign }, 178 | TokenCheck { oper: String::from(">>>="), token: Token::UnsignedShiftRightAssign }, 179 | TokenCheck { oper: String::from("&="), token: Token::AndAssign }, 180 | TokenCheck { oper: String::from("^="), token: Token::ExclusiveOrAssign }, 181 | TokenCheck { oper: String::from("|="), token: Token::OrAssign }, 182 | TokenCheck { oper: String::from("&&="), token: Token::LogicalAndAssign }, 183 | TokenCheck { oper: String::from("||="), token: Token::LogicalOrAssign }, 184 | TokenCheck { oper: String::from("??="), token: Token::NullishCoalescingAssign }, 185 | ]; 186 | for token in token_list_assign.iter() { 187 | let mut code = String::from("2 "); 188 | code.push_str(token.oper.as_str()); 189 | code.push_str(" 3 "); 190 | code.push_str(token.oper.as_str()); 191 | code.push_str(" 2;"); 192 | let program = jsi_vm.parse(code).unwrap(); 193 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 194 | expression: Expression::Assign(AssignExpression { // 向右结合 2 op (3 op 2) 195 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2, 196 | operator: token.token.clone(), 197 | right: Box::new(Expression::Assign(AssignExpression { // 3 op 2 198 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("3"), value: 3f64 })), // 3 199 | operator: token.token.clone(), 200 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 201 | })), 202 | }) 203 | })]); 204 | } 205 | // TODO: single oper 206 | // 三目运算符 207 | let program = jsi_vm.parse(String::from("1 ? 2 ? 3: 4: 5;")).unwrap(); 208 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 209 | expression: Expression::Conditional(ConditionalExpression { 210 | condition: Box::new(Expression::Number(NumberLiteral { literal: String::from("1"), value: 1f64 })), 211 | when_true: Box::new(Expression::Conditional(ConditionalExpression { 212 | condition: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), 213 | when_true: Box::new(Expression::Number(NumberLiteral { literal: String::from("3"), value: 3f64 })), 214 | when_false: Box::new(Expression::Number(NumberLiteral { literal: String::from("4"), value: 4f64 })) 215 | })), 216 | when_false: Box::new(Expression::Number(NumberLiteral { literal: String::from("5"), value: 5f64 })) 217 | }) 218 | })]); 219 | } 220 | 221 | 222 | 223 | #[test] 224 | // 左结合性 a [op] b [op] c == (a [op] b) [op] c 225 | fn ast_lexer_associativity_left_exponentiation() { 226 | let token_list = vec![ 227 | TokenCheck { oper: String::from("+"), token: Token::Plus }, 228 | TokenCheck { oper: String::from("-"), token: Token::Subtract }, 229 | TokenCheck { oper: String::from("*"), token: Token::Multiply }, 230 | TokenCheck { oper: String::from("/"), token: Token::Slash }, 231 | TokenCheck { oper: String::from("%"), token: Token::Remainder }, 232 | TokenCheck { oper: String::from("<<"), token: Token::ShiftLeft }, 233 | TokenCheck { oper: String::from(">>"), token: Token::ShiftRight }, 234 | TokenCheck { oper: String::from(">>>"), token: Token::UnsignedShiftRight }, 235 | TokenCheck { oper: String::from("<"), token: Token::Less }, 236 | TokenCheck { oper: String::from(">"), token: Token::Greater }, 237 | TokenCheck { oper: String::from("in"), token: Token::In }, 238 | TokenCheck { oper: String::from("instanceof"), token: Token::Instanceof }, 239 | TokenCheck { oper: String::from("&"), token: Token::And }, 240 | TokenCheck { oper: String::from("^"), token: Token::ExclusiveOr }, 241 | TokenCheck { oper: String::from("|"), token: Token::Or }, 242 | TokenCheck { oper: String::from("&&"), token: Token::LogicalAnd }, 243 | TokenCheck { oper: String::from("||"), token: Token::LogicalOr }, 244 | TokenCheck { oper: String::from("??"), token: Token::NullishCoalescing }, 245 | ]; 246 | let mut jsi_vm = JSI::new(); 247 | for token in token_list.iter() { 248 | let mut code = String::from("2 "); 249 | code.push_str(token.oper.as_str()); 250 | code.push_str(" 3 "); 251 | code.push_str(token.oper.as_str()); 252 | code.push_str(" 2;"); 253 | let program = jsi_vm.parse(code).unwrap(); 254 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 255 | expression: Expression::Binary(BinaryExpression { // 向左结合 (2 op 3) op 2 256 | left: Box::new(Expression::Binary(BinaryExpression { // 2 op 3 257 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 258 | operator: token.token.clone(), 259 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("3"), value: 3f64 })), // 3 260 | })), 261 | operator: token.token.clone(), 262 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2, 263 | }) 264 | })]); 265 | } 266 | // TODO: queal 267 | } 268 | 269 | #[test] 270 | fn ast_lexer_priority_between_exponentiation_shift() { 271 | let mut jsi_vm = JSI::new(); 272 | let program = jsi_vm.parse(String::from("2 ** 3 >> 1;")).unwrap(); 273 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 274 | expression: Expression::Binary(BinaryExpression { 275 | left: Box::new(Expression::Binary(BinaryExpression { // 2 ** 3 276 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 277 | operator: Token::Exponentiation, // ** 278 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("3"), value: 3f64 })), // 3 279 | })), 280 | operator: Token::ShiftRight, // >> 281 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("1"), value: 1f64 })), // 1, 282 | }) 283 | })]); 284 | } 285 | 286 | 287 | #[test] 288 | fn ast_lexer_complex() { 289 | let mut jsi_vm = JSI::new(); 290 | let program = jsi_vm.parse(String::from("(1 + 2) * 3 - 4 ** 2 >> (1 * 4 -3 + 1 == 2 ? 1 : 2);")).unwrap(); // return value is 4 291 | assert_eq!(program.body, vec![Statement::Expression(ExpressionStatement { 292 | expression: Expression::Binary(BinaryExpression { 293 | left: Box::new(Expression::Binary(BinaryExpression { // (1 + 2) * 3 - 4 ** 2 294 | left: Box::new(Expression::Binary(BinaryExpression { // (1 + 2) * 3 295 | left: Box::new(Expression::Group(GroupExpression { // (1 + 2) 296 | expression: Box::new(Expression::Binary(BinaryExpression { // 1 + 2 297 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("1"), value: 1f64 })), // 1 298 | operator: Token::Plus, // + 299 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 300 | })) 301 | })), 302 | operator: Token::Multiply, // * 303 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("3"), value: 3f64 })), // 3 304 | })), 305 | operator: Token::Subtract, // - 306 | right: Box::new(Expression::Binary(BinaryExpression { // 4 ** 2 307 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("4"), value: 4f64 })), // 4 308 | operator: Token::Exponentiation, // - 309 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 310 | })), 311 | })), 312 | operator: Token::ShiftRight, // >> 313 | right: Box::new(Expression::Group(GroupExpression { // (1 * 4 -3 + 1 == 2 ? 1 : 2) 314 | expression: Box::new(Expression::Conditional(ConditionalExpression { // 1 * 4 -3 + 1 == 2 ? 1 : 2 315 | condition: Box::new(Expression::Binary(BinaryExpression { // 1 * 4 -3 + 1 == 2 316 | left: Box::new(Expression::Binary(BinaryExpression { // 1 * 4 -3 + 1 317 | left: Box::new(Expression::Binary(BinaryExpression { // 1 * 4 -3 318 | left: Box::new(Expression::Binary(BinaryExpression { // 1 * 4 319 | left: Box::new(Expression::Number(NumberLiteral { literal: String::from("1"), value: 1f64 })), // 1 320 | operator: Token::Multiply, // * 321 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("4"), value: 4f64 })), // 4 322 | })), 323 | operator: Token::Subtract, // - 324 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("3"), value: 3f64 })), // 3 325 | })), 326 | operator: Token::Plus, // + 327 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("1"), value: 1f64 })), // 1 328 | })), 329 | operator: Token::Equal, // = 330 | right: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 331 | })), 332 | when_true: Box::new(Expression::Number(NumberLiteral { literal: String::from("1"), value: 1f64 })), // 1 333 | when_false: Box::new(Expression::Number(NumberLiteral { literal: String::from("2"), value: 2f64 })), // 2 334 | })) 335 | })), 336 | }) 337 | })]); 338 | } 339 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::{Weak, Rc}; 3 | use crate::ast_node::{Statement, IdentifierLiteral, ClassType, CallContext, Expression}; 4 | use crate::ast_token::Token; 5 | use crate::builtins::boolean::create_boolean; 6 | use crate::builtins::function::get_builtin_function_name; 7 | use crate::builtins::number::create_number; 8 | use crate::builtins::object::{Object, Property}; 9 | use crate::builtins::string::create_string; 10 | use crate::bytecode::ByteCode; 11 | use crate::context::{Context}; 12 | use crate::error::{JSIResult, JSIError, JSIErrorType}; 13 | use crate::scope::{self, Scope}; 14 | 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct ValueInfo { 18 | // 变量名 19 | pub name: Option, 20 | // 值 21 | pub value: Value, 22 | // 访问的路径 23 | pub access_path: String, 24 | // a.c,a 就是 reference,c 就是 name 变量名 25 | pub reference: Option, 26 | pub is_const: bool, 27 | } 28 | 29 | impl ValueInfo { 30 | pub fn set_value(&mut self, ctx: &mut Context,value: Value) -> JSIResult> { 31 | if self.name == None { 32 | return Err(JSIError::new(JSIErrorType::SyntaxError, format!("Invalid left-hand side in assignment"), 0, 0)); 33 | } 34 | if self.is_const { 35 | return Err(JSIError::new(JSIErrorType::TypeError, format!("Assignment to constant variable"), 0, 0)); 36 | } 37 | self.value = value.clone(); 38 | let name = match &self.name { 39 | Some(name) => name.clone(), 40 | _ => String::from(""), 41 | }; 42 | if let Some(reference) = &self.reference { 43 | match reference { 44 | Value::Scope(scope) => { 45 | let scope_rc = scope.upgrade(); 46 | if let Some(scope)= scope_rc { 47 | scope.borrow_mut().set_value( name.clone(), value, false); 48 | } 49 | Ok(None) 50 | }, 51 | _ => { 52 | let object = reference.to_object(ctx); 53 | object.borrow_mut().define_property( name.clone(), Property { 54 | enumerable: false, 55 | value: value, 56 | }); 57 | Ok(None) 58 | } 59 | } 60 | } else { 61 | // no reference set value 62 | if name.len() > 0 { 63 | ctx.cur_scope.borrow_mut().set_value(name.clone(), value, self.is_const); 64 | } 65 | return Ok(Some(name.clone())) 66 | } 67 | } 68 | } 69 | 70 | #[derive(Debug)] 71 | pub enum Value { 72 | // 5种基本数据类型 73 | String(String), 74 | Number(f64), 75 | Boolean(bool), 76 | Null, 77 | Undefined, 78 | // 4 种引用类型 79 | Object(Rc>), 80 | Function(Rc>), 81 | Array(Rc>), 82 | Promise(Rc>), 83 | // 3 种基础数据类型的包装对象 84 | StringObj(Rc>), 85 | NumberObj(Rc>), 86 | BooleanObj(Rc>), 87 | // 其他 88 | NAN, 89 | // 一般是指向自己等情况 90 | RefObject(Weak>), 91 | Scope(Weak>), 92 | // 中断 93 | Interrupt(Token,Expression), 94 | // bytecode 95 | ByteCode(Vec), 96 | } 97 | 98 | #[derive(PartialEq, Debug)] 99 | pub enum ValueType { 100 | // 5种基本数据类型 101 | String, 102 | Number, 103 | Boolean, 104 | Null, 105 | Undefined, 106 | // 4 种引用类型 107 | Object, 108 | Function, 109 | Array, 110 | Promise, 111 | // 其他 112 | NAN, 113 | } 114 | 115 | impl PartialEq for Value { 116 | // 仅做简单比较,不能处理 Number(1.2) == 1.2 这种,需要通过 value.is_equal_to 117 | fn eq(&self, other: &Value) -> bool { 118 | match (self, other) { 119 | (Value::String(a), Value::String(b)) => *a == *b, 120 | (Value::Number(a), Value::Number(b)) => *a == *b, 121 | (Value::Boolean(a), Value::Boolean(b)) => *a == *b, 122 | (Value::Null, Value::Null) | (Value::Undefined, Value::Undefined) => true, 123 | _ => false, 124 | } 125 | } 126 | } 127 | 128 | impl Clone for Value { 129 | fn clone(&self) -> Value { 130 | match self { 131 | Value::Object(rc_value) => { 132 | Value::Object(Rc::clone(rc_value)) 133 | }, 134 | Value::Array(rc_value) => { 135 | Value::Array(Rc::clone(rc_value)) 136 | }, 137 | Value::Function(rc_value) => { 138 | Value::Function(Rc::clone(rc_value)) 139 | }, 140 | Value::Promise(rc_value) => { 141 | Value::Promise(Rc::clone(rc_value)) 142 | }, 143 | Value::StringObj(rc_value) => { 144 | Value::StringObj(Rc::clone(rc_value)) 145 | }, 146 | Value::NumberObj(rc_value) => { 147 | Value::NumberObj(Rc::clone(rc_value)) 148 | }, 149 | Value::BooleanObj(rc_value) => { 150 | Value::BooleanObj(Rc::clone(rc_value)) 151 | }, 152 | Value::String(str) => Value::String(str.clone()), 153 | Value::Number(num) => Value::Number(*num), 154 | Value::Boolean(bool) => Value::Boolean(*bool), 155 | Value::Null => Value::Null, 156 | Value::Undefined => Value::Undefined, 157 | Value::RefObject(obj) => { 158 | return Value::RefObject(obj.clone()); 159 | }, 160 | Value::Scope(obj) => { 161 | return Value::Scope(obj.clone()); 162 | }, 163 | Value::Interrupt(token, expr) => { 164 | return Value::Interrupt(token.clone(), expr.clone()); 165 | }, 166 | Value::ByteCode(bytecode) => { 167 | return Value::ByteCode(bytecode.clone()); 168 | }, 169 | _ => Value::Undefined, 170 | } 171 | } 172 | } 173 | 174 | impl Value { 175 | pub fn is_string(&self) -> bool { 176 | if let Value::String(_) = self { 177 | return true 178 | } 179 | if let Value::StringObj(_) = self { 180 | return true 181 | } 182 | return false 183 | } 184 | 185 | pub fn is_number(&self) -> bool { 186 | if let Value::Number(_) = self { 187 | return true 188 | } 189 | if let Value::NumberObj(_) = self { 190 | return true 191 | } 192 | return false 193 | } 194 | 195 | pub fn is_infinity(&self) -> bool { 196 | if let Value::Number(number) = self { 197 | return *number == f64::INFINITY || *number == -f64::INFINITY; 198 | } 199 | return false 200 | } 201 | 202 | pub fn is_nan(&self) -> bool { 203 | if let Value::NAN = self { 204 | return true 205 | } 206 | return false 207 | } 208 | 209 | pub fn to_string(&self, ctx: &mut Context) -> String { 210 | 211 | let mut self_value = self; 212 | let primitive_value = self.to_primitive_value(ctx); 213 | if let Some(value) = &primitive_value { 214 | self_value = value; 215 | } 216 | match self_value { 217 | Value::String(str) => str.clone(), 218 | Value::Number(number) => number.to_string(), 219 | Value::Boolean(bool) => { 220 | if *bool { 221 | String::from("true") 222 | } else { 223 | String::from("false") 224 | } 225 | }, 226 | Value::NAN => String::from("NaN"), 227 | _ => { 228 | let call_this = match self { 229 | Value::Object(_) | Value::Array(_) | Value::Function(_) | Value::Promise(_) => Some(self.clone()), 230 | Value::RefObject(_ref) => { 231 | let origin = _ref.upgrade(); 232 | if let Some(obj)= &origin { 233 | let obj = Rc::clone(obj); 234 | Some(Value::Object(obj)) 235 | } else { 236 | None 237 | } 238 | } 239 | _=> None 240 | }; 241 | if let Some(this) = call_this { 242 | let call_ctx = &mut CallContext { 243 | ctx, 244 | this, 245 | reference: None, 246 | func_name: String::from("toString"), 247 | }; 248 | let value = Object::call(call_ctx, String::from("toString"), vec![]); 249 | if let Ok(value) = value { 250 | return value.to_string(ctx) 251 | } 252 | let value = Object::call(call_ctx, String::from("valueOf"), vec![]); 253 | if let Ok(value) = value { 254 | return value.to_string(ctx) 255 | } 256 | } 257 | return String::from(""); 258 | }, 259 | } 260 | } 261 | 262 | pub fn to_number(&self, ctx: &mut Context) -> Option { 263 | let mut self_value = self; 264 | let primitive_value = self.to_primitive_value(ctx); 265 | if let Some(value) = &primitive_value { 266 | self_value = value; 267 | } 268 | match self_value { 269 | Value::String(str) => { 270 | match str.parse::() { 271 | Ok(num) => Some(num), 272 | _ => None, 273 | } 274 | }, 275 | Value::Number(number) => Some(*number), 276 | Value::Boolean(bool) => { 277 | if *bool { 278 | Some(1f64) 279 | } else { 280 | Some(0f64) 281 | } 282 | }, 283 | _ => { 284 | // TODO: throw error 285 | None 286 | } 287 | } 288 | } 289 | pub fn to_boolean(&self, ctx: &mut Context) -> bool { 290 | let mut self_value = self; 291 | let primitive_value = self.to_primitive_value(ctx); 292 | if let Some(value) = &primitive_value { 293 | self_value = value; 294 | } 295 | match self_value { 296 | Value::Undefined | Value::Null => false, 297 | Value::String(str) => { 298 | return str.len() > 0; 299 | }, 300 | Value::Number(num) => { 301 | return num.to_owned() != 0f64; 302 | }, 303 | Value::Boolean(boolean) => { 304 | return boolean.to_owned(); 305 | }, 306 | _ => { 307 | true 308 | } 309 | } 310 | } 311 | 312 | // 实例化对象 313 | pub fn instantiate_object(&self, ctx: &mut Context, args: Vec) -> JSIResult { 314 | let rc_obj = self.to_weak_rc_object(); 315 | if let Some(wrc) = rc_obj { 316 | let rc = wrc.upgrade(); 317 | if let Some(obj)= &rc { 318 | let obj = Rc::clone(obj); 319 | let create_method = obj.borrow().get_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string()); 320 | if let Some(create_method) = &create_method { 321 | if let Value::Function(function_define) = create_method { 322 | // 获取 function 定义 323 | let func_name = get_builtin_function_name(ctx, function_define); 324 | let fun_clone = Rc::clone(function_define); 325 | let fun_obj = (*fun_clone).borrow_mut(); 326 | let function_define_value = fun_obj.get_initializer().unwrap(); 327 | // 内置方法 328 | if let Statement::BuiltinFunction(builtin_function) = function_define_value.as_ref() { 329 | let mut call_ctx = CallContext{ 330 | ctx, 331 | this: Value::Function(Rc::clone(function_define)), 332 | reference: None, 333 | func_name, 334 | }; 335 | return (builtin_function)(&mut call_ctx, args); 336 | } 337 | } 338 | } 339 | } 340 | } 341 | Err(JSIError::new(JSIErrorType::Unknown, format!("todo: unsupported global Type"), 0, 0)) 342 | } 343 | 344 | pub fn to_object(&self, ctx: &mut Context) -> Rc> { 345 | // TODO: Cannot convert undefined or null to object 346 | let obj_value = self.to_object_value(ctx); 347 | match obj_value { 348 | Value::StringObj(obj) => { 349 | return obj; 350 | }, 351 | Value::NumberObj(obj) => { 352 | return obj; 353 | }, 354 | Value::BooleanObj(obj) => { 355 | return obj; 356 | }, 357 | _ => { 358 | let rc_obj = self.to_weak_rc_object(); 359 | if let Some(wrc) = rc_obj { 360 | let rc = wrc.upgrade(); 361 | if let Some(obj)= &rc { 362 | return Rc::clone(obj); 363 | } 364 | } 365 | Rc::new(RefCell::new(Object::new(ClassType::Object,None))) 366 | } 367 | } 368 | 369 | } 370 | 371 | pub fn to_object_value(&self, ctx: &mut Context) -> Value { 372 | match self { 373 | Value::String(string) => { 374 | create_string(ctx, Value::String(string.to_owned())) 375 | }, 376 | Value::Number(number) => { 377 | create_number(ctx, Value::Number(number.to_owned())) 378 | }, 379 | Value::Boolean(boolean) => { 380 | create_boolean(ctx, Value::Boolean(boolean.to_owned())) 381 | }, 382 | _ => { 383 | self.clone() 384 | } 385 | } 386 | } 387 | 388 | pub fn is_primitive_value(&self) -> bool { 389 | match self { 390 | Value::String(_) => true, 391 | Value::StringObj(_) => true, 392 | Value::Number(_) => true, 393 | Value::NumberObj(_) => true, 394 | Value::Boolean(_) => true, 395 | Value::BooleanObj(_) => true, 396 | Value::Undefined => true, 397 | Value::Null => true, 398 | _ => false 399 | } 400 | } 401 | 402 | // check value is null of undefined 403 | pub fn is_not_strict_null(&self) -> bool { 404 | match self { 405 | Value::Undefined => true, 406 | Value::Null => true, 407 | _ => false 408 | } 409 | } 410 | 411 | // check value is Object 412 | pub fn is_object(&self) -> bool { 413 | match self { 414 | Value::StringObj(_) => true, 415 | Value::NumberObj(_) => true, 416 | Value::BooleanObj(_) => true, 417 | Value::Function(_) => true, 418 | Value::Array(_) => true, 419 | Value::Object(_) => true, 420 | Value::Promise(_) => true, 421 | _ => false 422 | } 423 | } 424 | 425 | // 到原始值,也就是 Boolean(false) => false 等 426 | pub fn to_primitive_value(&self, ctx: &mut Context) -> Option { 427 | let base_type_obj: Option<(ValueType, &Rc>)> = match self { 428 | Value::StringObj(obj) => Some((ValueType::String,obj)), 429 | Value::NumberObj(obj) => Some((ValueType::Number,obj)), 430 | Value::BooleanObj(obj) => Some((ValueType::Boolean,obj)), 431 | _ => None 432 | }; 433 | 434 | if let Some(type_obj) = base_type_obj { 435 | let mut call_ctx = CallContext{ 436 | ctx, 437 | this: self.clone(), 438 | reference: None, 439 | func_name: String::from("valueOf"), 440 | }; 441 | // 不会出错 442 | let value = Object::call(&mut call_ctx, String::from("valueOf"), vec![]).unwrap(); 443 | match type_obj.0 { 444 | ValueType::Number => { 445 | return Some(Value::Number(value.to_number(ctx).unwrap())); 446 | }, 447 | ValueType::Boolean => { 448 | return Some(Value::Boolean(value.to_boolean(ctx))); 449 | }, 450 | ValueType::String => { 451 | return Some(Value::String(value.to_string(ctx))); 452 | }, 453 | _ => {} 454 | } 455 | } 456 | 457 | return None; 458 | } 459 | 460 | pub fn to_value_info(&self) -> ValueInfo { 461 | return ValueInfo { name: None, value: self.clone(), access_path: String::from(""), reference: None, is_const: false } 462 | } 463 | 464 | 465 | pub fn to_weak_rc_object(&self) -> Option>> { 466 | match self { 467 | Value::Object(obj) => Some(Rc::downgrade(obj)), 468 | Value::Function(function) => Some(Rc::downgrade(function)), 469 | Value::Array(array) => Some(Rc::downgrade(array)), 470 | Value::Promise(promise) => Some(Rc::downgrade(promise)), 471 | Value::StringObj(obj) => Some(Rc::downgrade(obj)), 472 | Value::NumberObj(obj) => Some(Rc::downgrade(obj)), 473 | Value::BooleanObj(obj) => Some(Rc::downgrade(obj)), 474 | Value::RefObject(obj) => Some(obj.clone()), 475 | _ => None 476 | } 477 | } 478 | 479 | pub fn type_of(&self) -> String { 480 | match self { 481 | Value::Boolean(_) => String::from("boolean"), 482 | Value::Number(_) => String::from("number"), 483 | Value::NAN => String::from("number"), 484 | Value::String(_) => String::from("string"), 485 | Value::Undefined => String::from("undefined"), 486 | Value::Function(_) => String::from("function"), 487 | _ => String::from("object") 488 | } 489 | } 490 | 491 | fn get_value_type(&self) -> ValueType { 492 | match self { 493 | Value::Object(_) => ValueType::Object, 494 | Value::Function(_) => ValueType::Function, 495 | Value::Array(_) => ValueType::Array, 496 | Value::Promise(_) => ValueType::Object, 497 | Value::String(_) => ValueType::String, 498 | Value::StringObj(_) => ValueType::String, 499 | Value::Number(_) => ValueType::Number, 500 | Value::NumberObj(_) => ValueType::Number, 501 | Value::Boolean(_) => ValueType::Boolean, 502 | Value::BooleanObj(_) => ValueType::Boolean, 503 | Value::Null => ValueType::Null, 504 | Value::Undefined => ValueType::Undefined, 505 | Value::RefObject(refobj) => { 506 | let origin = refobj.upgrade(); 507 | if let Some(origin) = &origin { 508 | let origin_clone = Rc::clone(&origin); 509 | let origin_borrow = origin_clone.borrow(); 510 | return match &origin_borrow.class_type { 511 | // TODO: more type 512 | ClassType::Function => ValueType::Function, 513 | _ => ValueType::Object 514 | } 515 | } 516 | ValueType::Object 517 | }, 518 | _ => { 519 | // TODO: more 520 | ValueType::NAN 521 | }, 522 | } 523 | } 524 | 525 | pub fn is_equal_to(&self, ctx: &mut Context, other: &Value, is_check_type: bool) -> bool { 526 | let self_type = self.get_value_type(); 527 | let other_type = other.get_value_type(); 528 | let is_same_type = self_type == other_type; 529 | if is_check_type && !is_same_type { 530 | return false; 531 | } 532 | let mut self_value = self; 533 | let primitive_value = self.to_primitive_value(ctx); 534 | if let Some(value) = &primitive_value { 535 | self_value = value; 536 | } 537 | 538 | let mut other_value = other; 539 | let other_primitive_value = other.to_primitive_value(ctx); 540 | if let Some(value) = &other_primitive_value { 541 | other_value = value; 542 | } 543 | match (self_value, other_value) { 544 | (Value::String(a), Value::String(b)) => *a == *b, 545 | (Value::Number(a), Value::Number(b)) => *a == *b, 546 | (Value::Boolean(a), Value::Boolean(b)) => *a == *b, 547 | (Value::Null, Value::Null) | (Value::Undefined, Value::Undefined) => true, 548 | _ => { 549 | if self_value.is_primitive_value() && other_value.is_primitive_value() { 550 | return self_value.to_number(ctx) == other_value.to_number(ctx); 551 | } 552 | let left_obj_id = self_value.to_object(ctx).borrow().get_id(); 553 | let right_obj_id: Rc> = other_value.to_object(ctx); 554 | 555 | return left_obj_id == right_obj_id.borrow().get_id(); 556 | }, 557 | } 558 | } 559 | 560 | // 匿名方法,需要绑定name 561 | pub fn bind_name(&mut self, name: String) { 562 | match self { 563 | Value::Function(function) => { 564 | let mut function_define =function.borrow_mut(); 565 | let mut value = function_define.get_initializer().unwrap(); 566 | match *value { 567 | Statement::Function(func) => { 568 | if func.is_anonymous { 569 | let mut new_func = func.clone(); 570 | new_func.name = IdentifierLiteral { 571 | literal: name.clone() 572 | }; 573 | value = Box::new(Statement::Function(new_func)); 574 | function_define.set_value(Some(value)); 575 | function_define.define_property(String::from("name"), Property { enumerable: false, value: Value::String(String::from(name)) }); 576 | } 577 | }, 578 | _ => {} 579 | }; 580 | }, 581 | _ => {} 582 | } 583 | } 584 | 585 | } 586 | 587 | pub struct CallStatementOptions { 588 | pub label: Option, 589 | } 590 | 591 | pub const INSTANTIATE_OBJECT_METHOD_NAME: &str = "instantiate_object_method"; -------------------------------------------------------------------------------- /src/builtins/promise.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::E; 2 | use std::vec; 3 | use std::{rc::Rc}; 4 | use std::cell::{RefCell}; 5 | use super::object::Object; 6 | use crate::builtins::array::create_array; 7 | use crate::constants::{GLOBAL_PROMISE_NAME, PROTO_PROPERTY_NAME}; 8 | use crate::context::{Context}; 9 | use crate::error::{JSIError, JSIErrorType}; 10 | use crate::{value::{Value, INSTANTIATE_OBJECT_METHOD_NAME}, ast_node::{ClassType, CallContext}, error::JSIResult}; 11 | 12 | use super::global::{get_global_object_prototype_by_name, get_global_object_by_name}; 13 | use super::{object::{create_object, Property}, function::builtin_function}; 14 | 15 | pub const PROMISE_STATE: &str = "[[PromiseState]]"; 16 | pub const PROMISE_FULFILLED_VALUE: &str = "[[PromiseFulfilledValue]]"; 17 | pub const PROMISE_REJECTED_REASON: &str = "[[PromiseRejectedReason]]"; 18 | pub const PROMISE_FULFILLED_REACTIONS: &str = "[[PromiseFulfilledReactions]]"; 19 | pub const PROMISE_REJECTED_REACTIONS: &str = "[[PromiseRejectedReactions]]"; 20 | 21 | pub fn create_promise(ctx: &mut Context, init: Value) -> Value { 22 | let (promise, resolve_fn, reject_fn) = create_promise_helper(ctx); 23 | 24 | if let Value::Function(init_function) = init { 25 | let mut call_ctx = CallContext { 26 | ctx, 27 | // TODO: 28 | this: Value::Undefined, 29 | reference: None, 30 | func_name: String::from(""), 31 | }; 32 | call_ctx.call_function(init_function, None, None, vec![resolve_fn, reject_fn]).unwrap(); 33 | } 34 | 35 | Value::Promise(promise) 36 | } 37 | 38 | 39 | pub fn create_promise_helper(ctx: &mut Context) -> (Rc>, Value, Value) { 40 | let global_promise = get_global_object_by_name(ctx, GLOBAL_PROMISE_NAME); 41 | let promise = create_object(ctx, ClassType::Promise, None); 42 | { 43 | let promise_rc = Rc::clone(&promise); 44 | let mut promise_mut = promise_rc.borrow_mut(); 45 | promise_mut.constructor = Some(Rc::downgrade(&global_promise)); 46 | promise_mut.set_inner_property_value(PROMISE_STATE.to_string(), Value::String("pending".to_string())); 47 | 48 | // 创建数组,用来存放 pending 状态的 then 回调 49 | let fulfilled_callbacks = create_array(ctx, 0); 50 | promise_mut.set_inner_property_value(PROMISE_FULFILLED_REACTIONS.to_string(), fulfilled_callbacks); 51 | 52 | let rejected_callbacks = create_array(ctx, 0); 53 | promise_mut.set_inner_property_value(PROMISE_REJECTED_REACTIONS.to_string(), rejected_callbacks); 54 | 55 | 56 | // 绑定原型链 57 | let promise_proto = get_global_object_prototype_by_name(ctx, GLOBAL_PROMISE_NAME); 58 | promise_mut.set_inner_property_value(PROTO_PROPERTY_NAME.to_string(), Value::RefObject(Rc::downgrade(&promise_proto))); 59 | } 60 | 61 | let resolve_fn = builtin_function(ctx, "resolve".to_string(), 1f64, resolve); 62 | if let Value::Function(resolve_function) = &resolve_fn { 63 | let mut resolve_function_mut = resolve_function.borrow_mut(); 64 | resolve_function_mut.set_inner_property_value(String::from("promise"), Value::RefObject(Rc::downgrade(&promise))); 65 | } 66 | 67 | let reject_fn = builtin_function(ctx, "reject".to_string(), 1f64, reject); 68 | if let Value::Function(reject_function) = &reject_fn { 69 | let mut reject_function_mut = reject_function.borrow_mut(); 70 | reject_function_mut.set_inner_property_value(String::from("promise"), Value::RefObject(Rc::downgrade(&promise))); 71 | } 72 | 73 | return (promise, resolve_fn, reject_fn); 74 | } 75 | 76 | // 全局构造方法 77 | pub fn bind_global_promise(ctx: &mut Context) { 78 | let global_promise = get_global_object_by_name(ctx, GLOBAL_PROMISE_NAME); 79 | let mut global_promise_borrowed = (*global_promise).borrow_mut(); 80 | let create_function = builtin_function(ctx, INSTANTIATE_OBJECT_METHOD_NAME.to_string(), 1f64, create); 81 | global_promise_borrowed.set_inner_property_value(INSTANTIATE_OBJECT_METHOD_NAME.to_string(), create_function); 82 | 83 | let resolve_name = String::from("resolve"); 84 | global_promise_borrowed.property.insert(resolve_name.clone(), Property { enumerable: true, value: builtin_function(ctx, resolve_name, 1f64, resolve_static) }); 85 | 86 | let reject_name = String::from("reject"); 87 | global_promise_borrowed.property.insert(reject_name.clone(), Property { enumerable: true, value: builtin_function(ctx, reject_name, 1f64, reject_static) }); 88 | 89 | // 原型方法 then 90 | if let Some(props) = &global_promise_borrowed.prototype { 91 | let prototype_rc = Rc::clone(props); 92 | let mut prototype_mut = prototype_rc.borrow_mut(); 93 | prototype_mut.define_builtin_function_property(ctx, String::from("then"), 2, then); 94 | } 95 | } 96 | 97 | // 创建 Promise 实例 98 | fn create(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 99 | if args.len() == 0 { 100 | return Err(JSIError::new(JSIErrorType::TypeError, "Promise resolver is not a function".to_string(), 0, 0)); 101 | } 102 | let executor = &args[0]; 103 | if !matches!(executor, Value::Function(_)) { 104 | return Err(JSIError::new(JSIErrorType::TypeError, "Promise resolver is not a function".to_string(), 0, 0)); 105 | } 106 | 107 | Ok(create_promise(call_ctx.ctx, executor.to_owned())) 108 | } 109 | 110 | 111 | // resolve 方法 112 | fn resolve(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 113 | 114 | let resolve_fn = call_ctx.reference.as_ref().and_then(|r| r.upgrade()).expect("resolve rc error"); 115 | 116 | let promise = resolve_fn.borrow().get_inner_property_value(String::from("promise")).unwrap(); 117 | 118 | if let Value::RefObject(promise_rc_weak) = promise { 119 | if let Some(promise_rc) = promise_rc_weak.upgrade() { 120 | let mut promise_mut = promise_rc.borrow_mut(); 121 | // 设置 Promise 的状态为 fulfilled 122 | promise_mut.set_inner_property_value(PROMISE_STATE.to_string(), Value::String("fulfilled".to_string())); 123 | // 处理 resolve 的值 124 | let value = args.get(0).cloned().unwrap_or(Value::Undefined); 125 | promise_mut.set_inner_property_value(PROMISE_FULFILLED_VALUE.to_string(), value.clone()); 126 | 127 | // 执行所有 resolve 回调 128 | let all_reactions = promise_mut.get_inner_property_value(PROMISE_FULFILLED_REACTIONS.to_string()).unwrap(); 129 | exec_all_reactions(call_ctx.ctx, all_reactions, value, true); 130 | 131 | // 清空 fulfilled 和 rejected 回调数组 132 | promise_mut.set_inner_property_value(PROMISE_FULFILLED_REACTIONS.to_string(), Value::Undefined); 133 | promise_mut.set_inner_property_value(PROMISE_REJECTED_REACTIONS.to_string(), Value::Undefined); 134 | // 清空 parent 引用,防止循环引用 135 | promise_mut.set_inner_property_value(String::from("parent"), Value::Undefined); 136 | } 137 | } 138 | 139 | Ok(Value::Undefined) 140 | } 141 | 142 | // reject 方法 143 | fn reject(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 144 | let reject_fn = call_ctx.reference.as_ref().and_then(|r| r.upgrade()).expect("reject rc error"); 145 | let promise = reject_fn.borrow().get_inner_property_value(String::from("promise")).unwrap(); 146 | if let Value::RefObject(promise_rc_weak) = promise { 147 | let upgrade = promise_rc_weak.upgrade(); 148 | if let Some(promise_rc) = upgrade { 149 | let mut promise_mut = promise_rc.borrow_mut(); 150 | // 设置 Promise 的状态为 rejected 151 | promise_mut.set_inner_property_value(PROMISE_STATE.to_string(), Value::String("rejected".to_string())); 152 | // 处理 reject 的原因 153 | let reason = args.get(0).cloned().unwrap_or(Value::Undefined); 154 | promise_mut.set_inner_property_value(PROMISE_REJECTED_REASON.to_string(), reason.clone()); 155 | 156 | // 执行所有 reject 回调 157 | let all_reactions = promise_mut.get_inner_property_value(PROMISE_REJECTED_REACTIONS.to_string()).unwrap(); 158 | 159 | exec_all_reactions(call_ctx.ctx, all_reactions, reason, false); 160 | 161 | // 清空 fulfilled 和 rejected 回调数组 162 | promise_mut.set_inner_property_value(PROMISE_FULFILLED_REACTIONS.to_string(), Value::Undefined); 163 | promise_mut.set_inner_property_value(PROMISE_REJECTED_REACTIONS.to_string(), Value::Undefined); 164 | // 清空 parent 引用,防止循环引用 165 | promise_mut.set_inner_property_value(String::from("parent"), Value::Undefined); 166 | 167 | } 168 | } 169 | 170 | Ok(Value::Undefined) 171 | } 172 | 173 | fn then(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 174 | let on_fulfilled = args.get(0).cloned().unwrap_or(Value::Undefined); 175 | let on_rejected = args.get(1).cloned().unwrap_or(Value::Undefined); 176 | // then 的逻辑是返回信的 Promise 177 | // let result_promise = create_promise(call_ctx.ctx, Value::Undefined); 178 | // 获取 result 的 fulfilled 和 rejected 回调数组 179 | let (result_promise, new_resolve_fn, new_reject_fn) = create_promise_helper(call_ctx.ctx); 180 | 181 | let this_promise_obj = get_promise_object_from_this(&call_ctx.this).unwrap(); 182 | let this_promise = this_promise_obj.borrow_mut(); 183 | let state = this_promise.get_inner_property_value(PROMISE_STATE.to_string()).unwrap(); 184 | 185 | { 186 | // 把 this 和 result promise 关联起来 187 | let mut result_promise_mut = result_promise.borrow_mut(); 188 | result_promise_mut.set_inner_property_value(String::from("parent"), Value::Promise(Rc::clone(&this_promise_obj))); 189 | } 190 | 191 | if let Value::String(state_str) = state { 192 | if state_str == String::from("fulfilled") { 193 | let fulfilled_value = this_promise.get_inner_property_value(PROMISE_FULFILLED_VALUE.to_string()).unwrap_or(Value::Undefined); 194 | execute_promise_reaction(call_ctx.ctx, on_fulfilled, fulfilled_value, vec![new_resolve_fn, new_reject_fn], true); 195 | } else if state_str == String::from("rejected") { 196 | let rejected_reason = this_promise.get_inner_property_value(PROMISE_REJECTED_REASON.to_string()).unwrap_or(Value::Undefined); 197 | execute_promise_reaction(call_ctx.ctx, on_rejected, rejected_reason, vec![new_resolve_fn, new_reject_fn], false); 198 | } else if state_str == String::from("pending") { 199 | // 把 on_fulfilled 和 on_rejected 存储起来,并且执行 new_resolve_fn 和 new_reject_fn 200 | let fulfilled_reactions = this_promise.get_inner_property_value(PROMISE_FULFILLED_REACTIONS.to_string()).unwrap(); 201 | if let Value::Array(fulfill_array) = fulfilled_reactions { 202 | let mut fulfill_array_mut = fulfill_array.borrow_mut(); 203 | let length = fulfill_array_mut.get_inner_property_value("length".to_string()).unwrap_or(Value::Number(0f64)); 204 | if let Value::Number(len) = length { 205 | fulfill_array_mut.set_inner_property_value(len.to_string(), on_fulfilled.clone()); 206 | // ${len}_rsolve_fn 207 | fulfill_array_mut.set_inner_property_value(format!("{}_resolve_fn", len), new_resolve_fn.clone()); 208 | fulfill_array_mut.set_inner_property_value(format!("{}_reject_fn", len), new_reject_fn.clone()); 209 | fulfill_array_mut.set_inner_property_value("length".to_string(), Value::Number(len + 1f64)); 210 | } 211 | } 212 | let rejected_reactions = this_promise.get_inner_property_value(PROMISE_REJECTED_REACTIONS.to_string()).unwrap(); 213 | if let Value::Array(reject_array) = rejected_reactions { 214 | let mut reject_array_mut = reject_array.borrow_mut(); 215 | let length = reject_array_mut.get_inner_property_value("length".to_string()).unwrap_or(Value::Number(0f64)); 216 | if let Value::Number(len) = length { 217 | reject_array_mut.set_inner_property_value(len.to_string(), on_rejected.clone()); 218 | // ${len}_rsolve_fn 219 | reject_array_mut.set_inner_property_value(format!("{}_resolve_fn", len), new_resolve_fn.clone()); 220 | reject_array_mut.set_inner_property_value(format!("{}_reject_fn", len), new_reject_fn.clone()); 221 | reject_array_mut.set_inner_property_value("length".to_string(), Value::Number(len + 1f64)); 222 | } 223 | } 224 | } 225 | } 226 | 227 | return Ok(Value::Promise(result_promise)); 228 | } 229 | 230 | fn exec_all_reactions(ctx: &mut Context, reactions_array_obj: Value, value: Value, is_fulfilled: bool) { 231 | if let Value::Array(reactions_array) = reactions_array_obj { 232 | let reactions_borrowed = reactions_array.borrow(); 233 | if let Some(length_prop) = reactions_borrowed.get_inner_property_value("length".to_string()) { 234 | if let Value::Number(length) = length_prop { 235 | for i in 0..(length as usize) { 236 | let handler = reactions_borrowed.get_inner_property_value(i.to_string()).unwrap_or(Value::Undefined); 237 | let next_resolve = reactions_borrowed.get_inner_property_value(format!("{}_resolve_fn", i)).unwrap_or(Value::Undefined); 238 | let next_reject = reactions_borrowed.get_inner_property_value(format!("{}_reject_fn", i)).unwrap_or(Value::Undefined); 239 | execute_promise_reaction(ctx, handler, value.clone(), vec![next_resolve, next_reject], is_fulfilled); 240 | } 241 | } 242 | } 243 | } 244 | } 245 | 246 | // 执行 Promise 的回调 247 | // then_handler 代表传入到 then 的回调方法,is_fulfilled 为 true 代表执行 onFulfilled,否则执行 onRejected 248 | fn execute_promise_reaction(ctx: &mut Context, then_handler: Value, value: Value, next_resolve_reject: Vec, is_fulfilled: bool) { 249 | // 执行完 then 回调后的返回值 250 | let next_data = if let Value::Undefined = then_handler { 251 | if is_fulfilled { 252 | Ok(value.clone()) 253 | } else { 254 | Err(JSIError::new(JSIErrorType::TypeError, "".to_string(), 0, 0)) 255 | } 256 | } else if let Value::Function(then_handler_fun) = then_handler { 257 | let mut call_ctx = CallContext { 258 | ctx, 259 | this: Value::Undefined, 260 | reference: None, 261 | func_name: String::from("then_handler_fun"), 262 | }; 263 | call_ctx.call_function(then_handler_fun, None, None, vec![value.clone()]) 264 | } else { 265 | Ok(value.clone()) 266 | }; 267 | 268 | match next_data { 269 | Ok(resolved_value) => { 270 | if let Value::Promise(resolved_promise_value) = resolved_value { 271 | // 根据 promise 的状态,调用下一个 promise 的 resolve 或 reject 方法 272 | let resolved_promise = resolved_promise_value.borrow(); 273 | let state = resolved_promise.get_inner_property_value(String::from("[[PromiseState]]")).unwrap(); 274 | if let Value::String(state_str) = state { 275 | if state_str == String::from("fulfilled") { 276 | let fulfilled_value = resolved_promise.get_inner_property_value(String::from("[[PromiseFulfilledValue]]")).unwrap_or(Value::Undefined); 277 | // 调用下一个 promise 的 resolve 方法 278 | if let Some(next_resolve) = next_resolve_reject.get(0) { 279 | if let Value::Function(next_resolve_fun) = next_resolve { 280 | let mut call_ctx = CallContext { 281 | ctx, 282 | this: Value::Undefined, 283 | reference: Some(Rc::downgrade(next_resolve_fun)), 284 | func_name: String::from("next_resolve_fun"), 285 | }; 286 | call_ctx.call_function(Rc::clone(next_resolve_fun), None, None, vec![fulfilled_value.clone()]).unwrap(); 287 | } 288 | } 289 | } else if state_str == String::from("rejected") { 290 | let rejected_reason = resolved_promise.get_inner_property_value(String::from("[[PromiseRejectedReason]]")).unwrap_or(Value::Undefined); 291 | // 调用下一个 promise 的 reject 方法 292 | if let Some(next_reject) = next_resolve_reject.get(1) { 293 | if let Value::Function(next_reject_fun) = next_reject { 294 | let mut call_ctx = CallContext { 295 | ctx, 296 | this: Value::Undefined, 297 | reference: Some(Rc::downgrade(next_reject_fun)), 298 | func_name: String::from("next_reject_fun"), 299 | }; 300 | 301 | call_ctx.call_function(Rc::clone(next_reject_fun), None, None, vec![rejected_reason.clone()]).unwrap(); 302 | } 303 | } 304 | } else { 305 | // TODO, resolve 又返回了一个 pending 状态的 promise 306 | // 在这个 promise 被 resolve 或 reject 的时候,再调用下一个 promise 的 resolve 或 reject 方法 307 | } 308 | } 309 | } else { 310 | // 调用下一个 promise 的 resolve 方法 311 | if let Some(next_resolve) = next_resolve_reject.get(0) { 312 | if let Value::Function(next_resolve_fun) = next_resolve { 313 | let mut call_ctx = CallContext { 314 | ctx, 315 | this: Value::Undefined, 316 | reference: Some(Rc::downgrade(next_resolve_fun)), 317 | func_name: String::from("next_resolve_fun"), 318 | }; 319 | call_ctx.call_function(Rc::clone(next_resolve_fun), None, None, vec![resolved_value.clone()]).unwrap(); 320 | } 321 | } 322 | } 323 | }, 324 | Err(err) => { 325 | // 调用下一个 promise 的 reject 方法 326 | if let Some(next_reject) = next_resolve_reject.get(1) { 327 | if let Value::Function(next_reject_fun) = next_reject { 328 | let mut call_ctx = CallContext { 329 | ctx, 330 | this: Value::Undefined, 331 | reference: Some(Rc::downgrade(next_reject_fun)), 332 | func_name: String::from("next_reject_fun"), 333 | }; 334 | call_ctx.call_function(Rc::clone(next_reject_fun), None, None, vec![Value::String(err.message)]).unwrap(); 335 | } 336 | } 337 | } 338 | } 339 | 340 | } 341 | 342 | // Promise.resolve 静态方法 343 | fn resolve_static(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 344 | let value = args.get(0).or(Some(&Value::Undefined)).unwrap(); 345 | 346 | if let Value::Promise(_) = value { 347 | return Ok(value.to_owned()); 348 | } 349 | 350 | let promise_value = create_promise(call_ctx.ctx, Value::Undefined); 351 | 352 | if let Value::Promise(promise_rc) = &promise_value { 353 | let mut promise_mut = promise_rc.borrow_mut(); 354 | promise_mut.set_inner_property_value(PROMISE_STATE.to_string(), Value::String("fulfilled".to_string())); 355 | promise_mut.set_inner_property_value(PROMISE_FULFILLED_VALUE.to_string(), value.to_owned()); 356 | } 357 | Ok(promise_value) 358 | } 359 | 360 | // Promise.reject 静态方法 361 | fn reject_static(call_ctx: &mut CallContext, args: Vec) -> JSIResult { 362 | let reason = args.get(0).or(Some(&Value::Undefined)).unwrap(); 363 | 364 | let promise_value = create_promise(call_ctx.ctx, Value::Undefined); 365 | 366 | if let Value::Promise(promise_rc) = &promise_value { 367 | let mut promise_mut = promise_rc.borrow_mut(); 368 | promise_mut.set_inner_property_value(PROMISE_STATE.to_string(), Value::String("rejected".to_string())); 369 | promise_mut.set_inner_property_value(PROMISE_REJECTED_REASON.to_string(), reason.to_owned()); 370 | } 371 | Ok(promise_value) 372 | } 373 | 374 | fn get_promise_object_from_this<'a>(this_value: &'a Value) -> Option<&'a Rc>> { 375 | match &this_value { 376 | Value::Promise(promise_rc) => Some(promise_rc), 377 | _ => None, 378 | } 379 | } --------------------------------------------------------------------------------