Changed commandline and return codes for test harness.

The different backends can now be selected by command-line options,
and when none is specified, masm is used. The difference is that the
environment variable USE_MOTH is not used anymore.

The return codes are 0 or -1, so the test harness can easily check for
errors.

The ECMA test262 harness uses the $ERROR function to report errors. When
the environment variable IN_TEST_HARNESS is set, this function will get
added to the global object.

Change-Id: I291c72332ea50892afb99f9a0a15004ffc81c200
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Erik Verbruggen 2012-11-08 12:45:40 +01:00 committed by Simon Hausmann
parent fe308861dc
commit a55e563279
1 changed files with 117 additions and 46 deletions

163
main.cpp
View File

@ -70,7 +70,8 @@ static inline bool protect(const void *addr, size_t size)
return mprotect(reinterpret_cast<void*>(roundAddr), size + (iaddr - roundAddr), mode) == 0;
}
static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QString &source,
static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName,
const QString &source, bool useInterpreter,
QQmlJS::Codegen::Mode mode = QQmlJS::Codegen::GlobalCode);
namespace builtins {
@ -95,13 +96,36 @@ struct Print: FunctionObject
struct Eval: FunctionObject
{
Eval(Context *scope): FunctionObject(scope) {}
Eval(Context *scope, bool useInterpreter): FunctionObject(scope), useInterpreter(useInterpreter) {}
virtual void call(Context *ctx)
{
const QString code = ctx->argument(0).toString(ctx)->toQString();
evaluate(ctx, QStringLiteral("eval code"), code, QQmlJS::Codegen::EvalCode);
evaluate(ctx, QStringLiteral("eval code"), code, useInterpreter, QQmlJS::Codegen::EvalCode);
}
private:
bool useInterpreter;
};
struct TestHarnessError: FunctionObject
{
TestHarnessError(Context *scope, bool &errorInTestHarness): FunctionObject(scope), errorOccurred(errorInTestHarness) {}
virtual void call(Context *ctx)
{
errorOccurred = true;
for (unsigned int i = 0; i < ctx->argumentCount; ++i) {
String *s = ctx->argument(i).toString(ctx);
if (i)
std::cerr << ' ';
std::cerr << qPrintable(s->toQString());
}
std::cerr << std::endl;
}
bool &errorOccurred;
};
} // builtins
@ -145,7 +169,7 @@ int compileFiles(const QStringList &files)
compile(fileName, source);
}
}
return 0;
return EXIT_SUCCESS;
}
int evaluateCompiledCode(const QStringList &files)
@ -159,7 +183,7 @@ int evaluateCompiledCode(const QStringList &files)
QFunctionPointer ptr = lib.resolve("%entry");
// qDebug("_%%entry resolved to address %p", ptr);
if (!ptr)
return -1;
return EXIT_FAILURE;
void (*code)(VM::Context *) = (void (*)(VM::Context *)) ptr;
VM::ExecutionEngine vm;
@ -175,19 +199,20 @@ int evaluateCompiledCode(const QStringList &files)
std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl;
else
std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
return -2;
return EXIT_FAILURE;
}
code(ctx);
}
return 0;
return EXIT_SUCCESS;
}
#endif
static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QString &source,
QQmlJS::Codegen::Mode mode)
static int evaluate(QQmlJS::VM::Context *ctx, const QString &fileName,
const QString &source, bool useInterpreter,
QQmlJS::Codegen::Mode mode)
{
using namespace QQmlJS;
@ -202,8 +227,6 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
assert(code);
assert(! (size_t(code) & 15));
static bool useMoth = !qgetenv("USE_MOTH").isNull();
{
QQmlJS::Engine ee, *engine = &ee;
Lexer lexer(engine);
@ -224,7 +247,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
Codegen cg;
globalCode = cg(program, &module, mode);
if (useMoth) {
if (useInterpreter) {
Moth::InstructionSelection isel(vm, &module, code);
foreach (IR::Function *function, module.functions)
isel(function);
@ -240,7 +263,7 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
}
if (! globalCode)
return;
return EXIT_FAILURE;
}
if (! ctx->activation.isObject())
@ -256,10 +279,10 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
std::cerr << "Uncaught exception: " << qPrintable(e->value.toString(ctx)->toQString()) << std::endl;
else
std::cerr << "Uncaught exception: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
return;
return EXIT_FAILURE;
}
if (useMoth) {
if (useInterpreter) {
Moth::VME vme;
vme(ctx, code);
} else {
@ -267,9 +290,11 @@ static void evaluate(QQmlJS::VM::Context *ctx, const QString &fileName, const QS
}
if (! ctx->result.isUndefined()) {
if (! qgetenv("SHOW_EXIT_VALUE").isNull())
if (! qgetenv("SHOW_EXIT_VALUE").isEmpty())
std::cout << "exit value: " << qPrintable(ctx->result.toString(ctx)->toQString()) << std::endl;
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
@ -278,38 +303,84 @@ int main(int argc, char *argv[])
QStringList args = app.arguments();
args.removeFirst();
enum {
use_masm,
use_moth,
use_llvm_compiler,
use_llvm_runtime
} mode = use_masm;
if (!args.isEmpty()) {
if (args.first() == QLatin1String("--jit")) {
mode = use_masm;
args.removeFirst();
}
if (args.first() == QLatin1String("--interpret")) {
mode = use_moth;
args.removeFirst();
}
#ifndef QMLJS_NO_LLVM
if (args.isEmpty()) {
std::cerr << "Usage: v4 [--compile|--aot] file..." << std::endl;
return 0;
}
if (args.first() == QLatin1String("--compile")) {
mode = use_llvm_compiler;
args.removeFirst();
}
if (args.first() == QLatin1String("--compile")) {
args.removeFirst();
return compileFiles(args);
} else if (args.first() == QLatin1String("--aot")) {
args.removeFirst();
return evaluateCompiledCode(args);
}
#endif
QQmlJS::VM::ExecutionEngine vm;
QQmlJS::VM::Context *ctx = vm.rootContext;
QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue();
globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")),
QQmlJS::VM::Value::fromObject(new builtins::Print(ctx)));
globalObject->__put__(ctx, vm.identifier(QStringLiteral("eval")),
QQmlJS::VM::Value::fromObject(new builtins::Eval(ctx)));
foreach (const QString &fn, args) {
QFile file(fn);
if (file.open(QFile::ReadOnly)) {
const QString code = QString::fromUtf8(file.readAll());
file.close();
evaluate(vm.rootContext, fn, code);
if (args.first() == QLatin1String("--aot")) {
mode = use_llvm_runtime;
args.removeFirst();
}
#endif // QMLJS_NO_LLVM
if (args.first() == QLatin1String("--help")) {
std::cerr << "Usage: v4 [|--jit|--interpret|--compile|--aot] file..." << std::endl;
return EXIT_SUCCESS;
}
}
switch (mode) {
#ifdef QMLJS_NO_LLVM
case use_llvm_compiler:
case use_llvm_runtime:
std::cerr << "LLVM backend was not built, compiler is unavailable." << std::endl;
return EXIT_FAILURE;
#else // QMLJS_NO_LLVM
case use_llvm_compiler:
return compileFiles(args);
case use_llvm_runtime:
return evaluateCompiledCode(args);
#endif // QMLJS_NO_LLVM
case use_masm:
case use_moth: {
bool useInterpreter = mode == use_moth;
QQmlJS::VM::ExecutionEngine vm;
QQmlJS::VM::Context *ctx = vm.rootContext;
QQmlJS::VM::Object *globalObject = vm.globalObject.objectValue();
globalObject->__put__(ctx, vm.identifier(QStringLiteral("print")),
QQmlJS::VM::Value::fromObject(new builtins::Print(ctx)));
globalObject->__put__(ctx, vm.identifier(QStringLiteral("eval")),
QQmlJS::VM::Value::fromObject(new builtins::Eval(ctx, useInterpreter)));
bool errorInTestHarness = false;
if (!qgetenv("IN_TEST_HARNESS").isEmpty())
globalObject->__put__(ctx, vm.identifier(QStringLiteral("$ERROR")),
QQmlJS::VM::Value::fromObject(new builtins::TestHarnessError(ctx, errorInTestHarness)));
foreach (const QString &fn, args) {
QFile file(fn);
if (file.open(QFile::ReadOnly)) {
const QString code = QString::fromUtf8(file.readAll());
file.close();
int exitCode = evaluate(vm.rootContext, fn, code, useInterpreter);
if (exitCode != EXIT_SUCCESS)
return exitCode;
if (errorInTestHarness)
return EXIT_FAILURE;
}
}
} return EXIT_SUCCESS;
}
}