1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:37:35 +00:00

LibJS: Initial class implementation; allow super expressions in object

literal methods; add EnvrionmentRecord fields and methods to
LexicalEnvironment

Adding EnvrionmentRecord's fields and methods lets us throw an exception
when |this| is not initialized, which occurs when the super constructor
in a derived class has not yet been called, or when |this| has already
been initialized (the super constructor was already called).
This commit is contained in:
Jack Karamanian 2020-06-08 13:31:21 -05:00 committed by Andreas Kling
parent a535d58cac
commit 7533fd8b02
18 changed files with 967 additions and 92 deletions

View file

@ -74,7 +74,7 @@ Value BigIntConstructor::call(Interpreter& interpreter)
Value BigIntConstructor::construct(Interpreter& interpreter)
{
interpreter.throw_exception<TypeError>(ErrorType::NotACtor, "BigInt");
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, "BigInt");
return {};
}

View file

@ -36,6 +36,7 @@
M(BigIntBadOperatorOtherType, "Cannot use %s operator with BigInt and other type") \
M(BigIntIntArgument, "BigInt argument must be an integer") \
M(BigIntInvalidValue, "Invalid value for BigInt: %s") \
M(ClassDoesNotExtendAConstructorOrNull, "Class extends value %s is not a constructor or null") \
M(Convert, "Cannot convert %s to %s") \
M(ConvertUndefinedToObject, "Cannot convert undefined to object") \
M(DescChangeNonConfigurable, "Cannot change attributes of non-configurable property '%s'") \
@ -51,7 +52,7 @@
M(JsonCircular, "Cannot stringify circular object") \
M(JsonMalformed, "Malformed JSON string") \
M(NotA, "Not a %s object") \
M(NotACtor, "%s is not a constructor") \
M(NotAConstructor, "%s is not a constructor") \
M(NotAFunction, "%s is not a function") \
M(NotAFunctionNoParam, "Not a function") \
M(NotAn, "Not an %s object") \
@ -63,6 +64,8 @@
M(ObjectSetPrototypeOfReturnedFalse, "Object's [[SetPrototypeOf]] method returned false") \
M(ObjectSetPrototypeOfTwoArgs, "Object.setPrototypeOf requires at least two arguments") \
M(ObjectPreventExtensionsReturnedFalse, "Object's [[PreventExtensions]] method returned false") \
M(ObjectPrototypeNullOrUndefinedOnSuperPropertyAccess, \
"Object prototype must not be %s on a super property access") \
M(ObjectPrototypeWrongType, "Prototype must be an object or null") \
M(ProxyCallWithNew, "Proxy must be called with the 'new' operator") \
M(ProxyConstructorBadType, "Expected %s argument of Proxy constructor to be object, got %s") \
@ -138,6 +141,8 @@
M(ReflectBadDescriptorArgument, "Descriptor argument is not an object") \
M(StringRawCannotConvert, "Cannot convert property 'raw' to object from %s") \
M(StringRepeatCountMustBe, "repeat count must be a %s number") \
M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
M(ThisIsAlreadyInitialized, "|this| is already initialized") \
M(ToObjectNullOrUndef, "ToObject on null or undefined") \
M(UnknownIdentifier, "'%s' is not defined") \
/* LibWeb bindings */ \

View file

@ -35,6 +35,11 @@ class Function : public Object {
JS_OBJECT(Function, Object);
public:
enum class ConstructorKind {
Base,
Derived,
};
virtual ~Function();
virtual void initialize(Interpreter&, GlobalObject&) override { }
@ -49,15 +54,15 @@ public:
BoundFunction* bind(Value bound_this_value, Vector<Value> arguments);
Value bound_this() const
{
return m_bound_this;
}
Value bound_this() const { return m_bound_this; }
const Vector<Value>& bound_arguments() const
{
return m_bound_arguments;
}
const Vector<Value>& bound_arguments() const { return m_bound_arguments; }
Value home_object() const { return m_home_object; }
void set_home_object(Value home_object) { m_home_object = home_object; }
ConstructorKind constructor_kind() const { return m_constructor_kind; };
void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; }
protected:
explicit Function(Object& prototype);
@ -67,6 +72,8 @@ private:
virtual bool is_function() const final { return true; }
Value m_bound_this;
Vector<Value> m_bound_arguments;
Value m_home_object;
ConstructorKind m_constructor_kind = ConstructorKind::Base;
};
}

View file

@ -24,7 +24,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/LexicalEnvironment.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
@ -32,12 +36,24 @@ LexicalEnvironment::LexicalEnvironment()
{
}
LexicalEnvironment::LexicalEnvironment(EnvironmentRecordType environment_record_type)
: m_environment_record_type(environment_record_type)
{
}
LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, LexicalEnvironment* parent)
: m_parent(parent)
, m_variables(move(variables))
{
}
LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, LexicalEnvironment* parent, EnvironmentRecordType environment_record_type)
: m_parent(parent)
, m_variables(move(variables))
, m_environment_record_type(environment_record_type)
{
}
LexicalEnvironment::~LexicalEnvironment()
{
}
@ -46,6 +62,10 @@ void LexicalEnvironment::visit_children(Visitor& visitor)
{
Cell::visit_children(visitor);
visitor.visit(m_parent);
visitor.visit(m_this_value);
visitor.visit(m_home_object);
visitor.visit(m_new_target);
visitor.visit(m_current_function);
for (auto& it : m_variables)
visitor.visit(it.value.value);
}
@ -60,4 +80,53 @@ void LexicalEnvironment::set(const FlyString& name, Variable variable)
m_variables.set(name, variable);
}
bool LexicalEnvironment::has_super_binding() const
{
return m_environment_record_type == EnvironmentRecordType::Function && this_binding_status() != ThisBindingStatus::Lexical && m_home_object.is_object();
}
Value LexicalEnvironment::get_super_base()
{
ASSERT(has_super_binding());
if (m_home_object.is_object())
return m_home_object.as_object().prototype();
return {};
}
bool LexicalEnvironment::has_this_binding() const
{
// More like "is_capable_of_having_a_this_binding".
switch (m_environment_record_type) {
case EnvironmentRecordType::Declarative:
case EnvironmentRecordType::Object:
return false;
case EnvironmentRecordType::Function:
return this_binding_status() != ThisBindingStatus::Lexical;
case EnvironmentRecordType::Module:
case EnvironmentRecordType::Global:
return true;
}
ASSERT_NOT_REACHED();
}
Value LexicalEnvironment::get_this_binding() const
{
ASSERT(has_this_binding());
if (this_binding_status() == ThisBindingStatus::Uninitialized)
return interpreter().throw_exception<ReferenceError>(ErrorType::ThisHasNotBeenInitialized);
return m_this_value;
}
void LexicalEnvironment::bind_this_value(Value this_value)
{
ASSERT(has_this_binding());
if (m_this_binding_status == ThisBindingStatus::Initialized) {
interpreter().throw_exception<ReferenceError>(ErrorType::ThisIsAlreadyInitialized);
return;
}
m_this_value = this_value;
m_this_binding_status = ThisBindingStatus::Initialized;
}
}

View file

