svf-tools 1.0.976 → 1.0.978
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/svf/include/Graphs/ThreadCallGraph.h +0 -6
- package/svf/include/MTA/LockAnalysis.h +41 -31
- package/svf/include/MTA/MHP.h +34 -54
- package/svf/include/MTA/MTAStat.h +1 -2
- package/svf/include/MTA/TCT.h +36 -18
- package/svf/include/Util/CxtStmt.h +13 -12
- package/svf/include/Util/Options.h +1 -20
- package/svf/include/Util/SVFUtil.h +8 -43
- package/svf/include/Util/ThreadAPI.h +42 -85
- package/svf/lib/Graphs/ThreadCallGraph.cpp +1 -73
- package/svf/lib/MTA/LockAnalysis.cpp +83 -75
- package/svf/lib/MTA/MHP.cpp +142 -121
- package/svf/lib/MTA/MTA.cpp +2 -40
- package/svf/lib/MTA/MTAStat.cpp +7 -33
- package/svf/lib/MTA/TCT.cpp +30 -30
- package/svf/lib/Util/CallGraphBuilder.cpp +0 -15
- package/svf/lib/Util/Options.cpp +0 -62
- package/svf/lib/Util/ThreadAPI.cpp +27 -6
- package/svf-llvm/lib/SVFIRExtAPI.cpp +0 -26
- package/svf-llvm/tools/MTA/CMakeLists.txt +1 -1
- package/svf-llvm/tools/MTA/mta.cpp +0 -8
- package/svf/include/MTA/FSMPTA.h +0 -270
- package/svf/include/MTA/MTAResultValidator.h +0 -448
- package/svf/include/MTA/PCG.h +0 -229
- package/svf/lib/MTA/FSMPTA.cpp +0 -792
- package/svf/lib/MTA/PCG.cpp +0 -364
- package/svf-llvm/tools/MTA/LockResultValidator.cpp +0 -251
- package/svf-llvm/tools/MTA/LockResultValidator.h +0 -84
- package/svf-llvm/tools/MTA/MTAAnnotator.cpp +0 -293
- package/svf-llvm/tools/MTA/MTAAnnotator.h +0 -120
- package/svf-llvm/tools/MTA/MTAResultValidator.cpp +0 -716
- package/svf-llvm/tools/MTA/MTAResultValidator.h +0 -337
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* MTAAnnotator.cpp
|
|
3
|
-
*
|
|
4
|
-
* Created on: May 4, 2014
|
|
5
|
-
* Author: Yulei Sui, Peng Di
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
#include "Util/Options.h"
|
|
9
|
-
#include "MTAAnnotator.h"
|
|
10
|
-
#include "MTA/LockAnalysis.h"
|
|
11
|
-
#include "MemoryModel/PointsTo.h"
|
|
12
|
-
#include <sstream>
|
|
13
|
-
|
|
14
|
-
using namespace SVF;
|
|
15
|
-
using namespace SVFUtil;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
void MTAAnnotator::annotateDRCheck(Instruction* inst)
|
|
19
|
-
{
|
|
20
|
-
std::string str;
|
|
21
|
-
std::stringstream rawstr(str);
|
|
22
|
-
rawstr << DR_CHECK;
|
|
23
|
-
|
|
24
|
-
/// memcpy and memset is not annotated
|
|
25
|
-
if (StoreInst* st = SVFUtil::dyn_cast<StoreInst>(inst))
|
|
26
|
-
{
|
|
27
|
-
numOfAnnotatedSt++;
|
|
28
|
-
addMDTag(inst, st->getPointerOperand(), rawstr.str());
|
|
29
|
-
}
|
|
30
|
-
else if (LoadInst* ld = SVFUtil::dyn_cast<LoadInst>(inst))
|
|
31
|
-
{
|
|
32
|
-
numOfAnnotatedLd++;
|
|
33
|
-
addMDTag(inst, ld->getPointerOperand(), rawstr.str());
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
void MTAAnnotator::collectLoadStoreInst(SVFModule* mod)
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
for (Module& M : LLVMModuleSet::getLLVMModuleSet()->getLLVMModules())
|
|
41
|
-
{
|
|
42
|
-
for (Module::const_iterator F = M.begin(), E = M.end(); F != E; ++F)
|
|
43
|
-
{
|
|
44
|
-
if (SVFUtil::isExtCall(LLVMModuleSet::getLLVMModuleSet()->getSVFFunction(&*F)))
|
|
45
|
-
continue;
|
|
46
|
-
for (const_inst_iterator II = inst_begin(*F), E = inst_end(*F); II != E; ++II)
|
|
47
|
-
{
|
|
48
|
-
const Instruction* inst = &*II;
|
|
49
|
-
if (SVFUtil::isa<LoadInst>(inst))
|
|
50
|
-
{
|
|
51
|
-
loadset.insert(inst);
|
|
52
|
-
}
|
|
53
|
-
else if (SVFUtil::isa<StoreInst>(inst))
|
|
54
|
-
{
|
|
55
|
-
storeset.insert(inst);
|
|
56
|
-
}
|
|
57
|
-
else if (isMemset(inst))
|
|
58
|
-
{
|
|
59
|
-
storeset.insert(inst);
|
|
60
|
-
}
|
|
61
|
-
else if (isMemcpy(inst))
|
|
62
|
-
{
|
|
63
|
-
storeset.insert(inst);
|
|
64
|
-
loadset.insert(inst);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
numOfAllSt = storeset.size();
|
|
71
|
-
numOfAllLd = loadset.size();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const Value* MTAAnnotator::getStoreOperand(const Instruction* inst)
|
|
75
|
-
{
|
|
76
|
-
if (const StoreInst* st = SVFUtil::dyn_cast<StoreInst>(inst))
|
|
77
|
-
{
|
|
78
|
-
return st->getPointerOperand();
|
|
79
|
-
}
|
|
80
|
-
else if (isMemset(inst))
|
|
81
|
-
{
|
|
82
|
-
return inst->getOperand(0);
|
|
83
|
-
}
|
|
84
|
-
else if (isMemcpy(inst))
|
|
85
|
-
{
|
|
86
|
-
return inst->getOperand(0);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
assert(false);
|
|
90
|
-
return nullptr;
|
|
91
|
-
}
|
|
92
|
-
const Value* MTAAnnotator::getLoadOperand(const Instruction* inst)
|
|
93
|
-
{
|
|
94
|
-
if (const LoadInst* ld = SVFUtil::dyn_cast<LoadInst>(inst))
|
|
95
|
-
{
|
|
96
|
-
return ld->getPointerOperand();
|
|
97
|
-
}
|
|
98
|
-
else if (isMemcpy(inst))
|
|
99
|
-
{
|
|
100
|
-
return inst->getOperand(1);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
assert(false);
|
|
104
|
-
return nullptr;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
void MTAAnnotator::initialize(MHP* m, LockAnalysis* la)
|
|
108
|
-
{
|
|
109
|
-
mhp = m;
|
|
110
|
-
lsa = la;
|
|
111
|
-
if (!Options::AnnoFlag())
|
|
112
|
-
return;
|
|
113
|
-
collectLoadStoreInst(mhp->getTCT()->getPTA()->getModule());
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
void MTAAnnotator::pruneThreadLocal(PointerAnalysis* pta)
|
|
117
|
-
{
|
|
118
|
-
bool AnnoLocal = Options::AnnoFlag() & ANNO_LOCAL;
|
|
119
|
-
if (!AnnoLocal)
|
|
120
|
-
return;
|
|
121
|
-
|
|
122
|
-
DBOUT(DGENERAL, outs() << pasMsg("Run annotator prune thread local pairs\n"));
|
|
123
|
-
SVFIR* pag = pta->getPAG();
|
|
124
|
-
PointsTo nonlocalobjs;
|
|
125
|
-
PointsTo worklist;
|
|
126
|
-
|
|
127
|
-
/// find fork arguments' objects
|
|
128
|
-
const SVFStmt::SVFStmtSetTy& forkedges = pag->getPTASVFStmtSet(SVFStmt::ThreadFork);
|
|
129
|
-
for (SVFStmt::SVFStmtSetTy::const_iterator it = forkedges.begin(), eit = forkedges.end(); it != eit; ++it)
|
|
130
|
-
{
|
|
131
|
-
PAGEdge* edge = *it;
|
|
132
|
-
worklist |= pta->getPts(edge->getDstID());
|
|
133
|
-
worklist |= pta->getPts(edge->getSrcID());
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/// find global pointer-to objects
|
|
137
|
-
const SVFIR::SVFStmtSet& globaledges = pag->getGlobalSVFStmtSet();
|
|
138
|
-
for (SVFIR::SVFStmtSet::const_iterator it = globaledges.begin(), eit = globaledges.end(); it != eit; ++it)
|
|
139
|
-
{
|
|
140
|
-
const PAGEdge* edge = *it;
|
|
141
|
-
if (edge->getEdgeKind() == SVFStmt::Addr)
|
|
142
|
-
{
|
|
143
|
-
worklist.set(edge->getSrcID());
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/// find all non-local objects that are transitively pointed by global and fork arguments.
|
|
148
|
-
while (!worklist.empty())
|
|
149
|
-
{
|
|
150
|
-
NodeID obj = worklist.find_first();
|
|
151
|
-
nonlocalobjs.set(obj);
|
|
152
|
-
worklist.reset(obj);
|
|
153
|
-
PointsTo pts = pta->getPts(obj);
|
|
154
|
-
for (PointsTo::iterator pit = pts.begin(), epit = pts.end(); pit != epit; ++pit)
|
|
155
|
-
{
|
|
156
|
-
if (!nonlocalobjs.test(*pit))
|
|
157
|
-
worklist.set(*pit);
|
|
158
|
-
}
|
|
159
|
-
NodeBS fields = pag->getAllFieldsObjVars(obj);
|
|
160
|
-
for (NodeBS::iterator pit = fields.begin(), epit = fields.end(); pit != epit; ++pit)
|
|
161
|
-
{
|
|
162
|
-
if (!nonlocalobjs.test(*pit))
|
|
163
|
-
worklist.set(*pit);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/// compute all store and load instructions that may operate a non-local object.
|
|
168
|
-
InstSet needannost;
|
|
169
|
-
InstSet needannold;
|
|
170
|
-
for (InstSet::iterator it = storeset.begin(), eit = storeset.end(); it != eit; ++it)
|
|
171
|
-
{
|
|
172
|
-
PointsTo pts = pta->getPts(pag->getValueNode(LLVMModuleSet::getLLVMModuleSet()->getSVFValue(getStoreOperand(*it))));
|
|
173
|
-
for (PointsTo::iterator pit = pts.begin(), epit = pts.end(); pit != epit; ++pit)
|
|
174
|
-
{
|
|
175
|
-
if (nonlocalobjs.test(*pit))
|
|
176
|
-
{
|
|
177
|
-
needannost.insert(*it);
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
for (InstSet::iterator it = loadset.begin(), eit = loadset.end(); it != eit; ++it)
|
|
184
|
-
{
|
|
185
|
-
PointsTo pts = pta->getPts(pag->getValueNode(LLVMModuleSet::getLLVMModuleSet()->getSVFValue(getLoadOperand(*it))));
|
|
186
|
-
for (PointsTo::iterator pit = pts.begin(), epit = pts.end(); pit != epit; ++pit)
|
|
187
|
-
{
|
|
188
|
-
if (nonlocalobjs.test(*pit))
|
|
189
|
-
{
|
|
190
|
-
needannold.insert(*it);
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
storeset = needannost;
|
|
197
|
-
loadset = needannold;
|
|
198
|
-
|
|
199
|
-
numOfNonLocalSt = storeset.size();
|
|
200
|
-
numOfNonLocalLd = loadset.size();
|
|
201
|
-
}
|
|
202
|
-
void MTAAnnotator::pruneAliasMHP(PointerAnalysis* pta)
|
|
203
|
-
{
|
|
204
|
-
|
|
205
|
-
bool AnnoMHP = Options::AnnoFlag() & ANNO_MHP;
|
|
206
|
-
bool AnnoAlias = Options::AnnoFlag() & ANNO_ALIAS;
|
|
207
|
-
|
|
208
|
-
if (!AnnoMHP && !AnnoAlias)
|
|
209
|
-
return;
|
|
210
|
-
|
|
211
|
-
DBOUT(DGENERAL, outs() << pasMsg("Run annotator prune Alias or MHP pairs\n"));
|
|
212
|
-
InstSet needannost;
|
|
213
|
-
InstSet needannold;
|
|
214
|
-
for (InstSet::iterator it1 = storeset.begin(), eit1 = storeset.end(); it1 != eit1; ++it1)
|
|
215
|
-
{
|
|
216
|
-
const SVFInstruction* inst1 = LLVMModuleSet::getLLVMModuleSet()->getSVFInstruction(*it1);
|
|
217
|
-
for (InstSet::iterator it2 = it1, eit2 = storeset.end(); it2 != eit2; ++it2)
|
|
218
|
-
{
|
|
219
|
-
const SVFInstruction* inst2 = LLVMModuleSet::getLLVMModuleSet()->getSVFInstruction(*it2);
|
|
220
|
-
const SVFValue* v1 = LLVMModuleSet::getLLVMModuleSet()->getSVFValue(getStoreOperand(*it1));
|
|
221
|
-
const SVFValue* v2 = LLVMModuleSet::getLLVMModuleSet()->getSVFValue(getStoreOperand(*it2));
|
|
222
|
-
|
|
223
|
-
if(!pta->alias(v1, v2))
|
|
224
|
-
continue;
|
|
225
|
-
|
|
226
|
-
if (AnnoMHP)
|
|
227
|
-
{
|
|
228
|
-
if (mhp->mayHappenInParallel(inst1, inst2) && !lsa->isProtectedByCommonLock(inst1, inst2))
|
|
229
|
-
{
|
|
230
|
-
needannost.insert(*it1);
|
|
231
|
-
needannost.insert(*it2);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
else
|
|
235
|
-
{
|
|
236
|
-
/// if it1 == it2, mhp analysis will annotate it1 that locates in loop or recursion.
|
|
237
|
-
/// but alias analysis fails to determine whether it1 is in loop or recursion, that means
|
|
238
|
-
/// all store instructions will be annotated by alias analysis to guarantee sound.
|
|
239
|
-
needannost.insert(*it1);
|
|
240
|
-
needannost.insert(*it2);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
for (InstSet::iterator it2 = loadset.begin(), eit2 = loadset.end(); it2 != eit2; ++it2)
|
|
244
|
-
{
|
|
245
|
-
const SVFInstruction* inst2 = LLVMModuleSet::getLLVMModuleSet()->getSVFInstruction(*it2);
|
|
246
|
-
const SVFValue* v1 = LLVMModuleSet::getLLVMModuleSet()->getSVFValue(getStoreOperand(*it1));
|
|
247
|
-
const SVFValue* v2 = LLVMModuleSet::getLLVMModuleSet()->getSVFValue(getLoadOperand(*it2));
|
|
248
|
-
|
|
249
|
-
if(!pta->alias(v1,v2))
|
|
250
|
-
continue;
|
|
251
|
-
|
|
252
|
-
if (AnnoMHP)
|
|
253
|
-
{
|
|
254
|
-
if (mhp->mayHappenInParallel(inst1, inst2) && !lsa->isProtectedByCommonLock(inst1, inst2))
|
|
255
|
-
{
|
|
256
|
-
needannost.insert(*it1);
|
|
257
|
-
needannold.insert(*it2);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
else
|
|
261
|
-
{
|
|
262
|
-
needannost.insert(*it1);
|
|
263
|
-
needannold.insert(*it2);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
storeset = needannost;
|
|
268
|
-
loadset = needannold;
|
|
269
|
-
|
|
270
|
-
if (AnnoMHP)
|
|
271
|
-
{
|
|
272
|
-
numOfMHPSt = storeset.size();
|
|
273
|
-
numOfMHPLd = loadset.size();
|
|
274
|
-
}
|
|
275
|
-
else if (AnnoAlias)
|
|
276
|
-
{
|
|
277
|
-
numOfAliasSt = storeset.size();
|
|
278
|
-
numOfAliasLd = loadset.size();
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
void MTAAnnotator::performAnnotate()
|
|
282
|
-
{
|
|
283
|
-
if (!Options::AnnoFlag())
|
|
284
|
-
return;
|
|
285
|
-
for (InstSet::iterator it = storeset.begin(), eit = storeset.end(); it != eit; ++it)
|
|
286
|
-
{
|
|
287
|
-
annotateDRCheck(const_cast<Instruction*>(*it));
|
|
288
|
-
}
|
|
289
|
-
for (InstSet::iterator it = loadset.begin(), eit = loadset.end(); it != eit; ++it)
|
|
290
|
-
{
|
|
291
|
-
annotateDRCheck(const_cast<Instruction*>(*it));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* MTAAnnotator.h
|
|
3
|
-
*
|
|
4
|
-
* Created on: May 4, 2014
|
|
5
|
-
* Author: Peng Di
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
#ifndef MTAANNOTATOR_H_
|
|
9
|
-
#define MTAANNOTATOR_H_
|
|
10
|
-
|
|
11
|
-
#include "SVF-LLVM/BasicTypes.h"
|
|
12
|
-
#include "SVF-LLVM/LLVMModule.h"
|
|
13
|
-
#include "SVFIR/SVFValue.h"
|
|
14
|
-
#include "Util/Annotator.h"
|
|
15
|
-
#include "MTA/MHP.h"
|
|
16
|
-
|
|
17
|
-
namespace SVF
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
/*!
|
|
21
|
-
* MTA annotation
|
|
22
|
-
*/
|
|
23
|
-
class MTAAnnotator: public Annotator
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
public:
|
|
27
|
-
typedef Set<const Instruction*> InstSet;
|
|
28
|
-
/// Constructor
|
|
29
|
-
MTAAnnotator(): mhp(nullptr),lsa(nullptr)
|
|
30
|
-
{
|
|
31
|
-
numOfAllSt = 0;
|
|
32
|
-
numOfAllLd = 0;
|
|
33
|
-
numOfNonLocalSt = 0;
|
|
34
|
-
numOfNonLocalLd = 0;
|
|
35
|
-
numOfAliasSt = 0;
|
|
36
|
-
numOfAliasLd = 0;
|
|
37
|
-
numOfMHPSt = 0;
|
|
38
|
-
numOfMHPLd = 0;
|
|
39
|
-
numOfAnnotatedSt = 0;
|
|
40
|
-
numOfAnnotatedLd = 0;
|
|
41
|
-
}
|
|
42
|
-
/// Destructor
|
|
43
|
-
virtual ~MTAAnnotator()
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
/// Annotation
|
|
48
|
-
//@{
|
|
49
|
-
void annotateDRCheck(Instruction* inst);
|
|
50
|
-
//@}
|
|
51
|
-
|
|
52
|
-
/// Initialize
|
|
53
|
-
void initialize(MHP* mhp, LockAnalysis* lsa);
|
|
54
|
-
|
|
55
|
-
/// Prune candidate instructions that are thread local
|
|
56
|
-
void pruneThreadLocal(PointerAnalysis*pta);
|
|
57
|
-
|
|
58
|
-
/// Prune candidate instructions that non-mhp and non-alias with others
|
|
59
|
-
void pruneAliasMHP(PointerAnalysis* pta);
|
|
60
|
-
|
|
61
|
-
/// Perform annotation
|
|
62
|
-
void performAnnotate();
|
|
63
|
-
|
|
64
|
-
/// Collect all load and store instruction
|
|
65
|
-
void collectLoadStoreInst(SVFModule* mod);
|
|
66
|
-
|
|
67
|
-
/// Get operand of store and load
|
|
68
|
-
const Value* getStoreOperand(const Instruction* inst);
|
|
69
|
-
const Value* getLoadOperand(const Instruction* inst);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
/// Check if Function "F" is memset
|
|
73
|
-
inline bool isMemset(const Instruction* I)
|
|
74
|
-
{
|
|
75
|
-
const SVFInstruction* svfInst = LLVMModuleSet::getLLVMModuleSet()->getSVFInstruction(I);
|
|
76
|
-
const SVFFunction* F =SVFUtil::getCallee(svfInst);
|
|
77
|
-
return F && F->getName().find("llvm.memset") != std::string::npos;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/// Check if Function "F" is memcpy
|
|
81
|
-
inline bool isMemcpy(const Instruction* I)
|
|
82
|
-
{
|
|
83
|
-
const SVFInstruction* svfInst = LLVMModuleSet::getLLVMModuleSet()->getSVFInstruction(I);
|
|
84
|
-
const SVFFunction* F = SVFUtil::getCallee(svfInst);
|
|
85
|
-
return F && F->getName().find("llvm.memcpy") != std::string::npos;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private:
|
|
89
|
-
MHP* mhp;
|
|
90
|
-
LockAnalysis* lsa;
|
|
91
|
-
InstSet loadset;
|
|
92
|
-
InstSet storeset;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// RCMemoryPartitioning mp;
|
|
96
|
-
|
|
97
|
-
public:
|
|
98
|
-
|
|
99
|
-
u32_t numOfAllSt;
|
|
100
|
-
u32_t numOfAllLd;
|
|
101
|
-
u32_t numOfNonLocalSt;
|
|
102
|
-
u32_t numOfNonLocalLd;
|
|
103
|
-
u32_t numOfAliasSt;
|
|
104
|
-
u32_t numOfAliasLd;
|
|
105
|
-
u32_t numOfMHPSt;
|
|
106
|
-
u32_t numOfMHPLd;
|
|
107
|
-
u32_t numOfAnnotatedSt;
|
|
108
|
-
u32_t numOfAnnotatedLd;
|
|
109
|
-
|
|
110
|
-
/// Constant INTERLEV_FLAG values
|
|
111
|
-
//@{
|
|
112
|
-
static const u32_t ANNO_MHP= 0x04;
|
|
113
|
-
static const u32_t ANNO_ALIAS= 0x02;
|
|
114
|
-
static const u32_t ANNO_LOCAL= 0x01;
|
|
115
|
-
//@}
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
} // End namespace SVF
|
|
119
|
-
|
|
120
|
-
#endif /* MTAANNOTATOR_H_ */
|