blob: d937be8476a9e35cd631f7f8c541f13a542525ee [file] [log] [blame]
David Grossd80e58b2017-07-24 11:41:12 -07001/*
2 * Copyright 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_ // NOLINT
18#define _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_
19
20#include <string>
21#include <utility>
22
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/StringMap.h"
25#include "llvm/ADT/StringSet.h"
26#include "clang/AST/APValue.h"
27
28#include "slang_assert.h"
29
30namespace slang {
31
32class RSContext;
33class RSExportForEach;
34class RSExportFunc;
35class RSExportRecordType;
36class RSExportReduce;
37class RSExportType;
38class RSExportVar;
39
40// ---------------------
41// class ReflectionState
42// ---------------------
43//
44// This class is used to collect data from 32-bit compilation for use
45// during the reflected code generation that occurs during 64-bit
46// compilation. The data is used for two purposes:
47//
48// 1) Accommodating rs object handle size differences when laying out
49// data (in particular, variables and records).
50// 2) Emitting errors when differences between 32-bit and 64-bit
51// compilation cannot be tolerated in the reflected code (for
52// example, size_t has different sizes, and so cannot be part
53// of any exportable).
54//
55// The model for using this class is as follows:
56// a) Instantiate a class instance. The instance is in the S_Initial
57// state.
58// b) Call openJava32() to move the instance to the S_OpenJava32
59// ("Collecting") state.
60// c) Run the reflection pass on all files in 32-bit mode. Do not
61// actually generate reflected code; but call various methods on
62// the instance (begin*(), declare*(), end*(), etc.) to collect
63// information.
64// d) Call closeJava32() to move the instance to the S_ClosedJava32
65// state.
66// e) Call openJava64() to move the instance to the S_OpenJava64
67// ("Using") state.
68// f) Run the reflection pass on all files in 64-bit mode. Call the
69// same methods as in step (c), as well as some further methods to
70// query the information collected in step (c) in order to handle
71// layout differences. All error reporting for 32-bit versus
72// 64-bit differences is handled in the methods themselves.
73// g) Call closeJava64 to move the instance to the S_ClosedJava64
74// state.
75// h) Destroy the instance.
76//
77// There are two exceptions to this model:
78//
79// 1) If not doing both 32-bit and 64-bit compilation, then skip steps
80// (b), (d), (e), and (g). (This is what happens if reflecting C++
81// instead of Java, or reflecting Java but using the -m32 or -m64
82// option.) In this case, the methods called in steps (c) and (f)
83// are no-ops: They do not collect information, they do not report
84// errors, and they return "no information available" when step (f)
85// asks for 32-bit layout related information.
86// 2) The class instance can be moved to the S_Bad state by class
87// ReflectionState::Tentative (see that class for more information)
88// when reflection itself aborts due to some error. The only legal
89// thing to do with an instance in this state is invoke its
90// destructor.
91//
92// All exported entities except for Records have slot numbers assigned
93// in reflection order. These slot numbers must match up between
94// 32-bit and 64-bit compilation. Therefore, we (generally) require
95// that entities be presented to ReflectionState (via begin*() or
96// declare*()) in the same order during the Collecting and Using
97// phases. This presentation order is generally the same as lexical
98// order in the user code, which makes it simple to emit meaningful
99// diagnostics when the order is inconsistent (for example, 32-bit and
100// 64-bit compilation disagree on the name of the kernel in a
101// particular slot). ReflectionState generally builds up an array of
102// each sort of entity, in the presentation order. There are two
103// exceptions:
104//
105// a) Records, as mentioned above. Exported Records have no slot
106// number, and therefore reflection order doesn't matter. In
107// practice, Records aren't necessarily reflected in consistent
108// order, because they are determined to be exported as a
109// consequence of determining that other entities are to be
110// exported; and variations between 32-bit and 64-bit compilation
111// can therefore result in inconsistent Record reflection order.
112// Therefore, ReflectionState builds up a map of Records.
113// b) ForEach kernels. ForEach kernels are not necessarily reflected
114// in lexical order (there is some sorting to segregate root
115// kernel, old-style kernels, and new-style kernels). In order to
116// give meaningful diagnostics for slot order mismatches, it's
117// enough to solve the simpler problem of giving meaningful
118// diagnostics for lexical order mismatches (although this is
119// stricter than necessary because of the sorting that occurs
120// before slot assignment). Therefore, ReflectionState builds up
121// an array of ForEaches in lexical order rather than in
122// presentation (slot) order, and accesses the array randomly
123// rather than sequentially.
124//
125class ReflectionState {
126 private:
127 // Set this to true to turn everything into a no-op, just as if none
128 // of the open*() or close*() methods were ever called.
129 static const bool kDisabled = false;
130
131 public:
132 ReflectionState() :
133 mRSC(nullptr),
134 mState(S_Initial),
135 mForEachOpen(-1),
136 mOutputClassOpen(false),
137 mRecordsState(RS_Initial),
138 mStringSet(nullptr) { }
139 ~ReflectionState();
140
141 ReflectionState(const ReflectionState &) = delete;
142 void operator=(const ReflectionState &) = delete;
143
144 // For use in the debugger.
145 void dump();
146
147 // A possibly-present value describing a property for a 32-bit target.
148 // When .first is false, the value is absent, and .second is unspecified.
149 typedef std::pair<bool, size_t> Val32;
150 static Val32 NoVal32() { return Val32(false, ~size_t(0)); }
151
152 void openJava32(size_t NumFiles);
153 void closeJava32();
154 void openJava64();
155 void closeJava64();
156
157 bool isCollecting() const { return mState==S_OpenJava32; }
158
159 // ----------------------------------------------------------------------
160
161 // Use these methods during the "Collecting" phase to track
162 // information about a class being generated -- a script class or a
163 // type class. We call such a class "Divergent" if it needs to have
164 // at least one runtime check to distinguish between 32-bit and
165 // 64-bit targets.
166 //
167 // Indicate that we are beginning to generate the class.
168 //
169 void beginOutputClass() {
170 slangAssert(!mOutputClassOpen && !isClosed());
171 mOutputClassOpen = true;
172 mOutputClassDivergent = false;
173 }
174 //
175 // Record the fact that we've learned the class is divergent.
176 //
177 void setOutputClassDivergent() {
178 slangAssert(mOutputClassOpen);
179 mOutputClassDivergent = true;
180 }
181 //
182 // Indicate that we've finished generating the class. Returns
183 // true IFF we've learned the class is divergent.
184 //
185 bool endOutputClass() {
186 slangAssert(mOutputClassOpen);
187 mOutputClassOpen = false;
188 return mOutputClassDivergent;
189 }
190
191 // ----------------------------------------------------------------------
192
193 // --------------------------------
194 // class ReflectionState::Tentative
195 // --------------------------------
196 //
197 // This class aids in error handling. The model is as follows:
198 // a) Instantiate the class with a pointer to a ReflectionState
199 // instance.
200 // b) Before destroying the class instance, if there have been no
201 // errors, call the ok() method on the instance.
202 // c) When the instance is destroyed, if ok() has not been called on
203 // it, this class will put the ReflectionState into the S_Bad
204 // state.
205 //
206 // The idea is to "poison" the ReflectionState if we quit reflection
207 // early because of some error -- we don't want to get in a
208 // situation where we only have partial information from the
209 // Collecting phase (because of quitting early) but try to use it
210 // during the Using phase.
211 //
212 friend class Tentative;
213 class Tentative {
214 public:
215 Tentative(ReflectionState *state) : mState(state) { }
216 ~Tentative() { if (mState) mState->mState = ReflectionState::S_Bad; }
217
218 void ok() { mState = nullptr; }
219
220 Tentative(const Tentative &) = delete;
221 void operator=(const Tentative &) = delete;
222
223 private:
224 ReflectionState *mState;
225 };
226
227 // ----------------------------------------------------------------------
228
229 // Model for ForEach kernels (per File):
230 //
231 // a) beginForEaches(number_of_non_dummy_root_kernels_in_file)
232 // b) mixture of declareForEachDummyRoot() calls and
233 // beginForEach()..endForEach() calls
234 // c) endForEaches()
235 //
236 // For a given ForEach kernel:
237 //
238 // b1) beginForEach()
239 // b2) call any number of addForEachIn() (one per input)
240 // b3) call any number of addForEachParam() (one per param)
241 // b4) call addForEachSignatureMetadata() (if it's reflected)
242 // b5) call endForEach()
243 //
244 // b2, b3, b4 can occur in any order
245
246 void beginForEaches(size_t Count);
247
248 void declareForEachDummyRoot(const RSExportForEach *) { /* we don't care */ };
249
250 void beginForEach(const RSExportForEach *EF);
251
252 void addForEachIn(const RSExportForEach *EF, const RSExportType *Type);
253
254 void addForEachParam(const RSExportForEach *EF, const RSExportType *Type);
255
256 void addForEachSignatureMetadata(const RSExportForEach *EF, unsigned Metadata);
257
258 void endForEach();
259
260 void endForEaches();
261
262 // ----------------------------------------------------------------------
263
264 // Model for Invokable functions (per File):
265 //
266 // a) beginInvokables(number_of_invokables_in_file)
267 // b) declareInvokable() for each Invokable (order must be
268 // consistent between 32-bit and 64-bit compile)
269 // c) endInvokables()
270
271 void beginInvokables(size_t Count) {
272 mInvokablesOrderFatal = false;
273 begin(&File::mInvokables, Count);
274 }
275
276 void declareInvokable(const RSExportFunc *EF);
277
278 void endInvokables();
279
280 // ----------------------------------------------------------------------
281
282 // Model for reduction kernels (per File):
283 //
284 // a) beginReduces(number_of_reduction_kernels_in_file)
285 // b) declareReduce() for each reduction kernel (order must be
286 // consistent between 32-bit and 64-bit compile)
287 // c) endReduces()
288
289 void beginReduces(size_t Count) {
290 mReducesOrderFatal = false;
291 begin(&File::mReduces, Count);
292 }
293
294 void declareReduce(const RSExportReduce *ER, bool IsExportable);
295
296 void endReduces();
297
298 // ----------------------------------------------------------------------
299
300 // Model for records (per File):
301 //
302 // a) beginRecords()
303 // b) declareRecord() for each Record (order doesn't matter)
304 // c) endRecords()
305 //
306 // And at any time during the Using phase, can call getRecord32() to
307 // get information from the 32-bit compile (Collecting phase).
308
309 void beginRecords();
310
311 // An "Ordinary" record is anything other than an
312 // internally-synthesized helper record. We do not emit diagnostics
313 // for mismatched helper records -- we assume that the constructs
314 // from which those helper records were derived are also mismatched,
315 // and that we'll get diagnostics for those constructs.
316 void declareRecord(const RSExportRecordType *ERT, bool Ordinary = true);
317
318 void endRecords();
319
320 class Record32;
321
322 // During the Using phase, obtain information about a Record from
323 // the Collecting phase. ERT should be from the Using phase, not
324 // the Collecting phase. The value returned from this function is
325 // valid for the lifetime of the ReflectionState instance.
326 Record32 getRecord32(const RSExportRecordType *ERT);
327
328 // ----------------------------------------------------------------------
329
330 // Model for Variables (per file):
331 //
332 // a) beginVariables(number_of_exported_variables_in_file)
333 // b) declareVariable() for each Variable (order must be consistent
334 // between 32-bit and 64-bit); in the Using phase, returns some
335 // information about the Variable from 32-bit compilation
336 // c) endVariables()
337
338 void beginVariables(size_t Count) {
339 mVariablesOrderFatal = false;
340 begin(&File::mVariables, Count);
341 }
342
343 // If isUsing(), returns variable's 32-bit AllocSize; otherwise, returns NoVal32().
344 Val32 declareVariable(const RSExportVar *EV);
345
346 void endVariables();
347
348 // ----------------------------------------------------------------------
349
350 // ReflectionState has a notion of "current file". After an
351 // openJava*() or closeJava*() call, there is no current file.
352 // Calling the nextFile() method when in the Collecting or Using
353 // state "advances" to the next file in the list of files being
354 // compiled, whose properties are specified by the arguments to
355 // nextFile(). All of the various begin*(), declare*(), end*()
356 // etc. calls implicitly refer to entities in the current file.
357 //
358 // RSC must remain valid until the next call to nextFile() or the
359 // next S_* state change.
360 void nextFile(const RSContext *RSC, const std::string &PackageName, const std::string &RSSourceFileName);
361
362 // ----------------------------------------------------------------------
363
364 private:
365 enum State {
366 S_Initial, // No captured information
367 S_OpenJava32, // Capturing information for 32-bit Java
368 S_ClosedJava32, // Captured information for 32-bit Java
369 S_OpenJava64, // Capturing information for 64-bit Java
370 S_ClosedJava64, // Captured information for 64-bit Java
371 S_Bad, // Abnormal termination
372 };
373
374 // context associated with compilation of the current file
375 const RSContext *mRSC;
376
377 State mState;
378
379 /*== ForEach ==================================================================*/
380
381 // The data in this section is transient during ForEach processing
382 // for each File.
383
384 int mForEachOpen; // if nonnegative, then ordinal of beginForEach() without matching endForEach()
385 bool mForEachFatal; // fatal mismatch in comparing ForEach; do no further comparisons for it
386
387 // Tracks mismatches discovered during the Use phase.
388 // There are two possibilities:
389 // - if (ordinal + 1) is greater than the number of ForEaches from the Collecting phase,
390 // then this is an "extra" ForEach discovered during the Use phase
391 // - otherwise the Collecting phase and the Use phase disagree on the name of the
392 // ForEach at this ordinal position (the Collecting phase's kernel name is
393 // available in mFiles.Current().mForEaches[ordinal].mName)
394 llvm::SmallVector<const RSExportForEach *, 0> mForEachesBad;
395
396 // During the Use phase, keep track of how many ForEach ordinals we
397 // have seen that correspond to ordinals seen during the Collect
398 // phase. This helps determine whether we have to issue errors at
399 // endForEaches().
400 size_t mNumForEachesMatchedByOrdinal;
401
402 /*== Invokable ================================================================*/
403
404 // 32-bit and 64-bit compiles need to see invokables in the same
405 // order, because of slot number assignment. Once we see the first
406 // name mismatch in the sequence of invokables for a given File, it
407 // doesn't make sense to issue further diagnostics regarding
408 // invokables for that File.
409 bool mInvokablesOrderFatal;
410
411 /*== OutputClass ==============================================================*/
412
413 // This data tracks information about a class being generated -- a
414 // script class or a type class. We call such a class "Divergent"
415 // if it needs to have at least one runtime check to distinguish
416 // between 32-bit and 64-bit targets.
417
418 bool mOutputClassOpen; // beginOutputClass() without matching endOutputClass()
419 bool mOutputClassDivergent; // has class been marked divergent?
420
421 /*== Record ===================================================================*/
422
423 // This field enforces necessary discipline on the use of
424 // beginRecords()/declareRecord()/endRecord().
425 enum {
426 RS_Initial, // no beginRecords() yet for current File
427 RS_Open, // beginRecords() but no endRecords() for current File
428 RS_Closed // endRecords() for current File
429 } mRecordsState;
430
431 // During the Use phase, keep track of how many records we have seen
432 // that have same-named counterparts seen during the Collect phase.
433 // This helps determine whether we have to issue errors at
434 // endRecords().
435 size_t mNumRecordsMatchedByName;
436
437 /*== Reduce ===================================================================*/
438
439 // 32-bit and 64-bit compiles need to see reduction kernels in the
440 // same order, because of slot number assignment. Once we see the
441 // first name mismatch in the sequence of reduction kernels for a
442 // given File, it doesn't make sense to issue further diagnostics
443 // regarding reduction kernels for that File.
444 bool mReducesOrderFatal;
445
446 /*== Variable =================================================================*/
447
448 // 32-bit and 64-bit compiles need to see variables in the same
449 // order, because of slot number assignment. Once we see the first
450 // name mismatch in the sequence of variables for a given File, it
451 // doesn't make sense to issue further diagnostics regarding
452 // variables for that File.
453 bool mVariablesOrderFatal;
454
455 /*=============================================================================*/
456
457 bool isActive() const { return isCollecting() || isUsing(); }
458 bool isClosed() const { return mState==S_ClosedJava32 || mState==S_ClosedJava64; }
459 bool isUsing() const { return mState==S_OpenJava64; }
460
461 // For anything with a type (such as a Variable or a Record field),
462 // the type is represented via its name. To save space, we don't
463 // create multiple instances of the same name -- we have a canonical
464 // instance in mStringSet, and use a StringRef to refer to it. The
465 // method canon() returns a StringRef to the canonical
466 // instance, creating the instance if necessary.
467 llvm::StringRef canon(const std::string &String);
468 llvm::StringSet<> *mStringSet;
469
470 // Synthesize a name for the specified type. There should be a
471 // one-to-one correspondence between the name and a C type (after
472 // typedefs and integer expressions have been "flattened", and
473 // considering a struct type to be identified solely by its name).
474 static std::string getUniqueTypeName(const RSExportType *T);
475
476 // ------------------------------
477 // template class ArrayWithCursor
478 // ------------------------------
479 //
480 // This class represents a fixed-length dynamically-allocated array
481 // (length is specified by a method call after instantiation) along
482 // with a cursor that traverses the array. The behavior of the
483 // class is very specific to the needs of ReflectionState.
484 //
485 // The model for using this class is as follows:
486 // a) Instantiate a class instance. The instance is in the
487 // S_Initial state.
488 // b) Call BeginCollecting() with an array capacity. This allocates
489 // the array members and moves the instance to the S_Collecting
490 // state. The array size (contrast with capacity) is zero, and
491 // the cursor has not been placed.
492 // c) Call CollectNext() a number of times equal to the capacity.
493 // Each time CollectNext() is called, it extends the array size
494 // by 1, and advances the cursor to the "new" member. The idea
495 // is to set the value of the "new" member at this time.
496 // d) Call BeginUsing(). This moves the instance to the S_Using
497 // state and "unplaces" the cursor.
498 // e) Call UseNext() a number of times equal to the capacity. Each
499 // time UseNext() is called, it advances the cursor to the next
500 // member (first member, the first time it is called).
501 // The cursor is stepping through the members that were "created"
502 // by CollectNext() during the S_Collecting state; the idea is to
503 // look at their values.
504 // f) Destroy the instance.
505 //
506 template <typename Member> class ArrayWithCursor {
507 public:
508 ArrayWithCursor() : mState(S_Initial), mMembers(nullptr), mCapacity(0), mSize(0), mCursor(~size_t(0)) { }
509
510 ~ArrayWithCursor() { delete [] mMembers; }
511
512 ArrayWithCursor(const ArrayWithCursor &) = delete;
513 void operator=(const ArrayWithCursor &) = delete;
514
515 void BeginCollecting(size_t Size) {
516 slangAssert(mState == S_Initial);
517 mState = S_Collecting;
518 mMembers = new Member[Size];
519 mCapacity = Size;
520 }
521 // Increments the array size, advances the cursor to the new
522 // member, and returns a reference to that member.
523 Member &CollectNext() {
524 slangAssert((mState == S_Collecting) && (mCursor + 1 == mSize) && (mSize < mCapacity));
525 ++mSize;
526 return mMembers[++mCursor];
527 }
528
529 void BeginUsing() {
530 slangAssert((mState == S_Collecting) && (mCursor + 1 == mSize) && (mSize == mCapacity));
531 mState = S_Using;
532 mCursor = ~size_t(0);
533 }
534 // Advances the cursor to the next member, and returns a reference
535 // to that member.
536 Member &UseNext() {
537 slangAssert((mState == S_Using) && (mCursor + 1 < mSize));
538 return mMembers[++mCursor];
539 }
540
541 // Is the cursor on the last array member?
542 bool isFinished() const {
543 return mCursor + 1 == mSize;
544 }
545
546 size_t Size() const { return mSize; }
547
548 // Return a reference to the member under the cursor.
549 Member &Current() {
550 slangAssert(mCursor < mSize);
551 return mMembers[mCursor];
552 }
553 const Member &Current() const {
554 slangAssert(mCursor < mSize);
555 return mMembers[mCursor];
556 }
557 // Return the cursor position (zero-based). Cursor must have been
558 // placed (i.e., if we're Collecting, we must have called
559 // CollectNext() at least once; and if we're Using, we must have
560 // called UseNext() at least once).
561 size_t CurrentIdx() const {
562 slangAssert(mCursor < mSize);
563 return mCursor;
564 }
565
566 // Return a reference to the specified member. Must be within the
567 // array size (not merely within its capacity).
568 Member &operator[](size_t idx) {
569 slangAssert(idx < mSize);
570 return mMembers[idx];
571 }
572 const Member &operator[](size_t idx) const {
573 slangAssert(idx < mSize);
574 return mMembers[idx];
575 }
576
577 private:
578 enum State { S_Initial, S_Collecting, S_Using };
579 State mState;
580
581 Member *mMembers;
582 size_t mCapacity;
583 size_t mSize;
584 size_t mCursor;
585 };
586
587
588 struct File {
589 File() : mForEaches(nullptr) { }
590 ~File() { delete [] mForEaches; }
591
592 File(const File &) = delete;
593 void operator=(const File &) = delete;
594
595 std::string mPackageName;
596 std::string mRSSourceFileName;
597
598 struct ForEach {
599 ForEach() : mState(S_Initial) { }
600 ForEach(const ForEach &) = delete;
601 void operator=(const ForEach &) = delete;
602
603 enum {
604 S_Initial, // ForEach has been instantiated
605 S_Collected, // beginForEach() has been called while Collecting
606 S_UseMatched // beginForEach() has been called while Using,
607 // and found this ForEach
608 } mState;
609
610 std::string mName;
611
612 // Types. mIns[] and mOut can be null in case we have an
613 // old-style kernel with a void* input or output.
614 ArrayWithCursor<llvm::StringRef> mIns;
615 ArrayWithCursor<llvm::StringRef> mParams;
616 llvm::StringRef mOut;
617 bool mHasOut; // to distinguish between no output and void* output.
618
619 unsigned mSignatureMetadata;
620 bool mIsKernel; // new-style (by-value) rather than old-style
621 };
622 ForEach *mForEaches; // indexed by ordinal (lexical order)
623 size_t mForEachCount;
624
625 struct Invokable {
626 Invokable() : mParams(nullptr) { }
627 ~Invokable() { delete [] mParams; }
628
629 Invokable(const Invokable &) = delete;
630 void operator=(const Invokable &) = delete;
631
632 std::string mName;
633 llvm::StringRef *mParams; // Types
634 size_t mParamCount;
635 };
636 ArrayWithCursor<Invokable> mInvokables;
637
638 // There are two things we need to do with a Record:
639 // - Support structure sizes and layouts that differ between
640 // 32-bit and 64-bit compilation.
641 // - Do consistency checking between 32-bit and 64-bit compilation.
642 //
643 // TODO: Move this out of File to avoid duplication? That is,
644 // instead of tracking Records on a per-File basis, instead
645 // track them globally?
646 //
647 // (Because of ODR, we shouldn't have inconsistencies
648 // between Files.)
649 //
650 struct Record {
651 Record() : mFields(nullptr) { }
652 ~Record() { delete [] mFields; }
653
654 Record(const Record &) = delete;
655 void operator=(const Record &) = delete;
656
657 struct Field {
658 std::string mName;
659 llvm::StringRef mType;
660 size_t mPrePadding; // this.OffsetInParent - (prev.OffsetInParent + prev.AllocSize)
661 size_t mPostPadding; // this.AllocSize - this.StoreSize
662 size_t mOffset; // this.OffsetInParent
663 size_t mStoreSize; // this.StoreSize
664 };
665 Field *mFields;
666 size_t mFieldCount;
667 size_t mPostPadding; // padding after the end of the padded
668 // last field
669 size_t mAllocSize;
670 bool mOrdinary; // anything other than an
671 // internally-synthesized helper
672 // record. We do not emit diagnostics
673 // for inconsistent helper records.
674 bool mMatchedByName; // has declareRecord() been called on
675 // this record during the Using phase?
676 };
677 llvm::StringMap<Record> mRecords;
678
679 struct Reduce {
680 Reduce() : mAccumIns(nullptr) { }
681 ~Reduce() { delete [] mAccumIns; }
682
683 Reduce(const Reduce &) = delete;
684 void operator=(const Reduce &) = delete;
685
686 std::string mName;
687
688 // only apply to exportable
689 llvm::StringRef *mAccumIns; // Types
690 size_t mAccumInCount;
691 llvm::StringRef mResult; // Type
692
693 bool mIsExportable;
694 };
695 ArrayWithCursor<Reduce> mReduces;
696
697 struct Variable {
698 Variable() : mInitializers(nullptr) { }
699 ~Variable() { delete [] mInitializers; }
700
701 Variable(const Variable &) = delete;
702 void operator=(const Variable &) = delete;
703
704 std::string mName;
705 llvm::StringRef mType;
706 clang::APValue *mInitializers;
707 size_t mInitializerCount;
708 size_t mAllocSize;
709 bool mIsConst;
710 };
711 ArrayWithCursor<Variable> mVariables;
712
713 };
714 ArrayWithCursor<File> mFiles;
715
716 // Utility template -- common pattern used by many begin*() methods.
717 template <typename Member>
718 void begin(ArrayWithCursor<Member> File::*Array, size_t Count) {
719 slangAssert(!isClosed());
720 if (!isActive())
721 return;
722
723 auto &file = mFiles.Current();
724 if (isCollecting())
725 (file.*Array).BeginCollecting(Count);
726 if (isUsing())
727 (file.*Array).BeginUsing();
728 }
729
730 public:
731
732 // This class represents 32-bit layout information built up during
733 // the Collecting phase, for use during the Using phase. It
734 // provides an interface between class ReflectionState and client
735 // code that actually performs reflection.
736 class Record32 {
737 friend class ReflectionState;
738
739 public:
740 Record32() : mRecord(nullptr) { }
741
742 Val32 getRecordPostPadding() const {
743 if (!mRecord)
744 return NoVal32();
745 return Val32(true, mRecord->mPostPadding);
746 }
747
748 Val32 getRecordAllocSize() const {
749 if (!mRecord)
750 return NoVal32();
751 return Val32(true, mRecord->mAllocSize);
752 }
753
754 std::pair<Val32, Val32> getFieldPreAndPostPadding(unsigned idx) const {
755 if (!mRecord || idx >= mRecord->mFieldCount)
756 return std::make_pair(NoVal32(), NoVal32());
757 const File::Record::Field &field = mRecord->mFields[idx];
758 return std::make_pair(Val32(true, field.mPrePadding), Val32(true, field.mPostPadding));
759 }
760
761 std::pair<Val32, Val32> getFieldOffsetAndStoreSize(unsigned idx) const {
762 if (!mRecord || idx >= mRecord->mFieldCount)
763 return std::make_pair(NoVal32(), NoVal32());
764 const File::Record::Field &field = mRecord->mFields[idx];
765 return std::make_pair(Val32(true, field.mOffset), Val32(true, field.mStoreSize));
766 }
767
768 private:
769 Record32(const File::Record *Record) : mRecord(Record) { }
770 const File::Record *mRecord;
771 };
772};
773
774}
775
776#endif // _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_ NOLINT