mycpp-examples

Coverage Report

Created: 2022-07-15 02:23

/home/uke/oil/mycpp/my_runtime.h
Line
Count
Source (jump to first uncovered line)
1
// my_runtime.h: Statically typed Python builtins.
2
//
3
// Builtin types: tuples, NotImplementedError, AssertionError
4
// Builtin functions: print(), repr(), ord()
5
// Builtin operators: str_concat(), str_repeat(), list_repeat()
6
// Builtin methods: Str::join, etc.  Note that Str is declared in gc_heap.h.
7
//
8
// TODO: Rename this file to my_builtins.{h,cc}?
9
10
#ifndef MY_RUNTIME_H
11
#define MY_RUNTIME_H
12
13
#include <algorithm>  // min(), sort()
14
#include <climits>    // CHAR_BIT
15
16
#include "gc_heap.h"
17
18
// TODO: Don't use 'using' in header
19
using gc_heap::AllocStr;
20
using gc_heap::CopyStr;
21
using gc_heap::Dict;
22
using gc_heap::List;
23
using gc_heap::StackRoots;
24
using gc_heap::Str;
25
26
class IndexError {};
27
class ValueError {};
28
class KeyError {};
29
30
class EOFError {};
31
32
class NotImplementedError {
33
 public:
34
0
  NotImplementedError() {
35
0
  }
36
0
  explicit NotImplementedError(int i) {  // e.g. in expr_to_ast
37
0
  }
38
0
  explicit NotImplementedError(const char* s) {
39
0
  }
40
0
  explicit NotImplementedError(Str* s) {
41
0
  }
42
};
43
44
class AssertionError {
45
 public:
46
0
  AssertionError() {
47
0
  }
48
0
  explicit AssertionError(int i) {  // e.g. in expr_to_ast
49
0
  }
50
0
  explicit AssertionError(const char* s) {
51
0
  }
52
0
  explicit AssertionError(Str* s) {
53
0
  }
54
};
55
56
template <class A, class B>
57
class Tuple2 {
58
 public:
59
112
  Tuple2(A a, B b) : a_(a), b_(b) {
60
112
  }
Unexecuted instantiation: _ZN6Tuple2IPN7gc_heap3StrES2_EC2ES2_S2_
_ZN6Tuple2IiPN7gc_heap3StrEEC2EiS2_
Line
Count
Source
59
6
  Tuple2(A a, B b) : a_(a), b_(b) {
60
6
  }
_ZN6Tuple2IPN7gc_heap3StrEiEC2ES2_i
Line
Count
Source
59
48
  Tuple2(A a, B b) : a_(a), b_(b) {
60
48
  }
_ZN6Tuple2IN9expr_asdl5tok_eEPN7gc_heap3StrEEC2ES1_S4_
Line
Count
Source
59
58
  Tuple2(A a, B b) : a_(a), b_(b) {
60
58
  }
61
115
  A at0() {
62
115
    return a_;
63
115
  }
_ZN6Tuple2IiPN7gc_heap3StrEE3at0Ev
Line
Count
Source
61
9
  A at0() {
62
9
    return a_;
63
9
  }
_ZN6Tuple2IPN7gc_heap3StrEiE3at0Ev
Line
Count
Source
61
48
  A at0() {
62
48
    return a_;
63
48
  }
_ZN6Tuple2IN9expr_asdl5tok_eEPN7gc_heap3StrEE3at0Ev
Line
Count
Source
61
58
  A at0() {
62
58
    return a_;
63
58
  }
64
113
  B at1() {
65
113
    return b_;
66
113
  }
_ZN6Tuple2IiPN7gc_heap3StrEE3at1Ev
Line
Count
Source
64
9
  B at1() {
65
9
    return b_;
66
9
  }
_ZN6Tuple2IPN7gc_heap3StrEiE3at1Ev
Line
Count
Source
64
46
  B at1() {
65
46
    return b_;
66
46
  }
_ZN6Tuple2IN9expr_asdl5tok_eEPN7gc_heap3StrEE3at1Ev
Line
Count
Source
64
58
  B at1() {
65
58
    return b_;
66
58
  }
67
68
 private:
69
  A a_;
70
  B b_;
71
};
72
73
template <class A, class B, class C>
74
class Tuple3 {
75
 public:
76
  Tuple3(A a, B b, C c) : a_(a), b_(b), c_(c) {
77
  }
78
  A at0() {
79
    return a_;
80
  }
81
  B at1() {
82
    return b_;
83
  }
84
  C at2() {
85
    return c_;
86
  }
87
88
 private:
89
  A a_;
90
  B b_;
91
  C c_;
92
};
93
94
template <class A, class B, class C, class D>
95
class Tuple4 {
96
 public:
97
  Tuple4(A a, B b, C c, D d) : a_(a), b_(b), c_(c), d_(d) {
98
  }
99
  A at0() {
100
    return a_;
101
  }
102
  B at1() {
103
    return b_;
104
  }
105
  C at2() {
106
    return c_;
107
  }
108
  D at3() {
109
    return d_;
110
  }
111
112
 private:
113
  A a_;
114
  B b_;
115
  C c_;
116
  D d_;
117
};
118
119
void println_stderr(Str* s);
120
121
void print(Str* s);
122
123
Str* repr(Str* s);
124
125
//
126
// Conversion Functions
127
//
128
129
bool _str_to_int(Str* s, int* result, int base);  // for testing only
130
int to_int(Str* s);
131
int to_int(Str* s, int base);
132
133
// int(a == b) used in arithmetic evaluator
134
0
inline int to_int(bool b) {
135
0
  return b;
136
0
}
137
138
1
inline bool to_bool(int i) {
139
1
  return i != 0;
140
1
}
141
142
0
inline bool to_bool(Str* s) {
143
0
  return len(s) != 0;
144
0
}
145
146
0
inline double to_float(Str* s) {
147
0
  NotImplemented();  // Uncalled
148
0
}
149
150
// https://stackoverflow.com/questions/3919995/determining-sprintf-buffer-size-whats-the-standard/11092994#11092994
151
// Notes:
152
// - Python 2.7's intobject.c has an erroneous +6
153
// - This is 13, but len('-2147483648') is 11, which means we only need 12?
154
// - This formula is valid for octal(), because 2^(3 bits) = 8
155
const int kIntBufSize = CHAR_BIT * sizeof(int) / 3 + 3;
156
157
14
inline Str* str(int i) {
158
  // Use a static buffer first because we don't know what n is until we call
159
  // snprintf().
160
14
  char buf[kIntBufSize];
161
14
  int length = snprintf(buf, kIntBufSize, "%d", i);
162
14
  return CopyStr(buf, length);
163
14
}
164
165
0
inline Str* str(double f) {  // TODO: should be double
166
0
  NotImplemented();          // Uncalled
167
0
}
168
169
33
inline int ord(Str* s) {
170
33
  assert(len(s) == 1);
171
  // signed to unsigned conversion, so we don't get values like -127
172
0
  uint8_t c = static_cast<uint8_t>(s->data_[0]);
173
33
  return c;
174
33
}
175
176
30
inline Str* chr(int i) {
177
30
  auto result = AllocStr(1);
178
30
  result->data_[0] = i;
179
30
  return result;
180
30
}
181
182
//
183
// Comparison and Sorting
184
//
185
186
0
inline int int_cmp(int a, int b) {
187
0
  if (a == b) {
188
0
    return 0;
189
0
  }
190
0
  return a < b ? -1 : 1;
191
0
}
192
193
// Used by [[ a > b ]] and so forth
194
0
inline int str_cmp(gc_heap::Str* a, gc_heap::Str* b) {
195
0
  int len_a = len(a);
196
0
  int len_b = len(b);
197
0
198
0
  int min = std::min(len_a, len_b);
199
0
  if (min == 0) {
200
0
    return int_cmp(len_a, len_b);
201
0
  }
202
0
  int comp = memcmp(a->data_, b->data_, min);
203
0
  if (comp == 0) {
204
0
    return int_cmp(len_a, len_b);  // tiebreaker
205
0
  }
206
0
  return comp;
207
0
}
208
209
0
inline bool _cmp(gc_heap::Str* a, gc_heap::Str* b) {
210
0
  return str_cmp(a, b) < 0;
211
0
}
212
213
// This is a METHOD definition.  It's in my_runtime.h so that gc_heap.h doesn't
214
// need to #include <algorithm>.  I think that would bloat all the ASDL types.
215
template <typename T>
216
void gc_heap::List<T>::sort() {
217
  std::sort(slab_->items_, slab_->items_ + len_, _cmp);
218
}
219
220
template <typename V>
221
List<Str*>* sorted(Dict<Str*, V>* d) {
222
  auto keys = d->keys();
223
  keys->sort();
224
  return keys;
225
}
226
227
// Is this only used by unit tests?
228
0
inline bool str_equals0(const char* c_string, Str* s) {
229
0
  int n = strlen(c_string);
230
0
  if (len(s) == n) {
231
0
    return memcmp(s->data_, c_string, n) == 0;
232
0
  } else {
233
0
    return false;
234
0
  }
235
0
}
236
237
//
238
// Free Standing Str, List, and Dict Functions
239
//
240
241
Str* str_concat(Str* a, Str* b);           // a + b when a and b are strings
242
Str* str_concat3(Str* a, Str* b, Str* c);  // for os_path::join()
243
244
Str* str_repeat(Str* s, int times);  // e.g. ' ' * 3
245
246
// e.g. ('a' in 'abc')
247
60
inline bool str_contains(Str* haystack, Str* needle) {
248
60
  if (len(needle) == 1) {
249
60
    return memchr(haystack->data_, needle->data_[0], len(haystack));
250
60
  }
251
252
  // NOTE(Jesse): Not sure what this TODO means.  Remove strstr and do it
253
  // ourselves maybe?
254
  // TODO: Implement substring
255
0
  assert(0);
256
257
  // cstring-TODO: this not rely on NUL termination
258
0
  const char* p = strstr(haystack->data_, needle->data_);
259
0
  return p != nullptr;
260
60
}
261
262
// ints, floats, enums like Kind
263
// e.g. 1 in [1, 2, 3]
264
template <typename T>
265
2
inline bool list_contains(List<T>* haystack, T needle) {
266
  // StackRoots _roots({&haystack});  // doesn't allocate
267
268
2
  int n = len(haystack);
269
3
  for (int i = 0; i < n; ++i) {
270
3
    if (haystack->index_(i) == needle) {
271
2
      return true;
272
2
    }
273
3
  }
274
0
  return false;
275
2
}
276
277
// e.g. 'a' in ['a', 'b', 'c']
278
2
inline bool list_contains(List<Str*>* haystack, Str* needle) {
279
  // StackRoots _roots({&haystack, &needle});  // doesn't allocate
280
281
2
  int n = len(haystack);
282
4
  for (int i = 0; i < n; ++i) {
283
3
    if (str_equals(haystack->index_(i), needle)) {
284
1
      return true;
285
1
    }
286
3
  }
287
1
  return false;
288
2
}
289
290
// TODO: mycpp can just generate the constructor instead?
291
// e.g. [None] * 3
292
template <typename T>
293
1
List<T>* list_repeat(T item, int times) {
294
1
  return gc_heap::NewList<T>(item, times);
295
1
}
296
297
template <typename K, typename V>
298
inline bool dict_contains(Dict<K, V>* haystack, K needle) {
299
  return haystack->position_of_key(needle) != -1;
300
}
301
302
// NOTE: This iterates over bytes.
303
class StrIter {
304
 public:
305
120
  explicit StrIter(Str* s) : s_(s), i_(0), len_(len(s)) {
306
    // We need this because StrIter is directly on the stack, and s_ could be
307
    // moved during iteration.
308
120
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&s_));
309
120
  }
