setProperty(ctx, name, Value::fromObject(ctx->engine->newNativeFunction(ctx, code)));
}
-Value Object::getProperty(Context *ctx, String *name, PropertyAttributes *attributes)
+Value Object::getProperty(Context *ctx, String *name)
{
if (name->isEqualTo(ctx->engine->id___proto__))
return Value::fromObject(prototype);
- else if (Value *v = getPropertyDescriptor(ctx, name, attributes))
- return *v;
+
+ PropertyDescriptor tmp;
+ if (PropertyDescriptor *p = getPropertyDescriptor(ctx, name, &tmp)) {
+ if (p->isData())
+ return p->value;
+ if (!p->get)
+ return Value::undefinedValue();
+ FunctionObject *f = p->get->asFunctionObject();
+ if (f) {
+ f->call(ctx);
+ return ctx->result;
+ }
+ }
return Value::undefinedValue();
}
-Value *Object::getOwnProperty(Context *, String *name, PropertyAttributes *attributes)
+// Section 8.12.1
+PropertyDescriptor *Object::getOwnProperty(Context *, String *name)
{
- if (members) {
- if (Property *prop = members->find(name)) {
- if (attributes)
- *attributes = prop->attributes;
- return &prop->value;
- }
- }
+ if (members)
+ return members->find(name);
return 0;
}
-Value *Object::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes)
+PropertyDescriptor *Object::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill)
{
- if (Value *prop = getOwnProperty(ctx, name, attributes))
- return prop;
- else if (prototype)
- return prototype->getPropertyDescriptor(ctx, name, attributes);
+ if (PropertyDescriptor *p = getOwnProperty(ctx, name))
+ return p;
+
+ if (prototype)
+ return prototype->getPropertyDescriptor(ctx, name, to_fill);
return 0;
}
-void Object::setProperty(Context *, String *name, const Value &value, bool flag)
+// Section 8.12.5
+void Object::setProperty(Context *ctx, String *name, const Value &value, bool throwException)
{
- Q_UNUSED(flag);
+ if (!canSetProperty(ctx, name)) {
+ if (throwException)
+ __qmljs_throw_type_error(ctx);
+ return;
+ }
if (! members)
- members = new Table();
+ members = new PropertyTable();
- members->insert(name, value);
+ PropertyDescriptor *pd = getOwnProperty(ctx, name);
+ if (pd) {
+ if (pd->isData()) {
+ pd->value = value;
+ return;
+ }
+ }
+ PropertyDescriptor *p = members->insert(name);
+ *p = PropertyDescriptor::fromValue(value);
}
+// Section 8.12.4
bool Object::canSetProperty(Context *ctx, String *name)
{
- PropertyAttributes attrs = PropertyAttributes();
- if (getOwnProperty(ctx, name, &attrs)) {
- return attrs & WritableAttribute;
- } else if (! prototype) {
+ if (PropertyDescriptor *p = getOwnProperty(ctx, name)) {
+ if (p->isAccessor())
+ return p->get != 0;
+ return p->isWritable();
+ }
+
+ if (! prototype)
return extensible;
- } else if (prototype->getPropertyDescriptor(ctx, name, &attrs)) {
- return attrs & WritableAttribute;
+
+ PropertyDescriptor tmp;
+ if (PropertyDescriptor *p = prototype->getPropertyDescriptor(ctx, name, &tmp)) {
+ if (p->isAccessor())
+ return p->get != 0;
+ if (!extensible)
+ return false;
+ return p->isWritable();
} else {
return extensible;
}
return false;
}
-void Object::defineOwnProperty(Context *ctx, const Value &getter, const Value &setter, bool flag)
+bool Object::defineOwnProperty(Context *ctx, String *name, const Value &getter, const Value &setter, bool flag)
{
- Q_UNUSED(getter);
- Q_UNUSED(setter);
- Q_UNUSED(flag);
- ctx->throwUnimplemented(QStringLiteral("defineOwnProperty"));
+ if (!members)
+ members = new PropertyTable();
+
+ PropertyDescriptor *p = getOwnProperty(ctx, name);
+ if (!p) {
+ if (!extensible)
+ goto reject;
+ }
+
+ reject:
+ if (flag)
+ __qmljs_throw_type_error(ctx);
+ return false;
}
String *ForEachIteratorObject::nextPropertyName()
{
- Property *p = 0;
+ PropertyTableEntry *p = 0;
while (1) {
if (!current)
return 0;
}
}
-Value ArrayObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes)
+Value ArrayObject::getProperty(Context *ctx, String *name)
{
if (name->isEqualTo(ctx->engine->id_length))
return Value::fromDouble(value.size());
- return Object::getProperty(ctx, name, attributes);
+ return Object::getProperty(ctx, name);
}
bool FunctionObject::hasInstance(Context *ctx, const Value &value)
function->code(ctx, function->codeData);
}
-Value RegExpObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes)
+Value RegExpObject::getProperty(Context *ctx, String *name)
{
QString n = name->toQString();
if (n == QLatin1String("source"))
return Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption);
else if (n == QLatin1String("lastIndex"))
return lastIndex;
- return Object::getProperty(ctx, name, attributes);
+ return Object::getProperty(ctx, name);
}
function->code(ctx, function->codeData);
}
-Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes)
+PropertyDescriptor *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill)
{
if (context) {
for (unsigned int i = 0; i < context->varCount; ++i) {
String *var = context->vars[i];
if (__qmljs_string_equal(context, var, name)) {
- if (attributes)
- *attributes = PropertyAttributes(*attributes | WritableAttribute);
- return &context->locals[i];
+ *to_fill = PropertyDescriptor::fromValue(context->locals[i]);
+ to_fill->writable = PropertyDescriptor::Set;
+ return to_fill;
}
}
for (unsigned int i = 0; i < context->formalCount; ++i) {
String *formal = context->formals[i];
if (__qmljs_string_equal(context, formal, name)) {
- if (attributes)
- *attributes = PropertyAttributes(*attributes | WritableAttribute);
- return &context->arguments[i];
+ *to_fill = PropertyDescriptor::fromValue(context->arguments[i]);
+ to_fill->writable = PropertyDescriptor::Set;
+ return to_fill;
}
}
if (name->isEqualTo(ctx->engine->id_arguments)) {
arguments.objectValue()->prototype = ctx->engine->objectPrototype;
}
- return &arguments;
+ *to_fill = PropertyDescriptor::fromValue(arguments);
+ return to_fill;
}
}
- if (Value *prop = Object::getPropertyDescriptor(ctx, name, attributes))
- return prop;
- return 0;
+
+ return Object::getPropertyDescriptor(ctx, name, to_fill);
}
-Value ArgumentsObject::getProperty(Context *ctx, String *name, PropertyAttributes *attributes)
+Value ArgumentsObject::getProperty(Context *ctx, String *name)
{
if (name->isEqualTo(ctx->engine->id_length))
return Value::fromDouble(context->argumentCount);
- return Object::getProperty(ctx, name, attributes);
+ return Object::getProperty(ctx, name);
}
-Value *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes)
+PropertyDescriptor *ArgumentsObject::getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill)
{
if (context) {
const quint32 i = Value::fromString(name).toUInt32(ctx);
- if (i < context->argumentCount)
- return &context->arguments[i];
+ if (i < context->argumentCount) {
+ *to_fill = PropertyDescriptor::fromValue(context->arguments[i]);
+ return to_fill;
+ }
}
- return Object::getPropertyDescriptor(ctx, name, attributes);
+
+ return Object::getPropertyDescriptor(ctx, name, to_fill);
}
ExecutionEngine::ExecutionEngine()
mutable unsigned _hashValue;
};
-struct Property {
- String *name;
- Value value;
- PropertyAttributes attributes;
- Property *next;
- int index;
+struct PropertyDescriptor {
+ enum Type {
+ Generic,
+ Data,
+ Accessor
+ };
+ enum State {
+ Undefined,
+ Unset,
+ Set
+ };
+ union {
+ Value value;
+ struct {
+ Object *get;
+ Object *set;
+ };
+ };
+ uint type : 8;
+ uint writable : 8;
+ uint enumberable : 8;
+ uint configurable : 8;
+
+ static inline PropertyDescriptor fromValue(Value v) {
+ PropertyDescriptor pd;
+ pd.value = v;
+ pd.type = Data;
+ pd.writable = Set;
+ pd.enumberable = Set;
+ pd.configurable = Set;
+ return pd;
+ }
+ static inline PropertyDescriptor fromAccessor(Object *getter, Object *setter) {
+ PropertyDescriptor pd;
+ pd.get = getter;
+ pd.set = setter;
+ pd.type = Accessor;
+ pd.writable = Undefined;
+ pd.enumberable = Set;
+ pd.configurable = Set;
+ return pd;
+ }
- inline Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes)
- { init(name, value, flags); }
+ inline bool isData() const { return type == Data; }
+ inline bool isAccessor() const { return type == Accessor; }
+ inline bool isGeneric() const { return type == Generic; }
- inline void init(String *name, const Value &value, PropertyAttributes flags = NoAttributes)
- {
- this->name = name;
- this->value = value;
- this->attributes = flags;
- this->next = 0;
- this->index = -1;
- }
+ inline bool isWritable() const { return writable == Set; }
+ inline bool isEnumerable() const { return enumberable == Set; }
+ inline bool isConfigurable() const { return configurable == Set; }
+};
- inline bool isWritable() const { return attributes & WritableAttribute; }
- inline bool isEnumerable() const { return attributes & EnumerableAttribute; }
- inline bool isConfigurable() const { return attributes & ConfigurableAttribute; }
+struct PropertyTableEntry {
+ PropertyDescriptor descriptor;
+ String *name;
+ PropertyTableEntry *next;
+ int index;
+
+ inline PropertyTableEntry(String *name)
+ : name(name),
+ next(0),
+ index(-1)
+ { }
inline bool hasName(String *n) const { return name->isEqualTo(n); }
inline unsigned hashValue() const { return name->hashValue(); }
};
-class Table
+class PropertyTable
{
- Q_DISABLE_COPY(Table)
+ Q_DISABLE_COPY(PropertyTable)
public:
- Table()
+ PropertyTable()
: _properties(0)
, _buckets(0)
, _freeList(0)
, _bucketCount(0)
, _allocated(0) {}
- ~Table()
+ ~PropertyTable()
{
qDeleteAll(_properties, _properties + _propertyCount + 1);
delete[] _properties;
inline bool isEmpty() const { return _propertyCount == -1; }
- typedef Property **iterator;
+ typedef PropertyTableEntry **iterator;
inline iterator begin() const { return _properties; }
inline iterator end() const { return _properties + (_propertyCount + 1); }
bool remove(String *name)
{
- if (Property *prop = find(name)) {
+ if (PropertyTableEntry *prop = findEntry(name)) {
// ### TODO check if the property can be removed
- Property *bucket = _buckets[prop->hashValue() % _bucketCount];
+ PropertyTableEntry *bucket = _buckets[prop->hashValue() % _bucketCount];
if (bucket == prop) {
bucket = bucket->next;
} else {
- for (Property *it = bucket; it; it = it->next) {
+ for (PropertyTableEntry *it = bucket; it; it = it->next) {
if (it->next == prop) {
it->next = it->next->next;
break;
return true;
}
- Property *find(String *name) const
+ PropertyTableEntry *findEntry(String *name) const
{
if (_properties) {
- for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
+ for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
if (prop && (prop->name == name || prop->hasName(name)))
return prop;
}
return 0;
}
- Property *insert(String *name, const Value &value)
+ PropertyDescriptor *find(String *name) const
{
- if (Property *prop = find(name)) {
- prop->value = value;
- return prop;
+ if (_properties) {
+ for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
+ if (prop && (prop->name == name || prop->hasName(name)))
+ return &prop->descriptor;
+ }
}
+ return 0;
+ }
+
+ PropertyDescriptor *insert(String *name)
+ {
+ if (PropertyTableEntry *prop = findEntry(name))
+ return &prop->descriptor;
+
if (++_propertyCount == _allocated) {
if (! _allocated)
_allocated = 4;
else
_allocated *= 2;
- Property **properties = new Property*[_allocated];
+ PropertyTableEntry **properties = new PropertyTableEntry*[_allocated];
std::copy(_properties, _properties + _propertyCount, properties);
delete[] _properties;
_properties = properties;
}
- Property *prop;
+ PropertyTableEntry *prop;
if (_freeList) {
prop = _freeList;
_freeList = _freeList->next;
- prop->init(name, value);
} else {
- prop = new Property(name, value);
+ prop = new PropertyTableEntry(name);
}
prop->index = _propertyCount;
if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) {
rehash();
} else {
- Property *&bucket = _buckets[prop->hashValue() % _bucketCount];
+ PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount];
prop->next = bucket;
bucket = prop;
}
- return prop;
+ return &prop->descriptor;
}
private:
_bucketCount = 11;
delete[] _buckets;
- _buckets = new Property *[_bucketCount];
- std::fill(_buckets, _buckets + _bucketCount, (Property *) 0);
+ _buckets = new PropertyTableEntry *[_bucketCount];
+ std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0);
for (int i = 0; i <= _propertyCount; ++i) {
- Property *prop = _properties[i];
- Property *&bucket = _buckets[prop->hashValue() % _bucketCount];
+ PropertyTableEntry *prop = _properties[i];
+ PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount];
prop->next = bucket;
bucket = prop;
}
private:
friend struct ForEachIteratorObject;
- Property **_properties;
- Property **_buckets;
- Property *_freeList;
+ PropertyTableEntry **_properties;
+ PropertyTableEntry **_buckets;
+ PropertyTableEntry *_freeList;
int _propertyCount;
int _bucketCount;
int _allocated;
struct Object {
Object *prototype;
String *klass;
- Table *members;
+ PropertyTable *members;
bool extensible;
Object()
virtual ActivationObject *asActivationObject() { return 0; }
virtual ArgumentsObject *asArgumentsObject() { return 0; }
- virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0);
- virtual Value *getOwnProperty(Context *ctx, String *name, PropertyAttributes *attributes = 0);
- virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes = 0);
- virtual void setProperty(Context *ctx, String *name, const Value &value, bool flag = false);
+ virtual Value getProperty(Context *ctx, String *name);
+ virtual PropertyDescriptor *getOwnProperty(Context *ctx, String *name);
+ virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill);
+ virtual void setProperty(Context *ctx, String *name, const Value &value, bool throwException = false);
virtual bool canSetProperty(Context *ctx, String *name);
virtual bool hasProperty(Context *ctx, String *name) const;
virtual bool deleteProperty(Context *ctx, String *name, bool flag);
- virtual void defineOwnProperty(Context *ctx, const Value &getter, const Value &setter, bool flag = false);
+ virtual bool defineOwnProperty(Context *ctx, String *name, const Value &getter, const Value &setter, bool flag = false);
//
// helpers
ArrayObject(const Array &value): value(value) {}
virtual QString className() { return QStringLiteral("Array"); }
virtual ArrayObject *asArrayObject() { return this; }
- virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes);
+ virtual Value getProperty(Context *ctx, String *name);
};
struct FunctionObject: Object {
RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {}
virtual QString className() { return QStringLiteral("RegExp"); }
virtual RegExpObject *asRegExpObject() { return this; }
- virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes);
+ virtual Value getProperty(Context *ctx, String *name);
};
struct ErrorObject: Object {
ActivationObject(Context *context): context(context), arguments(Value::undefinedValue()) {}
virtual QString className() { return QStringLiteral("Activation"); }
virtual ActivationObject *asActivationObject() { return this; }
- virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes);
+ virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill);
};
struct ArgumentsObject: Object {
ArgumentsObject(Context *context): context(context) {}
virtual QString className() { return QStringLiteral("Arguments"); }
virtual ArgumentsObject *asArgumentsObject() { return this; }
- virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes);
- virtual Value *getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes);
+ virtual Value getProperty(Context *ctx, String *name);
+ virtual PropertyDescriptor *getPropertyDescriptor(Context *ctx, String *name, PropertyDescriptor *to_fill);
};
struct ExecutionEngine
return isObject() ? objectValue()->getProperty(ctx, name) : undefinedValue();
}
-Value *Value::getPropertyDescriptor(Context *ctx, String *name) const
-{
- return isObject() ? objectValue()->getPropertyDescriptor(ctx, name) : 0;
-}
-
void Context::init(ExecutionEngine *eng)
{
engine = eng;
{
for (Context *ctx = this; ctx; ctx = ctx->parent) {
if (ctx->activation.isObject()) {
- if (Value *prop = ctx->activation.objectValue()->getPropertyDescriptor(this, name)) {
- return prop;
+ PropertyDescriptor tmp;
+ if (PropertyDescriptor *pd = ctx->activation.objectValue()->getPropertyDescriptor(this, name, &tmp)) {
+ return &pd->value;
}
}
}
STRING_HINT
};
-enum PropertyAttributes {
- NoAttributes = 0,
- ValueAttribute = 1,
- WritableAttribute = 2,
- EnumerableAttribute = 4,
- ConfigurableAttribute = 8
-};
-
struct Object;
struct String;
+struct PropertyDescriptor;
struct Context;
struct FunctionObject;
struct BooleanObject;
ActivationObject *asArgumentsObject() const;
Value property(Context *ctx, String *name) const;
- Value *getPropertyDescriptor(Context *ctx, String *name) const;
};
extern "C" {
ctx->result = Value::fromObject(ctx->engine->newObject());
}
-Value ObjectCtor::getProperty(Context *ctx, String *name, PropertyAttributes *attributes)
+Value ObjectCtor::getProperty(Context *ctx, String *name)
{
if (name == ctx->engine->id_length)
return Value::fromDouble(1);
- return Object::getProperty(ctx, name, attributes);
+ return Object::getProperty(ctx, name);
}
void ObjectPrototype::init(Context *ctx, const Value &ctor)
else {
ArrayObject *array = ctx->engine->newArrayObject()->asArrayObject();
Array &a = array->value;
- if (Table *members = O.objectValue()->members) {
- for (Property **it = members->begin(), **end = members->end(); it != end; ++it) {
- if (Property *prop = *it) {
+ if (PropertyTable *members = O.objectValue()->members) {
+ for (PropertyTableEntry **it = members->begin(), **end = members->end(); it != end; ++it) {
+ if (PropertyTableEntry *prop = *it) {
a.push(Value::fromString(prop->name));
}
}
virtual void construct(Context *ctx);
virtual void call(Context *ctx);
- virtual Value getProperty(Context *ctx, String *name, PropertyAttributes *attributes);
+ virtual Value getProperty(Context *ctx, String *name);
};
struct ObjectPrototype: Object