code:
#include "scriptbuilder.h"
#include <assert.h>
#include <vector>
#ifdef _WIN32
#include <windows.h> // MultiByteToWideChar()
#endif
using namespace std;

#include <stdio.h>
#if defined(_MSC_VER) && !defined(_WIN32_WCE) && !defined(__S3E__)
#include <direct.h>
#endif
#ifdef _WIN32_WCE
#include <windows.h> // For GetModuleFileName()
#endif

#if defined(__S3E__) || defined(__APPLE__) || defined(__GNUC__)
#include <unistd.h> // For getcwd()
#endif

BEGIN_AS_NAMESPACE

// Helper functions
static string GetCurrentDir();
static string GetAbsolutePath(const string& path);

CScriptBuilder::CScriptBuilder() {
  engine = 0;
  module = 0;

  includeCallback = 0;
  includeParam = 0;

  pragmaCallback = 0;
  pragmaParam = 0;
}

void CScriptBuilder::SetIncludeCallback(INCLUDECALLBACK_t callback, void* userParam) {
  includeCallback = callback;
  includeParam = userParam;
}

void CScriptBuilder::SetPragmaCallback(PRAGMACALLBACK_t callback, void* userParam) {
  pragmaCallback = callback;
  pragmaParam = userParam;
}

int CScriptBuilder::StartNewModule(asIScriptEngine* inEngine, const char* moduleName) {
  if (inEngine == 0)
    return -1;

  engine = inEngine;
  module = inEngine->GetModule(moduleName, asGM_ALWAYS_CREATE);
  if (module == 0)
    return -1;

  ClearAll();

  return 0;
}

asIScriptEngine* CScriptBuilder::GetEngine() { return engine; }

asIScriptModule* CScriptBuilder::GetModule() { return module; }

unsigned int CScriptBuilder::GetSectionCount() const { return (unsigned int)(includedScripts.size()); }

string CScriptBuilder::GetSectionName(unsigned int idx) const {
  if (idx >= includedScripts.size())
    return "";

#ifdef _WIN32
  set<string, ci_less>::const_iterator it = includedScripts.begin();
#else
  set<string>::const_iterator it = includedScripts.begin();
#endif
  while (idx-- > 0)
    it++;
  return *it;
}

// Returns 1 if the section was included
// Returns 0 if the section was not included because it had already been included before
// Returns <0 if there was an error
int CScriptBuilder::AddSectionFromFile(const char* filename) {
  // The file name stored in the set should be the fully resolved name because
  // it is possible to name the same file in multiple ways using relative paths.
  string fullpath = GetAbsolutePath(filename);

  if (IncludeIfNotAlreadyIncluded(fullpath.c_str())) {
    int r = LoadScriptSection(fullpath.c_str());
    if (r < 0)
      return r;
    else
      return 1;
  }

  return 0;
}

// Returns 1 if the section was included
// Returns 0 if the section was not included because it had already been included before
// Returns <0 if there was an error
int CScriptBuilder::AddSectionFromMemory(const char* sectionName, const char* scriptCode, unsigned int scriptLength, int lineOffset) {
  if (IncludeIfNotAlreadyIncluded(sectionName)) {
    int r = ProcessScriptSection(scriptCode, scriptLength, sectionName, lineOffset);
    if (r < 0)
      return r;
    else
      return 1;
  }

  return 0;
}

int CScriptBuilder::BuildModule() { return Build(); }

void CScriptBuilder::DefineWord(const char* word) {
  string sword = word;
  if (definedWords.find(sword) == definedWords.end()) {
    definedWords.insert(sword);
  }
}
void CScriptBuilder::UnDefineWord(const char* word) {
  std::string sword = word;
  if (definedWords.find(sword) != definedWords.end()) {
    definedWords.erase(definedWords.find(sword));
  }
}

void CScriptBuilder::ClearAll() {
  includedScripts.clear();

#if AS_PROCESS_METADATA == 1
  currentClass = "";
  currentNamespace = "";

  foundDeclarations.clear();
  typeMetadataMap.clear();
  funcMetadataMap.clear();
  varMetadataMap.clear();
#endif
}

bool CScriptBuilder::IncludeIfNotAlreadyIncluded(const char* filename) {
  string scriptFile = filename;
  if (includedScripts.find(scriptFile) != includedScripts.end()) {
    // Already included
    return false;
  }

  // Add the file to the set of included sections
  includedScripts.insert(scriptFile);

  return true;
}

int CScriptBuilder::LoadScriptSection(const char* filename) {
  // Open the script file
  string scriptFile = filename;
#if _MSC_VER >= 1500 && !defined(__S3E__)
#ifdef _WIN32
  // Convert the filename from UTF8 to UTF16
  wchar_t bufUTF16_name[10000] = {0};
  wchar_t bufUTF16_mode[10] = {0};
  MultiByteToWideChar(CP_UTF8, 0, filename, -1, bufUTF16_name, 10000);
  MultiByteToWideChar(CP_UTF8, 0, "rb", -1, bufUTF16_mode, 10);

  FILE* f = 0;
  _wfopen_s(&f, bufUTF16_name, bufUTF16_mode);
#else
  FILE* f = 0;
  fopen_s(&f, scriptFile.c_str(), "rb");
#endif
#else
  FILE* f = fopen(scriptFile.c_str(), "rb");
#endif
  if (f == 0) {
    // Write a message to the engine's message callback
    string msg = "Failed to open script file '" + GetAbsolutePath(scriptFile) + "'";
    engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str());

    // TODO: Write the file where this one was included from

    return -1;
  }

  // Determine size of the file
  fseek(f, 0, SEEK_END);
  int len = ftell(f);
  fseek(f, 0, SEEK_SET);

  // On Win32 it is possible to do the following instead
  // int len = _filelength(_fileno(f));

  // Read the entire file
  string code;
  size_t c = 0;
  if (len > 0) {
    code.resize(len);
    c = fread(&code[0], len, 1, f);
  }

  fclose(f);

  if (c == 0 && len > 0) {
    // Write a message to the engine's message callback
    string msg = "Failed to load script file '" + GetAbsolutePath(scriptFile) + "'";
    engine->WriteMessage(filename, 0, 0, asMSGTYPE_ERROR, msg.c_str());
    return -1;
  }

  // Process the script section even if it is zero length so that the name is registered
  return ProcessScriptSection(code.c_str(), (unsigned int)(code.length()), filename, 0);
}

int CScriptBuilder::ProcessScriptSection(const char* script, unsigned int length, const char* sectionname, int lineOffset) {
  vector<string> includes;

  // Perform a superficial parsing of the script first to store the metadata
  if (length)
    modifiedScript.assign(script, length);
  else
    modifiedScript = script;

  // First perform the checks for #if directives to exclude code that shouldn't be compiled
  unsigned int pos = 0;
  int nested = 0;
  int line = 0;
  while (pos < modifiedScript.size()) {
    asUINT len;
    asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
    if (t == asTC_UNKNOWN && modifiedScript[pos] == '#' && (pos + 1 < modifiedScript.size())) {
      int start = pos++;

      // Is this an #if directive?
      asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);

      std::string token;
      token.assign(&modifiedScript[pos], len);

      pos += len;
      if (token == "if" || token == "ifndef" || token == "ifdef") {
        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        if (t == asTC_WHITESPACE) {
          pos += len;
          t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        }

        if (t == asTC_IDENTIFIER) {
          std::string word;
          word.assign(&modifiedScript[pos], len);

          // Overwrite the #if directive with space characters to avoid compiler error
          pos += len;
          OverwriteCode(start, pos - start);

          // Has this identifier been defined by the application or not?
          if (definedWords.find(word) == definedWords.end() && token != "ifndef") {
            // Exclude all the code until and including the #endif
            pos = ExcludeCode(pos);
          } else {
            nested++;
          }
        } else if (t == asTC_VALUE || t == asTC_KEYWORD) {
          std::string word;
          word.assign(&modifiedScript[pos], len);

          // Overwrite the #if directive with space characters to avoid compiler error
          pos += len;
          OverwriteCode(start, pos - start);

          if (word == "1" || word == "true") {
            nested++;
          } else {
            // Exclude all the code until and including the #endif
            pos = ExcludeCode(pos);
          }
        }
      } else if (token == "endif") {
        // Only remove the #endif if there was a matching #if
        if (nested > 0) {
          OverwriteCode(start, pos - start);
          nested--;
        }
      } else if (token == "define") {
        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        if (t == asTC_WHITESPACE) {
          pos += len;
          t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        }
        if (t == asTC_IDENTIFIER) {
          std::string word;
          word.assign(&modifiedScript[pos], len);
          if (definedWords.find(word) == definedWords.end())
            DefineWord(word.c_str());
          else {
            std::string msg = "Redefinition of " + std::string(word);
            engine->WriteMessage(sectionname, line, 0, asMSGTYPE_WARNING, msg.c_str());
          }
        }
        pos += len;
        // Overwrite the #if directive with space characters to avoid compiler error
        OverwriteCode(start, pos - start);
      } else if (token == "undef") {
        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        if (t == asTC_WHITESPACE) {
          pos += len;
          t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        }
        if (t == asTC_IDENTIFIER) {
          std::string word;
          word.assign(&modifiedScript[pos], len);
          if (definedWords.find(word) != definedWords.end())
            UnDefineWord(word.c_str());
          else {
            std::string msg = "Unable to undefine " + std::string(word) + " it is not defined";
            engine->WriteMessage(sectionname, line, 0, asMSGTYPE_WARNING, msg.c_str());
          }
        } else {
          engine->WriteMessage(sectionname, line, 0, asMSGTYPE_ERROR, "Invalid value for #undef");
          return -1;
        }
        // Overwrite the #if directive with space characters to avoid compiler error
        pos += len;
        OverwriteCode(start, pos - start);
      }
    } else
      pos += len;
    line++;
  }

#if AS_PROCESS_METADATA == 1
  // Preallocate memory
  string name, declaration;
  vector<string> metadata;
  declaration.reserve(100);
