| 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 | 110 | 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 | 32 |  |  |  |  |  | static inline void* get (SV* arg) { | 
| 60 | 32 |  |  |  |  |  | MAGIC* mg = _get_magic(arg); | 
| 61 | 32 | 50 |  |  |  |  | return mg ? mg->mg_ptr : NULL; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 23 |  |  |  |  |  | static inline void set (SV* arg, void* ptr) { | 
| 65 | 23 | 50 |  |  |  |  | auto marker = xs::Sv::PayloadMarker::get(); | 
| 66 | 23 | 50 |  |  |  |  | marker->svt_clear = typemap::object::TypemapMarker::get(); | 
| 67 | 23 |  |  |  |  |  | marker->svt_free = _on_free; | 
| 68 | 23 |  |  |  |  |  | _set_br(marker, BACKREF()); | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | MAGIC* mg; | 
| 71 | 23 | 50 |  |  |  |  | Newx(mg, 1, MAGIC); | 
| 72 | 23 |  |  |  |  |  | mg->mg_moremagic = SvMAGIC(arg); | 
| 73 | 23 |  |  |  |  |  | SvMAGIC_set(arg, mg); | 
| 74 | 23 |  |  |  |  |  | mg->mg_virtual = marker; | 
| 75 | 23 |  |  |  |  |  | mg->mg_type = PERL_MAGIC_ext; | 
| 76 | 23 |  |  |  |  |  | mg->mg_len = 0; | 
| 77 | 23 |  |  |  |  |  | mg->mg_obj = nullptr; | 
| 78 | 23 |  |  |  |  |  | mg->mg_ptr = (char*)ptr; | 
| 79 | 23 |  |  |  |  |  | mg->mg_private = 0; | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | #ifdef USE_ITHREADS | 
| 82 |  |  |  |  |  |  | marker->svt_dup = _on_svdup; | 
| 83 |  |  |  |  |  |  | mg->mg_flags = MGf_DUP; | 
| 84 |  |  |  |  |  |  | #else | 
| 85 | 23 |  |  |  |  |  | mg->mg_flags = 0; | 
| 86 |  |  |  |  |  |  | #endif | 
| 87 | 23 |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 23 | 50 |  |  |  |  | static Sv out (const TYPE& var, const Sv& proto) { return _out(var, proto, BACKREF()); } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | private: | 
| 92 | 32 |  |  |  |  |  | static inline MAGIC* _get_magic (SV* sv) { | 
| 93 | 32 |  |  |  |  |  | auto marker = typemap::object::TypemapMarker::get(); | 
| 94 |  |  |  |  |  |  | MAGIC *mg; | 
| 95 | 32 | 50 |  |  |  |  | for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) if (mg->mg_virtual && mg->mg_virtual->svt_clear == marker) return mg; | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 96 | 0 |  |  |  |  |  | return NULL; | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 23 |  |  |  |  |  | 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 | 46 |  |  |  |  |  | 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 | 92 |  |  |  |  |  | static int _on_free (pTHX_ SV* sv, MAGIC* mg) {return throw_guard(Sub(), [=]() -> int { | 
| 200 |  |  |  |  |  |  | using IType = typename Typemap::IType; | 
| 201 | 23 |  |  |  |  |  | TYPEMAP downgraded = IType::template in(mg->mg_ptr); | 
| 202 | 23 | 50 |  |  |  |  | TYPE var = Typemap::template cast(downgraded); | 
| 203 | 23 | 50 |  |  |  |  | if (!var) throw "TYPEMAP PANIC: bad object in sv"; | 
| 204 | 23 | 50 |  |  |  |  | Typemap::dispose(var, sv); | 
| 205 | 23 |  |  |  |  |  | return 0; | 
| 206 | 46 | 50 |  |  |  |  | });} | 
| 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 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | 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 | 110 |  |  |  |  |  | template  static inline T           in        (void* p)     { return static_cast(p); } | 
| 251 | 46 |  |  |  |  |  | template  static inline const void* out       (T* var)      { refcnt_inc(var); return var; } | 
| 252 | 46 |  |  |  |  |  | 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 | 156 |  |  |  |  |  | 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 | 32 |  |  |  |  |  | static TYPE in (SV* arg) { | 
| 302 | 32 | 50 |  |  |  |  | if (!SvOBJECT(arg)) { | 
| 303 | 32 | 50 |  |  |  |  | if (SvROK(arg)) { | 
| 304 | 32 |  |  |  |  |  | arg = SvRV(arg); | 
| 305 | 32 | 50 |  |  |  |  | if (!SvOBJECT(arg)) throw "arg is a reference to non-object"; | 
| 306 |  |  |  |  |  |  | } | 
| 307 | 0 | 0 |  |  |  |  | else if (!SvOK(arg)) return TYPE(); | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 308 | 0 |  |  |  |  |  | else throw "arg is not a reference to object"; | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 32 | 50 |  |  |  |  | auto ptr = IStorage::get(arg); | 
| 312 | 32 | 50 |  |  |  |  | if (ptr) { | 
| 313 | 32 |  |  |  |  |  | TYPEMAP downgraded = IType::template in(ptr); | 
| 314 | 32 | 50 |  |  |  |  | TYPE ret = cast(downgraded); | 
| 315 | 32 | 50 |  |  |  |  | if (ret) { | 
| 316 | 32 | 50 |  |  |  |  | if (!std::is_const::value && SvREADONLY(arg)) throw "cannot modify read-only object"; | 
| 317 | 32 |  |  |  |  |  | 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 |  |  |  |  |  | auto package = Typemap::package(); | 
| 324 | 0 |  |  |  |  |  | auto package_view = panda::string_view{ package.data(),  package.length() }; | 
| 325 | 32 |  |  |  |  |  | typemap::object::_throw_incorrect_arg(arg, typeid (TYPE), package_view); | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 | 46 |  |  |  |  |  | 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 | 23 |  |  |  |  |  | static Sv create (const TYPE& var, const Sv& proto = Sv()) { | 
| 344 | 23 | 50 |  |  |  |  | if (!var) return &PL_sv_undef; | 
| 345 | 46 |  |  |  |  |  | Sv rv; | 
| 346 |  |  |  |  |  |  | SV* base; | 
| 347 | 23 | 50 |  |  |  |  | if (proto) { | 
| 348 | 0 | 0 |  |  |  |  | if (SvROK(proto)) { // ref to object/base | 
| 349 | 0 | 0 |  |  |  |  | rv = proto; | 
| 350 | 0 |  |  |  |  |  | base = SvRV(proto); | 
| 351 |  |  |  |  |  |  | } | 
| 352 | 0 | 0 |  |  |  |  | else if (proto.type() <= SVt_PVMG) { // class name | 
| 353 | 0 | 0 |  |  |  |  | if (SvOBJECT(proto)) { | 
| 354 | 0 |  |  |  |  |  | base = proto; | 
| 355 | 0 | 0 |  |  |  |  | rv = Sv::noinc(newRV_noinc(base)); | 
| 356 |  |  |  |  |  |  | } else { | 
| 357 | 0 | 0 |  |  |  |  | base = newSV_type(SVt_PVMG); | 
| 358 | 0 | 0 |  |  |  |  | rv = Sv::noinc(newRV_noinc(base)); | 
| 359 | 0 | 0 |  |  |  |  | sv_bless(rv, gv_stashsv(proto, GV_ADD)); | 
|  |  | 0 |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | } | 
| 361 | 0 |  |  |  |  |  | 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 |  |  |  |  |  | 
| 364 | 0 | 0 |  |  |  |  | base = newSV_type(SVt_PVMG); | 
| 365 | 0 | 0 |  |  |  |  | rv = Sv::noinc(newRV_noinc(base)); | 
| 366 | 0 | 0 |  |  |  |  | sv_bless(rv, proto.get()); | 
| 367 | 0 |  |  |  |  |  | goto ATTACH; // skip optional blessing | 
| 368 |  |  |  |  |  |  | } | 
| 369 |  |  |  |  |  |  | else { // base given | 
| 370 | 0 | 0 |  |  |  |  | rv = Sv::noinc(newRV(proto)); | 
| 371 | 0 |  |  |  |  |  | base = proto; | 
| 372 |  |  |  |  |  |  | } | 
| 373 |  |  |  |  |  |  | } | 
| 374 |  |  |  |  |  |  | else { // nothing given, create ref to undef | 
| 375 | 23 | 50 |  |  |  |  | base = newSV_type(SVt_PVMG); | 
| 376 | 23 | 50 |  |  |  |  | rv = Sv::noinc(newRV_noinc(base)); | 
| 377 | 23 |  |  |  |  |  | goto BLESS; // definitely needs blessing | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  |  | 
| 380 | 0 | 0 |  |  |  |  | if (!SvOBJECT(base)) { // not blessed -> bless to default typemap's class | 
| 381 |  |  |  |  |  |  | BLESS: | 
| 382 | 23 | 100 |  |  |  |  | static PERL_THREAD_LOCAL HV* stash = gv_stashpvn(Typemap::package().data(), Typemap::package().length(), GV_ADD); | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 383 | 23 | 50 |  |  |  |  | sv_bless(rv, stash); // TODO: custom faster bless | 
| 384 |  |  |  |  |  |  | } | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | ATTACH: | 
| 387 | 23 | 50 |  |  |  |  | IStorage::set(base, const_cast(IType::out(IType::template cast(var)))); | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | if (std::is_const::value) SvREADONLY_on(base); | 
| 390 |  |  |  |  |  |  |  | 
| 391 | 23 |  |  |  |  |  | return rv; | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | static TYPEMAP dup (const TYPEMAP& obj) { return obj; } | 
| 395 |  |  |  |  |  |  |  | 
| 396 | 23 |  |  |  |  |  | static void dispose (const TYPE& var, SV* arg) { | 
| 397 | 23 |  |  |  |  |  | IType::destroy(var, arg); | 
| 398 | 23 |  |  |  |  |  | } | 
| 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 | 55 | 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 | 110 |  |  |  |  |  | template  static inline TO _cast (FROM v, StaticCast)  { return IType::template cast(v); } | 
| 417 |  |  |  |  |  |  | }; | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | } |