/*
   +----------------------------------------------------------------------+
   | HipHop for PHP                                                       |
   +----------------------------------------------------------------------+
   | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com)         |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
*/

#include <runtime/base/class_info.h>
#include <runtime/base/complex_types.h>
#include <runtime/base/externals.h>
#include <runtime/base/hphp_system.h>
#include <runtime/base/variable_serializer.h>
#include <runtime/base/variable_unserializer.h>
#include <util/util.h>
#include <util/lock.h>

using namespace std;

namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
// statics

bool ClassInfo::s_loaded = false;
ClassInfo *ClassInfo::s_systemFuncs = NULL;
ClassInfo *ClassInfo::s_userFuncs = NULL;
ClassInfo::ClassMap ClassInfo::s_classes;
ClassInfo::ClassMap ClassInfo::s_interfaces;
ClassInfoHook *ClassInfo::s_hook = NULL;

Array ClassInfo::GetSystemFunctions() {
  ASSERT(s_loaded);

  Array ret = Array::Create();
  if (s_systemFuncs) {
    const MethodVec &methods = s_systemFuncs->getMethodsVec();
    for (unsigned i = 0; i < methods.size(); i++) {
      ret.append(methods[i]->name);
    }
  }
  return ret;
}

Array ClassInfo::GetUserFunctions() {
  ASSERT(s_loaded);

  Array ret = Array::Create();
  if (s_userFuncs) {
    const MethodVec &methods = s_userFuncs->getMethodsVec();
    for (unsigned i = 0; i < methods.size(); i++) {
      ret.append(methods[i]->name);
    }
  }
  if (s_hook) {
    Array dyn = s_hook->getUserFunctions();
    if (!dyn.isNull()) {
      ret.merge(dyn);
    }
  }
  return ret;
}

const ClassInfo::MethodInfo *ClassInfo::FindFunction(CStrRef name) {
  ASSERT(!name.isNull());
  ASSERT(s_loaded);

  const MethodInfo *ret = s_systemFuncs->getMethodInfo(name);
  if (ret == NULL && s_hook) {
    ret = s_hook->findFunction(name);
  }
  if (ret == NULL) {
    ret = s_userFuncs->getMethodInfo(name);
  }
  return ret;
}

Array ClassInfo::GetClasses(bool declaredOnly) {
  ASSERT(s_loaded);

  Array ret;
  for (ClassMap::const_iterator iter = s_classes.begin();
       iter != s_classes.end(); ++iter) {
    if (!declaredOnly || iter->second->isDeclared()) {
      ret.append(iter->first);
    }
  }
  if (s_hook) {
    Array dyn = s_hook->getClasses();
    if (!dyn.isNull()) {
      ret.merge(dyn);
    }
  }
  return ret;
}

bool ClassInfo::HasClass(CStrRef name) {
  ASSERT(!name.isNull());
  ASSERT(s_loaded);

  if (s_hook && s_hook->findClass(name)) return true;

  return s_classes.find(name) != s_classes.end();
}

const ClassInfo *ClassInfo::FindClass(CStrRef name) {
  ASSERT(!name.isNull());
  ASSERT(s_loaded);

  if (s_hook) {
    const ClassInfo *cl = s_hook->findClass(name);
    if (cl) return cl;
  }
  ClassMap::const_iterator iter = s_classes.find(name);
  if (iter != s_classes.end()) {
    return iter->second;
  }
  return NULL;
}

Array ClassInfo::GetInterfaces(bool declaredOnly) {
  ASSERT(s_loaded);

  Array ret;
  for (ClassMap::const_iterator iter = s_interfaces.begin();
       iter != s_interfaces.end(); ++iter) {
    if (!declaredOnly || iter->second->isDeclared()) {
      ret.append(iter->first);
    }
  }
  if (s_hook) {
    Array dyn = s_hook->getInterfaces();
    if (!dyn.isNull()) {
      ret.merge(dyn);
    }
  }
  return ret;
}

bool ClassInfo::HasInterface(CStrRef name) {
  ASSERT(!name.isNull());
  ASSERT(s_loaded);

  if (s_hook && s_hook->findInterface(name)) return true;
  return s_interfaces.find(name) != s_interfaces.end();
}

