1 /*
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 
30 namespace slang {
31 
32 class RSContext;
33 class RSExportForEach;
34 class RSExportFunc;
35 class RSExportRecordType;
36 class RSExportReduce;
37 class RSExportType;
38 class 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 //
125 class 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:
ReflectionState()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;
NoVal32()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 
isCollecting()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   //
beginOutputClass()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   //
setOutputClassDivergent()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   //
endOutputClass()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:
Tentative(ReflectionState * state)215     Tentative(ReflectionState *state) : mState(state) { }
~Tentative()216     ~Tentative() { if (mState) mState->mState = ReflectionState::S_Bad; }
217 
ok()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 
declareForEachDummyRoot(const RSExportForEach *)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 
beginInvokables(size_t Count)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 
beginReduces(size_t Count)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 
beginVariables(size_t Count)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 
isActive()457   bool isActive() const { return isCollecting() || isUsing(); }
isClosed()458   bool isClosed() const { return mState==S_ClosedJava32 || mState==S_ClosedJava64; }
isUsing()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:
ArrayWithCursor()508     ArrayWithCursor() : mState(S_Initial), mMembers(nullptr), mCapacity(0), mSize(0), mCursor(~size_t(0)) { }
509 
~ArrayWithCursor()510     ~ArrayWithCursor() { delete [] mMembers; }
511 
512     ArrayWithCursor(const ArrayWithCursor &) = delete;
513     void operator=(const ArrayWithCursor &) = delete;
514 
BeginCollecting(size_t Size)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.
CollectNext()523     Member &CollectNext() {
524       slangAssert((mState == S_Collecting) && (mCursor + 1 == mSize) && (mSize < mCapacity));
525       ++mSize;
526       return mMembers[++mCursor];
527     }
528 
BeginUsing()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.
UseNext()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?
isFinished()542     bool isFinished() const {
543       return mCursor + 1 == mSize;
544     }
545 
Size()546     size_t Size() const { return mSize; }
547 
548     // Return a reference to the member under the cursor.
Current()549     Member &Current() {
550       slangAssert(mCursor < mSize);
551       return mMembers[mCursor];
552     }
Current()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).
CurrentIdx()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 {
FileFile589     File() : mForEaches(nullptr) { }
~FileFile590     ~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 {
ForEachFile::ForEach599       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 {
InvokableFile::Invokable626       Invokable() : mParams(nullptr) { }
~InvokableFile::Invokable627       ~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 {
RecordFile::Record651       Record() : mFields(nullptr) { }
~RecordFile::Record652       ~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 {
ReduceFile::Reduce680       Reduce() : mAccumIns(nullptr) { }
~ReduceFile::Reduce681       ~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 {
VariableFile::Variable698       Variable() : mInitializers(nullptr) { }
~VariableFile::Variable699       ~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>
begin(ArrayWithCursor<Member> File::* Array,size_t Count)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:
Record32()740     Record32() : mRecord(nullptr) { }
741 
getRecordPostPadding()742     Val32 getRecordPostPadding() const {
743       if (!mRecord)
744         return NoVal32();
745       return Val32(true, mRecord->mPostPadding);
746     }
747 
getRecordAllocSize()748     Val32 getRecordAllocSize() const {
749       if (!mRecord)
750         return NoVal32();
751       return Val32(true, mRecord->mAllocSize);
752     }
753 
getFieldPreAndPostPadding(unsigned idx)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 
getFieldOffsetAndStoreSize(unsigned idx)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:
Record32(const File::Record * Record)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
777