Implement one-definition-rule (ODR) feature.

When compiling multiple RS files, we say two RS files A and B break ODR
iff:

1. They have at least one common struct named [S] and [S] will be reflected
to ScriptField_[S].java, and
2. [S] defined in A is not *exactly the same* (number of fields, field
type and field name) as the one defined in B.

This CL detects such error.
diff --git a/Android.mk b/Android.mk
index 05066e5..0a8dc14 100644
--- a/Android.mk
+++ b/Android.mk
@@ -202,6 +202,7 @@
 	slang_rs_context.cpp	\
 	slang_rs_pragma_handler.cpp	\
 	slang_rs_backend.cpp	\
+	slang_rs_exportable.cpp	\
 	slang_rs_export_type.cpp	\
 	slang_rs_export_element.cpp	\
 	slang_rs_export_var.cpp	\
diff --git a/llvm-rs-cc.cpp b/llvm-rs-cc.cpp
index afd99b4..955d7c6 100644
--- a/llvm-rs-cc.cpp
+++ b/llvm-rs-cc.cpp
@@ -365,8 +365,9 @@
   std::list<std::pair<const char*, const char*> > IOFiles;
   std::list<std::pair<const char*, const char*> > DepFiles;
 
-  llvm::OwningPtr<SlangRS> Compiler(new SlangRS(Opts.mTriple, Opts.mCPU,
-                                                Opts.mFeatures));
+  llvm::OwningPtr<SlangRS> Compiler(new SlangRS());
+
+  Compiler->init(Opts.mTriple, Opts.mCPU, Opts.mFeatures);
 
   for (int i = 0, e = Inputs.size(); i != e; i++) {
     const char *InputFile = Inputs[i];
diff --git a/slang.cpp b/slang.cpp
index cd3cf93..98610c6 100644
--- a/slang.cpp
+++ b/slang.cpp
@@ -185,6 +185,7 @@
   mDiagClient = new DiagnosticBuffer();
   // This takes the ownership of mDiagClient.
   mDiagnostics->setClient(mDiagClient);
+  initDiagnostic();
   return;
 }
 
@@ -272,11 +273,15 @@
                      OT);
 }
 
-Slang::Slang(const std::string &Triple, const std::string &CPU,
-             const std::vector<std::string> &Features)
-    : mDiagClient(NULL),
-      mOT(OT_Default) {
+Slang::Slang() : mInitialized(false), mDiagClient(NULL), mOT(OT_Default) {
   GlobalInitialization();
+  return;
+}
+
+void Slang::init(const std::string &Triple, const std::string &CPU,
+                 const std::vector<std::string> &Features) {
+  if (mInitialized)
+    return;
 
   createDiagnostic();
   llvm::install_fatal_error_handler(LLVMErrorHandler, mDiagnostics.getPtr());
@@ -285,6 +290,8 @@
   createFileManager();
   createSourceManager();
 
+  mInitialized = true;
+
   return;
 }
 
diff --git a/slang.h b/slang.h
index 96a1390..ea33567 100644
--- a/slang.h
+++ b/slang.h
@@ -71,6 +71,8 @@
   } OutputType;
 
  private:
+  bool mInitialized;
+
   // The diagnostics engine instance (for status reporting during compilation)
   llvm::IntrusiveRefCntPtr<clang::Diagnostic> mDiagnostics;
   // The clients of diagnostics engine. The ownership is taken by the
@@ -151,8 +153,10 @@
 
   static void GlobalInitialization();
 
-  Slang(const std::string &Triple, const std::string &CPU,
-        const std::vector<std::string> &Features);
+  Slang();
+
+  void init(const std::string &Triple, const std::string &CPU,
+            const std::vector<std::string> &Features);
 
   bool setInputSource(llvm::StringRef InputFile, const char *Text,
                       size_t TextLength);
diff --git a/slang_rs.cpp b/slang_rs.cpp
index b71e904..566071e 100644
--- a/slang_rs.cpp
+++ b/slang_rs.cpp
@@ -24,6 +24,7 @@
 
 #include "slang_rs_backend.h"
 #include "slang_rs_context.h"
+#include "slang_rs_export_type.h"
 
 using namespace slang;
 
@@ -64,6 +65,92 @@
   return RSSlangReflectUtils::GenerateBitCodeAccessor(BCAccessorContext);
 }
 
