1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "private/qv4object_p.h" |
5 | #include "private/qv4runtime_p.h" |
6 | #include "private/qv4functionobject_p.h" |
7 | #include "private/qv4errorobject_p.h" |
8 | #include "private/qv4globalobject_p.h" |
9 | #include "private/qv4codegen_p.h" |
10 | #include "private/qv4objectproto_p.h" |
11 | #include "private/qv4mm_p.h" |
12 | #include "private/qv4context_p.h" |
13 | #include "private/qv4script_p.h" |
14 | #include "private/qv4string_p.h" |
15 | #include "private/qv4module_p.h" |
16 | #include "private/qqmlbuiltinfunctions_p.h" |
17 | |
18 | #include <QtCore/QCoreApplication> |
19 | #include <QtCore/QFile> |
20 | #include <QtCore/QFileInfo> |
21 | #include <QtCore/QDateTime> |
22 | #include <private/qqmljsengine_p.h> |
23 | #include <private/qqmljslexer_p.h> |
24 | #include <private/qqmljsparser_p.h> |
25 | #include <private/qqmljsast_p.h> |
26 | |
27 | #include <iostream> |
28 | |
29 | static void showException(QV4::ExecutionContext *ctx, const QV4::Value &exception, const QV4::StackTrace &trace) |
30 | { |
31 | QV4::Scope scope(ctx); |
32 | QV4::ScopedValue ex(scope, exception); |
33 | QV4::ErrorObject *e = ex->as<QV4::ErrorObject>(); |
34 | if (!e) { |
35 | std::cerr << "Uncaught exception: " << qPrintable(ex->toQString()) << std::endl; |
36 | } else { |
37 | std::cerr << "Uncaught exception: " << qPrintable(e->toQStringNoThrow()) << std::endl; |
38 | } |
39 | |
40 | for (const QV4::StackFrame &frame : trace) { |
41 | std::cerr << " at " << qPrintable(frame.function) << " (" << qPrintable(frame.source); |
42 | if (frame.line >= 0) |
43 | std::cerr << ':' << frame.line; |
44 | std::cerr << ')' << std::endl; |
45 | } |
46 | } |
47 | |
48 | int main(int argc, char *argv[]) |
49 | { |
50 | QCoreApplication app(argc, argv); |
51 | QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); |
52 | QStringList args = app.arguments(); |
53 | args.removeFirst(); |
54 | |
55 | bool runAsQml = false; |
56 | bool runAsModule = false; |
57 | bool cache = false; |
58 | |
59 | if (!args.isEmpty()) { |
60 | if (args.constFirst() == QLatin1String("--jit" )) { |
61 | qputenv(varName: "QV4_JIT_CALL_THRESHOLD" , value: QByteArray("0" )); |
62 | args.removeFirst(); |
63 | } |
64 | if (args.constFirst() == QLatin1String("--interpret" )) { |
65 | qputenv(varName: "QV4_FORCE_INTERPRETER" , value: QByteArray("1" )); |
66 | args.removeFirst(); |
67 | } |
68 | |
69 | if (args.constFirst() == QLatin1String("--qml" )) { |
70 | runAsQml = true; |
71 | args.removeFirst(); |
72 | } |
73 | |
74 | if (args.constFirst() == QLatin1String("--module" )) { |
75 | runAsModule = true; |
76 | args.removeFirst(); |
77 | } |
78 | |
79 | if (args.constFirst() == QLatin1String("--cache" )) { |
80 | cache = true; |
81 | args.removeFirst(); |
82 | } |
83 | |
84 | if (args.constFirst() == QLatin1String("--help" )) { |
85 | std::cerr << "Usage: qmljs [|--jit|--interpret|--qml] file..." << std::endl; |
86 | return EXIT_SUCCESS; |
87 | } |
88 | } |
89 | |
90 | QV4::ExecutionEngine vm; |
91 | |
92 | QV4::Scope scope(&vm); |
93 | QV4::ScopedContext ctx(scope, vm.rootContext()); |
94 | |
95 | QV4::GlobalExtensions::init(globalObject: vm.globalObject, extensions: QJSEngine::ConsoleExtension | QJSEngine::GarbageCollectionExtension); |
96 | |
97 | for (const QString &fn : std::as_const(t&: args)) { |
98 | QV4::ScopedValue result(scope); |
99 | if (runAsModule) { |
100 | auto module = vm.loadModule(url: QUrl::fromLocalFile(localfile: QFileInfo(fn).absoluteFilePath())); |
101 | if (module.compiled) { |
102 | if (module.compiled->instantiate(engine: &vm)) |
103 | module.compiled->evaluate(); |
104 | } else if (module.native) { |
105 | // Nothing to do. Native modules have no global code. |
106 | } else { |
107 | vm.throwError(QStringLiteral("Could not load module file" )); |
108 | } |
109 | } else { |
110 | QFile file(fn); |
111 | if (!file.open(flags: QFile::ReadOnly)) { |
112 | std::cerr << "Error: cannot open file " << fn.toUtf8().constData() << std::endl; |
113 | return EXIT_FAILURE; |
114 | } |
115 | QScopedPointer<QV4::Script> script; |
116 | if (cache && QFile::exists(fileName: fn + QLatin1Char('c'))) { |
117 | QQmlRefPointer<QV4::ExecutableCompilationUnit> unit |
118 | = QV4::ExecutableCompilationUnit::create(); |
119 | QString error; |
120 | if (unit->loadFromDisk(url: QUrl::fromLocalFile(localfile: fn), sourceTimeStamp: QFileInfo(fn).lastModified(), errorString: &error)) { |
121 | script.reset(other: new QV4::Script(&vm, nullptr, unit)); |
122 | } else { |
123 | std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl; |
124 | } |
125 | } |
126 | if (!script) { |
127 | QByteArray ba = file.readAll(); |
128 | const QString code = QString::fromUtf8(utf8: ba.constData(), size: ba.size()); |
129 | file.close(); |
130 | |
131 | script.reset(other: new QV4::Script(ctx, QV4::Compiler::ContextType::Global, code, fn)); |
132 | script->parseAsBinding = runAsQml; |
133 | script->parse(); |
134 | } |
135 | if (!scope.hasException()) { |
136 | const auto unit = script->compilationUnit; |
137 | if (cache && unit && !(unit->unitData()->flags & QV4::CompiledData::Unit::StaticData)) { |
138 | if (unit->unitData()->sourceTimeStamp == 0) { |
139 | const_cast<QV4::CompiledData::Unit*>(unit->unitData())->sourceTimeStamp = QFileInfo(fn).lastModified().toMSecsSinceEpoch(); |
140 | } |
141 | QString saveError; |
142 | if (!unit->saveToDisk(unitUrl: QUrl::fromLocalFile(localfile: fn), errorString: &saveError)) { |
143 | std::cout << "Error saving JS cache file: " << qPrintable(saveError) << std::endl; |
144 | } |
145 | } |
146 | // QElapsedTimer t; t.start(); |
147 | result = script->run(); |
148 | // std::cout << t.elapsed() << " ms. elapsed" << std::endl; |
149 | } |
150 | } |
151 | if (scope.hasException()) { |
152 | QV4::StackTrace trace; |
153 | QV4::ScopedValue ex(scope, scope.engine->catchException(trace: &trace)); |
154 | showException(ctx, exception: ex, trace); |
155 | return EXIT_FAILURE; |
156 | } |
157 | if (!result->isUndefined()) { |
158 | if (! qgetenv(varName: "SHOW_EXIT_VALUE" ).isEmpty()) |
159 | std::cout << "exit value: " << qPrintable(result->toQString()) << std::endl; |
160 | } |
161 | } |
162 | |
163 | return EXIT_SUCCESS; |
164 | } |
165 | |