mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-08 12:19:37 +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();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VariableDeclaration::dump(int indent) const
|
void VariableDeclaration::dump(int indent) const
|
||||||
{
|
{
|
||||||
ASTNode::dump(indent);
|
ASTNode::dump(indent);
|
||||||
@ -406,4 +405,26 @@ Value ObjectExpression::execute(Interpreter& interpreter) const
|
|||||||
return Value(interpreter.heap().allocate<Object>());
|
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"; }
|
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 <AK/LogStream.h>
|
||||||
#include <LibJS/Cell.h>
|
#include <LibJS/Cell.h>
|
||||||
#include <LibJS/Object.h>
|
#include <LibJS/Object.h>
|
||||||
|
#include <LibJS/PrimitiveString.h>
|
||||||
#include <LibJS/Value.h>
|
#include <LibJS/Value.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
void Cell::Visitor::visit(Value value)
|
void Cell::Visitor::visit(Value value)
|
||||||
{
|
{
|
||||||
if (value.is_object())
|
if (value.is_cell())
|
||||||
visit(value.as_object());
|
visit(value.as_cell());
|
||||||
}
|
}
|
||||||
|
|
||||||
const LogStream& operator<<(const LogStream& stream, const Cell* cell)
|
const LogStream& operator<<(const LogStream& stream, const Cell* cell)
|
||||||
|
@ -25,7 +25,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
#include <LibJS/Heap.h>
|
||||||
#include <LibJS/Object.h>
|
#include <LibJS/Object.h>
|
||||||
|
#include <LibJS/PrimitiveString.h>
|
||||||
|
#include <LibJS/StringObject.h>
|
||||||
#include <LibJS/Value.h>
|
#include <LibJS/Value.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
@ -50,6 +53,9 @@ String Value::to_string() const
|
|||||||
return String::format("{%s}", as_object()->class_name());
|
return String::format("{%s}", as_object()->class_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_string())
|
||||||
|
return m_value.as_string->string();
|
||||||
|
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +70,7 @@ bool Value::to_boolean() const
|
|||||||
case Type::Undefined:
|
case Type::Undefined:
|
||||||
return false;
|
return false;
|
||||||
case Type::String:
|
case Type::String:
|
||||||
return String(as_string()).is_empty();
|
return !as_string()->string().is_empty();
|
||||||
case Type::Object:
|
case Type::Object:
|
||||||
return true;
|
return true;
|
||||||
default:
|
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)
|
Value greater_than(Value lhs, Value rhs)
|
||||||
{
|
{
|
||||||
ASSERT(lhs.is_number());
|
ASSERT(lhs.is_number());
|
||||||
|
@ -50,6 +50,7 @@ public:
|
|||||||
bool is_string() const { return m_type == Type::String; }
|
bool is_string() const { return m_type == Type::String; }
|
||||||
bool is_object() const { return m_type == Type::Object; }
|
bool is_object() const { return m_type == Type::Object; }
|
||||||
bool is_boolean() const { return m_type == Type::Boolean; }
|
bool is_boolean() const { return m_type == Type::Boolean; }
|
||||||
|
bool is_cell() const { return is_string() || is_object(); }
|
||||||
|
|
||||||
explicit Value(bool value)
|
explicit Value(bool value)
|
||||||
: m_type(Type::Boolean)
|
: m_type(Type::Boolean)
|
||||||
@ -75,6 +76,12 @@ public:
|
|||||||
m_value.as_object = object;
|
m_value.as_object = object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explicit Value(PrimitiveString* string)
|
||||||
|
: m_type(Type::String)
|
||||||
|
{
|
||||||
|
m_value.as_string = string;
|
||||||
|
}
|
||||||
|
|
||||||
explicit Value(Type type)
|
explicit Value(Type type)
|
||||||
: m_type(type)
|
: m_type(type)
|
||||||
{
|
{
|
||||||
@ -106,23 +113,38 @@ public:
|
|||||||
return m_value.as_object;
|
return m_value.as_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StringImpl* as_string() const
|
PrimitiveString* as_string()
|
||||||
{
|
{
|
||||||
ASSERT(is_string());
|
ASSERT(is_string());
|
||||||
return m_value.as_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;
|
String to_string() const;
|
||||||
bool to_boolean() const;
|
bool to_boolean() const;
|
||||||
|
|
||||||
|
Value to_object(Heap&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Type m_type { Type::Undefined };
|
Type m_type { Type::Undefined };
|
||||||
|
|
||||||
union {
|
union {
|
||||||
bool as_bool;
|
bool as_bool;
|
||||||
double as_double;
|
double as_double;
|
||||||
StringImpl* as_string;
|
PrimitiveString* as_string;
|
||||||
Object* as_object;
|
Object* as_object;
|
||||||
|
Cell* as_cell;
|
||||||
} m_value;
|
} m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,21 +28,22 @@
|
|||||||
#include <LibJS/AST.h>
|
#include <LibJS/AST.h>
|
||||||
#include <LibJS/Interpreter.h>
|
#include <LibJS/Interpreter.h>
|
||||||
#include <LibJS/Object.h>
|
#include <LibJS/Object.h>
|
||||||
|
#include <LibJS/PrimitiveString.h>
|
||||||
#include <LibJS/Value.h>
|
#include <LibJS/Value.h>
|
||||||
#include <stdio.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()
|
int main()
|
||||||
{
|
{
|
||||||
auto program = make<JS::Program>();
|
JS::Interpreter interpreter;
|
||||||
build_program(*program);
|
|
||||||
|
|
||||||
|
auto program = make<JS::Program>();
|
||||||
|
build_program(*program, interpreter.heap());
|
||||||
program->dump(0);
|
program->dump(0);
|
||||||
|
|
||||||
JS::Interpreter interpreter;
|
|
||||||
auto result = interpreter.run(*program);
|
auto result = interpreter.run(*program);
|
||||||
dbg() << "Interpreter returned " << result;
|
dbg() << "Interpreter returned " << result;
|
||||||
|
|
||||||
@ -125,4 +126,13 @@ void build_program(JS::Program& program)
|
|||||||
program.append<JS::FunctionDeclaration>("foo", move(block));
|
program.append<JS::FunctionDeclaration>("foo", move(block));
|
||||||
program.append<JS::CallExpression>("foo");
|
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
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user