310
120
  ~StrIter() {
311
120
    gc_heap::gHeap.PopRoot();
312
120
  }
313
146
  void Next() {
314
146
    i_++;
315
146
  }
316
266
  bool Done() {
317
266
    return i_ >= len_;
318
266
  }
319
147
  Str* Value() {  // similar to index_()
320
147
    Str* result = AllocStr(1);
321
147
    result->data_[0] = s_->data_[i_];
322
    // assert(result->data_[1] == '\0');
323
147
    return result;
324
147
  }
325
326
 private:
327
  Str* s_;
328
  int i_;
329
  int len_;
330
331
  DISALLOW_COPY_AND_ASSIGN(StrIter)
332
};
333
334
template <class T>
335
class ListIter {
336
 public:
337
103
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
338
    // We need this because ListIter is directly on the stack, and L_ could be
339
    // moved during iteration.
340
103
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&L_));
341
103
  }
_ZN8ListIterIPN7gc_heap3StrEEC2EPNS0_4ListIS2_EE
Line
Count
Source
337
16
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
338
    // We need this because ListIter is directly on the stack, and L_ could be
339
    // moved during iteration.
340
16
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&L_));
341
16
  }
_ZN8ListIterIiEC2EPN7gc_heap4ListIiEE
Line
Count
Source
337
4
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
338
    // We need this because ListIter is directly on the stack, and L_ could be
