8

QtScript を使用して、開発およびテストの目的でアプリケーションの一部を自動化しています。アサーションをテストしたいところまで来ました。「スタンドアロン アサーション ライブラリ」に基づいていますか? そして、Debian リポジトリで見つけたものは、Should.js に行きました。

require()ノードの機能に依存するため、Qt アプリケーションにロードするのに問題があります。「CommonJSのrequire()をサポートする」から始めて、以下のコードで終わる、これのバージョンを実装してみました。

それを機能させることはできますか、それともこのアプローチでは運命づけられていますか? should.js のビットを 1 つのファイルにコピーしたほうがよいでしょうか? フォークを最新の状態に保つ責任を負いたくありません。(このコードを再配布するつもりはないので、ライセンスは問題ではありません)。

これが私の MCVE です。すみません、これ以上短くできませんでした!

should.cpp

#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QScriptEngine>
#include <QScriptContext>
#include <QScriptContextInfo>
#include <QTextStream>


// Primitive implementation of Node.js require().
// N.B. Supports only .js sources.
QScriptValue require(QScriptContext* context, QScriptEngine* engine)
{
    const QString moduleName = context->argument(0).toString();

    // First, look in our modules cache
    QScriptValue modules = engine->globalObject().property("$MODULES");
    QScriptValue module = modules.property(moduleName);
    if (module.isValid()) {
        auto cached_file = module.property("filename");
        auto time_stamp = module.property("timestamp");
        auto code = module.property("code");
        if (code.isObject() && cached_file.isString() && time_stamp.isDate()) {
            if (QFileInfo(cached_file.toString()).lastModified() == time_stamp.toDateTime()) {
                qDebug() << "found up-to-date module for require of" << moduleName;
                return code;
            } else {
                qDebug() << "cache stale for" << moduleName;
            }
        }
    } else {
        // Prepare a cache entry, as some modules recursively include each
        // other.  This way, they at least get the partial definition of the
        // other, rather than a stack overflow.
        module = engine->newObject();
        modules.setProperty(moduleName, module);
    }

    qDebug() << "require" << moduleName;

    // resolve filename relative to the calling script
    QString filename = moduleName + ".js";
    for (auto *p = context;  p;  p = p->parentContext()) {
        QScriptContextInfo info(p);
        auto parent_file = info.fileName();
        if (parent_file.isEmpty())
            continue;
        // else, we reached a context with a filename
        QDir base_dir = QFileInfo(parent_file).dir();
        filename = base_dir.filePath(filename);
        if (QFile::exists(filename)) {
            break;
        }
    }

    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly)) {
        return context->throwValue(QString("Failed to open %0").arg(moduleName));
    }

    QTextStream in(&file);
    in.setCodec("UTF-8");
    auto script = in.readAll();
    file.close();

#if 0
    // I had to disable this, because it barfs on "get not()" definition - is
    // that a Node extension?  Will it cause me problems even if I get require()
    // working?
    auto syntax_check = QScriptEngine::checkSyntax(script);
    if (syntax_check.state() != QScriptSyntaxCheckResult::Valid) {
        return context->throwValue(QString("%2:%0:%1: Syntax error: %3")
                                   .arg(syntax_check.errorLineNumber())
                                   .arg(syntax_check.errorColumnNumber())
                                   .arg(filename, syntax_check.errorMessage()));
    }
#endif

    // create a new context, and capture the module's exports
    QScriptContext* newContext = engine->pushContext();
    QScriptValue exports = engine->newObject();
    newContext->activationObject().setProperty("exports", exports);
    module.setProperty("code", exports);
    module.setProperty("filename", filename);
    module.setProperty("timestamp", engine->newDate(QFileInfo(filename).lastModified()));
    // run the script
    engine->evaluate(script, filename);
    // get the exports
    module.setProperty("code", newContext->activationObject().property("exports"));
    engine->popContext();
    if (engine->hasUncaughtException())
        return engine->uncaughtException();
    qDebug() << "loaded" << moduleName;
    return exports;
}


int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);
    QScriptEngine engine;

    // register global require() function
    auto global = engine.globalObject();
    global.setProperty("require", engine.newFunction(require));
    global.setProperty("$MODULES", engine.newObject());

    engine.evaluate("var should = require('/usr/lib/nodejs/should/lib/should');");

    if (engine.hasUncaughtException()) {
        qCritical() << engine.uncaughtException().toString().toStdString().c_str();
        qWarning() << engine.uncaughtExceptionBacktrace().join("\n").toStdString().c_str();
        return 1;
    }
    return 0;
}

メイクファイル

check: should
    ./should

CXXFLAGS += -std=c++11 -Wall -Wextra -Werror
CXXFLAGS += -fPIC
CXXFLAGS += $(shell pkg-config --cflags Qt5Script)
LDLIBS += $(shell pkg-config --libs Qt5Script)

出力は

require "/usr/lib/nodejs/should/lib/should" 
require "./util" 
require "./inspect" 
found up-to-date module for require of "./util" 
loaded "./inspect" 
require "assert" 
Failed to open assert 
<eval>() at /usr/lib/nodejs/should/lib/./util.js:126
<native>() at -1
<native>('./util') at -1
<eval>() at /usr/lib/nodejs/should/lib/should.js:8
<native>() at -1
<native>('/usr/lib/nodejs/should/lib/should') at -1
<global>() at 1

require(ついでに -代わりにスタックトレースで実際の関数名を取得するにはどうすれ<native>ばよいですか? スロットがこれを管理するので、できるはずですよね?)

4

1 に答える 1

1

私はそれをもう少し詳しく調べました.C++ Qt requireシステムを書き直すことは、最初に考えていたよりも少し時間がかかります. require-ing コア モジュールを持つライブラリにも問題があります (requireネイティブ モジュールが原因で、未定義の動作が発生する可能性があります - 読み取り: おそらく動作しません)。

アプローチ #1 -C++ require()実装:

node require()質問とリンクで開始されたように、C++ Qt でカスタムを実装します。の仕組みの詳細については、node.js require()こちらを参照してください。検索パスにコアnodeモジュールが必要です (ソース リポジトリrequire()から取得できます)。node.js

アプローチ #2 - 使用browserify

#1 で解決しようとしている問題は、基本的に JavaScript ファイルのロードとキャッシュであるため、同じ目的で既に存在するものを使用しないでください。このようにして、手動作業を回避javascriptし、ブラウザで動作することを強く示すバンドルを作成できます (より制限された環境node.js)。

$ npm install -g browserify
$ npm install expect

index.js

var expect = require('expect');
expect(1).toEqual(1);

そして実行しますbrowserify

$ browserify index.js -o bundle.js

あなたのQt C++

QString script = loadFile("/path/to/bundle.js");
engine.evaluate(script);

私たちは回避策にrequire()たどり着きましたが、相互運用性については確信が持てません。とQtSyntax Errorまた、いくつかのモジュールで QtScript からのいくつかに遭遇しjsたため、最初はそう見えるかもしれませんが、これは特効薬ではありません。

注: これも興味深いプロジェクトです: https://github.com/svalaskevicius/qtjs-generator

于 2015-12-09T22:40:07.297 に答える