2 * Copyright 2001-2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Rene Gollent (rene@gollent.com)
7 * Erik Jaesler (erik@cgsoftware.com)
8 * Alex Wilson (yourpalal2@gmail.com)
11 /*! BArchivable mix-in class defines the archiving protocol.
12 Also some global archiving functions.
25 #include <AppFileInfo.h>
26 #include <Archivable.h>
34 #include <binary_compatibility/Support.h>
36 #include "ArchivingManagers.h"
42 using namespace BPrivate::Archiving
;
44 const char* B_CLASS_FIELD
= "class";
45 const char* B_ADD_ON_FIELD
= "add_on";
46 const int32 FUNC_NAME_LEN
= 1024;
48 // TODO: consider moving these to a separate module, and making them more
49 // full-featured (e.g., taking NS::ClassName::Function(Param p) instead
50 // of just NS::ClassName)
54 demangle_class_name(const char* name
, BString
& out
)
56 // TODO: add support for template classes
57 // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
66 while ((nameLen
= strtoul(name
, (char**)&name
, 10))) {
71 out
.Append(name
, nameLen
);
79 // The name is in a namespace
80 int namespaceCount
= 0;
83 // more than 10 namespaces deep
84 if (!isdigit(*++name
))
87 namespaceCount
= strtoul(name
, (char**)&name
, 10);
91 namespaceCount
= name
[0] - '0';
95 for (int i
= 0; i
< namespaceCount
- 1; i
++) {
96 if (!isdigit(name
[0]))
99 int nameLength
= strtoul(name
, (char**)&name
, 10);
100 out
.Append(name
, nameLength
);
106 int nameLength
= strtoul(name
, (char**)&name
, 10);
107 out
.Append(name
, nameLength
);
115 mangle_class_name(const char* name
, BString
& out
)
117 // TODO: add support for template classes
118 // _find__t12basic_string3ZcZt18string_char_traits1ZcZt24__default_alloc_template2b0i0PCccUlUl
121 // testthree::testfour::Testthree::Testfour
122 // up into little bite-sized pieces
124 string
origName(name
);
125 vector
<string
> spacenames
;
127 string::size_type pos
= 0;
128 string::size_type oldpos
= 0;
129 while (pos
!= string::npos
) {
130 pos
= origName
.find_first_of("::", oldpos
);
131 spacenames
.push_back(string(origName
, oldpos
, pos
- oldpos
));
132 pos
= origName
.find_first_not_of("::", pos
);
137 // Now mangle it into this:
138 // 9testthree8testfour9Testthree8Testfour
139 // (for __GNUC__ > 2)
140 // this isn't always the proper mangled class name, it should
141 // actually have an 'N' prefix and 'E' suffix if the name is
142 // in > 0 namespaces, but these would have to be removed in
143 // build_function_name() (the only place this function is called)
144 // so we don't add them.
146 // Q49testthree8testfour9Testthree8Testfour
147 // (for __GNUC__ == 2)
161 for (unsigned int i
= 0; i
< spacenames
.size(); ++i
) {
162 out
<< (int)spacenames
[i
].length();
163 out
+= spacenames
[i
].c_str();
169 build_function_name(const BString
& className
, BString
& funcName
)
173 // This is what we're after:
174 // Instantiate__Q28OpenBeOS11BArchivableP8BMessage
175 mangle_class_name(className
.String(), funcName
);
177 funcName
.Prepend("_ZN");
178 funcName
.Append("11InstantiateE");
180 funcName
.Prepend("Instantiate__");
182 funcName
.Append("P8BMessage");
187 add_private_namespace(BString
& name
)
189 if (name
.Compare("_", 1) != 0)
192 name
.Prepend("BPrivate::");
197 static instantiation_func
198 find_function_in_image(BString
& funcName
, image_id id
, status_t
& err
)
200 instantiation_func instantiationFunc
= NULL
;
201 err
= get_image_symbol(id
, funcName
.String(), B_SYMBOL_TYPE_TEXT
,
202 (void**)&instantiationFunc
);
206 return instantiationFunc
;
211 check_signature(const char* signature
, image_info
& info
)
213 if (signature
== NULL
) {
214 // If it wasn't specified, anything "matches"
218 // Get image signature
219 BFile
file(info
.name
, B_READ_ONLY
);
220 status_t err
= file
.InitCheck();
224 char imageSignature
[B_MIME_TYPE_LENGTH
];
225 BAppFileInfo
appFileInfo(&file
);
226 err
= appFileInfo
.GetSignature(imageSignature
);
228 syslog(LOG_ERR
, "instantiate_object - couldn't get mime sig for %s",
233 if (strcmp(signature
, imageSignature
) != 0)
234 return B_MISMATCHED_VALUES
;
243 find_instantiation_func(const char* className
, const char* signature
,
246 if (className
== NULL
) {
251 thread_info threadInfo
;
252 status_t err
= get_thread_info(find_thread(NULL
), &threadInfo
);
258 instantiation_func instantiationFunc
= NULL
;
259 image_info imageInfo
;
261 BString name
= className
;
262 for (int32 pass
= 0; pass
< 2; pass
++) {
264 build_function_name(name
, funcName
);
266 // for each image_id in team_id
268 while (instantiationFunc
== NULL
269 && get_next_image_info(threadInfo
.team
, &cookie
, &imageInfo
)
271 instantiationFunc
= find_function_in_image(funcName
, imageInfo
.id
,
274 if (instantiationFunc
!= NULL
) {
275 // if requested, save the image id in
276 // which the function was found
282 // Check if we have a private class, and add the BPrivate namespace
283 // (for backwards compatibility)
284 if (!add_private_namespace(name
))
288 if (instantiationFunc
!= NULL
289 && check_signature(signature
, imageInfo
) != B_OK
)
292 return instantiationFunc
;
295 } // namespace BPrivate
298 // #pragma mark - BArchivable
301 BArchivable::BArchivable()
303 fArchivingToken(NULL_TOKEN
)
308 BArchivable::BArchivable(BMessage
* from
)
310 fArchivingToken(NULL_TOKEN
)
312 if (BUnarchiver::IsArchiveManaged(from
)) {
313 BUnarchiver::PrepareArchive(from
);
314 BUnarchiver(from
).RegisterArchivable(this);
319 BArchivable::~BArchivable()
325 BArchivable::Archive(BMessage
* into
, bool deep
) const
328 // TODO: logging/other error reporting?
332 if (BManagerBase::ArchiveManager(into
))
333 BArchiver(into
).RegisterArchivable(this);
336 status_t status
= demangle_class_name(typeid(*this).name(), name
);
340 return into
->AddString(B_CLASS_FIELD
, name
);
345 BArchivable::Instantiate(BMessage
* from
)
347 debugger("Can't create a plain BArchivable object");
353 BArchivable::Perform(perform_code d
, void* arg
)
356 case PERFORM_CODE_ALL_UNARCHIVED
:
358 perform_data_all_unarchived
* data
=
359 (perform_data_all_unarchived
*)arg
;
361 data
->return_value
= BArchivable::AllUnarchived(data
->archive
);
365 case PERFORM_CODE_ALL_ARCHIVED
:
367 perform_data_all_archived
* data
=
368 (perform_data_all_archived
*)arg
;
370 data
->return_value
= BArchivable::AllArchived(data
->archive
);
375 return B_NAME_NOT_FOUND
;
380 BArchivable::AllUnarchived(const BMessage
* archive
)
387 BArchivable::AllArchived(BMessage
* archive
) const
393 // #pragma mark - BArchiver
396 BArchiver::BArchiver(BMessage
* archive
)
398 fManager(BManagerBase::ArchiveManager(archive
)),
402 if (fManager
== NULL
)
403 fManager
= new BArchiveManager(this);
407 BArchiver::~BArchiver()
410 fManager
->ArchiverLeaving(this, B_OK
);
415 BArchiver::AddArchivable(const char* name
, BArchivable
* archivable
, bool deep
)
418 status_t err
= GetTokenForArchivable(archivable
, deep
, token
);
423 return fArchive
->AddInt32(name
, token
);
428 BArchiver::GetTokenForArchivable(BArchivable
* archivable
,
429 bool deep
, int32
& _token
)
431 return fManager
->ArchiveObject(archivable
, deep
, _token
);
436 BArchiver::IsArchived(BArchivable
* archivable
)
438 return fManager
->IsArchived(archivable
);
443 BArchiver::Finish(status_t err
)
446 debugger("Finish() called multiple times on same BArchiver.");
450 return fManager
->ArchiverLeaving(this, err
);
455 BArchiver::ArchiveMessage() const
462 BArchiver::RegisterArchivable(const BArchivable
* archivable
)
464 fManager
->RegisterArchivable(archivable
);
468 // #pragma mark - BUnarchiver
471 BUnarchiver::BUnarchiver(const BMessage
* archive
)
473 fManager(BManagerBase::UnarchiveManager(archive
)),
480 BUnarchiver::~BUnarchiver()
482 if (!fFinished
&& fManager
)
483 fManager
->UnarchiverLeaving(this, B_OK
);
489 BUnarchiver::GetObject
<BArchivable
>(int32 token
,
490 ownership_policy owning
, BArchivable
*& object
)
492 _CallDebuggerIfManagerNull();
493 return fManager
->GetArchivableForToken(token
, owning
, object
);
499 BUnarchiver::FindObject
<BArchivable
>(const char* name
,
500 int32 index
, ownership_policy owning
, BArchivable
*& archivable
)
504 status_t err
= fArchive
->FindInt32(name
, index
, &token
);
508 return GetObject(token
, owning
, archivable
);
513 BUnarchiver::IsInstantiated(int32 token
)
515 _CallDebuggerIfManagerNull();
516 return fManager
->IsInstantiated(token
);
521 BUnarchiver::IsInstantiated(const char* field
, int32 index
)
524 if (fArchive
->FindInt32(field
, index
, &token
) == B_OK
)
525 return IsInstantiated(token
);
532 BUnarchiver::Finish(status_t err
)
535 debugger("Finish() called multiple times on same BArchiver.");
539 return fManager
->UnarchiverLeaving(this, err
);
546 BUnarchiver::ArchiveMessage() const
553 BUnarchiver::AssumeOwnership(BArchivable
* archivable
)
555 _CallDebuggerIfManagerNull();
556 fManager
->AssumeOwnership(archivable
);
561 BUnarchiver::RelinquishOwnership(BArchivable
* archivable
)
563 _CallDebuggerIfManagerNull();
564 fManager
->RelinquishOwnership(archivable
);
569 BUnarchiver::IsArchiveManaged(const BMessage
* archive
)
571 // managed child archives will return here
572 if (BManagerBase::ManagerPointer(archive
))
578 // managed top level archives return here
580 if (archive
->FindBool(kManagedField
, &dummy
) == B_OK
)
589 BUnarchiver::InstantiateObject
<BArchivable
>(BMessage
* from
,
590 BArchivable
* &object
)
592 BUnarchiver
unarchiver(BUnarchiver::PrepareArchive(from
));
593 object
= instantiate_object(from
);
594 return unarchiver
.Finish();
599 BUnarchiver::PrepareArchive(BMessage
* &archive
)
601 // this check allows PrepareArchive to be
602 // called on new or old-style archives
603 if (BUnarchiver::IsArchiveManaged(archive
)) {
604 BUnarchiveManager
* manager
= BManagerBase::UnarchiveManager(archive
);
606 manager
= new BUnarchiveManager(archive
);
616 BUnarchiver::RegisterArchivable(BArchivable
* archivable
)
618 _CallDebuggerIfManagerNull();
619 fManager
->RegisterArchivable(archivable
);
624 BUnarchiver::_CallDebuggerIfManagerNull()
627 debugger("BUnarchiver used with legacy or unprepared archive.");
635 instantiate_object(BMessage
* archive
, image_id
* _id
)
637 status_t statusBuffer
;
638 status_t
* status
= &statusBuffer
;
643 if (archive
== NULL
) {
644 syslog(LOG_ERR
, "instantiate_object failed: NULL BMessage argument");
645 *status
= B_BAD_VALUE
;
649 // Get class name from archive
650 const char* className
= NULL
;
651 status_t err
= archive
->FindString(B_CLASS_FIELD
, &className
);
653 syslog(LOG_ERR
, "instantiate_object failed: Failed to find an entry "
654 "defining the class name (%s).", strerror(err
));
655 *status
= B_BAD_VALUE
;
659 // Get sig from archive
660 const char* signature
= NULL
;
661 bool hasSignature
= archive
->FindString(B_ADD_ON_FIELD
, &signature
) == B_OK
;
663 instantiation_func instantiationFunc
= BPrivate::find_instantiation_func(
664 className
, signature
, _id
);
666 // if find_instantiation_func() can't locate Class::Instantiate()
667 // and a signature was specified
668 if (!instantiationFunc
&& hasSignature
) {
669 // use BRoster::FindApp() to locate an app or add-on with the symbol
672 err
= Roster
.FindApp(signature
, &ref
);
674 // if an entry_ref is obtained
677 err
= entry
.SetTo(&ref
);
681 err
= entry
.GetPath(&path
);
684 syslog(LOG_ERR
, "instantiate_object failed: Error finding app "
685 "with signature \"%s\" (%s)", signature
, strerror(err
));
690 // load the app/add-on
691 image_id addOn
= load_add_on(path
.Path());
693 syslog(LOG_ERR
, "instantiate_object failed: Could not load "
694 "add-on %s: %s.", path
.Path(), strerror(addOn
));
703 BString name
= className
;
704 for (int32 pass
= 0; pass
< 2; pass
++) {
706 build_function_name(name
, funcName
);
708 instantiationFunc
= find_function_in_image(funcName
, addOn
, err
);
709 if (instantiationFunc
!= NULL
)
712 // Check if we have a private class, and add the BPrivate namespace
713 // (for backwards compatibility)
714 if (!add_private_namespace(name
))
718 if (instantiationFunc
== NULL
) {
719 syslog(LOG_ERR
, "instantiate_object failed: Failed to find exported "
720 "Instantiate static function for class %s.", className
);
721 *status
= B_NAME_NOT_FOUND
;
724 } else if (instantiationFunc
== NULL
) {
725 syslog(LOG_ERR
, "instantiate_object failed: No signature specified "
726 "in archive, looking for class \"%s\".", className
);
727 *status
= B_NAME_NOT_FOUND
;
731 // if Class::Instantiate(BMessage*) was found
732 if (instantiationFunc
!= NULL
) {
733 // use to create and return an object instance
734 return instantiationFunc(archive
);
742 instantiate_object(BMessage
* from
)
744 return instantiate_object(from
, NULL
);
748 // #pragma mark - support_globals
752 validate_instantiation(BMessage
* from
, const char* className
)
754 // Make sure our params are kosher -- original skimped here =P
760 BString name
= className
;
761 for (int32 pass
= 0; pass
< 2; pass
++) {
762 const char* archiveClassName
;
763 for (int32 index
= 0; from
->FindString(B_CLASS_FIELD
, index
,
764 &archiveClassName
) == B_OK
; ++index
) {
765 if (name
== archiveClassName
)
769 if (!add_private_namespace(name
))
773 errno
= B_MISMATCHED_VALUES
;
774 syslog(LOG_ERR
, "validate_instantiation failed on class %s.", className
);
781 find_instantiation_func(const char* className
, const char* signature
)
783 return BPrivate::find_instantiation_func(className
, signature
, NULL
);
788 find_instantiation_func(const char* className
)
790 return find_instantiation_func(className
, NULL
);
795 find_instantiation_func(BMessage
* archive
)
797 if (archive
== NULL
) {
802 const char* name
= NULL
;
803 const char* signature
= NULL
;
804 if (archive
->FindString(B_CLASS_FIELD
, &name
) != B_OK
805 || archive
->FindString(B_ADD_ON_FIELD
, &signature
)) {
810 return find_instantiation_func(name
, signature
);
814 // #pragma mark - BArchivable binary compatibility
820 _ReservedArchivable1__11BArchivable(BArchivable
* archivable
,
821 const BMessage
* archive
)
824 perform_data_all_unarchived performData
;
825 performData
.archive
= archive
;
827 archivable
->Perform(PERFORM_CODE_ALL_UNARCHIVED
, &performData
);
828 return performData
.return_value
;
833 _ReservedArchivable2__11BArchivable(BArchivable
* archivable
,
837 perform_data_all_archived performData
;
838 performData
.archive
= archive
;
840 archivable
->Perform(PERFORM_CODE_ALL_ARCHIVED
, &performData
);
841 return performData
.return_value
;
848 _ZN11BArchivable20_ReservedArchivable1Ev(BArchivable
* archivable
,
849 const BMessage
* archive
)
852 perform_data_all_unarchived performData
;
853 performData
.archive
= archive
;
855 archivable
->Perform(PERFORM_CODE_ALL_UNARCHIVED
, &performData
);
856 return performData
.return_value
;
861 _ZN11BArchivable20_ReservedArchivable2Ev(BArchivable
* archivable
,
865 perform_data_all_archived performData
;
866 performData
.archive
= archive
;
868 archivable
->Perform(PERFORM_CODE_ALL_ARCHIVED
, &performData
);
869 return performData
.return_value
;
872 #endif // _GNUC__ > 2
875 void BArchivable::_ReservedArchivable3() {}