339
    // moved during iteration.
340
4
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&L_));
341
4
  }
_ZN8ListIterIP6Tuple2IPN7gc_heap3StrEiEEC2EPNS1_4ListIS5_EE
Line
Count
Source
337
1
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
338
    // We need this because ListIter is directly on the stack, and L_ could be
339
    // moved during iteration.
340
1
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&L_));
341
1
  }
_ZN8ListIterIP6Tuple2IiPN7gc_heap3StrEEEC2EPNS1_4ListIS5_EE
Line
Count
Source
337
2
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
338
    // We need this because ListIter is directly on the stack, and L_ could be
339
    // moved during iteration.
340
2
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&L_));
341
2
  }
Unexecuted instantiation: _ZN8ListIterIPN10hnode_asdl7hnode_tEEC2EPN7gc_heap4ListIS2_EE
_ZN8ListIterIPN10hnode_asdl5fieldEEC2EPN7gc_heap4ListIS2_EE
Line
Count
Source
337
79
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
338
    // We need this because ListIter is directly on the stack, and L_ could be
339
    // moved during iteration.
340
79
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&L_));
341
79
  }
_ZN8ListIterIbEC2EPN7gc_heap4ListIbEE
Line
Count
Source
337
1
  explicit ListIter(List<T>* L) : L_(L), i_(0) {
338
    // We need this because ListIter is directly on the stack, and L_ could be
339
    // moved during iteration.
340
1
    gc_heap::gHeap.PushRoot(reinterpret_cast<gc_heap::Obj**>(&L_));
341
1
  }
