Skip to content
Draft
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
81 changes: 72 additions & 9 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ mod debug;
mod helper;

use boa_engine::context::time::JsInstant;
use boa_engine::interop::JsThis;
use boa_engine::job::{GenericJob, TimeoutJob};
use boa_engine::{
Context, JsError, JsResult, Source,
Context, Finalize, JsData, JsError, JsObject, JsResult, JsValue, Source, Trace, boa_class,
builtins::promise::PromiseState,
context::ContextBuilder,
job::{Job, JobExecutor, NativeAsyncJob, PromiseJob},
js_string,
module::{Module, SimpleModuleLoader},
optimizer::OptimizerOptions,
script::Script,
Expand All @@ -30,6 +32,13 @@ use color_eyre::{
};
use colored::Colorize;
use debug::init_boa_debug_object;
#[cfg(all(
target_arch = "x86_64",
target_os = "linux",
target_env = "gnu",
feature = "dhat"
))]
use jemallocator as _;
use rustyline::{EditMode, Editor, config::Config, error::ReadlineError};
use std::collections::BTreeMap;
use std::{
Expand All @@ -43,14 +52,6 @@ use std::{
rc::Rc,
};

#[cfg(all(
target_arch = "x86_64",
target_os = "linux",
target_env = "gnu",
feature = "dhat"
))]
use jemallocator as _;

#[cfg(all(
target_arch = "x86_64",
target_os = "linux",
Expand Down Expand Up @@ -381,6 +382,66 @@ fn evaluate_files(args: &Opt, context: &mut Context, loader: &SimpleModuleLoader
Ok(())
}

fn init_debug_stuff_do_not_merge(context: &mut Context) {
#[derive(Debug, Trace, Finalize, JsData)]
struct X;

#[boa_class(extends = "Base")]
impl X {
#[boa(constructor)]
#[boa(length = 0)]
fn new(this: JsThis<JsObject>, context: &mut Context) -> Self {
Self
}

fn foo(JsThis(this): JsThis<JsObject>, context: &mut Context) -> u32 {
eprintln!("this: {}", JsValue::new(this.clone()).display());

eprintln!(
"zthis.foo: {}",
boa_engine::JsValue::from(this.get(js_string!("foo"), context).unwrap())
.display()
.to_string()
);
eprintln!(
"zthis.baseFoo: {}",
boa_engine::JsValue::from(this.get(js_string!("baseFoo"), context).unwrap())
.display()
.to_string()
);
eprintln!(
"zthis.proto: {}",
boa_engine::JsValue::from(this.prototype().unwrap()).display_obj(true)
);

this.get(js_string!("baseFoo"), context)
.unwrap()
.as_callable()
.expect("as callable")
.call(&this.clone().into(), &[JsValue::from(1)], context)
.expect("baseFoo() call")
.to_u32(context)
.expect("to_u32")
+ 1
}
}

context
.eval(Source::from_bytes(
r"
class Base {
static baseStatic() { return 'hello'; }
baseFoo(a) { return a + 1 }
}
",
))
.expect("eval failed");

context
.register_global_class::<X>()
.expect("global_class registration");
}

