Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #include "qv4include_p.h"
++ #include "qv4scopedvalue_p.h"
++ #include "qv4jscall_p.h"
++
++ #include <QtQml/qjsengine.h>
++ #if QT_CONFIG(qml_network)
++ #include <QtNetwork/qnetworkrequest.h>
++ #include <QtNetwork/qnetworkreply.h>
++ #endif
++ #include <QtCore/qfile.h>
++ #include <QtQml/qqmlfile.h>
++
++ #include <private/qqmlengine_p.h>
++ #include <private/qqmlscriptblob_p.h>
++ #include <private/qqmlscriptdata_p.h>
++ #include <private/qv4context_p.h>
++ #include <private/qv4engine_p.h>
++ #include <private/qv4functionobject_p.h>
++ #include <private/qv4script_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4)
++ {
++ QV4::Scope scope(v4);
++
++ // XXX It seems inefficient to create this object from scratch each time.
++ QV4::ScopedObject o(scope, v4->newObject());
++ QV4::ScopedString s(scope);
++ QV4::ScopedValue v(scope);
++ o->put((s = v4->newString(QStringLiteral("OK"))), (v = QV4::Value::fromInt32(Ok)));
++ o->put((s = v4->newString(QStringLiteral("LOADING"))), (v = QV4::Value::fromInt32(Loading)));
++ o->put((s = v4->newString(QStringLiteral("NETWORK_ERROR"))), (v = QV4::Value::fromInt32(NetworkError)));
++ o->put((s = v4->newString(QStringLiteral("EXCEPTION"))), (v = QV4::Value::fromInt32(Exception)));
++ return o.asReturnedValue();
++ }
++
++ void QV4Include::populateResultValue(QV4::Object *o, Status status, const QString &statusText)
++ {
++ QV4::Scope scope(o->engine());
++ QV4::ScopedString statusKey(scope, scope.engine->newString(QStringLiteral("status")));
++ QV4::ScopedValue statusValue(scope, QV4::Value::fromInt32(status));
++ o->put(statusKey, statusValue);
++ if (statusText.isEmpty())
++ return;
++
++ QV4::ScopedString textKey(scope, scope.engine->newString(QStringLiteral("statusText")));
++ QV4::ScopedValue textValue(scope, scope.engine->newString(statusText));
++ o->put(textKey, textValue);
++ }
++
++ QV4Include::QV4Include(
++ QV4::Object *result, QV4::FunctionObject *callbackFunction, QV4::QmlContext *qmlContext)
++ {
++ Q_ASSERT(result);
++ QV4::ExecutionEngine *engine = result->engine();
++ m_resultObject.set(engine, result->asReturnedValue());
++ if (callbackFunction)
++ m_callbackFunction.set(engine, callbackFunction->asReturnedValue());
++ if (qmlContext)
++ m_qmlContext.set(engine, qmlContext->asReturnedValue());
++ }
++
++ void QV4Include::callback(QV4::FunctionObject *callback, QV4::Object *result)
++ {
++ if (!callback)
++ return;
++
++ QV4::Scope scope(callback->engine());
++ QV4::JSCallArguments jsCallData(scope, 1);
++ *jsCallData.thisObject = scope.engine->globalObject->asReturnedValue();
++ jsCallData.args[0] = result;
++ callback->call(jsCallData);
++ if (scope.hasException())
++ scope.engine->catchException();
++ }
++
++ /*
++ Documented in qv4engine.cpp
++ */
++ QJSValue QV4Include::method_include(QV4::ExecutionEngine *engine, const QUrl &url,
++ const QJSValue &callbackFunction)
++ {
++ QQmlRefPointer<QQmlContextData> context = engine->callingQmlContext();
++
++ if ((!context || !context->isJSContext()) && engine->qmlEngine()) {
++ return QJSValuePrivate::fromReturnedValue(
++ engine->throwError(
++ QString::fromUtf8(
++ "Qt.include(): Can only be called from JavaScript files")));
++ }
++
++ // The JavaScript compilation unit inherits its context when doing Qt.include().
++ // This means we need to turn fast lookups off at compile time.
++ // Signal this through the URL fragment.
++ QUrl includeUrl = url;
++ includeUrl.setFragment(QLatin1String("include"));
++ QQmlRefPointer<QQmlScriptBlob> scriptBlob = engine->typeLoader()->getScript(includeUrl);
++
++ QV4::Scope scope(engine);
++ QV4::ScopedObject result(scope, resultValue(engine));
++ QV4::ScopedFunctionObject callback(
++ scope, QJSValuePrivate::asReturnedValue(&callbackFunction));
++ QV4::Scoped<QV4::QmlContext> qmlContext(scope, scope.engine->qmlContext());
++
++ if (scriptBlob->isCompleteOrError()) {
++ processScriptBlob(scriptBlob.data(), result, callback, qmlContext);
++ return QJSValuePrivate::fromReturnedValue(result.asReturnedValue());
++ }
++
++ scriptBlob->registerCallback(new QV4Include(result, callback, qmlContext));
++ populateResultValue(result, Loading);
++ return QJSValuePrivate::fromReturnedValue(result.asReturnedValue());
++ }
++
++ void QV4Include::ready(QQmlNotifyingBlob *notifyingBlob)
++ {
++ Q_ASSERT(notifyingBlob);
++ Q_ASSERT(notifyingBlob->type() == QQmlDataBlob::JavaScriptFile);
++
++ processScriptBlob(
++ static_cast<QQmlScriptBlob *>(notifyingBlob), m_resultObject.as<QV4::Object>(),
++ m_callbackFunction.as<QV4::FunctionObject>(), m_qmlContext.as<QV4::QmlContext>());
++
++ delete this;
++ }
++
++ void QV4Include::processScriptBlob(
++ QQmlScriptBlob *scriptBlob, QV4::Object *result, QV4::FunctionObject *callbackFunction,
++ QV4::QmlContext *qmlContext)
++ {
++ Q_ASSERT(scriptBlob);
++ Q_ASSERT(result);
++
++ QV4::Scope scope(result->engine());
++
++ if (scriptBlob->isError()) {
++ const QList<QQmlError> errors = scriptBlob->errors();
++ Q_ASSERT(!errors.isEmpty());
++ const QString errorString = QLatin1String("Error opening source file %1: %2")
++ .arg(scriptBlob->finalUrlString(), errors[0].toString());
++ populateResultValue(result, NetworkError, errorString);
++ callback(callbackFunction, result);
++ return;
++ }
++
++ Q_ASSERT(scriptBlob->isComplete());
++ const auto cu = scope.engine->executableCompilationUnit(
++ scriptBlob->scriptData()->compilationUnit());
++ if (QV4::Function *vmFunction = cu->rootFunction()) {
++ QScopedValueRollback<QV4::Function *> savedGlobal(scope.engine->globalCode, vmFunction);
++ vmFunction->call(nullptr, nullptr, 0, qmlContext ? qmlContext : scope.engine->rootContext());
++ }
++
++ if (scope.hasException()) {
++ QV4::ScopedValue ex(scope, scope.engine->catchException());
++ populateResultValue(result, Exception);
++ QV4::ScopedString exception(scope, scope.engine->newString(QStringLiteral("exception")));
++ result->put(exception, ex);
++ } else {
++ populateResultValue(result, Ok);
++ }
++
++ callback(callbackFunction, result);
++ }
++
++ QT_END_NAMESPACE
++