void SQLTransaction::openTransactionAndPreflight() { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); ASSERT(m_lockAcquired); LOG(StorageAPI, "Opening and preflighting transaction %p", this); // If the database was deleted, jump to the error callback if (m_database->deleted()) { m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); handleTransactionError(); return; } // Set the maximum usage for this transaction if this transactions is not read-only if (!m_readOnly) { acquireOriginLock(); m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); } ASSERT(!m_sqliteTransaction); m_sqliteTransaction = std::make_unique<SQLiteTransaction>(m_database->sqliteDatabase(), m_readOnly); m_database->resetDeletes(); m_database->disableAuthorizer(); m_sqliteTransaction->begin(); m_database->enableAuthorizer(); // Spec 4.3.2.1+2: Open a transaction to the database, jumping to the error callback if that fails if (!m_sqliteTransaction->inProgress()) { ASSERT(!m_database->sqliteDatabase().transactionInProgress()); m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to begin transaction", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); m_sqliteTransaction = nullptr; handleTransactionError(); return; } // Note: We intentionally retrieve the actual version even with an empty expected version. // In multi-process browsers, we take this opportinutiy to update the cached value for // the actual version. In single-process browsers, this is just a map lookup. String actualVersion; if (!m_database->getActualVersionForTransaction(actualVersion)) { m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to read version", m_database->sqliteDatabase().lastError(), m_database->sqliteDatabase().lastErrorMsg()); m_database->disableAuthorizer(); m_sqliteTransaction = nullptr; m_database->enableAuthorizer(); handleTransactionError(); return; } m_hasVersionMismatch = !m_database->expectedVersion().isEmpty() && (m_database->expectedVersion() != actualVersion); // Spec 4.3.2.3: Perform preflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPreflight(*this)) { m_database->disableAuthorizer(); m_sqliteTransaction = nullptr; m_database->enableAuthorizer(); m_transactionError = m_wrapper->sqlError(); if (!m_transactionError) m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occurred during transaction preflight"); handleTransactionError(); return; } // Spec 4.3.2.4: Invoke the transaction callback with the new SQLTransaction object if (m_callbackWrapper.hasCallback()) { scheduleCallback(&SQLTransaction::deliverTransactionCallback); return; } // If we have no callback to make, skip pass to the state after: runStatements(); }
static bool runStatement(ASTNode* node, MemoryStack* stack, Scope* scope, RuntimeError* error) { switch (node->nodeType) { case AST_STATEMENT_TYPE_DEFINITION: { if (getTypeDefinition(scope, node->typeDefinition.identifier, false)) { *error = RuntimeError{ RET_TYPE_ALREADY_DEFINED, node->typeDefinition.identifier, node->typeDefinition.lineNumber }; return false; } else { TypeDefinition* typeDef = scopePushToList(scope, stack, &scope->types); typeDef->identifier = node->typeDefinition.identifier; typeDef->members = {}; ListIterator<ASTNode> iterator = makeIterator(&node->typeDefinition.definitions); int currOffsetInBytes = 0; while (hasNext(&iterator)) { ASTNode* node = getNext(&iterator); StructMember* member = scopePushToList(scope, stack, &typeDef->members); member->identifier = node->variableDeclaration.identifier; member->type = makeType(scope, node->variableDeclaration.typeIdentifier); member->offsetInBytes = currOffsetInBytes; currOffsetInBytes += member->type.definition->totalSizeInBytes; } typeDef->totalSizeInBytes = currOffsetInBytes; return true; } } break; case AST_STATEMENT_VARIABLE_DECLARATION: { if (getVariable(scope, node->variableDeclaration.identifier, false)) { *error = RuntimeError{ RET_VARIABLE_ALREADY_DEFINED, node->variableDeclaration.identifier, node->variableDeclaration.lineNumber }; return false; } else { Variable* var = defineAVariable( node->variableDeclaration.identifier, makeType(scope, node->variableDeclaration.typeIdentifier), stack, scope); if (node->variableDeclaration.expression) return runExpression(node->variableDeclaration.expression, stack, scope, error, &var->value); else return true; // TODO: set default value } } break; case AST_STATEMENT_FUNCTION_DEFINITION: { Function func; func.type = FT_INTERNAL; func.identifier.name = node->functionDefinition.identifier; func.identifier.arguments = {}; bool stillWorking = true; if (!node->functionDefinition.returnType) func.returnType = Type{ nullptr, 0 }; else { func.returnType = makeType(scope, node->functionDefinition.returnType); if (!func.returnType.definition) { *error = RuntimeError{ RET_UNDEFINED_TYPE, node->functionDefinition.returnType->typeIdentifier.name, node->functionDefinition.lineNumber }; stillWorking = false; } } ListIterator<ASTNode> args = makeIterator(&node->functionDefinition.arguments); while (stillWorking && hasNext(&args)) { ASTNode* arg = getNext(&args); Argument* argument = scopePushToList(scope, stack, &func.identifier.arguments); argument->identifier = arg->variableDeclaration.identifier; argument->type = makeType(scope, arg->variableDeclaration.typeIdentifier); if (!argument->type.definition) { *error = RuntimeError{ RET_UNDEFINED_TYPE, arg->variableDeclaration.typeIdentifier->typeIdentifier.name, arg->variableDeclaration.lineNumber }; stillWorking = false; } } func.body = node->functionDefinition.body; Function* hasFunc = getFunction(scope, func.identifier, false); if (hasFunc) { *error = RuntimeError{ RET_FUNCTION_ALREADY_DEFINED, node->functionDefinition.identifier, node->functionDefinition.lineNumber }; stillWorking = false; } else { *scopePushToList(scope, stack, &scope->functions) = func; } return stillWorking; } break; case AST_STATEMENT_IF: { Scope ifScope = makeScope(scope); Value conditionResult = pushValue(&ifScope, stack, Type{ getTypeDefinition(scope, makeSlice("s32")), false }); bool stillWorking = runExpression(node->ifStatement.condition, stack, scope, error, &conditionResult); if (stillWorking) { if (*(int*)conditionResult.memory != 0) { stillWorking = runStatement(node->ifStatement.ifCase, stack, &ifScope, error); } else if (node->ifStatement.elseCase != nullptr) { stillWorking = runStatement(node->ifStatement.elseCase, stack, &ifScope, error); } } scopeFreeMemory(&ifScope, stack); return stillWorking; } break; case AST_STATEMENT_WHILE: { Scope whileScope = makeScope(scope); Value conditionResult = pushValue(&whileScope, stack, Type{ getTypeDefinition(scope, makeSlice("s32")), false }); bool stillWorking = runExpression(node->whileStatement.condition, stack, scope, error, &conditionResult); if (stillWorking) { while (*(int*)conditionResult.memory != 0) { if (!(stillWorking = runStatement(node->whileStatement.body, stack, &whileScope, error))) break; if (!(stillWorking = runExpression(node->whileStatement.condition, stack, scope, error, &conditionResult))) break; } } scopeFreeMemory(&whileScope, stack); return stillWorking; } break; case AST_STATEMENT_ASSIGNMENT: { Value value; if (getValue(scope, node->assignment.lValue, error, &value)) { return runExpression(node->assignment.expression, stack, scope, error, &value); } else return false; } break; case AST_STATEMENTS_BLOCK: { Scope blockScope = makeScope(scope); bool result = runStatements(&node->statementsBlock.statements, stack, &blockScope, error); scopeFreeMemory(&blockScope, stack); return result; } break; case AST_FUNCTION_CALL: { return runFunctionCall(node, stack, scope, error, nullptr); } break; default: { assert(!"this is not a statement!"); return false; } break; } }