fn main() -> Result<()> {
color_eyre::config::HookBuilder::default()
.display_location_section(false)
Expand Down Expand Up @@ -413,6 +474,8 @@ fn main() -> Result<()> {
init_boa_debug_object(&mut context);
}

init_debug_stuff_do_not_merge(&mut context);

// Configure optimizer options
let mut optimizer_options = OptimizerOptions::empty();
optimizer_options.set(OptimizerOptions::STATISTICS, args.optimizer_statistics);
Expand Down
18 changes: 18 additions & 0 deletions core/engine/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,15 @@ fn function_construct(
argument_count: usize,
context: &mut InternalMethodCallContext<'_>,
) -> JsResult<CallValue> {
eprintln!(
"function_construct {} {}",
JsValue::from(this_function_object.clone()).display(),
argument_count
);
eprintln!(
"function_construct proto: {}",
JsValue::from(this_function_object.prototype().unwrap()).display()
);
context.check_runtime_limits()?;

let function = this_function_object
Expand All @@ -1100,6 +1109,11 @@ fn function_construct(
let env_fp = environments.len() as u32;

let new_target = context.vm.stack.pop();
eprintln!(
"function_construct new_target: {} {}",
new_target.display_obj(false),
argument_count
);

let this = if code.is_derived_constructor() {
None
Expand All @@ -1110,6 +1124,10 @@ fn function_construct(
// see <https://tc39.es/ecma262/#sec-getprototypefromconstructor>
let prototype =
get_prototype_from_constructor(&new_target, StandardConstructors::object, context)?;
eprintln!(
"function_construct proto: {}",
JsValue::from(prototype.clone()).display()
);
let this = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
Expand Down
52 changes: 43 additions & 9 deletions core/engine/src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ use crate::{
object::{ConstructorBuilder, FunctionBinding, JsFunction, JsObject, NativeObject, PROTOTYPE},
property::{Attribute, PropertyDescriptor, PropertyKey},
};
use boa_engine::object::JsPrototype;
use boa_parser::Source;

/// Native class.
///
Expand Down Expand Up @@ -161,11 +163,16 @@ pub trait Class: NativeObject + Sized {
/// could lead to weird errors like missing inherited methods or incorrect internal data.
/// </div>
fn construct(
new_target: &JsValue,
this_function_object: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsObject> {
if new_target.is_undefined() {
eprintln!(
"Class::construct {} {}",
Self::NAME,
this_function_object.display()
);
if this_function_object.is_undefined() {
return Err(JsNativeError::typ()
.with_message(format!(
"cannot call constructor of native class `{}` without new",
Expand All @@ -175,10 +182,14 @@ pub trait Class: NativeObject + Sized {
}

let prototype = 'proto: {
let realm = if let Some(constructor) = new_target.as_object() {
if let Some(proto) = constructor.get(PROTOTYPE, context)?.as_object() {
break 'proto proto.clone();
}
let realm = if let Some(constructor) = this_function_object.as_object() {
// if let Some(proto) = constructor.get(PROTOTYPE, context)?.as_object() {
// eprintln!(
// "construct: {}",
// JsValue::from(proto.clone()).display().to_string()
// );
// break 'proto proto.clone();
// }
constructor.get_function_realm(context)?
} else {
context.realm().clone()
Expand All @@ -193,11 +204,20 @@ pub trait Class: NativeObject + Sized {
})?
.prototype()
};
let r = context.realm();

let data = Self::data_constructor(new_target, args, context)?;
let data = Self::data_constructor(this_function_object, args, context)?;

let object =
JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), prototype, data);
eprintln!(
"Class::construct proto: {}",
JsValue::from(prototype.clone()).display()
);
let object = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype.clone(),
data,
);
object.set_prototype(Some(prototype));

Self::object_constructor(&object, args, context)?;

Expand Down Expand Up @@ -264,6 +284,20 @@ impl<'ctx> ClassBuilder<'ctx> {
self.builder.build()
}

/// Set the prototype of the class's constructor, making it a subclass.
pub fn extends<T>(&mut self, value: T) -> &mut Self
where
T: Into<JsPrototype>,
{
let p = value.into();
eprintln!("ClassBuilder::extends {:?}", p);
self.builder.inherit(p.clone());
if let Some(p) = p {
self.builder.custom_prototype(p.prototype());
}
self
}

/// Add a method to the class.
///
/// It is added to `prototype`.
Expand Down
7 changes: 6 additions & 1 deletion core/engine/src/context/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use boa_gc::{Finalize, Trace};

use crate::{
JsSymbol,
JsSymbol, JsValue,
builtins::{Array, OrdinaryObject, iterable::IteratorPrototypes, uri::UriFunctions},
js_string,
object::{
Expand Down Expand Up @@ -88,6 +88,11 @@ impl Default for StandardConstructor {
impl StandardConstructor {
/// Creates a new `StandardConstructor` from the constructor and the prototype.
pub(crate) fn new(constructor: JsFunction, prototype: JsObject) -> Self {
eprintln!(
"StandardConstructor::new\n ctor: {constructor:?}\n ctor.proto: {}\n prototype: {}",
JsValue::from(constructor.prototype().clone().unwrap()).display(),
JsValue::from(prototype.clone()).display(),
);
Self {
constructor,
prototype,
Expand Down
29 changes: 29 additions & 0 deletions core/engine/src/interop/into_js_arguments.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::builtins::TypeError;
use crate::js_error;
use boa_engine::object::Object;
use boa_engine::value::TryFromJs;
use boa_engine::{Context, JsNativeError, JsObject, JsResult, JsValue, NativeObject};
Expand Down Expand Up @@ -205,6 +207,33 @@ impl<'a, T: TryFromJs> TryFromJsArgument<'a> for JsAll<T> {
}
}

/// Captures the `super` keyword in a JS class. This will only work on
/// classes. It can be typed or not.
#[derive(Debug, Clone)]
pub struct JsSuper(JsObject);

impl JsSuper {
/// Equivalent of calling `super(...)` in the constructor. It is undefined
/// behaviour to call this in
pub fn call(&self, args: &[JsValue], context: &mut Context) -> JsResult<()> {
self.0.construct(args, Some(&self.0), context)?;
Ok(())
}
}

impl<'a> TryFromJsArgument<'a> for JsSuper {
fn try_from_js_argument(
this: &'a JsValue,
rest: &'a [JsValue],
_context: &mut Context,
) -> JsResult<(Self, &'a [JsValue])> {
let o = this
.as_object()
.ok_or_else(|| js_error!(TypeError: "this must be an object"))?;
Ok((Self(o), rest))
}
}

/// Captures the `this` value in a JS function. Although this can be
/// specified multiple times as argument, it will always be filled
/// with clone of the same value.
Expand Down
Loading