// NOTE: FindInterface() currently cannot find interfaces redeclared by
// classes.
const ClassInfo *ClassInfo::FindInterface(CStrRef name) {
  ASSERT(!name.isNull());
  ASSERT(s_loaded);

  if (s_hook) {
    const ClassInfo *iface = s_hook->findInterface(name);
    if (iface) return iface;
  }
  ClassMap::const_iterator iter = s_interfaces.find(name);
  if (iter != s_interfaces.end()) {
    return iter->second;
  }
  return NULL;
}

const ClassInfo::ConstantInfo *ClassInfo::FindConstant(CStrRef name) {
  ASSERT(!name.isNull());
  ASSERT(s_loaded);
  const ConstantInfo *info;
  info = s_systemFuncs->getConstantInfo(name);
  if (info) return info;
  if (s_hook) {
    info = s_hook->findConstant(name);
    if (info) return info;
  }
  info = s_userFuncs->getConstantInfo(name);
  return info;
}

ClassInfo::ConstantInfo::ConstantInfo() : callbacks(NULL), deferred(true) {
}

Variant ClassInfo::ConstantInfo::getValue() const {
  if (deferred) {
    if (callbacks == NULL) {
      return get_constant(name);
    }
    return callbacks->os_constant(name);
  }
  if (!svalue.empty()) {
    try {
      VariableUnserializer vu(svalue.data(), svalue.size(),
                              VariableUnserializer::Serialize);
      return vu.unserialize();
    } catch (Exception &e) {
      ASSERT(false);
    }
  }
  return value;
}

void ClassInfo::ConstantInfo::setValue(CVarRef value) {
  VariableSerializer vs(VariableSerializer::Serialize);
  String s = vs.serialize(value, true);
  svalue = string(s.data(), s.size());
  deferred = false;
}

void ClassInfo::ConstantInfo::setStaticValue(CVarRef v) {
  value = v;
  value.setStatic();
  deferred = false;
}

Array ClassInfo::GetConstants() {
  ASSERT(s_loaded);
  Array res;
  Array dyn;
  {
    const ConstantMap &scm = s_systemFuncs->getConstants();
    for (ConstantMap::const_iterator it = scm.begin(); it != scm.end(); ++it) {
      res.set(it->first, it->second->getValue());
    }
  }
  {
    const ConstantMap &ucm = s_userFuncs->getConstants();
    for (ConstantMap::const_iterator it = ucm.begin(); it != ucm.end(); ++it) {
      res.set(it->first, it->second->getValue());
    }
  }
  if (s_hook) {
    dyn = s_hook->getConstants();
    if (!dyn.isNull()) {
      res.merge(dyn);
    }
  }
  dyn = get_globals()->getDynamicConstants();
  if (!dyn.isNull()) {
    res.merge(dyn);
  }
  return res;
}

void ClassInfo::GetClassMethods(MethodVec &ret, CStrRef classname,
                                int type /* = 0 */) {
  if (!classname.empty()) {
    const ClassInfo *classInfo = NULL;
    switch (type) {
    case 0:
      classInfo = FindClass(classname);
      if (classInfo == NULL) {
        classInfo = FindInterface(classname);
        type = 2;
      }
      break;
    case 1:
      classInfo = FindClass(classname);
      break;
    case 2:
      classInfo = FindInterface(classname);
      break;
    default:
      ASSERT(false);
    }

    if (classInfo) {
      const ClassInfo::MethodVec &methods = classInfo->getMethodsVec();
      ret.insert(ret.end(), methods.begin(), methods.end());

      if (type != 2) {
        CStrRef parentClass = classInfo->getParentClass();
        if (!parentClass.empty()) {
          GetClassMethods(ret, parentClass, 1);
        }
      }

      const ClassInfo::InterfaceVec &interfaces =
        classInfo->getInterfacesVec();
      for (unsigned int i = 0; i < interfaces.size(); i++) {
        GetClassMethods(ret, interfaces[i], 2);
      }
    }
  }
}

void ClassInfo::GetClassProperties(PropertyMap &props, CStrRef classname) {
  if (!classname.empty()) {
    const ClassInfo *classInfo = FindClass(classname);
    if (classInfo) {
      classInfo->getAllProperties(props);
    }
  }
}

void ClassInfo::GetClassProperties(PropertyVec &props, CStrRef classname) {
  if (!classname.empty()) {
    const ClassInfo *classInfo = FindClass(classname);
    if (classInfo) {
      classInfo->getAllProperties(props);
    }
  }
}

