blob: 8d16f12d32eed9398b3870885ff6e53909e19bda [file] [log] [blame]
Ted Kremeneka90ccfe2008-01-31 19:34:24 +00001//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- C++ -*-==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This files defines RValue, LValue, and NonLValue, classes that represent
11// abstract r-values for use with path-sensitive value tracking.
12//
13//===----------------------------------------------------------------------===//
14
Ted Kremenekcc409b72008-02-14 17:30:51 +000015#include "clang/Analysis/PathSensitive/RValues.h"
Ted Kremeneka90ccfe2008-01-31 19:34:24 +000016
17using namespace clang;
18using llvm::dyn_cast;
19using llvm::cast;
20using llvm::APSInt;
21
22//===----------------------------------------------------------------------===//
23// SymbolManager.
24//===----------------------------------------------------------------------===//
25
26SymbolID SymbolManager::getSymbol(ParmVarDecl* D) {
Ted Kremenekd131c4f2008-02-07 05:48:01 +000027 SymbolID& X = DataToSymbol[getKey(D)];
Ted Kremeneka90ccfe2008-01-31 19:34:24 +000028
29 if (!X.isInitialized()) {
30 X = SymbolToData.size();
Ted Kremenekd131c4f2008-02-07 05:48:01 +000031 SymbolToData.push_back(SymbolDataParmVar(D));
Ted Kremeneka90ccfe2008-01-31 19:34:24 +000032 }
33
34 return X;
35}
36
Ted Kremenekd131c4f2008-02-07 05:48:01 +000037SymbolID SymbolManager::getContentsOfSymbol(SymbolID sym) {
38 SymbolID& X = DataToSymbol[getKey(sym)];
39
40 if (!X.isInitialized()) {
41 X = SymbolToData.size();
42 SymbolToData.push_back(SymbolDataContentsOf(sym));
43 }
44
45 return X;
46}
47
Ted Kremenekfeb01f62008-02-06 17:32:17 +000048QualType SymbolData::getType() const {
49 switch (getKind()) {
50 default:
51 assert (false && "getType() not implemented for this symbol.");
52
53 case ParmKind:
Ted Kremenekd131c4f2008-02-07 05:48:01 +000054 return cast<SymbolDataParmVar>(this)->getDecl()->getType();
55
Ted Kremenekfeb01f62008-02-06 17:32:17 +000056 }
57}
58
Ted Kremeneka90ccfe2008-01-31 19:34:24 +000059SymbolManager::SymbolManager() {}
60SymbolManager::~SymbolManager() {}
61
62//===----------------------------------------------------------------------===//
Ted Kremenek1fbdb022008-02-05 21:32:43 +000063// Values and ValueManager.
Ted Kremeneka90ccfe2008-01-31 19:34:24 +000064//===----------------------------------------------------------------------===//
65
66ValueManager::~ValueManager() {
67 // Note that the dstor for the contents of APSIntSet will never be called,
68 // so we iterate over the set and invoke the dstor for each APSInt. This
69 // frees an aux. memory allocated to represent very large constants.
70 for (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I)
71 I->getValue().~APSInt();
72}
73
Ted Kremenek1fbdb022008-02-05 21:32:43 +000074const APSInt& ValueManager::getValue(const APSInt& X) {
Ted Kremeneka90ccfe2008-01-31 19:34:24 +000075 llvm::FoldingSetNodeID ID;
76 void* InsertPos;
77 typedef llvm::FoldingSetNodeWrapper<APSInt> FoldNodeTy;
78
79 X.Profile(ID);
80 FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos);
81
82 if (!P) {
83 P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>();
84 new (P) FoldNodeTy(X);
85 APSIntSet.InsertNode(P, InsertPos);
86 }
87
88 return *P;
89}
90
Ted Kremenek1fbdb022008-02-05 21:32:43 +000091const APSInt& ValueManager::getValue(uint64_t X, unsigned BitWidth,
92 bool isUnsigned) {
Ted Kremeneka90ccfe2008-01-31 19:34:24 +000093 APSInt V(BitWidth, isUnsigned);
94 V = X;
95 return getValue(V);
96}
97
Ted Kremenek1fbdb022008-02-05 21:32:43 +000098const APSInt& ValueManager::getValue(uint64_t X, QualType T,
99 SourceLocation Loc) {
100
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000101 unsigned bits = Ctx.getTypeSize(T, Loc);
102 APSInt V(bits, T->isUnsignedIntegerType());
103 V = X;
104 return getValue(V);
105}
106
Ted Kremenek1fbdb022008-02-05 21:32:43 +0000107const SymIntConstraint&
108ValueManager::getConstraint(SymbolID sym, BinaryOperator::Opcode Op,
109 const llvm::APSInt& V) {
110
111 llvm::FoldingSetNodeID ID;
112 SymIntConstraint::Profile(ID, sym, Op, V);
113 void* InsertPos;
114
115 SymIntConstraint* C = SymIntCSet.FindNodeOrInsertPos(ID, InsertPos);
116
117 if (!C) {
118 C = (SymIntConstraint*) BPAlloc.Allocate<SymIntConstraint>();
119 new (C) SymIntConstraint(sym, Op, V);
120 SymIntCSet.InsertNode(C, InsertPos);
121 }
122
123 return *C;
124}
125
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000126
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000127
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000128
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000129//===----------------------------------------------------------------------===//
130// Transfer function dispatch for Non-LValues.
131//===----------------------------------------------------------------------===//
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000132
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000133static const
134llvm::APSInt& EvaluateAPSInt(ValueManager& ValMgr, BinaryOperator::Opcode Op,
135 const llvm::APSInt& V1, const llvm::APSInt& V2) {
136
137 switch (Op) {
138 default:
139 assert (false && "Invalid Opcode.");
140
141 case BinaryOperator::Mul:
142 return ValMgr.getValue( V1 * V2 );
143
144 case BinaryOperator::Div:
145 return ValMgr.getValue( V1 / V2 );
146
147 case BinaryOperator::Rem:
148 return ValMgr.getValue( V1 % V2 );
149
150 case BinaryOperator::Add:
151 return ValMgr.getValue( V1 + V2 );
152
153 case BinaryOperator::Sub:
154 return ValMgr.getValue( V1 - V2 );
155
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000156 case BinaryOperator::Shl:
Ted Kremenek59c2d262008-02-08 07:14:58 +0000157 return ValMgr.getValue( V1.operator<<( (unsigned) V2.getZExtValue() ));
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000158
159 case BinaryOperator::Shr:
Ted Kremenek59c2d262008-02-08 07:14:58 +0000160 return ValMgr.getValue( V1.operator>>( (unsigned) V2.getZExtValue() ));
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000161
162 case BinaryOperator::LT:
163 return ValMgr.getTruthValue( V1 < V2 );
164
165 case BinaryOperator::GT:
166 return ValMgr.getTruthValue( V1 > V2 );
167
168 case BinaryOperator::LE:
169 return ValMgr.getTruthValue( V1 <= V2 );
170
171 case BinaryOperator::GE:
172 return ValMgr.getTruthValue( V1 >= V2 );
173
174 case BinaryOperator::EQ:
175 return ValMgr.getTruthValue( V1 == V2 );
176
177 case BinaryOperator::NE:
178 return ValMgr.getTruthValue( V1 != V2 );
179
180 // Note: LAnd, LOr, Comma are handled specially by higher-level logic.
181
182 case BinaryOperator::And:
183 return ValMgr.getValue( V1 & V2 );
184
185 case BinaryOperator::Or:
186 return ValMgr.getValue( V1 | V2 );
187 }
188}
189
190nonlval::ConcreteInt
191nonlval::ConcreteInt::EvalBinaryOp(ValueManager& ValMgr,
192 BinaryOperator::Opcode Op,
193 const nonlval::ConcreteInt& RHS) const {
194
195 return EvaluateAPSInt(ValMgr, Op, getValue(), RHS.getValue());
196}
197
198
199 // Bitwise-Complement.
200
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000201
202nonlval::ConcreteInt
203nonlval::ConcreteInt::EvalComplement(ValueManager& ValMgr) const {
204 return ValMgr.getValue(~getValue());
205}
206
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000207 // Unary Minus.
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000208
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000209nonlval::ConcreteInt
210nonlval::ConcreteInt::EvalMinus(ValueManager& ValMgr, UnaryOperator* U) const {
211 assert (U->getType() == U->getSubExpr()->getType());
212 assert (U->getType()->isIntegerType());
213 return ValMgr.getValue(-getValue());
Ted Kremenekc5d3b4c2008-02-04 16:58:30 +0000214}
215
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000216//===----------------------------------------------------------------------===//
217// Transfer function dispatch for LValues.
218//===----------------------------------------------------------------------===//
219
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000220lval::ConcreteInt
221lval::ConcreteInt::EvalBinaryOp(ValueManager& ValMgr,
222 BinaryOperator::Opcode Op,
223 const lval::ConcreteInt& RHS) const {
224
225 assert (Op == BinaryOperator::Add || Op == BinaryOperator::Sub ||
226 (Op >= BinaryOperator::LT && Op <= BinaryOperator::NE));
227
228 return EvaluateAPSInt(ValMgr, Op, getValue(), RHS.getValue());
229}
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000230
231NonLValue LValue::EQ(ValueManager& ValMgr, const LValue& RHS) const {
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000232 switch (getSubKind()) {
233 default:
234 assert(false && "EQ not implemented for this LValue.");
Ted Kremenek22031182008-02-08 02:57:34 +0000235 return cast<NonLValue>(UnknownVal());
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000236
Ted Kremenek0806acf2008-02-05 23:08:41 +0000237 case lval::ConcreteIntKind:
238 if (isa<lval::ConcreteInt>(RHS)) {
239 bool b = cast<lval::ConcreteInt>(this)->getValue() ==
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000240 cast<lval::ConcreteInt>(RHS).getValue();
241
Ted Kremenek0806acf2008-02-05 23:08:41 +0000242 return NonLValue::GetIntTruthValue(ValMgr, b);
243 }
244 else if (isa<lval::SymbolVal>(RHS)) {
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000245
Ted Kremenek0806acf2008-02-05 23:08:41 +0000246 const SymIntConstraint& C =
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000247 ValMgr.getConstraint(cast<lval::SymbolVal>(RHS).getSymbol(),
248 BinaryOperator::EQ,
249 cast<lval::ConcreteInt>(this)->getValue());
Ted Kremenek0806acf2008-02-05 23:08:41 +0000250
251 return nonlval::SymIntConstraintVal(C);
252 }
253
254 break;
255
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000256 case lval::SymbolValKind: {
257 if (isa<lval::ConcreteInt>(RHS)) {
258
259 const SymIntConstraint& C =
Ted Kremenek0806acf2008-02-05 23:08:41 +0000260 ValMgr.getConstraint(cast<lval::SymbolVal>(this)->getSymbol(),
261 BinaryOperator::EQ,
262 cast<lval::ConcreteInt>(RHS).getValue());
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000263
264 return nonlval::SymIntConstraintVal(C);
265 }
Ted Kremenek0806acf2008-02-05 23:08:41 +0000266
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000267 assert (!isa<lval::SymbolVal>(RHS) && "FIXME: Implement unification.");
268
269 break;
Ted Kremenek0806acf2008-02-05 23:08:41 +0000270 }
Ted Kremenek0806acf2008-02-05 23:08:41 +0000271
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000272 case lval::DeclValKind:
Ted Kremenek0806acf2008-02-05 23:08:41 +0000273 if (isa<lval::DeclVal>(RHS)) {
274 bool b = cast<lval::DeclVal>(*this) == cast<lval::DeclVal>(RHS);
275 return NonLValue::GetIntTruthValue(ValMgr, b);
276 }
277
278 break;
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000279 }
Ted Kremenek0806acf2008-02-05 23:08:41 +0000280
281 return NonLValue::GetIntTruthValue(ValMgr, false);
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000282}
283
284NonLValue LValue::NE(ValueManager& ValMgr, const LValue& RHS) const {
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000285 switch (getSubKind()) {
286 default:
Ted Kremenek0806acf2008-02-05 23:08:41 +0000287 assert(false && "NE not implemented for this LValue.");
Ted Kremenek22031182008-02-08 02:57:34 +0000288 return cast<NonLValue>(UnknownVal());
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000289
Ted Kremenek0806acf2008-02-05 23:08:41 +0000290 case lval::ConcreteIntKind:
291 if (isa<lval::ConcreteInt>(RHS)) {
292 bool b = cast<lval::ConcreteInt>(this)->getValue() !=
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000293 cast<lval::ConcreteInt>(RHS).getValue();
Ted Kremenek0806acf2008-02-05 23:08:41 +0000294
295 return NonLValue::GetIntTruthValue(ValMgr, b);
296 }
297 else if (isa<lval::SymbolVal>(RHS)) {
298
299 const SymIntConstraint& C =
300 ValMgr.getConstraint(cast<lval::SymbolVal>(RHS).getSymbol(),
301 BinaryOperator::NE,
302 cast<lval::ConcreteInt>(this)->getValue());
303
304 return nonlval::SymIntConstraintVal(C);
305 }
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000306
Ted Kremenek0806acf2008-02-05 23:08:41 +0000307 break;
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000308
Ted Kremenek0806acf2008-02-05 23:08:41 +0000309 case lval::SymbolValKind: {
310 if (isa<lval::ConcreteInt>(RHS)) {
311
312 const SymIntConstraint& C =
313 ValMgr.getConstraint(cast<lval::SymbolVal>(this)->getSymbol(),
314 BinaryOperator::NE,
315 cast<lval::ConcreteInt>(RHS).getValue());
316
317 return nonlval::SymIntConstraintVal(C);
318 }
319
320 assert (!isa<lval::SymbolVal>(RHS) && "FIXME: Implement sym !=.");
321
322 break;
323 }
324
325 case lval::DeclValKind:
326 if (isa<lval::DeclVal>(RHS)) {
327 bool b = cast<lval::DeclVal>(*this) == cast<lval::DeclVal>(RHS);
328 return NonLValue::GetIntTruthValue(ValMgr, b);
329 }
330
331 break;
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000332 }
Ted Kremenek0806acf2008-02-05 23:08:41 +0000333
334 return NonLValue::GetIntTruthValue(ValMgr, true);
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000335}
336
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000337
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000338
339//===----------------------------------------------------------------------===//
340// Utility methods for constructing Non-LValues.
341//===----------------------------------------------------------------------===//
342
343NonLValue NonLValue::GetValue(ValueManager& ValMgr, uint64_t X, QualType T,
344 SourceLocation Loc) {
345
Ted Kremenek329f8542008-02-05 21:52:21 +0000346 return nonlval::ConcreteInt(ValMgr.getValue(X, T, Loc));
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000347}
348
349NonLValue NonLValue::GetValue(ValueManager& ValMgr, IntegerLiteral* I) {
Ted Kremenek329f8542008-02-05 21:52:21 +0000350 return nonlval::ConcreteInt(ValMgr.getValue(APSInt(I->getValue(),
351 I->getType()->isUnsignedIntegerType())));
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000352}
353
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000354NonLValue NonLValue::GetIntTruthValue(ValueManager& ValMgr, bool b) {
355 return nonlval::ConcreteInt(ValMgr.getTruthValue(b));
356}
357
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000358RValue RValue::GetSymbolValue(SymbolManager& SymMgr, ParmVarDecl* D) {
359 QualType T = D->getType();
360
361 if (T->isPointerType() || T->isReferenceType())
Ted Kremenek329f8542008-02-05 21:52:21 +0000362 return lval::SymbolVal(SymMgr.getSymbol(D));
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000363 else
Ted Kremenek329f8542008-02-05 21:52:21 +0000364 return nonlval::SymbolVal(SymMgr.getSymbol(D));
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000365}
366
Ted Kremenek2a502572008-02-12 21:37:56 +0000367//===----------------------------------------------------------------------===//
368// Utility methods for constructing LValues.
369//===----------------------------------------------------------------------===//
370
371LValue LValue::GetValue(AddrLabelExpr* E) {
372 return lval::GotoLabel(E->getLabel());
Ted Kremenek5b6dc2d2008-02-07 01:08:27 +0000373}
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000374
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000375//===----------------------------------------------------------------------===//
376// Pretty-Printing.
377//===----------------------------------------------------------------------===//
378
Ted Kremenek2a502572008-02-12 21:37:56 +0000379void RValue::print() const {
380 print(*llvm::cerr.stream());
381}
382
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000383void RValue::print(std::ostream& Out) const {
384 switch (getBaseKind()) {
Ted Kremenek53c641a2008-02-08 03:02:48 +0000385 case UnknownKind:
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000386 Out << "Invalid";
387 break;
388
389 case NonLValueKind:
390 cast<NonLValue>(this)->print(Out);
391 break;
392
393 case LValueKind:
394 cast<LValue>(this)->print(Out);
395 break;
396
397 case UninitializedKind:
398 Out << "Uninitialized";
399 break;
400
401 default:
402 assert (false && "Invalid RValue.");
403 }
404}
405
Ted Kremenek0806acf2008-02-05 23:08:41 +0000406static void printOpcode(std::ostream& Out, BinaryOperator::Opcode Op) {
Ted Kremenek7e593362008-02-07 15:20:13 +0000407 switch (Op) {
408 case BinaryOperator::Add: Out << "+" ; break;
409 case BinaryOperator::Sub: Out << "-" ; break;
Ted Kremenek0806acf2008-02-05 23:08:41 +0000410 case BinaryOperator::EQ: Out << "=="; break;
411 case BinaryOperator::NE: Out << "!="; break;
412 default: assert(false && "Not yet implemented.");
413 }
414}
415
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000416void NonLValue::print(std::ostream& Out) const {
417 switch (getSubKind()) {
Ted Kremenek329f8542008-02-05 21:52:21 +0000418 case nonlval::ConcreteIntKind:
419 Out << cast<nonlval::ConcreteInt>(this)->getValue().toString();
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000420
421 if (cast<nonlval::ConcreteInt>(this)->getValue().isUnsigned())
422 Out << 'U';
423
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000424 break;
425
Ted Kremenek329f8542008-02-05 21:52:21 +0000426 case nonlval::SymbolValKind:
Ted Kremenek0806acf2008-02-05 23:08:41 +0000427 Out << '$' << cast<nonlval::SymbolVal>(this)->getSymbol();
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000428 break;
Ted Kremenek0806acf2008-02-05 23:08:41 +0000429
430 case nonlval::SymIntConstraintValKind: {
431 const nonlval::SymIntConstraintVal& C =
432 *cast<nonlval::SymIntConstraintVal>(this);
433
434 Out << '$' << C.getConstraint().getSymbol() << ' ';
435 printOpcode(Out, C.getConstraint().getOpcode());
436 Out << ' ' << C.getConstraint().getInt().toString();
Ted Kremenekcf78b6a2008-02-06 22:50:25 +0000437
438 if (C.getConstraint().getInt().isUnsigned())
439 Out << 'U';
440
Ted Kremenek0806acf2008-02-05 23:08:41 +0000441 break;
442 }
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000443
444 default:
445 assert (false && "Pretty-printed not implemented for this NonLValue.");
446 break;
447 }
448}
449
Ted Kremenekd131c4f2008-02-07 05:48:01 +0000450
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000451void LValue::print(std::ostream& Out) const {
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000452 switch (getSubKind()) {
Ted Kremenek329f8542008-02-05 21:52:21 +0000453 case lval::ConcreteIntKind:
454 Out << cast<lval::ConcreteInt>(this)->getValue().toString()
Ted Kremeneka6e4d212008-02-01 06:36:40 +0000455 << " (LValue)";
456 break;
457
Ted Kremenek329f8542008-02-05 21:52:21 +0000458 case lval::SymbolValKind:
Ted Kremenek0806acf2008-02-05 23:08:41 +0000459 Out << '$' << cast<lval::SymbolVal>(this)->getSymbol();
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000460 break;
Ted Kremenek2a502572008-02-12 21:37:56 +0000461
462 case lval::GotoLabelKind:
463 Out << "&&"
464 << cast<lval::GotoLabel>(this)->getLabel()->getID()->getName();
465 break;
Ted Kremenek08b66252008-02-06 04:31:33 +0000466
Ted Kremenek329f8542008-02-05 21:52:21 +0000467 case lval::DeclValKind:
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000468 Out << '&'
Ted Kremenek329f8542008-02-05 21:52:21 +0000469 << cast<lval::DeclVal>(this)->getDecl()->getIdentifier()->getName();
Ted Kremeneka90ccfe2008-01-31 19:34:24 +0000470 break;
471
472 default:
473 assert (false && "Pretty-printed not implemented for this LValue.");
474 break;
475 }
476}
Ted Kremenekd131c4f2008-02-07 05:48:01 +0000477