Home | History | Annotate | Download | only in slang
      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:
    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
    777