void ScalarExpression::analyzeProgram(AnalysisResultPtr ar) { if (ar->getPhase() == AnalysisResult::AnalyzeAll) { auto const id = HPHP::toLower(getIdentifier()); switch (m_type) { case T_LINE: m_translated = folly::to<std::string>(line1()); break; case T_NS_C: m_translated = m_value; break; // case T_TRAIT_C: Note: T_TRAIT_C is translated at parse time case T_CLASS_C: case T_METHOD_C: { if (!m_translated.empty()) break; BlockScopeRawPtr b = getScope(); while (b && b->is(BlockScope::FunctionScope)) { b = b->getOuterScope(); } m_translated.clear(); if (b && b->is(BlockScope::ClassScope)) { auto clsScope = dynamic_pointer_cast<ClassScope>(b); if (!clsScope->isTrait()) { m_translated = clsScope->getOriginalName(); } } if (m_type == T_METHOD_C) { if (FunctionScopePtr func = getFunctionScope()) { if (b && b->is(BlockScope::ClassScope)) { m_translated += "::"; } if (func->isClosure()) { m_translated += "{closure}"; } else { m_translated += func->getOriginalName(); } } } break; } case T_FUNC_C: if (FunctionScopePtr func = getFunctionScope()) { if (func->isClosure()) { m_translated = "{closure}"; } else { m_translated = func->getOriginalName(); } } break; default: break; } } }
void Type::outputCPPDecl(CodeGenerator &cg, AnalysisResultConstPtr ar, BlockScopeRawPtr scope) { cg_printf(getCPPDecl(cg, ar, scope).c_str()); if (isSpecificObject() && cg.isFileOrClassHeader() && scope) { if (scope->getContainingClass()) { scope->getContainingClass()->addUsedClassHeader(m_name); } else if (scope->getContainingFile()) { scope->getContainingFile()->addUsedClassHeader(m_name); } } }
static inline void DumpScopeWithDeps(BlockScopeRawPtr scope) { assert(scope->is(BlockScope::FunctionScope) || scope->is(BlockScope::ClassScope)); DumpScope(scope, ""); const BlockScopeRawPtrFlagsVec &ordered = scope->getOrderedUsers(); for (BlockScopeRawPtrFlagsVec::const_iterator it = ordered.begin(), end = ordered.end(); it != end; ++it) { BlockScopeRawPtrFlagsVec::value_type pf = *it; auto prefix = folly::to<string>(" (", pf->second, ") "); DumpScope(pf->first, prefix.c_str()); } }
void Symbol::beginLocal(BlockScopeRawPtr scope) { m_prevCoerced = m_coerced; if (isClosureVar()) { ExpressionListPtr useVars = scope->getContainingFunction()->getClosureVars(); assert(useVars); // linear scan for now, since most use var lists are // fairly short bool found = false; for (int i = 0; i < useVars->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*useVars)[i]); if (m_name == param->getName()) { // bootstrap use var with parameter type m_coerced = param->getType(); found = true; break; } } if (!found) assert(false); assert(!isRefClosureVar() || (m_coerced && m_coerced->is(Type::KindOfVariant))); } else { m_coerced.reset(); } }
void ClassScope::setVolatile() { if (!m_volatile) { m_volatile = true; Lock lock(s_depsMutex); const BlockScopeRawPtrFlagsVec &orderedUsers = getOrderedUsers(); for (BlockScopeRawPtrFlagsVec::const_iterator it = orderedUsers.begin(), end = orderedUsers.end(); it != end; ++it) { BlockScopeRawPtrFlagsVec::value_type pf = *it; if (pf->second & UseKindParentRef) { BlockScopeRawPtr scope = pf->first; if (scope->is(BlockScope::ClassScope)) { ((HPHP::ClassScope*)scope.get())->setVolatile(); } } } } }
ClassScopePtr StaticClassName::resolveClassWithChecks() { ClassScopePtr cls = resolveClass(); if (!m_class && !cls) { Construct *self = dynamic_cast<Construct*>(this); BlockScopeRawPtr scope = self->getScope(); if (isRedeclared()) { scope->getVariables()->setAttribute(VariableTable::NeedGlobalPointer); } else if (scope->isFirstPass()) { ClassScopeRawPtr cscope = scope->getContainingClass(); if (!cscope || !cscope->isTrait() || (!isSelf() && !isParent())) { Compiler::Error(Compiler::UnknownClass, self->shared_from_this()); } } } return cls; }
ClassScopePtr StaticClassName::resolveClass() { m_present = false; m_unknown = true; if (m_class) return ClassScopePtr(); BlockScopeRawPtr scope = originalScope(this); if (m_self) { if (ClassScopePtr self = scope->getContainingClass()) { m_origClassName = self->getOriginalName(); m_present = true; m_unknown = false; return self; } } else if (m_parent) { if (ClassScopePtr self = scope->getContainingClass()) { if (!self->getOriginalParent().empty()) { m_origClassName = self->getOriginalParent(); m_present = true; } } else { m_parent = false; } } ClassScopePtr cls = scope->getContainingProgram()->findClass(m_origClassName); if (cls) { m_unknown = false; if (cls->isVolatile()) { ClassScopeRawPtr c = scope->getContainingClass(); if (c && c->isNamed(m_origClassName)) { c.reset(); } m_present = c.get() != nullptr; if (cls->isRedeclaring()) { cls = c; if (!m_present) m_redeclared = true; } } else { m_present = true; } } return cls; }
ClassScopePtr Type::getClass(AnalysisResultPtr ar, BlockScopeRawPtr scope) { if (m_name.empty()) return ClassScopePtr(); ClassScopePtr cls = ar->findClass(m_name); if (cls->isRedeclaring()) { if (!scope) { cls.reset(); } else { cls = scope->findExactClass(Util::toLower(m_name)); } } return cls; }
string Type::getCPPDecl(AnalysisResultConstPtr ar, BlockScopeRawPtr scope, CodeGenerator *cg /* = 0 */) { switch (m_kindOf) { case KindOfBoolean: return "bool"; case KindOfInt32: return "int"; case KindOfInt64: return "int64"; case KindOfDouble: return "double"; case KindOfString: return "String"; case KindOfArray: return "Array"; case KindOfObject: { ClassScopePtr cls(getClass(ar, scope)); if (!cls) return "Object"; if (cg && cg->isFileOrClassHeader() && scope) { if (scope->getContainingClass()) { scope->getContainingClass()->addUsedClassHeader(cls); } else if (scope->getContainingFile()) { scope->getContainingFile()->addUsedClassHeader(cls); } } return Option::SmartPtrPrefix + cls->getId(); } case KindOfNumeric: return "Numeric"; case KindOfPrimitive: return "Primitive"; case KindOfPlusOperand: return "PlusOperand"; case KindOfSequence: return "Sequence"; default: return "Variant"; } }
void BlockScope::addUse(BlockScopeRawPtr user, int useKinds) { if ((is(ClassScope) || is(FunctionScope)) && !isBuiltin()) { if (user.get() == this) { m_selfUser |= useKinds; return; } Lock lock(s_depsMutex); Lock l2(s_jobStateMutex); auto val = m_userMap.emplace(user, useKinds); if (val.second) { m_orderedUsers.push_back(&*val.first); user->m_orderedDeps.emplace_back(BlockScopeRawPtr{this}, &(val.first->second)); assert(user->getMark() != BlockScope::MarkReady && user->getMark() != BlockScope::MarkWaiting); } else { val.first->second |= useKinds; } } }
TypePtr ConstantTable::check(BlockScopeRawPtr context, const std::string &name, TypePtr type, bool coerce, AnalysisResultConstPtr ar, ConstructPtr construct, const std::vector<std::string> &bases, BlockScope *&defScope) { ASSERT(!m_blockScope.is(BlockScope::FunctionScope)); bool isClassScope = m_blockScope.is(BlockScope::ClassScope); TypePtr actualType; defScope = NULL; if (name == "true" || name == "false") { actualType = Type::Boolean; } else { Symbol *sym = getSymbol(name); if (!sym) { if (ar->getPhase() != AnalysisResult::AnalyzeInclude) { if (isClassScope) { ClassScopeRawPtr parent = findBase(ar, name, bases); if (parent) { actualType = parent->getConstants()->check( context, name, type, coerce, ar, construct, bases, defScope); if (defScope) return actualType; } } Compiler::Error(Compiler::UseUndeclaredConstant, construct); actualType = isClassScope ? Type::Variant : Type::String; } } else { ASSERT(sym->isPresent()); ASSERT(sym->getType()); ASSERT(sym->isConstant()); defScope = &m_blockScope; if (isClassScope) { // if the current scope is a function scope, grab the lock. // otherwise if it's a class scope, then *try* to grab the lock. if (context->is(BlockScope::FunctionScope)) { GET_LOCK(BlockScopeRawPtr(&m_blockScope)); return setType(ar, sym, type, coerce); } else { TRY_LOCK(BlockScopeRawPtr(&m_blockScope)); return setType(ar, sym, type, coerce); } } else { Lock lock(m_blockScope.getMutex()); return setType(ar, sym, type, coerce); } } } return actualType; }
bool StaticClassName::checkPresent() { if (m_self || m_parent || m_static) return true; BlockScopeRawPtr scope = originalScope(this); FileScopeRawPtr currentFile = scope->getContainingFile(); if (currentFile) { AnalysisResultPtr ar = currentFile->getContainingProgram(); ClassScopeRawPtr cls = ar->findClass(m_className); if (!cls) return false; if (!cls->isVolatile()) return true; if (currentFile->resolveClass(cls)) return true; if (currentFile->checkClass(m_className)) return true; } if (ClassScopePtr self = scope->getContainingClass()) { if (m_className == self->getName() || self->derivesFrom(scope->getContainingProgram(), m_className, true, false)) { return true; } } return false; }
static inline string GetDocName(AnalysisResultPtr ar, BlockScopeRawPtr scope, const string &name) { ClassScopePtr c(ar->findClass(name)); if (c && c->isRedeclaring()) { ClassScopePtr exact(scope->findExactClass(c)); return exact ? exact->getDocName() : c->getScopeName(); // if we can't tell which redec class, // then don't use the redec name } // TODO: pick a better way of signaling unknown? return c ? c->getDocName() : "UnknownClass"; }
void BlockScope::addUse(BlockScopeRawPtr user, int useKinds) { if ((is(ClassScope) || is(FunctionScope)) && !isBuiltin()) { if (user.get() == this) { m_selfUser |= useKinds; return; } Lock lock(s_depsMutex); Lock l2(s_jobStateMutex); std::pair<BlockScopeRawPtrFlagsHashMap::iterator,bool> val = m_userMap.insert(BlockScopeRawPtrFlagsHashMap::value_type(user, useKinds)); if (val.second) { m_orderedUsers.push_back(&*val.first); user->m_orderedDeps.push_back( std::make_pair(BlockScopeRawPtr(this), &(val.first->second))); assert(user->getMark() != BlockScope::MarkReady && user->getMark() != BlockScope::MarkWaiting); } else { val.first->second |= useKinds; } } }
bool BlockScope::hasUser(BlockScopeRawPtr user, int useKinds) const { if (is(ClassScope) ? static_cast<const HPHP::ClassScope*>(this)->isUserClass() : is(FunctionScope) && static_cast<const HPHP::FunctionScope*>(this)->isUserFunction()) { if (user.get() == this) { return m_selfUser & useKinds; } Lock lock(s_depsMutex); const auto it = m_userMap.find(user); return it != m_userMap.end() && it->second & useKinds; } return true; // builtins/systems always have a user of anybody }
void Symbol::triggerUpdates(BlockScopeRawPtr scope) const { int useKind = BlockScope::GetNonStaticRefUseKind(getHash()); if (isConstant()) { useKind = BlockScope::UseKindConstRef; if (m_declaration) { BlockScopeRawPtr declScope(m_declaration->getScope()); /** * Constants can only belong to a file or class scope */ assert(scope->is(BlockScope::FileScope) || scope->is(BlockScope::ClassScope)); /** * Constants can only be declared in a function or * class scope */ assert(declScope->is(BlockScope::FunctionScope) || declScope->is(BlockScope::ClassScope)); /** * For class scopes, the declaration scope *must* * match the scope the symbol lives in */ assert(!scope->is(BlockScope::ClassScope) || scope == declScope); /** * For file scopes, the declaration scope *must* * live in a function scope */ assert(!scope->is(BlockScope::FileScope) || declScope->is(BlockScope::FunctionScope)); /** * This is really only for file scopes (constants created with * define('FOO', ...)). const FOO = 1 outside of a class is re-written * into a define('FOO', 1) by earlier phases of the compiler */ if (scope->is(BlockScope::FileScope)) { declScope->announceUpdates(BlockScope::UseKindConstRef); return; } } } else if (isStatic()) { useKind = BlockScope::UseKindStaticRef; } else if (isParameter()) { useKind = BlockScope::UseKindCallerParam; } if (isPassClosureVar()) { useKind |= BlockScope::UseKindClosure; } scope->addUpdates(useKind); }
TypePtr VariableTable::checkProperty(BlockScopeRawPtr context, Symbol *sym, TypePtr type, bool coerce, AnalysisResultConstPtr ar) { always_assert(sym->isPresent()); if (sym->isOverride()) { Symbol *base; ClassScopePtr parent = findParent(ar, sym->getName(), base); assert(parent); assert(parent.get() != &m_blockScope); assert(base && !base->isPrivate()); if (context->is(BlockScope::FunctionScope)) { GET_LOCK(parent); type = parent->getVariables()->setType(ar, base, type, coerce); } else { TRY_LOCK(parent); type = parent->getVariables()->setType(ar, base, type, coerce); } } return setType(ar, sym, type, coerce); }
int DepthFirstVisitor<Pre, OptVisitor>::visitScope(BlockScopeRawPtr scope) { int updates, all_updates = 0; StatementPtr stmt = scope->getStmt(); if (MethodStatementPtr m = dynamic_pointer_cast<MethodStatement>(stmt)) { WriteLock lock(m->getFunctionScope()->getInlineMutex()); do { scope->clearUpdated(); if (Option::LocalCopyProp || Option::EliminateDeadCode) { AliasManager am(-1); if (am.optimize(this->m_data.m_ar, m)) { scope->addUpdates(BlockScope::UseKindCaller); } } else { StatementPtr rep = this->visitStmtRecur(stmt); always_assert(!rep); } updates = scope->getUpdated(); all_updates |= updates; } while (updates); if (all_updates & BlockScope::UseKindCaller && !m->getFunctionScope()->getInlineAsExpr()) { all_updates &= ~BlockScope::UseKindCaller; } return all_updates; } do { scope->clearUpdated(); StatementPtr rep = this->visitStmtRecur(stmt); always_assert(!rep); updates = scope->getUpdated(); all_updates |= updates; } while (updates); return all_updates; }
FunctionScopeRawPtr Expression::getOriginalFunction() { BlockScopeRawPtr scope = getOriginalScope(); return scope ? scope->getContainingFunction() : FunctionScopeRawPtr(); }
void Construct::recomputeEffects() { BlockScopeRawPtr scope = getScope(); if (scope) scope->incEffectsTag(); }
int BlockScope::ScopeCompare::cmp(const BlockScopeRawPtr &p1, const BlockScopeRawPtr &p2) const { int d1 = p1->m_kind - p2->m_kind; if (d1) return d1; return strcasecmp(p1->getName().c_str(), p2->getName().c_str()); }
ClassScopeRawPtr Expression::getOriginalClass() { BlockScopeRawPtr scope = getOriginalScope(); return scope ? scope->getContainingClass() : ClassScopeRawPtr(); }