mirror of https://github.com/boa-dev/boa.git
Browse Source
- Move `Console` to `Context` - Change `Context::global()` to `Context::global_object()` - Remove some `use std::borrow::Borrow` - Add some pub exports - Add `Context::eval()` - Deprecate forward_val, forward, exec - Make boa_cli use Context::eval() - Deprecated forward forward_val and exec - Make deprecated functionspull/688/head
Halid Odat
4 years ago
committed by
GitHub
77 changed files with 1558 additions and 1922 deletions
@ -0,0 +1,496 @@ |
|||||||
|
//! Javascript context.
|
||||||
|
|
||||||
|
use crate::{ |
||||||
|
builtins::{ |
||||||
|
self, |
||||||
|
function::{Function, FunctionFlags, NativeFunction}, |
||||||
|
object::ObjectData, |
||||||
|
object::{GcObject, Object, PROTOTYPE}, |
||||||
|
Console, Symbol, |
||||||
|
}, |
||||||
|
class::{Class, ClassBuilder}, |
||||||
|
exec::Interpreter, |
||||||
|
property::{Property, PropertyKey}, |
||||||
|
realm::Realm, |
||||||
|
syntax::{ |
||||||
|
ast::{ |
||||||
|
node::{ |
||||||
|
statement_list::RcStatementList, Call, FormalParameter, Identifier, New, |
||||||
|
StatementList, |
||||||
|
}, |
||||||
|
Const, Node, |
||||||
|
}, |
||||||
|
Parser, |
||||||
|
}, |
||||||
|
value::{PreferredType, RcString, RcSymbol, Type, Value}, |
||||||
|
BoaProfiler, Executable, Result, |
||||||
|
}; |
||||||
|
use std::result::Result as StdResult; |
||||||
|
|
||||||
|
/// Javascript context. It is the primary way to interact with the runtime.
|
||||||
|
///
|
||||||
|
/// For each `Context` instance a new instance of runtime is created.
|
||||||
|
/// It means that it is safe to use different contexts in different threads,
|
||||||
|
/// but each `Context` instance must be used only from a single thread.
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct Context { |
||||||
|
/// realm holds both the global object and the environment
|
||||||
|
realm: Realm, |
||||||
|
|
||||||
|
/// The current executor.
|
||||||
|
executor: Interpreter, |
||||||
|
|
||||||
|
/// Symbol hash.
|
||||||
|
///
|
||||||
|
/// For now this is an incremented u32 number.
|
||||||
|
symbol_count: u32, |
||||||
|
|
||||||
|
/// console object state.
|
||||||
|
console: Console, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for Context { |
||||||
|
fn default() -> Self { |
||||||
|
let realm = Realm::create(); |
||||||
|
let executor = Interpreter::new(); |
||||||
|
let mut context = Self { |
||||||
|
realm, |
||||||
|
executor, |
||||||
|
symbol_count: 0, |
||||||
|
console: Console::default(), |
||||||
|
}; |
||||||
|
|
||||||
|
// Add new builtIns to Context Realm
|
||||||
|
// At a later date this can be removed from here and called explicitly,
|
||||||
|
// but for now we almost always want these default builtins
|
||||||
|
context.create_intrinsics(); |
||||||
|
|
||||||
|
context |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Context { |
||||||
|
/// Create a new `Context`.
|
||||||
|
pub fn new() -> Self { |
||||||
|
Default::default() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn realm(&self) -> &Realm { |
||||||
|
&self.realm |
||||||
|
} |
||||||
|
|
||||||
|
pub fn realm_mut(&mut self) -> &mut Realm { |
||||||
|
&mut self.realm |
||||||
|
} |
||||||
|
|
||||||
|
pub fn executor(&mut self) -> &mut Interpreter { |
||||||
|
&mut self.executor |
||||||
|
} |
||||||
|
|
||||||
|
/// A helper function for getting a immutable reference to the `console` object.
|
||||||
|
pub(crate) fn console(&self) -> &Console { |
||||||
|
&self.console |
||||||
|
} |
||||||
|
|
||||||
|
/// A helper function for getting a mutable reference to the `console` object.
|
||||||
|
pub(crate) fn console_mut(&mut self) -> &mut Console { |
||||||
|
&mut self.console |
||||||
|
} |
||||||
|
|
||||||
|
/// Sets up the default global objects within Global
|
||||||
|
fn create_intrinsics(&mut self) { |
||||||
|
let _timer = BoaProfiler::global().start_event("create_intrinsics", "interpreter"); |
||||||
|
// Create intrinsics, add global objects here
|
||||||
|
builtins::init(self); |
||||||
|
} |
||||||
|
|
||||||
|
/// Generates a new `Symbol` internal hash.
|
||||||
|
///
|
||||||
|
/// This currently is an incremented value.
|
||||||
|
#[inline] |
||||||
|
fn generate_hash(&mut self) -> u32 { |
||||||
|
let hash = self.symbol_count; |
||||||
|
self.symbol_count += 1; |
||||||
|
hash |
||||||
|
} |
||||||
|
|
||||||
|
/// Construct a new `Symbol` with an optional description.
|
||||||
|
#[inline] |
||||||
|
pub fn construct_symbol(&mut self, description: Option<RcString>) -> RcSymbol { |
||||||
|
RcSymbol::from(Symbol::new(self.generate_hash(), description)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Construct an empty object.
|
||||||
|
#[inline] |
||||||
|
pub fn construct_object(&self) -> GcObject { |
||||||
|
let object_prototype = self |
||||||
|
.global_object() |
||||||
|
.get_field("Object") |
||||||
|
.get_field(PROTOTYPE); |
||||||
|
GcObject::new(Object::create(object_prototype)) |
||||||
|
} |
||||||
|
|
||||||
|
/// <https://tc39.es/ecma262/#sec-call>
|
||||||
|
pub(crate) fn call(&mut self, f: &Value, this: &Value, args: &[Value]) -> Result<Value> { |
||||||
|
match *f { |
||||||
|
Value::Object(ref object) => object.call(this, args, self), |
||||||
|
_ => self.throw_type_error("not a function"), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Return the global object.
|
||||||
|
pub fn global_object(&self) -> &Value { |
||||||
|
&self.realm.global_obj |
||||||
|
} |
||||||
|
|
||||||
|
/// Constructs a `RangeError` with the specified message.
|
||||||
|
pub fn construct_range_error<M>(&mut self, message: M) -> Value |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
// Runs a `new RangeError(message)`.
|
||||||
|
New::from(Call::new( |
||||||
|
Identifier::from("RangeError"), |
||||||
|
vec![Const::from(message.into()).into()], |
||||||
|
)) |
||||||
|
.run(self) |
||||||
|
.expect_err("RangeError should always throw") |
||||||
|
} |
||||||
|
|
||||||
|
/// Throws a `RangeError` with the specified message.
|
||||||
|
pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value> |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
Err(self.construct_range_error(message)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Constructs a `TypeError` with the specified message.
|
||||||
|
pub fn construct_type_error<M>(&mut self, message: M) -> Value |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
// Runs a `new TypeError(message)`.
|
||||||
|
New::from(Call::new( |
||||||
|
Identifier::from("TypeError"), |
||||||
|
vec![Const::from(message.into()).into()], |
||||||
|
)) |
||||||
|
.run(self) |
||||||
|
.expect_err("TypeError should always throw") |
||||||
|
} |
||||||
|
|
||||||
|
/// Throws a `TypeError` with the specified message.
|
||||||
|
pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value> |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
Err(self.construct_type_error(message)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Constructs a `ReferenceError` with the specified message.
|
||||||
|
pub fn construct_reference_error<M>(&mut self, message: M) -> Value |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
New::from(Call::new( |
||||||
|
Identifier::from("ReferenceError"), |
||||||
|
vec![Const::from(message.into() + " is not defined").into()], |
||||||
|
)) |
||||||
|
.run(self) |
||||||
|
.expect_err("ReferenceError should always throw") |
||||||
|
} |
||||||
|
|
||||||
|
/// Throws a `ReferenceError` with the specified message.
|
||||||
|
pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value> |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
Err(self.construct_reference_error(message)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Constructs a `SyntaxError` with the specified message.
|
||||||
|
pub fn construct_syntax_error<M>(&mut self, message: M) -> Value |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
New::from(Call::new( |
||||||
|
Identifier::from("SyntaxError"), |
||||||
|
vec![Const::from(message.into()).into()], |
||||||
|
)) |
||||||
|
.run(self) |
||||||
|
.expect_err("SyntaxError should always throw") |
||||||
|
} |
||||||
|
|
||||||
|
/// Throws a `SyntaxError` with the specified message.
|
||||||
|
pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value> |
||||||
|
where |
||||||
|
M: Into<String>, |
||||||
|
{ |
||||||
|
Err(self.construct_syntax_error(message)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions
|
||||||
|
pub(crate) fn create_function<P, B>( |
||||||
|
&mut self, |
||||||
|
params: P, |
||||||
|
body: B, |
||||||
|
flags: FunctionFlags, |
||||||
|
) -> Value |
||||||
|
where |
||||||
|
P: Into<Box<[FormalParameter]>>, |
||||||
|
B: Into<StatementList>, |
||||||
|
{ |
||||||
|
let function_prototype = self |
||||||
|
.global_object() |
||||||
|
.get_field("Function") |
||||||
|
.get_field(PROTOTYPE); |
||||||
|
|
||||||
|
// Every new function has a prototype property pre-made
|
||||||
|
let proto = Value::new_object(Some(self.global_object())); |
||||||
|
|
||||||
|
let params = params.into(); |
||||||
|
let params_len = params.len(); |
||||||
|
let func = Function::Ordinary { |
||||||
|
flags, |
||||||
|
body: RcStatementList::from(body.into()), |
||||||
|
params, |
||||||
|
environment: self.realm.environment.get_current_environment().clone(), |
||||||
|
}; |
||||||
|
|
||||||
|
let new_func = Object::function(func, function_prototype); |
||||||
|
|
||||||
|
let val = Value::from(new_func); |
||||||
|
|
||||||
|
// Set constructor field to the newly created Value (function object)
|
||||||
|
proto.set_field("constructor", val.clone()); |
||||||
|
|
||||||
|
val.set_field(PROTOTYPE, proto); |
||||||
|
val.set_field("length", Value::from(params_len)); |
||||||
|
|
||||||
|
val |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a new builin function.
|
||||||
|
pub fn create_builtin_function( |
||||||
|
&mut self, |
||||||
|
name: &str, |
||||||
|
length: usize, |
||||||
|
body: NativeFunction, |
||||||
|
) -> Result<GcObject> { |
||||||
|
let function_prototype = self |
||||||
|
.global_object() |
||||||
|
.get_field("Function") |
||||||
|
.get_field(PROTOTYPE); |
||||||
|
|
||||||
|
// Every new function has a prototype property pre-made
|
||||||
|
let proto = Value::new_object(Some(self.global_object())); |
||||||
|
let mut function = Object::function( |
||||||
|
Function::BuiltIn(body.into(), FunctionFlags::CALLABLE), |
||||||
|
function_prototype, |
||||||
|
); |
||||||
|
function.set(PROTOTYPE.into(), proto); |
||||||
|
function.set("length".into(), length.into()); |
||||||
|
function.set("name".into(), name.into()); |
||||||
|
|
||||||
|
Ok(GcObject::new(function)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Register a global function.
|
||||||
|
pub fn register_global_function( |
||||||
|
&mut self, |
||||||
|
name: &str, |
||||||
|
length: usize, |
||||||
|
body: NativeFunction, |
||||||
|
) -> Result<()> { |
||||||
|
let function = self.create_builtin_function(name, length, body)?; |
||||||
|
self.global_object().set_field(name, function); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Converts an array object into a rust vector of values.
|
||||||
|
///
|
||||||
|
/// This is useful for the spread operator, for any other object an `Err` is returned
|
||||||
|
pub(crate) fn extract_array_properties(&mut self, value: &Value) -> StdResult<Vec<Value>, ()> { |
||||||
|
if let Value::Object(ref x) = value { |
||||||
|
// Check if object is array
|
||||||
|
if let ObjectData::Array = x.borrow().data { |
||||||
|
let length = value.get_field("length").as_number().unwrap() as i32; |
||||||
|
let values = (0..length) |
||||||
|
.map(|idx| value.get_field(idx.to_string())) |
||||||
|
.collect(); |
||||||
|
return Ok(values); |
||||||
|
} |
||||||
|
// Check if object is a Map
|
||||||
|
else if let ObjectData::Map(ref map) = x.borrow().data { |
||||||
|
let values = map |
||||||
|
.iter() |
||||||
|
.map(|(key, value)| { |
||||||
|
// Construct a new array containing the key-value pair
|
||||||
|
let array = Value::new_object(Some( |
||||||
|
&self |
||||||
|
.realm() |
||||||
|
.environment |
||||||
|
.get_global_object() |
||||||
|
.expect("Could not get global object"), |
||||||
|
)); |
||||||
|
array.set_data(ObjectData::Array); |
||||||
|
array.as_object_mut().expect("object").set_prototype( |
||||||
|
self.realm() |
||||||
|
.environment |
||||||
|
.get_binding_value("Array") |
||||||
|
.expect("Array was not initialized") |
||||||
|
.get_field(PROTOTYPE), |
||||||
|
); |
||||||
|
array.set_field("0", key); |
||||||
|
array.set_field("1", value); |
||||||
|
array.set_field("length", Value::from(2)); |
||||||
|
array |
||||||
|
}) |
||||||
|
.collect(); |
||||||
|
return Ok(values); |
||||||
|
} |
||||||
|
|
||||||
|
return Err(()); |
||||||
|
} |
||||||
|
|
||||||
|
Err(()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Converts an object to a primitive.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript][spec]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive
|
||||||
|
pub(crate) fn ordinary_to_primitive( |
||||||
|
&mut self, |
||||||
|
o: &Value, |
||||||
|
hint: PreferredType, |
||||||
|
) -> Result<Value> { |
||||||
|
// 1. Assert: Type(O) is Object.
|
||||||
|
debug_assert!(o.get_type() == Type::Object); |
||||||
|
// 2. Assert: Type(hint) is String and its value is either "string" or "number".
|
||||||
|
debug_assert!(hint == PreferredType::String || hint == PreferredType::Number); |
||||||
|
|
||||||
|
// 3. If hint is "string", then
|
||||||
|
// a. Let methodNames be « "toString", "valueOf" ».
|
||||||
|
// 4. Else,
|
||||||
|
// a. Let methodNames be « "valueOf", "toString" ».
|
||||||
|
let method_names = if hint == PreferredType::String { |
||||||
|
["toString", "valueOf"] |
||||||
|
} else { |
||||||
|
["valueOf", "toString"] |
||||||
|
}; |
||||||
|
|
||||||
|
// 5. For each name in methodNames in List order, do
|
||||||
|
for name in &method_names { |
||||||
|
// a. Let method be ? Get(O, name).
|
||||||
|
let method: Value = o.get_field(*name); |
||||||
|
// b. If IsCallable(method) is true, then
|
||||||
|
if method.is_function() { |
||||||
|
// i. Let result be ? Call(method, O).
|
||||||
|
let result = self.call(&method, &o, &[])?; |
||||||
|
// ii. If Type(result) is not Object, return result.
|
||||||
|
if !result.is_object() { |
||||||
|
return Ok(result); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 6. Throw a TypeError exception.
|
||||||
|
self.throw_type_error("cannot convert object to primitive value") |
||||||
|
} |
||||||
|
|
||||||
|
/// https://tc39.es/ecma262/#sec-hasproperty
|
||||||
|
pub(crate) fn has_property(&self, obj: &Value, key: &PropertyKey) -> bool { |
||||||
|
if let Some(obj) = obj.as_object() { |
||||||
|
obj.has_property(key) |
||||||
|
} else { |
||||||
|
false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub(crate) fn set_value(&mut self, node: &Node, value: Value) -> Result<Value> { |
||||||
|
match node { |
||||||
|
Node::Identifier(ref name) => { |
||||||
|
self.realm |
||||||
|
.environment |
||||||
|
.set_mutable_binding(name.as_ref(), value.clone(), true); |
||||||
|
Ok(value) |
||||||
|
} |
||||||
|
Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node |
||||||
|
.obj() |
||||||
|
.run(self)? |
||||||
|
.set_field(get_const_field_node.field(), value)), |
||||||
|
Node::GetField(ref get_field) => { |
||||||
|
let field = get_field.field().run(self)?; |
||||||
|
let key = field.to_property_key(self)?; |
||||||
|
Ok(get_field.obj().run(self)?.set_field(key, value)) |
||||||
|
} |
||||||
|
_ => panic!("TypeError: invalid assignment to {}", node), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Register a global class of type `T`, where `T` implemets `Class`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```ignore
|
||||||
|
/// #[derive(Debug, Trace, Finalize)]
|
||||||
|
/// struct MyClass;
|
||||||
|
///
|
||||||
|
/// impl Class for MyClass {
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// context.register_global_class::<MyClass>();
|
||||||
|
/// ```
|
||||||
|
pub fn register_global_class<T>(&mut self) -> Result<()> |
||||||
|
where |
||||||
|
T: Class, |
||||||
|
{ |
||||||
|
let mut class_builder = ClassBuilder::new::<T>(self); |
||||||
|
T::init(&mut class_builder)?; |
||||||
|
|
||||||
|
let class = class_builder.build(); |
||||||
|
let property = Property::data_descriptor(class.into(), T::ATTRIBUTE); |
||||||
|
self.global_object() |
||||||
|
.as_object_mut() |
||||||
|
.unwrap() |
||||||
|
.insert_property(T::NAME, property); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn parser_expr(src: &str) -> StdResult<StatementList, String> { |
||||||
|
Parser::new(src.as_bytes()) |
||||||
|
.parse_all() |
||||||
|
.map_err(|e| e.to_string()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Evaluates the given code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
///# use boa::Context;
|
||||||
|
/// let mut context = Context::new();
|
||||||
|
///
|
||||||
|
/// let value = context.eval("1 + 3").unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(value.is_number());
|
||||||
|
/// assert_eq!(value.as_number().unwrap(), 4.0);
|
||||||
|
/// ```
|
||||||
|
#[allow(clippy::unit_arg, clippy::drop_copy)] |
||||||
|
pub fn eval(&mut self, src: &str) -> Result<Value> { |
||||||
|
let main_timer = BoaProfiler::global().start_event("Main", "Main"); |
||||||
|
|
||||||
|
let result = match Self::parser_expr(src) { |
||||||
|
Ok(expr) => expr.run(self), |
||||||
|
Err(e) => self.throw_type_error(e), |
||||||
|
}; |
||||||
|
|
||||||
|
// The main_timer needs to be dropped before the BoaProfiler is.
|
||||||
|
drop(main_timer); |
||||||
|
BoaProfiler::global().drop(); |
||||||
|
|
||||||
|
result |
||||||
|
} |
||||||
|
} |
@ -1,17 +1,16 @@ |
|||||||
use super::{Interpreter, InterpreterState}; |
use super::{Context, InterpreterState}; |
||||||
use crate::{exec::Executable, syntax::ast::node::Break, Realm}; |
use crate::{exec::Executable, syntax::ast::node::Break}; |
||||||
|
|
||||||
#[test] |
#[test] |
||||||
fn check_post_state() { |
fn check_post_state() { |
||||||
let realm = Realm::create(); |
let mut engine = Context::new(); |
||||||
let mut engine = Interpreter::new(realm); |
|
||||||
|
|
||||||
let brk: Break = Break::new("label"); |
let brk: Break = Break::new("label"); |
||||||
|
|
||||||
brk.run(&mut engine).unwrap(); |
brk.run(&mut engine).unwrap(); |
||||||
|
|
||||||
assert_eq!( |
assert_eq!( |
||||||
engine.get_current_state(), |
engine.executor().get_current_state(), |
||||||
&InterpreterState::Break(Some("label".to_string())) |
&InterpreterState::Break(Some("label".to_string())) |
||||||
); |
); |
||||||
} |
} |
||||||
|
@ -1,96 +0,0 @@ |
|||||||
use super::*; |
|
||||||
use crate::{ |
|
||||||
exec::Executable, |
|
||||||
syntax::ast::{ |
|
||||||
node::{Call, Identifier, New}, |
|
||||||
Const, |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
impl Interpreter { |
|
||||||
/// Constructs a `RangeError` with the specified message.
|
|
||||||
pub fn construct_range_error<M>(&mut self, message: M) -> Value |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
// Runs a `new RangeError(message)`.
|
|
||||||
New::from(Call::new( |
|
||||||
Identifier::from("RangeError"), |
|
||||||
vec![Const::from(message.into()).into()], |
|
||||||
)) |
|
||||||
.run(self) |
|
||||||
.expect_err("RangeError should always throw") |
|
||||||
} |
|
||||||
|
|
||||||
/// Throws a `RangeError` with the specified message.
|
|
||||||
pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value> |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
Err(self.construct_range_error(message)) |
|
||||||
} |
|
||||||
|
|
||||||
/// Constructs a `TypeError` with the specified message.
|
|
||||||
pub fn construct_type_error<M>(&mut self, message: M) -> Value |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
// Runs a `new TypeError(message)`.
|
|
||||||
New::from(Call::new( |
|
||||||
Identifier::from("TypeError"), |
|
||||||
vec![Const::from(message.into()).into()], |
|
||||||
)) |
|
||||||
.run(self) |
|
||||||
.expect_err("TypeError should always throw") |
|
||||||
} |
|
||||||
|
|
||||||
/// Throws a `TypeError` with the specified message.
|
|
||||||
pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value> |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
Err(self.construct_type_error(message)) |
|
||||||
} |
|
||||||
|
|
||||||
/// Constructs a `ReferenceError` with the specified message.
|
|
||||||
pub fn construct_reference_error<M>(&mut self, message: M) -> Value |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
New::from(Call::new( |
|
||||||
Identifier::from("ReferenceError"), |
|
||||||
vec![Const::from(message.into() + " is not defined").into()], |
|
||||||
)) |
|
||||||
.run(self) |
|
||||||
.expect_err("ReferenceError should always throw") |
|
||||||
} |
|
||||||
|
|
||||||
/// Throws a `ReferenceError` with the specified message.
|
|
||||||
pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value> |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
Err(self.construct_reference_error(message)) |
|
||||||
} |
|
||||||
|
|
||||||
/// Constructs a `SyntaxError` with the specified message.
|
|
||||||
pub fn construct_syntax_error<M>(&mut self, message: M) -> Value |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
New::from(Call::new( |
|
||||||
Identifier::from("SyntaxError"), |
|
||||||
vec![Const::from(message.into()).into()], |
|
||||||
)) |
|
||||||
.run(self) |
|
||||||
.expect_err("SyntaxError should always throw") |
|
||||||
} |
|
||||||
|
|
||||||
/// Throws a `SyntaxError` with the specified message.
|
|
||||||
pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value> |
|
||||||
where |
|
||||||
M: Into<String>, |
|
||||||
{ |
|
||||||
Err(self.construct_syntax_error(message)) |
|
||||||
} |
|
||||||
} |
|
@ -1,14 +1,16 @@ |
|||||||
use super::{Executable, Interpreter, InterpreterState}; |
use super::{Context, Executable, InterpreterState}; |
||||||
use crate::{syntax::ast::node::Return, Result, Value}; |
use crate::{syntax::ast::node::Return, Result, Value}; |
||||||
|
|
||||||
impl Executable for Return { |
impl Executable for Return { |
||||||
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { |
fn run(&self, interpreter: &mut Context) -> Result<Value> { |
||||||
let result = match self.expr() { |
let result = match self.expr() { |
||||||
Some(ref v) => v.run(interpreter), |
Some(ref v) => v.run(interpreter), |
||||||
None => Ok(Value::undefined()), |
None => Ok(Value::undefined()), |
||||||
}; |
}; |
||||||
// Set flag for return
|
// Set flag for return
|
||||||
interpreter.set_current_state(InterpreterState::Return); |
interpreter |
||||||
|
.executor() |
||||||
|
.set_current_state(InterpreterState::Return); |
||||||
result |
result |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -1,9 +1,9 @@ |
|||||||
use super::{Executable, Interpreter}; |
use super::{Context, Executable}; |
||||||
use crate::{syntax::ast::node::Throw, Result, Value}; |
use crate::{syntax::ast::node::Throw, Result, Value}; |
||||||
|
|
||||||
impl Executable for Throw { |
impl Executable for Throw { |
||||||
#[inline] |
#[inline] |
||||||
fn run(&self, interpreter: &mut Interpreter) -> Result<Value> { |
fn run(&self, interpreter: &mut Context) -> Result<Value> { |
||||||
Err(self.expr().run(interpreter)?) |
Err(self.expr().run(interpreter)?) |
||||||
} |
} |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue