/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** As a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv4codegen_p.h" #include "qv4util_p.h" #include "qv4engine_p.h" #include #include #include #include #include #include #include #include #include #include #ifndef V4_BOOTSTRAP #include #endif #include #include #ifdef CONST #undef CONST #endif using namespace QV4; using namespace QQmlJS; using namespace AST; static inline void setLocation(IR::Stmt *s, const SourceLocation &loc) { if (s && loc.isValid()) s->location = loc; } Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) , _env(0) , _allowFuncDecls(true) , defaultProgramMode(defaultProgramMode) { } void Codegen::ScanFunctions::operator()(Node *node) { if (node) node->accept(this); } void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) { Environment *e = _cg->newEnvironment(node, _env, compilationMode); if (!e->isStrict) e->isStrict = _cg->_strictMode; _envStack.append(e); _env = e; } void Codegen::ScanFunctions::leaveEnvironment() { _envStack.pop(); _env = _envStack.isEmpty() ? 0 : _envStack.top(); } void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) { for (SourceElements *it = ast; it; it = it->next) { if (StatementSourceElement *stmt = cast(it->element)) { if (ExpressionStatement *expr = cast(stmt->statement)) { if (StringLiteral *strLit = cast(expr->expression)) { // Use the source code, because the StringLiteral's // value might have escape sequences in it, which is not // allowed. if (strLit->literalToken.length < 2) continue; QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); if (str == QStringLiteral("use strict")) { _env->isStrict = true; } else { // TODO: give a warning. } continue; } } } break; } } void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) { if (_env->isStrict) { if (name == QLatin1String("implements") || name == QLatin1String("interface") || name == QLatin1String("let") || name == QLatin1String("package") || name == QLatin1String("private") || name == QLatin1String("protected") || name == QLatin1String("public") || name == QLatin1String("static") || name == QLatin1String("yield")) { _cg->throwSyntaxError(loc, QStringLiteral("Unexpected strict mode reserved word")); } } } void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) { while (parameters) { if (parameters->name == QStringLiteral("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; parameters = parameters->next; } } bool Codegen::ScanFunctions::visit(Program *ast) { enterEnvironment(ast, defaultProgramMode); checkDirectivePrologue(ast->elements); return true; } void Codegen::ScanFunctions::endVisit(Program *) { leaveEnvironment(); } bool Codegen::ScanFunctions::visit(CallExpression *ast) { if (! _env->hasDirectEval) { if (IdentifierExpression *id = cast(ast->base)) { if (id->name == QStringLiteral("eval")) { if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; _env->hasDirectEval = true; } } } int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); return true; } bool Codegen::ScanFunctions::visit(NewMemberExpression *ast) { int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); return true; } bool Codegen::ScanFunctions::visit(ArrayLiteral *ast) { int index = 0; for (ElementList *it = ast->elements; it; it = it->next) { for (Elision *elision = it->elision; elision; elision = elision->next) ++index; ++index; } if (ast->elision) { for (Elision *elision = ast->elision->next; elision; elision = elision->next) ++index; } _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); return true; } bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) { if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); checkName(ast->name, ast->identifierToken); if (ast->name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); return true; } bool Codegen::ScanFunctions::visit(IdentifierExpression *ast) { checkName(ast->name, ast->identifierToken); if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; return true; } bool Codegen::ScanFunctions::visit(ExpressionStatement *ast) { if (FunctionExpression* expr = AST::cast(ast->expression)) { if (!_allowFuncDecls) _cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration")); enterFunction(expr, /*enterName*/ true); Node::accept(expr->formals, this); Node::accept(expr->body, this); leaveEnvironment(); return false; } else { SourceLocation firstToken = ast->firstSourceLocation(); if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QStringLiteral("function")) { _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); } } return true; } bool Codegen::ScanFunctions::visit(FunctionExpression *ast) { enterFunction(ast, /*enterName*/ false); return true; } void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) { if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); } void Codegen::ScanFunctions::endVisit(FunctionExpression *) { leaveEnvironment(); } bool Codegen::ScanFunctions::visit(ObjectLiteral *ast) { int argc = 0; for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { QString key = it->assignment->name->asString(); if (QV4::String::toArrayIndex(key) != UINT_MAX) ++argc; ++argc; if (AST::cast(it->assignment)) ++argc; } _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); Node::accept(ast->properties, this); return false; } bool Codegen::ScanFunctions::visit(PropertyGetterSetter *ast) { TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/0, /*isExpression*/false); return true; } void Codegen::ScanFunctions::endVisit(PropertyGetterSetter *) { leaveEnvironment(); } bool Codegen::ScanFunctions::visit(FunctionDeclaration *ast) { enterFunction(ast, /*enterName*/ true, /*isExpression */false); return true; } void Codegen::ScanFunctions::endVisit(FunctionDeclaration *) { leaveEnvironment(); } bool Codegen::ScanFunctions::visit(WithStatement *ast) { if (_env->isStrict) { _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); return false; } return true; } bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) { { TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); Node::accept(ast->statement, this); } Node::accept(ast->expression, this); return false; } bool Codegen::ScanFunctions::visit(ForStatement *ast) { Node::accept(ast->initialiser, this); Node::accept(ast->condition, this); Node::accept(ast->expression, this); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); Node::accept(ast->statement, this); return false; } bool Codegen::ScanFunctions::visit(LocalForStatement *ast) { Node::accept(ast->declarations, this); Node::accept(ast->condition, this); Node::accept(ast->expression, this); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); Node::accept(ast->statement, this); return false; } bool Codegen::ScanFunctions::visit(ForEachStatement *ast) { Node::accept(ast->initialiser, this); Node::accept(ast->expression, this); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); Node::accept(ast->statement, this); return false; } bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { Node::accept(ast->declaration, this); Node::accept(ast->expression, this); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); Node::accept(ast->statement, this); return false; } bool Codegen::ScanFunctions::visit(ThisExpression *) { _env->usesThis = true; return false; } bool Codegen::ScanFunctions::visit(Block *ast) { TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); Node::accept(ast->statements, this); return false; } void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) { bool wasStrict = false; if (_env) { _env->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. if (expr) _env->enter(name, Environment::FunctionDefinition, expr); if (name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; wasStrict = _env->isStrict; } enterEnvironment(ast, FunctionCode); checkForArguments(formals); _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); _env->formals = formals; if (body) checkDirectivePrologue(body->elements); if (wasStrict || _env->isStrict) { QStringList args; for (FormalParameterList *it = formals; it; it = it->next) { QString arg = it->name.toString(); if (args.contains(arg)) { _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg)); return; } if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) { _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg)); return; } args += arg; } } } Codegen::Codegen(bool strict) : _module(0) , _function(0) , _block(0) , _exitBlock(0) , _returnAddress(0) , _env(0) , _loop(0) , _labelledStatement(0) , _scopeAndFinally(0) , _strictMode(strict) , _fileNameIsUrl(false) , hasError(false) { } void Codegen::generateFromProgram(const QString &fileName, const QString &sourceCode, Program *node, QV4::IR::Module *module, CompilationMode mode, const QStringList &inheritedLocals) { Q_ASSERT(node); _module = module; _env = 0; _module->setFileName(fileName); ScanFunctions scan(this, sourceCode, mode); scan(node); defineFunction(QStringLiteral("%entry"), node, 0, node->elements, inheritedLocals); qDeleteAll(_envMap); _envMap.clear(); } void Codegen::generateFromFunctionExpression(const QString &fileName, const QString &sourceCode, AST::FunctionExpression *ast, QV4::IR::Module *module) { _module = module; _module->setFileName(fileName); _env = 0; ScanFunctions scan(this, sourceCode, GlobalCode); // fake a global environment scan.enterEnvironment(0, FunctionCode); scan(ast); scan.leaveEnvironment(); defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); qDeleteAll(_envMap); _envMap.clear(); } void Codegen::enterEnvironment(Node *node) { _env = _envMap.value(node); Q_ASSERT(_env); } void Codegen::leaveEnvironment() { Q_ASSERT(_env); _env = _env->parent; } void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) { _loop = new Loop(node, breakBlock, continueBlock, _loop); _loop->labelledStatement = _labelledStatement; // consume the enclosing labelled statement _loop->scopeAndFinally = _scopeAndFinally; _labelledStatement = 0; } void Codegen::leaveLoop() { Loop *current = _loop; _loop = _loop->parent; delete current; } IR::Expr *Codegen::member(IR::Expr *base, const QString *name) { if (hasError) return 0; if (base->asTemp() || base->asArgLocal()) return _block->MEMBER(base, name); else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), base); return _block->MEMBER(_block->TEMP(t), name); } } IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index) { if (hasError) return 0; if (! base->asTemp() || base->asArgLocal()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), base); base = _block->TEMP(t); } if (! index->asTemp() || index->asArgLocal()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), index); index = _block->TEMP(t); } Q_ASSERT(base->asTemp() || base->asArgLocal()); Q_ASSERT(index->asTemp() || index->asArgLocal()); return _block->SUBSCRIPT(base->asTemp(), index->asTemp()); } IR::Expr *Codegen::argument(IR::Expr *expr) { if (expr && !expr->asTemp()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), expr); expr = _block->TEMP(t); } return expr; } // keeps references alive, converts other expressions to temps IR::Expr *Codegen::reference(IR::Expr *expr) { if (hasError) return 0; if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember() && !expr->asSubscript()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), expr); expr = _block->TEMP(t); } return expr; } IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr, const SourceLocation &loc) { if (hasError) return 0; Q_ASSERT(op != IR::OpIncrement); Q_ASSERT(op != IR::OpDecrement); if (IR::Const *c = expr->asConst()) { if (c->type == IR::NumberType) { switch (op) { case IR::OpNot: return _block->CONST(IR::BoolType, !c->value); case IR::OpUMinus: return _block->CONST(IR::NumberType, -c->value); case IR::OpUPlus: return expr; case IR::OpCompl: return _block->CONST(IR::NumberType, ~QV4::Primitive::toInt32(c->value)); case IR::OpIncrement: return _block->CONST(IR::NumberType, c->value + 1); case IR::OpDecrement: return _block->CONST(IR::NumberType, c->value - 1); default: break; } } } if (!expr->asTemp() && !expr->asArgLocal()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), expr), loc); expr = _block->TEMP(t); } Q_ASSERT(expr->asTemp() || expr->asArgLocal()); return _block->UNOP(op, expr); } IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AST::SourceLocation &loc) { if (hasError) return 0; if (IR::Const *c1 = left->asConst()) { if (IR::Const *c2 = right->asConst()) { if (c1->type == IR::NumberType && c2->type == IR::NumberType) { switch (op) { case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); case IR::OpBitAnd: return _block->CONST(IR::NumberType, int(c1->value) & int(c2->value)); case IR::OpBitOr: return _block->CONST(IR::NumberType, int(c1->value) | int(c2->value)); case IR::OpBitXor: return _block->CONST(IR::NumberType, int(c1->value) ^ int(c2->value)); case IR::OpDiv: return _block->CONST(IR::NumberType, c1->value / c2->value); case IR::OpEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); case IR::OpNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); case IR::OpStrictEqual: return _block->CONST(IR::BoolType, c1->value == c2->value); case IR::OpStrictNotEqual: return _block->CONST(IR::BoolType, c1->value != c2->value); case IR::OpGe: return _block->CONST(IR::BoolType, c1->value >= c2->value); case IR::OpGt: return _block->CONST(IR::BoolType, c1->value > c2->value); case IR::OpLe: return _block->CONST(IR::BoolType, c1->value <= c2->value); case IR::OpLt: return _block->CONST(IR::BoolType, c1->value < c2->value); case IR::OpLShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) << (QV4::Primitive::toUInt32(c2->value) & 0x1f)); case IR::OpMod: return _block->CONST(IR::NumberType, std::fmod(c1->value, c2->value)); case IR::OpMul: return _block->CONST(IR::NumberType, c1->value * c2->value); case IR::OpOr: return _block->CONST(IR::NumberType, c1->value ? c1->value : c2->value); case IR::OpRShift: return _block->CONST(IR::NumberType, QV4::Primitive::toInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f)); case IR::OpSub: return _block->CONST(IR::NumberType, c1->value - c2->value); case IR::OpURShift: return _block->CONST(IR::NumberType,QV4::Primitive::toUInt32(c1->value) >> (QV4::Primitive::toUInt32(c2->value) & 0x1f)); case IR::OpInstanceof: case IR::OpIn: break; case IR::OpIfTrue: // unary ops case IR::OpNot: case IR::OpUMinus: case IR::OpUPlus: case IR::OpCompl: case IR::OpIncrement: case IR::OpDecrement: case IR::OpInvalid: break; } } } } else if (op == IR::OpAdd) { if (IR::String *s1 = left->asString()) { if (IR::String *s2 = right->asString()) { return _block->STRING(_function->newString(*s1->value + *s2->value)); } } } if (!left->asTemp() && !left->asArgLocal()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), left), loc); left = _block->TEMP(t); } if (!right->asTemp() && !right->asArgLocal()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), right), loc); right = _block->TEMP(t); } Q_ASSERT(left->asTemp() || left->asArgLocal()); Q_ASSERT(right->asTemp() || right->asArgLocal()); return _block->BINOP(op, left, right); } IR::Expr *Codegen::call(IR::Expr *base, IR::ExprList *args) { if (hasError) return 0; base = reference(base); return _block->CALL(base, args); } IR::Stmt *Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op) { if (hasError) return 0; Q_ASSERT(target->isLValue()); if (op != IR::OpInvalid) { return move(target, binop(op, target, source)); } if (!source->asTemp() && !source->asConst() && !target->asTemp() && !source->asArgLocal() && !target->asArgLocal()) { unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); } if (source->asConst() && !target->asTemp() && !target->asArgLocal()) { unsigned t = _block->newTemp(); _block->MOVE(_block->TEMP(t), source); source = _block->TEMP(t); } return _block->MOVE(target, source); } IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { if (hasError) return 0; if (! (cond->asTemp() || cond->asBinop())) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), cond); cond = _block->TEMP(t); } return _block->CJUMP(cond, iftrue, iffalse); } void Codegen::accept(Node *node) { if (hasError) return; if (node) node->accept(this); } void Codegen::statement(Statement *ast) { _block->nextLocation = ast->firstSourceLocation(); accept(ast); } void Codegen::statement(ExpressionNode *ast) { if (! ast) { return; } else { Result r(nx); qSwap(_expr, r); accept(ast); if (hasError) return; qSwap(_expr, r); if (r.format == ex) { if (r->asCall()) { _block->EXP(*r); // the nest nx representation for calls is EXP(CALL(c..)) } else if (r->asTemp() || r->asArgLocal()) { // there is nothing to do } else { unsigned t = _block->newTemp(); move(_block->TEMP(t), *r); } } } } void Codegen::condition(ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { if (ast) { Result r(iftrue, iffalse); qSwap(_expr, r); accept(ast); qSwap(_expr, r); if (r.format == ex) { setLocation(cjump(*r, r.iftrue, r.iffalse), ast->firstSourceLocation()); } } } Codegen::Result Codegen::expression(ExpressionNode *ast) { Result r; if (ast) { qSwap(_expr, r); accept(ast); qSwap(_expr, r); } return r; } Codegen::Result Codegen::sourceElement(SourceElement *ast) { Result r(nx); if (ast) { qSwap(_expr, r); accept(ast); qSwap(_expr, r); } return r; } Codegen::UiMember Codegen::uiObjectMember(UiObjectMember *ast) { UiMember m; if (ast) { qSwap(_uiMember, m); accept(ast); qSwap(_uiMember, m); } return m; } void Codegen::functionBody(FunctionBody *ast) { if (ast) sourceElements(ast->elements); } void Codegen::program(Program *ast) { if (ast) { sourceElements(ast->elements); } } void Codegen::sourceElements(SourceElements *ast) { for (SourceElements *it = ast; it; it = it->next) { sourceElement(it->element); if (hasError) return; } } void Codegen::variableDeclaration(VariableDeclaration *ast) { IR::Expr *initializer = 0; if (!ast->expression) return; Result expr = expression(ast->expression); if (hasError) return; Q_ASSERT(expr.code); initializer = *expr; int initialized = _block->newTemp(); move(_block->TEMP(initialized), initializer); move(identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), _block->TEMP(initialized)); } void Codegen::variableDeclarationList(VariableDeclarationList *ast) { for (VariableDeclarationList *it = ast; it; it = it->next) { variableDeclaration(it->declaration); } } bool Codegen::visit(ArgumentList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(CaseBlock *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(CaseClause *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(CaseClauses *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(Catch *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(DefaultClause *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(ElementList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(Elision *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(Finally *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(FormalParameterList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(FunctionBody *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(Program *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(PropertyAssignmentList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(PropertyNameAndValue *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(PropertyGetterSetter *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(SourceElements *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(StatementList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiArrayMemberList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiImport *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiHeaderItemList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiPragma *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiObjectInitializer *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiObjectMemberList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiParameterList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiProgram *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiQualifiedId *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(UiQualifiedPragmaId *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(VariableDeclaration *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(VariableDeclarationList *) { Q_ASSERT(!"unreachable"); return false; } bool Codegen::visit(Expression *ast) { if (hasError) return false; statement(ast->left); accept(ast->right); return false; } bool Codegen::visit(ArrayLiteral *ast) { if (hasError) return false; IR::ExprList *args = 0; IR::ExprList *current = 0; for (ElementList *it = ast->elements; it; it = it->next) { for (Elision *elision = it->elision; elision; elision = elision->next) { IR::ExprList *arg = _function->New(); if (!current) { args = arg; } else { current->next = arg; } current = arg; current->expr = _block->CONST(IR::MissingType, 0); } Result expr = expression(it->expression); IR::ExprList *arg = _function->New(); if (!current) { args = arg; } else { current->next = arg; } current = arg; IR::Expr *exp = *expr; if (exp->asTemp() || expr->asArgLocal() || exp->asConst()) { current->expr = exp; } else { unsigned value = _block->newTemp(); move(_block->TEMP(value), exp); current->expr = _block->TEMP(value); } } for (Elision *elision = ast->elision; elision; elision = elision->next) { IR::ExprList *arg = _function->New(); if (!current) { args = arg; } else { current->next = arg; } current = arg; current->expr = _block->CONST(IR::MissingType, 0); } const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args)); _expr.code = _block->TEMP(t); return false; } bool Codegen::visit(ArrayMemberExpression *ast) { if (hasError) return false; Result base = expression(ast->base); Result index = expression(ast->expression); _expr.code = subscript(*base, *index); return false; } static IR::AluOp baseOp(int op) { switch ((QSOperator::Op) op) { case QSOperator::InplaceAnd: return IR::OpBitAnd; case QSOperator::InplaceSub: return IR::OpSub; case QSOperator::InplaceDiv: return IR::OpDiv; case QSOperator::InplaceAdd: return IR::OpAdd; case QSOperator::InplaceLeftShift: return IR::OpLShift; case QSOperator::InplaceMod: return IR::OpMod; case QSOperator::InplaceMul: return IR::OpMul; case QSOperator::InplaceOr: return IR::OpBitOr; case QSOperator::InplaceRightShift: return IR::OpRShift; case QSOperator::InplaceURightShift: return IR::OpURShift; case QSOperator::InplaceXor: return IR::OpBitXor; default: return IR::OpInvalid; } } bool Codegen::visit(BinaryExpression *ast) { if (hasError) return false; if (ast->op == QSOperator::And) { if (_expr.accept(cx)) { IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); condition(ast->left, iftrue, _expr.iffalse); _block = iftrue; condition(ast->right, _expr.iftrue, _expr.iffalse); } else { IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); const unsigned r = _block->newTemp(); move(_block->TEMP(r), *expression(ast->left)); setLocation(cjump(_block->TEMP(r), iftrue, endif), ast->operatorToken); _block = iftrue; move(_block->TEMP(r), *expression(ast->right)); _block->JUMP(endif); _expr.code = _block->TEMP(r); _block = endif; } return false; } else if (ast->op == QSOperator::Or) { if (_expr.accept(cx)) { IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); condition(ast->left, _expr.iftrue, iffalse); _block = iffalse; condition(ast->right, _expr.iftrue, _expr.iffalse); } else { IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); const unsigned r = _block->newTemp(); move(_block->TEMP(r), *expression(ast->left)); setLocation(cjump(_block->TEMP(r), endif, iffalse), ast->operatorToken); _block = iffalse; move(_block->TEMP(r), *expression(ast->right)); _block->JUMP(endif); _block = endif; _expr.code = _block->TEMP(r); } return false; } IR::Expr* left = *expression(ast->left); switch (ast->op) { case QSOperator::Or: case QSOperator::And: break; case QSOperator::Assign: { if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; IR::Expr* right = *expression(ast->right); if (!left->isLValue()) { throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue")); return false; } if (_expr.accept(nx)) { move(left, right); } else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), right); move(left, _block->TEMP(t)); _expr.code = _block->TEMP(t); } break; } case QSOperator::InplaceAnd: case QSOperator::InplaceSub: case QSOperator::InplaceDiv: case QSOperator::InplaceAdd: case QSOperator::InplaceLeftShift: case QSOperator::InplaceMod: case QSOperator::InplaceMul: case QSOperator::InplaceOr: case QSOperator::InplaceRightShift: case QSOperator::InplaceURightShift: case QSOperator::InplaceXor: { if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; IR::Expr* right = *expression(ast->right); if (!left->isLValue()) { throwSyntaxError(ast->operatorToken, QStringLiteral("left-hand side of inplace operator is not an lvalue")); return false; } if (_expr.accept(nx)) { move(left, right, baseOp(ast->op)); } else { const unsigned t = _block->newTemp(); move(_block->TEMP(t), right); move(left, _block->TEMP(t), baseOp(ast->op)); _expr.code = left; } break; } case QSOperator::In: case QSOperator::InstanceOf: case QSOperator::Equal: case QSOperator::NotEqual: case QSOperator::Ge: case QSOperator::Gt: case QSOperator::Le: case QSOperator::Lt: case QSOperator::StrictEqual: case QSOperator::StrictNotEqual: { if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), left), ast->operatorToken); left = _block->TEMP(t); } IR::Expr* right = *expression(ast->right); if (_expr.accept(cx)) { setLocation(cjump(binop(IR::binaryOperator(ast->op), left, right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken); } else { IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right, ast->operatorToken); if (e->asConst() || e->asString()) _expr.code = e; else { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), e), ast->operatorToken); _expr.code = _block->TEMP(t); } } break; } case QSOperator::Add: case QSOperator::BitAnd: case QSOperator::BitOr: case QSOperator::BitXor: case QSOperator::Div: case QSOperator::LShift: case QSOperator::Mod: case QSOperator::Mul: case QSOperator::RShift: case QSOperator::Sub: case QSOperator::URShift: { if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), left), ast->operatorToken); left = _block->TEMP(t); } IR::Expr* right = *expression(ast->right); IR::Expr *e = binop(IR::binaryOperator(ast->op), left, right, ast->operatorToken); if (e->asConst() || e->asString()) _expr.code = e; else { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), e), ast->operatorToken); _expr.code = _block->TEMP(t); } break; } } // switch return false; } bool Codegen::visit(CallExpression *ast) { if (hasError) return false; Result base = expression(ast->base); IR::ExprList *args = 0, **args_it = &args; for (ArgumentList *it = ast->arguments; it; it = it->next) { Result arg = expression(it->expression); IR::Expr *actual = argument(*arg); *args_it = _function->New(); (*args_it)->init(actual); args_it = &(*args_it)->next; } _expr.code = call(*base, args); return false; } bool Codegen::visit(ConditionalExpression *ast) { if (hasError) return true; IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); const unsigned t = _block->newTemp(); condition(ast->expression, iftrue, iffalse); _block = iftrue; move(_block->TEMP(t), *expression(ast->ok)); _block->JUMP(endif); _block = iffalse; move(_block->TEMP(t), *expression(ast->ko)); _block->JUMP(endif); _block = endif; _expr.code = _block->TEMP(t); return false; } bool Codegen::visit(DeleteExpression *ast) { if (hasError) return false; IR::Expr* expr = *expression(ast->expression); // Temporaries cannot be deleted IR::ArgLocal *al = expr->asArgLocal(); if (al && al->index < static_cast(_env->members.size())) { // Trying to delete a function argument might throw. if (_function->isStrict) { throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); return false; } _expr.code = _block->CONST(IR::BoolType, 0); return false; } if (_function->isStrict && expr->asName()) { throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); return false; } // [[11.4.1]] Return true if it's not a reference if (expr->asConst() || expr->asString()) { _expr.code = _block->CONST(IR::BoolType, 1); return false; } // Return values from calls are also not a reference, but we have to // perform the call to allow for side effects. if (expr->asCall()) { _block->EXP(expr); _expr.code = _block->CONST(IR::BoolType, 1); return false; } if (expr->asTemp() || (expr->asArgLocal() && expr->asArgLocal()->index >= static_cast(_env->members.size()))) { _expr.code = _block->CONST(IR::BoolType, 1); return false; } IR::ExprList *args = _function->New(); args->init(reference(expr)); _expr.code = call(_block->NAME(IR::Name::builtin_delete, ast->deleteToken.startLine, ast->deleteToken.startColumn), args); return false; } bool Codegen::visit(FalseLiteral *) { if (hasError) return false; if (_expr.accept(cx)) { _block->JUMP(_expr.iffalse); } else { _expr.code = _block->CONST(IR::BoolType, 0); } return false; } bool Codegen::visit(FieldMemberExpression *ast) { if (hasError) return false; Result base = expression(ast->base); _expr.code = member(*base, _function->newString(ast->name.toString())); return false; } bool Codegen::visit(FunctionExpression *ast) { if (hasError) return false; int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); _expr.code = _block->CLOSURE(function); return false; } IR::Expr *Codegen::identifier(const QString &name, int line, int col) { if (hasError) return 0; uint scope = 0; Environment *e = _env; IR::Function *f = _function; while (f && e->parent) { if (f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) return _block->NAME(name, line, col); int index = e->findMember(name); Q_ASSERT (index < e->members.size()); if (index != -1) { IR::ArgLocal *al = _block->LOCAL(index, scope); if (name == QStringLiteral("arguments") || name == QStringLiteral("eval")) al->isArgumentsOrEval = true; return al; } const int argIdx = f->indexOfArgument(&name); if (argIdx != -1) return _block->ARG(argIdx, scope); if (!f->isStrict && f->hasDirectEval) return _block->NAME(name, line, col); ++scope; e = e->parent; f = f->outer; } // This hook allows implementing QML lookup semantics if (IR::Expr *fallback = fallbackNameLookup(name, line, col)) return fallback; if (!e->parent && (!f || !f->insideWithOrCatch) && _env->compilationMode != EvalCode && e->compilationMode != QmlBinding) return _block->GLOBALNAME(name, line, col); // global context or with. Lookup by name return _block->NAME(name, line, col); } IR::Expr *Codegen::fallbackNameLookup(const QString &name, int line, int col) { Q_UNUSED(name) Q_UNUSED(line) Q_UNUSED(col) return 0; } bool Codegen::visit(IdentifierExpression *ast) { if (hasError) return false; _expr.code = identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn); return false; } bool Codegen::visit(NestedExpression *ast) { if (hasError) return false; accept(ast->expression); return false; } bool Codegen::visit(NewExpression *ast) { if (hasError) return false; Result base = expression(ast->expression); IR::Expr *expr = *base; if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), expr); expr = _block->TEMP(t); } _expr.code = _block->NEW(expr, 0); return false; } bool Codegen::visit(NewMemberExpression *ast) { if (hasError) return false; Result base = expression(ast->base); IR::Expr *expr = *base; if (expr && !expr->asTemp() && !expr->asArgLocal() && !expr->asName() && !expr->asMember()) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), expr); expr = _block->TEMP(t); } IR::ExprList *args = 0, **args_it = &args; for (ArgumentList *it = ast->arguments; it; it = it->next) { Result arg = expression(it->expression); IR::Expr *actual = argument(*arg); *args_it = _function->New(); (*args_it)->init(actual); args_it = &(*args_it)->next; } const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->NEW(expr, args)); _expr.code = _block->TEMP(t); return false; } bool Codegen::visit(NotExpression *ast) { if (hasError) return false; Result expr = expression(ast->expression); const unsigned r = _block->newTemp(); setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken); _expr.code = _block->TEMP(r); return false; } bool Codegen::visit(NullExpression *) { if (hasError) return false; if (_expr.accept(cx)) _block->JUMP(_expr.iffalse); else _expr.code = _block->CONST(IR::NullType, 0); return false; } bool Codegen::visit(NumericLiteral *ast) { if (hasError) return false; if (_expr.accept(cx)) { if (ast->value) _block->JUMP(_expr.iftrue); else _block->JUMP(_expr.iffalse); } else { _expr.code = _block->CONST(IR::NumberType, ast->value); } return false; } struct ObjectPropertyValue { ObjectPropertyValue() : value(0) , getter(-1) , setter(-1) {} IR::Expr *value; int getter; // index in _module->functions or -1 if not set int setter; bool hasGetter() const { return getter >= 0; } bool hasSetter() const { return setter >= 0; } }; bool Codegen::visit(ObjectLiteral *ast) { if (hasError) return false; QMap valueMap; for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { QString name = it->assignment->name->asString(); if (PropertyNameAndValue *nv = AST::cast(it->assignment)) { Result value = expression(nv->value); ObjectPropertyValue &v = valueMap[name]; if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.value)) { throwSyntaxError(nv->lastSourceLocation(), QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name)); return false; } valueMap[name].value = *value; } else if (PropertyGetterSetter *gs = AST::cast(it->assignment)) { const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); ObjectPropertyValue &v = valueMap[name]; if (v.value || (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) || (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) { throwSyntaxError(gs->lastSourceLocation(), QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name)); return false; } if (gs->type == PropertyGetterSetter::Getter) v.getter = function; else v.setter = function; } else { Q_UNREACHABLE(); } } // The linked-list arguments to builtin_define_object_literal // begin with a CONST counting the number of key/value pairs, followed by the // key value pairs, followed by the array entries. IR::ExprList *args = _function->New(); IR::Const *entryCountParam = _function->New(); entryCountParam->init(IR::SInt32Type, 0); args->expr = entryCountParam; args->next = 0; IR::ExprList *keyValueEntries = 0; IR::ExprList *currentKeyValueEntry = 0; int keyValueEntryCount = 0; IR::ExprList *arrayEntries = 0; IR::ExprList *currentArrayEntry = 0; for (QMap::iterator it = valueMap.begin(); it != valueMap.end(); ) { IR::ExprList **currentPtr = 0; uint keyAsIndex = QV4::String::toArrayIndex(it.key()); if (keyAsIndex != UINT_MAX) { if (!arrayEntries) { arrayEntries = _function->New(); currentArrayEntry = arrayEntries; } else { currentArrayEntry->next = _function->New(); currentArrayEntry = currentArrayEntry->next; } currentPtr = ¤tArrayEntry; IR::Const *idx = _function->New(); idx->init(IR::UInt32Type, keyAsIndex); (*currentPtr)->expr = idx; } else { if (!keyValueEntries) { keyValueEntries = _function->New(); currentKeyValueEntry = keyValueEntries; } else { currentKeyValueEntry->next = _function->New(); currentKeyValueEntry = currentKeyValueEntry->next; } currentPtr = ¤tKeyValueEntry; (*currentPtr)->expr = _block->NAME(it.key(), 0, 0); keyValueEntryCount++; } IR::ExprList *¤t = *currentPtr; if (it->value) { current->next = _function->New(); current = current->next; current->expr = _block->CONST(IR::BoolType, true); unsigned value = _block->newTemp(); move(_block->TEMP(value), it->value); current->next = _function->New(); current = current->next; current->expr = _block->TEMP(value); } else { current->next = _function->New(); current = current->next; current->expr = _block->CONST(IR::BoolType, false); unsigned getter = _block->newTemp(); unsigned setter = _block->newTemp(); move(_block->TEMP(getter), it->hasGetter() ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); move(_block->TEMP(setter), it->hasSetter() ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); current->next = _function->New(); current = current->next; current->expr = _block->TEMP(getter); current->next = _function->New(); current = current->next; current->expr = _block->TEMP(setter); } it = valueMap.erase(it); } entryCountParam->value = keyValueEntryCount; if (keyValueEntries) args->next = keyValueEntries; if (arrayEntries) { if (currentKeyValueEntry) currentKeyValueEntry->next = arrayEntries; else args->next = arrayEntries; } const unsigned t = _block->newTemp(); move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal, ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); _expr.code = _block->TEMP(t); return false; } bool Codegen::visit(PostDecrementExpression *ast) { if (hasError) return false; Result expr = expression(ast->base); if (!expr->isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; } if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken)) return false; const unsigned oldValue = _block->newTemp(); setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->decrementToken)), ast->decrementToken); const unsigned newValue = _block->newTemp(); setLocation(move(_block->TEMP(newValue), binop(IR::OpSub, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->decrementToken)), ast->decrementToken); setLocation(move(*expr, _block->TEMP(newValue)), ast->decrementToken); if (!_expr.accept(nx)) _expr.code = _block->TEMP(oldValue); return false; } bool Codegen::visit(PostIncrementExpression *ast) { if (hasError) return false; Result expr = expression(ast->base); if (!expr->isLValue()) { throwReferenceError(ast->base->lastSourceLocation(), QStringLiteral("Invalid left-hand side expression in postfix operation")); return false; } if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken)) return false; const unsigned oldValue = _block->newTemp(); setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->incrementToken)), ast->incrementToken); const unsigned newValue = _block->newTemp(); setLocation(move(_block->TEMP(newValue), binop(IR::OpAdd, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->incrementToken)), ast->incrementToken); setLocation(move(*expr, _block->TEMP(newValue)), ast->incrementToken); if (!_expr.accept(nx)) _expr.code = _block->TEMP(oldValue); return false; } bool Codegen::visit(PreDecrementExpression *ast) { if (hasError) return false; Result expr = expression(ast->expression); if (!expr->isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; } if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->decrementToken)) return false; IR::Expr *op = binop(IR::OpSub, *expr, _block->CONST(IR::NumberType, 1), ast->decrementToken); if (_expr.accept(nx)) { setLocation(move(*expr, op), ast->decrementToken); } else { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), op), ast->decrementToken); setLocation(move(*expr, _block->TEMP(t)), ast->decrementToken); _expr.code = _block->TEMP(t); } return false; } bool Codegen::visit(PreIncrementExpression *ast) { if (hasError) return false; Result expr = expression(ast->expression); if (!expr->isLValue()) { throwReferenceError(ast->expression->lastSourceLocation(), QStringLiteral("Prefix ++ operator applied to value that is not a reference.")); return false; } if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(*expr, ast->incrementToken)) return false; IR::Expr *op = binop(IR::OpAdd, unop(IR::OpUPlus, *expr, ast->incrementToken), _block->CONST(IR::NumberType, 1), ast->incrementToken); if (_expr.accept(nx)) { setLocation(move(*expr, op), ast->incrementToken); } else { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), op), ast->incrementToken); setLocation(move(*expr, _block->TEMP(t)), ast->incrementToken); _expr.code = _block->TEMP(t); } return false; } bool Codegen::visit(RegExpLiteral *ast) { if (hasError) return false; _expr.code = _block->REGEXP(_function->newString(ast->pattern.toString()), ast->flags); return false; } bool Codegen::visit(StringLiteral *ast) { if (hasError) return false; _expr.code = _block->STRING(_function->newString(ast->value.toString())); return false; } bool Codegen::visit(ThisExpression *ast) { if (hasError) return false; _expr.code = _block->NAME(QStringLiteral("this"), ast->thisToken.startLine, ast->thisToken.startColumn); return false; } bool Codegen::visit(TildeExpression *ast) { if (hasError) return false; Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken); _expr.code = _block->TEMP(t); return false; } bool Codegen::visit(TrueLiteral *) { if (hasError) return false; if (_expr.accept(cx)) { _block->JUMP(_expr.iftrue); } else { _expr.code = _block->CONST(IR::BoolType, 1); } return false; } bool Codegen::visit(TypeOfExpression *ast) { if (hasError) return false; Result expr = expression(ast->expression); IR::ExprList *args = _function->New(); args->init(reference(*expr)); _expr.code = call(_block->NAME(IR::Name::builtin_typeof, ast->typeofToken.startLine, ast->typeofToken.startColumn), args); return false; } bool Codegen::visit(UnaryMinusExpression *ast) { if (hasError) return false; Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), unop(IR::OpUMinus, *expr, ast->minusToken)), ast->minusToken); _expr.code = _block->TEMP(t); return false; } bool Codegen::visit(UnaryPlusExpression *ast) { if (hasError) return false; Result expr = expression(ast->expression); const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), unop(IR::OpUPlus, *expr, ast->plusToken)), ast->plusToken); _expr.code = _block->TEMP(t); return false; } bool Codegen::visit(VoidExpression *ast) { if (hasError) return false; statement(ast->expression); _expr.code = _block->CONST(IR::UndefinedType, 0); return false; } bool Codegen::visit(FunctionDeclaration * ast) { if (hasError) return false; if (_env->compilationMode == QmlBinding) move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); _expr.accept(nx); return false; } int Codegen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body, const QStringList &inheritedLocals) { Loop *loop = 0; qSwap(_loop, loop); QStack exceptionHandlers; qSwap(_exceptionHandlers, exceptionHandlers); ScopeAndFinally *scopeAndFinally = 0; enterEnvironment(ast); IR::Function *function = _module->newFunction(name, _function); int functionIndex = _module->functions.count() - 1; IR::BasicBlock *entryBlock = function->newBasicBlock(0); IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock); function->hasDirectEval = _env->hasDirectEval || _env->compilationMode == EvalCode || _module->debugMode; // Conditional breakpoints are like eval in the function function->usesArgumentsObject = _env->parent && (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); function->usesThis = _env->usesThis; function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); function->isStrict = _env->isStrict; function->isNamedExpression = _env->isNamedFunctionExpression; AST::SourceLocation loc = ast->firstSourceLocation(); function->line = loc.startLine; function->column = loc.startColumn; if (function->usesArgumentsObject) _env->enter(QStringLiteral("arguments"), Environment::VariableDeclaration); // variables in global code are properties of the global context object, not locals as with other functions. if (_env->compilationMode == FunctionCode || _env->compilationMode == QmlBinding) { unsigned t = 0; for (Environment::MemberMap::iterator it = _env->members.begin(), end = _env->members.end(); it != end; ++it) { const QString &local = it.key(); function->LOCAL(local); (*it).index = t; entryBlock->MOVE(entryBlock->LOCAL(t, 0), entryBlock->CONST(IR::UndefinedType, 0)); ++t; } } else { if (!_env->isStrict) { foreach (const QString &inheritedLocal, inheritedLocals) { function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); Environment::Member member = { Environment::UndefinedMember, static_cast(tempIndex), 0 }; _env->members.insert(inheritedLocal, member); } } IR::ExprList *args = 0; for (Environment::MemberMap::const_iterator it = _env->members.constBegin(), cend = _env->members.constEnd(); it != cend; ++it) { const QString &local = it.key(); IR::ExprList *next = function->New(); next->expr = entryBlock->NAME(local, 0, 0); next->next = args; args = next; } if (args) { IR::ExprList *next = function->New(); next->expr = entryBlock->CONST(IR::BoolType, false); // ### Investigate removal of bool deletable next->next = args; args = next; entryBlock->EXP(entryBlock->CALL(entryBlock->NAME(IR::Name::builtin_declare_vars, 0, 0), args)); } } unsigned returnAddress = entryBlock->newTemp(); entryBlock->MOVE(entryBlock->TEMP(returnAddress), entryBlock->CONST(IR::UndefinedType, 0)); setLocation(exitBlock->RET(exitBlock->TEMP(returnAddress)), ast->lastSourceLocation()); qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); qSwap(_scopeAndFinally, scopeAndFinally); for (FormalParameterList *it = formals; it; it = it->next) { _function->RECEIVE(it->name.toString()); } foreach (const Environment::Member &member, _env->members) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); if (! _env->parent) { move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), _block->CLOSURE(function)); } else { Q_ASSERT(member.index >= 0); move(_block->LOCAL(member.index, 0), _block->CLOSURE(function)); } } } if (_function->usesArgumentsObject) { move(identifier(QStringLiteral("arguments"), ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), _block->CALL(_block->NAME(IR::Name::builtin_setup_argument_object, ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); } if (_function->usesThis && !_function->isStrict) { // make sure we convert this to an object _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_convert_this_to_object, ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); } beginFunctionBodyHook(); sourceElements(body); _function->addBasicBlock(_exitBlock); _block->JUMP(_exitBlock); qSwap(_function, function); qSwap(_block, entryBlock); qSwap(_exitBlock, exitBlock); qSwap(_returnAddress, returnAddress); qSwap(_scopeAndFinally, scopeAndFinally); qSwap(_exceptionHandlers, exceptionHandlers); qSwap(_loop, loop); leaveEnvironment(); return functionIndex; } bool Codegen::visit(FunctionSourceElement *ast) { if (hasError) return false; statement(ast->declaration); return false; } bool Codegen::visit(StatementSourceElement *ast) { if (hasError) return false; statement(ast->statement); return false; } bool Codegen::visit(Block *ast) { if (hasError) return false; for (StatementList *it = ast->statements; it; it = it->next) { statement(it->statement); } return false; } bool Codegen::visit(BreakStatement *ast) { if (hasError) return false; if (!_loop) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop")); return false; } Loop *loop = 0; if (ast->label.isEmpty()) loop = _loop; else { for (loop = _loop; loop; loop = loop->parent) { if (loop->labelledStatement && loop->labelledStatement->label == ast->label) break; } if (!loop) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); return false; } } unwindException(loop->scopeAndFinally); _block->JUMP(loop->breakBlock); return false; } bool Codegen::visit(ContinueStatement *ast) { if (hasError) return false; Loop *loop = 0; if (ast->label.isEmpty()) { for (loop = _loop; loop; loop = loop->parent) { if (loop->continueBlock) break; } } else { for (loop = _loop; loop; loop = loop->parent) { if (loop->labelledStatement && loop->labelledStatement->label == ast->label) { if (!loop->continueBlock) loop = 0; break; } } if (!loop) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString())); return false; } } if (!loop) { throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("continue outside of loop")); return false; } unwindException(loop->scopeAndFinally); _block->JUMP(loop->continueBlock); return false; } bool Codegen::visit(DebuggerStatement *) { Q_UNIMPLEMENTED(); return false; } bool Codegen::visit(DoWhileStatement *ast) { if (hasError) return true; IR::BasicBlock *loopbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *loopcond = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *loopend = _function->newBasicBlock(exceptionHandler()); enterLoop(ast, loopend, loopcond); _block->JUMP(loopbody); _block = loopbody; statement(ast->statement); _block->JUMP(loopcond); _block = loopcond; condition(ast->expression, loopbody, loopend); _block = loopend; leaveLoop(); return false; } bool Codegen::visit(EmptyStatement *) { if (hasError) return true; return false; } bool Codegen::visit(ExpressionStatement *ast) { if (hasError) return true; if (_env->compilationMode == EvalCode || _env->compilationMode == QmlBinding) { Result e = expression(ast->expression); if (*e) move(_block->TEMP(_returnAddress), *e); } else { statement(ast->expression); } return false; } bool Codegen::visit(ForEachStatement *ast) { if (hasError) return true; IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); int objectToIterateOn = _block->newTemp(); move(_block->TEMP(objectToIterateOn), *expression(ast->expression)); IR::ExprList *args = _function->New(); args->init(_block->TEMP(objectToIterateOn)); int iterator = _block->newTemp(); move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); enterLoop(ast, foreachend, foreachin); _block->JUMP(foreachin); _block = foreachbody; int temp = _block->newTemp(); move(*expression(ast->initialiser), _block->TEMP(temp)); statement(ast->statement); _block->JUMP(foreachin); _block = foreachin; args = _function->New(); args->init(_block->TEMP(iterator)); move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); int null = _block->newTemp(); move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken); _block = foreachend; leaveLoop(); return false; } bool Codegen::visit(ForStatement *ast) { if (hasError) return true; IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler()); statement(ast->initialiser); _block->JUMP(forcond); enterLoop(ast, forend, forstep); _block = forcond; if (ast->condition) condition(ast->condition, forbody, forend); else _block->JUMP(forbody); _block = forbody; statement(ast->statement); _block->JUMP(forstep); _block = forstep; statement(ast->expression); _block->JUMP(forcond); _block = forend; leaveLoop(); return false; } bool Codegen::visit(IfStatement *ast) { if (hasError) return true; IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(exceptionHandler()) : 0; IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler()); condition(ast->expression, iftrue, ast->ko ? iffalse : endif); _block = iftrue; statement(ast->ok); _block->JUMP(endif); if (ast->ko) { _block = iffalse; statement(ast->ko); _block->JUMP(endif); } _block = endif; return false; } bool Codegen::visit(LabelledStatement *ast) { if (hasError) return true; // check that no outer loop contains the label Loop *l = _loop; while (l) { if (l->labelledStatement && l->labelledStatement->label == ast->label) { QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString()); throwSyntaxError(ast->firstSourceLocation(), error); return false; } l = l->parent; } _labelledStatement = ast; if (AST::cast(ast->statement) || AST::cast(ast->statement) || AST::cast(ast->statement) || AST::cast(ast->statement) || AST::cast(ast->statement) || AST::cast(ast->statement) || AST::cast(ast->statement)) { statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop. } else { IR::BasicBlock *breakBlock = _function->newBasicBlock(exceptionHandler()); enterLoop(ast->statement, breakBlock, /*continueBlock*/ 0); statement(ast->statement); _block->JUMP(breakBlock); _block = breakBlock; leaveLoop(); } return false; } bool Codegen::visit(LocalForEachStatement *ast) { if (hasError) return true; IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler()); variableDeclaration(ast->declaration); int iterator = _block->newTemp(); move(_block->TEMP(iterator), *expression(ast->expression)); IR::ExprList *args = _function->New(); args->init(_block->TEMP(iterator)); move(_block->TEMP(iterator), _block->CALL(_block->NAME(IR::Name::builtin_foreach_iterator_object, 0, 0), args)); _block->JUMP(foreachin); enterLoop(ast, foreachend, foreachin); _block = foreachbody; int temp = _block->newTemp(); move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); statement(ast->statement); _block->JUMP(foreachin); _block = foreachin; args = _function->New(); args->init(_block->TEMP(iterator)); move(_block->TEMP(temp), _block->CALL(_block->NAME(IR::Name::builtin_foreach_next_property_name, 0, 0), args)); int null = _block->newTemp(); move(_block->TEMP(null), _block->CONST(IR::NullType, 0)); setLocation(cjump(_block->BINOP(IR::OpStrictNotEqual, _block->TEMP(temp), _block->TEMP(null)), foreachbody, foreachend), ast->forToken); _block = foreachend; leaveLoop(); return false; } bool Codegen::visit(LocalForStatement *ast) { if (hasError) return true; IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *forend = _function->newBasicBlock(exceptionHandler()); variableDeclarationList(ast->declarations); _block->JUMP(forcond); enterLoop(ast, forend, forstep); _block = forcond; if (ast->condition) condition(ast->condition, forbody, forend); else _block->JUMP(forbody); _block = forbody; statement(ast->statement); _block->JUMP(forstep); _block = forstep; statement(ast->expression); _block->JUMP(forcond); _block = forend; leaveLoop(); return false; } bool Codegen::visit(ReturnStatement *ast) { if (hasError) return true; if (_env->compilationMode != FunctionCode && _env->compilationMode != QmlBinding) { throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } if (ast->expression) { Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); } unwindException(0); _block->JUMP(_exitBlock); return false; } bool Codegen::visit(SwitchStatement *ast) { if (hasError) return true; IR::BasicBlock *switchend = _function->newBasicBlock(exceptionHandler()); if (ast->block) { int lhs = _block->newTemp(); move(_block->TEMP(lhs), *expression(ast->expression)); IR::BasicBlock *switchcond = _function->newBasicBlock(exceptionHandler()); _block->JUMP(switchcond); IR::BasicBlock *previousBlock = 0; QHash blockMap; enterLoop(ast, switchend, 0); for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; _block = _function->newBasicBlock(exceptionHandler()); blockMap[clause] = _block; if (previousBlock && !previousBlock->isTerminated()) previousBlock->JUMP(_block); for (StatementList *it2 = clause->statements; it2; it2 = it2->next) statement(it2->statement); previousBlock = _block; } if (ast->block->defaultClause) { _block = _function->newBasicBlock(exceptionHandler()); blockMap[ast->block->defaultClause] = _block; if (previousBlock && !previousBlock->isTerminated()) previousBlock->JUMP(_block); for (StatementList *it2 = ast->block->defaultClause->statements; it2; it2 = it2->next) statement(it2->statement); previousBlock = _block; } for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; _block = _function->newBasicBlock(exceptionHandler()); blockMap[clause] = _block; if (previousBlock && !previousBlock->isTerminated()) previousBlock->JUMP(_block); for (StatementList *it2 = clause->statements; it2; it2 = it2->next) statement(it2->statement); previousBlock = _block; } leaveLoop(); _block->JUMP(switchend); _block = switchcond; for (CaseClauses *it = ast->block->clauses; it; it = it->next) { CaseClause *clause = it->clause; Result rhs = expression(clause->expression); IR::BasicBlock *iftrue = blockMap[clause]; IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken); _block = iffalse; } for (CaseClauses *it = ast->block->moreClauses; it; it = it->next) { CaseClause *clause = it->clause; Result rhs = expression(clause->expression); IR::BasicBlock *iftrue = blockMap[clause]; IR::BasicBlock *iffalse = _function->newBasicBlock(exceptionHandler()); setLocation(cjump(binop(IR::OpStrictEqual, _block->TEMP(lhs), *rhs), iftrue, iffalse), clause->caseToken); _block = iffalse; } if (DefaultClause *defaultClause = ast->block->defaultClause) { setLocation(_block->JUMP(blockMap[ast->block->defaultClause]), defaultClause->defaultToken); } } _block->JUMP(switchend); _block = switchend; return false; } bool Codegen::visit(ThrowStatement *ast) { if (hasError) return true; Result expr = expression(ast->expression); move(_block->TEMP(_returnAddress), *expr); IR::ExprList *throwArgs = _function->New(); throwArgs->expr = _block->TEMP(_returnAddress); _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), throwArgs)); return false; } bool Codegen::visit(TryStatement *ast) { if (hasError) return true; _function->hasTry = true; if (_function->isStrict && ast->catchExpression && (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) { throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode")); return false; } IR::BasicBlock *surroundingExceptionHandler = exceptionHandler(); // We always need a finally body to clean up the exception handler // exceptions thrown in finally get caught by the surrounding catch block IR::BasicBlock *finallyBody = 0; IR::BasicBlock *catchBody = 0; IR::BasicBlock *catchExceptionHandler = 0; IR::BasicBlock *end = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock); if (ast->finallyExpression) finallyBody = _function->newBasicBlock(surroundingExceptionHandler, IR::Function::DontInsertBlock); if (ast->catchExpression) { // exception handler for the catch body catchExceptionHandler = _function->newBasicBlock(0, IR::Function::DontInsertBlock); pushExceptionHandler(catchExceptionHandler); catchBody = _function->newBasicBlock(catchExceptionHandler, IR::Function::DontInsertBlock); popExceptionHandler(); pushExceptionHandler(catchBody); } else { Q_ASSERT(finallyBody); pushExceptionHandler(finallyBody); } IR::BasicBlock *tryBody = _function->newBasicBlock(exceptionHandler()); _block->JUMP(tryBody); ScopeAndFinally tcf(_scopeAndFinally, ast->finallyExpression); _scopeAndFinally = &tcf; _block = tryBody; statement(ast->statement); _block->JUMP(finallyBody ? finallyBody : end); popExceptionHandler(); if (ast->catchExpression) { pushExceptionHandler(catchExceptionHandler); _function->addBasicBlock(catchBody); _block = catchBody; ++_function->insideWithOrCatch; IR::ExprList *catchArgs = _function->New(); catchArgs->init(_block->STRING(_function->newString(ast->catchExpression->name.toString()))); _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_catch_scope, 0, 0), catchArgs)); { ScopeAndFinally scope(_scopeAndFinally, ScopeAndFinally::CatchScope); _scopeAndFinally = &scope; statement(ast->catchExpression->statement); _scopeAndFinally = scope.parent; } _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); --_function->insideWithOrCatch; _block->JUMP(finallyBody ? finallyBody : end); popExceptionHandler(); _function->addBasicBlock(catchExceptionHandler); catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); if (finallyBody || surroundingExceptionHandler) catchExceptionHandler->JUMP(finallyBody ? finallyBody : surroundingExceptionHandler); else catchExceptionHandler->EXP(catchExceptionHandler->CALL(catchExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); } _scopeAndFinally = tcf.parent; if (finallyBody) { _function->addBasicBlock(finallyBody); _block = finallyBody; int hasException = _block->newTemp(); move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_unwind_exception, /*line*/0, /*column*/0), 0)); if (ast->finallyExpression && ast->finallyExpression->statement) statement(ast->finallyExpression->statement); IR::ExprList *arg = _function->New(); arg->expr = _block->TEMP(hasException); _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_throw, /*line*/0, /*column*/0), arg)); _block->JUMP(end); } _function->addBasicBlock(end); _block = end; return false; } void Codegen::unwindException(Codegen::ScopeAndFinally *outest) { int savedDepthForWidthOrCatch = _function->insideWithOrCatch; ScopeAndFinally *scopeAndFinally = _scopeAndFinally; qSwap(_scopeAndFinally, scopeAndFinally); while (_scopeAndFinally != outest) { switch (_scopeAndFinally->type) { case ScopeAndFinally::WithScope: // fall through case ScopeAndFinally::CatchScope: _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0))); _scopeAndFinally = _scopeAndFinally->parent; --_function->insideWithOrCatch; break; case ScopeAndFinally::TryScope: { ScopeAndFinally *tc = _scopeAndFinally; _scopeAndFinally = tc->parent; if (tc->finally && tc->finally->statement) statement(tc->finally->statement); break; } } } qSwap(_scopeAndFinally, scopeAndFinally); _function->insideWithOrCatch = savedDepthForWidthOrCatch; } bool Codegen::visit(VariableStatement *ast) { if (hasError) return true; variableDeclarationList(ast->declarations); return false; } bool Codegen::visit(WhileStatement *ast) { if (hasError) return true; IR::BasicBlock *whilecond = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *whilebody = _function->newBasicBlock(exceptionHandler()); IR::BasicBlock *whileend = _function->newBasicBlock(exceptionHandler()); enterLoop(ast, whileend, whilecond); _block->JUMP(whilecond); _block = whilecond; condition(ast->expression, whilebody, whileend); _block = whilebody; statement(ast->statement); _block->JUMP(whilecond); _block = whileend; leaveLoop(); return false; } bool Codegen::visit(WithStatement *ast) { if (hasError) return true; _function->hasWith = true; // need an exception handler for with to cleanup the with scope IR::BasicBlock *withExceptionHandler = _function->newBasicBlock(exceptionHandler()); withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); if (!exceptionHandler()) withExceptionHandler->EXP(withExceptionHandler->CALL(withExceptionHandler->NAME(IR::Name::builtin_rethrow, 0, 0), 0)); else withExceptionHandler->JUMP(exceptionHandler()); pushExceptionHandler(withExceptionHandler); IR::BasicBlock *withBlock = _function->newBasicBlock(exceptionHandler()); _block->JUMP(withBlock); _block = withBlock; int withObject = _block->newTemp(); _block->MOVE(_block->TEMP(withObject), *expression(ast->expression)); IR::ExprList *args = _function->New(); args->init(_block->TEMP(withObject)); _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_push_with_scope, 0, 0), args)); ++_function->insideWithOrCatch; { ScopeAndFinally scope(_scopeAndFinally); _scopeAndFinally = &scope; statement(ast->statement); _scopeAndFinally = scope.parent; } --_function->insideWithOrCatch; _block->EXP(_block->CALL(_block->NAME(IR::Name::builtin_pop_scope, 0, 0), 0)); popExceptionHandler(); IR::BasicBlock *next = _function->newBasicBlock(exceptionHandler()); _block->JUMP(next); _block = next; return false; } bool Codegen::visit(UiArrayBinding *) { Q_ASSERT(!"not implemented"); return false; } bool Codegen::visit(UiObjectBinding *) { Q_ASSERT(!"not implemented"); return false; } bool Codegen::visit(UiObjectDefinition *) { Q_ASSERT(!"not implemented"); return false; } bool Codegen::visit(UiPublicMember *) { Q_ASSERT(!"not implemented"); return false; } bool Codegen::visit(UiScriptBinding *) { Q_ASSERT(!"not implemented"); return false; } bool Codegen::visit(UiSourceElement *) { Q_ASSERT(!"not implemented"); return false; } bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) { if (!_env->isStrict) return false; if (IR::Name *n = expr->asName()) { if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments")) return false; } else if (IR::ArgLocal *al = expr->asArgLocal()) { if (!al->isArgumentsOrEval) return false; } else { return false; } throwSyntaxError(loc, QStringLiteral("Variable name may not be eval or arguments in strict mode")); return true; } void Codegen::throwSyntaxError(const SourceLocation &loc, const QString &detail) { if (hasError) return; hasError = true; QQmlJS::DiagnosticMessage error; error.message = detail; error.loc = loc; _errors << error; } void Codegen::throwReferenceError(const SourceLocation &loc, const QString &detail) { if (hasError) return; hasError = true; QQmlJS::DiagnosticMessage error; error.message = detail; error.loc = loc; _errors << error; } QList Codegen::errors() const { return _errors; } #ifndef V4_BOOTSTRAP QList Codegen::qmlErrors() const { QList qmlErrors; // Short circuit to avoid costly (de)heap allocation of QUrl if there are no errors. if (_errors.size() == 0) return qmlErrors; qmlErrors.reserve(_errors.size()); QUrl url(_fileNameIsUrl ? QUrl(_module->fileName) : QUrl::fromLocalFile(_module->fileName)); foreach (const QQmlJS::DiagnosticMessage &msg, _errors) { QQmlError e; e.setUrl(url); e.setLine(msg.loc.startLine); e.setColumn(msg.loc.startColumn); e.setDescription(msg.message); qmlErrors << e; } return qmlErrors; } void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) { if (hasError) return; hasError = true; engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); } void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) { if (hasError) return; hasError = true; engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn); } #endif // V4_BOOTSTRAP