// // ScriptCache.cpp // libraries/metavoxels/src // // Created by Andrzej Kapolka on 2/4/14. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include #include #include #include #include #include "AttributeRegistry.h" #include "ScriptCache.h" static int scriptValueMetaTypeId = qRegisterMetaType(); static bool scriptValueComparators = QMetaType::registerComparators(); bool operator==(const QScriptValue& first, const QScriptValue& second) { if (first.isUndefined()) { return second.isUndefined(); } else if (first.isNull()) { return second.isNull(); } else if (first.isBool()) { return second.isBool() && first.toBool() == second.toBool(); } else if (first.isNumber()) { return second.isNumber() && first.toNumber() == second.toNumber(); } else if (first.isString()) { return second.isString() && first.toString() == second.toString(); } else if (first.isVariant()) { return second.isVariant() && first.toVariant() == second.toVariant(); } else if (first.isQObject()) { return second.isQObject() && first.toQObject() == second.toQObject(); } else if (first.isQMetaObject()) { return second.isQMetaObject() && first.toQMetaObject() == second.toQMetaObject(); } else if (first.isDate()) { return second.isDate() && first.toDateTime() == second.toDateTime(); } else if (first.isRegExp()) { return second.isRegExp() && first.toRegExp() == second.toRegExp(); } else if (first.isArray()) { if (!second.isArray()) { return false; } int length = first.property(ScriptCache::getInstance()->getLengthString()).toInt32(); if (second.property(ScriptCache::getInstance()->getLengthString()).toInt32() != length) { return false; } for (int i = 0; i < length; i++) { if (first.property(i) != second.property(i)) { return false; } } return true; } else if (first.isObject()) { if (!second.isObject()) { return false; } int propertyCount = 0; for (QScriptValueIterator it(first); it.hasNext(); ) { it.next(); if (second.property(it.scriptName()) != it.value()) { return false; } propertyCount++; } // make sure the second has exactly as many properties as the first for (QScriptValueIterator it(second); it.hasNext(); ) { it.next(); if (--propertyCount < 0) { return false; } } return true; } else { // if none of the above tests apply, first must be invalid return !second.isValid(); } } bool operator!=(const QScriptValue& first, const QScriptValue& second) { return !(first == second); } bool operator<(const QScriptValue& first, const QScriptValue& second) { return first.lessThan(second); } ScriptCache* ScriptCache::getInstance() { static ScriptCache cache; return &cache; } ScriptCache::ScriptCache() : _engine(NULL) { setEngine(new QScriptEngine(this)); } void ScriptCache::setEngine(QScriptEngine* engine) { if (_engine && _engine->parent() == this) { delete _engine; } AttributeRegistry::getInstance()->configureScriptEngine(_engine = engine); _parametersString = engine->toStringHandle("parameters"); _lengthString = engine->toStringHandle("length"); _nameString = engine->toStringHandle("name"); _typeString = engine->toStringHandle("type"); _generatorString = engine->toStringHandle("generator"); } QSharedPointer ScriptCache::getValue(const ParameterizedURL& url) { QSharedPointer value = _networkValues.value(url); if (value.isNull()) { value = QSharedPointer(url.getParameters().isEmpty() ? (NetworkValue*)new RootNetworkValue(getProgram(url.getURL())) : (NetworkValue*)new DerivedNetworkValue(getValue(url.getURL()), url.getParameters())); _networkValues.insert(url, value); } return value; } QSharedPointer ScriptCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { return QSharedPointer(new NetworkProgram(this, url), &Resource::allReferencesCleared); } NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) : Resource(url), _cache(cache) { } void NetworkProgram::downloadFinished(QNetworkReply* reply) { _program = QScriptProgram(QTextStream(reply).readAll(), reply->url().toString()); reply->deleteLater(); finishedLoading(true); emit loaded(); } NetworkValue::~NetworkValue() { } RootNetworkValue::RootNetworkValue(const QSharedPointer& program) : _program(program) { } QScriptValue& RootNetworkValue::getValue() { if (!_value.isValid() && _program->isLoaded()) { _value = _program->getCache()->getEngine()->evaluate(_program->getProgram()); } return _value; } const QList& RootNetworkValue::getParameterInfo() { if (isLoaded() && _parameterInfo.isEmpty()) { ScriptCache* cache = _program->getCache(); QScriptEngine* engine = cache->getEngine(); QScriptValue parameters = _value.property(cache->getParametersString()); if (parameters.isArray()) { int length = parameters.property(cache->getLengthString()).toInt32(); for (int i = 0; i < length; i++) { QScriptValue parameter = parameters.property(i); ParameterInfo info = { engine->toStringHandle(parameter.property(cache->getNameString()).toString()), QMetaType::type(parameter.property(cache->getTypeString()).toString().toUtf8().constData()) }; _parameterInfo.append(info); } } } return _parameterInfo; } DerivedNetworkValue::DerivedNetworkValue(const QSharedPointer& baseValue, const ScriptHash& parameters) : _baseValue(baseValue), _parameters(parameters) { } QScriptValue& DerivedNetworkValue::getValue() { if (!_value.isValid() && _baseValue->isLoaded()) { RootNetworkValue* root = static_cast(_baseValue.data()); ScriptCache* cache = root->getProgram()->getCache(); QScriptValue generator = _baseValue->getValue().property(cache->getGeneratorString()); if (generator.isFunction()) { QScriptValueList arguments; foreach (const ParameterInfo& parameter, root->getParameterInfo()) { arguments.append(cache->getEngine()->newVariant(_parameters.value(parameter.name))); } _value = generator.call(QScriptValue(), arguments); } else { _value = _baseValue->getValue(); } } return _value; }