mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 02:37:36 +00:00
LibJS: Support array holes, encoded as empty JS::Value
This patch adds a new kind of JS::Value, the empty value. It's what you get when you do JSValue() (or most commonly, {} in C++.) An empty Value signifies the absence of a value, and should never be visible to JavaScript itself. As of right now, it's used for array holes and as a return value when an exception has been thrown and we just want to unwind. This patch is a bit of a mess as I had to fix a whole bunch of code that was relying on JSValue() being undefined, etc.
This commit is contained in:
parent
5495f06af5
commit
bdffc9e7fb
9 changed files with 102 additions and 35 deletions
|
@ -77,6 +77,7 @@ Optional<Value> Object::get_own_property(const Object& this_object, const FlyStr
|
|||
return {};
|
||||
|
||||
auto value_here = m_storage[metadata.value().offset];
|
||||
ASSERT(!value_here.is_empty());
|
||||
if (value_here.is_object() && value_here.as_object().is_native_property()) {
|
||||
auto& native_property = static_cast<const NativeProperty&>(value_here.as_object());
|
||||
auto& interpreter = const_cast<Object*>(this)->interpreter();
|
||||
|
@ -126,8 +127,12 @@ Optional<Value> Object::get_by_index(i32 property_index) const
|
|||
|
||||
const Object* object = this;
|
||||
while (object) {
|
||||
if (static_cast<size_t>(property_index) < object->m_elements.size())
|
||||
return object->m_elements[property_index];
|
||||
if (static_cast<size_t>(property_index) < object->m_elements.size()) {
|
||||
auto value = object->m_elements[property_index];
|
||||
if (value.is_empty())
|
||||
return {};
|
||||
return value;
|
||||
}
|
||||
object = object->prototype();
|
||||
}
|
||||
return {};
|
||||
|
@ -159,6 +164,7 @@ Optional<Value> Object::get(PropertyName property_name) const
|
|||
|
||||
void Object::put_by_index(i32 property_index, Value value)
|
||||
{
|
||||
ASSERT(!value.is_empty());
|
||||
if (property_index < 0)
|
||||
return put(String::number(property_index), value);
|
||||
// FIXME: Implement some kind of sparse storage for arrays with huge indices.
|
||||
|
@ -169,6 +175,7 @@ void Object::put_by_index(i32 property_index, Value value)
|
|||
|
||||
void Object::put(const FlyString& property_name, Value value)
|
||||
{
|
||||
ASSERT(!value.is_empty());
|
||||
bool ok;
|
||||
i32 property_index = property_name.to_int(ok);
|
||||
if (ok && property_index >= 0)
|
||||
|
@ -231,8 +238,11 @@ bool Object::has_own_property(const FlyString& property_name) const
|
|||
{
|
||||
bool ok;
|
||||
i32 property_index = property_name.to_int(ok);
|
||||
if (ok && property_index >= 0)
|
||||
return static_cast<size_t>(property_index) < m_elements.size();
|
||||
if (ok && property_index >= 0) {
|
||||
if (static_cast<size_t>(property_index) >= m_elements.size())
|
||||
return false;
|
||||
return !m_elements[property_index].is_empty();
|
||||
}
|
||||
return shape().lookup(property_name).has_value();
|
||||
}
|
||||
|
||||
|
|
|
@ -64,8 +64,10 @@ Value ObjectConstructor::get_own_property_names(Interpreter& interpreter)
|
|||
if (interpreter.exception())
|
||||
return {};
|
||||
auto* result = interpreter.heap().allocate<Array>();
|
||||
for (size_t i = 0; i < object->elements().size(); ++i)
|
||||
result->push(js_string(interpreter, String::number(i)));
|
||||
for (size_t i = 0; i < object->elements().size(); ++i) {
|
||||
if (!object->elements()[i].is_empty())
|
||||
result->push(js_string(interpreter, String::number(i)));
|
||||
}
|
||||
|
||||
for (auto& it : object->shape().property_table())
|
||||
result->push(js_string(interpreter, it.key));
|
||||
|
|
|
@ -33,10 +33,13 @@ namespace JS {
|
|||
class PropertyName {
|
||||
public:
|
||||
enum class Type {
|
||||
Invalid,
|
||||
Number,
|
||||
String,
|
||||
};
|
||||
|
||||
PropertyName() {}
|
||||
|
||||
explicit PropertyName(i32 index)
|
||||
: m_type(Type::Number)
|
||||
, m_number(index)
|
||||
|
@ -50,6 +53,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
bool is_valid() const { return m_type != Type::Invalid; }
|
||||
bool is_number() const { return m_type == Type::Number; }
|
||||
bool is_string() const { return m_type == Type::String; }
|
||||
|
||||
|
@ -57,7 +61,7 @@ public:
|
|||
const FlyString& as_string() const { return m_string; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
Type m_type { Type::Invalid };
|
||||
FlyString m_string;
|
||||
i32 m_number { 0 };
|
||||
};
|
||||
|
|
|
@ -39,6 +39,14 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
Value::Value()
|
||||
: m_type(Type::Empty)
|
||||
{
|
||||
// dbg() << "Create empty value";
|
||||
// dump_backtrace();
|
||||
}
|
||||
|
||||
|
||||
bool Value::is_array() const
|
||||
{
|
||||
return is_object() && as_object().is_array();
|
||||
|
@ -119,6 +127,9 @@ Object* Value::to_object(Heap& heap) const
|
|||
Value Value::to_number() const
|
||||
{
|
||||
switch (m_type) {
|
||||
case Type::Empty:
|
||||
ASSERT_NOT_REACHED();
|
||||
return {};
|
||||
case Type::Boolean:
|
||||
return Value(m_value.as_bool ? 1 : 0);
|
||||
case Type::Number:
|
||||
|
@ -280,6 +291,9 @@ Value typed_eq(Value lhs, Value rhs)
|
|||
return Value(false);
|
||||
|
||||
switch (lhs.type()) {
|
||||
case Value::Type::Empty:
|
||||
ASSERT_NOT_REACHED();
|
||||
return {};
|
||||
case Value::Type::Undefined:
|
||||
return Value(true);
|
||||
case Value::Type::Null:
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace JS {
|
|||
class Value {
|
||||
public:
|
||||
enum class Type {
|
||||
Empty,
|
||||
Undefined,
|
||||
Null,
|
||||
Number,
|
||||
|
@ -44,6 +45,7 @@ public:
|
|||
Boolean,
|
||||
};
|
||||
|
||||
bool is_empty() const { return m_type == Type::Empty; }
|
||||
bool is_undefined() const { return m_type == Type::Undefined; }
|
||||
bool is_null() const { return m_type == Type::Null; }
|
||||
bool is_number() const { return m_type == Type::Number; }
|
||||
|
@ -56,10 +58,7 @@ public:
|
|||
bool is_nan() const { return is_number() && __builtin_isnan(as_double()); }
|
||||
bool is_infinity() const { return is_number() && __builtin_isinf(as_double()); }
|
||||
|
||||
Value()
|
||||
: m_type(Type::Undefined)
|
||||
{
|
||||
}
|
||||
Value();
|
||||
|
||||
explicit Value(bool value)
|
||||
: m_type(Type::Boolean)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue