Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libs/jsparser/src/symbol/builtin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@
- [NaN, NAN]
- [async, ASYNC]
- all
- apply
- arguments
- assign
- at
- bind
- call
- cause
- charAt
- charCodeAt
Expand Down
23 changes: 1 addition & 22 deletions libs/jsruntime/src/backend/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,28 +370,7 @@ pub(crate) extern "C" fn runtime_create_capture<X>(
target: *mut Value,
) -> HandleMut<Capture> {
logger::debug!(event = "runtime_create_capture", ?target);

debug_assert!(
std::alloc::Layout::from_size_align(
std::mem::size_of::<Capture>(),
std::mem::align_of::<Capture>()
)
.is_ok()
);
// SAFETY: `from_size_align()` always succeeds.
const LAYOUT: std::alloc::Layout = unsafe {
std::alloc::Layout::from_size_align_unchecked(
std::mem::size_of::<Capture>(),
std::mem::align_of::<Capture>(),
)
};

runtime.heap.alloc_layout_mut(LAYOUT, move |ptr| {
// SAFETY: `ptr` is a non-null pointer to a `Capture`.
let capture = unsafe { ptr.cast::<Capture>().as_mut() };
capture.target = target;
// `capture.escaped` will be filled with an actual value.
})
runtime.create_capture(target)
}

pub(crate) extern "C" fn runtime_create_closure<X>(
Expand Down
16 changes: 12 additions & 4 deletions libs/jsruntime/src/builtins/builtin_mod.rs.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
// This file was automagically generated with:
// template: {{@template}}

logging::define_logger! {"bee::jsruntime::builtins::{{metadata.id}}"}

mod imp;

use jsgc::HandleMut;
use jsparser::Symbol;

use crate::Runtime;
use crate::logger;
use crate::types::CallContext;
use crate::types::Object;
use crate::types::Property;
Expand Down Expand Up @@ -53,12 +54,14 @@ impl<X> Runtime<X> {
slots: &[],
prototype: None,
});
let _ = constructor.define_own_property(Symbol::{{symbol}}.into(), Property::data_wxc(Value::Object(func)));
let result = constructor.define_own_property(Symbol::{{symbol}}.into(), Property::data_wxc(Value::Object(func)));
debug_assert!(matches!(result, Ok(true)));

{{/if}}
{{/each}}
// TODO(refactor): bind outside this function
let _ = self.builtins.{{metadata.id}}_prototype.define_own_property(Symbol::CONSTRUCTOR.into(), Property::data_wxc(Value::Object(constructor)));
let result = self.builtins.{{metadata.id}}_prototype.define_own_property(Symbol::CONSTRUCTOR.into(), Property::data_wxc(Value::Object(constructor)));
debug_assert!(matches!(result, Ok(true)));

constructor
}
Expand All @@ -85,7 +88,8 @@ impl<X> Runtime<X> {
slots: &[],
prototype: None,
});
let _ = prototype.define_own_property(Symbol::{{symbol}}.into(), Property::data_wxc(Value::Object(func)));
let result = prototype.define_own_property(Symbol::{{symbol}}.into(), Property::data_wxc(Value::Object(func)));
debug_assert!(matches!(result, Ok(true)));