#endif

  // Then check for meta data and pre-processor directives
  pos = 0;
  while (pos < modifiedScript.size()) {
    asUINT len = 0;
    asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
    if (t == asTC_COMMENT || t == asTC_WHITESPACE) {
      pos += len;
      continue;
    }
    string token;
    token.assign(&modifiedScript[pos], len);

#if AS_PROCESS_METADATA == 1
    // Skip possible decorators before class and interface declarations
    if (token == "shared" || token == "abstract" || token == "mixin" || token == "external") {
      pos += len;
      continue;
    }

    // Check if class or interface so the metadata for members can be gathered
    if (currentClass == "" && (token == "class" || token == "interface")) {
      // Get the identifier after "class"
      do {
        pos += len;
        if (pos >= modifiedScript.size()) {
          t = asTC_UNKNOWN;
          break;
        }
        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
      } while (t == asTC_COMMENT || t == asTC_WHITESPACE);

      if (t == asTC_IDENTIFIER) {
        currentClass = modifiedScript.substr(pos, len);

        // Search until first { or ; is encountered
        while (pos < modifiedScript.length()) {
          engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);

          // If start of class section encountered stop
          if (modifiedScript[pos] == '{') {
            pos += len;
            break;
          } else if (modifiedScript[pos] == ';') {
            // The class declaration has ended and there are no children
            currentClass = "";
            pos += len;
            break;
          }

          // Check next symbol
          pos += len;
        }
      }

      continue;
    }

    // Check if end of class
    if (currentClass != "" && token == "}") {
      currentClass = "";
      pos += len;
      continue;
    }

    // Check if namespace so the metadata for members can be gathered
    if (token == "namespace") {
      // Get the identifier after "namespace"
      do {
        pos += len;
        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
      } while (t == asTC_COMMENT || t == asTC_WHITESPACE);

      if (currentNamespace != "")
        currentNamespace += "::";
      currentNamespace += modifiedScript.substr(pos, len);

      // Search until first { is encountered
      while (pos < modifiedScript.length()) {
        engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);

        // If start of namespace section encountered stop
        if (modifiedScript[pos] == '{') {
          pos += len;
          break;
        }

        // Check next symbol
        pos += len;
      }

      continue;
    }

    // Check if end of namespace
    if (currentNamespace != "" && token == "}") {
      size_t found = currentNamespace.rfind("::");
      if (found != string::npos) {
        currentNamespace.erase(found);
      } else {
        currentNamespace = "";
      }
      pos += len;
      continue;
    }

    // Is this the start of metadata?
    if (token == "[") {
      // Get the metadata string
      pos = ExtractMetadata(pos, metadata);

      // Determine what this metadata is for
      int type;
      ExtractDeclaration(pos, name, declaration, type);

      // Store away the declaration in a map for lookup after the build has completed
      if (type > 0) {
        SMetadataDecl decl(metadata, name, declaration, type, currentClass, currentNamespace);
        foundDeclarations.push_back(decl);
      }
    } else
#endif
      // Is this a preprocessor directive?
      if (token == "#" && (pos + 1 < modifiedScript.size())) {
        int start = pos++;

        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        if (t == asTC_IDENTIFIER) {
          token.assign(&modifiedScript[pos], len);
          if (token == "include") {
            pos += len;
            t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
            if (t == asTC_WHITESPACE) {
              pos += len;
              t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
            }

            if (t == asTC_VALUE && len > 2 && (modifiedScript[pos] == '"' || modifiedScript[pos] == '\'')) {
              // Get the include file
              string includefile;
              includefile.assign(&modifiedScript[pos + 1], len - 2);
              pos += len;

              // Make sure the includeFile doesn't contain any line breaks
              size_t p = includefile.find('\n');
              if (p != string::npos) {
                // TODO: Show the correct line number for the error
                string str = "Invalid file name for #include; it contains a line-break: '" + includefile.substr(0, p) + "'";
                engine->WriteMessage(sectionname, 0, 0, asMSGTYPE_ERROR, str.c_str());
              } else {
                // Store it for later processing
                includes.push_back(includefile);

                // Overwrite the include directive with space characters to avoid compiler error
                OverwriteCode(start, pos - start);
              }
            }
          } else if (token == "pragma") {
            // Read until the end of the line
            pos += len;
            for (; pos < modifiedScript.size() && modifiedScript[pos] != '\n'; pos++)
              ;

            // Call the pragma callback
            string pragmaText(&modifiedScript[start + 7], pos - start - 7);
            int r = pragmaCallback ? pragmaCallback(pragmaText, *this, pragmaParam) : -1;
            if (r < 0) {
              // TODO: Report the correct line number
              engine->WriteMessage(sectionname, 0, 0, asMSGTYPE_ERROR, "Invalid #pragma directive");
              return r;
            }

            // Overwrite the pragma directive with space characters to avoid compiler error
            OverwriteCode(start, pos - start);
          }
        } else {
          // Check for lines starting with #!, e.g. shebang interpreter directive. These will be treated as comments and removed by the preprocessor
          if (modifiedScript[pos] == '!') {
            // Read until the end of the line
            pos += len;
            for (; pos < modifiedScript.size() && modifiedScript[pos] != '\n'; pos++)
              ;

            // Overwrite the directive with space characters to avoid compiler error
            OverwriteCode(start, pos - start);
          }
        }
      }
      // Don't search for metadata/includes within statement blocks or between tokens in statements
      else {
        pos = SkipStatement(pos);
      }
  }

  // Build the actual script
  engine->SetEngineProperty(asEP_COPY_SCRIPT_SECTIONS, true);
  module->AddScriptSection(sectionname, modifiedScript.c_str(), modifiedScript.size(), lineOffset);

  if (includes.size() > 0) {
    // If the callback has been set, then call it for each included file
    if (includeCallback) {
      for (int n = 0; n < (int)includes.size(); n++) {
        int r = includeCallback(includes[n].c_str(), sectionname, this, includeParam);
        if (r < 0)
          return r;
      }
    } else {
      // By default we try to load the included file from the relative directory of the current file

      // Determine the path of the current script so that we can resolve relative paths for includes
      string path = sectionname;
      size_t posOfSlash = path.find_last_of("/\\");
      if (posOfSlash != string::npos)
        path.resize(posOfSlash + 1);
      else
        path = "";

      // Load the included scripts
      for (int n = 0; n < (int)includes.size(); n++) {
        // If the include is a relative path, then prepend the path of the originating script
        if (includes[n].find_first_of("/\\") != 0 && includes[n].find_first_of(":") == string::npos) {
          includes[n] = path + includes[n];
        }

        // Include the script section
        int r = AddSectionFromFile(includes[n].c_str());
        if (r < 0)
          return r;
      }
    }
  }

  return 0;
}

int CScriptBuilder::Build() {
  int r = module->Build();
  if (r < 0)
    return r;

#if AS_PROCESS_METADATA == 1
  // After the script has been built, the metadata strings should be
  // stored for later lookup by function id, type id, and variable index
  for (int n = 0; n < (int)foundDeclarations.size(); n++) {
    SMetadataDecl* decl = &foundDeclarations[n];
    module->SetDefaultNamespace(decl->nameSpace.c_str());
    if (decl->type == MDT_TYPE) {
      // Find the type id
      int typeId = module->GetTypeIdByDecl(decl->declaration.c_str());
      assert(typeId >= 0);
      if (typeId >= 0)
        typeMetadataMap.insert(map<int, vector<string>>::value_type(typeId, decl->metadata));
    } else if (decl->type == MDT_FUNC) {
      if (decl->parentClass == "") {
        // Find the function id
        asIScriptFunction* func = module->GetFunctionByDecl(decl->declaration.c_str());
        assert(func);
        if (func)
          funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
      } else {
        // Find the method id
        int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str());
        assert(typeId > 0);
        map<int, SClassMetadata>::iterator it = classMetadataMap.find(typeId);
        if (it == classMetadataMap.end()) {
          classMetadataMap.insert(map<int, SClassMetadata>::value_type(typeId, SClassMetadata(decl->parentClass)));
          it = classMetadataMap.find(typeId);
        }

        asITypeInfo* type = engine->GetTypeInfoById(typeId);
        asIScriptFunction* func = type->GetMethodByDecl(decl->declaration.c_str());
        assert(func);
        if (func)
          it->second.funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
      }
    } else if (decl->type == MDT_VIRTPROP) {
      if (decl->parentClass == "") {
        // Find the global virtual property accessors
        asIScriptFunction* func = module->GetFunctionByName(("get_" + decl->declaration).c_str());
        if (func)
          funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
        func = module->GetFunctionByName(("set_" + decl->declaration).c_str());
        if (func)
          funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
      } else {
        // Find the method virtual property accessors
        int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str());
        assert(typeId > 0);
        map<int, SClassMetadata>::iterator it = classMetadataMap.find(typeId);
        if (it == classMetadataMap.end()) {
          classMetadataMap.insert(map<int, SClassMetadata>::value_type(typeId, SClassMetadata(decl->parentClass)));
          it = classMetadataMap.find(typeId);
        }

        asITypeInfo* type = engine->GetTypeInfoById(typeId);
        asIScriptFunction* func = type->GetMethodByName(("get_" + decl->declaration).c_str());
        if (func)
          it->second.funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
        func = type->GetMethodByName(("set_" + decl->declaration).c_str());
        if (func)
          it->second.funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
      }
    } else if (decl->type == MDT_VAR) {
      if (decl->parentClass == "") {
        // Find the global variable index
        int varIdx = module->GetGlobalVarIndexByName(decl->declaration.c_str());
        assert(varIdx >= 0);
        if (varIdx >= 0)
          varMetadataMap.insert(map<int, vector<string>>::value_type(varIdx, decl->metadata));
      } else {
        int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str());
        assert(typeId > 0);

        // Add the classes if needed
        map<int, SClassMetadata>::iterator it = classMetadataMap.find(typeId);
        if (it == classMetadataMap.end()) {
          classMetadataMap.insert(map<int, SClassMetadata>::value_type(typeId, SClassMetadata(decl->parentClass)));
          it = classMetadataMap.find(typeId);
        }

        // Add the variable to class
        asITypeInfo* objectType = engine->GetTypeInfoById(typeId);
        int idx = -1;

        // Search through all properties to get proper declaration
        for (asUINT i = 0; i < (asUINT)objectType->GetPropertyCount(); ++i) {
          const char* name;
          objectType->GetProperty(i, &name);
          if (decl->declaration == name) {
            idx = i;
            break;
          }
        }

        // If found, add it
        assert(idx >= 0);
        if (idx >= 0)
          it->second.varMetadataMap.insert(map<int, vector<string>>::value_type(idx, decl->metadata));
      }
    } else if (decl->type == MDT_FUNC_OR_VAR) {
      if (decl->parentClass == "") {
        // Find the global variable index
        int varIdx = module->GetGlobalVarIndexByName(decl->name.c_str());
        if (varIdx >= 0)
          varMetadataMap.insert(map<int, vector<string>>::value_type(varIdx, decl->metadata));
        else {
          asIScriptFunction* func = module->GetFunctionByDecl(decl->declaration.c_str());
          assert(func);
          if (func)
            funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
        }
      } else {
        int typeId = module->GetTypeIdByDecl(decl->parentClass.c_str());
        assert(typeId > 0);

        // Add the classes if needed
        map<int, SClassMetadata>::iterator it = classMetadataMap.find(typeId);
        if (it == classMetadataMap.end()) {
          classMetadataMap.insert(map<int, SClassMetadata>::value_type(typeId, SClassMetadata(decl->parentClass)));
          it = classMetadataMap.find(typeId);
        }

        // Add the variable to class
        asITypeInfo* objectType = engine->GetTypeInfoById(typeId);
        int idx = -1;

        // Search through all properties to get proper declaration
        for (asUINT i = 0; i < (asUINT)objectType->GetPropertyCount(); ++i) {
          const char* name;
          objectType->GetProperty(i, &name);
          if (decl->name == name) {
            idx = i;
            break;
          }
        }

        // If found, add it
        if (idx >= 0)
          it->second.varMetadataMap.insert(map<int, vector<string>>::value_type(idx, decl->metadata));
        else {
          // Look for the matching method instead
          asITypeInfo* type = engine->GetTypeInfoById(typeId);
          asIScriptFunction* func = type->GetMethodByDecl(decl->declaration.c_str());
          if (func)
            it->second.funcMetadataMap.insert(map<int, vector<string>>::value_type(func->GetId(), decl->metadata));
        }
      }
    }
  }
  module->SetDefaultNamespace("");
