2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_ANALYSIS_RESULT_H_
18 #define incl_HPHP_ANALYSIS_RESULT_H_
20 #include <compiler/code_generator.h>
21 #include <compiler/analysis/code_error.h>
22 #include <compiler/option.h>
23 #include <compiler/analysis/block_scope.h>
24 #include <compiler/analysis/symbol_table.h>
25 #include <compiler/analysis/function_container.h>
26 #include <compiler/package.h>
28 #include <util/string_bag.h>
29 #include <util/thread_local.h>
31 #include <boost/graph/adjacency_list.hpp>
32 #include <tbb/concurrent_hash_map.h>
35 ///////////////////////////////////////////////////////////////////////////////
38 DECLARE_BOOST_TYPES(ClassScope
);
39 DECLARE_BOOST_TYPES(FileScope
);
40 DECLARE_BOOST_TYPES(FunctionScope
);
41 DECLARE_BOOST_TYPES(Location
);
42 DECLARE_BOOST_TYPES(AnalysisResult
);
43 DECLARE_BOOST_TYPES(ScalarExpression
);
45 class AnalysisResult
: public BlockScope
, public FunctionContainer
{
48 * There are multiple passes over our syntax trees. This lists all of them.
76 enum GlobalSymbolType
{
77 KindOfStaticGlobalVariable
,
78 KindOfDynamicGlobalVariable
,
79 KindOfMethodStaticVariable
,
80 KindOfClassStaticVariable
,
81 KindOfDynamicConstant
,
83 KindOfRedeclaredFunction
,
84 KindOfRedeclaredClass
,
85 KindOfRedeclaredClassId
,
87 KindOfLazyStaticInitializer
,
94 explicit Locker(const AnalysisResult
*ar
) :
95 m_ar(const_cast<AnalysisResult
*>(ar
)),
96 m_mutex(m_ar
->getMutex()) {
99 explicit Locker(AnalysisResultConstPtr ar
) :
100 m_ar(const_cast<AnalysisResult
*>(ar
.get())),
101 m_mutex(m_ar
->getMutex()) {
104 Locker(const Locker
&l
) : m_ar(l
.m_ar
), m_mutex(l
.m_mutex
) {
105 const_cast<Locker
&>(l
).m_ar
= 0;
108 if (m_ar
) m_mutex
.unlock();
110 AnalysisResultPtr
get() const {
111 return m_ar
->shared_from_this();
113 AnalysisResult
*operator->() const {
117 AnalysisResult
*m_ar
;
123 Locker
lock() const { return Locker(this); }
124 void setPackage(Package
*package
) { m_package
= package
;}
125 void setParseOnDemand(bool v
) { m_parseOnDemand
= v
;}
126 bool isParseOnDemand() const { return m_package
&& m_parseOnDemand
;}
127 void setParseOnDemandDirs(const std::vector
<std::string
> &dirs
) {
128 assert(m_package
&& !m_parseOnDemand
);
129 m_parseOnDemandDirs
= dirs
;
133 * create_function() generates extra PHP code that defines the lambda.
134 * Stores the code in a temporary string, so we can parse this as an
135 * extra file appended to parsed code.
137 void appendExtraCode(const std::string
&key
, const std::string
&code
);
138 void appendExtraCode(const std::string
&key
, const std::string
&code
) const;
139 void parseExtraCode(const std::string
&key
);
141 Phase
getPhase() const { return m_phase
;}
142 void setPhase(Phase phase
) { m_phase
= phase
;}
144 int getFunctionCount() const;
145 int getClassCount() const;
146 void countReturnTypes(std::map
<std::string
, int> &counts
);
148 void addEntryPoint(const std::string
&name
);
149 void addEntryPoints(const std::vector
<std::string
> &names
);
151 void loadBuiltinFunctions();
153 void analyzeProgram(bool system
= false);
154 void analyzeIncludes();
155 void analyzeProgramFinal();
156 void analyzePerfectVirtuals();
159 void docJson(const std::string
&filename
);
160 void visitFiles(void (*cb
)(AnalysisResultPtr
, StatementPtr
, void*),
163 void getScopesSet(BlockScopeRawPtrQueue
&v
);
170 * Force all class variables to be variants, since l-val or reference
171 * of dynamic properties are used.
173 void forceClassVariants(
174 ClassScopePtr curScope
,
176 bool acquireLocks
= false);
179 * Force specified variable of all classes to be variants.
181 void forceClassVariants(
182 const std::string
&name
,
183 ClassScopePtr curScope
,
185 bool acquireLocks
= false);
188 * Code generation functions.
190 bool outputAllPHP(CodeGenerator::Output output
);
193 * Parser creates a FileScope upon parsing a new file.
195 void parseOnDemand(const std::string
&name
) const;
196 void parseOnDemandByClass(const std::string
&name
) const {
197 parseOnDemandBy(name
, Option::AutoloadClassMap
);
199 void parseOnDemandByFunction(const std::string
&name
) const {
200 parseOnDemandBy(name
, Option::AutoloadFuncMap
);
202 void parseOnDemandByConstant(const std::string
&name
) const {
203 parseOnDemandBy(name
, Option::AutoloadConstMap
);
205 void parseOnDemandBy(const std::string
&name
,
206 const std::map
<std::string
,std::string
>& amap
) const;
207 FileScopePtr
findFileScope(const std::string
&name
) const;
208 const StringToFileScopePtrMap
&getAllFiles() { return m_files
;}
209 const std::vector
<FileScopePtr
> &getAllFilesVector() {
213 void addFileScope(FileScopePtr fileScope
);
218 bool declareFunction(FunctionScopePtr funcScope
) const;
219 bool declareClass(ClassScopePtr classScope
) const;
220 void declareUnknownClass(const std::string
&name
);
221 bool declareConst(FileScopePtr fs
, const std::string
&name
);
226 void link(FileScopePtr user
, FileScopePtr provider
);
227 bool addClassDependency(FileScopePtr usingFile
,
228 const std::string
&className
);
229 bool addFunctionDependency(FileScopePtr usingFile
,
230 const std::string
&functionName
);
231 bool addIncludeDependency(FileScopePtr usingFile
,
232 const std::string
&includeFilename
);
233 bool addConstantDependency(FileScopePtr usingFile
,
234 const std::string
&constantName
);
236 ClassScopePtr
findClass(const std::string
&className
) const;
237 ClassScopePtr
findClass(const std::string
&className
,
240 * Find all the redeclared classes by the name, excluding system classes.
241 * Note that system classes cannot be redeclared.
243 const ClassScopePtrVec
&findRedeclaredClasses(
244 const std::string
&className
) const;
246 * Find all the classes by the name, including system classes.
248 ClassScopePtrVec
findClasses(const std::string
&className
) const;
249 bool classMemberExists(const std::string
&name
, FindClassBy by
) const;
250 ClassScopePtr
findExactClass(ConstructPtr cs
, const std::string
&name
) const;
251 bool checkClassPresent(ConstructPtr cs
, const std::string
&name
) const;
252 FunctionScopePtr
findFunction(const std::string
&funcName
) const ;
253 BlockScopeConstPtr
findConstantDeclarer(const std::string
&constName
) const {
254 return const_cast<AnalysisResult
*>(this)->findConstantDeclarer(constName
);
256 BlockScopePtr
findConstantDeclarer(const std::string
&constName
);
258 bool isConstantDeclared(const std::string
&constName
) const;
259 bool isConstantRedeclared(const std::string
&constName
) const;
260 bool isSystemConstant(const std::string
&constName
) const;
263 * For function declaration parsing.
265 static std::string
prepareFile(const char *root
, const std::string
&fileName
,
266 bool chop
, bool stripPath
= true);
268 void setOutputPath(const std::string
&path
) {
271 const std::string
&getOutputPath() {
276 * Literal string to String precomputation
278 std::string
getLiteralStringName(int64_t hash
, int index
, bool iproxy
= false);
279 std::string
getLitVarStringName(int64_t hash
, int index
, bool iproxy
= false);
280 int getLiteralStringId(const std::string
&s
, int &index
);
283 * Profiling runtime parameter type
285 std::string
getFuncId(ClassScopePtr cls
, FunctionScopePtr func
);
286 std::vector
<const char *> &getFuncTableBucket(FunctionScopePtr func
);
288 std::set
<std::string
> m_variableTableFunctions
;
289 std::set
<int> m_concatLengths
;
290 int m_arrayLitstrKeyMaxSize
;
291 int m_arrayIntegerKeyMaxSize
;
293 std::string
getHashedName(int64_t hash
, int index
, const char *prefix
,
294 bool longName
= false);
295 void addNamedLiteralVarString(const std::string
&s
);
296 void addNamedScalarVarArray(const std::string
&s
);
297 StringToClassScopePtrVecMap
getExtensionClasses();
298 void addInteger(int64_t n
);
301 bool m_parseOnDemand
;
302 std::vector
<std::string
> m_parseOnDemandDirs
;
304 StringToFileScopePtrMap m_files
;
305 FileScopePtrVec m_fileScopes
;
307 StringBag m_extraCodeFileNames
;
308 std::map
<std::string
, std::string
> m_extraCodes
;
310 StringToClassScopePtrMap m_systemClasses
;
311 StringToFunctionScopePtrMap m_functionDecs
;
312 StringToFunctionScopePtrVecMap m_functionReDecs
;
313 StringToClassScopePtrVecMap m_classDecs
;
314 StringToClassScopePtrVecMap m_methodToClassDecs
;
315 StringToFileScopePtrMap m_constDecs
;
316 std::set
<std::string
> m_constRedeclared
;
318 bool m_classForcedVariants
[2];
320 StatementPtrVec m_stmts
;
323 std::string m_outputPath
;
325 AnalysisResultPtr
shared_from_this() {
326 return boost::static_pointer_cast
<AnalysisResult
>
327 (BlockScope::shared_from_this());
330 AnalysisResultConstPtr
shared_from_this() const {
331 return boost::static_pointer_cast
<const AnalysisResult
>
332 (BlockScope::shared_from_this());
336 BlockScopePtrVec m_ignoredScopes
;
338 typedef boost::adjacency_list
<boost::setS
, boost::vecS
> Graph
;
339 typedef boost::graph_traits
<Graph
>::vertex_descriptor vertex_descriptor
;
340 typedef boost::graph_traits
<Graph
>::adjacency_iterator adjacency_iterator
;
341 Mutex m_depGraphMutex
;
343 typedef std::map
<vertex_descriptor
, FileScopePtr
> VertexToFileScopePtrMap
;
344 VertexToFileScopePtrMap m_fileVertMap
;
347 * Checks whether the file is in one of the on-demand parsing directories.
349 bool inParseOnDemandDirs(const std::string
&filename
) const;
351 void collectFunctionsAndClasses(FileScopePtr fs
);
354 * Making sure symbol orders are not different even with multithreading, so
355 * to make sure generated code are consistent every time.
357 void canonicalizeSymbolOrder();
360 * Checks circular class derivations that can cause stack overflows for
361 * subsequent analysis. Also checks to make sure no two redundant parents.
363 void checkClassDerivations();
365 int getFileSize(FileScopePtr fs
);
368 static DECLARE_THREAD_LOCAL(BlockScopeRawPtr
, s_currentScopeThreadLocal
);
369 static DECLARE_THREAD_LOCAL(BlockScopeRawPtrFlagsHashMap
,
370 s_changedScopesMapThreadLocal
);
372 #ifdef HPHP_INSTRUMENT_PROCESS_PARALLEL
373 static int s_NumDoJobCalls
;
374 static ConcurrentBlockScopeRawPtrIntHashMap s_DoJobUniqueScopes
;
375 static int s_NumForceRerunGlobal
;
376 static int s_NumReactivateGlobal
;
377 static int s_NumForceRerunUseKinds
;
378 static int s_NumReactivateUseKinds
;
379 #endif /* HPHP_INSTRUMENT_PROCESS_PARALLEL */
382 template <typename Visitor
>
383 void processScopesParallel(const char *id
, void *opaque
= nullptr);
385 template <typename Visitor
>
386 void preWaitCallback(bool first
,
387 const BlockScopeRawPtrQueue
&scopes
,
390 template <typename Visitor
>
391 bool postWaitCallback(bool first
,
393 const BlockScopeRawPtrQueue
&scopes
,
397 ///////////////////////////////////////////////////////////////////////////////
400 class RescheduleException
: public Exception
{
402 explicit RescheduleException(BlockScopeRawPtr scope
) :
403 Exception(), m_scope(scope
) {}
404 BlockScopeRawPtr
&getScope() { return m_scope
; }
405 #ifdef HPHP_INSTRUMENT_TYPE_INF
406 static int s_NumReschedules
;
407 static int s_NumForceRerunSelfCaller
;
408 static int s_NumRetTypesChanged
;
409 #endif /* HPHP_INSTRUMENT_TYPE_INF */
411 BlockScopeRawPtr m_scope
;
414 class SetCurrentScope
{
416 explicit SetCurrentScope(BlockScopeRawPtr scope
) {
417 assert(!((*AnalysisResult::s_currentScopeThreadLocal
).get()));
418 *AnalysisResult::s_currentScopeThreadLocal
= scope
;
419 scope
->setInVisitScopes(true);
422 (*AnalysisResult::s_currentScopeThreadLocal
)->setInVisitScopes(false);
423 AnalysisResult::s_currentScopeThreadLocal
.destroy();
427 #define IMPLEMENT_INFER_AND_CHECK_ASSERT(scope) \
429 assert(AnalysisResult::s_currentScopeThreadLocal->get()); \
430 assert(AnalysisResult::s_currentScopeThreadLocal->get() == \
432 (scope)->getInferTypesMutex().assertOwnedBySelf(); \
435 #ifdef HPHP_INSTRUMENT_TYPE_INF
436 typedef std::pair
< const char *, int > LEntry
;
438 struct LEntryHasher
{
439 bool equal(const LEntry
&l1
, const LEntry
&l2
) const {
442 return l1
.second
== l2
.second
&&
443 strcmp(l1
.first
, l2
.first
) == 0;
445 size_t hash(const LEntry
&l
) const {
447 return hash_string(l
.first
) ^ l
.second
;
451 typedef tbb::concurrent_hash_map
< LEntry
, int, LEntryHasher
>
453 #endif /* HPHP_INSTRUMENT_TYPE_INF */
456 friend class TryLock
;
457 friend class ConditionalTryLock
;
459 #ifdef HPHP_INSTRUMENT_TYPE_INF
460 static LProfileMap s_LockProfileMap
;
461 #endif /* HPHP_INSTRUMENT_TYPE_INF */
463 inline bool acquireImpl(BlockScopeRawPtr scopeToLock
) {
464 // A class scope can NEVER grab a lock on a function scope
465 BlockScopeRawPtr current ATTRIBUTE_UNUSED
=
466 *(AnalysisResult::s_currentScopeThreadLocal
.get());
468 assert(!current
->is(BlockScope::ClassScope
) ||
469 !scopeToLock
->is(BlockScope::FunctionScope
));
470 return m_mutex
.tryLock();
472 #ifdef HPHP_INSTRUMENT_TYPE_INF
473 BaseTryLock(BlockScopeRawPtr scopeToLock
,
474 const char * fromFunction
,
476 bool lockCondition
= true,
478 : m_profiler(profile
),
479 m_mutex(scopeToLock
->getInferTypesMutex()),
481 if (LIKELY(lockCondition
)) {
482 bool success
= acquireImpl(scopeToLock
);
483 if (UNLIKELY(!success
)) {
484 // put entry in profiler
485 LProfileMap::accessor acc
;
486 LEntry
key(fromFunction
, fromLine
);
487 if (!s_LockProfileMap
.insert(acc
, key
)) {
493 // could not acquire lock, throw reschedule exception
494 throw RescheduleException(scopeToLock
);
498 m_mutex
.assertOwnedBySelf();
502 explicit BaseTryLock(BlockScopeRawPtr scopeToLock
,
503 bool lockCondition
= true,
505 : m_profiler(profile
),
506 m_mutex(scopeToLock
->getInferTypesMutex()),
508 if (LIKELY(lockCondition
)) {
509 bool success
= acquireImpl(scopeToLock
);
510 if (UNLIKELY(!success
)) {
511 // could not acquire lock, throw reschedule exception
512 throw RescheduleException(scopeToLock
);
516 m_mutex
.assertOwnedBySelf();
519 #endif /* HPHP_INSTRUMENT_TYPE_INF */
522 if (m_acquired
) m_mutex
.unlock();
525 LockProfiler m_profiler
;
526 InferTypesMutex
& m_mutex
;
530 class TryLock
: public BaseTryLock
{
532 #ifdef HPHP_INSTRUMENT_TYPE_INF
533 TryLock(BlockScopeRawPtr scopeToLock
,
534 const char * fromFunction
,
536 bool profile
= true) :
537 BaseTryLock(scopeToLock
, fromFunction
, fromLine
, true, profile
) {}
539 explicit TryLock(BlockScopeRawPtr scopeToLock
,
540 bool profile
= true) :
541 BaseTryLock(scopeToLock
, true, profile
) {}
542 #endif /* HPHP_INSTRUMENT_TYPE_INF */
545 class ConditionalTryLock
: public BaseTryLock
{
547 #ifdef HPHP_INSTRUMENT_TYPE_INF
548 ConditionalTryLock(BlockScopeRawPtr scopeToLock
,
549 const char * fromFunction
,
552 bool profile
= true) :
553 BaseTryLock(scopeToLock
, fromFunction
, fromLine
, condition
, profile
) {}
555 ConditionalTryLock(BlockScopeRawPtr scopeToLock
,
557 bool profile
= true) :
558 BaseTryLock(scopeToLock
, condition
, profile
) {}
559 #endif /* HPHP_INSTRUMENT_TYPE_INF */
562 #define GET_LOCK(scopeToLock) \
563 SimpleLock _lock((scopeToLock)->getInferTypesMutex())
565 #define COND_GET_LOCK(scopeToLock, condition) \
566 SimpleConditionalLock _clock((scopeToLock)->getInferTypesMutex(), \
569 #define GET_LOCK_THIS() \
570 SimpleLock _lock(this->getInferTypesMutex())
572 #define COND_GET_LOCK_THIS(condition) \
573 SimpleConditionalLock _clock(this->getInferTypesMutex(), (condition))
575 #ifdef HPHP_INSTRUMENT_TYPE_INF
576 #define TRY_LOCK(scopeToLock) \
577 TryLock _tl((scopeToLock), __PRETTY_FUNCTION__, __LINE__)
579 #define COND_TRY_LOCK(scopeToLock, condition) \
580 ConditionalTryLock _ctl((scopeToLock), __PRETTY_FUNCTION__, \
583 #define TRY_LOCK(scopeToLock) \
584 TryLock _tl((scopeToLock))
586 #define COND_TRY_LOCK(scopeToLock, condition) \
587 ConditionalTryLock _ctl((scopeToLock), (condition))
588 #endif /* HPHP_INSTRUMENT_TYPE_INF */
590 #define TRY_LOCK_THIS() \
591 TRY_LOCK(BlockScopeRawPtr(this))
593 #define COND_TRY_LOCK_THIS(condition) \
594 COND_TRY_LOCK(BlockScopeRawPtr(this), condition)
596 ///////////////////////////////////////////////////////////////////////////////
598 #endif // incl_HPHP_ANALYSIS_RESULT_H_