void ClassInfo::GetClassSymbolNames(CArrRef names, bool interface,
                                    std::vector<String> &classes,
                                    std::vector<String> *clsMethods,
                                    std::vector<String> *clsProperties,
                                    std::vector<String> *clsConstants) {
  if (clsMethods || clsProperties || clsConstants) {
    for (ArrayIter iter(names); iter; ++iter) {
      String clsname = iter.second().toString();
      classes.push_back(clsname);

      const ClassInfo *cls;
      if (interface) {
        cls = FindInterface(clsname.data());
      } else {
        cls = FindClass(clsname.data());
      }
      ASSERT(cls);
      if (clsMethods) {
        const ClassInfo::MethodVec &methods = cls->getMethodsVec();
        for (unsigned int i = 0; i < methods.size(); i++) {
          clsMethods->push_back(clsname + "::" + methods[i]->name);
        }
      }
      if (clsProperties) {
        const ClassInfo::PropertyVec &properties = cls->getPropertiesVec();
        for (ClassInfo::PropertyVec::const_iterator iter = properties.begin();
             iter != properties.end(); ++iter) {
          clsProperties->push_back(clsname + "::$" + (*iter)->name);
        }
      }
      if (clsConstants) {
        const ClassInfo::ConstantVec &constants = cls->getConstantsVec();
        for (ClassInfo::ConstantVec::const_iterator iter = constants.begin();
             iter != constants.end(); ++iter) {
          clsConstants->push_back(clsname + "::" + (*iter)->name);
        }
      }
    }
  } else {
    for (ArrayIter iter(names); iter; ++iter) {
      classes.push_back(iter.second().toString());
    }
  }
}

void ClassInfo::GetSymbolNames(std::vector<String> &classes,
                               std::vector<String> &functions,
                               std::vector<String> &constants,
                               std::vector<String> *clsMethods,
                               std::vector<String> *clsProperties,
                               std::vector<String> *clsConstants) {
  static unsigned int methodSize = 128;
  static unsigned int propSize   = 128;
  static unsigned int constSize  = 128;

  if (clsMethods) {
    clsMethods->reserve(methodSize);
  }
  if (clsProperties) {
    clsProperties->reserve(propSize);
  }
  if (clsConstants) {
    clsConstants->reserve(constSize);
  }

  GetClassSymbolNames(GetClasses(true), false, classes,
                      clsMethods, clsProperties, clsConstants);
  GetClassSymbolNames(GetInterfaces(true), true, classes,
                      clsMethods, clsProperties, clsConstants);

  if (clsMethods && methodSize < clsMethods->size()) {
    methodSize = clsMethods->size();
  }
  if (clsProperties && propSize < clsProperties->size()) {
    propSize = clsProperties->size();
  }
  if (constSize && constSize < clsConstants->size()) {
    constSize = clsConstants->size();
  }

  Array funcs1 = ClassInfo::GetSystemFunctions();
  Array funcs2 = ClassInfo::GetUserFunctions();
  functions.reserve(funcs1.size() + funcs2.size());
  for (ArrayIter iter(funcs1); iter; ++iter) {
    functions.push_back(iter.second().toString());
  }
  for (ArrayIter iter(funcs2); iter; ++iter) {
    functions.push_back(iter.second().toString());
  }

  Array consts = GetConstants();
  constants.reserve(consts.size());
  for (ArrayIter iter(consts); iter; ++iter) {
    constants.push_back(iter.first().toString());
  }
}

///////////////////////////////////////////////////////////////////////////////
// ClassInfo

bool ClassInfo::isDeclared() const {
  if (m_attribute & IsVolatile) {
    return get_globals()->class_exists(m_name);
  }
  return true;
}

void ClassInfo::getAllParentsVec(ClassVec &parents) const {
  CStrRef parent = getParentClass();
  if (!parent.empty()) {
    parents.push_back(parent);
    const ClassInfo *info = FindClass(parent);
    if (info) info->getAllParentsVec(parents);
  }
}

void ClassInfo::getAllInterfacesVec(InterfaceVec &interfaces) const {
  const InterfaceVec &ifs = getInterfacesVec();
  for (unsigned int i = 0; i < ifs.size(); i++) {
    CStrRef intf = ifs[i];
    interfaces.push_back(intf);
    const ClassInfo *info = FindInterface(intf);
    if (info) info->getAllInterfacesVec(interfaces);
  }
}

bool ClassInfo::derivesFrom(CStrRef name, bool considerInterface) const {
  ASSERT(!name.isNull());
  return derivesFromImpl(name, considerInterface);
}