#endif

  return 0;
}

int CScriptBuilder::SkipStatement(int pos) {
  asUINT len = 0;

  // Skip until ; or { whichever comes first
  while (pos < (int)modifiedScript.length() && modifiedScript[pos] != ';' && modifiedScript[pos] != '{') {
    engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
    pos += len;
  }

  // Skip entire statement block
  if (pos < (int)modifiedScript.length() && modifiedScript[pos] == '{') {
    pos += 1;

    // Find the end of the statement block
    int level = 1;
    while (level > 0 && pos < (int)modifiedScript.size()) {
      asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
      if (t == asTC_KEYWORD) {
        if (modifiedScript[pos] == '{')
          level++;
        else if (modifiedScript[pos] == '}')
          level--;
      }

      pos += len;
    }
  } else
    pos += 1;

  return pos;
}

// Overwrite all code with blanks until the matching #endif
int CScriptBuilder::ExcludeCode(int pos) {
  asUINT len = 0;
  int nested = 0;
  while (pos < (int)modifiedScript.size()) {
    engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
    if (modifiedScript[pos] == '#') {
      modifiedScript[pos] = ' ';
      pos++;

      // Is it an #if or #endif directive?
      engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
      string token;
      token.assign(&modifiedScript[pos], len);
      OverwriteCode(pos, len);

      if (token == "if" || token == "ifndef" || token == "ifdef") {
        nested++;
      } else if (token == "endif") {
        if (nested-- == 0) {
          pos += len;
          break;
        }
      }
    } else if (modifiedScript[pos] != '\n') {
      OverwriteCode(pos, len);
    }
    pos += len;
  }

  return pos;
}

// Overwrite all characters except line breaks with blanks
void CScriptBuilder::OverwriteCode(int start, int len) {
  char* code = &modifiedScript[start];
  for (int n = 0; n < len; n++) {
    if (*code != '\n')
      *code = ' ';
    code++;
  }
}

#if AS_PROCESS_METADATA == 1
int CScriptBuilder::ExtractMetadata(int pos, vector<string>& metadata) {
  metadata.clear();

  // Extract all metadata. They can be separated by whitespace and comments
  for (;;) {
    string metadataString = "";

    // Overwrite the metadata with space characters to allow compilation
    modifiedScript[pos] = ' ';

    // Skip opening brackets
    pos += 1;

    int level = 1;
    asUINT len = 0;
    while (level > 0 && pos < (int)modifiedScript.size()) {
      asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
      if (t == asTC_KEYWORD) {
        if (modifiedScript[pos] == '[')
          level++;
        else if (modifiedScript[pos] == ']')
          level--;
      }

      // Copy the metadata to our buffer
      if (level > 0)
        metadataString.append(&modifiedScript[pos], len);

      // Overwrite the metadata with space characters to allow compilation
      if (t != asTC_WHITESPACE)
        OverwriteCode(pos, len);

      pos += len;
    }

    metadata.push_back(metadataString);

    // Check for more metadata. Possibly separated by comments
    asETokenClass t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
    while (t == asTC_COMMENT || t == asTC_WHITESPACE) {
      pos += len;
      t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
    }

    if (modifiedScript[pos] != '[')
      break;
  }

  return pos;
}

int CScriptBuilder::ExtractDeclaration(int pos, string& name, string& declaration, int& type) {
  declaration = "";
  type = 0;

  int start = pos;

  std::string token;
  asUINT len = 0;
  asETokenClass t = asTC_WHITESPACE;

  // Skip white spaces, comments, and leading decorators
  do {
    pos += len;
    t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
    token.assign(&modifiedScript[pos], len);
  } while (t == asTC_WHITESPACE || t == asTC_COMMENT || token == "private" || token == "protected" || token == "shared" || token == "external" || token == "final" || token == "abstract");

  // We're expecting, either a class, interface, function, or variable declaration
  if (t == asTC_KEYWORD || t == asTC_IDENTIFIER) {
    token.assign(&modifiedScript[pos], len);
    if (token == "interface" || token == "class" || token == "enum") {
      // Skip white spaces and comments
      do {
        pos += len;
        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
      } while (t == asTC_WHITESPACE || t == asTC_COMMENT);

      if (t == asTC_IDENTIFIER) {
        type = MDT_TYPE;
        declaration.assign(&modifiedScript[pos], len);
        pos += len;
        return pos;
      }
    } else {
      // For function declarations, store everything up to the start of the
      // statement block, except for succeeding decorators (final, override, etc)

      // For variable declaration store just the name as there can only be one

      // We'll only know if the declaration is a variable or function declaration
      // when we see the statement block, or absense of a statement block.
      bool hasParenthesis = false;
      int nestedParenthesis = 0;
      declaration.append(&modifiedScript[pos], len);
      pos += len;
      for (; pos < (int)modifiedScript.size();) {
        t = engine->ParseToken(&modifiedScript[pos], modifiedScript.size() - pos, &len);
        token.assign(&modifiedScript[pos], len);
        if (t == asTC_KEYWORD) {
          if (token == "{" && nestedParenthesis == 0) {
            if (hasParenthesis) {
              // We've found the end of a function signature
              type = MDT_FUNC;
            } else {
              // We've found a virtual property. Just keep the name
              declaration = name;
              type = MDT_VIRTPROP;
            }
            return pos;
          }
          if ((token == "=" && !hasParenthesis) || token == ";") {
            if (hasParenthesis) {
              // The declaration is ambigous. It can be a variable with initialization, or a function prototype
              type = MDT_FUNC_OR_VAR;
            } else {
              // Substitute the declaration with just the name
              declaration = name;
              type = MDT_VAR;
            }
            return pos;
          } else if (token == "(") {
            nestedParenthesis++;

            // This is the first parenthesis we encounter. If the parenthesis isn't followed
            // by a statement block, then this is a variable declaration, in which case we
            // should only store the type and name of the variable, not the initialization parameters.
            hasParenthesis = true;
          } else if (token == ")") {
            nestedParenthesis--;
          }
        } else if (t == asTC_IDENTIFIER) {
          name = token;
        }

        // Skip trailing decorators
        if (!hasParenthesis || nestedParenthesis > 0 || t != asTC_IDENTIFIER || (token != "final" && token != "override" && token != "delete" && token != "property"))
          declaration += token;

        pos += len;
      }
    }
  }

  return start;
}

vector<string> CScriptBuilder::GetMetadataForType(int typeId) {
  map<int, vector<string>>::iterator it = typeMetadataMap.find(typeId);
  if (it != typeMetadataMap.end())
    return it->second;

  return vector<string>();
}

vector<string> CScriptBuilder::GetMetadataForFunc(asIScriptFunction* func) {
  if (func) {
    map<int, vector<string>>::iterator it = funcMetadataMap.find(func->GetId());
    if (it != funcMetadataMap.end())
      return it->second;
  }

  return vector<string>();
}

vector<string> CScriptBuilder::GetMetadataForVar(int varIdx) {
  map<int, vector<string>>::iterator it = varMetadataMap.find(varIdx);
  if (it != varMetadataMap.end())
    return it->second;

  return vector<string>();
}

vector<string> CScriptBuilder::GetMetadataForTypeProperty(int typeId, int varIdx) {
  map<int, SClassMetadata>::iterator typeIt = classMetadataMap.find(typeId);
  if (typeIt == classMetadataMap.end())
    return vector<string>();

  map<int, vector<string>>::iterator propIt = typeIt->second.varMetadataMap.find(varIdx);
  if (propIt == typeIt->second.varMetadataMap.end())
    return vector<string>();

  return propIt->second;
}

vector<string> CScriptBuilder::GetMetadataForTypeMethod(int typeId, asIScriptFunction* method) {
  if (method) {
    map<int, SClassMetadata>::iterator typeIt = classMetadataMap.find(typeId);
    if (typeIt == classMetadataMap.end())
      return vector<string>();

    map<int, vector<string>>::iterator methodIt = typeIt->second.funcMetadataMap.find(method->GetId());
    if (methodIt == typeIt->second.funcMetadataMap.end())
      return vector<string>();

    return methodIt->second;
  }

  return vector<string>();
}
#endif

