Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
876 lines (676 sloc) 17.4 KB
/*
* 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 <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <syslog.h>
#include <typeinfo>
#include <vector>
#include <AppFileInfo.h>
#include <Archivable.h>
#include <Entry.h>
#include <List.h>
#include <OS.h>
#include <Path.h>
#include <Roster.h>
#include <String.h>
#include <binary_compatibility/Support.h>
#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<string> 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<BArchivable>(int32 token,
ownership_policy owning, BArchivable*& object)
{
_CallDebuggerIfManagerNull();
return fManager->GetArchivableForToken(token, owning, object);
}
template<>
status_t
BUnarchiver::FindObject<BArchivable>(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<BArchivable>(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)
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() {}