+bool SlangRS::checkODR() {
+  for (RSContext::ExportableList::iterator I = mRSContext->exportable_begin(),
+          E = mRSContext->exportable_end();
+       I != E;
+       I++) {
+    RSExportable *E = *I;
+    if (E->getKind() != RSExportable::EX_TYPE)
+      continue;
+
+    RSExportType *ET = static_cast<RSExportType *>(E);
+    if (ET->getClass() != RSExportType::ExportClassRecord)
+      continue;
+
+    RSExportRecordType *ERT = static_cast<RSExportRecordType *>(ET);
+
+    // Artificial record types (create by us not by user in the source) always
+    // conforms the ODR.
+    if (ERT->isArtificial())
+      continue;
+
+    // Key to lookup ERT in ReflectedDefinitions
+    llvm::StringRef RDKey(ERT->getName());
+    ReflectedDefinitionListTy::const_iterator RD =
+        ReflectedDefinitions.find(RDKey);
+
+    if (RD != ReflectedDefinitions.end()) {
+      const RSExportRecordType *Reflected = RD->getValue().first;
+      // There's a record (struct) with the same name reflected before. Enforce
+      // ODR checking - the Reflected must hold *exactly* the same "definition"
+      // as the one defined previously. We say two record types A and B have the
+      // same definition iff:
+      //
+      //  struct A {              struct B {
+      //    Type(a1) a1,            Type(b1) b1,
+      //    Type(a2) a2,            Type(b1) b2,
+      //    ...                     ...
+      //    Type(aN) aN             Type(b3) b3,
+      //  };                      }
+      //  Cond. #1. They have same number of fields, i.e., N = M;
+      //  Cond. #2. for (i := 1 to N)
+      //              Type(ai) = Type(bi) must hold;
+      //  Cond. #3. for (i := 1 to N)
+      //              Name(ai) = Name(bi) must hold;
+      //
+      // where,
+      //  Type(F) = the type of field F and
+      //  Name(F) = the field name.
+
+      bool PassODR = false;
+      // Cond. #1 and Cond. #2
+      if (Reflected->equals(ERT)) {
+        // Cond #3.
+        RSExportRecordType::const_field_iterator AI = Reflected->fields_begin(),
+                                                 BI = ERT->fields_begin();
+
+        for (unsigned i = 0, e = Reflected->getFields().size(); i != e; i++) {
+          if ((*AI)->getName() != (*BI)->getName())
+            break;
+          AI++;
+          BI++;
+        }
+        PassODR = (AI == (Reflected->fields_end()));
+      }
+
+      if (!PassODR) {
+        getDiagnostics().Report(mDiagErrorODR) << Reflected->getName()
+                                               << getInputFileName()
+                                               << RD->getValue().second;
+        return false;
+      }
+    } else {
+      llvm::StringMapEntry<ReflectedDefinitionTy> *ME =
+          llvm::StringMapEntry<ReflectedDefinitionTy>::Create(RDKey.begin(),
+                                                              RDKey.end());
+      ME->setValue(std::make_pair(ERT, getInputFileName().c_str()));
+
+      if (!ReflectedDefinitions.insert(ME))
+        delete ME;
+
+      // Take the ownership of ERT such that it won't be freed in ~RSContext().
+      ERT->keep();
+    }
+
+  }
+  return true;
+}
 
 void SlangRS::initDiagnostic() {
   clang::Diagnostic &Diag = getDiagnostics();
@@ -75,6 +162,16 @@
   Diag.setDiagnosticMapping(
       clang::diag::ext_typecheck_convert_discards_qualifiers,
       clang::diag::MAP_ERROR);
+
+  mDiagErrorInvalidOutputDepParameter =
+      Diag.getCustomDiagID(clang::Diagnostic::Error,
+                           "invalid parameter for output dependencies files.");
+
+  mDiagErrorODR =
+      Diag.getCustomDiagID(clang::Diagnostic::Error,
+                           "type '%0' in different translation unit (%1 v.s. "
+                           "%2) has incompatible type definition");
+
   return;
 }
 
@@ -126,11 +223,7 @@
   return false;
 }
 
-SlangRS::SlangRS(const std::string &Triple, const std::string &CPU,
-                 const std::vector<std::string> &Features)
-    : Slang(Triple, CPU, Features),
-      mRSContext(NULL),
-      mAllowRSPrefix(false) {
+SlangRS::SlangRS() : Slang(), mRSContext(NULL), mAllowRSPrefix(false) {
   return;
 }
 
@@ -147,8 +240,7 @@
     return true;
 
   if (OutputDep && (DepFiles.size() != IOFiles.size())) {
-    fprintf(stderr, "SlangRS::compile() : Invalid parameter for output "
-                    "dependencies files.\n");
+    getDiagnostics().Report(mDiagErrorInvalidOutputDepParameter);
     return false;
   }
 
@@ -208,13 +300,29 @@
           return false;
     }
 
