/// <summary> /// Remove existing hardware breakpoint /// </summary> /// <param name="ptr">Breakpoint address</param> /// <returns>true on success</returns> bool Thread::RemoveHWBP( ptr_t ptr ) { _CONTEXT64 context64 = { 0 }; _CONTEXT32 context32 = { 0 }; bool use64 = !_core->native()->GetWow64Barrier().x86OS; bool res = use64 ? GetContext( context64, CONTEXT64_DEBUG_REGISTERS, true ) : GetContext( context32, CONTEXT_DEBUG_REGISTERS, true ); auto pDR7 = use64 ? reinterpret_cast<regDR7*>(&context64.Dr7) : reinterpret_cast<regDR7*>(&context32.Dr7); if (!res) return false; // Search for breakpoint for (int i = 0; i < 4; i++) { if ((&context64.Dr0)[i] == ptr || (&context32.Dr0)[i] == static_cast<DWORD>(ptr)) { use64 ? *(&context64.Dr0 + i) = 0 : *(&context32.Dr0 + i) = 0; pDR7->setLocal( i, 0 ); pDR7->setLen( i, 0 ); pDR7->setRW( i, 0 ); if (pDR7->empty()) pDR7->l_enable = 0; return use64 ? SetContext( context64 ) : SetContext( context32 ); } } return false; }
/// <summary> /// Add hardware breakpoint to thread /// </summary> /// <param name="addr">Breakpoint address</param> /// <param name="type">Breakpoint type(read/write/execute)</param> /// <param name="length">Number of bytes to include into breakpoint</param> /// <returns>Index of used breakpoint; -1 if failed</returns> int Thread::AddHWBP( ptr_t addr, HWBPType type, HWBPLength length ) { _CONTEXT64 context64 = { 0 }; _CONTEXT32 context32 = { 0 }; bool use64 = !_core->native()->GetWow64Barrier().x86OS; // CONTEXT_DEBUG_REGISTERS can be operated without thread suspension bool res = use64 ? GetContext( context64, CONTEXT64_DEBUG_REGISTERS, true ) : GetContext( context32, CONTEXT_DEBUG_REGISTERS, true ); auto pDR7 = use64 ? reinterpret_cast<regDR7*>(&context64.Dr7) : reinterpret_cast<regDR7*>(&context32.Dr7); if (!res) return -1; // Get free DR int freeIdx = pDR7->getFreeIndex(); // If all 4 registers are occupied - error if (freeIdx < 0) { LastNtStatus( STATUS_NO_MORE_ENTRIES ); return -1; } // Enable corresponding HWBP and local BP flag pDR7->l_enable = 1; pDR7->setLocal( freeIdx, 1 ); pDR7->setRW( freeIdx, static_cast<char>(type) ); pDR7->setLen( freeIdx, static_cast<char>(length) ); use64 ? *(&context64.Dr0 + freeIdx) = addr : *(&context32.Dr0 + freeIdx) = static_cast<DWORD>(addr); // Write values to registers res = use64 ? SetContext( context64, true ) : SetContext( context32, true ); return res ? freeIdx : -1; }
void AutostartItem::setEnabled(bool enable) { XdgDesktopFile f = file(); if (enable) f.removeEntry("Hidden"); else f.setValue("Hidden", true); setLocal(f); }
static int classifyPrimitive(CallExpr *call, bool inLocal) { int is = classifyPrimitive(call); // If it's in a local block, it's always local. is = setLocal(is, inLocal); return is; }
KGpgSignKey::KGpgSignKey(QObject *parent, const QString &signer, KGpgKeyNode *key, const bool local, const carefulCheck checking) : KGpgEditKeyTransaction(parent, key->getId(), QString(), false, false), KGpgSignTransactionHelper(signer, !local, checking) { insertArgument(1, QLatin1String( "-u" )); insertArgument(2, signer); m_signerPos = 2; addArgumentRef(&m_signerPos); addArgument(QLatin1String("save")); setKey(key); setLocal(local); }
/// <summary> /// Remove existing hardware breakpoint /// </summary> /// <param name="idx">Breakpoint index</param> /// <returns>true on success</returns> bool Thread::RemoveHWBP( int idx ) { if (idx < 0 || idx > 4) return false; _CONTEXT64 context64 = { 0 }; _CONTEXT32 context32 = { 0 }; bool use64 = !_core->native()->GetWow64Barrier().x86OS; bool res = use64 ? GetContext( context64, CONTEXT64_DEBUG_REGISTERS, true ) : GetContext( context32, CONTEXT_DEBUG_REGISTERS, true ); auto pDR7 = use64 ? reinterpret_cast<regDR7*>(&context64.Dr7) : reinterpret_cast<regDR7*>(&context32.Dr7); if (!res) return false; pDR7->setLocal( idx, 0 ); pDR7->setLen( idx, 0 ); pDR7->setRW( idx, 0 ); if (pDR7->empty()) pDR7->l_enable = 0; return use64 ? SetContext( context64 ) : SetContext( context32 ); }
// Dispatch. as_value Function::call(const fn_call& fn) { // Extract caller before pushing ourself on the call stack VM& vm = getVM(fn); as_object* caller = vm.calling() ? &vm.currentCall().function() : 0; // Set up local stack frame, for parameters and locals. FrameGuard guard(getVM(fn), *this); CallFrame& cf = guard.callFrame(); DisplayObject* target = _env.target(); DisplayObject* orig_target = _env.get_original_target(); // Some features are version-dependant. const int swfversion = getSWFVersion(fn); if (swfversion < 6) { // In SWF5, when 'this' is a DisplayObject it becomes // the target for this function call. // See actionscript.all/setProperty.as DisplayObject* ch = get<DisplayObject>(fn.this_ptr); if (ch) { target = ch; orig_target = ch; } } /// This is only needed for SWF5 (temp switch of target) /// We do always and base 'target' value on SWF version. /// TODO: simplify code by maybe using a custom as_environment /// instead, so to get an "original" target being /// the one set now (rather then the really original one) /// TODO: test scope when calling functions defined in another timeline /// (target, in particular). TargetGuard targetGuard(_env, target, orig_target); // Push the arguments onto the local frame. for (size_t i = 0, n = _args.size(); i < n; ++i) { assert(_args[i].reg == 0); if (i < fn.nargs) { setLocal(cf, _args[i].name, fn.arg(i)); } else { // Still declare named arguments, even if // they are not passed from caller // See bug #22203 declareLocal(cf, _args[i].name); } } // Add 'this' setLocal(cf, NSV::PROP_THIS, fn.this_ptr ? fn.this_ptr : as_value()); as_object* super = fn.super ? fn.super : fn.this_ptr ? fn.this_ptr->get_super() : 0; // Add 'super' (SWF6+ only) if (super && swfversion > 5) { setLocal(cf, NSV::PROP_SUPER, super); } // Add 'arguments' as_object* args = getGlobal(fn).createArray(); // Put 'arguments' in a local var. setLocal(cf, NSV::PROP_ARGUMENTS, getArguments(*this, *args, fn, caller)); // Execute the actions. as_value result; ActionExec(*this, _env, &result, fn.this_ptr)(); return result; }
bool run() { for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; variable->clearVotes(); } // Identify the set of variables that are always subject to the same structure // checks. For now, only consider monomorphic structure checks (one structure). for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { NodeIndex nodeIndex = block->at(indexInBlock); Node& node = m_graph[nodeIndex]; if (!node.shouldGenerate()) continue; switch (node.op()) { case CheckStructure: { Node& child = m_graph[node.child1()]; if (child.op() != GetLocal) break; VariableAccessData* variable = child.variableAccessData(); variable->vote(VoteStructureCheck); if (variable->isCaptured() || variable->structureCheckHoistingFailed()) break; if (!isCellSpeculation(variable->prediction())) break; noticeStructureCheck(variable, node.structureSet()); break; } case ForwardCheckStructure: case ForwardStructureTransitionWatchpoint: // We currently rely on the fact that we're the only ones who would // insert this node. ASSERT_NOT_REACHED(); break; case GetByOffset: case PutByOffset: case PutStructure: case StructureTransitionWatchpoint: case AllocatePropertyStorage: case ReallocatePropertyStorage: case GetPropertyStorage: case GetByVal: case PutByVal: case PutByValAlias: case GetArrayLength: case CheckArray: case GetIndexedPropertyStorage: case Phantom: // Don't count these uses. break; default: m_graph.vote(node, VoteOther); break; } } } // Disable structure hoisting on variables that appear to mostly be used in // contexts where it doesn't make sense. for (unsigned i = m_graph.m_variableAccessData.size(); i--;) { VariableAccessData* variable = &m_graph.m_variableAccessData[i]; if (!variable->isRoot()) continue; if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting()) continue; HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) continue; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) dataLog("Zeroing the structure to hoist for %s because the ratio is %lf.\n", m_graph.nameOfVariableAccessData(variable), variable->voteRatio()); #endif iter->second.m_structure = 0; } // Identify the set of variables that are live across a structure clobber. Operands<VariableAccessData*> live( m_graph.m_blocks[0]->variablesAtTail.numberOfArguments(), m_graph.m_blocks[0]->variablesAtTail.numberOfLocals()); for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; ASSERT(live.numberOfArguments() == block->variablesAtTail.numberOfArguments()); ASSERT(live.numberOfLocals() == block->variablesAtTail.numberOfLocals()); for (unsigned i = live.size(); i--;) { NodeIndex indexAtTail = block->variablesAtTail[i]; VariableAccessData* variable; if (indexAtTail == NoNode) variable = 0; else variable = m_graph[indexAtTail].variableAccessData(); live[i] = variable; } for (unsigned indexInBlock = block->size(); indexInBlock--;) { NodeIndex nodeIndex = block->at(indexInBlock); Node& node = m_graph[nodeIndex]; if (!node.shouldGenerate()) continue; switch (node.op()) { case GetLocal: case Flush: // This is a birth. live.operand(node.local()) = node.variableAccessData(); break; case SetLocal: case SetArgument: ASSERT(live.operand(node.local())); // Must be live. ASSERT(live.operand(node.local()) == node.variableAccessData()); // Must have the variable we expected. // This is a death. live.operand(node.local()) = 0; break; // Use the CFA's notion of what clobbers the world. case ValueAdd: if (m_graph.addShouldSpeculateInteger(node)) break; if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) break; clobber(live); break; case CompareLess: case CompareLessEq: case CompareGreater: case CompareGreaterEq: case CompareEq: { Node& left = m_graph[node.child1()]; Node& right = m_graph[node.child2()]; if (Node::shouldSpeculateInteger(left, right)) break; if (Node::shouldSpeculateNumber(left, right)) break; if (node.op() == CompareEq) { if ((m_graph.isConstant(node.child1().index()) && m_graph.valueOfJSConstant(node.child1().index()).isNull()) || (m_graph.isConstant(node.child2().index()) && m_graph.valueOfJSConstant(node.child2().index()).isNull())) break; if (Node::shouldSpeculateFinalObject(left, right)) break; if (Node::shouldSpeculateArray(left, right)) break; if (left.shouldSpeculateFinalObject() && right.shouldSpeculateFinalObjectOrOther()) break; if (right.shouldSpeculateFinalObject() && left.shouldSpeculateFinalObjectOrOther()) break; if (left.shouldSpeculateArray() && right.shouldSpeculateArrayOrOther()) break; if (right.shouldSpeculateArray() && left.shouldSpeculateArrayOrOther()) break; } clobber(live); break; } case GetByVal: case PutByVal: case PutByValAlias: if (m_graph.byValIsPure(node)) break; clobber(live); break; case GetMyArgumentsLengthSafe: case GetMyArgumentByValSafe: case GetById: case GetByIdFlush: case PutStructure: case PhantomPutStructure: case PutById: case PutByIdDirect: case Call: case Construct: case Resolve: case ResolveBase: case ResolveBaseStrictPut: case ResolveGlobal: clobber(live); break; default: ASSERT(node.op() != Phi); break; } } } bool changed = false; #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin(); it != m_map.end(); ++it) { if (!it->second.m_structure) { dataLog("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->first)); continue; } if (it->second.m_isClobbered && !it->second.m_structure->transitionWatchpointSetIsStillValid()) { dataLog("Not hoisting checks for %s because the structure is clobbered and has an invalid watchpoint set.\n", m_graph.nameOfVariableAccessData(it->first)); continue; } dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->first)); } #endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) // Make changes: // 1) If a variable's live range does not span a clobber, then inject structure // checks before the SetLocal. // 2) If a variable's live range spans a clobber but is watchpointable, then // inject structure checks before the SetLocal and replace all other structure // checks on that variable with structure transition watchpoints. InsertionSet<NodeIndex> insertionSet; for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { BasicBlock* block = m_graph.m_blocks[blockIndex].get(); if (!block) continue; for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { NodeIndex nodeIndex = block->at(indexInBlock); Node& node = m_graph[nodeIndex]; // Be careful not to use 'node' after appending to the graph. In those switch // cases where we need to append, we first carefully extract everything we need // from the node, before doing any appending. if (!node.shouldGenerate()) continue; switch (node.op()) { case SetArgument: { ASSERT(!blockIndex); // Insert a GetLocal and a CheckStructure immediately following this // SetArgument, if the variable was a candidate for structure hoisting. // If the basic block previously only had the SetArgument as its // variable-at-tail, then replace it with this GetLocal. VariableAccessData* variable = node.variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->second.m_structure) break; if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid()) break; node.ref(); CodeOrigin codeOrigin = node.codeOrigin; Node getLocal(GetLocal, codeOrigin, OpInfo(variable), nodeIndex); getLocal.predict(variable->prediction()); getLocal.ref(); NodeIndex getLocalIndex = m_graph.size(); m_graph.append(getLocal); insertionSet.append(indexInBlock + 1, getLocalIndex); Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), getLocalIndex); checkStructure.ref(); NodeIndex checkStructureIndex = m_graph.size(); m_graph.append(checkStructure); insertionSet.append(indexInBlock + 1, checkStructureIndex); if (block->variablesAtTail.operand(variable->local()) == nodeIndex) block->variablesAtTail.operand(variable->local()) = getLocalIndex; m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocalIndex); changed = true; break; } case SetLocal: { VariableAccessData* variable = node.variableAccessData(); HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable); if (iter == m_map.end()) break; if (!iter->second.m_structure) break; if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid()) break; // First insert a dead SetLocal to tell OSR that the child's value should // be dropped into this bytecode variable if the CheckStructure decides // to exit. CodeOrigin codeOrigin = node.codeOrigin; NodeIndex child1 = node.child1().index(); Node setLocal(SetLocal, codeOrigin, OpInfo(variable), child1); NodeIndex setLocalIndex = m_graph.size(); m_graph.append(setLocal); insertionSet.append(indexInBlock, setLocalIndex); m_graph[child1].ref(); // Use a ForwardCheckStructure to indicate that we should exit to the // next bytecode instruction rather than reexecuting the current one. Node checkStructure(ForwardCheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), child1); checkStructure.ref(); NodeIndex checkStructureIndex = m_graph.size(); m_graph.append(checkStructure); insertionSet.append(indexInBlock, checkStructureIndex); changed = true; break; } case CheckStructure: { Node& child = m_graph[node.child1()]; if (child.op() != GetLocal) break; HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(child.variableAccessData()); if (iter == m_map.end()) break; if (!iter->second.m_structure) break; if (!iter->second.m_isClobbered) { node.setOpAndDefaultFlags(Phantom); ASSERT(node.refCount() == 1); break; } if (!iter->second.m_structure->transitionWatchpointSetIsStillValid()) break; ASSERT(iter->second.m_structure == node.structureSet().singletonStructure()); node.convertToStructureTransitionWatchpoint(); changed = true; break; } default: break; } } insertionSet.execute(*block); } return changed; }
BOOL CGameObject::net_Spawn (CSE_Abstract* DC) { VERIFY (!m_spawned); m_spawned = true; m_spawn_time = Device.dwFrame; m_ai_obstacle = xr_new<ai_obstacle>(this); CSE_Abstract *E = (CSE_Abstract*)DC; VERIFY (E); const CSE_Visual *visual = smart_cast<const CSE_Visual*>(E); if (visual) { cNameVisual_set (visual_name(E)); if (visual->flags.test(CSE_Visual::flObstacle)) { ISpatial *self = smart_cast<ISpatial*>(this); self->spatial.type |= STYPE_OBSTACLE; } } // Naming cName_set (E->s_name); cNameSect_set (E->s_name); if (E->name_replace()[0]) cName_set (E->name_replace()); bool demo_spectator = false; if (Level().IsDemoPlayStarted() && E->ID == u16(-1)) { Msg("* Spawning demo spectator ..."); demo_spectator = true; } else { R_ASSERT(Level().Objects.net_Find(E->ID) == NULL); } setID (E->ID); // if (GameID() != eGameIDSingle) // Msg ("CGameObject::net_Spawn -- object %s[%x] setID [%d]", *(E->s_name), this, E->ID); // XForm XFORM().setXYZ (E->o_Angle); Position().set (E->o_Position); #ifdef DEBUG if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrackName(),*cName())==0) { Msg("CGameObject::net_Spawn obj %s Position set from CSE_Abstract %f,%f,%f",PH_DBG_ObjectTrackName(),Position().x,Position().y,Position().z); } #endif VERIFY (_valid(renderable.xform)); VERIFY (!fis_zero(DET(renderable.xform))); CSE_ALifeObject *O = smart_cast<CSE_ALifeObject*>(E); if (O && xr_strlen(O->m_ini_string)) { #pragma warning(push) #pragma warning(disable:4238) m_ini_file = xr_new<CInifile>( &IReader ( (void*)(*(O->m_ini_string)), O->m_ini_string.size() ), FS.get_path("$game_config$")->m_Path ); #pragma warning(pop) } m_story_id = ALife::_STORY_ID(-1); if (O) m_story_id = O->m_story_id; // Net params setLocal (E->s_flags.is(M_SPAWN_OBJECT_LOCAL)); if (Level().IsDemoPlay()) //&& OnClient()) { if (!demo_spectator) { setLocal(FALSE); } }; setReady (TRUE); if (!demo_spectator) g_pGameLevel->Objects.net_Register (this); m_server_flags.one (); if (O) { m_server_flags = O->m_flags; if (O->m_flags.is(CSE_ALifeObject::flVisibleForAI)) spatial.type |= STYPE_VISIBLEFORAI; else spatial.type = (spatial.type | STYPE_VISIBLEFORAI) ^ STYPE_VISIBLEFORAI; } reload (*cNameSect()); if(!g_dedicated_server) CScriptBinder::reload (*cNameSect()); reinit (); if(!g_dedicated_server) CScriptBinder::reinit (); #ifdef DEBUG if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrackName(),*cName())==0) { Msg("CGameObject::net_Spawn obj %s After Script Binder reinit %f,%f,%f",PH_DBG_ObjectTrackName(),Position().x,Position().y,Position().z); } #endif //load custom user data from server if(!E->client_data.empty()) { // Msg ("client data is present for object [%d][%s], load is processed",ID(),*cName()); IReader ireader = IReader(&*E->client_data.begin(), E->client_data.size()); net_Load (ireader); } else { // Msg ("no client data for object [%d][%s], load is skipped",ID(),*cName()); } // if we have a parent if ( ai().get_level_graph() ) { if ( E->ID_Parent == 0xffff ) { CSE_ALifeObject* l_tpALifeObject = smart_cast<CSE_ALifeObject*>(E); if (l_tpALifeObject && ai().level_graph().valid_vertex_id(l_tpALifeObject->m_tNodeID)) ai_location().level_vertex (l_tpALifeObject->m_tNodeID); else { CSE_Temporary* l_tpTemporary = smart_cast<CSE_Temporary*> (E); if (l_tpTemporary && ai().level_graph().valid_vertex_id(l_tpTemporary->m_tNodeID)) ai_location().level_vertex (l_tpTemporary->m_tNodeID); } if (l_tpALifeObject && ai().game_graph().valid_vertex_id(l_tpALifeObject->m_tGraphID)) ai_location().game_vertex (l_tpALifeObject->m_tGraphID); validate_ai_locations (false); // validating position if ( UsedAI_Locations() && ai().level_graph().inside( ai_location().level_vertex_id(), Position() ) && can_validate_position_on_spawn() ) Position().y = EPS_L + ai().level_graph().vertex_plane_y(*ai_location().level_vertex(),Position().x,Position().z); } else { CSE_ALifeObject* const alife_object = smart_cast<CSE_ALifeObject*>(E); if ( alife_object && ai().level_graph().valid_vertex_id(alife_object->m_tNodeID) ) { ai_location().level_vertex (alife_object->m_tNodeID); ai_location().game_vertex (alife_object->m_tGraphID); } } } inherited::net_Spawn (DC); m_bObjectRemoved = false; spawn_supplies (); #ifdef DEBUG if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrackName(),*cName())==0) { Msg("CGameObject::net_Spawn obj %s Before CScriptBinder::net_Spawn %f,%f,%f",PH_DBG_ObjectTrackName(),Position().x,Position().y,Position().z); } BOOL ret =CScriptBinder::net_Spawn(DC); #else return (CScriptBinder::net_Spawn(DC)); #endif #ifdef DEBUG if(ph_dbg_draw_mask1.test(ph_m1_DbgTrackObject)&&stricmp(PH_DBG_ObjectTrackName(),*cName())==0) { Msg("CGameObject::net_Spawn obj %s Before CScriptBinder::net_Spawn %f,%f,%f",PH_DBG_ObjectTrackName(),Position().x,Position().y,Position().z); } return ret; #endif }
void setValue(Token t, ValuePtr v) { std::string vname = t.value->toString(); if (!setLocal(vname, v)) setGlobal(vname, v); }
static int markFastSafeFn(FnSymbol *fn, int recurse, std::set<FnSymbol*>& visited) { // First, handle functions we've already visited. if (visited.count(fn) != 0) { if (fn->hasFlag(FLAG_FAST_ON)) return FAST_AND_LOCAL; if (fn->hasFlag(FLAG_LOCAL_FN)) return LOCAL_NOT_FAST; return NOT_FAST_NOT_LOCAL; } // Now, add fn to the set of visited functions, // since we will categorize it now. visited.insert(fn); // Next, classify extern functions if (fn->hasFlag(FLAG_EXTERN)) { if (fn->hasFlag(FLAG_FAST_ON_SAFE_EXTERN)) { // Make sure the FAST_ON and LOCAL_FN flags are set. fn->addFlag(FLAG_FAST_ON); fn->addFlag(FLAG_LOCAL_FN); return FAST_AND_LOCAL; } else if(fn->hasFlag(FLAG_LOCAL_FN)) { return LOCAL_NOT_FAST; } else { // Other extern functions are not fast or local. return NOT_FAST_NOT_LOCAL; } } // Next, go through function bodies. // We will set maybefast=false if we see something in the function // that is local but not suitable for a signal handler // (mostly allocation or locking). // We will return NOT_FAST_NOT_LOCAL immediately if we see something // in the function that is not local. bool maybefast = true; if (fn->hasFlag(FLAG_NON_BLOCKING)) maybefast = false; std::vector<CallExpr*> calls; collectCallExprs(fn, calls); for_vector(CallExpr, call, calls) { bool inLocal = fn->hasFlag(FLAG_LOCAL_FN) || inLocalBlock(call); if (call->primitive) { int is = classifyPrimitive(call, inLocal); if (!isLocal(is)) { // FAST_NOT_LOCAL or NOT_FAST_NOT_LOCAL return NOT_FAST_NOT_LOCAL; } // is == FAST_AND_LOCAL requires no action if (is == LOCAL_NOT_FAST) { maybefast = false; } } else { if (recurse<=0 || !call->isResolved()) { // didn't resolve or past too much recursion. // No function calls allowed return NOT_FAST_NOT_LOCAL; } else { // Handle nested 'on' statements if (call->resolvedFunction()->hasFlag(FLAG_ON_BLOCK)) { if (inLocal) { maybefast = false; } else { return NOT_FAST_NOT_LOCAL; } } // is the call to a fast/local function? int is = markFastSafeFn(call->resolvedFunction(), recurse - 1, visited); // Remove NOT_LOCAL parts if it's in a local block is = setLocal(is, inLocal); if (!isLocal(is)) { return NOT_FAST_NOT_LOCAL; } if (is == LOCAL_NOT_FAST) { maybefast = false; } // otherwise, possibly still fast. } } }