svf-tools 1.0.966 → 1.0.968
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/index.html +10 -2
- package/package.json +1 -1
- package/svf/include/AE/Core/IntervalValue.h +97 -54
- package/svf/include/AE/Core/NumericValue.h +748 -115
- package/svf/include/MemoryModel/AccessPath.h +1 -0
- package/svf/include/SVFIR/SVFStatements.h +1 -0
- package/svf/lib/AE/Core/RelationSolver.cpp +17 -11
- package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +1 -10
- package/svf/lib/AE/Svfexe/BufOverflowChecker.cpp +1 -9
- package/svf/lib/AE/Svfexe/SVFIR2AbsState.cpp +75 -87
- package/svf/lib/MemoryModel/AccessPath.cpp +27 -26
- package/svf-llvm/tools/AE/ae.cpp +218 -1
- package/svf-llvm/tools/Example/svf-ex.cpp +0 -1
package/index.html
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
<p><a href="https://github.com/svf-tools/SVF">SVF</a> is a static tool that enables scalable and precise value-flow analysis for source code. SVF allows value-flow construction and pointer analysis to be performed iteratively, thereby providing increasingly improved precision for both. </p>
|
|
28
28
|
|
|
29
|
-
For
|
|
29
|
+
For pointer analysis frameworks that work for Java and Rust, we refer to <a href="https://qilinpta.github.io/"> Qilin</a> and <a href="https://rustanlys.github.io/rupta"> Rupta </a>.
|
|
30
30
|
|
|
31
31
|
<h2>
|
|
32
32
|
<a id="what-kind-of-analyses-does-svf-provide" class="anchor" href="#what-kind-of-analyses-does-svf-provide" aria-hidden="true"><span class="octicon octicon-link"></span></a>What kind of analyses does SVF provide?</h2>
|
|
@@ -66,6 +66,14 @@ GPLv3
|
|
|
66
66
|
|
|
67
67
|
<p> Please make a <b>pull request</b> or <b>email us</b> if you have a paper for this list.</p>
|
|
68
68
|
|
|
69
|
+
<p> Xiao Cheng, Jiawei Ren and Yulei Sui. <a href = "https://yuleisui.github.io/publications/fse24a.pdf">Fast Graph Simplification for Path-Sensitive Typestate Analysis through Tempo-Spatial Multi-Point Slicing </a>. ACM International Conference on the Foundations of Software Engineering. (FSE'24) </p>
|
|
70
|
+
|
|
71
|
+
<p> Xiao Cheng, Jiawei Wang and Yulei Sui. <a href= "https://yuleisui.github.io/publications/icse24a.pdf"> Precise Sparse Abstract Execution via Cross-Domain Interaction. </a> 46th International Conference on Software Engineering (ICSE'24) </p>
|
|
72
|
+
|
|
73
|
+
<p> Yuxiang Lei, Camille Bossut, Yulei Sui and Qirun Zhang. <a href="https://dl.acm.org/doi/pdf/10.1145/3656451"> Context-Free Language Reachability via Skewed Tabulation. </a> ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI'24) </p>
|
|
74
|
+
|
|
75
|
+
<p> Yuxiang Lei, Yulei Sui, Shin Hwei Tan, Qirun Zhang. <a href="https://yuleisui.github.io/publications/pldi23.pdf">Recursive State Machine Guided Graph Folding for Context-Free Language Reachability. </a> 44th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI'23) </p>
|
|
76
|
+
|
|
69
77
|
<p> Yuxiang Lei, Yulei Sui, Shuo Ding, and Qirun Zhang. <a href ="https://yuleisui.github.io/publications/oopsla22.pdf"> Taming Transitive Redundancy for Context-Free Language Reachability</a>. ACM SIGPLAN Conference on Object-Oriented Programming, Systems, Languages, and Applications (OOPSLA'22) </p>
|
|
70
78
|
|
|
71
79
|
<p>Yaohui Chen, Peng Li, Jun Xu, Shengjian Guo,Rundong Zhou, Yulong Zhang, Tao Wei, and Long Lu. <a href ="https://arxiv.org/pdf/1906.07327.pdf">SAVIOR: Towards Bug-Driven Hybrid Testing</a>, 41st IEEE Symposium on Security and Privacy (S&P'20)</p>
|
|
@@ -112,7 +120,7 @@ for Multithreaded Programs</a>, International Symposium on Code Generation and O
|
|
|
112
120
|
<h2>
|
|
113
121
|
<a id="contacts" class="anchor" href="#contacts" aria-hidden="true"><span class="octicon octicon-link"></span></a>Contacts</h2>
|
|
114
122
|
Any comments, contributions and collaborations are welcomed.
|
|
115
|
-
Please contact
|
|
123
|
+
Please contact <a href="mailto:y.sui@unsw.edu.au">Yulei Sui</a> if you have any questions.
|
|
116
124
|
|
|
117
125
|
|
|
118
126
|
</section>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svf-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.968",
|
|
4
4
|
"description": "* <b>[TypeClone](https://github.com/SVF-tools/SVF/wiki/TypeClone) published in our [ECOOP paper](https://yuleisui.github.io/publications/ecoop20.pdf) is now available in SVF </b> * <b>SVF now uses a single script for its build. Just type [`source ./build.sh`](https://github.com/SVF-tools/SVF/blob/master/build.sh) in your terminal, that's it!</b> * <b>SVF now supports LLVM-10.0.0! </b> * <b>We thank [bsauce](https://github.com/bsauce) for writing a user manual of SVF ([link1](https://www.jianshu.com/p/068a08ec749c) and [link2](https://www.jianshu.com/p/777c30d4240e)) in Chinese </b> * <b>SVF now supports LLVM-9.0.0 (Thank [Byoungyoung Lee](https://github.com/SVF-tools/SVF/issues/142) for his help!). </b> * <b>SVF now supports a set of [field-sensitive pointer analyses](https://yuleisui.github.io/publications/sas2019a.pdf). </b> * <b>[Use SVF as an external lib](https://github.com/SVF-tools/SVF/wiki/Using-SVF-as-a-lib-in-your-own-tool) for your own project (Contributed by [Hongxu Chen](https://github.com/HongxuChen)). </b> * <b>SVF now supports LLVM-7.0.0. </b> * <b>SVF now supports Docker. [Try SVF in Docker](https://github.com/SVF-tools/SVF/wiki/Try-SVF-in-Docker)! </b> * <b>SVF now supports [LLVM-6.0.0](https://github.com/svf-tools/SVF/pull/38) (Contributed by [Jack Anthony](https://github.com/jackanth)). </b> * <b>SVF now supports [LLVM-4.0.0](https://github.com/svf-tools/SVF/pull/23) (Contributed by Jared Carlson. Thank [Jared](https://github.com/jcarlson23) and [Will](https://github.com/dtzWill) for their in-depth [discussions](https://github.com/svf-tools/SVF/pull/18) about updating SVF!) </b> * <b>SVF now supports analysis for C++ programs.</b> <br />",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -51,26 +51,35 @@ private:
|
|
|
51
51
|
// Upper bound
|
|
52
52
|
BoundedInt _ub;
|
|
53
53
|
|
|
54
|
-
// Invariant: isBottom() <=> _lb =
|
|
54
|
+
// Invariant: isBottom() <=> _lb = +inf && _ub = -inf
|
|
55
55
|
public:
|
|
56
|
+
friend IntervalValue operator+(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
57
|
+
friend IntervalValue operator-(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
58
|
+
friend IntervalValue operator*(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
59
|
+
friend IntervalValue operator/(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
60
|
+
friend IntervalValue operator<<(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
61
|
+
friend IntervalValue operator>>(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
62
|
+
friend IntervalValue operator&(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
63
|
+
friend IntervalValue operator|(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
64
|
+
friend IntervalValue operator^(const IntervalValue &lhs, const IntervalValue &rhs);
|
|
56
65
|
|
|
57
66
|
bool isTop() const
|
|
58
67
|
{
|
|
59
|
-
return
|
|
68
|
+
return _lb.is_minus_infinity() && _ub.is_plus_infinity();
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
bool isBottom() const
|
|
63
72
|
{
|
|
64
|
-
return
|
|
73
|
+
return _lb.is_plus_infinity() && _ub.is_minus_infinity();
|
|
65
74
|
}
|
|
66
75
|
|
|
67
|
-
/// Get minus infinity -
|
|
76
|
+
/// Get minus infinity -inf
|
|
68
77
|
static BoundedInt minus_infinity()
|
|
69
78
|
{
|
|
70
79
|
return BoundedInt::minus_infinity();
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
/// Get plus infinity +
|
|
82
|
+
/// Get plus infinity +inf
|
|
74
83
|
static BoundedInt plus_infinity()
|
|
75
84
|
{
|
|
76
85
|
return BoundedInt::plus_infinity();
|
|
@@ -81,16 +90,16 @@ public:
|
|
|
81
90
|
return e.is_infinity();
|
|
82
91
|
}
|
|
83
92
|
|
|
84
|
-
/// Create the IntervalValue [-
|
|
93
|
+
/// Create the IntervalValue [-inf, +inf]
|
|
85
94
|
static IntervalValue top()
|
|
86
95
|
{
|
|
87
96
|
return IntervalValue(minus_infinity(), plus_infinity());
|
|
88
97
|
}
|
|
89
98
|
|
|
90
|
-
/// Create the bottom IntervalValue
|
|
99
|
+
/// Create the bottom IntervalValue [+inf, -inf]
|
|
91
100
|
static IntervalValue bottom()
|
|
92
101
|
{
|
|
93
|
-
return IntervalValue(
|
|
102
|
+
return IntervalValue(plus_infinity(), minus_infinity());
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
/// Create default IntervalValue
|
|
@@ -108,7 +117,10 @@ public:
|
|
|
108
117
|
explicit IntervalValue(BoundedInt n) : IntervalValue(n, n) {}
|
|
109
118
|
|
|
110
119
|
/// Create the IntervalValue [lb, ub]
|
|
111
|
-
explicit IntervalValue(BoundedInt lb, BoundedInt ub) : _lb(std::move(lb)), _ub(std::move(ub))
|
|
120
|
+
explicit IntervalValue(BoundedInt lb, BoundedInt ub) : _lb(std::move(lb)), _ub(std::move(ub))
|
|
121
|
+
{
|
|
122
|
+
assert((isBottom() || _lb.leq(_ub)) && "lower bound should be less than or equal to upper bound");
|
|
123
|
+
}
|
|
112
124
|
|
|
113
125
|
explicit IntervalValue(s64_t lb, s64_t ub) : IntervalValue(BoundedInt(lb), BoundedInt(ub)) {}
|
|
114
126
|
|
|
@@ -218,25 +230,6 @@ public:
|
|
|
218
230
|
return this->_ub;
|
|
219
231
|
}
|
|
220
232
|
|
|
221
|
-
/// Set the lower bound
|
|
222
|
-
void setLb(const BoundedInt &lb)
|
|
223
|
-
{
|
|
224
|
-
this->_lb = lb;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/// Set the upper bound
|
|
228
|
-
void setUb(const BoundedInt &ub)
|
|
229
|
-
{
|
|
230
|
-
this->_ub = ub;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/// Set the lower bound
|
|
234
|
-
void setValue(const BoundedInt &lb, const BoundedInt &ub)
|
|
235
|
-
{
|
|
236
|
-
this->_lb = lb;
|
|
237
|
-
this->_ub = ub;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
233
|
/// Return true if the IntervalValue is [0, 0]
|
|
241
234
|
bool is_zero() const
|
|
242
235
|
{
|
|
@@ -289,8 +282,8 @@ public:
|
|
|
289
282
|
/// Set current IntervalValue as bottom
|
|
290
283
|
void set_to_bottom()
|
|
291
284
|
{
|
|
292
|
-
this->_lb =
|
|
293
|
-
this->_ub =
|
|
285
|
+
this->_lb = plus_infinity();
|
|
286
|
+
this->_ub = minus_infinity();
|
|
294
287
|
}
|
|
295
288
|
|
|
296
289
|
/// Set current IntervalValue as top
|
|
@@ -430,8 +423,7 @@ public:
|
|
|
430
423
|
}
|
|
431
424
|
else
|
|
432
425
|
{
|
|
433
|
-
|
|
434
|
-
this->_ub = other.ub();
|
|
426
|
+
setValue(other.lb(), other.ub());
|
|
435
427
|
}
|
|
436
428
|
}
|
|
437
429
|
else if (other.isBottom())
|
|
@@ -440,8 +432,7 @@ public:
|
|
|
440
432
|
}
|
|
441
433
|
else
|
|
442
434
|
{
|
|
443
|
-
this->
|
|
444
|
-
this->_ub = max(this->ub(), other.ub());
|
|
435
|
+
setValue(min(this->lb(), other.lb()), max(this->ub(), other.ub()));
|
|
445
436
|
}
|
|
446
437
|
}
|
|
447
438
|
|
|
@@ -459,8 +450,7 @@ public:
|
|
|
459
450
|
}
|
|
460
451
|
else
|
|
461
452
|
{
|
|
462
|
-
|
|
463
|
-
this->_ub = !ub().geq(other.ub()) ? plus_infinity() : this->ub();
|
|
453
|
+
setValue(!lb().leq(other.lb()) ? minus_infinity() : this->lb(), !ub().geq(other.ub()) ? plus_infinity() : this->ub());
|
|
464
454
|
}
|
|
465
455
|
}
|
|
466
456
|
|
|
@@ -477,8 +467,7 @@ public:
|
|
|
477
467
|
}
|
|
478
468
|
else
|
|
479
469
|
{
|
|
480
|
-
this->_lb
|
|
481
|
-
this->_ub = is_infinite(this->ub()) ? other._ub : this->_ub;
|
|
470
|
+
setValue(is_infinite(this->lb()) ? other._lb : this->_lb, is_infinite(this->ub()) ? other._ub : this->_ub);
|
|
482
471
|
}
|
|
483
472
|
}
|
|
484
473
|
|
|
@@ -491,10 +480,14 @@ public:
|
|
|
491
480
|
}
|
|
492
481
|
else
|
|
493
482
|
{
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
if (this->isBottom())
|
|
483
|
+
if (!(max(this->_lb, other.lb()).leq(min(this->_ub, other.ub()))))
|
|
484
|
+
{
|
|
497
485
|
this->set_to_bottom();
|
|
486
|
+
}
|
|
487
|
+
else
|
|
488
|
+
{
|
|
489
|
+
setValue(max(this->_lb, other.lb()), min(this->_ub, other.ub()));
|
|
490
|
+
}
|
|
498
491
|
}
|
|
499
492
|
}
|
|
500
493
|
|
|
@@ -530,7 +523,24 @@ public:
|
|
|
530
523
|
}
|
|
531
524
|
return rawStr.str();
|
|
532
525
|
}
|
|
526
|
+
private:
|
|
527
|
+
/// Set the lower bound
|
|
528
|
+
void setValue(const BoundedInt &lb, const BoundedInt &ub)
|
|
529
|
+
{
|
|
530
|
+
assert((isBottom() || _lb.leq(_ub)) && "lower bound should be less than or equal to upper bound");
|
|
531
|
+
this->_lb = lb;
|
|
532
|
+
this->_ub = ub;
|
|
533
|
+
}
|
|
533
534
|
|
|
535
|
+
private:
|
|
536
|
+
// internal use for create bottom-tolerant IntervalValue
|
|
537
|
+
static IntervalValue create(const BoundedInt& lb, const BoundedInt& ub)
|
|
538
|
+
{
|
|
539
|
+
if (!lb.leq(ub))
|
|
540
|
+
return IntervalValue::bottom();
|
|
541
|
+
else
|
|
542
|
+
return IntervalValue(lb, ub);
|
|
543
|
+
}
|
|
534
544
|
}; // end class IntervalValue
|
|
535
545
|
|
|
536
546
|
/// Add IntervalValues
|
|
@@ -583,7 +593,7 @@ inline IntervalValue operator*(const IntervalValue &lhs,
|
|
|
583
593
|
BoundedInt lu = lhs.lb() * rhs.ub();
|
|
584
594
|
BoundedInt ul = lhs.ub() * rhs.lb();
|
|
585
595
|
BoundedInt uu = lhs.ub() * rhs.ub();
|
|
586
|
-
std::vector<
|
|
596
|
+
std::vector<BoundedInt> vec{ll, lu, ul, uu};
|
|
587
597
|
return IntervalValue(BoundedInt::min(vec),
|
|
588
598
|
BoundedInt::max(vec));
|
|
589
599
|
}
|
|
@@ -599,7 +609,22 @@ inline IntervalValue operator/(const IntervalValue &lhs,
|
|
|
599
609
|
}
|
|
600
610
|
else if (rhs.contains(0))
|
|
601
611
|
{
|
|
602
|
-
|
|
612
|
+
IntervalValue lb = IntervalValue::create(rhs.lb(), -1);
|
|
613
|
+
IntervalValue ub = IntervalValue::create(1, rhs.ub());
|
|
614
|
+
IntervalValue l_res = lhs / lb;
|
|
615
|
+
IntervalValue r_res = lhs / ub;
|
|
616
|
+
l_res.join_with(r_res);
|
|
617
|
+
return l_res;
|
|
618
|
+
}
|
|
619
|
+
else if (lhs.contains(0))
|
|
620
|
+
{
|
|
621
|
+
IntervalValue lb = IntervalValue::create(lhs.lb(), -1);
|
|
622
|
+
IntervalValue ub = IntervalValue::create(1, lhs.ub());
|
|
623
|
+
IntervalValue l_res = lb / rhs;
|
|
624
|
+
IntervalValue r_res = ub / rhs;
|
|
625
|
+
l_res.join_with(r_res);
|
|
626
|
+
l_res.join_with(IntervalValue(0));
|
|
627
|
+
return l_res;
|
|
603
628
|
}
|
|
604
629
|
else
|
|
605
630
|
{
|
|
@@ -608,10 +633,11 @@ inline IntervalValue operator/(const IntervalValue &lhs,
|
|
|
608
633
|
BoundedInt lu = lhs.lb() / rhs.ub();
|
|
609
634
|
BoundedInt ul = lhs.ub() / rhs.lb();
|
|
610
635
|
BoundedInt uu = lhs.ub() / rhs.ub();
|
|
611
|
-
std::vector<
|
|
636
|
+
std::vector<BoundedInt> vec{ll, lu, ul, uu};
|
|
612
637
|
|
|
613
|
-
|
|
614
|
-
|
|
638
|
+
IntervalValue res = IntervalValue(BoundedInt::min(vec),
|
|
639
|
+
BoundedInt::max(vec));
|
|
640
|
+
return res;
|
|
615
641
|
}
|
|
616
642
|
}
|
|
617
643
|
|
|
@@ -872,9 +898,26 @@ inline IntervalValue operator<<(const IntervalValue &lhs, const IntervalValue &r
|
|
|
872
898
|
shift.meet_with(IntervalValue(0, IntervalValue::plus_infinity()));
|
|
873
899
|
if (shift.isBottom())
|
|
874
900
|
return IntervalValue::bottom();
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
901
|
+
BoundedInt lb = 0;
|
|
902
|
+
// If the shift is greater than 32, the result is always 0
|
|
903
|
+
if ((s32_t) shift.lb().getNumeral() >= 32 || shift.lb().is_infinity())
|
|
904
|
+
{
|
|
905
|
+
lb = IntervalValue::minus_infinity();
|
|
906
|
+
}
|
|
907
|
+
else
|
|
908
|
+
{
|
|
909
|
+
lb = (1 << (s32_t) shift.lb().getNumeral());
|
|
910
|
+
}
|
|
911
|
+
BoundedInt ub = 0;
|
|
912
|
+
if (shift.ub().is_infinity())
|
|
913
|
+
{
|
|
914
|
+
ub = IntervalValue::plus_infinity();
|
|
915
|
+
}
|
|
916
|
+
else
|
|
917
|
+
{
|
|
918
|
+
ub = (1 << (s32_t) shift.ub().getNumeral());
|
|
919
|
+
}
|
|
920
|
+
IntervalValue coeff(lb, ub);
|
|
878
921
|
return lhs * coeff;
|
|
879
922
|
}
|
|
880
923
|
}
|
|
@@ -895,8 +938,8 @@ inline IntervalValue operator>>(const IntervalValue &lhs, const IntervalValue &r
|
|
|
895
938
|
return IntervalValue::bottom();
|
|
896
939
|
if (lhs.contains(0))
|
|
897
940
|
{
|
|
898
|
-
IntervalValue l(lhs.lb(), -1);
|
|
899
|
-
IntervalValue u(1, lhs.ub());
|
|
941
|
+
IntervalValue l = IntervalValue::create(lhs.lb(), -1);
|
|
942
|
+
IntervalValue u = IntervalValue::create(1, lhs.ub());
|
|
900
943
|
IntervalValue tmp = l >> rhs;
|
|
901
944
|
tmp.join_with(u >> rhs);
|
|
902
945
|
tmp.join_with(IntervalValue(0));
|
|
@@ -908,7 +951,7 @@ inline IntervalValue operator>>(const IntervalValue &lhs, const IntervalValue &r
|
|
|
908
951
|
BoundedInt lu = lhs.lb() >> shift.ub();
|
|
909
952
|
BoundedInt ul = lhs.ub() >> shift.lb();
|
|
910
953
|
BoundedInt uu = lhs.ub() >> shift.ub();
|
|
911
|
-
std::vector<
|
|
954
|
+
std::vector<BoundedInt> vec{ll, lu, ul, uu};
|
|
912
955
|
return IntervalValue(BoundedInt::min(vec),
|
|
913
956
|
BoundedInt::max(vec));
|
|
914
957
|
}
|
|
@@ -962,7 +1005,7 @@ inline IntervalValue operator|(const IntervalValue &lhs, const IntervalValue &rh
|
|
|
962
1005
|
rhs.lb().getNumeral() >= 0 && !rhs.ub().is_infinity())
|
|
963
1006
|
{
|
|
964
1007
|
s64_t m = std::max(lhs.ub().getNumeral(), rhs.ub().getNumeral());
|
|
965
|
-
s64_t ub = next_power_of_2(s64_t(m
|
|
1008
|
+
s64_t ub = next_power_of_2(s64_t(m)) - 1;
|
|
966
1009
|
return IntervalValue((s64_t) 0, (s64_t) ub);
|
|
967
1010
|
}
|
|
968
1011
|
else
|
|
@@ -991,7 +1034,7 @@ inline IntervalValue operator^(const IntervalValue &lhs, const IntervalValue &rh
|
|
|
991
1034
|
rhs.lb().getNumeral() >= 0 && !rhs.ub().is_infinity())
|
|
992
1035
|
{
|
|
993
1036
|
s64_t m = std::max(lhs.ub().getNumeral(), rhs.ub().getNumeral());
|
|
994
|
-
s64_t ub = next_power_of_2(s64_t(m
|
|
1037
|
+
s64_t ub = next_power_of_2(s64_t(m)) - 1;
|
|
995
1038
|
return IntervalValue((s64_t) 0, (s64_t) ub);
|
|
996
1039
|
}
|
|
997
1040
|
else
|