+    if (!checkODR())
+      return false;
+
     IOFileIter++;
   }
 
   return true;
 }
 
+void SlangRS::reset() {
+  delete mRSContext;
+  mRSContext = NULL;
+  Slang::reset();
+  return;
+}
+
 SlangRS::~SlangRS() {
   delete mRSContext;
+  for (ReflectedDefinitionListTy::iterator I = ReflectedDefinitions.begin(),
+          E = ReflectedDefinitions.end();
+       I != E;
+       I++) {
+    delete I->getValue().first;
+  }
   return;
 }
diff --git a/slang_rs.h b/slang_rs.h
index 6769744..613de76 100644
--- a/slang_rs.h
+++ b/slang_rs.h
@@ -23,10 +23,13 @@
 #include <vector>
 #include <string>
 
+#include "llvm/ADT/StringMap.h"
+
 #include "slang_rs_reflect_utils.h"
 
 namespace slang {
   class RSContext;
+  class RSExportRecordType;
 
 class SlangRS : public Slang {
  private:
@@ -35,6 +38,20 @@
 
   bool mAllowRSPrefix;
 
+  // Custom diagnostic identifiers
+  unsigned mDiagErrorInvalidOutputDepParameter;
+  unsigned mDiagErrorODR;
+
+  // FIXME: Should be std::list<RSExportable *> here. But currently we only
+  //        check ODR on record type.
+  //
+  // ReflectedDefinitions maps record type name to a pair:
+  //  <its RSExportRecordType instance,
+  //   the first file contains this record type definition>
+  typedef std::pair<RSExportRecordType*, const char*> ReflectedDefinitionTy;
+  typedef llvm::StringMap<ReflectedDefinitionTy> ReflectedDefinitionListTy;
+  ReflectedDefinitionListTy ReflectedDefinitions;
+
   // The package name that's really applied will be filled in RealPackageName.
   bool reflectToJava(const std::string &OutputPathBase,
                      const std::string &OutputPackageName,
@@ -43,6 +60,8 @@
   bool generateBitcodeAccessor(const std::string &OutputPathBase,
                                const std::string &PackageName);
 
+  bool checkODR();
+
  protected:
   virtual void initDiagnostic();
   virtual void initPreprocessor();
@@ -57,8 +76,7 @@
  public:
   static bool IsRSHeaderFile(const char *File);
 
-  SlangRS(const std::string &Triple, const std::string &CPU,
-          const std::vector<std::string> &Features);
+  SlangRS();
 
   // Compile bunch of RS files given in the llvm-rs-cc arguments. Return true if
   // all given input files are successfully compiled without errors.
@@ -69,7 +87,7 @@
   //             target>. If @OutputDep is true, this parameter must be given
   //             with the same number of pairs given in @IOFiles.
   //
-  // @IncludePaths - User-defined include path.
+  // @IncludePaths - User-defined include paths.
   //
   // @AdditionalDepTargets - User-defined files added to the dependencies.
   //
@@ -79,7 +97,7 @@
   //
   // @AllowRSPrefix - true to allow user-defined function prefixed with 'rs'.
   //
-  // @OutputDep - true if output dependecies file.
+  // @OutputDep - true if output dependecies file for each input file.
   //
   // @JavaReflectionPathBase - The path base for storing reflection files.
   //
@@ -96,6 +114,8 @@
                const std::string &JavaReflectionPathBase,
                const std::string &JavaReflectionPackageName);
 
+  virtual void reset();
+
   virtual ~SlangRS();
 };
 }
diff --git a/slang_rs_context.cpp b/slang_rs_context.cpp
index 1bd096c..a63b2ad 100644
--- a/slang_rs_context.cpp
+++ b/slang_rs_context.cpp
@@ -282,6 +282,7 @@
           E = mExportables.end();
        I != E;
        I++) {
-    delete *I;
+    if (!(*I)->isKeep())
+      delete *I;
   }
 }
diff --git a/slang_rs_context.h b/slang_rs_context.h
index accd63f..e4e0416 100644
--- a/slang_rs_context.h
+++ b/slang_rs_context.h
@@ -133,6 +133,13 @@
     if (E != NULL)
       mExportables.push_back(E);
   }
+  typedef ExportableList::iterator exportable_iterator;
+  exportable_iterator exportable_begin() {
+    return mExportables.begin();
+  }
+  exportable_iterator exportable_end() {
+    return mExportables.end();
+  }
 
   typedef ExportVarList::const_iterator const_export_var_iterator;
   const_export_var_iterator export_vars_begin() const {
diff --git a/slang_rs_export_func.cpp b/slang_rs_export_func.cpp
index f729c12..ba83290 100644
--- a/slang_rs_export_func.cpp
+++ b/slang_rs_export_func.cpp
@@ -112,7 +112,7 @@
     return false;
 
   const llvm::StructLayout *ParamTySL =
-      mContext->getTargetData()->getStructLayout(ParamTy);
+      getRSContext()->getTargetData()->getStructLayout(ParamTy);
 
   unsigned Index = 0;
   for (RSExportRecordType::const_field_iterator FI = ERT->fields_begin(),
@@ -135,7 +135,7 @@
 
     // Check size
     size_t T1Size = RSExportType::GetTypeAllocSize(F->getType());
-    size_t T2Size = mContext->getTargetData()->getTypeAllocSize(T2);
+    size_t T2Size = getRSContext()->getTargetData()->getTypeAllocSize(T2);
 
     if (T1Size != T2Size)
       return false;
diff --git a/slang_rs_export_func.h b/slang_rs_export_func.h
index 868aa4e..dbe46ee 100644
--- a/slang_rs_export_func.h
+++ b/slang_rs_export_func.h
@@ -41,13 +41,11 @@
   friend class RSContext;
 
  private:
-  RSContext *mContext;
   std::string mName;
   RSExportRecordType *mParamPacketType;
 
   RSExportFunc(RSContext *Context, const llvm::StringRef &Name)
     : RSExportable(Context, RSExportable::EX_FUNC),
-      mContext(Context),
       mName(Name.data(), Name.size()),
       mParamPacketType(NULL) {
     return;
@@ -71,7 +69,6 @@
   }
 
   inline const std::string &getName() const { return mName; }
-  inline RSContext *getRSContext() const { return mContext; }
 
   inline bool hasParam() const
     { return (mParamPacketType && !mParamPacketType->getFields().empty()); }
diff --git a/slang_rs_export_type.cpp b/slang_rs_export_type.cpp
index ca4e188..416cdfa 100644
--- a/slang_rs_export_type.cpp
+++ b/slang_rs_export_type.cpp
@@ -29,6 +29,10 @@
 #include "slang_rs_context.h"
 #include "slang_rs_export_element.h"
 
+#define CHECK_PARENT_EQUALITY(ParentClass, E) \
+  if (!ParentClass::equals(E))                \
+    return false;
+
 using namespace slang;
 
 /****************************** RSExportType ******************************/
@@ -413,7 +417,6 @@
                            ExportClass Class,
                            const llvm::StringRef &Name)
     : RSExportable(Context, RSExportable::EX_TYPE),
-      mContext(Context),
       mClass(Class),
       // Make a copy on Name since memory stored @Name is either allocated in
       // ASTContext or allocated in GetTypeName which will be destroyed later.
@@ -428,6 +431,18 @@
   return;
 }
 
+void RSExportType::keep() {
+  // Invalidate converted LLVM type.
+  mLLVMType = NULL;
+  RSExportable::keep();
+  return;
+}
+
+bool RSExportType::equals(const RSExportable *E) const {
+  CHECK_PARENT_EQUALITY(RSExportable, E);
+  return (static_cast<const RSExportType*>(E)->getClass() == getClass());
+}
+
 /************************** RSExportPrimitiveType **************************/
 llvm::ManagedStatic<RSExportPrimitiveType::RSObjectTypeMapTy>
 RSExportPrimitiveType::RSObjectTypeMap;
@@ -649,6 +664,11 @@
   return NULL;
 }
 
+bool RSExportPrimitiveType::equals(const RSExportable *E) const {
+  CHECK_PARENT_EQUALITY(RSExportType, E);
+  return (static_cast<const RSExportPrimitiveType*>(E)->getType() == getType());
+}
+
 /**************************** RSExportPointerType ****************************/
 
 const clang::Type *RSExportPointerType::IntegerType = NULL;
@@ -681,6 +701,17 @@
   return llvm::PointerType::getUnqual(PointeeType);
 }
 
