mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-06 11:09:05 +03:00
LibJS: Support "hello friends".length
The above snippet is a MemberExpression that necessitates the implicit construction of a StringObject wrapper around a PrimitiveString. We then do a property lookup (a "get") on the StringObject, where we find the "length" property. This is pretty neat! :^)
This commit is contained in:
parent
6ec6fa1a02
commit
542108421e
Notes:
sideshowbarker
2024-07-19 08:46:33 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/542108421ed
@ -387,7 +387,6 @@ Value VariableDeclaration::execute(Interpreter& interpreter) const
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
|
||||
void VariableDeclaration::dump(int indent) const
|
||||
{
|
||||
ASTNode::dump(indent);
|
||||
@ -406,4 +405,26 @@ Value ObjectExpression::execute(Interpreter& interpreter) const
|
||||
return Value(interpreter.heap().allocate<Object>());
|
||||
}
|
||||
|
||||
void MemberExpression::dump(int indent) const
|
||||
{
|
||||
ASTNode::dump(indent);
|
||||
m_object->dump(indent + 1);
|
||||
m_property->dump(indent + 1);
|
||||
}
|
||||
|
||||
Value MemberExpression::execute(Interpreter& interpreter) const
|
||||
{
|
||||
auto object_result = m_object->execute(interpreter).to_object(interpreter.heap());
|
||||
ASSERT(object_result.is_object());
|
||||
|
||||
String property_name;
|
||||
if (m_property->is_identifier()) {
|
||||
property_name = static_cast<const Identifier&>(*m_property).string();
|
||||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return object_result.as_object()->get(property_name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -363,4 +363,22 @@ private:
|
||||
virtual const char* class_name() const override { return "ObjectExpression"; }
|
||||
};
|
||||
|
||||
class MemberExpression final : public Expression {
|
||||
public:
|
||||
MemberExpression(NonnullOwnPtr<Expression> object, NonnullOwnPtr<Expression> property)
|
||||
: m_object(move(object))
|
||||
, m_property(move(property))
|
||||
{
|
||||
}
|
||||
|
||||
virtual Value execute(Interpreter&) const override;
|
||||
virtual void dump(int indent) const override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "MemberExpression"; }
|
||||
|
||||
NonnullOwnPtr<Expression> m_object;
|
||||
NonnullOwnPtr<Expression> m_property;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -27,14 +27,15 @@
|
||||
#include <AK/LogStream.h>
|
||||
#include <LibJS/Cell.h>
|
||||
#include <LibJS/Object.h>
|
||||
#include <LibJS/PrimitiveString.h>
|
||||
#include <LibJS/Value.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
void Cell::Visitor::visit(Value value)
|
||||
{
|
||||
if (value.is_object())
|
||||
visit(value.as_object());
|
||||
if (value.is_cell())
|
||||
visit(value.as_cell());
|
||||
}
|
||||
|
||||
const LogStream& operator<<(const LogStream& stream, const Cell* cell)
|
||||
|
@ -25,7 +25,10 @@
|
||||
*/
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibJS/Heap.h>
|
||||
#include <LibJS/Object.h>
|
||||
#include <LibJS/PrimitiveString.h>
|
||||
#include <LibJS/StringObject.h>
|
||||
#include <LibJS/Value.h>
|
||||
|
||||
namespace JS {
|
||||
@ -50,6 +53,9 @@ String Value::to_string() const
|
||||
return String::format("{%s}", as_object()->class_name());
|
||||
}
|
||||
|
||||
if (is_string())
|
||||
return m_value.as_string->string();
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
@ -64,7 +70,7 @@ bool Value::to_boolean() const
|
||||
case Type::Undefined:
|
||||
return false;
|
||||
case Type::String:
|
||||
return String(as_string()).is_empty();
|
||||
return !as_string()->string().is_empty();
|
||||
case Type::Object:
|
||||
return true;
|
||||
default:
|
||||
@ -72,6 +78,17 @@ bool Value::to_boolean() const
|
||||
}
|
||||
}
|
||||
|
||||
Value Value::to_object(Heap& heap) const
|
||||
{
|
||||
if (is_object())
|
||||
return Value(as_object());
|
||||
|
||||
if (is_string())
|
||||
return Value(heap.allocate<StringObject>(m_value.as_string));
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
Value greater_than(Value lhs, Value rhs)
|
||||
{
|
||||
ASSERT(lhs.is_number());
|
||||
|
@ -50,6 +50,7 @@ public:
|
||||
bool is_string() const { return m_type == Type::String; }
|
||||
bool is_object() const { return m_type == Type::Object; }
|
||||
bool is_boolean() const { return m_type == Type::Boolean; }
|
||||
bool is_cell() const { return is_string() || is_object(); }
|
||||
|
||||
explicit Value(bool value)
|
||||
: m_type(Type::Boolean)
|
||||
@ -75,6 +76,12 @@ public:
|
||||
m_value.as_object = object;
|
||||
}
|
||||
|
||||
explicit Value(PrimitiveString* string)
|
||||
: m_type(Type::String)
|
||||
{
|
||||
m_value.as_string = string;
|
||||
}
|
||||
|
||||
explicit Value(Type type)
|
||||
: m_type(type)
|
||||
{
|
||||
@ -106,23 +113,38 @@ public:
|
||||
return m_value.as_object;
|
||||
}
|
||||
|
||||
const StringImpl* as_string() const
|
||||
PrimitiveString* as_string()
|
||||
{
|
||||
ASSERT(is_string());
|
||||
return m_value.as_string;
|
||||
}
|
||||
|
||||
const PrimitiveString* as_string() const
|
||||
{
|
||||
ASSERT(is_string());
|
||||
return m_value.as_string;
|
||||
}
|
||||
|
||||
Cell* as_cell()
|
||||
{
|
||||
ASSERT(is_cell());
|
||||
return m_value.as_cell;
|
||||
}
|
||||
|
||||
String to_string() const;
|
||||
bool to_boolean() const;
|
||||
|
||||
Value to_object(Heap&) const;
|
||||
|
||||
private:
|
||||
Type m_type { Type::Undefined };
|
||||
|
||||
union {
|
||||
bool as_bool;
|
||||
double as_double;
|
||||
StringImpl* as_string;
|
||||
PrimitiveString* as_string;
|
||||
Object* as_object;
|
||||
Cell* as_cell;
|
||||
} m_value;
|
||||
};
|
||||
|
||||
|
@ -28,21 +28,22 @@
|
||||
#include <LibJS/AST.h>
|
||||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Object.h>
|
||||
#include <LibJS/PrimitiveString.h>
|
||||
#include <LibJS/Value.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define PROGRAM 2
|
||||
#define PROGRAM 4
|
||||
|
||||
static void build_program(JS::Program&);
|
||||
static void build_program(JS::Program&, JS::Heap&);
|
||||
|
||||
int main()
|
||||
{
|
||||
auto program = make<JS::Program>();
|
||||
build_program(*program);
|
||||
JS::Interpreter interpreter;
|
||||
|
||||
auto program = make<JS::Program>();
|
||||
build_program(*program, interpreter.heap());
|
||||
program->dump(0);
|
||||
|
||||
JS::Interpreter interpreter;
|
||||
auto result = interpreter.run(*program);
|
||||
dbg() << "Interpreter returned " << result;
|
||||
|
||||
@ -125,4 +126,13 @@ void build_program(JS::Program& program)
|
||||
program.append<JS::FunctionDeclaration>("foo", move(block));
|
||||
program.append<JS::CallExpression>("foo");
|
||||
}
|
||||
#elif PROGRAM == 4
|
||||
void build_program(JS::Program& program, JS::Heap& heap)
|
||||
{
|
||||
// "hello friends".length
|
||||
|
||||
program.append<JS::MemberExpression>(
|
||||
make<JS::Literal>(JS::Value(js_string(heap, "hello friends"))),
|
||||
make<JS::Identifier>("length"));
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user