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 66 87 75.8
branch 32 110 29.0
condition n/a
subroutine n/a
pod n/a
total 98 197 49.7


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