Skip to content

Commit

Permalink
Fix class inherit from null
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Sep 26, 2023
1 parent 25c120b commit 721e5db
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 25 deletions.
2 changes: 2 additions & 0 deletions boa_engine/src/vm/opcode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,8 @@ generate_opcodes! {

/// Get the prototype of a superclass and push it on the stack.
///
/// Additionally this sets the `[[prototype]]` of the class and the `DERIVED` flag.
///
/// Operands:
///
/// Stack: class, superclass **=>** class, superclass.prototype
Expand Down
75 changes: 50 additions & 25 deletions boa_engine/src/vm/opcode/push/class/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,42 +25,67 @@ impl Operation for PushClassPrototype {

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let superclass = context.vm.pop();
let class = context.vm.pop();

if let Some(superclass) = superclass.as_constructor() {
// // Taken from `15.7.14 Runtime Semantics: ClassDefinitionEvaluation`:
// <https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation>
//
// 8. Else
// f. If superclass is null, then
let (proto_parent, constructor_parent) = if superclass.is_null() {
// i. Let protoParent be null.
// ii. Let constructorParent be %Function.prototype%.
//
// NOTE(HalidOdat): We set constructorParent to None, it is resolved in `SetClassPrototype` opcode.
(JsValue::null(), None)

// h. Else,
} else if let Some(superclass) = superclass.as_constructor() {
// i. Let protoParent be ? Get(superclass, "prototype").
let proto = superclass.get(PROTOTYPE, context)?;

// ii. If protoParent is not an Object and protoParent is not null, throw a TypeError exception.
if !proto.is_object() && !proto.is_null() {
return Err(JsNativeError::typ()
.with_message("superclass prototype must be an object or null")
.into());
}

let class = context.vm.pop();
{
let class_object = class.as_object().expect("class must be object");
class_object.set_prototype(Some(superclass.clone()));

let mut class_object_mut = class_object.borrow_mut();
let class_function = class_object_mut
.as_function_mut()
.expect("class must be function object");
if let FunctionKind::Ordinary {
constructor_kind, ..
} = class_function.kind_mut()
{
*constructor_kind = ConstructorKind::Derived;
}
}
// iii. Let constructorParent be superclass.
(proto, Some(superclass.clone()))

context.vm.push(class);
context.vm.push(proto);
Ok(CompletionType::Normal)
} else if superclass.is_null() {
context.vm.push(JsValue::Null);
Ok(CompletionType::Normal)
// g. Else if IsConstructor(superclass) is false, then
} else {
Err(JsNativeError::typ()
// i. Throw a TypeError exception.
return Err(JsNativeError::typ()
.with_message("superclass must be a constructor")
.into())
.into());
};

let class_object = class.as_object().expect("class must be object");

if let Some(constructor_parent) = constructor_parent {
class_object.set_prototype(Some(constructor_parent));
}

let mut class_object_mut = class_object.borrow_mut();
let class_function = class_object_mut
.as_function_mut()
.expect("class must be function object");

// 17. If ClassHeritageopt is present, set F.[[ConstructorKind]] to derived.
if let FunctionKind::Ordinary {
constructor_kind, ..
} = class_function.kind_mut()
{
*constructor_kind = ConstructorKind::Derived;
}

drop(class_object_mut);

context.vm.push(class);
context.vm.push(proto_parent);

Ok(CompletionType::Normal)
}
}
1 change: 1 addition & 0 deletions boa_engine/src/vm/opcode/set/class_prototype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl Operation for SetClassPrototype {
_ => unreachable!(),
};

// 9.Let proto be OrdinaryObjectCreate(protoParent).
let proto = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
prototype,
Expand Down

0 comments on commit 721e5db

Please sign in to comment.