/* * Copyright 2001-2012 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Rene Gollent (rene@gollent.com) * Erik Jaesler (erik@cgsoftware.com) * Alex Wilson (yourpalal2@gmail.com) */ /*! BArchivable mix-in class defines the archiving protocol. Also some global archiving functions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ArchivingManagers.h" using std::string; using std::vector; using namespace BPrivate::Archiving; const char* B_CLASS_FIELD = "class"; const char* B_ADD_ON_FIELD = "add_on"; const int32 FUNC_NAME_LEN = 1024; // TODO: consider moving these to a separate module, and making them more // full-featured (e.g., taking NS::ClassName::Function(Param p) instead // of just NS::ClassName) static status_t demangle_class_name(const char* name, BString& out) { // TODO: add support for template classes // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl out = ""; #if __GNUC__ >= 4 if (name[0] == 'N') name++; int nameLen; bool first = true; while ((nameLen = strtoul(name, (char**)&name, 10))) { if (!first) out += "::"; else first = false; out.Append(name, nameLen); name += nameLen; } if (first) return B_BAD_VALUE; #else if (name[0] == 'Q') { // The name is in a namespace int namespaceCount = 0; name++; if (name[0] == '_') { // more than 10 namespaces deep if (!isdigit(*++name)) return B_BAD_VALUE; namespaceCount = strtoul(name, (char**)&name, 10); if (name[0] != '_') return B_BAD_VALUE; } else namespaceCount = name[0] - '0'; name++; for (int i = 0; i < namespaceCount - 1; i++) { if (!isdigit(name[0])) return B_BAD_VALUE; int nameLength = strtoul(name, (char**)&name, 10); out.Append(name, nameLength); out += "::"; name += nameLength; } } int nameLength = strtoul(name, (char**)&name, 10); out.Append(name, nameLength); #endif return B_OK; } static void mangle_class_name(const char* name, BString& out) { // TODO: add support for template classes // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl // Chop this: // testthree::testfour::Testthree::Testfour // up into little bite-sized pieces int count = 0; string origName(name); vector spacenames; string::size_type pos = 0; string::size_type oldpos = 0; while (pos != string::npos) { pos = origName.find_first_of("::", oldpos); spacenames.push_back(string(origName, oldpos, pos - oldpos)); pos = origName.find_first_not_of("::", pos); oldpos = pos; ++count; } // Now mangle it into this: // 9testthree8testfour9Testthree8Testfour // (for __GNUC__ > 2) // this isn't always the proper mangled class name, it should // actually have an 'N' prefix and 'E' suffix if the name is // in > 0 namespaces, but these would have to be removed in // build_function_name() (the only place this function is called) // so we don't add them. // or this: // Q49testthree8testfour9Testthree8Testfour // (for __GNUC__ == 2) out = ""; #if __GNUC__ == 2 if (count > 1) { out += 'Q'; if (count > 10) out += '_'; out << count; if (count > 10) out += '_'; } #endif for (unsigned int i = 0; i < spacenames.size(); ++i) { out << (int)spacenames[i].length(); out += spacenames[i].c_str(); } } static void build_function_name(const BString& className, BString& funcName) { funcName = ""; // This is what we're after: // Instantiate__Q28OpenBeOS11BArchivableP8BMessage mangle_class_name(className.String(), funcName); #if __GNUC__ >= 4 funcName.Prepend("_ZN"); funcName.Append("11InstantiateE"); #else funcName.Prepend("Instantiate__"); #endif funcName.Append("P8BMessage"); } static bool add_private_namespace(BString& name) { if (name.Compare("_", 1) != 0) return false; name.Prepend("BPrivate::"); return true; } static instantiation_func find_function_in_image(BString& funcName, image_id id, status_t& err) { instantiation_func instantiationFunc = NULL; err = get_image_symbol(id, funcName.String(), B_SYMBOL_TYPE_TEXT, (void**)&instantiationFunc); if (err != B_OK) return NULL; return instantiationFunc; } static status_t check_signature(const char* signature, image_info& info) { if (signature == NULL) { // If it wasn't specified, anything "matches" return B_OK; } // Get image signature BFile file(info.name, B_READ_ONLY); status_t err = file.InitCheck(); if (err != B_OK) return err; char imageSignature[B_MIME_TYPE_LENGTH]; BAppFileInfo appFileInfo(&file); err = appFileInfo.GetSignature(imageSignature); if (err != B_OK) { syslog(LOG_ERR, "instantiate_object - couldn't get mime sig for %s", info.name); return err; } if (strcmp(signature, imageSignature) != 0) return B_MISMATCHED_VALUES; return B_OK; } namespace BPrivate { instantiation_func find_instantiation_func(const char* className, const char* signature, image_id* id) { if (className == NULL) { errno = B_BAD_VALUE; return NULL; } thread_info threadInfo; status_t err = get_thread_info(find_thread(NULL), &threadInfo); if (err != B_OK) { errno = err; return NULL; } instantiation_func instantiationFunc = NULL; image_info imageInfo; BString name = className; for (int32 pass = 0; pass < 2; pass++) { BString funcName; build_function_name(name, funcName); // for each image_id in team_id int32 cookie = 0; while (instantiationFunc == NULL && get_next_image_info(threadInfo.team, &cookie, &imageInfo) == B_OK) { instantiationFunc = find_function_in_image(funcName, imageInfo.id, err); } if (instantiationFunc != NULL) { // if requested, save the image id in // which the function was found if (id != NULL) *id = imageInfo.id; break; } // Check if we have a private class, and add the BPrivate namespace // (for backwards compatibility) if (!add_private_namespace(name)) break; } if (instantiationFunc != NULL && check_signature(signature, imageInfo) != B_OK) return NULL; return instantiationFunc; } } // namespace BPrivate // #pragma mark - BArchivable BArchivable::BArchivable() : fArchivingToken(NULL_TOKEN) { } BArchivable::BArchivable(BMessage* from) : fArchivingToken(NULL_TOKEN) { if (BUnarchiver::IsArchiveManaged(from)) { BUnarchiver::PrepareArchive(from); BUnarchiver(from).RegisterArchivable(this); } } BArchivable::~BArchivable() { } status_t BArchivable::Archive(BMessage* into, bool deep) const { if (!into) { // TODO: logging/other error reporting? return B_BAD_VALUE; } if (BManagerBase::ArchiveManager(into)) BArchiver(into).RegisterArchivable(this); BString name; status_t status = demangle_class_name(typeid(*this).name(), name); if (status != B_OK) return status; return into->AddString(B_CLASS_FIELD, name); } BArchivable* BArchivable::Instantiate(BMessage* from) { debugger("Can't create a plain BArchivable object"); return NULL; } status_t BArchivable::Perform(perform_code d, void* arg) { switch (d) { case PERFORM_CODE_ALL_UNARCHIVED: { perform_data_all_unarchived* data = (perform_data_all_unarchived*)arg; data->return_value = BArchivable::AllUnarchived(data->archive); return B_OK; } case PERFORM_CODE_ALL_ARCHIVED: { perform_data_all_archived* data = (perform_data_all_archived*)arg; data->return_value = BArchivable::AllArchived(data->archive); return B_OK; } } return B_NAME_NOT_FOUND; } status_t BArchivable::AllUnarchived(const BMessage* archive) { return B_OK; } status_t BArchivable::AllArchived(BMessage* archive) const { return B_OK; } // #pragma mark - BArchiver BArchiver::BArchiver(BMessage* archive) : fManager(BManagerBase::ArchiveManager(archive)), fArchive(archive), fFinished(false) { if (fManager == NULL) fManager = new BArchiveManager(this); } BArchiver::~BArchiver() { if (!fFinished) fManager->ArchiverLeaving(this, B_OK); } status_t BArchiver::AddArchivable(const char* name, BArchivable* archivable, bool deep) { int32 token; status_t err = GetTokenForArchivable(archivable, deep, token); if (err != B_OK) return err; return fArchive->AddInt32(name, token); } status_t BArchiver::GetTokenForArchivable(BArchivable* archivable, bool deep, int32& _token) { return fManager->ArchiveObject(archivable, deep, _token); } bool BArchiver::IsArchived(BArchivable* archivable) { return fManager->IsArchived(archivable); } status_t BArchiver::Finish(status_t err) { if (fFinished) debugger("Finish() called multiple times on same BArchiver."); fFinished = true; return fManager->ArchiverLeaving(this, err); } BMessage* BArchiver::ArchiveMessage() const { return fArchive; } void BArchiver::RegisterArchivable(const BArchivable* archivable) { fManager->RegisterArchivable(archivable); } // #pragma mark - BUnarchiver BUnarchiver::BUnarchiver(const BMessage* archive) : fManager(BManagerBase::UnarchiveManager(archive)), fArchive(archive), fFinished(false) { } BUnarchiver::~BUnarchiver() { if (!fFinished && fManager) fManager->UnarchiverLeaving(this, B_OK); } template<> status_t BUnarchiver::GetObject(int32 token, ownership_policy owning, BArchivable*& object) { _CallDebuggerIfManagerNull(); return fManager->GetArchivableForToken(token, owning, object); } template<> status_t BUnarchiver::FindObject(const char* name, int32 index, ownership_policy owning, BArchivable*& archivable) { archivable = NULL; int32 token; status_t err = fArchive->FindInt32(name, index, &token); if (err != B_OK) return err; return GetObject(token, owning, archivable); } bool BUnarchiver::IsInstantiated(int32 token) { _CallDebuggerIfManagerNull(); return fManager->IsInstantiated(token); } bool BUnarchiver::IsInstantiated(const char* field, int32 index) { int32 token; if (fArchive->FindInt32(field, index, &token) == B_OK) return IsInstantiated(token); return false; } status_t BUnarchiver::Finish(status_t err) { if (fFinished) debugger("Finish() called multiple times on same BArchiver."); fFinished = true; if (fManager) return fManager->UnarchiverLeaving(this, err); else return B_OK; } const BMessage* BUnarchiver::ArchiveMessage() const { return fArchive; } void BUnarchiver::AssumeOwnership(BArchivable* archivable) { _CallDebuggerIfManagerNull(); fManager->AssumeOwnership(archivable); } void BUnarchiver::RelinquishOwnership(BArchivable* archivable) { _CallDebuggerIfManagerNull(); fManager->RelinquishOwnership(archivable); } bool BUnarchiver::IsArchiveManaged(const BMessage* archive) { // managed child archives will return here if (BManagerBase::ManagerPointer(archive)) return true; if (archive == NULL) return false; // managed top level archives return here bool dummy; if (archive->FindBool(kManagedField, &dummy) == B_OK) return true; return false; } template<> status_t BUnarchiver::InstantiateObject(BMessage* from, BArchivable* &object) { BUnarchiver unarchiver(BUnarchiver::PrepareArchive(from)); object = instantiate_object(from); return unarchiver.Finish(); } BMessage* BUnarchiver::PrepareArchive(BMessage* &archive) { // this check allows PrepareArchive to be // called on new or old-style archives if (BUnarchiver::IsArchiveManaged(archive)) { BUnarchiveManager* manager = BManagerBase::UnarchiveManager(archive); if (!manager) manager = new BUnarchiveManager(archive); manager->Acquire(); } return archive; } void BUnarchiver::RegisterArchivable(BArchivable* archivable) { _CallDebuggerIfManagerNull(); fManager->RegisterArchivable(archivable); } void BUnarchiver::_CallDebuggerIfManagerNull() { if (!fManager) debugger("BUnarchiver used with legacy or unprepared archive."); } // #pragma mark - BArchivable* instantiate_object(BMessage* archive, image_id* _id) { status_t statusBuffer; status_t* status = &statusBuffer; if (_id != NULL) status = _id; // Check our params if (archive == NULL) { syslog(LOG_ERR, "instantiate_object failed: NULL BMessage argument"); *status = B_BAD_VALUE; return NULL; } // Get class name from archive const char* className = NULL; status_t err = archive->FindString(B_CLASS_FIELD, &className); if (err) { syslog(LOG_ERR, "instantiate_object failed: Failed to find an entry " "defining the class name (%s).", strerror(err)); *status = B_BAD_VALUE; return NULL; } // Get sig from archive const char* signature = NULL; bool hasSignature = archive->FindString(B_ADD_ON_FIELD, &signature) == B_OK; instantiation_func instantiationFunc = BPrivate::find_instantiation_func( className, signature, _id); // if find_instantiation_func() can't locate Class::Instantiate() // and a signature was specified if (!instantiationFunc && hasSignature) { // use BRoster::FindApp() to locate an app or add-on with the symbol BRoster Roster; entry_ref ref; err = Roster.FindApp(signature, &ref); // if an entry_ref is obtained BEntry entry; if (err == B_OK) err = entry.SetTo(&ref); BPath path; if (err == B_OK) err = entry.GetPath(&path); if (err != B_OK) { syslog(LOG_ERR, "instantiate_object failed: Error finding app " "with signature \"%s\" (%s)", signature, strerror(err)); *status = err; return NULL; } // load the app/add-on image_id addOn = load_add_on(path.Path()); if (addOn < B_OK) { syslog(LOG_ERR, "instantiate_object failed: Could not load " "add-on %s: %s.", path.Path(), strerror(addOn)); *status = addOn; return NULL; } // Save the image_id if (_id != NULL) *_id = addOn; BString name = className; for (int32 pass = 0; pass < 2; pass++) { BString funcName; build_function_name(name, funcName); instantiationFunc = find_function_in_image(funcName, addOn, err); if (instantiationFunc != NULL) break; // Check if we have a private class, and add the BPrivate namespace // (for backwards compatibility) if (!add_private_namespace(name)) break; } if (instantiationFunc == NULL) { syslog(LOG_ERR, "instantiate_object failed: Failed to find exported " "Instantiate static function for class %s.", className); *status = B_NAME_NOT_FOUND; return NULL; } } else if (instantiationFunc == NULL) { syslog(LOG_ERR, "instantiate_object failed: No signature specified " "in archive, looking for class \"%s\".", className); *status = B_NAME_NOT_FOUND; return NULL; } // if Class::Instantiate(BMessage*) was found if (instantiationFunc != NULL) { // use to create and return an object instance return instantiationFunc(archive); } return NULL; } BArchivable* instantiate_object(BMessage* from) { return instantiate_object(from, NULL); } // #pragma mark - support_globals bool validate_instantiation(BMessage* from, const char* className) { // Make sure our params are kosher -- original skimped here =P if (!from) { errno = B_BAD_VALUE; return false; } BString name = className; for (int32 pass = 0; pass < 2; pass++) { const char* archiveClassName; for (int32 index = 0; from->FindString(B_CLASS_FIELD, index, &archiveClassName) == B_OK; ++index) { if (name == archiveClassName) { errno = B_OK; return true; } } if (!add_private_namespace(name)) break; } errno = B_MISMATCHED_VALUES; syslog(LOG_ERR, "validate_instantiation failed on class %s.", className); return false; } instantiation_func find_instantiation_func(const char* className, const char* signature) { return BPrivate::find_instantiation_func(className, signature, NULL); } instantiation_func find_instantiation_func(const char* className) { return find_instantiation_func(className, NULL); } instantiation_func find_instantiation_func(BMessage* archive) { if (archive == NULL) { errno = B_BAD_VALUE; return NULL; } const char* name = NULL; const char* signature = NULL; if (archive->FindString(B_CLASS_FIELD, &name) != B_OK || archive->FindString(B_ADD_ON_FIELD, &signature)) { errno = B_BAD_VALUE; return NULL; } return find_instantiation_func(name, signature); } // #pragma mark - BArchivable binary compatibility #if __GNUC__ == 2 extern "C" status_t _ReservedArchivable1__11BArchivable(BArchivable* archivable, const BMessage* archive) { // AllUnarchived perform_data_all_unarchived performData; performData.archive = archive; archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData); return performData.return_value; } extern "C" status_t _ReservedArchivable2__11BArchivable(BArchivable* archivable, BMessage* archive) { // AllArchived perform_data_all_archived performData; performData.archive = archive; archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData); return performData.return_value; } #elif __GNUC__ > 2 extern "C" status_t _ZN11BArchivable20_ReservedArchivable1Ev(BArchivable* archivable, const BMessage* archive) { // AllUnarchived perform_data_all_unarchived performData; performData.archive = archive; archivable->Perform(PERFORM_CODE_ALL_UNARCHIVED, &performData); return performData.return_value; } extern "C" status_t _ZN11BArchivable20_ReservedArchivable2Ev(BArchivable* archivable, BMessage* archive) { // AllArchived perform_data_all_archived performData; performData.archive = archive; archivable->Perform(PERFORM_CODE_ALL_ARCHIVED, &performData); return performData.return_value; } #endif // _GNUC__ > 2 void BArchivable::_ReservedArchivable3() {}