bool ClassInfo::derivesFromImpl(CStrRef name, bool considerInterface) const {
  if (name->isame(getParentClass().get())) {
    return true;
  }

  // We don't support redeclared parents anyway.
  const ClassInfo *parent = getParentClassInfo();
  if (parent && parent->derivesFromImpl(name, considerInterface)) {
    return true;
  }

  if (considerInterface) {
    const InterfaceSet &interfaces = getInterfaces();
    for (InterfaceSet::const_iterator iter = interfaces.begin();
         iter != interfaces.end(); ++iter) {
      if (name->isame(iter->get())) {
        return true;
      }
      const ClassInfo *parent = FindInterface(*iter);
      if (parent && parent->derivesFromImpl(name, considerInterface)) {
        return true;
      }
    }
  }
  return false;
}

bool ClassInfo::IsSubClass(CStrRef className1, CStrRef className2,
                           bool considerInterface) {
  const ClassInfo *clsInfo1 = ClassInfo::FindClass(className1);
  if (!clsInfo1) return false;

  return clsInfo1->derivesFrom(className2, considerInterface);
}

ClassInfo::MethodInfo *ClassInfo::getMethodInfo(CStrRef name) const {
  ASSERT(!name.isNull());

  const MethodMap &methods = getMethods();
  MethodMap::const_iterator iter = methods.find(name);
  if (iter != methods.end()) {
    return iter->second;
  }
  return NULL;
}

ClassInfo::MethodInfo *ClassInfo::hasMethod(CStrRef name,
                                            ClassInfo* &classInfo) const {
  ASSERT(!name.isNull());
  classInfo = (ClassInfo *)this;
  const MethodMap &methods = getMethods();
  MethodMap::const_iterator it = methods.find(name);
  if (it != methods.end()) {
    ClassInfo::MethodInfo *m = it->second;
    if (m->invokeFn) {
      return (*(m->invokeFn) != m->invokeFailedFn) ? m : NULL;
    }
    return m;
  }
  const ClassInfo *parent = getParentClassInfo();
  if (parent) return parent->hasMethod(name, classInfo);
  return NULL;
}

// internal function  className::methodName or callObject->methodName
bool ClassInfo::HasAccess(CStrRef className, CStrRef methodName,
                          bool staticCall, bool hasCallObject) {
  // It has to be either a static call or a call with an object.
  ASSERT(staticCall || hasCallObject);
  const ClassInfo *clsInfo = ClassInfo::FindClass(className);
  if (!clsInfo || !clsInfo->isDeclared()) return false;
  ClassInfo *defClass;
  ClassInfo::MethodInfo *methodInfo =
    clsInfo->hasMethod(methodName, defClass);
  if (!methodInfo) return false;
  if (methodInfo->attribute & ClassInfo::IsPublic) return true;
  const ClassInfo *ctxClass =
    ClassInfo::FindClass(FrameInjection::GetClassName(true));
  bool hasObject = hasCallObject || FrameInjection::GetThis(true);
  if (ctxClass) {
    return ctxClass->checkAccess(defClass, methodInfo, staticCall, hasObject);
  }
  return false;
}

bool ClassInfo::checkAccess(ClassInfo *defClass,
                            MethodInfo *methodInfo,
                            bool staticCall,
                            bool hasObject) const {
  ASSERT(defClass && methodInfo);
  if ((m_name->isame(defClass->m_name.get()))) {
    if (methodInfo->attribute & ClassInfo::IsStatic) return true;
    return hasObject;
  }

  if (methodInfo->attribute & ClassInfo::IsStatic) {
    if (methodInfo->attribute & ClassInfo::IsPublic) return true;
    if (methodInfo->attribute & ClassInfo::IsProtected) {
      return derivesFrom(defClass->m_name, false) ||
             defClass->derivesFrom(m_name, false);
    }
    return false;
  }
  if (!hasObject && !staticCall) return false;
  if (methodInfo->attribute & ClassInfo::IsPublic) return true;
  if (methodInfo->attribute & ClassInfo::IsProtected) {
    return derivesFrom(defClass->m_name, false) ||
           defClass->derivesFrom(m_name, false);
  }
  return false;
}

const char *ClassInfo::getConstructor() const {
  ClassInfo *defClass;
  if (hasMethod("__construct", defClass)) {
    return "__construct";
  }
  if (hasMethod(m_name, defClass)) {
    return m_name;
  }
  return NULL;
}