{{/if}}
{{/each}}
Expand Down Expand Up @@ -140,6 +144,9 @@ extern "C" fn {{imp}}<X>(
context: &mut CallContext,
retv: &mut Value,
) -> Status {
{{#if options.no_adapter}}
imp::{{imp}}(runtime, context, retv)
{{else}}
match imp::{{imp}}(runtime, context) {
Ok(value) => {
*retv = value;
Expand All @@ -150,6 +157,7 @@ extern "C" fn {{imp}}<X>(
Status::Exception
}
}
{{/if}}
}
{{/if}}
{{/each}}
3 changes: 2 additions & 1 deletion libs/jsruntime/src/builtins/error/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ use jsparser::Symbol;

use crate::Error;
use crate::Runtime;
use crate::logger;
use crate::types::CallContext;
use crate::types::Object;
use crate::types::Property;
use crate::types::String;
use crate::types::Value;

use super::logger;

//#sec-error-message constructor
pub fn constructor<X>(runtime: &mut Runtime<X>, context: &mut CallContext) -> Result<Value, Error> {
logger::debug!(event = "error_constructor");
Expand Down
8 changes: 4 additions & 4 deletions libs/jsruntime/src/builtins/function/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Function

* [x] [Function](https://tc39.es/ecma262/#sec-function-p1-p2-pn-body)
* [ ] [Function.prototype.apply](https://tc39.es/ecma262/#sec-function.prototype.apply)
* [ ] [Function.prototype.bind](https://tc39.es/ecma262/#sec-function.prototype.bind)
* [ ] [Function.prototype.call](https://tc39.es/ecma262/#sec-function.prototype.call)
* [x] [Function.prototype.apply](https://tc39.es/ecma262/#sec-function.prototype.apply)
* [x] [Function.prototype.bind](https://tc39.es/ecma262/#sec-function.prototype.bind)
* [x] [Function.prototype.call](https://tc39.es/ecma262/#sec-function.prototype.call)
* [ ] [Function.prototype.constructor](https://tc39.es/ecma262/#sec-function.prototype.constructor)
* [ ] [Function.prototype.toString](https://tc39.es/ecma262/#sec-function.prototype.tostring)
* [x] [Function.prototype.toString](https://tc39.es/ecma262/#sec-function.prototype.tostring)
* [ ] [Function.prototype \[ %Symbol.hasInstance% \]](https://tc39.es/ecma262/#sec-function.prototype-%symbol.hasinstance%)
177 changes: 176 additions & 1 deletion libs/jsruntime/src/builtins/function/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,32 @@
//$class Function
//$inherits object

use jsgc::HandleMut;
use jsparser::Symbol;

use crate::Error;
use crate::LambdaId;
use crate::Runtime;
use crate::logger;
use crate::types::CallContext;
use crate::types::Closure;
use crate::types::Object;
use crate::types::Status;
use crate::types::Value;

use super::logger;

macro_rules! catch {
($result:expr; $runtime:expr, $retv:expr) => {
match $result {
Ok(v) => v,
Err(err) => {
*$retv = $runtime.create_exception(err);
return Status::Exception;
}
}
};
}

//#sec-function-p1-p2-pn-body constructor
pub fn constructor<X>(
_runtime: &mut Runtime<X>,
Expand All @@ -16,3 +36,158 @@ pub fn constructor<X>(
logger::debug!(event = "function");
runtime_todo!("TODO: Function constructor")
}

//#sec-function.prototype.apply prototype.function { "no_adapter": true }
pub fn function_prototype_apply<X>(
runtime: &mut Runtime<X>,
context: &mut CallContext,
retv: &mut Value,
) -> Status {
logger::debug!(event = "function_prototype_apply");
let func = catch!(runtime.this_func(context.this()); runtime, retv);
let this = context.arg(0);
match context.arg(1) {
Value::None => unreachable!(),
Value::Undefined | Value::Null => {
// TODO: PrepareForTailCall()
runtime.call(context, func, this, &[], retv)
}
args => {
let args = catch!(runtime.create_vec_from_array_like(args); runtime, retv);
// TODO: PrepareForTailCall()
runtime.call(context, func, this, &args, retv)
}
}
}

//#sec-function.prototype.bind prototype.function
pub fn function_prototype_bind<X>(
runtime: &mut Runtime<X>,
context: &mut CallContext,
) -> Result<Value, Error> {
logger::debug!(event = "function_prototype_bind");
let target = runtime.this_func(context.this())?;
let this = context.arg(0);
let args = &context.args()[1..];
let mut bound_func = runtime.bound_function_create(target, this, args)?;
let length = match target.get_own_property(&Symbol::LENGTH.into()) {
Some(prop) => {
let target_length = runtime.value_to_length(prop.value())?;
if target_length > args.len() as u64 {
target_length - args.len() as u64
} else {
0
}
}
None => 0,
};
debug_assert!(length <= u16::MAX as u64);
runtime.set_function_length(&mut bound_func, length as u16);
let target_name = target
.get_value(&Symbol::NAME.into())
.unwrap_or(&Value::Undefined);
if let Value::String(name) = target_name {
runtime.set_function_name(&mut bound_func, *name);
}
Ok(Value::Object(bound_func))
}

//#sec-function.prototype.call prototype.function { "no_adapter": true }
pub fn function_prototype_call<X>(
runtime: &mut Runtime<X>,
context: &mut CallContext,
retv: &mut Value,
) -> Status {
logger::debug!(event = "function_prototype_call");
let func = catch!(runtime.this_func(context.this()); runtime, retv);
let this = context.arg(0);
let args = &context.args()[1..];
// TODO: PrepareForTailCall()
runtime.call(context, func, this, args, retv)
}

//#sec-function.prototype.tostring prototype.function
pub fn function_prototype_to_string<X>(
_runtime: &mut Runtime<X>,
_context: &mut CallContext,
) -> Result<Value, Error> {
logger::debug!(event = "function_prototype_to_string");
runtime_todo!("TODO: Function.prototype.toString()")
}

impl<X> Runtime<X> {
fn this_func(&mut self, this: &Value) -> Result<HandleMut<Object>, Error> {
match this {
Value::None => unreachable!(),
Value::Object(v) if v.is_callable() => Ok(*v),
_ => type_error!(),
}
}

// #sec-boundfunctioncreate
fn bound_function_create(
&mut self,
func: HandleMut<Object>,
this: &Value,
args: &[Value],
) -> Result<HandleMut<Object>, Error> {
extern "C" fn bound_function<X>(
runtime: &mut Runtime<X>,
context: &mut CallContext,
retv: &mut Value,
) -> Status {
let closure = context.closure();
let (func, this, mut args) = closure.get_bound_function_params();
args.extend_from_slice(context.args());
// TODO(feat): [[Construct]], newTarget
runtime.call(context, func, this, &args, retv)
}

let prototype = func.prototype();
let mut obj = self.create_object();
if let Some(prototype) = prototype {
obj.set_prototype(prototype);
}

let num_captures = 2 + args.len() as u16;
let mut closure = self.create_closure(bound_function::<X>, LambdaId::HOST, num_captures);

let mut func = Value::Object(func);
let mut capture = self.create_capture(&mut func as *mut Value);
capture.escape();
closure.put_capture(0, capture);

let mut this = this.clone();
let mut capture = self.create_capture(&mut this as *mut Value);
capture.escape();
closure.put_capture(1, capture);

for (i, arg) in args.iter().enumerate() {
let mut arg = arg.clone();
let mut capture = self.create_capture(&mut arg as *mut Value);
capture.escape();
closure.put_capture(2 + i, capture);
}

obj.set_closure(closure);

Ok(obj)
}
}

impl Closure {
fn get_bound_function_params(&self) -> (HandleMut<Object>, &Value, Vec<Value>) {
let captures = self.captures();
debug_assert!(captures.len() >= 2);
let func = match captures.first().expect("[[BoundTargetFunction]]").value() {
Value::Object(v) => *v,
_ => unreachable!(),
};
let this = captures.get(1).expect("[[BoundThis]]").value();
let args: Vec<Value> = captures[2..]
.iter()
.map(|capture| capture.value().clone())
.collect();
(func, this, args)
}
}
2 changes: 1 addition & 1 deletion libs/jsruntime/src/builtins/global/imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub fn define_function_constructor<X>(runtime: &mut Runtime<X>) {
//#_internalerror global.constructor { "name": "InternalError" }
pub fn define_internal_error_constructor<X>(runtime: &mut Runtime<X>) {
let constructor = runtime.create_internal_error_constructor();
runtime.define_constructor(Symbol::FUNCTION, constructor);
runtime.define_constructor(Symbol::INTERNAL_ERROR, constructor);
}

//#sec-constructor-properties-of-the-global-object-object global.constructor
Expand Down
3 changes: 2 additions & 1 deletion libs/jsruntime/src/builtins/global/mod.rs.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
// This file was automagically generated with:
// template: {{@template}}

logging::define_logger! {"bee::jsruntime::builtins::global"}

mod imp;

use jsgc::HandleMut;
use jsparser::Symbol;

use crate::Runtime;
use crate::logger;
use crate::types::CallContext;
use crate::types::Object;
use crate::types::Property;
Expand Down
2 changes: 1 addition & 1 deletion libs/jsruntime/src/builtins/imp.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function dataStream(impRs) {
log.error(`Incorrect ID line: ${line}`);
Deno.exit(1);
}
let options = undefined;
let options = {};
if (parts.length > 2) {
options = JSON.parse(parts.slice(2).join(' '));
}
Expand Down
Loading
Loading