AnalysisManager.h revision 9b663716449b618ba0390b1dbebc54fa8e971124
1bb8e6488bda12b41b32fc22397a44510cacdac50Zhongxing Xu//== AnalysisManager.h - Path sensitive analysis data manager ------*- C++ -*-//
2fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//
3fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//                     The LLVM Compiler Infrastructure
4fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//
5fda7832b000ff8927386f093b52c067641679469Zhongxing Xu// This file is distributed under the University of Illinois Open Source
6fda7832b000ff8927386f093b52c067641679469Zhongxing Xu// License. See LICENSE.TXT for details.
7fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//
8fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//===----------------------------------------------------------------------===//
9fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//
10fda7832b000ff8927386f093b52c067641679469Zhongxing Xu// This file defines the AnalysisManager class that manages the data and policy
11fda7832b000ff8927386f093b52c067641679469Zhongxing Xu// for path sensitive analysis.
12fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//
13fda7832b000ff8927386f093b52c067641679469Zhongxing Xu//===----------------------------------------------------------------------===//
14fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
155a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis#ifndef LLVM_CLANG_GR_ANALYSISMANAGER_H
165a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis#define LLVM_CLANG_GR_ANALYSISMANAGER_H
17fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
181309f9a3b225ea846e5822691c39a77423125505Ted Kremenek#include "clang/Analysis/AnalysisContext.h"
199b663716449b618ba0390b1dbebc54fa8e971124Ted Kremenek#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
209b663716449b618ba0390b1dbebc54fa8e971124Ted Kremenek#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
21fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
22fda7832b000ff8927386f093b52c067641679469Zhongxing Xunamespace clang {
23fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
24c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xunamespace idx {
25c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  class Indexer;
26c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  class TranslationUnit;
27c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu}
28c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
299ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremeneknamespace ento {
305a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis
31fda7832b000ff8927386f093b52c067641679469Zhongxing Xuclass AnalysisManager : public BugReporterData {
32b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  AnalysisContextManager AnaCtxMgr;
3318c7c06033cafe8c0cdcbe5759c802728688b49fZhongxing Xu  LocationContextManager LocCtxMgr;
34fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
35fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  ASTContext &Ctx;
36fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  Diagnostic &Diags;
37fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  const LangOptions &LangInfo;
38fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
39d07a0d0279c09d1017f8450fce575a94dc9703c0Zhongxing Xu  llvm::OwningPtr<PathDiagnosticClient> PD;
40d07a0d0279c09d1017f8450fce575a94dc9703c0Zhongxing Xu
41fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  // Configurable components creators.
42fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  StoreManagerCreator CreateStoreMgr;
43fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  ConstraintManagerCreator CreateConstraintMgr;
44fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
45c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  /// \brief Provide function definitions in other translation units. This is
46c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  /// NULL if we don't have multiple translation units. AnalysisManager does
47c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  /// not own the Indexer.
48c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  idx::Indexer *Idxer;
49c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
50fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  enum AnalysisScope { ScopeTU, ScopeDecl } AScope;
511eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
526362b893731ccf4480a96527db9e55e04b801503Zhongxing Xu  // The maximum number of exploded nodes the analyzer will generate.
53c09289d104b8e01ecd998e3f08b2b0561049e1dcZhongxing Xu  unsigned MaxNodes;
54c09289d104b8e01ecd998e3f08b2b0561049e1dcZhongxing Xu
5568625cf3eef2a6948fe42134aeeda91ed15e63d3Tom Care  // The maximum number of times the analyzer visit a block.
5668625cf3eef2a6948fe42134aeeda91ed15e63d3Tom Care  unsigned MaxVisit;
576362b893731ccf4480a96527db9e55e04b801503Zhongxing Xu
58fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool VisualizeEGDot;
59fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool VisualizeEGUbi;
60fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool PurgeDead;
615032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu
625032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  /// EargerlyAssume - A flag indicating how the engine should handle
635032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  //   expressions such as: 'x = (y != 0)'.  When this flag is true then
645032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  //   the subexpression 'y != 0' will be eagerly assumed to be true or false,
655032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  //   thus evaluating it to the integers 0 or 1 respectively.  The upside
665032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  //   is that this can increase analysis precision until we have a better way
675032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  //   to lazily evaluate such logic.  The downside is that it eagerly
685032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  //   bifurcates paths.
69fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool EagerlyAssume;
70fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool TrimGraph;
717b99d12b4ca67fccdf5090761ba257732e954e75Zhongxing Xu  bool InlineCall;
72d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek  bool EagerlyTrimEGraph;
73fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
74fda7832b000ff8927386f093b52c067641679469Zhongxing Xupublic:
75b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  AnalysisManager(ASTContext &ctx, Diagnostic &diags,
76fda7832b000ff8927386f093b52c067641679469Zhongxing Xu                  const LangOptions &lang, PathDiagnosticClient *pd,
77fda7832b000ff8927386f093b52c067641679469Zhongxing Xu                  StoreManagerCreator storemgr,
78c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu                  ConstraintManagerCreator constraintmgr,
79c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu                  idx::Indexer *idxer,
8068625cf3eef2a6948fe42134aeeda91ed15e63d3Tom Care                  unsigned maxnodes, unsigned maxvisit,
817b99d12b4ca67fccdf5090761ba257732e954e75Zhongxing Xu                  bool vizdot, bool vizubi, bool purge, bool eager, bool trim,
829121ba232903ebe61e7bbe14ca294cf0f07dfa96Marcin Swiderski                  bool inlinecall, bool useUnoptimizedCFG,
83d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek                  bool addImplicitDtors, bool addInitializers,
84d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek                  bool eagerlyTrimEGraph)
85fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
869121ba232903ebe61e7bbe14ca294cf0f07dfa96Marcin Swiderski    : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers),
879121ba232903ebe61e7bbe14ca294cf0f07dfa96Marcin Swiderski      Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd),
88c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu      CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),Idxer(idxer),
8968625cf3eef2a6948fe42134aeeda91ed15e63d3Tom Care      AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit),
90fda7832b000ff8927386f093b52c067641679469Zhongxing Xu      VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge),
91d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek      EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall),
92d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek      EagerlyTrimEGraph(eagerlyTrimEGraph) {}
9358f5ec7d56b1ebf5f90ee11226ebe7663f2821eaTed Kremenek
94b01f06ca72362d527bb65fbf018ac53df575ded1Ted Kremenek  ~AnalysisManager() { FlushDiagnostics(); }
95b01f06ca72362d527bb65fbf018ac53df575ded1Ted Kremenek
9658f5ec7d56b1ebf5f90ee11226ebe7663f2821eaTed Kremenek  void ClearContexts() {
9758f5ec7d56b1ebf5f90ee11226ebe7663f2821eaTed Kremenek    LocCtxMgr.clear();
9858f5ec7d56b1ebf5f90ee11226ebe7663f2821eaTed Kremenek    AnaCtxMgr.clear();
9958f5ec7d56b1ebf5f90ee11226ebe7663f2821eaTed Kremenek  }
1009b823e8e1ccb8a2cb49923bad22a80ca96f41f92Ted Kremenek
1019b823e8e1ccb8a2cb49923bad22a80ca96f41f92Ted Kremenek  AnalysisContextManager& getAnalysisContextManager() {
1029b823e8e1ccb8a2cb49923bad22a80ca96f41f92Ted Kremenek    return AnaCtxMgr;
1039b823e8e1ccb8a2cb49923bad22a80ca96f41f92Ted Kremenek  }
1041eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
105fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  StoreManagerCreator getStoreManagerCreator() {
106fda7832b000ff8927386f093b52c067641679469Zhongxing Xu    return CreateStoreMgr;
1077177dee8aee4b432911c91f1b788963bec0cac9fDaniel Dunbar  }
108fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
109fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  ConstraintManagerCreator getConstraintManagerCreator() {
110fda7832b000ff8927386f093b52c067641679469Zhongxing Xu    return CreateConstraintMgr;
111fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  }
1121eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
1132ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu  idx::Indexer *getIndexer() const { return Idxer; }
1142ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu
1155032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu  virtual ASTContext &getASTContext() {
116fda7832b000ff8927386f093b52c067641679469Zhongxing Xu    return Ctx;
117fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  }
1181eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
119fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  virtual SourceManager &getSourceManager() {
1205032ffe4259e7d436f2eb19e5a29fdae559e7c12Zhongxing Xu    return getASTContext().getSourceManager();
121fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  }
1221eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
123fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  virtual Diagnostic &getDiagnostic() {
124fda7832b000ff8927386f093b52c067641679469Zhongxing Xu    return Diags;
125fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  }
1261eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
127fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  const LangOptions &getLangOptions() const {
128fda7832b000ff8927386f093b52c067641679469Zhongxing Xu    return LangInfo;
129fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  }
1301eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump
131fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  virtual PathDiagnosticClient *getPathDiagnosticClient() {
1321eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump    return PD.get();
133fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  }
134b01f06ca72362d527bb65fbf018ac53df575ded1Ted Kremenek
135b01f06ca72362d527bb65fbf018ac53df575ded1Ted Kremenek  void FlushDiagnostics() {
136b01f06ca72362d527bb65fbf018ac53df575ded1Ted Kremenek    if (PD.get())
137b01f06ca72362d527bb65fbf018ac53df575ded1Ted Kremenek      PD->FlushDiagnostics();
138b01f06ca72362d527bb65fbf018ac53df575ded1Ted Kremenek  }
13918c7c06033cafe8c0cdcbe5759c802728688b49fZhongxing Xu
140c09289d104b8e01ecd998e3f08b2b0561049e1dcZhongxing Xu  unsigned getMaxNodes() const { return MaxNodes; }
141c09289d104b8e01ecd998e3f08b2b0561049e1dcZhongxing Xu
14268625cf3eef2a6948fe42134aeeda91ed15e63d3Tom Care  unsigned getMaxVisit() const { return MaxVisit; }
1436362b893731ccf4480a96527db9e55e04b801503Zhongxing Xu
144fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool shouldVisualizeGraphviz() const { return VisualizeEGDot; }
145fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
146fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool shouldVisualizeUbigraph() const { return VisualizeEGUbi; }
147fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
148fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool shouldVisualize() const {
149fda7832b000ff8927386f093b52c067641679469Zhongxing Xu    return VisualizeEGDot || VisualizeEGUbi;
150fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  }
151fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
152d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek  bool shouldEagerlyTrimExplodedGraph() const { return EagerlyTrimEGraph; }
153d767d81290288c030f3be0be1d3e62b9c8df51dcTed Kremenek
154fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool shouldTrimGraph() const { return TrimGraph; }
155fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
156fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool shouldPurgeDead() const { return PurgeDead; }
157fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
158fda7832b000ff8927386f093b52c067641679469Zhongxing Xu  bool shouldEagerlyAssume() const { return EagerlyAssume; }
159fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
1607b99d12b4ca67fccdf5090761ba257732e954e75Zhongxing Xu  bool shouldInlineCall() const { return InlineCall; }
1617b99d12b4ca67fccdf5090761ba257732e954e75Zhongxing Xu
162c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  bool hasIndexer() const { return Idxer != 0; }
163c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
16419b78d9e3dbbc27bbcbdd8c3017a00fe88849ecdZhongxing Xu  AnalysisContext *getAnalysisContextInAnotherTU(const Decl *D);
165c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
166b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  CFG *getCFG(Decl const *D) {
167b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu    return AnaCtxMgr.getContext(D)->getCFG();
168b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  }
169b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu
170b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  LiveVariables *getLiveVariables(Decl const *D) {
171b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu    return AnaCtxMgr.getContext(D)->getLiveVariables();
172b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  }
173b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu
174b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  ParentMap &getParentMap(Decl const *D) {
175b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu    return AnaCtxMgr.getContext(D)->getParentMap();
176b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  }
177b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu
1782ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu  AnalysisContext *getAnalysisContext(const Decl *D) {
179c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu    return AnaCtxMgr.getContext(D);
180c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  }
181c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
1822ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu  AnalysisContext *getAnalysisContext(const Decl *D, idx::TranslationUnit *TU) {
1832ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu    return AnaCtxMgr.getContext(D, TU);
1842ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu  }
1852ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu
186c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  const StackFrameContext *getStackFrame(AnalysisContext *Ctx,
187c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu                                         LocationContext const *Parent,
188892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek                                         const Stmt *S,
189d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu                                         const CFGBlock *Blk, unsigned Idx) {
190892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek    return LocCtxMgr.getStackFrame(Ctx, Parent, S, Blk, Idx);
191c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  }
192c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
193798d2ca60d1cd6de70d28a5ce60337a2b03a663fZhongxing Xu  // Get the top level stack frame.
194c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  const StackFrameContext *getStackFrame(Decl const *D,
1952ce43c8f43254a9edea53a20dc0e69195bc82ae0Zhongxing Xu                                         idx::TranslationUnit *TU) {
196892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek    return LocCtxMgr.getStackFrame(AnaCtxMgr.getContext(D, TU), 0, 0, 0, 0);
197b317f8f5ca8737a5bbad97a3f7566a2dbd2ed61bZhongxing Xu  }
198798d2ca60d1cd6de70d28a5ce60337a2b03a663fZhongxing Xu
199798d2ca60d1cd6de70d28a5ce60337a2b03a663fZhongxing Xu  // Get a stack frame with parent.
200d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu  StackFrameContext const *getStackFrame(const Decl *D,
201798d2ca60d1cd6de70d28a5ce60337a2b03a663fZhongxing Xu                                         LocationContext const *Parent,
202892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek                                         const Stmt *S,
203d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu                                         const CFGBlock *Blk, unsigned Idx) {
204892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek    return LocCtxMgr.getStackFrame(AnaCtxMgr.getContext(D), Parent, S,
205d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu                                   Blk,Idx);
206798d2ca60d1cd6de70d28a5ce60337a2b03a663fZhongxing Xu  }
207fda7832b000ff8927386f093b52c067641679469Zhongxing Xu};
208fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
2095a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis} // end GR namespace
2105a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis
2115a4f98ff943e6a501b0fe47ade007c9bbf96cb88Argyrios Kyrtzidis} // end clang namespace
212fda7832b000ff8927386f093b52c067641679469Zhongxing Xu
213fda7832b000ff8927386f093b52c067641679469Zhongxing Xu#endif
214