string GetAbsolutePath(const string& file) {
  string str = file;

  // If this is a relative path, complement it with the current path
  if (!((str.length() > 0 && (str[0] == '/' || str[0] == '\\')) || str.find(":") != string::npos)) {
    str = GetCurrentDir() + "/" + str;
  }

  // Replace backslashes for forward slashes
  size_t pos = 0;
  while ((pos = str.find("\\", pos)) != string::npos)
    str[pos] = '/';

  // Replace /./ with /
  pos = 0;
  while ((pos = str.find("/./", pos)) != string::npos)
    str.erase(pos + 1, 2);

  // For each /../ remove the parent dir and the /../
  pos = 0;
  while ((pos = str.find("/../")) != string::npos) {
    size_t pos2 = str.rfind("/", pos - 1);
    if (pos2 != string::npos)
      str.erase(pos2, pos + 3 - pos2);
    else {
      // The path is invalid
      break;
    }
  }

  return str;
}

string GetCurrentDir() {
  char buffer[1024];
#if defined(_MSC_VER) || defined(_WIN32)
#ifdef _WIN32_WCE
  static TCHAR apppath[MAX_PATH] = TEXT("");
  if (!apppath[0]) {
    GetModuleFileName(NULL, apppath, MAX_PATH);

    int appLen = _tcslen(apppath);

    // Look for the last backslash in the path, which would be the end
    // of the path itself and the start of the filename.  We only want
    // the path part of the exe's full-path filename
    // Safety is that we make sure not to walk off the front of the
    // array (in case the path is nothing more than a filename)
    while (appLen > 1) {
      if (apppath[appLen - 1] == TEXT('\\'))
        break;
      appLen--;
    }

    // Terminate the string after the trailing backslash
    apppath[appLen] = TEXT('\0');
  }
#ifdef _UNICODE
  wcstombs(buffer, apppath, min(1024, wcslen(apppath) * sizeof(wchar_t)));
#else
  memcpy(buffer, apppath, min(1024, strlen(apppath)));
#endif

  return buffer;
#elif defined(__S3E__)
  // Marmalade uses its own portable C library
  return getcwd(buffer, (int)1024);
#elif _XBOX_VER >= 200
  // XBox 360 doesn't support the getcwd function, just use the root folder
  return "game:/";
#elif defined(_M_ARM)
  // TODO: How to determine current working dir on Windows Phone?
  return "";
#else
  return _getcwd(buffer, (int)1024);
#endif // _MSC_VER
#elif defined(__APPLE__) || defined(__linux__)
  return getcwd(buffer, 1024);
#else
  return "";
#endif
}

END_AS_NAMESPACE


code:
#ifndef SCRIPTBUILDER_H
#define SCRIPTBUILDER_H

//---------------------------
// Compilation settings
//

// Set this flag to turn on/off metadata processing
//  0 = off
//  1 = on
#ifndef AS_PROCESS_METADATA
#define AS_PROCESS_METADATA 1
#endif

// TODO: Implement flags for turning on/off include directives and conditional programming

//---------------------------
// Declaration
//

#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif

#if defined(_MSC_VER) && _MSC_VER <= 1200
// disable the annoying warnings on MSVC 6
#pragma warning(disable : 4786)
#endif

#include <map>
#include <set>
#include <string.h> // _strcmpi
#include <string>
#include <vector>

BEGIN_AS_NAMESPACE

class CScriptBuilder;

// This callback will be called for each #include directive encountered by the
// builder. The callback should call the AddSectionFromFile or AddSectionFromMemory
// to add the included section to the script. If the include cannot be resolved
// then the function should return a negative value to abort the compilation.
typedef int (*INCLUDECALLBACK_t)(const char* include, const char* from, CScriptBuilder* builder, void* userParam);

// This callback will be called for each #pragma directive encountered by the builder.
// The application can interpret the pragmaText and decide what do to based on that.
// If the callback returns a negative value the builder will report an error and abort the compilation.
typedef int (*PRAGMACALLBACK_t)(const std::string& pragmaText, CScriptBuilder& builder, void* userParam);

// Helper class for loading and pre-processing script files to
// support include directives and metadata declarations
class CScriptBuilder {
public:
  CScriptBuilder();

  // Start a new module
  int StartNewModule(asIScriptEngine* engine, const char* moduleName);

  // Load a script section from a file on disk
  // Returns  1 if the file was included
  //          0 if the file had already been included before
  //         <0 on error
  int AddSectionFromFile(const char* filename);

  // Load a script section from memory
  // Returns  1 if the section was included
  //          0 if a section with the same name had already been included before
  //         <0 on error
  int AddSectionFromMemory(const char* sectionName, const char* scriptCode, unsigned int scriptLength = 0, int lineOffset = 0);

  // Build the added script sections
  int BuildModule();

  // Returns the engine
  asIScriptEngine* GetEngine();

  // Returns the current module
  asIScriptModule* GetModule();

  // Register the callback for resolving include directive
  void SetIncludeCallback(INCLUDECALLBACK_t callback, void* userParam);

  // Register the callback for resolving pragma directive
  void SetPragmaCallback(PRAGMACALLBACK_t callback, void* userParam);

  // Add a pre-processor define for conditional compilation
  void DefineWord(const char* word);
  // Remove a pre-processor define for conditional compilation
  void UnDefineWord(const char* word);

  // Enumerate included script sections
  unsigned int GetSectionCount() const;
  std::string GetSectionName(unsigned int idx) const;

#if AS_PROCESS_METADATA == 1
  // Get metadata declared for classes, interfaces, and enums
  std::vector<std::string> GetMetadataForType(int typeId);

  // Get metadata declared for functions
  std::vector<std::string> GetMetadataForFunc(asIScriptFunction* func);

  // Get metadata declared for global variables
  std::vector<std::string> GetMetadataForVar(int varIdx);

  // Get metadata declared for class variables
  std::vector<std::string> GetMetadataForTypeProperty(int typeId, int varIdx);

  // Get metadata declared for class methods
  std::vector<std::string> GetMetadataForTypeMethod(int typeId, asIScriptFunction* method);
#endif

protected:
  void ClearAll();
  int Build();
  int ProcessScriptSection(const char* script, unsigned int length, const char* sectionname, int lineOffset);
  int LoadScriptSection(const char* filename);
  bool IncludeIfNotAlreadyIncluded(const char* filename);

  int SkipStatement(int pos);

  int ExcludeCode(int start);
  void OverwriteCode(int start, int len);

  asIScriptEngine* engine;
  asIScriptModule* module;
  std::string modifiedScript;

  INCLUDECALLBACK_t includeCallback;
  void* includeParam;

  PRAGMACALLBACK_t pragmaCallback;
  void* pragmaParam;

#if AS_PROCESS_METADATA == 1
  int ExtractMetadata(int pos, std::vector<std::string>& outMetadata);
  int ExtractDeclaration(int pos, std::string& outName, std::string& outDeclaration, int& outType);

  enum METADATATYPE { MDT_TYPE = 1, MDT_FUNC = 2, MDT_VAR = 3, MDT_VIRTPROP = 4, MDT_FUNC_OR_VAR = 5 };

  // Temporary structure for storing metadata and declaration
  struct SMetadataDecl {
    SMetadataDecl(std::vector<std::string> m, std::string n, std::string d, int t, std::string c, std::string ns) : metadata(m), name(n), declaration(d), type(t), parentClass(c), nameSpace(ns) {}
    std::vector<std::string> metadata;
    std::string name;
    std::string declaration;
    int type;
    std::string parentClass;
    std::string nameSpace;
  };
  std::vector<SMetadataDecl> foundDeclarations;
  std::string currentClass;
  std::string currentNamespace;

  // Storage of metadata for global declarations
  std::map<int, std::vector<std::string>> typeMetadataMap;
  std::map<int, std::vector<std::string>> funcMetadataMap;
  std::map<int, std::vector<std::string>> varMetadataMap;

  // Storage of metadata for class member declarations
  struct SClassMetadata {
    SClassMetadata(const std::string& aName) : className(aName) {}
    std::string className;
    std::map<int, std::vector<std::string>> funcMetadataMap;
    std::map<int, std::vector<std::string>> varMetadataMap;
  };
  std::map<int, SClassMetadata> classMetadataMap;

#endif

#ifdef _WIN32
  // On Windows the filenames are case insensitive so the comparisons to
  // avoid duplicate includes must also be case insensitive. True case insensitive
  // is not easy as it must be language aware, but a simple implementation such
  // as strcmpi should suffice in almost all cases.
  //
  // ref: http://www.gotw.ca/gotw/029.htm
  // ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317761(v=vs.85).aspx
  // ref: http://site.icu-project.org/

  // TODO: Strings by default are treated as UTF8 encoded. If the application choses to
  //       use a different encoding, the comparison algorithm should be adjusted as well

  struct ci_less {
    bool operator()(const std::string& a, const std::string& b) const { return _stricmp(a.c_str(), b.c_str()) < 0; }
  };
  std::set<std::string, ci_less> includedScripts;
#else
  std::set<std::string> includedScripts;
#endif

  std::set<std::string> definedWords;
};

END_AS_NAMESPACE

#endif

repo_name: Antidote/openrpgeditor

license: mit


code:
#include <assert.h>
#include <string.h>
#include "scriptdictionary.h"
#include "../scriptarray/scriptarray.h"

BEGIN_AS_NAMESPACE

using namespace std;

//------------------------------------------------------------------------
// Object types are cached as user data to avoid costly runtime lookups

// We just define a number here that we assume nobody else is using for
// object type user data. The add-ons have reserved the numbers 1000
// through 1999 for this purpose, so we should be fine.
const asPWORD DICTIONARY_CACHE = 1003;

// This cache holds the object type of the dictionary type and array type
// so it isn't necessary to look this up each time the dictionary or array
// is created.
struct SDictionaryCache
{
	asITypeInfo *dictType;
	asITypeInfo *arrayType;
	asITypeInfo *keyType;

	// This is called from RegisterScriptDictionary
	static void Setup(asIScriptEngine *engine)
	{
		SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
		if( cache == 0 )
		{
			cache = new SDictionaryCache;
			engine->SetUserData(cache, DICTIONARY_CACHE);
			engine->SetEngineUserDataCleanupCallback(SDictionaryCache::Cleanup, DICTIONARY_CACHE);

			cache->dictType = engine->GetTypeInfoByName("dictionary");
			cache->arrayType = engine->GetTypeInfoByDecl("array<string>");
			cache->keyType = engine->GetTypeInfoByDecl("string");
		}
	}

	// This is called from the engine when shutting down
	static void Cleanup(asIScriptEngine *engine)
	{
		SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
		if( cache )
			delete cache;
	}
};

//--------------------------------------------------------------------------
// CScriptDictionary implementation