+void RSExportPointerType::keep() {
+  const_cast<RSExportType*>(mPointeeType)->keep();
+  RSExportType::keep();
+}
+
+bool RSExportPointerType::equals(const RSExportable *E) const {
+  CHECK_PARENT_EQUALITY(RSExportType, E);
+  return (static_cast<const RSExportPointerType*>(E)
+              ->getPointeeType()->equals(getPointeeType()));
+}
+
 /***************************** RSExportVectorType *****************************/
 const char* RSExportVectorType::VectorTypeNameStore[][3] = {
   /* 0 */ { "char2",      "char3",    "char4" },
@@ -778,6 +809,12 @@
   return llvm::VectorType::get(ElementType, getNumElement());
 }
 
+bool RSExportVectorType::equals(const RSExportable *E) const {
+  CHECK_PARENT_EQUALITY(RSExportPrimitiveType, E);
+  return (static_cast<const RSExportVectorType*>(E)->getNumElement()
+              == getNumElement());
+}
+
 /***************************** RSExportMatrixType *****************************/
 RSExportMatrixType *RSExportMatrixType::Create(RSContext *Context,
                                                const clang::RecordType *RT,
@@ -849,6 +886,11 @@
   return llvm::StructType::get(C, X, NULL);
 }
 
+bool RSExportMatrixType::equals(const RSExportable *E) const {
+  CHECK_PARENT_EQUALITY(RSExportType, E);
+  return (static_cast<const RSExportMatrixType*>(E)->getDim() == getDim());
+}
+
 /************************* RSExportConstantArrayType *************************/
 RSExportConstantArrayType
 *RSExportConstantArrayType::Create(RSContext *Context,
@@ -878,6 +920,18 @@
   return llvm::ArrayType::get(mElementType->getLLVMType(), getSize());
 }
 
+void RSExportConstantArrayType::keep() {
+  const_cast<RSExportType*>(mElementType)->keep();
+  RSExportType::keep();
+  return;
+}
+
+bool RSExportConstantArrayType::equals(const RSExportable *E) const {
+  CHECK_PARENT_EQUALITY(RSExportType, E);
+  return ((static_cast<const RSExportConstantArrayType*>(E)
+              ->getSize() == getSize()) && (mElementType->equals(E)));
+}
+
 /**************************** RSExportRecordType ****************************/
 RSExportRecordType *RSExportRecordType::Create(RSContext *Context,
                                                const clang::RecordType *RT,
@@ -966,3 +1020,34 @@
                                FieldTypes,
                                mIsPacked);
 }
+
+void RSExportRecordType::keep() {
+  for (std::list<const Field*>::iterator I = mFields.begin(),
+          E = mFields.end();
+       I != E;
+       I++) {
+    const_cast<RSExportType*>((*I)->getType())->keep();
+  }
+  RSExportType::keep();
+  return;
+}
+
+bool RSExportRecordType::equals(const RSExportable *E) const {
+  CHECK_PARENT_EQUALITY(RSExportType, E);
+
+  const RSExportRecordType *ERT = static_cast<const RSExportRecordType*>(E);
+
+  if (ERT->getFields().size() != getFields().size())
+    return false;
+
+  const_field_iterator AI = fields_begin(), BI = ERT->fields_begin();
+
+  for (unsigned i = 0, e = getFields().size(); i != e; i++) {
+    if (!(*AI)->getType()->equals((*BI)->getType()))
+      return false;
+    AI++;
+    BI++;
+  }
+
+  return true;
+}
diff --git a/slang_rs_export_type.h b/slang_rs_export_type.h
index 4f067a2..46f94d4 100644
--- a/slang_rs_export_type.h
+++ b/slang_rs_export_type.h
@@ -71,7 +71,6 @@
   } ExportClass;
 
  private:
-  RSContext *mContext;
   ExportClass mClass;
   std::string mName;
 
@@ -137,8 +136,10 @@
   static size_t GetTypeAllocSize(const RSExportType *ET);
   static unsigned char GetTypeAlignment(const RSExportType *ET);
 
-  const std::string &getName() const { return mName; }
-  inline RSContext *getRSContext() const { return mContext; }
+  inline const std::string &getName() const { return mName; }
+
+  virtual void keep();
+  virtual bool equals(const RSExportable *E) const;
 };  // RSExportType
 
 // Primitive types
@@ -261,6 +262,8 @@
   inline bool isRSObjectType() const {
     return ((mType >= DataTypeRSElement) && (mType < DataTypeMax));
   }
+
+  virtual bool equals(const RSExportable *E) const;
 };  // RSExportPrimitiveType
 
 