@ -40,11 +40,27 @@ struct Variable {
class LexicalEnvironment final : public Cell {
public:
enum class ThisBindingStatus {
Lexical,
Initialized,
Uninitialized,
};
enum class EnvironmentRecordType {
Declarative,
Function,
Global,
Object,
Module,
};
LexicalEnvironment();
LexicalEnvironment(EnvironmentRecordType);
LexicalEnvironment(HashMap<FlyString, Variable> variables, LexicalEnvironment* parent);
LexicalEnvironment(HashMap<FlyString, Variable> variables, LexicalEnvironment* parent, EnvironmentRecordType);
virtual ~LexicalEnvironment() override;
LexicalEnvironment* parent() { return m_parent; }
LexicalEnvironment* parent() const { return m_parent; }
Optional<Variable> get(const FlyString&) const;
void set(const FlyString&, Variable);
@ -53,12 +69,37 @@ public:
const HashMap<FlyString, Variable>& variables() const { return m_variables; }
void set_home_object(Value object) { m_home_object = object; }
bool has_super_binding() const;
Value get_super_base();
bool has_this_binding() const;
ThisBindingStatus this_binding_status() const { return m_this_binding_status; }
Value get_this_binding() const;
void bind_this_value(Value this_value);
// Not a standard operation.
void replace_this_binding(Value this_value) { m_this_value = this_value; }
Value new_target() const { return m_new_target; };
void set_new_target(Value new_target) { m_new_target = new_target; }
Function* current_function() const { return m_current_function; }
void set_current_function(Function& function) { m_current_function = &function; }
private:
virtual const char* class_name() const override { return "LexicalEnvironment"; }
virtual void visit_children(Visitor&) override;
LexicalEnvironment* m_parent { nullptr };
HashMap<FlyString, Variable> m_variables;
EnvironmentRecordType m_environment_record_type = EnvironmentRecordType::Declarative;
ThisBindingStatus m_this_binding_status = ThisBindingStatus::Uninitialized;
Value m_home_object;
Value m_this_value;
Value m_new_target;
// Corresponds to [[FunctionObject]]
Function* m_current_function { nullptr };
};
}

View file

@ -68,4 +68,9 @@ Value NativeFunction::construct(Interpreter&)
return {};
}
LexicalEnvironment* NativeFunction::create_environment()
{
return interpreter().heap().allocate<LexicalEnvironment>(global_object(), LexicalEnvironment::EnvironmentRecordType::Function);
}
}

View file

@ -53,7 +53,7 @@ protected:
private:
virtual bool is_native_function() const override { return true; }
virtual LexicalEnvironment* create_environment() override final { return nullptr; }
virtual LexicalEnvironment* create_environment() override final;
FlyString m_name;
AK::Function<Value(Interpreter&, GlobalObject&)> m_native_function;

View file

@ -437,7 +437,7 @@ bool Object::define_accessor(PropertyName property_name, Function& getter_or_set
accessor = &existing_property.as_accessor();
}
if (!accessor) {
accessor = Accessor::create(interpreter(), nullptr, nullptr);
accessor = Accessor::create(interpreter(), global_object(), nullptr, nullptr);
bool definition_success = define_property(property_name, accessor, attributes, throw_exceptions);
if (interpreter().exception())
return {};

View file

@ -66,8 +66,8 @@ ScriptFunction::ScriptFunction(GlobalObject& global_object, const FlyString& nam
void ScriptFunction::initialize(Interpreter& interpreter, GlobalObject& global_object)
{
Function::initialize(interpreter, global_object);
if (!is_arrow_function) {
Object* prototype = Object::create_empty(interpreter(), interpreter().global_object());
if (!m_is_arrow_function) {
Object* prototype = Object::create_empty(interpreter, global_object);
prototype->define_property("constructor", this, Attribute::Writable | Attribute::Configurable);
define_property("prototype", prototype, 0);
}
@ -99,9 +99,11 @@ LexicalEnvironment* ScriptFunction::create_environment()
}
}
}
if (variables.is_empty())
return m_parent_environment;
return heap().allocate<LexicalEnvironment>(global_object(), move(variables), m_parent_environment);
auto* environment = heap().allocate<LexicalEnvironment>(global_object(), move(variables), m_parent_environment, LexicalEnvironment::EnvironmentRecordType::Function);
environment->set_home_object(home_object());
environment->set_current_function(*this);
return environment;
}
Value ScriptFunction::call(Interpreter& interpreter)
@ -134,7 +136,7 @@ Value ScriptFunction::call(Interpreter& interpreter)
Value ScriptFunction::construct(Interpreter& interpreter)
{
if (m_is_arrow_function)
return interpreter.throw_exception<TypeError>(ErrorType::NotACtor, m_name.characters());
return interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, m_name.characters());
return call(interpreter);
}

View file

@ -76,7 +76,7 @@ Value SymbolConstructor::call(Interpreter& interpreter)
Value SymbolConstructor::construct(Interpreter& interpreter)
{
interpreter.throw_exception<TypeError>(ErrorType::NotACtor, "Symbol");
interpreter.throw_exception<TypeError>(ErrorType::NotAConstructor, "Symbol");
return {};
}