CScriptDictionary *CScriptDictionary::Create(asIScriptEngine *engine)
{
	// Use the custom memory routine from AngelScript to allow application to better control how much memory is used
	CScriptDictionary *obj = (CScriptDictionary*)asAllocMem(sizeof(CScriptDictionary));
	new(obj) CScriptDictionary(engine);
	return obj;
}

CScriptDictionary *CScriptDictionary::Create(asBYTE *buffer)
{
	// Use the custom memory routine from AngelScript to allow application to better control how much memory is used
	CScriptDictionary *obj = (CScriptDictionary*)asAllocMem(sizeof(CScriptDictionary));
	new(obj) CScriptDictionary(buffer);
	return obj;
}

CScriptDictionary::CScriptDictionary(asIScriptEngine *engine)
{
	Init(engine);
}

void CScriptDictionary::Init(asIScriptEngine *e)
{
	// We start with one reference
	refCount = 1;
	gcFlag = false;
	iterGuard = 0;

	// Keep a reference to the engine for as long as we live
	// We don't increment the reference counter, because the
	// engine will hold a pointer to the object in the GC.
	engine = e;

	// The dictionary object type is cached to avoid dynamically parsing it each time
	SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));

	// Notify the garbage collector of this object
	engine->NotifyGarbageCollectorOfNewObject(this, cache->dictType);
}

CScriptDictionary::CScriptDictionary(asBYTE *buffer)
{
	// This constructor will always be called from a script
	// so we can get the engine from the active context
	asIScriptContext *ctx = asGetActiveContext();
	Init(ctx->GetEngine());

	// Determine if the dictionary key type is registered as reference type or value type
	SDictionaryCache& cache = *reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
	bool keyAsRef = cache.keyType->GetFlags() & asOBJ_REF ? true : false;

	// Initialize the dictionary from the buffer
	asUINT length = *(asUINT*)buffer;
	buffer += 4;

	while( length-- )
	{
		// Align the buffer pointer on a 4 byte boundary in
		// case previous value was smaller than 4 bytes
		if( asPWORD(buffer) & 0x3 )
			buffer += 4 - (asPWORD(buffer) & 0x3);

		// Get the name value pair from the buffer and insert it in the dictionary
		dictKey_t name;
		if (keyAsRef)
		{
			name = **(dictKey_t**)buffer;
			buffer += sizeof(dictKey_t*);
		}
		else
		{
			name = *(dictKey_t*)buffer;
			buffer += sizeof(dictKey_t);
		}

		// Get the type id of the value
		int typeId = *(int*)buffer;
		buffer += sizeof(int);

		// Depending on the type id, the value will inline in the buffer or a pointer
		void *ref = (void*)buffer;

		if( typeId >= asTYPEID_INT8 && typeId <= asTYPEID_DOUBLE )
		{
			// Convert primitive values to either int64 or double, so we can use the overloaded Set methods
			asINT64 i64;
			double d;
			switch( typeId )
			{
			case asTYPEID_INT8:   i64 = *(char*)          ref; break;
			case asTYPEID_INT16:  i64 = *(short*)         ref; break;
			case asTYPEID_INT32:  i64 = *(int*)           ref; break;
			case asTYPEID_INT64:  i64 = *(asINT64*)       ref; break;
			case asTYPEID_UINT8:  i64 = *(unsigned char*) ref; break;
			case asTYPEID_UINT16: i64 = *(unsigned short*)ref; break;
			case asTYPEID_UINT32: i64 = *(unsigned int*)  ref; break;
			case asTYPEID_UINT64: i64 = *(asINT64*)       ref; break;
			case asTYPEID_FLOAT:  d   = *(float*)         ref; break;
			case asTYPEID_DOUBLE: d   = *(double*)        ref; break;
			}

			if( typeId >= asTYPEID_FLOAT )
				Set(name, d);
			else
				Set(name, i64);
		}
		else
		{
			if( (typeId & asTYPEID_MASK_OBJECT) &&
				!(typeId & asTYPEID_OBJHANDLE) &&
				(engine->GetTypeInfoById(typeId)->GetFlags() & asOBJ_REF) )
			{
				// Dereference the pointer to get the reference to the actual object
				ref = *(void**)ref;
			}

			Set(name, ref, typeId);
		}

		// Advance the buffer pointer with the size of the value
		if( typeId & asTYPEID_MASK_OBJECT )
		{
			asITypeInfo *ti = engine->GetTypeInfoById(typeId);
			if( ti->GetFlags() & asOBJ_VALUE )
				buffer += ti->GetSize();
			else
				buffer += sizeof(void*);
		}
		else if( typeId == 0 )
		{
			// null pointer
			buffer += sizeof(void*);
		}
		else
		{
			buffer += engine->GetSizeOfPrimitiveType(typeId);
		}
	}
}

CScriptDictionary::~CScriptDictionary()
{
	// Delete all keys and values
	DeleteAll();
}

void CScriptDictionary::AddRef() const
{
	// We need to clear the GC flag
	gcFlag = false;
	asAtomicInc(refCount);
}

void CScriptDictionary::Release() const
{
	// We need to clear the GC flag
	gcFlag = false;
	if( asAtomicDec(refCount) == 0 )
	{
		this->~CScriptDictionary();
		asFreeMem(const_cast<CScriptDictionary*>(this));
	}
}

int CScriptDictionary::GetRefCount()
{
	return refCount;
}

void CScriptDictionary::SetGCFlag()
{
	gcFlag = true;
}

bool CScriptDictionary::GetGCFlag()
{
	return gcFlag;
}

void CScriptDictionary::EnumReferences(asIScriptEngine *inEngine)
{
	// TODO: If garbage collection can be done from a separate thread, then this method must be
	//       protected so that it doesn't get lost during the iteration if the dictionary is modified

	// Call the gc enum callback for each of the objects
	dictMap_t::iterator it;
	for( it = dict.begin(); it != dict.end(); it++ )
	{
		if (it->second.m_typeId & asTYPEID_MASK_OBJECT)
		{
			asITypeInfo *subType = engine->GetTypeInfoById(it->second.m_typeId);
			if ((subType->GetFlags() & asOBJ_VALUE) && (subType->GetFlags() & asOBJ_GC))
			{
				// For value types we need to forward the enum callback
				// to the object so it can decide what to do
				engine->ForwardGCEnumReferences(it->second.m_valueObj, subType);
			}
			else
			{
				// For others, simply notify the GC about the reference
				inEngine->GCEnumCallback(it->second.m_valueObj);
			}
		}
	}
}

void CScriptDictionary::ReleaseAllReferences(asIScriptEngine * /*engine*/)
{
	// We're being told to release all references in
	// order to break circular references for dead objects
	DeleteAll();
}

CScriptDictionary &CScriptDictionary::operator =(const CScriptDictionary &other)
{
	// Clear everything we had before
	DeleteAll();

	// Do a shallow copy of the dictionary
	dictMap_t::const_iterator it;
	for( it = other.dict.begin(); it != other.dict.end(); it++ )
	{
		if( it->second.m_typeId & asTYPEID_OBJHANDLE )
			Set(it->first, (void*)const_cast<void**>(&it->second.m_valueObj), it->second.m_typeId);
		else if( it->second.m_typeId & asTYPEID_MASK_OBJECT )
			Set(it->first, (void*)it->second.m_valueObj, it->second.m_typeId);
		else
			Set(it->first, (void*)const_cast<asINT64*>(&it->second.m_valueInt), it->second.m_typeId);
	}

	return *this;
}

CScriptDictValue *CScriptDictionary::operator[](const dictKey_t &key)
{
	// Return the existing value if it exists, else insert an empty value
	return &dict[key];
}

const CScriptDictValue *CScriptDictionary::operator[](const dictKey_t &key) const
{
	// Return the existing value if it exists
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return &it->second;

	// Else raise an exception
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
		ctx->SetException("Invalid access to non-existing value");

	return 0;
}

void CScriptDictionary::Set(const dictKey_t &key, void *value, int typeId)
{
	dictMap_t::iterator it;
	it = dict.find(key);
	if (it == dict.end())
	{
		it = dict.insert(dictMap_t::value_type(key, CScriptDictValue())).first;
		iterGuard++;
	}

	it->second.Set(engine, value, typeId);
}

// This overloaded method is implemented so that all integer and
// unsigned integers types will be stored in the dictionary as int64
// through implicit conversions. This simplifies the management of the
// numeric types when the script retrieves the stored value using a
// different type.
void CScriptDictionary::Set(const dictKey_t &key, const asINT64 &value)
{
	Set(key, const_cast<asINT64*>(&value), asTYPEID_INT64);
}

// This overloaded method is implemented so that all floating point types
// will be stored in the dictionary as double through implicit conversions.
// This simplifies the management of the numeric types when the script
// retrieves the stored value using a different type.
void CScriptDictionary::Set(const dictKey_t &key, const double &value)
{
	Set(key, const_cast<double*>(&value), asTYPEID_DOUBLE);
}

// Returns true if the value was successfully retrieved
bool CScriptDictionary::Get(const dictKey_t &key, void *value, int typeId) const
{
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return it->second.Get(engine, value, typeId);

	// AngelScript has already initialized the value with a default value,
	// so we don't have to do anything if we don't find the element, or if
	// the element is incompatible with the requested type.

	return false;
}

// Returns the type id of the stored value
int CScriptDictionary::GetTypeId(const dictKey_t &key) const
{
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return it->second.m_typeId;

	return -1;
}

bool CScriptDictionary::Get(const dictKey_t &key, asINT64 &value) const
{
	return Get(key, &value, asTYPEID_INT64);
}

bool CScriptDictionary::Get(const dictKey_t &key, double &value) const
{
	return Get(key, &value, asTYPEID_DOUBLE);
}

bool CScriptDictionary::Exists(const dictKey_t &key) const
{
	dictMap_t::const_iterator it;
	it = dict.find(key);
	if( it != dict.end() )
		return true;

	return false;
}

bool CScriptDictionary::IsEmpty() const
{
	if( dict.size() == 0 )
		return true;

	return false;
}

asUINT CScriptDictionary::GetSize() const
{
	return asUINT(dict.size());
}

bool CScriptDictionary::Delete(const dictKey_t &key)
{
	dictMap_t::iterator it;
	it = dict.find(key);
	if( it != dict.end() )
	{
		it->second.FreeValue(engine);
		dict.erase(it);
		iterGuard++;
		return true;
	}

	return false;
}

