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
123 changes: 123 additions & 0 deletions src/agent/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3793,6 +3793,127 @@ mod insn_impl {
push_completion(result.map(NormalCompletion::from)).expect(PUSHABLE);
Ok(())
}

pub(crate) fn define_async_method(chunk: &Rc<Chunk>) -> anyhow::Result<()> {
// Takes two args: idx into function stash and enumerable flag
// stack input: propkey object
// output: err/empty/PrivateElement
let info = sfd_operand(chunk)?;
let enumerable = usize_operand(chunk)? != 0;
let prop_key = pop_functionname()?;
let base_object = pop_obj()?;

// 2. Let envRecord be the LexicalEnvironment of the running execution context.
let env_record = current_lexical_environment().expect("environment should exist");
// 3. Let privateEnv be the running execution context's PrivateEnvironment.
let priv_env = current_private_environment();
// 4. Let sourceText be the source text matched by AsyncMethod.
// 5. Let closure be OrdinaryFunctionCreate(%AsyncFunction.prototype%, sourceText, UniqueFormalParameters, AsyncFunctionBody, non-lexical-this, envRecord, privateEnv).
let to_compile: Rc<AsyncMethod> = info.to_compile.clone().try_into()?;
let fb = &to_compile.body;
let prod_text_loc = to_compile.location().span;
let prod_text =
&info.parent_tree.text[prod_text_loc.starting_index..prod_text_loc.starting_index + prod_text_loc.length];
let chunk_name = nameify(prod_text, 50);
let mut compiled = Chunk::new(chunk_name, to_compile.location().starting_line);
let compilation_status = fb.compile_body(&mut compiled, &info.parent_tree, info);
if let Err(err) = compilation_status {
let typeerror = create_type_error(err.to_string());
push_completion(Err(typeerror)).expect(PUSHABLE);
return Ok(());
}

#[cfg(debug_assertions)]
for line in compiled.disassemble(&info.parent_tree.text) {
println!("{line}");
}

let prototype = intrinsic(IntrinsicId::AsyncFunctionPrototype);
let closure = ordinary_function_create(
prototype,
&info.source_text,
info.params.clone(),
info.body.clone(),
info.this_mode,
env_record,
priv_env,
info.strict,
Rc::new(compiled),
);
make_method(closure.o.to_function_obj().unwrap(), base_object.clone());
super::set_function_name(&closure, prop_key.clone(), Some("set".into()));

// 6. Perform MakeMethod(closure, obj).
// 7. Perform SetFunctionName(closure, propertyKey).
// 8. Return ? DefineMethodProperty(obj, propertyKey, closure, enumerable).
let rval =
super::define_method_property(&base_object, prop_key, closure, enumerable).map(NormalCompletion::from);
push_completion(rval).expect(PUSHABLE);
Ok(())
}
pub(crate) fn define_async_generator(chunk: &Rc<Chunk>) -> anyhow::Result<()> {
// Takes two args: idx into function stash and enumerable flag
// stack input: propkey object
// output: err/empty/PrivateElement
let info = sfd_operand(chunk)?;
let enumerable = usize_operand(chunk)? != 0;
let prop_key = pop_functionname()?;
let base_object = pop_obj()?;

// 2. Let envRecord be the LexicalEnvironment of the running execution context.
let env_record = current_lexical_environment().expect("environment should exist");
// 3. Let privateEnv be the running execution context's PrivateEnvironment.
let priv_env = current_private_environment();
// 4. Let sourceText be the source text matched by AsyncMethod.
// 5. Let closure be OrdinaryFunctionCreate(%AsyncGeneratorFunction.prototype%, sourceText, UniqueFormalParameters, AsyncFunctionBody, non-lexical-this, envRecord, privateEnv).
let to_compile: Rc<AsyncGeneratorMethod> = info.to_compile.clone().try_into()?;
let fb = &to_compile.body;
let prod_text_loc = to_compile.location().span;
let prod_text =
&info.parent_tree.text[prod_text_loc.starting_index..prod_text_loc.starting_index + prod_text_loc.length];
let chunk_name = nameify(prod_text, 50);
let mut compiled = Chunk::new(chunk_name, to_compile.location().starting_line);
let compilation_status = fb.compile_body(&mut compiled, &info.parent_tree, info);
if let Err(err) = compilation_status {
let typeerror = create_type_error(err.to_string());
push_completion(Err(typeerror)).expect(PUSHABLE);
return Ok(());
}

#[cfg(debug_assertions)]
for line in compiled.disassemble(&info.parent_tree.text) {
println!("{line}");
}

let prototype = intrinsic(IntrinsicId::AsyncGeneratorFunctionPrototype);
let closure = ordinary_function_create(
prototype,
&info.source_text,
info.params.clone(),
info.body.clone(),
info.this_mode,
env_record,
priv_env,
info.strict,
Rc::new(compiled),
);
make_method(closure.o.to_function_obj().unwrap(), base_object.clone());
super::set_function_name(&closure, prop_key.clone(), Some("set".into()));

let protoproto = intrinsic(IntrinsicId::AsyncGeneratorFunctionPrototypePrototype);
define_property_or_throw(
&closure,
"prototype",
PotentialPropertyDescriptor::new().value(protoproto).writable(true),
)
.expect(GOODOBJ);

// 8. Return ? DefineMethodProperty(obj, propertyKey, closure, enumerable).
let rval =
super::define_method_property(&base_object, prop_key, closure, enumerable).map(NormalCompletion::from);
push_completion(rval).expect(PUSHABLE);
Ok(())
}
pub(crate) fn generator_start_from_function(source: &Rc<SourceTree>) -> anyhow::Result<()> {
// 2. Let G be ? OrdinaryCreateFromConstructor(functionObject,
// "%GeneratorFunction.prototype.prototype%", « [[GeneratorState]], [[GeneratorContext]],
Expand Down Expand Up @@ -4847,6 +4968,8 @@ pub(crate) async fn execute(
Insn::DefineMethodProperty => insn_impl::define_method_property(&chunk).expect(GOODCODE),
Insn::DefineGetter => insn_impl::define_getter(&chunk).expect(GOODCODE),
Insn::DefineSetter => insn_impl::define_setter(&chunk).expect(GOODCODE),
Insn::DefineAsyncGenerator => insn_impl::define_async_generator(&chunk).expect(GOODCODE),
Insn::DefineAsyncMethod => insn_impl::define_async_method(&chunk).expect(GOODCODE),
Insn::GetParentsFromSuperclass => insn_impl::get_parents_from_superclass().expect(GOODCODE),
Insn::CreateDefaultConstructor => insn_impl::create_default_constructor().expect(GOODCODE),
Insn::MakeClassConstructorAndSetName => insn_impl::make_class_constructor_and_set_name().expect(GOODCODE),
Expand Down
6 changes: 5 additions & 1 deletion src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,11 @@ impl Chunk {
let flags_arg = self.opcodes[idx + 1] as usize;
(3, format!(" {insn:<24}/{}/{}", self.strings[pattern_arg], self.strings[flags_arg]))
}
Insn::DefineGetter | Insn::DefineSetter | Insn::InstantiateGeneratorMethod => {
Insn::DefineGetter
| Insn::DefineSetter
| Insn::InstantiateGeneratorMethod
| Insn::DefineAsyncMethod
| Insn::DefineAsyncGenerator => {
let arg = self.opcodes[idx] as usize;
let flag = self.opcodes[idx + 1] != 0;
(3, format!(" {:<24}{} {}", insn, arg, if flag { "enumerable" } else { "hidden" }))
Expand Down
106 changes: 104 additions & 2 deletions src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub(crate) enum Insn {
CreateUnmappedArguments,
Debugger,
Decrement,
DefineAsyncGenerator,
DefineAsyncMethod,
DefineGetter,
DefineMethod,
DefineMethodProperty,
Expand Down Expand Up @@ -407,6 +409,8 @@ impl fmt::Display for Insn {
Insn::EvaluateInitializedClassStaticFieldDefinition => "EVAL_CLS_STC_FLD_DEF",
Insn::EvaluateClassStaticBlockDefinition => "EVAL_CLASS_SBLK_DEF",
Insn::DefineMethod => "DEFINE_METHOD",
Insn::DefineAsyncMethod => "DEFINE_ASYNC_METH",
Insn::DefineAsyncGenerator => "DEFINE_ASYNC_GEN",
Insn::SetFunctionName => "SET_FUNC_NAME",
Insn::DefineMethodProperty => "DEF_METH_PROP",
Insn::DefineGetter => "DEF_GETTER",
Expand Down Expand Up @@ -1246,6 +1250,28 @@ impl AsyncFunctionExpression {
}
}

impl AsyncGeneratorBody {
pub(crate) fn compile_body(
&self,
chunk: &mut Chunk,
source: &Rc<SourceTree>,
info: &StashedFunctionData,
) -> anyhow::Result<CompilerStatusFlags> {
self.compile_evaluate_async_generator_body(chunk, source, info)
}

#[expect(unused_variables, clippy::unnecessary_wraps)]
pub(crate) fn compile_evaluate_async_generator_body(
&self,
chunk: &mut Chunk,
source: &Rc<SourceTree>,
info: &StashedFunctionData,
) -> anyhow::Result<CompilerStatusFlags> {
chunk.op(Insn::ToDo, self.location().starting_line);
Ok(CompilerStatusFlags::from(AlwaysAbruptResult))
}
}

impl AsyncArrowFunction {
#[cfg(test)]
pub(crate) fn params(&self) -> ParamSource {
Expand Down Expand Up @@ -12408,8 +12434,84 @@ impl MethodDefinition {
Ok(AlwaysAbruptResult)
}
MethodDefinition::Generator(r#gen) => r#gen.method_definition_evaluation(enumerable, chunk, strict, source),
MethodDefinition::Async(_) => todo!(),
MethodDefinition::AsyncGenerator(_) => todo!(),
MethodDefinition::Async(node) => {
// AsyncMethod : async ClassElementName ( UniqueFormalParameters ) { AsyncFunctionBody }
// 1. Let propertyKey be ? Evaluation of ClassElementName.
// 2. Let envRecord be the LexicalEnvironment of the running execution context.
// 3. Let privateEnv be the running execution context's PrivateEnvironment.
// 4. Let sourceText be the source text matched by AsyncMethod.
// 5. Let closure be OrdinaryFunctionCreate(%AsyncFunction.prototype%, sourceText, UniqueFormalParameters, AsyncFunctionBody, non-lexical-this, envRecord, privateEnv).
// 6. Perform MakeMethod(closure, obj).
// 7. Perform SetFunctionName(closure, propertyKey).
// 8. Return ? DefineMethodProperty(obj, propertyKey, closure, enumerable).

// start: object
// <name.evaluate> err/propKey object
// JUMP_IF_ABRUPT unwind_1 propKey object
// DEFINE_ASYNC_METH err/empty/PrivateElement
// JUMP exit
// unwind_1: err object
// UNWIND 1 err
// exit: err/empty/PrivateElement
let location = node.location();
let status = node.ident.compile(chunk, source)?;
let unwind = if status.maybe_abrupt() { Some(chunk.op_jump(Insn::JumpIfAbrupt, line)) } else { None };
let source_text = source.text
[location.span.starting_index..location.span.starting_index + location.span.length]
.to_string();
let info = StashedFunctionData {
source_text,
params: node.params.clone().into(),
body: node.body.clone().into(),
to_compile: node.clone().into(),
strict,
this_mode: ThisLexicality::NonLexicalThis,
parent_tree: source.clone(),
};
let idx = chunk.add_to_func_stash(info)?;
chunk.op_plus_two_args(Insn::DefineAsyncMethod, idx, u16::from(enumerable), line);
if let Some(unwind) = unwind {
let exit = chunk.op_jump(Insn::Jump, line);
chunk.fixup(unwind).expect("jump too short to fail");
chunk.op_plus_arg(Insn::Unwind, 1, line);
chunk.fixup(exit).expect("jump too short to fail");
}
Ok(AlwaysAbruptResult)
}
MethodDefinition::AsyncGenerator(node) => {
// start: object
// <name.evaluate> err/propKey object
// JUMP_IF_ABRUPT unwind_1 propKey object
// DEFINE_ASYNC_GEN err/empty/PrivateElement
// JUMP exit
// unwind_1: err object
// UNWIND 1 err
// exit: err/empty/PrivateElement
let location = node.location();
let status = node.name.compile(chunk, source)?;
let unwind = if status.maybe_abrupt() { Some(chunk.op_jump(Insn::JumpIfAbrupt, line)) } else { None };
let source_text = source.text
[location.span.starting_index..location.span.starting_index + location.span.length]
.to_string();
let info = StashedFunctionData {
source_text,
params: node.params.clone().into(),
body: node.body.clone().into(),
to_compile: node.clone().into(),
strict,
this_mode: ThisLexicality::NonLexicalThis,
parent_tree: source.clone(),
};
let idx = chunk.add_to_func_stash(info)?;
chunk.op_plus_two_args(Insn::DefineAsyncGenerator, idx, u16::from(enumerable), line);
if let Some(unwind) = unwind {
let exit = chunk.op_jump(Insn::Jump, line);
chunk.fixup(unwind).expect("jump too short to fail");
chunk.op_plus_arg(Insn::Unwind, 1, line);
chunk.fixup(exit).expect("jump too short to fail");
}
Ok(AlwaysAbruptResult)
}
MethodDefinition::Getter(name, body, location) => {
// MethodDefinition : get ClassElementName ( ) { FunctionBody }
// 1. Let propKey be ? Evaluation of ClassElementName.
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13828,8 +13828,8 @@ mod method_definition {
)]
#[test_case("set a(b){}", true, &[], true => Ok(svec(&["00001: set a(b){}", "STRING 0 (a)", "DEF_SETTER 0 enumerable"])); "setter")]
#[test_case("*a(){}", true, &[], true => Ok(svec(&["00001: *a(){}", "STRING 0 (a)", "GEN_METHOD 0 enumerable"])); "generator")]
#[test_case("async a(){}", true, &[], true => panics "not yet implemented"; "async function")]
#[test_case("async *a(){}", true, &[], true => panics "not yet implemented"; "async generator")]
#[test_case("async a(){}", true, &[], true => Ok(svec(&["00001: async a(){}", "STRING 0 (a)", "DEFINE_ASYNC_METH 0 enumerable"])); "async function")]
#[test_case("async *a(){}", true, &[], true => Ok(svec(&["00001: async *a(){}", "STRING 0 (a)", "DEFINE_ASYNC_GEN 0 enumerable"])); "async generator")]
fn method_definition_evaluation(
src: &str,
strict: bool,
Expand Down
Loading
Loading