/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtScript module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL-ONLY$ ** GNU Lesser General Public License Usage ** 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. ** ** If you have questions regarding the use of this file, please contact ** us via http://www.qt.io/contact-us/. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "config.h" #include "qscriptclassobject_p.h" #include "../api/qscriptengine.h" #include "../api/qscriptengine_p.h" #include "../api/qscriptcontext.h" #include "../api/qscriptcontext_p.h" #include "../api/qscriptclass.h" #include "../api/qscriptclasspropertyiterator.h" #include "Error.h" #include "PropertyNameArray.h" Q_DECLARE_METATYPE(QScriptContext*) Q_DECLARE_METATYPE(QScriptValue) Q_DECLARE_METATYPE(QScriptValueList) QT_BEGIN_NAMESPACE namespace QScript { ClassObjectDelegate::ClassObjectDelegate(QScriptClass *scriptClass) : m_scriptClass(scriptClass) { } ClassObjectDelegate::~ClassObjectDelegate() { } QScriptObjectDelegate::Type ClassObjectDelegate::type() const { return ClassObject; } bool ClassObjectDelegate::getOwnPropertySlot(QScriptObject* object, JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::PropertySlot &slot) { QScriptEnginePrivate *engine = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(engine, exec); // for compatibility with the old back-end, normal JS properties // are queried first. if (QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot)) return true; QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); QScriptString scriptName; QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); QScriptStringPrivate::init(scriptName, &scriptName_d); uint id = 0; QScriptClass::QueryFlags flags = m_scriptClass->queryProperty( scriptObject, scriptName, QScriptClass::HandlesReadAccess, &id); if (flags & QScriptClass::HandlesReadAccess) { QScriptValue value = m_scriptClass->property(scriptObject, scriptName, id); if (!value.isValid()) { // The class claims to have the property, but returned an invalid // value. Silently convert to undefined to avoid the invalid value // "escaping" into JS. value = QScriptValue(QScriptValue::UndefinedValue); } slot.setValue(engine->scriptValueToJSCValue(value)); return true; } return false; } bool ClassObjectDelegate::getOwnPropertyDescriptor(QScriptObject *object, JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::PropertyDescriptor &descriptor) { QScriptEnginePrivate *engine = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(engine, exec); // for compatibility with the old back-end, normal JS properties // are queried first. if (QScriptObjectDelegate::getOwnPropertyDescriptor(object, exec, propertyName, descriptor)) return true; QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); QScriptString scriptName; QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); QScriptStringPrivate::init(scriptName, &scriptName_d); uint id = 0; QScriptClass::QueryFlags qflags = m_scriptClass->queryProperty( scriptObject, scriptName, QScriptClass::HandlesReadAccess, &id); if (qflags & QScriptClass::HandlesReadAccess) { QScriptValue::PropertyFlags pflags = m_scriptClass->propertyFlags(scriptObject, scriptName, id); unsigned attribs = 0; if (pflags & QScriptValue::ReadOnly) attribs |= JSC::ReadOnly; if (pflags & QScriptValue::SkipInEnumeration) attribs |= JSC::DontEnum; if (pflags & QScriptValue::Undeletable) attribs |= JSC::DontDelete; if (pflags & QScriptValue::PropertyGetter) attribs |= JSC::Getter; if (pflags & QScriptValue::PropertySetter) attribs |= JSC::Setter; attribs |= pflags & QScriptValue::UserRange; // Rather than calling the getter, we could return an access descriptor here. QScriptValue value = m_scriptClass->property(scriptObject, scriptName, id); if (!value.isValid()) { // The class claims to have the property, but returned an invalid // value. Silently convert to undefined to avoid the invalid value // "escaping" into JS. value = QScriptValue(QScriptValue::UndefinedValue); } descriptor.setDescriptor(engine->scriptValueToJSCValue(value), attribs); return true; } return false; } void ClassObjectDelegate::put(QScriptObject* object, JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::JSValue value, JSC::PutPropertySlot &slot) { QScriptEnginePrivate *engine = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(engine, exec); QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); QScriptString scriptName; QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); QScriptStringPrivate::init(scriptName, &scriptName_d); uint id = 0; QScriptClass::QueryFlags flags = m_scriptClass->queryProperty( scriptObject, scriptName, QScriptClass::HandlesWriteAccess, &id); if (flags & QScriptClass::HandlesWriteAccess) { m_scriptClass->setProperty(scriptObject, scriptName, id, engine->scriptValueFromJSCValue(value)); return; } QScriptObjectDelegate::put(object, exec, propertyName, value, slot); } bool ClassObjectDelegate::deleteProperty(QScriptObject* object, JSC::ExecState *exec, const JSC::Identifier &propertyName) { // ### avoid duplication of put() QScriptEnginePrivate *engine = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(engine, exec); QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); QScriptString scriptName; QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); QScriptStringPrivate::init(scriptName, &scriptName_d); uint id = 0; QScriptClass::QueryFlags flags = m_scriptClass->queryProperty( scriptObject, scriptName, QScriptClass::HandlesWriteAccess, &id); if (flags & QScriptClass::HandlesWriteAccess) { if (m_scriptClass->propertyFlags(scriptObject, scriptName, id) & QScriptValue::Undeletable) return false; m_scriptClass->setProperty(scriptObject, scriptName, id, QScriptValue()); return true; } return QScriptObjectDelegate::deleteProperty(object, exec, propertyName); } void ClassObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecState *exec, JSC::PropertyNameArray &propertyNames, JSC::EnumerationMode mode) { // For compatibility with the old back-end, normal JS properties // are added first. QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); QScriptEnginePrivate *engine = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(engine, exec); QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); QScriptClassPropertyIterator *it = m_scriptClass->newIterator(scriptObject); if (it != 0) { while (it->hasNext()) { it->next(); QString name = it->name().toString(); propertyNames.add(JSC::Identifier(exec, name)); } delete it; } } JSC::CallType ClassObjectDelegate::getCallData(QScriptObject*, JSC::CallData &callData) { if (!m_scriptClass->supportsExtension(QScriptClass::Callable)) return JSC::CallTypeNone; callData.native.function = call; return JSC::CallTypeHost; } JSC::JSValue JSC_HOST_CALL ClassObjectDelegate::call(JSC::ExecState *exec, JSC::JSObject *callee, JSC::JSValue thisValue, const JSC::ArgList &args) { if (!callee->inherits(&QScriptObject::info)) return JSC::throwError(exec, JSC::TypeError, "callee is not a ClassObject object"); QScriptObject *obj = static_cast(callee); QScriptObjectDelegate *delegate = obj->delegate(); if (!delegate || (delegate->type() != QScriptObjectDelegate::ClassObject)) return JSC::throwError(exec, JSC::TypeError, "callee is not a ClassObject object"); QScriptClass *scriptClass = static_cast(delegate)->scriptClass(); QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); JSC::ExecState *oldFrame = eng_p->currentFrame; eng_p->pushContext(exec, thisValue, args, callee); QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); QScriptValue scriptObject = eng_p->scriptValueFromJSCValue(obj); QVariant result = scriptClass->extension(QScriptClass::Callable, QVariant::fromValue(ctx)); eng_p->popContext(); eng_p->currentFrame = oldFrame; return QScriptEnginePrivate::jscValueFromVariant(exec, result); } JSC::ConstructType ClassObjectDelegate::getConstructData(QScriptObject*, JSC::ConstructData &constructData) { if (!m_scriptClass->supportsExtension(QScriptClass::Callable)) return JSC::ConstructTypeNone; constructData.native.function = construct; return JSC::ConstructTypeHost; } JSC::JSObject* ClassObjectDelegate::construct(JSC::ExecState *exec, JSC::JSObject *callee, const JSC::ArgList &args) { Q_ASSERT(callee->inherits(&QScriptObject::info)); QScriptObject *obj = static_cast(callee); QScriptObjectDelegate *delegate = obj->delegate(); QScriptClass *scriptClass = static_cast(delegate)->scriptClass(); QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); JSC::ExecState *oldFrame = eng_p->currentFrame; eng_p->pushContext(exec, JSC::JSValue(), args, callee, true); QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); QScriptValue defaultObject = ctx->thisObject(); QScriptValue result = qvariant_cast(scriptClass->extension(QScriptClass::Callable, QVariant::fromValue(ctx))); if (!result.isObject()) result = defaultObject; eng_p->popContext(); eng_p->currentFrame = oldFrame; return JSC::asObject(eng_p->scriptValueToJSCValue(result)); } bool ClassObjectDelegate::hasInstance(QScriptObject* object, JSC::ExecState *exec, JSC::JSValue value, JSC::JSValue proto) { if (!scriptClass()->supportsExtension(QScriptClass::HasInstance)) return QScriptObjectDelegate::hasInstance(object, exec, value, proto); QScriptValueList args; QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(eng_p, exec); args << eng_p->scriptValueFromJSCValue(object) << eng_p->scriptValueFromJSCValue(value); QVariant result = scriptClass()->extension(QScriptClass::HasInstance, QVariant::fromValue(args)); return result.toBool(); } } // namespace QScript QT_END_NAMESPACE