void CScriptDictionary::DeleteAll()
{
	dictMap_t::iterator it;
	for( it = dict.begin(); it != dict.end(); it++ )
		it->second.FreeValue(engine);

	dict.clear();
	iterGuard++;
}

CScriptArray* CScriptDictionary::GetKeys() const
{
	// Retrieve the object type for the array<string> from the cache
	SDictionaryCache *cache = reinterpret_cast<SDictionaryCache*>(engine->GetUserData(DICTIONARY_CACHE));
	asITypeInfo *ti = cache->arrayType;

	// Create the array object
	CScriptArray *array = CScriptArray::Create(ti, asUINT(dict.size()));
	long current = -1;
	dictMap_t::const_iterator it;
	for( it = dict.begin(); it != dict.end(); it++ )
	{
		current++;
		*(dictKey_t*)array->At(current) = it->first;
	}

	return array;
}

//--------------------------------------------------------------------------
// Generic wrappers

void ScriptDictionaryFactory_Generic(asIScriptGeneric *gen)
{
	*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = CScriptDictionary::Create(gen->GetEngine());
}

void ScriptDictionaryListFactory_Generic(asIScriptGeneric *gen)
{
	asBYTE *buffer = (asBYTE*)gen->GetArgAddress(0);
	*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = CScriptDictionary::Create(buffer);
}

void ScriptDictionaryAddRef_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dict->AddRef();
}

void ScriptDictionaryRelease_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dict->Release();
}

void ScriptDictionaryAssign_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	CScriptDictionary *other = *(CScriptDictionary**)gen->GetAddressOfArg(0);
	*dict = *other;
	*(CScriptDictionary**)gen->GetAddressOfReturnLocation() = dict;
}

void ScriptDictionarySet_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	int typeId = gen->GetArgTypeId(1);
	dict->Set(*key, ref, typeId);
}

void ScriptDictionarySetInt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	dict->Set(*key, *(asINT64*)ref);
}

void ScriptDictionarySetFlt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	dict->Set(*key, *(double*)ref);
}

void ScriptDictionaryGet_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	int typeId = gen->GetArgTypeId(1);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, ref, typeId);
}

void ScriptDictionaryGetInt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(asINT64*)ref);
}

void ScriptDictionaryGetFlt_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	void *ref = *(void**)gen->GetAddressOfArg(1);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Get(*key, *(double*)ref);
}

void ScriptDictionaryExists_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	bool ret = dict->Exists(*key);
	*(bool*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptDictionaryIsEmpty_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	bool ret = dict->IsEmpty();
	*(bool*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptDictionaryGetSize_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	asUINT ret = dict->GetSize();
	*(asUINT*)gen->GetAddressOfReturnLocation() = ret;
}

void ScriptDictionaryDelete_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	*(bool*)gen->GetAddressOfReturnLocation() = dict->Delete(*key);
}

void ScriptDictionaryDeleteAll_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *dict = (CScriptDictionary*)gen->GetObject();
	dict->DeleteAll();
}

static void ScriptDictionaryGetRefCount_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}

static void ScriptDictionarySetGCFlag_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	self->SetGCFlag();
}

static void ScriptDictionaryGetGCFlag_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	*(bool*)gen->GetAddressOfReturnLocation() = self->GetGCFlag();
}

static void ScriptDictionaryEnumReferences_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->EnumReferences(engine);
}

static void ScriptDictionaryReleaseAllReferences_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
	self->ReleaseAllReferences(engine);
}

static void CScriptDictionaryGetKeys_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	*(CScriptArray**)gen->GetAddressOfReturnLocation() = self->GetKeys();
}

static void CScriptDictionary_opIndex_Generic(asIScriptGeneric *gen)
{
	CScriptDictionary *self = (CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = self->operator[](*key);
}

static void CScriptDictionary_opIndex_const_Generic(asIScriptGeneric *gen)
{
	const CScriptDictionary *self = (const CScriptDictionary*)gen->GetObject();
	dictKey_t *key = *(dictKey_t**)gen->GetAddressOfArg(0);
	*(const CScriptDictValue**)gen->GetAddressOfReturnLocation() = self->operator[](*key);
}

//-------------------------------------------------------------------------
// CScriptDictValue

CScriptDictValue::CScriptDictValue()
{
	m_valueObj = 0;
	m_typeId   = 0;
}

CScriptDictValue::CScriptDictValue(asIScriptEngine *engine, void *value, int typeId)
{
	m_valueObj = 0;
	m_typeId   = 0;
	Set(engine, value, typeId);
}

CScriptDictValue::~CScriptDictValue()
{
	if (m_valueObj && m_typeId)
	{
		asIScriptContext *ctx = asGetActiveContext();
		if (ctx)
			FreeValue(ctx->GetEngine());
		else
		{
			// Must not hold an object when destroyed, as then the object will never be freed
			assert((m_typeId & asTYPEID_MASK_OBJECT) == 0);
		}
	}
}

void CScriptDictValue::FreeValue(asIScriptEngine *engine)
{
	// If it is a handle or a ref counted object, call release
	if( m_typeId & asTYPEID_MASK_OBJECT )
	{
		// Let the engine release the object
		engine->ReleaseScriptObject(m_valueObj, engine->GetTypeInfoById(m_typeId));
		m_valueObj = 0;
		m_typeId = 0;
	}

	// For primitives, there's nothing to do
}

void CScriptDictValue::EnumReferences(asIScriptEngine *inEngine)
{
	// If we're holding a reference, we'll notify the garbage collector of it
	if (m_valueObj)
		inEngine->GCEnumCallback(m_valueObj);

	// The object type itself is also garbage collected
	if (m_typeId)
		inEngine->GCEnumCallback(inEngine->GetTypeInfoById(m_typeId));
}

void CScriptDictValue::Set(asIScriptEngine *engine, void *value, int typeId)
{
	FreeValue(engine);

	m_typeId = typeId;
	if( typeId & asTYPEID_OBJHANDLE )
	{
		// We're receiving a reference to the handle, so we need to dereference it
		m_valueObj = *(void**)value;
		engine->AddRefScriptObject(m_valueObj, engine->GetTypeInfoById(typeId));
	}
	else if( typeId & asTYPEID_MASK_OBJECT )
	{
		// Create a copy of the object
		m_valueObj = engine->CreateScriptObjectCopy(value, engine->GetTypeInfoById(typeId));
		if( m_valueObj == 0 )
		{
			asIScriptContext *ctx = asGetActiveContext();
			if( ctx )
				ctx->SetException("Cannot create copy of object");
		}
	}
	else
	{
		// Copy the primitive value
		// We receive a pointer to the value.
		int size = engine->GetSizeOfPrimitiveType(typeId);
		memcpy(&m_valueInt, value, size);
	}
}

void CScriptDictValue::Set(asIScriptEngine *engine, CScriptDictValue &value)
{
	if( value.m_typeId & asTYPEID_OBJHANDLE )
		Set(engine, (void*)&value.m_valueObj, value.m_typeId);
	else if( value.m_typeId & asTYPEID_MASK_OBJECT )
		Set(engine, (void*)value.m_valueObj, value.m_typeId);
	else
		Set(engine, (void*)&value.m_valueInt, value.m_typeId);
}

// This overloaded method is implemented so that all integer and
// unsigned integers types will be stored in the dictionary as int64
// through implicit conversions. This simplifies the management of the
// numeric types when the script retrieves the stored value using a
// different type.
void CScriptDictValue::Set(asIScriptEngine *engine, const asINT64 &value)
{
	Set(engine, const_cast<asINT64*>(&value), asTYPEID_INT64);
}

// This overloaded method is implemented so that all floating point types
// will be stored in the dictionary as double through implicit conversions.
// This simplifies the management of the numeric types when the script
// retrieves the stored value using a different type.
void CScriptDictValue::Set(asIScriptEngine *engine, const double &value)
{
	Set(engine, const_cast<double*>(&value), asTYPEID_DOUBLE);
}

bool CScriptDictValue::Get(asIScriptEngine *engine, void *value, int typeId) const
{
	// Return the value
	if( typeId & asTYPEID_OBJHANDLE )
	{
		// A handle can be retrieved if the stored type is a handle of same or compatible type
		// or if the stored type is an object that implements the interface that the handle refer to.
		if( (m_typeId & asTYPEID_MASK_OBJECT) )
		{
			// Don't allow the get if the stored handle is to a const, but the desired handle is not
			if( (m_typeId & asTYPEID_HANDLETOCONST) && !(typeId & asTYPEID_HANDLETOCONST) )
				return false;

			// RefCastObject will increment the refcount if successful
			engine->RefCastObject(m_valueObj, engine->GetTypeInfoById(m_typeId), engine->GetTypeInfoById(typeId), reinterpret_cast<void**>(value));

			return true;
		}
	}
	else if( typeId & asTYPEID_MASK_OBJECT )
	{
		// Verify that the copy can be made
		bool isCompatible = false;

		// Allow a handle to be value assigned if the wanted type is not a handle
		if( (m_typeId & ~(asTYPEID_OBJHANDLE | asTYPEID_HANDLETOCONST) ) == typeId && m_valueObj != 0 )
			isCompatible = true;

		// Copy the object into the given reference
		if( isCompatible )
		{
			engine->AssignScriptObject(value, m_valueObj, engine->GetTypeInfoById(typeId));

			return true;
		}
	}
	else
	{
		if( m_typeId == typeId )
		{
			int size = engine->GetSizeOfPrimitiveType(typeId);
			memcpy(value, &m_valueInt, size);
			return true;
		}

		// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
		// Only bool and enums needs to be treated separately
		if( typeId == asTYPEID_DOUBLE )
		{
			if( m_typeId == asTYPEID_INT64 )
				*(double*)value = double(m_valueInt);
			else if (m_typeId == asTYPEID_BOOL)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				char localValue;
				memcpy(&localValue, &m_valueInt, sizeof(char));
				*(double*)value = localValue ? 1.0 : 0.0;
			}
			else if (m_typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				int localValue;
				memcpy(&localValue, &m_valueInt, sizeof(int));
				*(double*)value = double(localValue); // enums are 32bit
			}
			else
			{
				// The stored type is an object
				// TODO: Check if the object has a conversion operator to a primitive value
				*(double*)value = 0;
				return false;
			}
			return true;
		}
		else if( typeId == asTYPEID_INT64 )
		{
			if( m_typeId == asTYPEID_DOUBLE )
				*(asINT64*)value = asINT64(m_valueFlt);
			else if (m_typeId == asTYPEID_BOOL)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				char localValue;
				memcpy(&localValue, &m_valueInt, sizeof(char));
				*(asINT64*)value = localValue ? 1 : 0;
			}
			else if (m_typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				int localValue;
				memcpy(&localValue, &m_valueInt, sizeof(int));
				*(asINT64*)value = localValue; // enums are 32bit
			}
			else
			{
				// The stored type is an object
				// TODO: Check if the object has a conversion operator to a primitive value
				*(asINT64*)value = 0;
				return false;
			}
			return true;
		}
		else if( typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0 )
		{
			// The desired type is an enum. These are always 32bit integers
			if( m_typeId == asTYPEID_DOUBLE )
				*(int*)value = int(m_valueFlt);
			else if( m_typeId == asTYPEID_INT64 )
				*(int*)value = int(m_valueInt);
			else if (m_typeId == asTYPEID_BOOL)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				char localValue;
				memcpy(&localValue, &m_valueInt, sizeof(char));
				*(int*)value = localValue ? 1 : 0;
			}
			else if (m_typeId > asTYPEID_DOUBLE && (m_typeId & asTYPEID_MASK_OBJECT) == 0)
			{
				// Use memcpy instead of type cast to make sure the code is endianess agnostic
				int localValue;
				memcpy(&localValue, &m_valueInt, sizeof(int));
				*(int*)value = localValue; // enums are 32bit
			}
			else
			{
				// The stored type is an object
				// TODO: Check if the object has a conversion operator to a primitive value
				*(int*)value = 0;
				return false;
			}
			return true;
		}
		else if( typeId == asTYPEID_BOOL )
		{
			if (m_typeId & asTYPEID_OBJHANDLE)
			{
				// TODO: Check if the object has a conversion operator to a primitive value
				*(bool*)value = m_valueObj ? true : false;
			}
			else if( m_typeId & asTYPEID_MASK_OBJECT )
			{
				// TODO: Check if the object has a conversion operator to a primitive value
				*(bool*)value = true;
			}
			else
			{
				// Compare only the bytes that were actually set
				asQWORD zero = 0;
				int size = engine->GetSizeOfPrimitiveType(m_typeId);
				*(bool*)value = memcmp(&m_valueInt, &zero, size) == 0 ? false : true;
			}
			return true;
		}
	}

	// It was not possible to retrieve the value using the desired typeId
	return false;
}

