void SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::advance() { ASSERT(m_positionNode); if (m_shouldStop) return; if (m_stopsOnFormControls && HTMLFormControlElement::enclosingFormControlElement(m_node)) { m_shouldStop = true; return; } m_positionNode = nullptr; m_textLength = 0; while (m_node && !m_havePassedStartNode) { // Don't handle node if we start iterating at [node, 0]. if (!m_handledNode && !(m_node == m_endNode && !m_endOffset)) { LayoutObject* layoutObject = m_node->layoutObject(); if (layoutObject && layoutObject->isText() && m_node->nodeType() == Node::TEXT_NODE) { // FIXME: What about CDATA_SECTION_NODE? if (layoutObject->style()->visibility() == VISIBLE && m_offset > 0) m_handledNode = handleTextNode(); } else if (layoutObject && (layoutObject->isLayoutPart() || TextIterator::supportsAltText(m_node))) { if (layoutObject->style()->visibility() == VISIBLE && m_offset > 0) m_handledNode = handleReplacedElement(); } else { m_handledNode = handleNonTextNode(); } if (m_positionNode) return; } if (!m_handledChildren && m_node->hasChildren()) { m_node = m_node->lastChild(); m_fullyClippedStack.pushFullyClippedState(m_node); } else { // Exit empty containers as we pass over them or containers // where [container, 0] is where we started iterating. if (!m_handledNode && canHaveChildrenForEditing(m_node) && m_node->parentNode() && (!m_node->lastChild() || (m_node == m_endNode && !m_endOffset))) { exitNode(); if (m_positionNode) { m_handledNode = true; m_handledChildren = true; return; } } // Exit all other containers. while (!m_node->previousSibling()) { if (!advanceRespectingRange(m_node->parentOrShadowHostNode())) break; m_fullyClippedStack.pop(); exitNode(); if (m_positionNode) { m_handledNode = true; m_handledChildren = true; return; } } m_fullyClippedStack.pop(); if (advanceRespectingRange(m_node->previousSibling())) m_fullyClippedStack.pushFullyClippedState(m_node); else m_node = nullptr; } // For the purpose of word boundary detection, // we should iterate all visible text and trailing (collapsed) whitespaces. m_offset = m_node ? maxOffsetIncludingCollapsedSpaces(m_node) : 0; m_handledNode = false; m_handledChildren = false; if (m_positionNode) return; } }
/** * @brief Computes the FuncInfo and returns it. */ ShPtr<OptimFuncInfo> OptimFuncInfoCFGTraversal::performComputation() { // First, we pre-compute varsAlwaysModifiedBeforeRead. The reason is that // their computation differs from the computation of the rest of the sets. precomputeAlwaysModifiedVarsBeforeRead(); // Every function's body is of the following form: // // (1) definitions of local variables, including assignments of global // variables into local variables // (2) other statements // // We store which variables are read/modified in (1). Then, we start the // traversal from (2). During the traversal, we check which variables are // read/modified and update funcInfo accordingly. The stored information // from (1) is used to compute the set of global variables which are read // in the function, but not modified. // // To give a specific example, consider the following code: // // def func(mango): // global orange // global plum // lychee = orange // achira = plum // orange = mango // plum = rand() // result = plum * apple + orange // orange = lychee // plum = achira // return result // // Here, even though the global variable orange is modified, its value // before calling func() is the same as after calling func(). Indeed, its // value is restored before the return statement. Hence, we may put it into // funcInfo->varsWithNeverChangedValue. // TODO Implement a more robust analysis. ShPtr<Statement> currStmt = traversedFunc->getBody(); while (isVarDefOrAssignStmt(currStmt)) { updateFuncInfo(currStmt); ShPtr<Expression> lhs(getLhs(currStmt)); ShPtr<Expression> rhs(getRhs(currStmt)); // If there is no right-hand side, it is a VarDefStmt with no // initializer, which we may skip. if (!rhs) { currStmt = currStmt->getSuccessor(); continue; } // If there are any function calls or dereferences, we have reached // (2). ShPtr<ValueData> currStmtData(va->getValueData(currStmt)); if (currStmtData->hasCalls() || currStmtData->hasDerefs()) { break; } // Check whether the statement is of the form localVar = globalVar. ShPtr<Variable> localVar(cast<Variable>(lhs)); ShPtr<Variable> globalVar(cast<Variable>(rhs)); if (!localVar || !globalVar || hasItem(globalVars, localVar) || !hasItem(globalVars, globalVar)) { // It is not of the abovementioned form, so skip it. currStmt = currStmt->getSuccessor(); continue; } storedGlobalVars[globalVar] = localVar; currStmt = currStmt->getSuccessor(); } // Perform the traversal only if we haven't reached the end of the function // yet. Since empty statements are not present in a CFG, skip them before // the traversal. if ((currStmt = skipEmptyStmts(currStmt))) { performTraversal(currStmt); } // We use the exit node of the CFG to check that every variable from // storedGlobalVars is retrieved its original value before every return. ShPtr<CFG::Node> exitNode(cfg->getExitNode()); // For every predecessor of the exit node... for (auto i = exitNode->pred_begin(), e = exitNode->pred_end(); i != e; ++i) { bool checkingShouldContinue = checkExitNodesPredecessor((*i)->getSrc()); if (!checkingShouldContinue) { break; } } // Update funcInfo using the remaining variables in storedGlobalVars. for (const auto &p : storedGlobalVars) { funcInfo->varsWithNeverChangedValue.insert(p.first); } // Update funcInfo->never{Read,Modified}Vars by global variables which are // untouched in this function. for (auto i = module->global_var_begin(), e = module->global_var_end(); i != e; ++i) { ShPtr<Variable> var((*i)->getVar()); if (!hasItem(funcInfo->mayBeReadVars, var) && !hasItem(funcInfo->mayBeModifiedVars, var)) { funcInfo->neverReadVars.insert(var); funcInfo->neverModifiedVars.insert(var); } } // If the cfg contains only a single non-{entry,exit} node, every // mayBe{Read,Modifed} variable can be turned into a always{Read,Modified} // variable. if (cfg->getNumberOfNodes() == 3) { addToSet(funcInfo->mayBeReadVars, funcInfo->alwaysReadVars); addToSet(funcInfo->mayBeModifiedVars, funcInfo->alwaysModifiedVars); } // Add all variables which are never read and never modified to // varsWithNeverChangedValue. VarSet neverReadAndModifedVars(setIntersection(funcInfo->neverReadVars, funcInfo->neverModifiedVars)); addToSet(neverReadAndModifedVars, funcInfo->varsWithNeverChangedValue); // Add all global variables are not read in this function into // varsAlwaysModifiedBeforeRead. addToSet(setDifference(globalVars, funcInfo->mayBeReadVars), funcInfo->varsAlwaysModifiedBeforeRead); return funcInfo; }