void ClassInfo::getAllProperties(PropertyMap &props) const {
  const PropertyMap &properties = getProperties();
  props.insert(properties.begin(), properties.end());

  CStrRef parentClass = getParentClass();
  if (!parentClass.empty()) {
    GetClassProperties(props, parentClass);
  }
}

void ClassInfo::getAllProperties(PropertyVec &props) const {
  const PropertyVec &properties = getPropertiesVec();
  props.insert(props.end(), properties.begin(), properties.end());

  CStrRef parentClass = getParentClass();
  if (!parentClass.empty()) {
    GetClassProperties(props, parentClass);
  }
}

void ClassInfo::filterProperties(Array &props, Attribute toRemove) const {
  const PropertyVec &properties = getPropertiesVec();
  for (unsigned i = 0; i < properties.size(); i++) {
    if (properties[i]->attribute & toRemove) {
      props.remove(properties[i]->name, true);
    }
  }
  const ClassInfo *parent = getParentClassInfo();
  if (parent) {
    parent->filterProperties(props, toRemove);
  }
}

ClassInfo::PropertyInfo *ClassInfo::getPropertyInfo(CStrRef name) const {
  ASSERT(!name.isNull());
  const PropertyMap &properties = getProperties();
  PropertyMap::const_iterator iter = properties.find(name);
  if (iter != properties.end()) {
    return iter->second;
  }
  return NULL;
}

bool ClassInfo::hasProperty(CStrRef name) const {
  ASSERT(!name.isNull());
  const PropertyMap &properties = getProperties();
  return properties.find(name) != properties.end();
}

ClassInfo::ConstantInfo *ClassInfo::getConstantInfo(CStrRef name) const {
  ASSERT(!name.isNull());
  const ConstantMap &constants = getConstants();
  ConstantMap::const_iterator iter = constants.find(name);
  if (iter != constants.end()) {
    return iter->second;
  }
  return NULL;
}

bool ClassInfo::hasConstant(CStrRef name) const {
  ASSERT(!name.isNull());
  const ConstantMap &constants = getConstants();
  return constants.find(name) != constants.end();
}

bool ClassInfo::PropertyInfo::isVisible(const ClassInfo *context) const {
  if ((attribute & ClassInfo::IsPublic) || context == owner) return true;
  if (!context) return false;
  if (attribute & ClassInfo::IsProtected) {
    return owner->derivesFrom(context->getName(), false) ||
           context->derivesFrom(owner->getName(), false);
  }
  ASSERT(attribute & ClassInfo::IsPrivate);
  return false;
}

///////////////////////////////////////////////////////////////////////////////
// load functions

static String makeStaticString(const char *s) {
  if (!s) return null_string;
  String str(s);
  if (!str.checkStatic()) {
    str->setStatic();
    StaticString::TheStaticStringSet().insert(str.get());
  }
  return str;
}