const void * CScriptDictValue::GetAddressOfValue() const
{
	if( (m_typeId & asTYPEID_MASK_OBJECT) && !(m_typeId & asTYPEID_OBJHANDLE) )
	{
		// Return the address to the object directly
		return m_valueObj;
	}

	// Return the address of the primitive or the pointer to the object
	return reinterpret_cast<const void*>(&m_valueObj);
}

bool CScriptDictValue::Get(asIScriptEngine *engine, asINT64 &value) const
{
	return Get(engine, &value, asTYPEID_INT64);
}

bool CScriptDictValue::Get(asIScriptEngine *engine, double &value) const
{
	return Get(engine, &value, asTYPEID_DOUBLE);
}

int CScriptDictValue::GetTypeId() const
{
	return m_typeId;
}

static void CScriptDictValue_Construct(void *mem)
{
	new(mem) CScriptDictValue();
}

static void CScriptDictValue_Destruct(CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->FreeValue(engine);
	}
	obj->~CScriptDictValue();
}

static CScriptDictValue &CScriptDictValue_opAssign(void *ref, int typeId, CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->Set(engine, ref, typeId);
	}
	return *obj;
}

static CScriptDictValue &CScriptDictValue_opAssign(const CScriptDictValue &other, CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->Set(engine, const_cast<CScriptDictValue&>(other));
	}

	return *obj;
}

static CScriptDictValue &CScriptDictValue_opAssign(double val, CScriptDictValue *obj)
{
	return CScriptDictValue_opAssign(&val, asTYPEID_DOUBLE, obj);
}

static CScriptDictValue &CScriptDictValue_opAssign(asINT64 val, CScriptDictValue *obj)
{
	return CScriptDictValue_opAssign(&val, asTYPEID_INT64, obj);
}

static void CScriptDictValue_opCast(void *ref, int typeId, CScriptDictValue *obj)
{
	asIScriptContext *ctx = asGetActiveContext();
	if( ctx )
	{
		asIScriptEngine *engine = ctx->GetEngine();
		obj->Get(engine, ref, typeId);
	}
}

static asINT64 CScriptDictValue_opConvInt(CScriptDictValue *obj)
{
	asINT64 value;
	CScriptDictValue_opCast(&value, asTYPEID_INT64, obj);
	return value;
}

static double CScriptDictValue_opConvDouble(CScriptDictValue *obj)
{
	double value;
	CScriptDictValue_opCast(&value, asTYPEID_DOUBLE, obj);
	return value;
}

//-------------------------------------------------------------------
// generic wrapper for CScriptDictValue

static void CScriptDictValue_opConvDouble_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	double value;
	self->Get(gen->GetEngine(), value);
	*(double*)gen->GetAddressOfReturnLocation() = value;
}

static void CScriptDictValue_opConvInt_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	asINT64 value;
	self->Get(gen->GetEngine(), value);
	*(asINT64*)gen->GetAddressOfReturnLocation() = value;
}

static void CScriptDictValue_opCast_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	self->Get(gen->GetEngine(), gen->GetArgAddress(0), gen->GetArgTypeId(0));
}

static void CScriptDictValue_opAssign_int64_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign((asINT64)gen->GetArgQWord(0), self);
}

static void CScriptDictValue_opAssign_double_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign(gen->GetArgDouble(0), self);
}

static void CScriptDictValue_opAssign_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign(gen->GetArgAddress(0), gen->GetArgTypeId(0), self);
}

static void CScriptDictValue_opCopyAssign_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	*(CScriptDictValue**)gen->GetAddressOfReturnLocation() = &CScriptDictValue_opAssign(*reinterpret_cast<CScriptDictValue*>(gen->GetArgAddress(0)), self);
}

static void CScriptDictValue_Construct_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	CScriptDictValue_Construct(self);
}

static void CScriptDictValue_Destruct_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	CScriptDictValue_Destruct(self);
}

static void CScriptDictValue_EnumReferences_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	self->EnumReferences(gen->GetEngine());
}

static void CScriptDictValue_FreeValue_Generic(asIScriptGeneric *gen)
{
	CScriptDictValue *self = (CScriptDictValue*)gen->GetObject();
	self->FreeValue(gen->GetEngine());
}

//----------------------------------------------------------------------------
// Foreach support
CScriptDictionary::CScriptDictIter::CScriptDictIter(const CScriptDictionary* dict) : iter(dict->begin()), refCount(1), iterGuard(dict->iterGuard) {}
CScriptDictionary::CScriptDictIter::~CScriptDictIter() {}

void CScriptDictionary::CScriptDictIter::AddRef() const
{
	asAtomicInc(refCount);
}

void CScriptDictionary::CScriptDictIter::Release() const
{
	if (asAtomicDec(refCount) == 0)
	{
		this->~CScriptDictIter();
		asFreeMem(const_cast<CScriptDictIter*>(this));
	}
}

CScriptDictionary::CScriptDictIter* CScriptDictionary::opForBegin() const
{
	// Use the custom memory routine from AngelScript to allow application to better control how much memory is used
	CScriptDictionary::CScriptDictIter* iter = (CScriptDictionary::CScriptDictIter*)asAllocMem(sizeof(CScriptDictionary::CScriptDictIter));
	new(iter) CScriptDictionary::CScriptDictIter(this);
	return iter;
}

bool CScriptDictionary::opForEnd(const CScriptDictionary::CScriptDictIter& iter) const
{
	if (iter.iterGuard != iterGuard)
		return true;

	if (iter.iter == end())
		return true;

	return false;
}

CScriptDictionary::CScriptDictIter* CScriptDictionary::opForNext(CScriptDictionary::CScriptDictIter& iter) const
{
	if (iter.iterGuard != iterGuard)
		iter.iter = end();
	else
		++iter.iter;
	return &iter;
}

const CScriptDictValue& CScriptDictionary::opForValue0(const CScriptDictionary::CScriptDictIter& iter) const
{
	return iter.iter.m_it->second;
}

const std::string& CScriptDictionary::opForValue1(const CScriptDictionary::CScriptDictIter& iter) const
{
	return iter.iter.m_it->first;
}

//--------------------------------------------------------------------------
// Register the type

void RegisterScriptDictionary(asIScriptEngine *engine)
{
	if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
		RegisterScriptDictionary_Generic(engine);
	else
		RegisterScriptDictionary_Native(engine);
}

