777 lines
28 KiB
C++
777 lines
28 KiB
C++
/*
|
|
* Copyright 2017, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_ // NOLINT
|
|
#define _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_
|
|
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringSet.h"
|
|
#include "clang/AST/APValue.h"
|
|
|
|
#include "slang_assert.h"
|
|
|
|
namespace slang {
|
|
|
|
class RSContext;
|
|
class RSExportForEach;
|
|
class RSExportFunc;
|
|
class RSExportRecordType;
|
|
class RSExportReduce;
|
|
class RSExportType;
|
|
class RSExportVar;
|
|
|
|
// ---------------------
|
|
// class ReflectionState
|
|
// ---------------------
|
|
//
|
|
// This class is used to collect data from 32-bit compilation for use
|
|
// during the reflected code generation that occurs during 64-bit
|
|
// compilation. The data is used for two purposes:
|
|
//
|
|
// 1) Accommodating rs object handle size differences when laying out
|
|
// data (in particular, variables and records).
|
|
// 2) Emitting errors when differences between 32-bit and 64-bit
|
|
// compilation cannot be tolerated in the reflected code (for
|
|
// example, size_t has different sizes, and so cannot be part
|
|
// of any exportable).
|
|
//
|
|
// The model for using this class is as follows:
|
|
// a) Instantiate a class instance. The instance is in the S_Initial
|
|
// state.
|
|
// b) Call openJava32() to move the instance to the S_OpenJava32
|
|
// ("Collecting") state.
|
|
// c) Run the reflection pass on all files in 32-bit mode. Do not
|
|
// actually generate reflected code; but call various methods on
|
|
// the instance (begin*(), declare*(), end*(), etc.) to collect
|
|
// information.
|
|
// d) Call closeJava32() to move the instance to the S_ClosedJava32
|
|
// state.
|
|
// e) Call openJava64() to move the instance to the S_OpenJava64
|
|
// ("Using") state.
|
|
// f) Run the reflection pass on all files in 64-bit mode. Call the
|
|
// same methods as in step (c), as well as some further methods to
|
|
// query the information collected in step (c) in order to handle
|
|
// layout differences. All error reporting for 32-bit versus
|
|
// 64-bit differences is handled in the methods themselves.
|
|
// g) Call closeJava64 to move the instance to the S_ClosedJava64
|
|
// state.
|
|
// h) Destroy the instance.
|
|
//
|
|
// There are two exceptions to this model:
|
|
//
|
|
// 1) If not doing both 32-bit and 64-bit compilation, then skip steps
|
|
// (b), (d), (e), and (g). (This is what happens if reflecting C++
|
|
// instead of Java, or reflecting Java but using the -m32 or -m64
|
|
// option.) In this case, the methods called in steps (c) and (f)
|
|
// are no-ops: They do not collect information, they do not report
|
|
// errors, and they return "no information available" when step (f)
|
|
// asks for 32-bit layout related information.
|
|
// 2) The class instance can be moved to the S_Bad state by class
|
|
// ReflectionState::Tentative (see that class for more information)
|
|
// when reflection itself aborts due to some error. The only legal
|
|
// thing to do with an instance in this state is invoke its
|
|
// destructor.
|
|
//
|
|
// All exported entities except for Records have slot numbers assigned
|
|
// in reflection order. These slot numbers must match up between
|
|
// 32-bit and 64-bit compilation. Therefore, we (generally) require
|
|
// that entities be presented to ReflectionState (via begin*() or
|
|
// declare*()) in the same order during the Collecting and Using
|
|
// phases. This presentation order is generally the same as lexical
|
|
// order in the user code, which makes it simple to emit meaningful
|
|
// diagnostics when the order is inconsistent (for example, 32-bit and
|
|
// 64-bit compilation disagree on the name of the kernel in a
|
|
// particular slot). ReflectionState generally builds up an array of
|
|
// each sort of entity, in the presentation order. There are two
|
|
// exceptions:
|
|
//
|
|
// a) Records, as mentioned above. Exported Records have no slot
|
|
// number, and therefore reflection order doesn't matter. In
|
|
// practice, Records aren't necessarily reflected in consistent
|
|
// order, because they are determined to be exported as a
|
|
// consequence of determining that other entities are to be
|
|
// exported; and variations between 32-bit and 64-bit compilation
|
|
// can therefore result in inconsistent Record reflection order.
|
|
// Therefore, ReflectionState builds up a map of Records.
|
|
// b) ForEach kernels. ForEach kernels are not necessarily reflected
|
|
// in lexical order (there is some sorting to segregate root
|
|
// kernel, old-style kernels, and new-style kernels). In order to
|
|
// give meaningful diagnostics for slot order mismatches, it's
|
|
// enough to solve the simpler problem of giving meaningful
|
|
// diagnostics for lexical order mismatches (although this is
|
|
// stricter than necessary because of the sorting that occurs
|
|
// before slot assignment). Therefore, ReflectionState builds up
|
|
// an array of ForEaches in lexical order rather than in
|
|
// presentation (slot) order, and accesses the array randomly
|
|
// rather than sequentially.
|
|
//
|
|
class ReflectionState {
|
|
private:
|
|
// Set this to true to turn everything into a no-op, just as if none
|
|
// of the open*() or close*() methods were ever called.
|
|
static const bool kDisabled = false;
|
|
|
|
public:
|
|
ReflectionState() :
|
|
mRSC(nullptr),
|
|
mState(S_Initial),
|
|
mForEachOpen(-1),
|
|
mOutputClassOpen(false),
|
|
mRecordsState(RS_Initial),
|
|
mStringSet(nullptr) { }
|
|
~ReflectionState();
|
|
|
|
ReflectionState(const ReflectionState &) = delete;
|
|
void operator=(const ReflectionState &) = delete;
|
|
|
|
// For use in the debugger.
|
|
void dump();
|
|
|
|
// A possibly-present value describing a property for a 32-bit target.
|
|
// When .first is false, the value is absent, and .second is unspecified.
|
|
typedef std::pair<bool, size_t> Val32;
|
|
static Val32 NoVal32() { return Val32(false, ~size_t(0)); }
|
|
|
|
void openJava32(size_t NumFiles);
|
|
void closeJava32();
|
|
void openJava64();
|
|
void closeJava64();
|
|
|
|
bool isCollecting() const { return mState==S_OpenJava32; }
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Use these methods during the "Collecting" phase to track
|
|
// information about a class being generated -- a script class or a
|
|
// type class. We call such a class "Divergent" if it needs to have
|
|
// at least one runtime check to distinguish between 32-bit and
|
|
// 64-bit targets.
|
|
//
|
|
// Indicate that we are beginning to generate the class.
|
|
//
|
|
void beginOutputClass() {
|
|
slangAssert(!mOutputClassOpen && !isClosed());
|
|
mOutputClassOpen = true;
|
|
mOutputClassDivergent = false;
|
|
}
|
|
//
|
|
// Record the fact that we've learned the class is divergent.
|
|
//
|
|
void setOutputClassDivergent() {
|
|
slangAssert(mOutputClassOpen);
|
|
mOutputClassDivergent = true;
|
|
}
|
|
//
|
|
// Indicate that we've finished generating the class. Returns
|
|
// true IFF we've learned the class is divergent.
|
|
//
|
|
bool endOutputClass() {
|
|
slangAssert(mOutputClassOpen);
|
|
mOutputClassOpen = false;
|
|
return mOutputClassDivergent;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// --------------------------------
|
|
// class ReflectionState::Tentative
|
|
// --------------------------------
|
|
//
|
|
// This class aids in error handling. The model is as follows:
|
|
// a) Instantiate the class with a pointer to a ReflectionState
|
|
// instance.
|
|
// b) Before destroying the class instance, if there have been no
|
|
// errors, call the ok() method on the instance.
|
|
// c) When the instance is destroyed, if ok() has not been called on
|
|
// it, this class will put the ReflectionState into the S_Bad
|
|
// state.
|
|
//
|
|
// The idea is to "poison" the ReflectionState if we quit reflection
|
|
// early because of some error -- we don't want to get in a
|
|
// situation where we only have partial information from the
|
|
// Collecting phase (because of quitting early) but try to use it
|
|
// during the Using phase.
|
|
//
|
|
friend class Tentative;
|
|
class Tentative {
|
|
public:
|
|
Tentative(ReflectionState *state) : mState(state) { }
|
|
~Tentative() { if (mState) mState->mState = ReflectionState::S_Bad; }
|
|
|
|
void ok() { mState = nullptr; }
|
|
|
|
Tentative(const Tentative &) = delete;
|
|
void operator=(const Tentative &) = delete;
|
|
|
|
private:
|
|
ReflectionState *mState;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Model for ForEach kernels (per File):
|
|
//
|
|
// a) beginForEaches(number_of_non_dummy_root_kernels_in_file)
|
|
// b) mixture of declareForEachDummyRoot() calls and
|
|
// beginForEach()..endForEach() calls
|
|
// c) endForEaches()
|
|
//
|
|
// For a given ForEach kernel:
|
|
//
|
|
// b1) beginForEach()
|
|
// b2) call any number of addForEachIn() (one per input)
|
|
// b3) call any number of addForEachParam() (one per param)
|
|
// b4) call addForEachSignatureMetadata() (if it's reflected)
|
|
// b5) call endForEach()
|
|
//
|
|
// b2, b3, b4 can occur in any order
|
|
|
|
void beginForEaches(size_t Count);
|
|
|
|
void declareForEachDummyRoot(const RSExportForEach *) { /* we don't care */ };
|
|
|
|
void beginForEach(const RSExportForEach *EF);
|
|
|
|
void addForEachIn(const RSExportForEach *EF, const RSExportType *Type);
|
|
|
|
void addForEachParam(const RSExportForEach *EF, const RSExportType *Type);
|
|
|
|
void addForEachSignatureMetadata(const RSExportForEach *EF, unsigned Metadata);
|
|
|
|
void endForEach();
|
|
|
|
void endForEaches();
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Model for Invokable functions (per File):
|
|
//
|
|
// a) beginInvokables(number_of_invokables_in_file)
|
|
// b) declareInvokable() for each Invokable (order must be
|
|
// consistent between 32-bit and 64-bit compile)
|
|
// c) endInvokables()
|
|
|
|
void beginInvokables(size_t Count) {
|
|
mInvokablesOrderFatal = false;
|
|
begin(&File::mInvokables, Count);
|
|
}
|
|
|
|
void declareInvokable(const RSExportFunc *EF);
|
|
|
|
void endInvokables();
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Model for reduction kernels (per File):
|
|
//
|
|
// a) beginReduces(number_of_reduction_kernels_in_file)
|
|
// b) declareReduce() for each reduction kernel (order must be
|
|
// consistent between 32-bit and 64-bit compile)
|
|
// c) endReduces()
|
|
|
|
void beginReduces(size_t Count) {
|
|
mReducesOrderFatal = false;
|
|
begin(&File::mReduces, Count);
|
|
}
|
|
|
|
void declareReduce(const RSExportReduce *ER, bool IsExportable);
|
|
|
|
void endReduces();
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Model for records (per File):
|
|
//
|
|
// a) beginRecords()
|
|
// b) declareRecord() for each Record (order doesn't matter)
|
|
// c) endRecords()
|
|
//
|
|
// And at any time during the Using phase, can call getRecord32() to
|
|
// get information from the 32-bit compile (Collecting phase).
|
|
|
|
void beginRecords();
|
|
|
|
// An "Ordinary" record is anything other than an
|
|
// internally-synthesized helper record. We do not emit diagnostics
|
|
// for mismatched helper records -- we assume that the constructs
|
|
// from which those helper records were derived are also mismatched,
|
|
// and that we'll get diagnostics for those constructs.
|
|
void declareRecord(const RSExportRecordType *ERT, bool Ordinary = true);
|
|
|
|
void endRecords();
|
|
|
|
class Record32;
|
|
|
|
// During the Using phase, obtain information about a Record from
|
|
// the Collecting phase. ERT should be from the Using phase, not
|
|
// the Collecting phase. The value returned from this function is
|
|
// valid for the lifetime of the ReflectionState instance.
|
|
Record32 getRecord32(const RSExportRecordType *ERT);
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// Model for Variables (per file):
|
|
//
|
|
// a) beginVariables(number_of_exported_variables_in_file)
|
|
// b) declareVariable() for each Variable (order must be consistent
|
|
// between 32-bit and 64-bit); in the Using phase, returns some
|
|
// information about the Variable from 32-bit compilation
|
|
// c) endVariables()
|
|
|
|
void beginVariables(size_t Count) {
|
|
mVariablesOrderFatal = false;
|
|
begin(&File::mVariables, Count);
|
|
}
|
|
|
|
// If isUsing(), returns variable's 32-bit AllocSize; otherwise, returns NoVal32().
|
|
Val32 declareVariable(const RSExportVar *EV);
|
|
|
|
void endVariables();
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// ReflectionState has a notion of "current file". After an
|
|
// openJava*() or closeJava*() call, there is no current file.
|
|
// Calling the nextFile() method when in the Collecting or Using
|
|
// state "advances" to the next file in the list of files being
|
|
// compiled, whose properties are specified by the arguments to
|
|
// nextFile(). All of the various begin*(), declare*(), end*()
|
|
// etc. calls implicitly refer to entities in the current file.
|
|
//
|
|
// RSC must remain valid until the next call to nextFile() or the
|
|
// next S_* state change.
|
|
void nextFile(const RSContext *RSC, const std::string &PackageName, const std::string &RSSourceFileName);
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
private:
|
|
enum State {
|
|
S_Initial, // No captured information
|
|
S_OpenJava32, // Capturing information for 32-bit Java
|
|
S_ClosedJava32, // Captured information for 32-bit Java
|
|
S_OpenJava64, // Capturing information for 64-bit Java
|
|
S_ClosedJava64, // Captured information for 64-bit Java
|
|
S_Bad, // Abnormal termination
|
|
};
|
|
|
|
// context associated with compilation of the current file
|
|
const RSContext *mRSC;
|
|
|
|
State mState;
|
|
|
|
/*== ForEach ==================================================================*/
|
|
|
|
// The data in this section is transient during ForEach processing
|
|
// for each File.
|
|
|
|
int mForEachOpen; // if nonnegative, then ordinal of beginForEach() without matching endForEach()
|
|
bool mForEachFatal; // fatal mismatch in comparing ForEach; do no further comparisons for it
|
|
|
|
// Tracks mismatches discovered during the Use phase.
|
|
// There are two possibilities:
|
|
// - if (ordinal + 1) is greater than the number of ForEaches from the Collecting phase,
|
|
// then this is an "extra" ForEach discovered during the Use phase
|
|
// - otherwise the Collecting phase and the Use phase disagree on the name of the
|
|
// ForEach at this ordinal position (the Collecting phase's kernel name is
|
|
// available in mFiles.Current().mForEaches[ordinal].mName)
|
|
llvm::SmallVector<const RSExportForEach *, 0> mForEachesBad;
|
|
|
|
// During the Use phase, keep track of how many ForEach ordinals we
|
|
// have seen that correspond to ordinals seen during the Collect
|
|
// phase. This helps determine whether we have to issue errors at
|
|
// endForEaches().
|
|
size_t mNumForEachesMatchedByOrdinal;
|
|
|
|
/*== Invokable ================================================================*/
|
|
|
|
// 32-bit and 64-bit compiles need to see invokables in the same
|
|
// order, because of slot number assignment. Once we see the first
|
|
// name mismatch in the sequence of invokables for a given File, it
|
|
// doesn't make sense to issue further diagnostics regarding
|
|
// invokables for that File.
|
|
bool mInvokablesOrderFatal;
|
|
|
|
/*== OutputClass ==============================================================*/
|
|
|
|
// This data tracks information about a class being generated -- a
|
|
// script class or a type class. We call such a class "Divergent"
|
|
// if it needs to have at least one runtime check to distinguish
|
|
// between 32-bit and 64-bit targets.
|
|
|
|
bool mOutputClassOpen; // beginOutputClass() without matching endOutputClass()
|
|
bool mOutputClassDivergent; // has class been marked divergent?
|
|
|
|
/*== Record ===================================================================*/
|
|
|
|
// This field enforces necessary discipline on the use of
|
|
// beginRecords()/declareRecord()/endRecord().
|
|
enum {
|
|
RS_Initial, // no beginRecords() yet for current File
|
|
RS_Open, // beginRecords() but no endRecords() for current File
|
|
RS_Closed // endRecords() for current File
|
|
} mRecordsState;
|
|
|
|
// During the Use phase, keep track of how many records we have seen
|
|
// that have same-named counterparts seen during the Collect phase.
|
|
// This helps determine whether we have to issue errors at
|
|
// endRecords().
|
|
size_t mNumRecordsMatchedByName;
|
|
|
|
/*== Reduce ===================================================================*/
|
|
|
|
// 32-bit and 64-bit compiles need to see reduction kernels in the
|
|
// same order, because of slot number assignment. Once we see the
|
|
// first name mismatch in the sequence of reduction kernels for a
|
|
// given File, it doesn't make sense to issue further diagnostics
|
|
// regarding reduction kernels for that File.
|
|
bool mReducesOrderFatal;
|
|
|
|
/*== Variable =================================================================*/
|
|
|
|
// 32-bit and 64-bit compiles need to see variables in the same
|
|
// order, because of slot number assignment. Once we see the first
|
|
// name mismatch in the sequence of variables for a given File, it
|
|
// doesn't make sense to issue further diagnostics regarding
|
|
// variables for that File.
|
|
bool mVariablesOrderFatal;
|
|
|
|
/*=============================================================================*/
|
|
|
|
bool isActive() const { return isCollecting() || isUsing(); }
|
|
bool isClosed() const { return mState==S_ClosedJava32 || mState==S_ClosedJava64; }
|
|
bool isUsing() const { return mState==S_OpenJava64; }
|
|
|
|
// For anything with a type (such as a Variable or a Record field),
|
|
// the type is represented via its name. To save space, we don't
|
|
// create multiple instances of the same name -- we have a canonical
|
|
// instance in mStringSet, and use a StringRef to refer to it. The
|
|
// method canon() returns a StringRef to the canonical
|
|
// instance, creating the instance if necessary.
|
|
llvm::StringRef canon(const std::string &String);
|
|
llvm::StringSet<> *mStringSet;
|
|
|
|
// Synthesize a name for the specified type. There should be a
|
|
// one-to-one correspondence between the name and a C type (after
|
|
// typedefs and integer expressions have been "flattened", and
|
|
// considering a struct type to be identified solely by its name).
|
|
static std::string getUniqueTypeName(const RSExportType *T);
|
|
|
|
// ------------------------------
|
|
// template class ArrayWithCursor
|
|
// ------------------------------
|
|
//
|
|
// This class represents a fixed-length dynamically-allocated array
|
|
// (length is specified by a method call after instantiation) along
|
|
// with a cursor that traverses the array. The behavior of the
|
|
// class is very specific to the needs of ReflectionState.
|
|
//
|
|
// The model for using this class is as follows:
|
|
// a) Instantiate a class instance. The instance is in the
|
|
// S_Initial state.
|
|
// b) Call BeginCollecting() with an array capacity. This allocates
|
|
// the array members and moves the instance to the S_Collecting
|
|
// state. The array size (contrast with capacity) is zero, and
|
|
// the cursor has not been placed.
|
|
// c) Call CollectNext() a number of times equal to the capacity.
|
|
// Each time CollectNext() is called, it extends the array size
|
|
// by 1, and advances the cursor to the "new" member. The idea
|
|
// is to set the value of the "new" member at this time.
|
|
// d) Call BeginUsing(). This moves the instance to the S_Using
|
|
// state and "unplaces" the cursor.
|
|
// e) Call UseNext() a number of times equal to the capacity. Each
|
|
// time UseNext() is called, it advances the cursor to the next
|
|
// member (first member, the first time it is called).
|
|
// The cursor is stepping through the members that were "created"
|
|
// by CollectNext() during the S_Collecting state; the idea is to
|
|
// look at their values.
|
|
// f) Destroy the instance.
|
|
//
|
|
template <typename Member> class ArrayWithCursor {
|
|
public:
|
|
ArrayWithCursor() : mState(S_Initial), mMembers(nullptr), mCapacity(0), mSize(0), mCursor(~size_t(0)) { }
|
|
|
|
~ArrayWithCursor() { delete [] mMembers; }
|
|
|
|
ArrayWithCursor(const ArrayWithCursor &) = delete;
|
|
void operator=(const ArrayWithCursor &) = delete;
|
|
|
|
void BeginCollecting(size_t Size) {
|
|
slangAssert(mState == S_Initial);
|
|
mState = S_Collecting;
|
|
mMembers = new Member[Size];
|
|
mCapacity = Size;
|
|
}
|
|
// Increments the array size, advances the cursor to the new
|
|
// member, and returns a reference to that member.
|
|
Member &CollectNext() {
|
|
slangAssert((mState == S_Collecting) && (mCursor + 1 == mSize) && (mSize < mCapacity));
|
|
++mSize;
|
|
return mMembers[++mCursor];
|
|
}
|
|
|
|
void BeginUsing() {
|
|
slangAssert((mState == S_Collecting) && (mCursor + 1 == mSize) && (mSize == mCapacity));
|
|
mState = S_Using;
|
|
mCursor = ~size_t(0);
|
|
}
|
|
// Advances the cursor to the next member, and returns a reference
|
|
// to that member.
|
|
Member &UseNext() {
|
|
slangAssert((mState == S_Using) && (mCursor + 1 < mSize));
|
|
return mMembers[++mCursor];
|
|
}
|
|
|
|
// Is the cursor on the last array member?
|
|
bool isFinished() const {
|
|
return mCursor + 1 == mSize;
|
|
}
|
|
|
|
size_t Size() const { return mSize; }
|
|
|
|
// Return a reference to the member under the cursor.
|
|
Member &Current() {
|
|
slangAssert(mCursor < mSize);
|
|
return mMembers[mCursor];
|
|
}
|
|
const Member &Current() const {
|
|
slangAssert(mCursor < mSize);
|
|
return mMembers[mCursor];
|
|
}
|
|
// Return the cursor position (zero-based). Cursor must have been
|
|
// placed (i.e., if we're Collecting, we must have called
|
|
// CollectNext() at least once; and if we're Using, we must have
|
|
// called UseNext() at least once).
|
|
size_t CurrentIdx() const {
|
|
slangAssert(mCursor < mSize);
|
|
return mCursor;
|
|
}
|
|
|
|
// Return a reference to the specified member. Must be within the
|
|
// array size (not merely within its capacity).
|
|
Member &operator[](size_t idx) {
|
|
slangAssert(idx < mSize);
|
|
return mMembers[idx];
|
|
}
|
|
const Member &operator[](size_t idx) const {
|
|
slangAssert(idx < mSize);
|
|
return mMembers[idx];
|
|
}
|
|
|
|
private:
|
|
enum State { S_Initial, S_Collecting, S_Using };
|
|
State mState;
|
|
|
|
Member *mMembers;
|
|
size_t mCapacity;
|
|
size_t mSize;
|
|
size_t mCursor;
|
|
};
|
|
|
|
|
|
struct File {
|
|
File() : mForEaches(nullptr) { }
|
|
~File() { delete [] mForEaches; }
|
|
|
|
File(const File &) = delete;
|
|
void operator=(const File &) = delete;
|
|
|
|
std::string mPackageName;
|
|
std::string mRSSourceFileName;
|
|
|
|
struct ForEach {
|
|
ForEach() : mState(S_Initial) { }
|
|
ForEach(const ForEach &) = delete;
|
|
void operator=(const ForEach &) = delete;
|
|
|
|
enum {
|
|
S_Initial, // ForEach has been instantiated
|
|
S_Collected, // beginForEach() has been called while Collecting
|
|
S_UseMatched // beginForEach() has been called while Using,
|
|
// and found this ForEach
|
|
} mState;
|
|
|
|
std::string mName;
|
|
|
|
// Types. mIns[] and mOut can be null in case we have an
|
|
// old-style kernel with a void* input or output.
|
|
ArrayWithCursor<llvm::StringRef> mIns;
|
|
ArrayWithCursor<llvm::StringRef> mParams;
|
|
llvm::StringRef mOut;
|
|
bool mHasOut; // to distinguish between no output and void* output.
|
|
|
|
unsigned mSignatureMetadata;
|
|
bool mIsKernel; // new-style (by-value) rather than old-style
|
|
};
|
|
ForEach *mForEaches; // indexed by ordinal (lexical order)
|
|
size_t mForEachCount;
|
|
|
|
struct Invokable {
|
|
Invokable() : mParams(nullptr) { }
|
|
~Invokable() { delete [] mParams; }
|
|
|
|
Invokable(const Invokable &) = delete;
|
|
void operator=(const Invokable &) = delete;
|
|
|
|
std::string mName;
|
|
llvm::StringRef *mParams; // Types
|
|
size_t mParamCount;
|
|
};
|
|
ArrayWithCursor<Invokable> mInvokables;
|
|
|
|
// There are two things we need to do with a Record:
|
|
// - Support structure sizes and layouts that differ between
|
|
// 32-bit and 64-bit compilation.
|
|
// - Do consistency checking between 32-bit and 64-bit compilation.
|
|
//
|
|
// TODO: Move this out of File to avoid duplication? That is,
|
|
// instead of tracking Records on a per-File basis, instead
|
|
// track them globally?
|
|
//
|
|
// (Because of ODR, we shouldn't have inconsistencies
|
|
// between Files.)
|
|
//
|
|
struct Record {
|
|
Record() : mFields(nullptr) { }
|
|
~Record() { delete [] mFields; }
|
|
|
|
Record(const Record &) = delete;
|
|
void operator=(const Record &) = delete;
|
|
|
|
struct Field {
|
|
std::string mName;
|
|
llvm::StringRef mType;
|
|
size_t mPrePadding; // this.OffsetInParent - (prev.OffsetInParent + prev.AllocSize)
|
|
size_t mPostPadding; // this.AllocSize - this.StoreSize
|
|
size_t mOffset; // this.OffsetInParent
|
|
size_t mStoreSize; // this.StoreSize
|
|
};
|
|
Field *mFields;
|
|
size_t mFieldCount;
|
|
size_t mPostPadding; // padding after the end of the padded
|
|
// last field
|
|
size_t mAllocSize;
|
|
bool mOrdinary; // anything other than an
|
|
// internally-synthesized helper
|
|
// record. We do not emit diagnostics
|
|
// for inconsistent helper records.
|
|
bool mMatchedByName; // has declareRecord() been called on
|
|
// this record during the Using phase?
|
|
};
|
|
llvm::StringMap<Record> mRecords;
|
|
|
|
struct Reduce {
|
|
Reduce() : mAccumIns(nullptr) { }
|
|
~Reduce() { delete [] mAccumIns; }
|
|
|
|
Reduce(const Reduce &) = delete;
|
|
void operator=(const Reduce &) = delete;
|
|
|
|
std::string mName;
|
|
|
|
// only apply to exportable
|
|
llvm::StringRef *mAccumIns; // Types
|
|
size_t mAccumInCount;
|
|
llvm::StringRef mResult; // Type
|
|
|
|
bool mIsExportable;
|
|
};
|
|
ArrayWithCursor<Reduce> mReduces;
|
|
|
|
struct Variable {
|
|
Variable() : mInitializers(nullptr) { }
|
|
~Variable() { delete [] mInitializers; }
|
|
|
|
Variable(const Variable &) = delete;
|
|
void operator=(const Variable &) = delete;
|
|
|
|
std::string mName;
|
|
llvm::StringRef mType;
|
|
clang::APValue *mInitializers;
|
|
size_t mInitializerCount;
|
|
size_t mAllocSize;
|
|
bool mIsConst;
|
|
};
|
|
ArrayWithCursor<Variable> mVariables;
|
|
|
|
};
|
|
ArrayWithCursor<File> mFiles;
|
|
|
|
// Utility template -- common pattern used by many begin*() methods.
|
|
template <typename Member>
|
|
void begin(ArrayWithCursor<Member> File::*Array, size_t Count) {
|
|
slangAssert(!isClosed());
|
|
if (!isActive())
|
|
return;
|
|
|
|
auto &file = mFiles.Current();
|
|
if (isCollecting())
|
|
(file.*Array).BeginCollecting(Count);
|
|
if (isUsing())
|
|
(file.*Array).BeginUsing();
|
|
}
|
|
|
|
public:
|
|
|
|
// This class represents 32-bit layout information built up during
|
|
// the Collecting phase, for use during the Using phase. It
|
|
// provides an interface between class ReflectionState and client
|
|
// code that actually performs reflection.
|
|
class Record32 {
|
|
friend class ReflectionState;
|
|
|
|
public:
|
|
Record32() : mRecord(nullptr) { }
|
|
|
|
Val32 getRecordPostPadding() const {
|
|
if (!mRecord)
|
|
return NoVal32();
|
|
return Val32(true, mRecord->mPostPadding);
|
|
}
|
|
|
|
Val32 getRecordAllocSize() const {
|
|
if (!mRecord)
|
|
return NoVal32();
|
|
return Val32(true, mRecord->mAllocSize);
|
|
}
|
|
|
|
std::pair<Val32, Val32> getFieldPreAndPostPadding(unsigned idx) const {
|
|
if (!mRecord || idx >= mRecord->mFieldCount)
|
|
return std::make_pair(NoVal32(), NoVal32());
|
|
const File::Record::Field &field = mRecord->mFields[idx];
|
|
return std::make_pair(Val32(true, field.mPrePadding), Val32(true, field.mPostPadding));
|
|
}
|
|
|
|
std::pair<Val32, Val32> getFieldOffsetAndStoreSize(unsigned idx) const {
|
|
if (!mRecord || idx >= mRecord->mFieldCount)
|
|
return std::make_pair(NoVal32(), NoVal32());
|
|
const File::Record::Field &field = mRecord->mFields[idx];
|
|
return std::make_pair(Val32(true, field.mOffset), Val32(true, field.mStoreSize));
|
|
}
|
|
|
|
private:
|
|
Record32(const File::Record *Record) : mRecord(Record) { }
|
|
const File::Record *mRecord;
|
|
};
|
|
};
|
|
|
|
}
|
|
|
|
#endif // _FRAMEWORKS_COMPILE_SLANG_REFLECTION_STATE_H_ NOLINT
|