ClassInfoUnique::ClassInfoUnique(const char **&p) {
  m_attribute = (Attribute)(int64)(*p++);

  // ClassInfoUnique is only created by ClassInfo::Load(), which is called
  // from hphp_process_init() in the thread-neutral initialization phase.
  // It is OK to create StaticStrings here, and throw the smart ptrs away,
  // because the underlying static StringData will not be released.
  m_name = makeStaticString(*p++);
  m_parent = makeStaticString(*p++);

  m_file = *p++;
  m_line1 = (int)(int64)(*p++);
  m_line2 = (int)(int64)(*p++);

  if (m_attribute & HasDocComment) {
    m_docComment = *p++;
  }

  while (*p) {
    String iface_name = makeStaticString(*p++);
    ASSERT(m_interfaces.find(iface_name) == m_interfaces.end());
    m_interfaces.insert(iface_name);
    m_interfacesVec.push_back(iface_name);
  }
  p++;

  while (*p) {
    MethodInfo *method = new MethodInfo();
    method->attribute = (Attribute)(int64)(*p++);
    method->name = makeStaticString(*p++);
    method->file = *p++;
    method->line1 = (int)(int64)(*p++);
    method->line2 = (int)(int64)(*p++);
    method->invokeFn = (Variant (**)(const Array& params))*p++;
    method->invokeFailedFn = (Variant (*)(const Array& params))*p++;
    if (method->attribute & HasDocComment) {
      method->docComment = *p++;
    }

    while (*p) {
      ParameterInfo *parameter = new ParameterInfo();
      parameter->attribute = (Attribute)(int64)(*p++);
      parameter->name = *p++;
      parameter->type = *p++;
      ASSERT(Util::toLower(parameter->type) == parameter->type);
      parameter->value = *p++;
      parameter->valueText = *p++;

      method->parameters.push_back(parameter);
    }
    p++;

    while (*p) {
      ConstantInfo *staticVariable = new ConstantInfo();
      staticVariable->name = makeStaticString(*p++);
      staticVariable->valueLen = (int64)(*p++);
      staticVariable->valueText = *p++;
      VariableUnserializer vu(staticVariable->valueText,
                              staticVariable->valueLen,
                              VariableUnserializer::Serialize);
      try {
        staticVariable->setStaticValue(vu.unserialize());
      } catch (Exception &e) {
        ASSERT(false);
      }
      method->staticVariables.push_back(staticVariable);
    }
    p++;

    ASSERT(m_methods.find(method->name) == m_methods.end());
    m_methods[method->name] = method;
    m_methodsVec.push_back(method);
  }
  p++;

  while (*p) {
    PropertyInfo *property = new PropertyInfo();
    property->attribute = (Attribute)(int64)(*p++);
    property->name = makeStaticString(*p++);
    property->owner = this;
    ASSERT(m_properties.find(property->name) == m_properties.end());
    m_properties[property->name] = property;
    m_propertiesVec.push_back(property);
  }
  p++;

  while (*p) {
    ConstantInfo *constant = new ConstantInfo();
    constant->name = makeStaticString(*p++);
    constant->valueLen = (int64)(*p++);
    constant->valueText = *p++;

    if (constant->valueText) {
      VariableUnserializer vu(constant->valueText,
                              constant->valueLen,
                              VariableUnserializer::Serialize);
      try {
        constant->setStaticValue(vu.unserialize());
      } catch (Exception &e) {
        ASSERT(false);
      }
    } else if (!m_name.empty()) {
      if (!(m_attribute & IsVolatile) && !(m_attribute & IsLazyInit)) {
        const ObjectStaticCallbacks *cwo = get_object_static_callbacks(m_name);
        if (cwo) {
          constant->callbacks = cwo;
        } else {
          ASSERT(false);
        }
      }
    }

    ASSERT(m_constants.find(constant->name) == m_constants.end());
    m_constants[constant->name] = constant;
    m_constantsVec.push_back(constant);
  }
  p++;
}

ClassInfoRedeclared::ClassInfoRedeclared(const char **&p) {
  m_attribute = (Attribute)(int64)(*p++);
  m_name = makeStaticString(*p++);
  m_redeclaredIdGetter = (int (*)())*p++;
  while (*p) {
    ClassInfo *cls = new ClassInfoUnique(p);
    m_redeclaredClasses.push_back(cls);
  }
  p++;
}

void ClassInfo::Load() {
  ASSERT(!s_loaded);
  const char **p = g_class_map;
  while (*p) {
    Attribute attribute = (Attribute)(int64)*p;
    ClassInfo *info = (attribute & IsRedeclared) ?
      static_cast<ClassInfo*>(new ClassInfoRedeclared(p)) :
      static_cast<ClassInfo*>(new ClassInfoUnique(p));

    if (info->m_name.empty()) {
      if (attribute & IsSystem) {
        ASSERT(s_systemFuncs == NULL);
        s_systemFuncs = info;
      } else {
        ASSERT(s_userFuncs == NULL);
        s_userFuncs = info;
      }
    } else if (attribute & IsInterface) {
      ASSERT(s_classes.find(info->m_name) == s_classes.end());
      ASSERT(s_interfaces.find(info->m_name) == s_interfaces.end());
      s_interfaces[info->m_name] = info;
    } else {
      ASSERT(s_classes.find(info->m_name) == s_classes.end());
      ASSERT(s_interfaces.find(info->m_name) == s_interfaces.end());
      s_classes[info->m_name] = info;
    }
  }

  ASSERT(s_systemFuncs);
  ASSERT(s_userFuncs);
  s_loaded = true;
}

ClassInfo::MethodInfo::~MethodInfo() {
  for (vector<const ParameterInfo *>::iterator it = parameters.begin();
       it != parameters.end(); ++it) {
    delete *it;
  }
  for (vector<const ConstantInfo *>::iterator it = staticVariables.begin();
       it != staticVariables.end(); ++it) {
    delete *it;
  }
}

///////////////////////////////////////////////////////////////////////////////
}