@@ -288,7 +291,11 @@
  public:
   static const clang::Type *IntegerType;
 
+  virtual void keep();
+
   inline const RSExportType *getPointeeType() const { return mPointeeType; }
+
+  virtual bool equals(const RSExportable *E) const;
 };  // RSExportPointerType
 
 
@@ -325,6 +332,8 @@
   static llvm::StringRef GetTypeName(const clang::ExtVectorType *EVT);
 
   inline unsigned getNumElement() const { return mNumElement; }
+
+  virtual bool equals(const RSExportable *E) const;
 };
 
 // Only *square* *float* matrix is supported by now.
@@ -359,6 +368,8 @@
                                     unsigned Dim);
 
   inline unsigned getDim() const { return mDim; }
+
+  virtual bool equals(const RSExportable *E) const;
 };
 
 class RSExportConstantArrayType : public RSExportType {
@@ -387,6 +398,9 @@
  public:
   inline unsigned getSize() const { return mSize; }
   inline const RSExportType *getElementType() const { return mElementType; }
+
+  virtual void keep();
+  virtual bool equals(const RSExportable *E) const;
 };
 
 class RSExportRecordType : public RSExportType {
@@ -465,6 +479,9 @@
   inline bool isArtificial() const { return mIsArtificial; }
   inline size_t getAllocSize() const { return mAllocSize; }
 
+  virtual void keep();
+  virtual bool equals(const RSExportable *E) const;
+
   ~RSExportRecordType() {
     for (std::list<const Field*>::iterator I = mFields.begin(),
              E = mFields.end();
diff --git a/slang_rs_export_var.cpp b/slang_rs_export_var.cpp
index be94cfa..ef5f31d 100644
--- a/slang_rs_export_var.cpp
+++ b/slang_rs_export_var.cpp
@@ -29,7 +29,6 @@
                          const clang::VarDecl *VD,
                          const RSExportType *ET)
     : RSExportable(Context, RSExportable::EX_VAR),
-      mContext(Context),
       mName(VD->getName().data(), VD->getName().size()),
       mET(ET),
       mIsConst(false) {
diff --git a/slang_rs_export_var.h b/slang_rs_export_var.h
index 1f7eefc..c062543 100644
--- a/slang_rs_export_var.h
+++ b/slang_rs_export_var.h
@@ -37,7 +37,6 @@
 class RSExportVar : public RSExportable {
   friend class RSContext;
  private:
-  RSContext *mContext;
   std::string mName;
   const RSExportType *mET;
   bool mIsConst;
@@ -51,7 +50,6 @@
  public:
   inline const std::string &getName() const { return mName; }
   inline const RSExportType *getType() const { return mET; }
-  inline RSContext *getRSContext() const { return mContext; }
   inline bool isConst() const { return mIsConst; }
 
   inline const clang::APValue &getInit() const { return mInit.Val; }
diff --git a/slang_rs_exportable.cpp b/slang_rs_exportable.cpp
new file mode 100644
index 0000000..53ab33d
--- /dev/null
+++ b/slang_rs_exportable.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#include "slang_rs_exportable.h"
+
+using namespace slang;
+
+void RSExportable::keep() {
+  // Invalidate associated Context.
+  mContext = NULL;
+  return;
+}
+
+bool RSExportable::equals(const RSExportable *E) const {
+  return ((E == NULL) ? false : (mK == E->mK));
+}
diff --git a/slang_rs_exportable.h b/slang_rs_exportable.h
index c8f360f..c3d0215 100644
--- a/slang_rs_exportable.h
+++ b/slang_rs_exportable.h
@@ -30,10 +30,14 @@
   };
 
  private:
+  RSContext *mContext;
+
   Kind mK;
 
  protected:
-  RSExportable(RSContext *Context, RSExportable::Kind K) : mK(K) {
+  RSExportable(RSContext *Context, RSExportable::Kind K)
+      : mContext(Context),
+        mK(K) {
     Context->newExportable(this);
     return;
   }
@@ -41,6 +45,16 @@
  public:
   inline Kind getKind() const { return mK; }
 
+  // When keep() is invoked, mKeep will set to true and the associated RSContext
+  // won't free this RSExportable object in its destructor. The deallcation
+  // responsibility is then transferred to the object who invoked this function.
+  virtual void keep();
+  inline bool isKeep() const { return (mContext == NULL); }
+
+  virtual bool equals(const RSExportable *E) const;
+
+  inline RSContext *getRSContext() const { return mContext; }
+
   virtual ~RSExportable() { }
 };
 }