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 28 30 93.3
branch 24 34 70.5
condition n/a
subroutine n/a
pod n/a
total 52 64 81.2


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
8             #include
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 struct TypemapMarker {
26 0           static int func (pTHX_ SV*, MAGIC*) { return 0; }
27 346 100         PANDA_GLOBAL_MEMBER_PTR(TypemapMarker, svt_clear_t, get, &func);
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
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::create(var, proto);
50             }
51             };
52              
53             template
54             struct ObjectStorageMG_Impl {
55             using PURE_TYPEMAP = std::remove_const_t>>;
56              
57             static const bool auto_disposable = true;
58              
59 120           static inline void* get (SV* arg) {
60 120           MAGIC* mg = _get_magic(arg);
61 120 100         return mg ? mg->mg_ptr : NULL;
62             }
63              
64             static inline void set (SV* arg, void* ptr) {
65             auto marker = xs::Sv::PayloadMarker::get();
66             marker->svt_clear = typemap::object::TypemapMarker::get();
67             marker->svt_free = _on_free;
68             _set_br(marker, BACKREF());
69              
70             MAGIC* mg;
71             Newx(mg, 1, MAGIC);
72             mg->mg_moremagic = SvMAGIC(arg);
73             SvMAGIC_set(arg, mg);
74             mg->mg_virtual = marker;
75             mg->mg_type = PERL_MAGIC_ext;
76             mg->mg_len = 0;
77             mg->mg_obj = nullptr;
78             mg->mg_ptr = (char*)ptr;
79             mg->mg_private = 0;
80              
81             #ifdef USE_ITHREADS
82             marker->svt_dup = _on_svdup;
83             mg->mg_flags = MGf_DUP;
84             #else
85             mg->mg_flags = 0;
86             #endif
87             }
88              
89             static Sv out (const TYPE& var, const Sv& proto) { return _out(var, proto, BACKREF()); }
90              
91             private:
92 120           static inline MAGIC* _get_magic (SV* sv) {
93 120           auto marker = typemap::object::TypemapMarker::get();
94             MAGIC *mg;
95 194 100         for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) if (mg->mg_virtual && mg->mg_virtual->svt_clear == marker) return mg;
    50          
    100          
96 3           return NULL;
97             }
98              
99             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             static inline Sv _out (const TYPE& var, const Sv& proto, std::false_type) { return Typemap::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::create(var, proto);
110             if (br->svobj) {
111             if (!std::is_const>::value) SvREADONLY_off(br->svobj);
112             if (!br->zombie) return Ref::create(br->svobj);
113             _from_zombie(Typemap::IType::template cast(var), br->svobj, _get_magic(br->svobj), br);
114             return Sv::noinc(newRV_noinc(br->svobj));
115             }
116             auto ret = Typemap::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::IType::template in(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::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::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::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::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::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::IType::retain(var);
197             }
198              
199             static int _on_free (pTHX_ SV* sv, MAGIC* mg) {return throw_guard(Sub(), [=]() -> int {
200             using IType = typename Typemap::IType;
201             TYPEMAP downgraded = IType::template in(mg->mg_ptr);
202             TYPE var = Typemap::template cast(downgraded);
203             if (!var) throw "TYPEMAP PANIC: bad object in sv";
204             Typemap::dispose(var, sv);
205             return 0;
206             });}
207              
208             static int _on_svdup (pTHX_ MAGIC* mg, CLONE_PARAMS*) { return throw_guard(Sub(), [=]() -> int {
209             using IType = typename Typemap::IType;
210             TYPEMAP downgraded = IType::template in(mg->mg_ptr);
211             TYPEMAP new_downgraded = Typemap::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 using ObjectStorageMG = ObjectStorageMG_Impl;
229             template using ObjectStorageMGBackref = ObjectStorageMG_Impl;
230              
231             struct ObjectTypePtr {
232             template static inline T in (void* p) { return static_cast(p); }
233             template static inline const void* out (T* var) { return var; }
234             template static inline void destroy (T* var, SV*) { delete var; }
235              
236             template static inline TO cast (FROM* var) { return static_cast(const_cast*>(var)); }
237             template static inline TO upgrade (FROM* var) { return panda::dyn_cast(var); }
238             };
239              
240             struct ObjectTypeForeignPtr {
241 330           template static inline T in (void* p) { return static_cast(p); }
242             template static inline const void* out (T* var) { return var; }
243             template static inline void destroy (T*, SV*) {}
244              
245 436           template static inline TO cast (FROM* var) { return static_cast(const_cast*>(var)); }
246             template static inline TO upgrade (FROM* var) { return panda::dyn_cast(var); }
247             };
248              
249             struct ObjectTypeRefcntPtr {
250             template static inline T in (void* p) { return static_cast(p); }
251             template static inline const void* out (T* var) { refcnt_inc(var); return var; }
252             template static inline void destroy (T* var, SV*) { refcnt_dec(var); }
253             template static inline void retain (T* var) { refcnt_inc(var); }
254             template static inline void release (T* var) { refcnt_dec(var); }
255             template static inline uint32_t use_count (T* var) { return refcnt_get(var); }
256              
257             template static inline TO cast (FROM* var) { return static_cast(const_cast*>(var)); }
258             template static inline TO upgrade (FROM* var) { return panda::dyn_cast(var); }
259             };
260              
261             struct ObjectTypeSharedPtr {
262             template static inline T in (void* p) { return *(static_cast(p)); }
263             template static inline const void* out (const std::shared_ptr& var) { return new std::shared_ptr(var); }
264              
265             template static inline void retain (const std::shared_ptr& var) {
266             char tmp[sizeof(var)];
267             new (tmp) std::shared_ptr(var);
268             }
269              
270             template static inline void release (const std::shared_ptr& var) {
271             std::shared_ptr tmp;
272             memcpy(&tmp, &var, sizeof(tmp));
273             }
274              
275             template static inline uint32_t use_count (const std::shared_ptr& var) { return var.use_count() - 1; }
276              
277             template static inline void destroy (const std::shared_ptr&, SV* arg) {
278             using sp_t = std::shared_ptr;
279             void* p = Typemap::IStorage::get(arg);
280             delete static_cast(p);
281             }
282              
283             template static inline TO cast (const std::shared_ptr& var) {
284             return std::static_pointer_cast(std::const_pointer_cast>(var));
285             }
286              
287             template static inline TO upgrade (const std::shared_ptr& var) {
288             return std::dynamic_pointer_cast(var);
289             }
290             };
291              
292             using StaticCast = std::true_type;
293             using DynamicCast = std::false_type;
294              
295             template class _IStorage, class CastType = StaticCast>
296             struct TypemapObject : TypemapBase {
297             using IType = _IType;
298             using IStorage = _IStorage;
299             using TypeNP = typename std::remove_pointer::type;
300              
301 122           static TYPE in (SV* arg) {
302 122 100         if (!SvOBJECT(arg)) {
303 26 100         if (SvROK(arg)) {
304 24           arg = SvRV(arg);
305 24 50         if (!SvOBJECT(arg)) throw "arg is a reference to non-object";
306             }
307 2 50         else if (!SvOK(arg)) return TYPE();
    50          
    50          
308 0           else throw "arg is not a reference to object";
309             }
310              
311 120 50         auto ptr = IStorage::get(arg);
312 120 100         if (ptr) {
313 117           TYPEMAP downgraded = IType::template in(ptr);
314 117 50         TYPE ret = cast(downgraded);
315 117 50         if (ret) {
316 117 50         if (!std::is_const::value && SvREADONLY(arg)) throw "cannot modify read-only object";
317 117           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 6           auto package = Typemap::package();
324 3           auto package_view = panda::string_view{ package.data(), package.length() };
325 122           typemap::object::_throw_incorrect_arg(arg, typeid (TYPE), package_view);
326             }
327              
328             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             static Sv create (const TYPE& var, const Sv& proto = Sv()) {
344             if (!var) return &PL_sv_undef;
345             Sv rv;
346             SV* base;
347             if (proto) {
348             if (SvROK(proto)) { // ref to object/base
349             rv = proto;
350             base = SvRV(proto);
351             }
352             else if (proto.type() <= SVt_PVMG) { // class name
353             if (SvOBJECT(proto)) {
354             base = proto;
355             rv = Sv::noinc(newRV_noinc(base));
356             } else {
357             base = newSV_type(SVt_PVMG);
358             rv = Sv::noinc(newRV_noinc(base));
359             sv_bless(rv, gv_stashsv(proto, GV_ADD));
360             }
361             goto ATTACH; // skip optional blessing
362             }
363             else if (proto.type() == SVt_PVHV && HvNAME(proto)) { // stash
364             base = newSV_type(SVt_PVMG);
365             rv = Sv::noinc(newRV_noinc(base));
366             sv_bless(rv, proto.get());
367             goto ATTACH; // skip optional blessing
368             }
369             else { // base given
370             rv = Sv::noinc(newRV(proto));
371             base = proto;
372             }
373             }
374             else { // nothing given, create ref to undef
375             base = newSV_type(SVt_PVMG);
376             rv = Sv::noinc(newRV_noinc(base));
377             goto BLESS; // definitely needs blessing
378             }
379              
380             if (!SvOBJECT(base)) { // not blessed -> bless to default typemap's class
381             BLESS:
382             static PERL_THREAD_LOCAL HV* stash = gv_stashpvn(Typemap::package().data(), Typemap::package().length(), GV_ADD);
383             sv_bless(rv, stash); // TODO: custom faster bless
384             }
385              
386             ATTACH:
387             IStorage::set(base, const_cast(IType::out(IType::template cast(var))));
388              
389             if (std::is_const::value) SvREADONLY_on(base);
390              
391             return rv;
392             }
393              
394             static TYPEMAP dup (const TYPEMAP& obj) { return obj; }
395              
396             static void dispose (const TYPE& var, SV* arg) {
397             IType::destroy(var, arg);
398             }
399              
400             static void destroy (const TYPE& var, SV* arg) {
401             if (!std::is_same::value) return;
402             if (!IStorage::auto_disposable) dispose(var, arg);
403             }
404              
405 165 50         template static inline TO cast (FROM v) { return _cast(v, CastType()); }
406              
407             static panda::string_view package () { typemap::object::_throw_no_package(typeid(TYPE)); return ""; }
408              
409             static Stash default_stash () {
410             static PERL_THREAD_LOCAL Stash stash = gv_stashpvn(Typemap::package().data(), Typemap::package().length(), GV_ADD);
411             return stash;
412             }
413              
414             private:
415             template static inline TO _cast (FROM v, DynamicCast) { return IType::template upgrade(v); }
416 330           template static inline TO _cast (FROM v, StaticCast) { return IType::template cast(v); }
417             };
418              
419             }