/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the V4VM module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv4objectproto_p.h" #include "qv4mm_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_OS_WIN # include # ifndef Q_OS_VXWORKS # include # else # include "qplatformdefs.h" # endif #else # include #endif using namespace QQmlJS::VM; DEFINE_MANAGED_VTABLE(ObjectCtor); ObjectCtor::ObjectCtor(ExecutionContext *scope) : FunctionObject(scope) { vtbl = &static_vtbl; } Value ObjectCtor::construct(Managed *that, ExecutionContext *ctx, Value *args, int argc) { ObjectCtor *ctor = static_cast(that); if (!argc || args[0].isUndefined() || args[0].isNull()) { Object *obj = ctx->engine->newObject(); Value proto = ctor->get(ctx, ctx->engine->id_prototype); if (proto.isObject()) obj->prototype = proto.objectValue(); return Value::fromObject(obj); } return __qmljs_to_object(ctx, args[0]); } Value ObjectCtor::call(Managed *, ExecutionContext *ctx, const Value &/*thisObject*/, Value *args, int argc) { if (!argc || args[0].isUndefined() || args[0].isNull()) return Value::fromObject(ctx->engine->newObject()); return __qmljs_to_object(ctx, args[0]); } void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) { ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this)); ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1)); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("create"), method_create, 2); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperty"), method_defineProperty, 3); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("defineProperties"), method_defineProperties, 2); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("seal"), method_seal, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("freeze"), method_freeze, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("preventExtensions"), method_preventExtensions, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isSealed"), method_isSealed, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isFrozen"), method_isFrozen, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isExtensible"), method_isExtensible, 1); ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("keys"), method_keys, 1); defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor); defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0); defineDefaultProperty(ctx, QStringLiteral("toLocaleString"), method_toLocaleString, 0); defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf, 0); defineDefaultProperty(ctx, QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1); defineDefaultProperty(ctx, QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1); defineDefaultProperty(ctx, QStringLiteral("propertyIsEnumerable"), method_propertyIsEnumerable, 1); defineDefaultProperty(ctx, QStringLiteral("__defineGetter__"), method_defineGetter, 0); defineDefaultProperty(ctx, QStringLiteral("__defineSetter__"), method_defineSetter, 0); } Value ObjectPrototype::method_getPrototypeOf(SimpleCallContext *ctx) { Value o = ctx->argument(0); if (! o.isObject()) ctx->throwTypeError(); Object *p = o.objectValue()->prototype; return p ? Value::fromObject(p) : Value::nullValue(); } Value ObjectPrototype::method_getOwnPropertyDescriptor(SimpleCallContext *ctx) { Value O = ctx->argument(0); if (!O.isObject()) ctx->throwTypeError(); String *name = ctx->argument(1).toString(ctx); PropertyAttributes attrs; Property *desc = O.objectValue()->__getOwnProperty__(name, &attrs); return fromPropertyDescriptor(ctx, desc, attrs); } Value ObjectPrototype::method_getOwnPropertyNames(SimpleCallContext *context) { Object *O = context->argumentCount ? context->arguments[0].asObject() : 0; if (!O) context->throwTypeError(); ArrayObject *array = context->engine->newArrayObject(context)->asArrayObject(); ObjectIterator it(context, O, ObjectIterator::NoFlags); while (1) { Value v = it.nextPropertyNameAsString(); if (v.isNull()) break; array->push_back(v); } return Value::fromObject(array); } Value ObjectPrototype::method_create(SimpleCallContext *ctx) { Value O = ctx->argument(0); if (!O.isObject() && !O.isNull()) ctx->throwTypeError(); Object *newObject = ctx->engine->newObject(); newObject->prototype = O.asObject(); Value objValue = Value::fromObject(newObject); if (ctx->argumentCount > 1 && !ctx->argument(1).isUndefined()) { ctx->arguments[0] = objValue; method_defineProperties(ctx); } return objValue; } Value ObjectPrototype::method_defineProperty(SimpleCallContext *ctx) { Value O = ctx->argument(0); if (!O.isObject()) ctx->throwTypeError(); String *name = ctx->argument(1).toString(ctx); Value attributes = ctx->argument(2); Property pd; PropertyAttributes attrs; toPropertyDescriptor(ctx, attributes, &pd, &attrs); if (!O.objectValue()->__defineOwnProperty__(ctx, name, pd, attrs)) ctx->throwTypeError(); return O; } Value ObjectPrototype::method_defineProperties(SimpleCallContext *ctx) { Value O = ctx->argument(0); if (!O.isObject()) ctx->throwTypeError(); Object *o = ctx->argument(1).toObject(ctx); ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); while (1) { uint index; String *name; PropertyAttributes attrs; Property *pd = it.next(&name, &index, &attrs); if (!pd) break; Property n; PropertyAttributes nattrs; toPropertyDescriptor(ctx, o->getValue(ctx, pd, attrs), &n, &nattrs); bool ok; if (name) ok = O.objectValue()->__defineOwnProperty__(ctx, name, n, nattrs); else ok = O.objectValue()->__defineOwnProperty__(ctx, index, n, nattrs); if (!ok) ctx->throwTypeError(); } return O; } Value ObjectPrototype::method_seal(SimpleCallContext *ctx) { if (!ctx->argument(0).isObject()) ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); o->extensible = false; o->internalClass = o->internalClass->sealed(); o->ensureArrayAttributes(); for (uint i = 0; i < o->arrayDataLen; ++i) { if (!o->arrayAttributes[i].isGeneric()) o->arrayAttributes[i].setConfigurable(false); } return ctx->argument(0); } Value ObjectPrototype::method_freeze(SimpleCallContext *ctx) { if (!ctx->argument(0).isObject()) ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); o->extensible = false; o->internalClass = o->internalClass->frozen(); o->ensureArrayAttributes(); for (uint i = 0; i < o->arrayDataLen; ++i) { if (!o->arrayAttributes[i].isGeneric()) o->arrayAttributes[i].setConfigurable(false); if (o->arrayAttributes[i].isData()) o->arrayAttributes[i].setWritable(false); } return ctx->argument(0); } Value ObjectPrototype::method_preventExtensions(SimpleCallContext *ctx) { if (!ctx->argument(0).isObject()) ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); o->extensible = false; return ctx->argument(0); } Value ObjectPrototype::method_isSealed(SimpleCallContext *ctx) { if (!ctx->argument(0).isObject()) ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); if (o->extensible) return Value::fromBoolean(false); if (o->internalClass != o->internalClass->sealed()) return Value::fromBoolean(false); if (!o->arrayDataLen) return Value::fromBoolean(true); if (!o->arrayAttributes) return Value::fromBoolean(false); for (uint i = 0; i < o->arrayDataLen; ++i) { if (!o->arrayAttributes[i].isGeneric()) if (o->arrayAttributes[i].isConfigurable()) return Value::fromBoolean(false); } return Value::fromBoolean(true); } Value ObjectPrototype::method_isFrozen(SimpleCallContext *ctx) { if (!ctx->argument(0).isObject()) ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); if (o->extensible) return Value::fromBoolean(false); if (o->internalClass != o->internalClass->frozen()) return Value::fromBoolean(false); if (!o->arrayDataLen) return Value::fromBoolean(true); if (!o->arrayAttributes) return Value::fromBoolean(false); for (uint i = 0; i < o->arrayDataLen; ++i) { if (!o->arrayAttributes[i].isGeneric()) if (o->arrayAttributes[i].isConfigurable() || o->arrayAttributes[i].isWritable()) return Value::fromBoolean(false); } return Value::fromBoolean(true); } Value ObjectPrototype::method_isExtensible(SimpleCallContext *ctx) { if (!ctx->argument(0).isObject()) ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); return Value::fromBoolean(o->extensible); } Value ObjectPrototype::method_keys(SimpleCallContext *ctx) { if (!ctx->argument(0).isObject()) ctx->throwTypeError(); Object *o = ctx->argument(0).objectValue(); ArrayObject *a = ctx->engine->newArrayObject(ctx); ObjectIterator it(ctx, o, ObjectIterator::EnumberableOnly); while (1) { uint index; String *name; Property *pd = it.next(&name, &index); if (!pd) break; Value key; if (name) { key = Value::fromString(name); } else { key = Value::fromDouble(index); key = __qmljs_to_string(key, ctx); } a->push_back(key); } return Value::fromObject(a); } Value ObjectPrototype::method_toString(SimpleCallContext *ctx) { if (ctx->thisObject.isUndefined()) { return Value::fromString(ctx, QStringLiteral("[object Undefined]")); } else if (ctx->thisObject.isNull()) { return Value::fromString(ctx, QStringLiteral("[object Null]")); } else { Value obj = __qmljs_to_object(ctx, ctx->thisObject); QString className = obj.objectValue()->className(); return Value::fromString(ctx, QString::fromUtf8("[object %1]").arg(className)); } } Value ObjectPrototype::method_toLocaleString(SimpleCallContext *ctx) { Object *o = ctx->thisObject.toObject(ctx); Value ts = o->get(ctx, ctx->engine->newString(QStringLiteral("toString"))); FunctionObject *f = ts.asFunctionObject(); if (!f) ctx->throwTypeError(); return f->call(ctx, Value::fromObject(o), 0, 0); } Value ObjectPrototype::method_valueOf(SimpleCallContext *ctx) { return Value::fromObject(ctx->thisObject.toObject(ctx)); } Value ObjectPrototype::method_hasOwnProperty(SimpleCallContext *ctx) { String *P = ctx->argument(0).toString(ctx); Object *O = ctx->thisObject.toObject(ctx); bool r = O->__getOwnProperty__(P) != 0; return Value::fromBoolean(r); } Value ObjectPrototype::method_isPrototypeOf(SimpleCallContext *ctx) { Value V = ctx->argument(0); if (! V.isObject()) return Value::fromBoolean(false); Object *O = ctx->thisObject.toObject(ctx); Object *proto = V.objectValue()->prototype; while (proto) { if (O == proto) return Value::fromBoolean(true); proto = proto->prototype; } return Value::fromBoolean(false); } Value ObjectPrototype::method_propertyIsEnumerable(SimpleCallContext *ctx) { String *p = ctx->argument(0).toString(ctx); Object *o = ctx->thisObject.toObject(ctx); PropertyAttributes attrs; o->__getOwnProperty__(p, &attrs); return Value::fromBoolean(attrs.isEnumerable()); } Value ObjectPrototype::method_defineGetter(SimpleCallContext *ctx) { if (ctx->argumentCount < 2) ctx->throwTypeError(); String *prop = ctx->argument(0).toString(ctx); FunctionObject *f = ctx->argument(1).asFunctionObject(); if (!f) ctx->throwTypeError(); Object *o = ctx->thisObject.toObject(ctx); Property pd = Property::fromAccessor(f, 0); o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); return Value::undefinedValue(); } Value ObjectPrototype::method_defineSetter(SimpleCallContext *ctx) { if (ctx->argumentCount < 2) ctx->throwTypeError(); String *prop = ctx->argument(0).toString(ctx); FunctionObject *f = ctx->argument(1).asFunctionObject(); if (!f) ctx->throwTypeError(); Object *o = ctx->thisObject.toObject(ctx); Property pd = Property::fromAccessor(0, f); o->__defineOwnProperty__(ctx, prop, pd, Attr_Accessor); return Value::undefinedValue(); } void ObjectPrototype::toPropertyDescriptor(ExecutionContext *ctx, Value v, Property *desc, PropertyAttributes *attrs) { if (!v.isObject()) ctx->throwTypeError(); Object *o = v.objectValue(); attrs->clear(); desc->setGetter(0); desc->setSetter(0); if (o->__hasProperty__(ctx->engine->id_enumerable)) attrs->setEnumerable(o->get(ctx, ctx->engine->id_enumerable).toBoolean()); if (o->__hasProperty__(ctx->engine->id_configurable)) attrs->setConfigurable(o->get(ctx, ctx->engine->id_configurable).toBoolean()); if (o->__hasProperty__(ctx->engine->id_get)) { Value get = o->get(ctx, ctx->engine->id_get); FunctionObject *f = get.asFunctionObject(); if (f) { desc->setGetter(f); } else if (get.isUndefined()) { desc->setGetter((FunctionObject *)0x1); } else { ctx->throwTypeError(); } attrs->setType(PropertyAttributes::Accessor); } if (o->__hasProperty__(ctx->engine->id_set)) { Value set = o->get(ctx, ctx->engine->id_set); FunctionObject *f = set.asFunctionObject(); if (f) { desc->setSetter(f); } else if (set.isUndefined()) { desc->setSetter((FunctionObject *)0x1); } else { ctx->throwTypeError(); } attrs->setType(PropertyAttributes::Accessor); } if (o->__hasProperty__(ctx->engine->id_writable)) { if (attrs->isAccessor()) ctx->throwTypeError(); attrs->setWritable(o->get(ctx, ctx->engine->id_writable).toBoolean()); // writable forces it to be a data descriptor desc->value = Value::undefinedValue(); } if (o->__hasProperty__(ctx->engine->id_value)) { if (attrs->isAccessor()) ctx->throwTypeError(); desc->value = o->get(ctx, ctx->engine->id_value); attrs->setType(PropertyAttributes::Data); } if (attrs->isGeneric()) desc->value = Value::deletedValue(); } Value ObjectPrototype::fromPropertyDescriptor(ExecutionContext *ctx, const Property *desc, PropertyAttributes attrs) { if (!desc) return Value::undefinedValue(); ExecutionEngine *engine = ctx->engine; // Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name. Object *o = engine->newObject(); Property pd; if (attrs.isData()) { pd.value = desc->value; o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("value")), pd, Attr_Data); pd.value = Value::fromBoolean(attrs.isWritable()); o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("writable")), pd, Attr_Data); } else { pd.value = desc->getter() ? Value::fromObject(desc->getter()) : Value::undefinedValue(); o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("get")), pd, Attr_Data); pd.value = desc->setter() ? Value::fromObject(desc->setter()) : Value::undefinedValue(); o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("set")), pd, Attr_Data); } pd.value = Value::fromBoolean(attrs.isEnumerable()); o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("enumerable")), pd, Attr_Data); pd.value = Value::fromBoolean(attrs.isConfigurable()); o->__defineOwnProperty__(ctx, engine->newString(QStringLiteral("configurable")), pd, Attr_Data); return Value::fromObject(o); }