1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Functionality for executing and parsing JavaScript.
//!
//! The simple `eval` function should cover most needs. It evalutes the supplied
//! code directly and returns the script's value.
//!
//! ```rust
//! # use chakracore as js;
//! # let runtime = js::Runtime::new().unwrap();
//! # let context = js::Context::new(&runtime).unwrap();
//! # let guard = context.make_current().unwrap();
//! let result = js::script::eval(&guard, "10 + 10").unwrap();
//! assert_eq!(result.to_integer(&guard), 20);
//! ```
//!
//! Another option is to parse the source code and execute it at a later time
//! with a function. This is done using the `parse` function:
//!
//! ```rust
//! # use chakracore as js;
//! # let runtime = js::Runtime::new().unwrap();
//! # let context = js::Context::new(&runtime).unwrap();
//! # let guard = context.make_current().unwrap();
//! let add = js::script::parse(&guard, "10 + 10").unwrap();
//! let result = add.call(&guard, &[]).unwrap();
//! assert_eq!(result.to_integer(&guard), 20);
//! ```
use std::slice;
use chakracore_sys::*;
use error::*;
use context::ContextGuard;
use util::{self, jstry};
use value;

/// Evaluates code directly.
pub fn eval(guard: &ContextGuard, code: &str) -> Result<value::Value> {
    eval_with_name(guard, "", code)
}

/// Evaluates code and associates it with a name.
pub fn eval_with_name(guard: &ContextGuard, name: &str, code: &str) -> Result<value::Value> {
    let (code, result) = process_code(guard, name, code, CodeAction::Execute);
    unsafe {
        util::handle_exception(guard, code).map(|_| value::Value::from_raw(result))
    }
}

/// Parses code and returns it as a function.
pub fn parse(guard: &ContextGuard, code: &str) -> Result<value::Function> {
    parse_with_name(guard, "", code)
}

/// Parses code and associates it with a name, returns it as a function.
pub fn parse_with_name(guard: &ContextGuard, name: &str, code: &str) -> Result<value::Function> {
    let (code, result) = process_code(guard, name, code, CodeAction::Parse);
    jstry(code).map(|_| unsafe { value::Function::from_raw(result) })
}

/// Used for processing code.
#[derive(Copy, Clone, Debug)]
enum CodeAction {
    Execute,
    Parse,
}

/// Either parses or executes a script.
fn process_code(guard: &ContextGuard, name: &str, code: &str, action: CodeAction) -> (JsErrorCode, JsValueRef) {
    let name = value::String::new(guard, name);
    let buffer = create_code_buffer(guard, code);

    let api = match action {
        CodeAction::Execute => JsRun,
        CodeAction::Parse => JsParse,
    };

    unsafe {
        let mut result = JsValueRef::new();
        let code = api(buffer.as_raw(),
                        generate_source_context(),
                        name.as_raw(),
                        JsParseScriptAttributeNone,
                        &mut result);
        (code, result)
    }
}

/// Creates an array buffer from immutable data (JSRT does not modify it internally).
fn create_code_buffer(guard: &ContextGuard, code: &str) -> value::ArrayBuffer {
    let bytes = code.as_bytes();
    unsafe {
        // It's assumed that the JSRT implementation does not modify the code buffer
        let slice = slice::from_raw_parts_mut(bytes.as_ptr() as *mut u8, bytes.len());
        value::ArrayBuffer::from_slice(guard, slice)
    }
}

/// Generates a new source context identifier.
fn generate_source_context() -> JsSourceContext {
    // TODO: handle source context identifier
    JsSourceContext::max_value()
}

#[cfg(test)]
mod tests {
    use {test, error, script};

    #[test]
    fn execute_exception() {
        test::run_with_context(|guard| {
            let error = script::eval(guard, "null[0] = 3;").unwrap_err();
            let result = script::eval(guard, "5 + 5").unwrap();

            assert_matches!(error.kind(), &error::ErrorKind::ScriptException(_));
            assert_eq!(result.to_integer(guard), 10);
        });
    }

    #[test]
    fn compile_exception() {
        test::run_with_context(|guard| {
            let error = script::eval(guard, "err)").unwrap_err();
            let result = script::eval(guard, "5 + 5").unwrap();

            assert_eq!(result.to_integer(guard), 10);
            assert_matches!(error.kind(), &error::ErrorKind::ScriptCompile(_));
        });
    }

    #[test]
    fn parse_script() {
        test::run_with_context(|guard| {
            let func = script::parse(guard, "new Number(10)").unwrap();
            let result = func.call(guard, &[]).unwrap();
            assert_eq!(result.to_integer(guard), 10);
        });
    }
}