File Coverage

/usr/local/lib/perl5/site_perl/5.26.1/x86_64-linux/XS/Framework.x/i/xs/typemap/object.h
Criterion Covered Total %
statement 78 96 81.2
branch 188 620 30.3
condition n/a
subroutine n/a
pod n/a
total 266 716 37.1


line stmt bran cond sub pod time code
1             #pragma once
2             #include "base.h"
3             #include "../Ref.h"
4             #include "../Stash.h"
5             #include "../catch.h"
6             #include "../Backref.h"
7             #include <typeinfo>
8             #include <panda/refcnt.h>
9              
10             namespace xs {
11              
12             using panda::refcnt_inc;
13             using panda::refcnt_dec;
14             using panda::refcnt_get;
15              
16             namespace typemap { namespace object {
17             using svt_clear_t = int(*)(pTHX_ SV*, MAGIC*);
18             using svt_copy_t = int(*)(pTHX_ SV*, MAGIC*, SV*, const char*, I32);
19              
20             extern CV* fake_dtor;
21             extern svt_copy_t backref_marker;
22              
23             void init ();
24              
25             template <class T> struct TypemapMarker {
26 0           static int func (pTHX_ SV*, MAGIC*) { return 0; }
27 47552 100         PANDA_GLOBAL_MEMBER_PTR(TypemapMarker, svt_clear_t, get, &func);
    100          
    100          
    100          
    100          
28             };
29              
30             panda::string type_details(const std::type_info&);
31             [[ noreturn ]] void _throw_incorrect_arg(SV* arg, const std::type_info& expected, panda::string_view package);
32             [[ noreturn ]] void _throw_no_package (const std::type_info&);
33             }}
34              
35             template <class TYPEMAP, class TYPE>
36             struct ObjectStorageIV {
37             static const bool auto_disposable = false;
38              
39             static inline void* get (SV* arg) {
40             return SvIOK(arg) ? (void*)SvIVX(arg) : nullptr;
41             }
42              
43             static inline void set (SV* arg, void* ptr) {
44             SvIOK_on(arg);
45             SvIVX(arg) = (IV)ptr;
46             }
47              
48             static Sv out (const TYPE& var, const Sv& proto) {
49             return Typemap<TYPE>::create(var, proto);
50             }
51             };
52              
53             template <class TYPEMAP, class TYPE, class BACKREF>
54             struct ObjectStorageMG_Impl {
55             using PURE_TYPEMAP = std::remove_const_t<std::remove_pointer_t<std::remove_reference_t<TYPEMAP>>>;
56              
57             static const bool auto_disposable = true;
58              
59 16615           static inline void* get (SV* arg) {
60 16615           MAGIC* mg = _get_magic(arg);
61 16615 50         return mg ? mg->mg_ptr : NULL;
    50          
    50          
    50          
    50          
62             }
63              
64 7161           static inline void set (SV* arg, void* ptr) {
65 7161 50         auto marker = xs::Sv::PayloadMarker<TYPE>::get();
    50          
    50          
    50          
    50          
66 7161 50         marker->svt_clear = typemap::object::TypemapMarker<PURE_TYPEMAP>::get();
    50          
    50          
    50          
    50          
67 7161           marker->svt_free = _on_free;
68 7161           _set_br(marker, BACKREF());
69              
70             MAGIC* mg;
71 7161 50         Newx(mg, 1, MAGIC);
    50          
    50          
    50          
    50          
72 7161           mg->mg_moremagic = SvMAGIC(arg);
73 7161           SvMAGIC_set(arg, mg);
74 7161           mg->mg_virtual = marker;
75 7161           mg->mg_type = PERL_MAGIC_ext;
76 7161           mg->mg_len = 0;
77 7161           mg->mg_obj = nullptr;
78 7161           mg->mg_ptr = (char*)ptr;
79 7161           mg->mg_private = 0;
80              
81             #ifdef USE_ITHREADS
82             marker->svt_dup = _on_svdup;
83             mg->mg_flags = MGf_DUP;
84             #else
85 7161           mg->mg_flags = 0;
86             #endif
87 7161           }
88              
89 7161 50         static Sv out (const TYPE& var, const Sv& proto) { return _out(var, proto, BACKREF()); }
    50          
    50          
    50          
    50          
90              
91             private:
92 16615           static inline MAGIC* _get_magic (SV* sv) {
93 16615           auto marker = typemap::object::TypemapMarker<PURE_TYPEMAP>::get();
94             MAGIC *mg;
95 18389 50         for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) if (mg->mg_virtual && mg->mg_virtual->svt_clear == marker) return mg;
    100          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
96 0           return NULL;
97             }
98              
99 7161           static inline void _set_br (Sv::payload_marker_t*, std::false_type) {}
100             static inline void _set_br (Sv::payload_marker_t* marker, std::true_type) {
101             marker->svt_copy = typemap::object::backref_marker;
102             marker->svt_local = _destroy_hook;
103             }
104              
105 14322           static inline Sv _out (const TYPE& var, const Sv& proto, std::false_type) { return Typemap<TYPE>::create(var, proto); }
106              
107             static inline Sv _out (const TYPE& var, const Sv& proto, std::true_type) {
108             auto br = Backref::get(var);
109             if (!br) return Typemap<TYPE>::create(var, proto);
110             if (br->svobj) {
111             if (!std::is_const<std::remove_pointer_t<TYPE>>::value) SvREADONLY_off(br->svobj);
112             if (!br->zombie) return Ref::create(br->svobj);
113             _from_zombie(Typemap<TYPE>::IType::template cast<TYPEMAP>(var), br->svobj, _get_magic(br->svobj), br);
114             return Sv::noinc(newRV_noinc(br->svobj));
115             }
116             auto ret = Typemap<TYPE>::create(var, proto);
117             br->svobj = SvRV(ret);
118             return ret;
119             }
120              
121             // this hook is invoked before perl frees SV and before DESTROY() method if SV is an object
122             // if non-zero value is returned then the destruction of SV is completely aborted (and DESTROY() method is not called)
123             static int _destroy_hook (pTHX_ SV* sv, MAGIC* mg) { return throw_guard(Sub(), [=]() -> int {
124             TYPEMAP var = Typemap<TYPE>::IType::template in<TYPEMAP>(mg->mg_ptr);
125             auto br = Backref::get(var);
126             if (!br) return 0;
127              
128             if (br->zombie) {
129             // as no one has strong reference to our zombie SV backref, its destruction is only possible in 2 cases:
130             // - decremented from C destructor of XSBackref class
131             // - perl is cleaning his arena in destruction phase
132             _restore_dtor(sv);
133             _from_zombie(var, sv, mg, br);
134             if (br->in_cdtor) Typemap<TYPE>::IType::retain(var); // avoid double deletion if refcnt policy of 'var' drops to 0 during deletion
135             else assert(PL_in_clean_objs);
136             return 0;
137             }
138              
139             // if we are the only owner or in global destroy phase there is no sense of making zombie
140             if (Typemap<TYPE>::IType::use_count(var) <= 1 || PL_in_clean_objs) {
141             _restore_dtor(sv);
142             br->svobj = NULL;
143             return 0;
144             }
145              
146             // perl SV goes out of scope, but C object is still accessible -> save SV to zombie
147             _to_zombie(var, sv, mg, br);
148             return 1;
149             });}
150              
151             struct ZombieMarker {};
152              
153             static inline MAGIC* _zombie_get_stash_magic (HV* stash) {
154             auto marker = xs::Sv::PayloadMarker<ZombieMarker>::get();
155             MAGIC *mg;
156             for (mg = SvMAGIC(stash); mg; mg = mg->mg_moremagic) if (mg->mg_virtual == marker) return mg;
157             return NULL;
158             }
159              
160             #if PERL_VERSION >= 24
161             // prevent S_curse from calling dtor
162             static inline void _ignore_dtor (SV* sv) {
163             auto stash = SvSTASH(sv);
164             auto meta = HvMROMETA(stash);
165             if (meta->destroy == typemap::object::fake_dtor) return;
166             auto stmg = _zombie_get_stash_magic(stash);
167             if (!stmg) stmg = Sv(stash).payload_attach(Sv::Payload(), xs::Sv::PayloadMarker<ZombieMarker>::get());
168             stmg->mg_obj = (SV*)meta->destroy;
169             stmg->mg_ptr = (char*)(uint64_t)meta->destroy_gen;
170             meta->destroy = typemap::object::fake_dtor;
171             meta->destroy_gen = PL_sub_generation; // make cache valid
172             }
173              
174             static inline void _restore_dtor (SV* sv) {
175             auto stash = SvSTASH(sv);
176             auto meta = HvMROMETA(stash);
177             if (meta->destroy != typemap::object::fake_dtor) return;
178             auto stmg = _zombie_get_stash_magic(stash);
179             meta->destroy = (CV*)stmg->mg_obj; // restore dtor
180             meta->destroy_gen = (uint64_t)stmg->mg_ptr;
181             }
182             #else
183             static inline void _ignore_dtor (SV*) {}
184             static inline void _restore_dtor (SV*) {}
185             #endif
186              
187             static inline void _to_zombie (const TYPEMAP& var, SV* sv, MAGIC*, const Backref* br) {
188             br->zombie = true;
189             SvREFCNT(sv)++;
190             Typemap<TYPE>::IType::release(var);
191             _ignore_dtor(sv);
192             }
193              
194             static inline void _from_zombie (const TYPEMAP& var, SV*, MAGIC*, const Backref* br) {
195             br->zombie = false;
196             Typemap<TYPE>::IType::retain(var);
197             }
198              
199 28644           static int _on_free (pTHX_ SV* sv, MAGIC* mg) {return throw_guard(Sub(), [=]() -> int {
200             using IType = typename Typemap<TYPE>::IType;
201 7161           TYPEMAP downgraded = IType::template in<TYPEMAP>(mg->mg_ptr);
202 7161 50         TYPE var = Typemap<TYPE>::template cast<TYPE,TYPEMAP>(downgraded);
    50          
    50          
    50          
    50          
203 7161 50         if (!var) throw "TYPEMAP PANIC: bad object in sv";
    50          
    50          
    50          
    50          
204 7161 50         Typemap<TYPE>::dispose(var, sv);
    50          
    50          
    50          
    50          
205 7161           return 0;
206 14322 50         });}
    50          
    50          
    50          
    50          
207              
208             static int _on_svdup (pTHX_ MAGIC* mg, CLONE_PARAMS*) { return throw_guard(Sub(), [=]() -> int {
209             using IType = typename Typemap<TYPE>::IType;
210             TYPEMAP downgraded = IType::template in<TYPEMAP>(mg->mg_ptr);
211             TYPEMAP new_downgraded = Typemap<TYPE>::dup(downgraded);
212             _on_svdup_br(downgraded, new_downgraded, BACKREF());
213             mg->mg_ptr = (char*)IType::out(new_downgraded);
214             return 0;
215             });}
216              
217             static void _on_svdup_br (const TYPEMAP&, const TYPEMAP&, std::false_type) {}
218             static void _on_svdup_br (const TYPEMAP& var, const TYPEMAP& new_var, std::true_type) {
219             auto br = Backref::get(var);
220             auto new_br = Backref::get(new_var);
221             if (br && br->svobj && new_br) {
222             new_br->svobj = MUTABLE_SV(ptr_table_fetch(PL_ptr_table, br->svobj));
223             assert(new_br->svobj);
224             }
225             }
226             };
227              
228             template <class TYPEMAP, class TYPE> using ObjectStorageMG = ObjectStorageMG_Impl<TYPEMAP,TYPE,std::false_type>;
229             template <class TYPEMAP, class TYPE> using ObjectStorageMGBackref = ObjectStorageMG_Impl<TYPEMAP,TYPE,std::true_type>;
230              
231             struct ObjectTypePtr {
232 11892           template <class T> static inline T in (void* p) { return static_cast<T>(p); }
233 2120           template <class T> static inline const void* out (T* var) { return var; }
234 2120 50         template <class T> static inline void destroy (T* var, SV*) { delete var; }
    50          
    50          
235              
236 14012           template <class TO, class FROM> static inline TO cast (FROM* var) { return static_cast<TO>(const_cast<std::remove_const_t<FROM>*>(var)); }
237             template <class TO, class FROM> static inline TO upgrade (FROM* var) { return panda::dyn_cast<TO>(var); }
238             };
239              
240             struct ObjectTypeForeignPtr {
241             template <class T> static inline T in (void* p) { return static_cast<T>(p); }
242             template <class T> static inline const void* out (T* var) { return var; }
243             template <class T> static inline void destroy (T*, SV*) {}
244              
245             template <class TO, class FROM> static inline TO cast (FROM* var) { return static_cast<TO>(const_cast<std::remove_const_t<FROM>*>(var)); }
246             template <class TO, class FROM> static inline TO upgrade (FROM* var) { return panda::dyn_cast<TO>(var); }
247             };
248              
249             struct ObjectTypeRefcntPtr {
250 35660           template <class T> static inline T in (void* p) { return static_cast<T>(p); }
251 12202           template <class T> static inline const void* out (T* var) { refcnt_inc(var); return var; }
252 12202           template <class T> static inline void destroy (T* var, SV*) { refcnt_dec(var); }
253             template <class T> static inline void retain (T* var) { refcnt_inc(var); }
254             template <class T> static inline void release (T* var) { refcnt_dec(var); }
255             template <class T> static inline uint32_t use_count (T* var) { return refcnt_get(var); }
256              
257 47862           template <class TO, class FROM> static inline TO cast (FROM* var) { return static_cast<TO>(const_cast<std::remove_const_t<FROM>*>(var)); }
258             template <class TO, class FROM> static inline TO upgrade (FROM* var) { return panda::dyn_cast<TO>(var); }
259             };
260              
261             struct ObjectTypeSharedPtr {
262             template <class T> static inline T in (void* p) { return *(static_cast<T*>(p)); }
263             template <class T> static inline const void* out (const std::shared_ptr<T>& var) { return new std::shared_ptr<T>(var); }
264              
265             template <class T> static inline void retain (const std::shared_ptr<T>& var) {
266             char tmp[sizeof(var)];
267             new (tmp) std::shared_ptr<T>(var);
268             }
269              
270             template <class T> static inline void release (const std::shared_ptr<T>& var) {
271             std::shared_ptr<T> tmp;
272             memcpy(&tmp, &var, sizeof(tmp));
273             }
274              
275             template <class T> static inline uint32_t use_count (const std::shared_ptr<T>& var) { return var.use_count() - 1; }
276              
277             template <class T> static inline void destroy (const std::shared_ptr<T>&, SV* arg) {
278             using sp_t = std::shared_ptr<T>;
279             void* p = Typemap<sp_t>::IStorage::get(arg);
280             delete static_cast<sp_t*>(p);
281             }
282              
283             template <class TO, class FROM> static inline TO cast (const std::shared_ptr<FROM>& var) {
284             return std::static_pointer_cast<typename TO::element_type>(std::const_pointer_cast<std::remove_const_t<FROM>>(var));
285             }
286              
287             template <class TO, class FROM> static inline TO upgrade (const std::shared_ptr<FROM>& var) {
288             return std::dynamic_pointer_cast<typename TO::element_type>(var);
289             }
290             };
291              
292             using StaticCast = std::true_type;
293             using DynamicCast = std::false_type;
294              
295             template <class TYPEMAP, class TYPE, class _IType, template<class,class> class _IStorage, class CastType = StaticCast>
296             struct TypemapObject : TypemapBase<TYPEMAP, TYPE> {
297             using IType = _IType;
298             using IStorage = _IStorage<TYPEMAP,TYPE>;
299             using TypeNP = typename std::remove_pointer<TYPE>::type;
300              
301 16615           static TYPE in (SV* arg) {
302 16615 50         if (!SvOBJECT(arg)) {
    50          
    50          
    50          
    50          
303 16615 50         if (SvROK(arg)) {
    50          
    50          
    50          
    50          
304 16615           arg = SvRV(arg);
305 16615 50         if (!SvOBJECT(arg)) throw "arg is a reference to non-object";
    50          
    50          
    50          
    50          
306             }
307 0 0         else if (!SvOK(arg)) return TYPE();
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
308 0           else throw "arg is not a reference to object";
309             }
310              
311 16615 50         auto ptr = IStorage::get(arg);
    50          
    50          
    50          
    50          
312 16615 50         if (ptr) {
    50          
    50          
    50          
    50          
313 16615           TYPEMAP downgraded = IType::template in<TYPEMAP>(ptr);
314 16615 50         TYPE ret = cast<TYPE,TYPEMAP>(downgraded);
    50          
    50          
    50          
    50          
315 16615 50         if (ret) {
    50          
    50          
    50          
    50          
316 16615 50         if (!std::is_const<TypeNP>::value && SvREADONLY(arg)) throw "cannot modify read-only object";
    50          
    50          
    50          
    50          
317 16615           return ret;
318             }
319             }
320              
321             // it's definitely developer bug, there is no known way to determine real object tyep
322             // from void*, see https://stackoverflow.com/questions/1718412/find-out-type-of-c-void-pointer
323 0 0         auto package = Typemap<TYPE>::package();
    0          
    0          
    0          
    0          
324 0           auto package_view = panda::string_view{ package.data(), package.length() };
325 16615           typemap::object::_throw_incorrect_arg(arg, typeid (TYPE), package_view);
326             }
327              
328 14322           static Sv out (const TYPE& var, const Sv& proto = Sv()) { return IStorage::out(var, proto); }
329              
330             /* proto is a hint for TypemapObject's out/create to attach 'var' to
331             * it might be:
332             * 1) blessed object (or reference to it): in this case it is used as final object
333             * typical usage - when calling next method
334             * PROTO = stash.call_next(...);
335             * 2) class name or stash to bless to: in this case reference to undef is created and blessed into this class
336             * typical usage - in constructor, to bless to the class 'new' was called into, not to default class
337             * PROTO = ST(0);
338             * 3) other values (or reference to it): in this case it is blessed to typemap's default class and used
339             * typical usage - in constructor or in overloaded typemap's create() method to specify object's base
340             * PROTO = Array::create();
341             * 4) empty: in this case reference to undef is created and blessed to typemap's default class and used
342             */
343 7161           static Sv create (const TYPE& var, const Sv& proto = Sv()) {
344 7161 50         if (!var) return &PL_sv_undef;
    50          
    50          
    50          
    50          
345 14322           Sv rv;
346             SV* base;
347 7161 50         if (proto) {
    50          
    50          
    50          
    100          
348 2502 0         if (SvROK(proto)) { // ref to object/base
    50          
    0          
    0          
    50          
349 0 0         rv = proto;
    0          
    0          
    0          
    0          
350 0           base = SvRV(proto);
351             }
352 2502 0         else if (proto.type() <= SVt_PVMG) { // class name
    50          
    0          
    0          
    50          
353 2502 0         if (SvOBJECT(proto)) {
    50          
    0          
    0          
    50          
354 0           base = proto;
355 0 0         rv = Sv::noinc(newRV_noinc(base));
    0          
    0          
    0          
    0          
356             } else {
357 2502 0         base = newSV_type(SVt_PVMG);
    50          
    0          
    0          
    50          
358 2502 0         rv = Sv::noinc(newRV_noinc(base));
    50          
    0          
    0          
    50          
359 2502 0         sv_bless(rv, gv_stashsv(proto, GV_ADD));
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
360             }
361 2502           goto ATTACH; // skip optional blessing
362             }
363 0 0         else if (proto.type() == SVt_PVHV && HvNAME(proto)) { // stash
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
364 0 0         base = newSV_type(SVt_PVMG);
    0          
    0          
    0          
    0          
365 0 0         rv = Sv::noinc(newRV_noinc(base));
    0          
    0          
    0          
    0          
366 0 0         sv_bless(rv, proto.get<HV>());
    0          
    0          
    0          
    0          
367 0           goto ATTACH; // skip optional blessing
368             }
369             else { // base given
370 0 0         rv = Sv::noinc(newRV(proto));
    0          
    0          
    0          
    0          
371 0           base = proto;
372             }
373             }
374             else { // nothing given, create ref to undef
375 4659 50         base = newSV_type(SVt_PVMG);
    0          
    50          
    50          
    50          
376 4659 50         rv = Sv::noinc(newRV_noinc(base));
    0          
    50          
    50          
    50          
377 4659           goto BLESS; // definitely needs blessing
378             }
379              
380 0 0         if (!SvOBJECT(base)) { // not blessed -> bless to default typemap's class
    0          
    0          
    0          
    0          
381             BLESS:
382 4659 100         static PERL_ITHREADS_LOCAL HV* stash = gv_stashpvn(Typemap<TYPE>::package().data(), Typemap<TYPE>::package().length(), GV_ADD);
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    50          
    50          
    0          
    100          
    50          
    50          
    50          
    50          
    0          
    100          
    50          
    50          
    50          
    50          
    0          
383 4659 50         sv_bless(rv, stash); // TODO: custom faster bless
    0          
    50          
    50          
    50          
384             }
385              
386             ATTACH:
387 7161 50         IStorage::set(base, const_cast<void*>(IType::out(IType::template cast<TYPEMAP>(var))));
    50          
    50          
    50          
    50          
    50          
    50          
388              
389             if (std::is_const<TypeNP>::value) SvREADONLY_on(base);
390              
391 7161           return rv;
392             }
393              
394             static TYPEMAP dup (const TYPEMAP& obj) { return obj; }
395              
396 7161           static void dispose (const TYPE& var, SV* arg) {
397 7161           IType::destroy(var, arg);
398 7161           }
399              
400             static void destroy (const TYPE& var, SV* arg) {
401             if (!std::is_same<TYPEMAP, TYPE>::value) return;
402             if (!IStorage::auto_disposable) dispose(var, arg);
403             }
404              
405 23776 50         template <class TO, class FROM> static inline TO cast (FROM v) { return _cast<TO, FROM>(v, CastType()); }
    50          
    50          
    50          
    50          
406              
407             static panda::string_view package () { typemap::object::_throw_no_package(typeid(TYPE)); return ""; }
408              
409             static Stash default_stash () {
410             static PERL_ITHREADS_LOCAL Stash stash = gv_stashpvn(Typemap<TYPE>::package().data(), Typemap<TYPE>::package().length(), GV_ADD);
411             return stash;
412             }
413              
414             private:
415             template <class TO, class FROM> static inline TO _cast (FROM v, DynamicCast) { return IType::template upgrade<TO>(v); }
416 47552           template <class TO, class FROM> static inline TO _cast (FROM v, StaticCast) { return IType::template cast<TO>(v); }
417             };
418              
419             }