342
103
  ~ListIter() {
343
103
    gc_heap::gHeap.PopRoot();
344
103
  }
_ZN8ListIterIPN7gc_heap3StrEED2Ev
Line
Count
Source
342
16
  ~ListIter() {
343
16
    gc_heap::gHeap.PopRoot();
344
16
  }
_ZN8ListIterIiED2Ev
Line
Count
Source
342
4
  ~ListIter() {
343
4
    gc_heap::gHeap.PopRoot();
344
4
  }
_ZN8ListIterIP6Tuple2IPN7gc_heap3StrEiEED2Ev
Line
Count
Source
342
1
  ~ListIter() {
343
1
    gc_heap::gHeap.PopRoot();
344
1
  }
_ZN8ListIterIP6Tuple2IiPN7gc_heap3StrEEED2Ev
Line
Count
Source
342
2
  ~ListIter() {
343
2
    gc_heap::gHeap.PopRoot();
344
2
  }
Unexecuted instantiation: _ZN8ListIterIPN10hnode_asdl7hnode_tEED2Ev
_ZN8ListIterIPN10hnode_asdl5fieldEED2Ev
Line
Count
Source
342
79
  ~ListIter() {
343
79
    gc_heap::gHeap.PopRoot();
344
79
  }
_ZN8ListIterIbED2Ev
Line
Count
Source
342
1
  ~ListIter() {
343
1
    gc_heap::gHeap.PopRoot();
344
1
  }
345
217
  void Next() {
346
217
    i_++;
347
217
  }
_ZN8ListIterIPN7gc_heap3StrEE4NextEv
Line
Count
Source
345
78
  void Next() {
346
78
    i_++;
347
78
  }
_ZN8ListIterIiE4NextEv
Line
Count
Source
345
13
  void Next() {
346
13
    i_++;
347
13
  }
_ZN8ListIterIP6Tuple2IPN7gc_heap3StrEiEE4NextEv
Line
Count
Source
345
2
  void Next() {
346
2
    i_++;
347
2
  }
_ZN8ListIterIP6Tuple2IiPN7gc_heap3StrEEE4NextEv
Line
Count
Source
345
4
  void Next() {
346
4
    i_++;
347
4
  }
Unexecuted instantiation: _ZN8ListIterIPN10hnode_asdl7hnode_tEE4NextEv
_ZN8ListIterIPN10hnode_asdl5fieldEE4NextEv
Line
Count
Source
345
118
  void Next() {
346
118
    i_++;
347
118
  }
_ZN8ListIterIbE4NextEv
Line
Count
Source
345
2
  void Next() {
346
2
    i_++;
347
2
  }
348
320
  bool Done() {
349
    // "unsigned size_t was a mistake"
350
320
    return i_ >= static_cast<int>(L_->len_);
351
320
  }
_ZN8ListIterIPN7gc_heap3StrEE4DoneEv
Line
Count
Source
348
94
  bool Done() {
349
    // "unsigned size_t was a mistake"
350
94
    return i_ >= static_cast<int>(L_->len_);
351
94
  }
_ZN8ListIterIiE4DoneEv
Line
Count
Source
348
17
  bool Done() {
349
    // "unsigned size_t was a mistake"
350
17
    return i_ >= static_cast<int>(L_->len_);
351
17
  }
_ZN8ListIterIP6Tuple2IPN7gc_heap3StrEiEE4DoneEv
Line
Count
Source
348
3
  bool Done() {
349
    // "unsigned size_t was a mistake"
350
3
    return i_ >= static_cast<int>(L_->len_);
351
3
  }
_ZN8ListIterIP6Tuple2IiPN7gc_heap3StrEEE4DoneEv
Line
Count
Source
348
6
  bool Done() {
349
    // "unsigned size_t was a mistake"
350
6
    return i_ >= static_cast<int>(L_->len_);
351
6
  }
Unexecuted instantiation: _ZN8ListIterIPN10hnode_asdl7hnode_tEE4DoneEv
_ZN8ListIterIPN10hnode_asdl5fieldEE4DoneEv
Line
Count
Source
348
197
  bool Done() {
349
    // "unsigned size_t was a mistake"
350
197
    return i_ >= static_cast<int>(L_->len_);
351
197
  }
_ZN8ListIterIbE4DoneEv
Line
Count
Source
348
3
  bool Done() {
349
    // "unsigned size_t was a mistake"
350
3
    return i_ >= static_cast<int>(L_->len_);
351
3
  }
352
241
  T Value() {
353
241
    return L_->slab_->items_[i_];
354
241
  }
_ZN8ListIterIPN7gc_heap3StrEE5ValueEv
Line
Count
Source
352
79
  T Value() {
353
79
    return L_->slab_->items_[i_];
354
79
  }
_ZN8ListIterIiE5ValueEv
Line
Count
Source
352
13
  T Value() {
353
13
    return L_->slab_->items_[i_];
354
13
  }
_ZN8ListIterIP6Tuple2IPN7gc_heap3StrEiEE5ValueEv
Line
Count
Source
352
2
  T Value() {
353
2
    return L_->slab_->items_[i_];
354
2
  }
_ZN8ListIterIP6Tuple2IiPN7gc_heap3StrEEE5ValueEv
Line
Count
Source
352
4
  T Value() {
353
4
    return L_->slab_->items_[i_];
354
4
  }
Unexecuted instantiation: _ZN8ListIterIPN10hnode_asdl7hnode_tEE5ValueEv
_ZN8ListIterIPN10hnode_asdl5fieldEE5ValueEv
Line
Count
Source
352
141
  T Value() {
353
141
    return L_->slab_->items_[i_];
354
141
  }
_ZN8ListIterIbE5ValueEv
Line
Count
Source
352
2
  T Value() {
353
2
    return L_->slab_->items_[i_];
354
2
  }
355
356
 private:
357
  List<T>* L_;
358
  int i_;
359
};
360
361
// TODO: Does using pointers rather than indices make this more efficient?
362
template <class T>
363
class ReverseListIter {
364
 public:
365
2
  explicit ReverseListIter(List<T>* L) : L_(L), i_(L_->len_ - 1) {
366
2
  }
_ZN15ReverseListIterIPN7gc_heap3StrEEC2EPNS0_4ListIS2_EE
Line
Count
Source
365
1
  explicit ReverseListIter(List<T>* L) : L_(L), i_(L_->len_ - 1) {
366
1
  }
_ZN15ReverseListIterIP6Tuple2IiPN7gc_heap3StrEEEC2EPNS1_4ListIS5_EE
Line
Count
Source
365
1
  explicit ReverseListIter(List<T>* L) : L_(L), i_(L_->len_ - 1) {
366
1
  }
367
4
  void Next() {
368
4
    i_--;
369
4
  }
_ZN15ReverseListIterIPN7gc_heap3StrEE4NextEv
Line
Count
Source
367
2
  void Next() {
368
2
    i_--;
369
2
  }
_ZN15ReverseListIterIP6Tuple2IiPN7gc_heap3StrEEE4NextEv
Line
Count
Source
367
2
  void Next() {
368
2
    i_--;
369
2
  }
370
6
  bool Done() {
371
6
    return i_ < 0;
372
6
  }
_ZN15ReverseListIterIPN7gc_heap3StrEE4DoneEv
Line
Count
Source
370
3
  bool Done() {
371
3
    return i_ < 0;
372
3
  }
_ZN15ReverseListIterIP6Tuple2IiPN7gc_heap3StrEEE4DoneEv
Line
Count
Source
370
3
  bool Done() {
371
3
    return i_ < 0;
372
3
  }
373
4
  T Value() {
374
4
    return L_->slab_->items_[i_];
375
4
  }
_ZN15ReverseListIterIPN7gc_heap3StrEE5ValueEv
Line
Count
Source
373
2
  T Value() {
374
2
    return L_->slab_->items_[i_];
375
2
  }
_ZN15ReverseListIterIP6Tuple2IiPN7gc_heap3StrEEE5ValueEv
Line
Count
Source
373
2
  T Value() {
374
2
    return L_->slab_->items_[i_];
375
2
  }
376
377
 private:
378
  List<T>* L_;
379
  int i_;
380
};
381
382
// TODO:
383
// - Look at entry_ to see if an item is deleted (or is a tombstone once we
384
// have hash chaining)
385
386
template <class K, class V>
387
class DictIter {
388
 public:
389
3
  explicit DictIter(Dict<K, V>* D) : D_(D), pos_(ValidPosAfter(0)) {
390
3
  }
391
9
  void Next() {
392
9
    pos_ = ValidPosAfter(pos_ + 1);
393
9
  }
394
12
  bool Done() {
395
12
    return pos_ == -1;
396
12
  }
397
9
  K Key() {
398
9
    return D_->keys_->items_[pos_];
399
9
  }
400
6
  V Value() {
401
6
    return D_->values_->items_[pos_];
402
6
  }
403
404
 private:
405
12
  int ValidPosAfter(int pos) {
406
    // Returns the position of a valid entry at or after index i_.  Or -1 if
407
    // there isn't one.  Advances i_ too.
408
12
    while (true) {
409
12
      if (pos >= D_->capacity_) {
410
0
        return -1;
411
0
      }
412
12
      int index = D_->entry_->items_[pos];
413
12
      if (index == gc_heap::kDeletedEntry) {
414
0
        ++pos;
415
0
        continue;  // increment again
416
0
      }
417
12
      if (index == gc_heap::kEmptyEntry) {
418
3
        return -1;
419
3
      }
420
9
      break;
421
12
    }
422
9
    return pos;
423
12
  }
424
425
  Dict<K, V>* D_;
426
  int pos_;
427
};
428
429
#endif  // MY_RUNTIME_H