void RegisterScriptDictionary_Native(asIScriptEngine *engine)
{
	int r;

	// The array<string> type must be available
	assert( engine->GetTypeInfoByDecl("array<string>") );

#if AS_CAN_USE_CPP11
	// With C++11 it is possible to use asGetTypeTraits to automatically determine the correct flags that represents the C++ class
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits<CScriptDictValue>()); assert( r >= 0 );
#else
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CD); assert( r >= 0 );
#endif
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptDictValue_Construct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptDictValue_Destruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictValue, EnumReferences), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictValue, FreeValue), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const dictionaryValue &in)", asFUNCTIONPR(CScriptDictValue_opAssign, (const CScriptDictValue &, CScriptDictValue *), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const ?&in)", asFUNCTIONPR(CScriptDictValue_opAssign, (void *, int, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const dictionaryValue &in)", asFUNCTIONPR(CScriptDictValue_opAssign, (const CScriptDictValue &, CScriptDictValue *), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const ?&in)", asFUNCTIONPR(CScriptDictValue_opAssign, (void *, int, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(double)", asFUNCTIONPR(CScriptDictValue_opAssign, (double, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(int64)", asFUNCTIONPR(CScriptDictValue_opAssign, (asINT64, CScriptDictValue*), CScriptDictValue &), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opCast(?&out)", asFUNCTIONPR(CScriptDictValue_opCast, (void *, int, CScriptDictValue*), void), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opConv(?&out)", asFUNCTIONPR(CScriptDictValue_opCast, (void *, int, CScriptDictValue*), void), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "int64 opConv()", asFUNCTIONPR(CScriptDictValue_opConvInt, (CScriptDictValue*), asINT64), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "double opConv()", asFUNCTIONPR(CScriptDictValue_opConvDouble, (CScriptDictValue*), double), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
	// Use the generic interface to construct the object since we need the engine pointer, we could also have retrieved the engine pointer from the active context
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_LIST_FACTORY, "dictionary @f(int &in) {repeat {string, ?}}", asFUNCTION(ScriptDictionaryListFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptDictionary,AddRef), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptDictionary,Release), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionary &opAssign(const dictionary &in)", asMETHODPR(CScriptDictionary, operator=, (const CScriptDictionary &), CScriptDictionary&), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const ?&in)", asMETHODPR(CScriptDictionary,Set,(const dictKey_t&,void*,int),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asMETHODPR(CScriptDictionary,Get,(const dictKey_t&,void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const int64&in)", asMETHODPR(CScriptDictionary,Set,(const dictKey_t&,const asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asMETHODPR(CScriptDictionary,Get,(const dictKey_t&,asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const double&in)", asMETHODPR(CScriptDictionary,Set,(const dictKey_t&,const double&),void), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asMETHODPR(CScriptDictionary,Get,(const dictKey_t&,double&) const,bool), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asMETHOD(CScriptDictionary,Exists), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool isEmpty() const", asMETHOD(CScriptDictionary, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "uint getSize() const", asMETHOD(CScriptDictionary, GetSize), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool delete(const string &in)", asMETHOD(CScriptDictionary,Delete), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asMETHOD(CScriptDictionary,DeleteAll), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "array<string> @getKeys() const", asMETHOD(CScriptDictionary,GetKeys), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionaryValue &opIndex(const string &in)", asMETHODPR(CScriptDictionary, operator[], (const dictKey_t &), CScriptDictValue*), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "const dictionaryValue &opIndex(const string &in) const", asMETHODPR(CScriptDictionary, operator[], (const dictKey_t &) const, const CScriptDictValue*), asCALL_THISCALL); assert( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptDictionary,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptDictionary,SetGCFlag), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptDictionary,GetGCFlag), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptDictionary,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptDictionary,ReleaseAllReferences), asCALL_THISCALL); assert( r >= 0 );

	// Support foreach
	r = engine->RegisterObjectType("dictionaryIter", 0, asOBJ_REF); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("dictionaryIter", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptDictionary::CScriptDictIter, AddRef), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("dictionaryIter", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptDictionary::CScriptDictIter, Release), asCALL_THISCALL); assert(r >= 0);

	r = engine->RegisterObjectMethod("dictionary", "dictionaryIter @opForBegin() const", asMETHODPR(CScriptDictionary, opForBegin, () const, CScriptDictionary::CScriptDictIter *), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionary", "bool opForEnd(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForEnd, (const CScriptDictionary::CScriptDictIter&) const, bool), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionary", "dictionaryIter @+ opForNext(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForNext, (CScriptDictionary::CScriptDictIter&) const, CScriptDictionary::CScriptDictIter*), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionary", "const dictionaryValue &opForValue0(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForValue0, (const CScriptDictionary::CScriptDictIter&) const, const CScriptDictValue &), asCALL_THISCALL); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionary", "const string &opForValue1(dictionaryIter @+) const", asMETHODPR(CScriptDictionary, opForValue1, (const CScriptDictionary::CScriptDictIter&) const, const string &), asCALL_THISCALL); assert(r >= 0);

#if AS_USE_STLNAMES == 1
	// Same as isEmpty
	r = engine->RegisterObjectMethod("dictionary", "bool empty() const", asMETHOD(CScriptDictionary, IsEmpty), asCALL_THISCALL); assert( r >= 0 );
	// Same as getSize
	r = engine->RegisterObjectMethod("dictionary", "uint size() const", asMETHOD(CScriptDictionary, GetSize), asCALL_THISCALL); assert( r >= 0 );
	// Same as delete
	r = engine->RegisterObjectMethod("dictionary", "void erase(const string &in)", asMETHOD(CScriptDictionary,Delete), asCALL_THISCALL); assert( r >= 0 );
	// Same as deleteAll
	r = engine->RegisterObjectMethod("dictionary", "void clear()", asMETHOD(CScriptDictionary,DeleteAll), asCALL_THISCALL); assert( r >= 0 );
#endif

	// Cache some things the dictionary will need at runtime
	SDictionaryCache::Setup(engine);
}

void RegisterScriptDictionary_Generic(asIScriptEngine *engine)
{
	int r;

	// Register the cleanup callback for the object type cache
	engine->SetEngineUserDataCleanupCallback(SDictionaryCache::Cleanup, DICTIONARY_CACHE);

#if AS_CAN_USE_CPP11
	// With C++11 it is possible to use asGetTypeTraits to automatically determine the correct flags that represents the C++ class
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits<CScriptDictValue>()); assert( r >= 0 );
#else
	r = engine->RegisterObjectType("dictionaryValue", sizeof(CScriptDictValue), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CD); assert( r >= 0 );
#endif
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptDictValue_Construct_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptDictValue_Destruct_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(CScriptDictValue_EnumReferences_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectBehaviour("dictionaryValue", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(CScriptDictValue_FreeValue_Generic), asCALL_GENERIC); assert(r >= 0);
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const dictionaryValue &in)", asFUNCTION(CScriptDictValue_opCopyAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const ?&in)", asFUNCTION(CScriptDictValue_opAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opHndlAssign(const dictionaryValue &in)", asFUNCTION(CScriptDictValue_opCopyAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(const ?&in)", asFUNCTION(CScriptDictValue_opAssign_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(double)", asFUNCTION(CScriptDictValue_opAssign_double_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "dictionaryValue &opAssign(int64)", asFUNCTION(CScriptDictValue_opAssign_int64_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opCast(?&out)", asFUNCTION(CScriptDictValue_opCast_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "void opConv(?&out)", asFUNCTION(CScriptDictValue_opCast_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "int64 opConv()", asFUNCTION(CScriptDictValue_opConvInt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionaryValue", "double opConv()", asFUNCTION(CScriptDictValue_opConvDouble_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectType("dictionary", sizeof(CScriptDictionary), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_FACTORY, "dictionary@ f()", asFUNCTION(ScriptDictionaryFactory_Generic), asCALL_GENERIC); assert( r>= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_LIST_FACTORY, "dictionary @f(int &in) {repeat {string, ?}}", asFUNCTION(ScriptDictionaryListFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptDictionaryAddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptDictionaryRelease_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionary &opAssign(const dictionary &in)", asFUNCTION(ScriptDictionaryAssign_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const ?&in)", asFUNCTION(ScriptDictionarySet_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, ?&out) const", asFUNCTION(ScriptDictionaryGet_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const int64&in)", asFUNCTION(ScriptDictionarySetInt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, int64&out) const", asFUNCTION(ScriptDictionaryGetInt_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "void set(const string &in, const double&in)", asFUNCTION(ScriptDictionarySetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool get(const string &in, double&out) const", asFUNCTION(ScriptDictionaryGetFlt_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "bool exists(const string &in) const", asFUNCTION(ScriptDictionaryExists_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool isEmpty() const", asFUNCTION(ScriptDictionaryIsEmpty_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "uint getSize() const", asFUNCTION(ScriptDictionaryGetSize_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "bool delete(const string &in)", asFUNCTION(ScriptDictionaryDelete_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "void deleteAll()", asFUNCTION(ScriptDictionaryDeleteAll_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "array<string> @getKeys() const", asFUNCTION(CScriptDictionaryGetKeys_Generic), asCALL_GENERIC); assert( r >= 0 );

	r = engine->RegisterObjectMethod("dictionary", "dictionaryValue &opIndex(const string &in)", asFUNCTION(CScriptDictionary_opIndex_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectMethod("dictionary", "const dictionaryValue &opIndex(const string &in) const", asFUNCTION(CScriptDictionary_opIndex_const_Generic), asCALL_GENERIC); assert( r >= 0 );

	// Register GC behaviours
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptDictionaryGetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptDictionarySetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptDictionaryGetGCFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryEnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("dictionary", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptDictionaryReleaseAllReferences_Generic), asCALL_GENERIC); assert( r >= 0 );

	// Cache some things the dictionary will need at runtime
	SDictionaryCache::Setup(engine);
}

//------------------------------------------------------------------
// Iterator implementation

CScriptDictionary::CIterator CScriptDictionary::begin() const
{
	return CIterator(*this, dict.begin());
}

CScriptDictionary::CIterator CScriptDictionary::end() const
{
	return CIterator(*this, dict.end());
}

CScriptDictionary::CIterator CScriptDictionary::find(const dictKey_t &key) const
{
	return CIterator(*this, dict.find(key));
}

CScriptDictionary::CIterator::CIterator(
		const CScriptDictionary &dict,
		dictMap_t::const_iterator it)
	: m_it(it), m_dict(dict)
{}

void CScriptDictionary::CIterator::operator++()
{
	++m_it;
}

void CScriptDictionary::CIterator::operator++(int)
{
	++m_it;

	// Normally the post increment would return a copy of the object with the original state,
	// but it is rarely used so we skip this extra copy to avoid unnecessary overhead
}

CScriptDictionary::CIterator &CScriptDictionary::CIterator::operator*()
{
	return *this;
}

bool CScriptDictionary::CIterator::operator==(const CIterator &other) const
{
	return m_it == other.m_it;
}

bool CScriptDictionary::CIterator::operator!=(const CIterator &other) const
{
	return m_it != other.m_it;
}

const dictKey_t &CScriptDictionary::CIterator::GetKey() const
{
	return m_it->first;
}

int CScriptDictionary::CIterator::GetTypeId() const
{
	return m_it->second.m_typeId;
}

bool CScriptDictionary::CIterator::GetValue(asINT64 &value) const
{
	return m_it->second.Get(m_dict.engine, &value, asTYPEID_INT64);
}

bool CScriptDictionary::CIterator::GetValue(double &value) const
{
	return m_it->second.Get(m_dict.engine, &value, asTYPEID_DOUBLE);
}

bool CScriptDictionary::CIterator::GetValue(void *value, int typeId) const
{
	return m_it->second.Get(m_dict.engine, value, typeId);
}

const void *CScriptDictionary::CIterator::GetAddressOfValue() const
{
	return m_it->second.GetAddressOfValue();
}

END_AS_NAMESPACE