| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* pdfmake_custom_ops.h - Custom op infrastructure for PDF::Make XS |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* Provides compile-time call checkers and runtime custom ops to replace |
|
5
|
|
|
|
|
|
|
* standard xsubpp method dispatch for hot paths. Reusable across the |
|
6
|
|
|
|
|
|
|
* Semantic ecosystem via ExtUtils::Depends. |
|
7
|
|
|
|
|
|
|
* |
|
8
|
|
|
|
|
|
|
* Three op patterns: |
|
9
|
|
|
|
|
|
|
* 1. CHAIN - $obj->method() returns $obj (Canvas, Arena, Writer) |
|
10
|
|
|
|
|
|
|
* 2. GETTER - $obj->field reads C struct field by offset |
|
11
|
|
|
|
|
|
|
* 3. CONST - Package::CONST() folds to OP_CONST at compile time |
|
12
|
|
|
|
|
|
|
* |
|
13
|
|
|
|
|
|
|
* Requires: xop_compat.h (from Object::Proto) for pre-5.14 fallback |
|
14
|
|
|
|
|
|
|
*/ |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#ifndef PDFMAKE_CUSTOM_OPS_H |
|
17
|
|
|
|
|
|
|
#define PDFMAKE_CUSTOM_OPS_H |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
#include "EXTERN.h" |
|
20
|
|
|
|
|
|
|
#include "perl.h" |
|
21
|
|
|
|
|
|
|
#include "XSUB.h" |
|
22
|
|
|
|
|
|
|
#include "xop_compat.h" |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
/*============================================================================ |
|
25
|
|
|
|
|
|
|
* Pointer unwrap — skips sv_derived_from check (already validated by caller) |
|
26
|
|
|
|
|
|
|
*==========================================================================*/ |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
#define PDFMAKE_UNWRAP(type, sv) \ |
|
29
|
|
|
|
|
|
|
INT2PTR(type, SvIV(SvRV(sv))) |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
/*============================================================================ |
|
32
|
|
|
|
|
|
|
* Chain op argument types |
|
33
|
|
|
|
|
|
|
*==========================================================================*/ |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
#define PDFMAKE_ARG_DOUBLE 0 |
|
36
|
|
|
|
|
|
|
#define PDFMAKE_ARG_STRING 1 |
|
37
|
|
|
|
|
|
|
#define PDFMAKE_ARG_INT 2 |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
/*============================================================================ |
|
40
|
|
|
|
|
|
|
* Chain dispatch table entry |
|
41
|
|
|
|
|
|
|
* |
|
42
|
|
|
|
|
|
|
* Each canvas/arena method maps to an entry with: |
|
43
|
|
|
|
|
|
|
* - C function pointer |
|
44
|
|
|
|
|
|
|
* - argument count (0-6 beyond self) |
|
45
|
|
|
|
|
|
|
* - argument type codes |
|
46
|
|
|
|
|
|
|
*==========================================================================*/ |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
typedef struct { |
|
49
|
|
|
|
|
|
|
void *func; |
|
50
|
|
|
|
|
|
|
int nargs; |
|
51
|
|
|
|
|
|
|
int arg_types[6]; |
|
52
|
|
|
|
|
|
|
int ret_mode; /* 0 = return self (chain), 1 = return int, 2 = void */ |
|
53
|
|
|
|
|
|
|
} pdfmake_chain_entry_t; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
/*============================================================================ |
|
56
|
|
|
|
|
|
|
* CHAIN pp function - generic chainable method dispatch |
|
57
|
|
|
|
|
|
|
* |
|
58
|
|
|
|
|
|
|
* Unwraps self, pops args from stack, calls C function via dispatch table, |
|
59
|
|
|
|
|
|
|
* returns self (already on stack). Used for Canvas, Arena, Writer. |
|
60
|
|
|
|
|
|
|
* |
|
61
|
|
|
|
|
|
|
* op_private holds the pointer type (which struct to unwrap to). |
|
62
|
|
|
|
|
|
|
* op_targ holds the dispatch table index. |
|
63
|
|
|
|
|
|
|
* The dispatch table pointer is stored in a package-level static. |
|
64
|
|
|
|
|
|
|
*==========================================================================*/ |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
/* Forward declarations for package-specific dispatch tables */ |
|
67
|
|
|
|
|
|
|
/* These are defined in the respective XS BOOT sections */ |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
/* |
|
70
|
|
|
|
|
|
|
* pp function for nullary chain ops: $obj->method() returns $obj |
|
71
|
|
|
|
|
|
|
* No args to pop — just unwrap, call, return self. |
|
72
|
|
|
|
|
|
|
* This is the simplest and most common canvas pattern. |
|
73
|
|
|
|
|
|
|
*/ |
|
74
|
|
|
|
|
|
|
typedef int (*pdfmake_nullary_fn)(void *self); |
|
75
|
|
|
|
|
|
|
typedef int (*pdfmake_unary_d_fn)(void *self, double a); |
|
76
|
|
|
|
|
|
|
typedef int (*pdfmake_binary_dd_fn)(void *self, double a, double b); |
|
77
|
|
|
|
|
|
|
typedef int (*pdfmake_ternary_ddd_fn)(void *self, double a, double b, double c); |
|
78
|
|
|
|
|
|
|
typedef int (*pdfmake_quad_dddd_fn)(void *self, double a, double b, double c, double d); |
|
79
|
|
|
|
|
|
|
typedef int (*pdfmake_hex_dddddd_fn)(void *self, double a, double b, double c, double d, double e, double f); |
|
80
|
|
|
|
|
|
|
typedef int (*pdfmake_unary_s_fn)(void *self, const char *s); |
|
81
|
|
|
|
|
|
|
typedef int (*pdfmake_sd_fn)(void *self, const char *s, double d); |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
/* Multiple dispatch tables — indexed by table ID (high bits of op_targ) */ |
|
84
|
|
|
|
|
|
|
#define PDFMAKE_MAX_CHAIN_TABLES 8 |
|
85
|
|
|
|
|
|
|
static pdfmake_chain_entry_t *pdfmake_chain_tables[PDFMAKE_MAX_CHAIN_TABLES]; |
|
86
|
|
|
|
|
|
|
static int pdfmake_chain_table_count = 0; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
/* Encode table_id + entry_index into op_targ */ |
|
89
|
|
|
|
|
|
|
#define PDFMAKE_CHAIN_TARG(table_id, index) (((table_id) << 16) | (index)) |
|
90
|
|
|
|
|
|
|
#define PDFMAKE_CHAIN_TABLE(targ) ((targ) >> 16) |
|
91
|
|
|
|
|
|
|
#define PDFMAKE_CHAIN_INDEX(targ) ((targ) & 0xFFFF) |
|
92
|
|
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
|
static OP* pp_pdfmake_chain(pTHX) { |
|
94
|
0
|
|
|
|
|
|
dSP; |
|
95
|
0
|
|
|
|
|
|
UV targ = PL_op->op_targ; |
|
96
|
0
|
|
|
|
|
|
int table_id = PDFMAKE_CHAIN_TABLE(targ); |
|
97
|
0
|
|
|
|
|
|
int idx = PDFMAKE_CHAIN_INDEX(targ); |
|
98
|
0
|
|
|
|
|
|
pdfmake_chain_entry_t *e = &pdfmake_chain_tables[table_id][idx]; |
|
99
|
0
|
|
|
|
|
|
int nargs = e->nargs; |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
/* Self is below the args on the stack */ |
|
102
|
0
|
|
|
|
|
|
SV *self_sv = *(SP - nargs); |
|
103
|
0
|
|
|
|
|
|
void *self = PDFMAKE_UNWRAP(void*, self_sv); |
|
104
|
0
|
|
|
|
|
|
int err = 0; |
|
105
|
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
switch (nargs) { |
|
107
|
0
|
|
|
|
|
|
case 0: |
|
108
|
0
|
|
|
|
|
|
err = ((pdfmake_nullary_fn)e->func)(self); |
|
109
|
0
|
|
|
|
|
|
break; |
|
110
|
0
|
|
|
|
|
|
case 1: |
|
111
|
0
|
0
|
|
|
|
|
if (e->arg_types[0] == PDFMAKE_ARG_STRING) { |
|
112
|
0
|
|
|
|
|
|
const char *a = SvPV_nolen(TOPs); |
|
113
|
0
|
|
|
|
|
|
SP--; |
|
114
|
0
|
|
|
|
|
|
err = ((pdfmake_unary_s_fn)e->func)(self, a); |
|
115
|
|
|
|
|
|
|
} else { |
|
116
|
0
|
|
|
|
|
|
double a = SvNV(TOPs); |
|
117
|
0
|
|
|
|
|
|
SP--; |
|
118
|
0
|
|
|
|
|
|
err = ((pdfmake_unary_d_fn)e->func)(self, a); |
|
119
|
|
|
|
|
|
|
} |
|
120
|
0
|
|
|
|
|
|
break; |
|
121
|
0
|
|
|
|
|
|
case 2: |
|
122
|
0
|
0
|
|
|
|
|
if (e->arg_types[0] == PDFMAKE_ARG_STRING) { |
|
123
|
|
|
|
|
|
|
/* string, double — e.g. Tf(font, size) */ |
|
124
|
0
|
|
|
|
|
|
double b = SvNV(TOPs); SP--; |
|
125
|
0
|
|
|
|
|
|
const char *a = SvPV_nolen(TOPs); SP--; |
|
126
|
0
|
|
|
|
|
|
err = ((pdfmake_sd_fn)e->func)(self, a, b); |
|
127
|
|
|
|
|
|
|
} else { |
|
128
|
0
|
|
|
|
|
|
double b = SvNV(TOPs); SP--; |
|
129
|
0
|
|
|
|
|
|
double a = SvNV(TOPs); SP--; |
|
130
|
0
|
|
|
|
|
|
err = ((pdfmake_binary_dd_fn)e->func)(self, a, b); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
0
|
|
|
|
|
|
break; |
|
133
|
0
|
|
|
|
|
|
case 3: { |
|
134
|
0
|
|
|
|
|
|
double c = SvNV(TOPs); SP--; |
|
135
|
0
|
|
|
|
|
|
double b = SvNV(TOPs); SP--; |
|
136
|
0
|
|
|
|
|
|
double a = SvNV(TOPs); SP--; |
|
137
|
0
|
|
|
|
|
|
err = ((pdfmake_ternary_ddd_fn)e->func)(self, a, b, c); |
|
138
|
0
|
|
|
|
|
|
break; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
0
|
|
|
|
|
|
case 4: { |
|
141
|
0
|
|
|
|
|
|
double d = SvNV(TOPs); SP--; |
|
142
|
0
|
|
|
|
|
|
double c = SvNV(TOPs); SP--; |
|
143
|
0
|
|
|
|
|
|
double b = SvNV(TOPs); SP--; |
|
144
|
0
|
|
|
|
|
|
double a = SvNV(TOPs); SP--; |
|
145
|
0
|
|
|
|
|
|
err = ((pdfmake_quad_dddd_fn)e->func)(self, a, b, c, d); |
|
146
|
0
|
|
|
|
|
|
break; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
0
|
|
|
|
|
|
case 6: { |
|
149
|
0
|
|
|
|
|
|
double f = SvNV(TOPs); SP--; |
|
150
|
0
|
|
|
|
|
|
double e_val = SvNV(TOPs); SP--; |
|
151
|
0
|
|
|
|
|
|
double d = SvNV(TOPs); SP--; |
|
152
|
0
|
|
|
|
|
|
double c = SvNV(TOPs); SP--; |
|
153
|
0
|
|
|
|
|
|
double b = SvNV(TOPs); SP--; |
|
154
|
0
|
|
|
|
|
|
double a = SvNV(TOPs); SP--; |
|
155
|
0
|
|
|
|
|
|
err = ((pdfmake_hex_dddddd_fn)e->func)(self, a, b, c, d, e_val, f); |
|
156
|
0
|
|
|
|
|
|
break; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
|
switch (e->ret_mode) { |
|
161
|
0
|
|
|
|
|
|
case 1: |
|
162
|
|
|
|
|
|
|
/* Return int result */ |
|
163
|
0
|
0
|
|
|
|
|
if (err < 0) croak("PDF::Make custom op failed (index %d)", idx); |
|
164
|
0
|
|
|
|
|
|
SETs(sv_2mortal(newSViv(err))); |
|
165
|
0
|
|
|
|
|
|
PUTBACK; |
|
166
|
0
|
|
|
|
|
|
RETURN; |
|
167
|
0
|
|
|
|
|
|
case 2: |
|
168
|
|
|
|
|
|
|
/* Void — just pop args, leave nothing */ |
|
169
|
0
|
|
|
|
|
|
SP = SP - nargs; |
|
170
|
0
|
|
|
|
|
|
PUTBACK; |
|
171
|
0
|
|
|
|
|
|
RETURN; |
|
172
|
0
|
|
|
|
|
|
default: |
|
173
|
|
|
|
|
|
|
/* Chain — return self */ |
|
174
|
0
|
0
|
|
|
|
|
if (err != 0) croak("PDF::Make custom op failed (index %d)", idx); |
|
175
|
0
|
|
|
|
|
|
PUTBACK; |
|
176
|
0
|
|
|
|
|
|
RETURN; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
/*============================================================================ |
|
181
|
|
|
|
|
|
|
* Chain call checker |
|
182
|
|
|
|
|
|
|
* |
|
183
|
|
|
|
|
|
|
* At compile time, replaces entersub with our custom op. |
|
184
|
|
|
|
|
|
|
* For nullary ops, creates a UNOP(self). |
|
185
|
|
|
|
|
|
|
* For ops with args, we DON'T rewrite the op tree — instead we just |
|
186
|
|
|
|
|
|
|
* replace the pp_addr of the entersub to avoid complex tree surgery. |
|
187
|
|
|
|
|
|
|
* This is simpler and still eliminates typemap/method-cache overhead. |
|
188
|
|
|
|
|
|
|
*==========================================================================*/ |
|
189
|
|
|
|
|
|
|
|
|
190
|
0
|
|
|
|
|
|
static OP* pdfmake_chain_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
191
|
0
|
|
|
|
|
|
IV idx = SvIV(ckobj); |
|
192
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
/* Simple approach: replace entersub's pp_addr directly. |
|
195
|
|
|
|
|
|
|
* The args remain on the stack in standard order. |
|
196
|
|
|
|
|
|
|
* This avoids complex op tree surgery for multi-arg methods. */ |
|
197
|
0
|
|
|
|
|
|
entersubop->op_ppaddr = pp_pdfmake_chain; |
|
198
|
0
|
|
|
|
|
|
cUNOPx(entersubop)->op_first->op_targ = idx; /* Store index */ |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
/* Actually, op_targ on entersub is used for pad allocation. |
|
201
|
|
|
|
|
|
|
* Store in op_private + a side table instead. */ |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
/* For safety, just use op_targ on the entersubop itself */ |
|
204
|
0
|
|
|
|
|
|
entersubop->op_targ = idx; |
|
205
|
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
|
return entersubop; |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
/*============================================================================ |
|
210
|
|
|
|
|
|
|
* GETTER ops — read C struct field by offset + type |
|
211
|
|
|
|
|
|
|
* |
|
212
|
|
|
|
|
|
|
* op_targ encodes: (byte_offset << 4) | field_type |
|
213
|
|
|
|
|
|
|
*==========================================================================*/ |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
#define PDFMAKE_FIELD_DOUBLE 0 |
|
216
|
|
|
|
|
|
|
#define PDFMAKE_FIELD_INT 1 |
|
217
|
|
|
|
|
|
|
#define PDFMAKE_FIELD_UV 2 |
|
218
|
|
|
|
|
|
|
#define PDFMAKE_FIELD_STRING 3 |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
#define PDFMAKE_GETTER_TARG(offset, type) (((UV)(offset) << 4) | (type)) |
|
221
|
|
|
|
|
|
|
|
|
222
|
0
|
|
|
|
|
|
static OP* pp_pdfmake_getter(pTHX) { |
|
223
|
0
|
|
|
|
|
|
dSP; |
|
224
|
0
|
|
|
|
|
|
SV *obj = TOPs; |
|
225
|
0
|
|
|
|
|
|
char *ptr = PDFMAKE_UNWRAP(char*, obj); |
|
226
|
0
|
|
|
|
|
|
UV encoded = PL_op->op_targ; |
|
227
|
0
|
|
|
|
|
|
int field_type = encoded & 0xF; |
|
228
|
0
|
|
|
|
|
|
size_t offset = encoded >> 4; |
|
229
|
|
|
|
|
|
|
|
|
230
|
0
|
|
|
|
|
|
switch (field_type) { |
|
231
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_DOUBLE: |
|
232
|
0
|
|
|
|
|
|
SETs(sv_2mortal(newSVnv(*(double*)(ptr + offset)))); |
|
233
|
0
|
|
|
|
|
|
break; |
|
234
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_INT: |
|
235
|
0
|
|
|
|
|
|
SETs(sv_2mortal(newSViv(*(int*)(ptr + offset)))); |
|
236
|
0
|
|
|
|
|
|
break; |
|
237
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_UV: |
|
238
|
0
|
|
|
|
|
|
SETs(sv_2mortal(newSVuv(*(UV*)(ptr + offset)))); |
|
239
|
0
|
|
|
|
|
|
break; |
|
240
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_STRING: { |
|
241
|
0
|
|
|
|
|
|
const char *val = *(const char**)(ptr + offset); |
|
242
|
0
|
0
|
|
|
|
|
SETs(val ? sv_2mortal(newSVpv(val, 0)) : &PL_sv_undef); |
|
243
|
0
|
|
|
|
|
|
break; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
} |
|
246
|
0
|
|
|
|
|
|
RETURN; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
|
|
249
|
0
|
|
|
|
|
|
static OP* pdfmake_getter_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
250
|
0
|
|
|
|
|
|
UV targ = SvUV(ckobj); |
|
251
|
|
|
|
|
|
|
OP *pushop, *selfop, *cvop; |
|
252
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
253
|
|
|
|
|
|
|
|
|
254
|
0
|
|
|
|
|
|
pushop = cUNOPx(entersubop)->op_first; |
|
255
|
0
|
0
|
|
|
|
|
if (!OpHAS_SIBLING(pushop)) |
|
256
|
0
|
|
|
|
|
|
pushop = cUNOPx(pushop)->op_first; |
|
257
|
|
|
|
|
|
|
|
|
258
|
0
|
0
|
|
|
|
|
selfop = OpSIBLING(pushop); |
|
259
|
0
|
|
|
|
|
|
cvop = selfop; |
|
260
|
0
|
0
|
|
|
|
|
while (OpHAS_SIBLING(cvop)) |
|
261
|
0
|
0
|
|
|
|
|
cvop = OpSIBLING(cvop); |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
/* Detach self from chain, free the rest */ |
|
264
|
0
|
|
|
|
|
|
OpMORESIB_set(pushop, cvop); |
|
265
|
0
|
|
|
|
|
|
OpLASTSIB_set(selfop, NULL); |
|
266
|
|
|
|
|
|
|
|
|
267
|
0
|
|
|
|
|
|
OP *newop = newUNOP(OP_NULL, 0, selfop); |
|
268
|
0
|
|
|
|
|
|
newop->op_type = OP_CUSTOM; |
|
269
|
0
|
|
|
|
|
|
newop->op_ppaddr = pp_pdfmake_getter; |
|
270
|
0
|
|
|
|
|
|
newop->op_targ = targ; |
|
271
|
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
op_free(entersubop); |
|
273
|
0
|
|
|
|
|
|
return newop; |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
/*============================================================================ |
|
277
|
|
|
|
|
|
|
* INDIRECT GETTER — read field through one pointer chase |
|
278
|
|
|
|
|
|
|
* |
|
279
|
|
|
|
|
|
|
* For wrapper structs: self->ptr_offset is a pointer, then read field |
|
280
|
|
|
|
|
|
|
* at field_offset from that pointer. |
|
281
|
|
|
|
|
|
|
* |
|
282
|
|
|
|
|
|
|
* op_targ encodes: (ptr_offset << 20) | (field_offset << 4) | field_type |
|
283
|
|
|
|
|
|
|
*==========================================================================*/ |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
#define PDFMAKE_INDIRECT_TARG(ptr_off, field_off, type) \ |
|
286
|
|
|
|
|
|
|
(((UV)(ptr_off) << 20) | ((UV)(field_off) << 4) | (type)) |
|
287
|
|
|
|
|
|
|
|
|
288
|
0
|
|
|
|
|
|
static OP* pp_pdfmake_indirect_getter(pTHX) { |
|
289
|
0
|
|
|
|
|
|
dSP; |
|
290
|
0
|
|
|
|
|
|
SV *obj = TOPs; |
|
291
|
0
|
|
|
|
|
|
char *wrapper = PDFMAKE_UNWRAP(char*, obj); |
|
292
|
0
|
|
|
|
|
|
UV encoded = PL_op->op_targ; |
|
293
|
0
|
|
|
|
|
|
int field_type = encoded & 0xF; |
|
294
|
0
|
|
|
|
|
|
size_t field_off = (encoded >> 4) & 0xFFFF; |
|
295
|
0
|
|
|
|
|
|
size_t ptr_off = encoded >> 20; |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
/* Follow the pointer: wrapper + ptr_off is a pointer to the inner struct */ |
|
298
|
0
|
|
|
|
|
|
char *inner = *(char**)(wrapper + ptr_off); |
|
299
|
0
|
0
|
|
|
|
|
if (!inner) { |
|
300
|
0
|
|
|
|
|
|
SETs(&PL_sv_undef); |
|
301
|
0
|
|
|
|
|
|
RETURN; |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
0
|
|
|
|
|
|
switch (field_type) { |
|
305
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_DOUBLE: |
|
306
|
0
|
|
|
|
|
|
SETs(sv_2mortal(newSVnv(*(double*)(inner + field_off)))); |
|
307
|
0
|
|
|
|
|
|
break; |
|
308
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_INT: |
|
309
|
0
|
|
|
|
|
|
SETs(sv_2mortal(newSViv(*(int*)(inner + field_off)))); |
|
310
|
0
|
|
|
|
|
|
break; |
|
311
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_UV: |
|
312
|
0
|
|
|
|
|
|
SETs(sv_2mortal(newSVuv(*(UV*)(inner + field_off)))); |
|
313
|
0
|
|
|
|
|
|
break; |
|
314
|
0
|
|
|
|
|
|
case PDFMAKE_FIELD_STRING: { |
|
315
|
0
|
|
|
|
|
|
const char *val = *(const char**)(inner + field_off); |
|
316
|
0
|
0
|
|
|
|
|
SETs(val ? sv_2mortal(newSVpv(val, 0)) : &PL_sv_undef); |
|
317
|
0
|
|
|
|
|
|
break; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
} |
|
320
|
0
|
|
|
|
|
|
RETURN; |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
|
|
323
|
0
|
|
|
|
|
|
static OP* pdfmake_indirect_getter_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
324
|
0
|
|
|
|
|
|
UV targ = SvUV(ckobj); |
|
325
|
|
|
|
|
|
|
OP *pushop, *selfop, *cvop; |
|
326
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
327
|
|
|
|
|
|
|
|
|
328
|
0
|
|
|
|
|
|
pushop = cUNOPx(entersubop)->op_first; |
|
329
|
0
|
0
|
|
|
|
|
if (!OpHAS_SIBLING(pushop)) |
|
330
|
0
|
|
|
|
|
|
pushop = cUNOPx(pushop)->op_first; |
|
331
|
0
|
0
|
|
|
|
|
selfop = OpSIBLING(pushop); |
|
332
|
0
|
|
|
|
|
|
cvop = selfop; |
|
333
|
0
|
0
|
|
|
|
|
while (OpHAS_SIBLING(cvop)) |
|
334
|
0
|
0
|
|
|
|
|
cvop = OpSIBLING(cvop); |
|
335
|
|
|
|
|
|
|
|
|
336
|
0
|
|
|
|
|
|
OpMORESIB_set(pushop, cvop); |
|
337
|
0
|
|
|
|
|
|
OpLASTSIB_set(selfop, NULL); |
|
338
|
|
|
|
|
|
|
|
|
339
|
0
|
|
|
|
|
|
OP *newop = newUNOP(OP_NULL, 0, selfop); |
|
340
|
0
|
|
|
|
|
|
newop->op_type = OP_CUSTOM; |
|
341
|
0
|
|
|
|
|
|
newop->op_ppaddr = pp_pdfmake_indirect_getter; |
|
342
|
0
|
|
|
|
|
|
newop->op_targ = targ; |
|
343
|
|
|
|
|
|
|
|
|
344
|
0
|
|
|
|
|
|
op_free(entersubop); |
|
345
|
0
|
|
|
|
|
|
return newop; |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_INDIRECT_GETTER(stash, method, wrap_type, ptr_field, inner_type, field, ftype) \ |
|
349
|
|
|
|
|
|
|
do { \ |
|
350
|
|
|
|
|
|
|
GV *_gv = gv_fetchmeth_pvn(stash, method, strlen(method), 0, 0); \ |
|
351
|
|
|
|
|
|
|
if (_gv && GvCV(_gv)) { \ |
|
352
|
|
|
|
|
|
|
SV *_ck = newSVuv(PDFMAKE_INDIRECT_TARG( \ |
|
353
|
|
|
|
|
|
|
offsetof(wrap_type, ptr_field), \ |
|
354
|
|
|
|
|
|
|
offsetof(inner_type, field), ftype)); \ |
|
355
|
|
|
|
|
|
|
cv_set_call_checker(GvCV(_gv), pdfmake_indirect_getter_call_checker, _ck); \ |
|
356
|
|
|
|
|
|
|
} \ |
|
357
|
|
|
|
|
|
|
} while(0) |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
/*============================================================================ |
|
360
|
|
|
|
|
|
|
* TYPE-TEST ops — compare struct field to constant, return bool |
|
361
|
|
|
|
|
|
|
* |
|
362
|
|
|
|
|
|
|
* op_targ encodes: (ptr_offset << 20) | (field_offset << 8) | expected_value |
|
363
|
|
|
|
|
|
|
* Used for: is_null, is_int, is_array, etc. on Obj wrapper |
|
364
|
|
|
|
|
|
|
*==========================================================================*/ |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
#define PDFMAKE_TYPETEST_TARG(ptr_off, field_off, expected) \ |
|
367
|
|
|
|
|
|
|
(((UV)(ptr_off) << 20) | ((UV)(field_off) << 8) | ((expected) & 0xFF)) |
|
368
|
|
|
|
|
|
|
|
|
369
|
0
|
|
|
|
|
|
static OP* pp_pdfmake_typetest(pTHX) { |
|
370
|
0
|
|
|
|
|
|
dSP; |
|
371
|
0
|
|
|
|
|
|
SV *obj = TOPs; |
|
372
|
0
|
|
|
|
|
|
char *wrapper = PDFMAKE_UNWRAP(char*, obj); |
|
373
|
0
|
|
|
|
|
|
UV encoded = PL_op->op_targ; |
|
374
|
0
|
|
|
|
|
|
int expected = encoded & 0xFF; |
|
375
|
0
|
|
|
|
|
|
size_t field_off = (encoded >> 8) & 0xFFF; |
|
376
|
0
|
|
|
|
|
|
size_t ptr_off = encoded >> 20; |
|
377
|
|
|
|
|
|
|
|
|
378
|
0
|
|
|
|
|
|
char *inner = *(char**)(wrapper + ptr_off); |
|
379
|
0
|
0
|
|
|
|
|
int actual = inner ? *(int*)(inner + field_off) : -1; |
|
380
|
|
|
|
|
|
|
|
|
381
|
0
|
0
|
|
|
|
|
SETs(boolSV(actual == expected)); |
|
382
|
0
|
|
|
|
|
|
RETURN; |
|
383
|
|
|
|
|
|
|
} |
|
384
|
|
|
|
|
|
|
|
|
385
|
0
|
|
|
|
|
|
static OP* pdfmake_typetest_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
386
|
0
|
|
|
|
|
|
UV targ = SvUV(ckobj); |
|
387
|
|
|
|
|
|
|
OP *pushop, *selfop, *cvop; |
|
388
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
389
|
|
|
|
|
|
|
|
|
390
|
0
|
|
|
|
|
|
pushop = cUNOPx(entersubop)->op_first; |
|
391
|
0
|
0
|
|
|
|
|
if (!OpHAS_SIBLING(pushop)) |
|
392
|
0
|
|
|
|
|
|
pushop = cUNOPx(pushop)->op_first; |
|
393
|
0
|
0
|
|
|
|
|
selfop = OpSIBLING(pushop); |
|
394
|
0
|
|
|
|
|
|
cvop = selfop; |
|
395
|
0
|
0
|
|
|
|
|
while (OpHAS_SIBLING(cvop)) |
|
396
|
0
|
0
|
|
|
|
|
cvop = OpSIBLING(cvop); |
|
397
|
|
|
|
|
|
|
|
|
398
|
0
|
|
|
|
|
|
OpMORESIB_set(pushop, cvop); |
|
399
|
0
|
|
|
|
|
|
OpLASTSIB_set(selfop, NULL); |
|
400
|
|
|
|
|
|
|
|
|
401
|
0
|
|
|
|
|
|
OP *newop = newUNOP(OP_NULL, 0, selfop); |
|
402
|
0
|
|
|
|
|
|
newop->op_type = OP_CUSTOM; |
|
403
|
0
|
|
|
|
|
|
newop->op_ppaddr = pp_pdfmake_typetest; |
|
404
|
0
|
|
|
|
|
|
newop->op_targ = targ; |
|
405
|
|
|
|
|
|
|
|
|
406
|
0
|
|
|
|
|
|
op_free(entersubop); |
|
407
|
0
|
|
|
|
|
|
return newop; |
|
408
|
|
|
|
|
|
|
} |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_TYPETEST(stash, method, wrap_type, ptr_field, inner_type, field, expected_val) \ |
|
411
|
|
|
|
|
|
|
do { \ |
|
412
|
|
|
|
|
|
|
GV *_gv = gv_fetchmeth_pvn(stash, method, strlen(method), 0, 0); \ |
|
413
|
|
|
|
|
|
|
if (_gv && GvCV(_gv)) { \ |
|
414
|
|
|
|
|
|
|
SV *_ck = newSVuv(PDFMAKE_TYPETEST_TARG( \ |
|
415
|
|
|
|
|
|
|
offsetof(wrap_type, ptr_field), \ |
|
416
|
|
|
|
|
|
|
offsetof(inner_type, field), expected_val)); \ |
|
417
|
|
|
|
|
|
|
cv_set_call_checker(GvCV(_gv), pdfmake_typetest_call_checker, _ck); \ |
|
418
|
|
|
|
|
|
|
} \ |
|
419
|
|
|
|
|
|
|
} while(0) |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
/*============================================================================ |
|
422
|
|
|
|
|
|
|
* ARENA CONSTRUCTOR ops — create PDF objects from arena |
|
423
|
|
|
|
|
|
|
* |
|
424
|
|
|
|
|
|
|
* All arena constructors follow the same pattern: |
|
425
|
|
|
|
|
|
|
* Newxz wrapper → set arena backref → arena_alloc → C_init(args) → bless |
|
426
|
|
|
|
|
|
|
* |
|
427
|
|
|
|
|
|
|
* op_targ indexes a dispatch table of init function + arg type. |
|
428
|
|
|
|
|
|
|
*==========================================================================*/ |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
#define PDFMAKE_ARENA_ARG_NONE 0 /* null, array, dict, stream */ |
|
431
|
|
|
|
|
|
|
#define PDFMAKE_ARENA_ARG_INT 1 /* bool(int), int(IV) */ |
|
432
|
|
|
|
|
|
|
#define PDFMAKE_ARENA_ARG_DOUBLE 2 /* real(NV) */ |
|
433
|
|
|
|
|
|
|
#define PDFMAKE_ARENA_ARG_STRING 3 /* name(str,len), str(str,len), hexstr(str,len) */ |
|
434
|
|
|
|
|
|
|
#define PDFMAKE_ARENA_ARG_REF 4 /* ref(num, gen) */ |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
typedef struct { |
|
437
|
|
|
|
|
|
|
int arg_type; |
|
438
|
|
|
|
|
|
|
/* The init function signature varies, stored as void* */ |
|
439
|
|
|
|
|
|
|
void *init_fn; |
|
440
|
|
|
|
|
|
|
} pdfmake_arena_ctor_entry_t; |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
#define PDFMAKE_MAX_ARENA_CTORS 16 |
|
443
|
|
|
|
|
|
|
static pdfmake_arena_ctor_entry_t pdfmake_arena_ctors[PDFMAKE_MAX_ARENA_CTORS]; |
|
444
|
|
|
|
|
|
|
static int pdfmake_arena_ctor_count = 0; |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
/* Forward declare the types we need */ |
|
447
|
|
|
|
|
|
|
typedef struct pdfmake_obj pdfmake_obj_t_fwd; |
|
448
|
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
|
static OP* pp_pdfmake_arena_ctor(pTHX) { |
|
450
|
0
|
|
|
|
|
|
dSP; dMARK; dAX; |
|
451
|
0
|
|
|
|
|
|
UV idx = PL_op->op_targ; |
|
452
|
0
|
|
|
|
|
|
pdfmake_arena_ctor_entry_t *e = &pdfmake_arena_ctors[idx]; |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
/* Self (arena wrapper) is ST(0) */ |
|
455
|
0
|
|
|
|
|
|
SV *arena_sv = ST(0); |
|
456
|
|
|
|
|
|
|
/* Unwrap to arena_xs_t — we know the struct layout: |
|
457
|
|
|
|
|
|
|
* { pdfmake_arena_t *arena } at offset 0 */ |
|
458
|
0
|
|
|
|
|
|
char *arena_xs = PDFMAKE_UNWRAP(char*, arena_sv); |
|
459
|
0
|
|
|
|
|
|
void *arena_ptr = *(void**)arena_xs; /* first field is arena pointer */ |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
/* Allocate wrapper + obj */ |
|
462
|
|
|
|
|
|
|
/* We inline the Newxz/alloc pattern here */ |
|
463
|
|
|
|
|
|
|
typedef struct { |
|
464
|
|
|
|
|
|
|
void *arena_xs_ptr; |
|
465
|
|
|
|
|
|
|
SV *arena_sv_ref; |
|
466
|
|
|
|
|
|
|
void *obj_ptr; |
|
467
|
|
|
|
|
|
|
} obj_wrap_t; |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
obj_wrap_t *wrap; |
|
470
|
0
|
|
|
|
|
|
Newxz(wrap, 1, obj_wrap_t); |
|
471
|
0
|
|
|
|
|
|
wrap->arena_xs_ptr = arena_xs; |
|
472
|
0
|
|
|
|
|
|
wrap->arena_sv_ref = SvREFCNT_inc(arena_sv); |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
/* Arena alloc for the obj (pdfmake_arena_alloc declared in pdfmake_arena.h) */ |
|
475
|
0
|
|
|
|
|
|
wrap->obj_ptr = pdfmake_arena_alloc(arena_ptr, 32); /* sizeof(pdfmake_obj_t) */ |
|
476
|
0
|
0
|
|
|
|
|
if (!wrap->obj_ptr) { |
|
477
|
0
|
|
|
|
|
|
SvREFCNT_dec(wrap->arena_sv_ref); |
|
478
|
0
|
|
|
|
|
|
Safefree(wrap); |
|
479
|
0
|
|
|
|
|
|
croak("Arena allocation failed"); |
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
/* Call the init function based on arg type */ |
|
483
|
|
|
|
|
|
|
typedef struct { int kind; } simple_obj_t; /* just need to write to *obj */ |
|
484
|
|
|
|
|
|
|
|
|
485
|
0
|
|
|
|
|
|
switch (e->arg_type) { |
|
486
|
0
|
|
|
|
|
|
case PDFMAKE_ARENA_ARG_NONE: { |
|
487
|
|
|
|
|
|
|
typedef simple_obj_t (*fn0)(void); |
|
488
|
0
|
|
|
|
|
|
simple_obj_t result = ((fn0)e->init_fn)(); |
|
489
|
0
|
|
|
|
|
|
*(simple_obj_t*)wrap->obj_ptr = result; |
|
490
|
0
|
|
|
|
|
|
SP = MARK; |
|
491
|
0
|
|
|
|
|
|
break; |
|
492
|
|
|
|
|
|
|
} |
|
493
|
0
|
|
|
|
|
|
case PDFMAKE_ARENA_ARG_INT: { |
|
494
|
|
|
|
|
|
|
typedef simple_obj_t (*fn_iv)(int64_t); |
|
495
|
0
|
|
|
|
|
|
int64_t val = SvIV(ST(1)); |
|
496
|
0
|
|
|
|
|
|
simple_obj_t result = ((fn_iv)e->init_fn)(val); |
|
497
|
0
|
|
|
|
|
|
*(simple_obj_t*)wrap->obj_ptr = result; |
|
498
|
0
|
|
|
|
|
|
SP = MARK; |
|
499
|
0
|
|
|
|
|
|
break; |
|
500
|
|
|
|
|
|
|
} |
|
501
|
0
|
|
|
|
|
|
case PDFMAKE_ARENA_ARG_DOUBLE: { |
|
502
|
|
|
|
|
|
|
typedef simple_obj_t (*fn_nv)(double); |
|
503
|
0
|
|
|
|
|
|
double val = SvNV(ST(1)); |
|
504
|
0
|
|
|
|
|
|
simple_obj_t result = ((fn_nv)e->init_fn)(val); |
|
505
|
0
|
|
|
|
|
|
*(simple_obj_t*)wrap->obj_ptr = result; |
|
506
|
0
|
|
|
|
|
|
SP = MARK; |
|
507
|
0
|
|
|
|
|
|
break; |
|
508
|
|
|
|
|
|
|
} |
|
509
|
0
|
|
|
|
|
|
case PDFMAKE_ARENA_ARG_STRING: { |
|
510
|
|
|
|
|
|
|
typedef simple_obj_t (*fn_str)(void*, const char*, size_t); |
|
511
|
|
|
|
|
|
|
STRLEN len; |
|
512
|
0
|
|
|
|
|
|
const char *str = SvPV(ST(1), len); |
|
513
|
0
|
|
|
|
|
|
simple_obj_t result = ((fn_str)e->init_fn)(arena_ptr, str, len); |
|
514
|
0
|
|
|
|
|
|
*(simple_obj_t*)wrap->obj_ptr = result; |
|
515
|
0
|
|
|
|
|
|
SP = MARK; |
|
516
|
0
|
|
|
|
|
|
break; |
|
517
|
|
|
|
|
|
|
} |
|
518
|
0
|
|
|
|
|
|
case PDFMAKE_ARENA_ARG_REF: { |
|
519
|
|
|
|
|
|
|
typedef simple_obj_t (*fn_ref)(uint32_t, uint16_t); |
|
520
|
0
|
|
|
|
|
|
uint32_t num = SvUV(ST(1)); |
|
521
|
0
|
|
|
|
|
|
uint16_t gen = (uint16_t)SvUV(ST(2)); |
|
522
|
0
|
|
|
|
|
|
simple_obj_t result = ((fn_ref)e->init_fn)(num, gen); |
|
523
|
0
|
|
|
|
|
|
*(simple_obj_t*)wrap->obj_ptr = result; |
|
524
|
0
|
|
|
|
|
|
SP = MARK; |
|
525
|
0
|
|
|
|
|
|
break; |
|
526
|
|
|
|
|
|
|
} |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
/* Bless and return */ |
|
530
|
0
|
|
|
|
|
|
SV *rv = newRV_noinc(newSViv(PTR2IV(wrap))); |
|
531
|
0
|
|
|
|
|
|
sv_bless(rv, gv_stashpv("PDF::Make::Obj", GV_ADD)); |
|
532
|
0
|
0
|
|
|
|
|
XPUSHs(sv_2mortal(rv)); |
|
533
|
0
|
|
|
|
|
|
PUTBACK; |
|
534
|
0
|
|
|
|
|
|
return NORMAL; |
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
|
|
537
|
0
|
|
|
|
|
|
static OP* pdfmake_arena_ctor_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
538
|
0
|
|
|
|
|
|
IV idx = SvIV(ckobj); |
|
539
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
540
|
0
|
|
|
|
|
|
entersubop->op_ppaddr = pp_pdfmake_arena_ctor; |
|
541
|
0
|
|
|
|
|
|
entersubop->op_targ = idx; |
|
542
|
0
|
|
|
|
|
|
return entersubop; |
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_ARENA_CTOR(stash, method_name, arg_type_val, init_func) \ |
|
546
|
|
|
|
|
|
|
do { \ |
|
547
|
|
|
|
|
|
|
int _idx = pdfmake_arena_ctor_count++; \ |
|
548
|
|
|
|
|
|
|
pdfmake_arena_ctors[_idx].arg_type = (arg_type_val); \ |
|
549
|
|
|
|
|
|
|
pdfmake_arena_ctors[_idx].init_fn = (void*)(init_func); \ |
|
550
|
|
|
|
|
|
|
GV *_gv = gv_fetchmeth_pvn(stash, method_name, \ |
|
551
|
|
|
|
|
|
|
strlen(method_name), 0, 0); \ |
|
552
|
|
|
|
|
|
|
if (_gv && GvCV(_gv)) { \ |
|
553
|
|
|
|
|
|
|
SV *_ck = newSViv((IV)_idx); \ |
|
554
|
|
|
|
|
|
|
cv_set_call_checker(GvCV(_gv), pdfmake_arena_ctor_call_checker, _ck); \ |
|
555
|
|
|
|
|
|
|
} \ |
|
556
|
|
|
|
|
|
|
} while(0) |
|
557
|
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
/*============================================================================ |
|
559
|
|
|
|
|
|
|
* META ops — getter/setter for string metadata fields |
|
560
|
|
|
|
|
|
|
* |
|
561
|
|
|
|
|
|
|
* op_targ indexes into a dispatch table of get/set function pairs. |
|
562
|
|
|
|
|
|
|
* At runtime: if 1 arg (just self) → call getter, return string. |
|
563
|
|
|
|
|
|
|
* if 2 args (self + value) → call setter, return value. |
|
564
|
|
|
|
|
|
|
* |
|
565
|
|
|
|
|
|
|
* Uses pp_addr replacement on entersub (not op tree rewrite) so args |
|
566
|
|
|
|
|
|
|
* stay on the stack and items count is preserved. |
|
567
|
|
|
|
|
|
|
*==========================================================================*/ |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
typedef const char* (*pdfmake_meta_get_fn)(void *self); |
|
570
|
|
|
|
|
|
|
typedef int (*pdfmake_meta_set_fn)(void *self, const char *val); |
|
571
|
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
typedef struct { |
|
573
|
|
|
|
|
|
|
pdfmake_meta_get_fn getter; |
|
574
|
|
|
|
|
|
|
pdfmake_meta_set_fn setter; |
|
575
|
|
|
|
|
|
|
} pdfmake_meta_entry_t; |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
#define PDFMAKE_MAX_META_ENTRIES 16 |
|
578
|
|
|
|
|
|
|
static pdfmake_meta_entry_t pdfmake_meta_table[PDFMAKE_MAX_META_ENTRIES]; |
|
579
|
|
|
|
|
|
|
static int pdfmake_meta_table_count = 0; |
|
580
|
|
|
|
|
|
|
|
|
581
|
0
|
|
|
|
|
|
static OP* pp_pdfmake_meta(pTHX) { |
|
582
|
0
|
|
|
|
|
|
dSP; dMARK; dAX; |
|
583
|
0
|
|
|
|
|
|
int count = SP - MARK; |
|
584
|
0
|
|
|
|
|
|
UV idx = PL_op->op_targ; |
|
585
|
0
|
|
|
|
|
|
pdfmake_meta_entry_t *e = &pdfmake_meta_table[idx]; |
|
586
|
|
|
|
|
|
|
|
|
587
|
0
|
|
|
|
|
|
SV *self_sv = ST(0); |
|
588
|
0
|
|
|
|
|
|
void *self = PDFMAKE_UNWRAP(void*, self_sv); |
|
589
|
|
|
|
|
|
|
|
|
590
|
0
|
0
|
|
|
|
|
if (count > 1) { |
|
591
|
|
|
|
|
|
|
/* Setter */ |
|
592
|
0
|
|
|
|
|
|
const char *val = SvPV_nolen(ST(1)); |
|
593
|
0
|
|
|
|
|
|
e->setter(self, val); |
|
594
|
0
|
|
|
|
|
|
SP = MARK; |
|
595
|
0
|
0
|
|
|
|
|
XPUSHs(ST(1)); |
|
596
|
|
|
|
|
|
|
} else { |
|
597
|
|
|
|
|
|
|
/* Getter */ |
|
598
|
0
|
|
|
|
|
|
const char *val = e->getter(self); |
|
599
|
0
|
|
|
|
|
|
SP = MARK; |
|
600
|
0
|
0
|
|
|
|
|
if (val) |
|
601
|
0
|
0
|
|
|
|
|
XPUSHs(sv_2mortal(newSVpv(val, 0))); |
|
602
|
|
|
|
|
|
|
else |
|
603
|
0
|
0
|
|
|
|
|
XPUSHs(&PL_sv_undef); |
|
604
|
|
|
|
|
|
|
} |
|
605
|
0
|
|
|
|
|
|
PUTBACK; |
|
606
|
0
|
|
|
|
|
|
return NORMAL; |
|
607
|
|
|
|
|
|
|
} |
|
608
|
|
|
|
|
|
|
|
|
609
|
0
|
|
|
|
|
|
static OP* pdfmake_meta_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
610
|
0
|
|
|
|
|
|
IV idx = SvIV(ckobj); |
|
611
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
612
|
0
|
|
|
|
|
|
entersubop->op_ppaddr = pp_pdfmake_meta; |
|
613
|
0
|
|
|
|
|
|
entersubop->op_targ = idx; |
|
614
|
0
|
|
|
|
|
|
return entersubop; |
|
615
|
|
|
|
|
|
|
} |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_META(stash, method_name, get_fn, set_fn) \ |
|
618
|
|
|
|
|
|
|
do { \ |
|
619
|
|
|
|
|
|
|
int _idx = pdfmake_meta_table_count++; \ |
|
620
|
|
|
|
|
|
|
pdfmake_meta_table[_idx].getter = (pdfmake_meta_get_fn)(get_fn); \ |
|
621
|
|
|
|
|
|
|
pdfmake_meta_table[_idx].setter = (pdfmake_meta_set_fn)(set_fn); \ |
|
622
|
|
|
|
|
|
|
GV *_gv = gv_fetchmeth_pvn(stash, method_name, \ |
|
623
|
|
|
|
|
|
|
strlen(method_name), 0, 0); \ |
|
624
|
|
|
|
|
|
|
if (_gv && GvCV(_gv)) { \ |
|
625
|
|
|
|
|
|
|
SV *_ck = newSViv((IV)_idx); \ |
|
626
|
|
|
|
|
|
|
cv_set_call_checker(GvCV(_gv), pdfmake_meta_call_checker, _ck); \ |
|
627
|
|
|
|
|
|
|
} \ |
|
628
|
|
|
|
|
|
|
} while(0) |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
/*============================================================================ |
|
631
|
|
|
|
|
|
|
* CONST ops — fold to OP_CONST at compile time |
|
632
|
|
|
|
|
|
|
*==========================================================================*/ |
|
633
|
|
|
|
|
|
|
|
|
634
|
28
|
|
|
|
|
|
static OP* pdfmake_const_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) { |
|
635
|
|
|
|
|
|
|
PERL_UNUSED_ARG(namegv); |
|
636
|
28
|
|
|
|
|
|
op_free(entersubop); |
|
637
|
28
|
|
|
|
|
|
return newSVOP(OP_CONST, 0, SvREFCNT_inc(ckobj)); |
|
638
|
|
|
|
|
|
|
} |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
/*============================================================================ |
|
641
|
|
|
|
|
|
|
* Registration macros |
|
642
|
|
|
|
|
|
|
*==========================================================================*/ |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
/* Register a chainable method's CV with the chain call checker */ |
|
645
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_CHAIN(stash, method_name, table_id, index) \ |
|
646
|
|
|
|
|
|
|
do { \ |
|
647
|
|
|
|
|
|
|
GV *_gv = gv_fetchmeth_pvn(stash, method_name, \ |
|
648
|
|
|
|
|
|
|
strlen(method_name), 0, 0); \ |
|
649
|
|
|
|
|
|
|
if (_gv && GvCV(_gv)) { \ |
|
650
|
|
|
|
|
|
|
SV *_ck = newSViv((IV)PDFMAKE_CHAIN_TARG(table_id, index)); \ |
|
651
|
|
|
|
|
|
|
cv_set_call_checker(GvCV(_gv), pdfmake_chain_call_checker, _ck); \ |
|
652
|
|
|
|
|
|
|
} \ |
|
653
|
|
|
|
|
|
|
} while(0) |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
/* Register a struct field getter */ |
|
656
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_GETTER(stash, method_name, struct_type, field, ftype) \ |
|
657
|
|
|
|
|
|
|
do { \ |
|
658
|
|
|
|
|
|
|
GV *_gv = gv_fetchmeth_pvn(stash, method_name, \ |
|
659
|
|
|
|
|
|
|
strlen(method_name), 0, 0); \ |
|
660
|
|
|
|
|
|
|
if (_gv && GvCV(_gv)) { \ |
|
661
|
|
|
|
|
|
|
SV *_ck = newSVuv(PDFMAKE_GETTER_TARG( \ |
|
662
|
|
|
|
|
|
|
offsetof(struct_type, field), ftype)); \ |
|
663
|
|
|
|
|
|
|
cv_set_call_checker(GvCV(_gv), pdfmake_getter_call_checker, _ck); \ |
|
664
|
|
|
|
|
|
|
} \ |
|
665
|
|
|
|
|
|
|
} while(0) |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
/* Register a constant (folds to OP_CONST at compile time) */ |
|
668
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_CONST(stash, name, value) \ |
|
669
|
|
|
|
|
|
|
do { \ |
|
670
|
|
|
|
|
|
|
GV *_gv = gv_fetchmeth_pvn(stash, name, strlen(name), 0, 0); \ |
|
671
|
|
|
|
|
|
|
if (_gv && GvCV(_gv)) { \ |
|
672
|
|
|
|
|
|
|
SV *_ck = newSViv((IV)(value)); \ |
|
673
|
|
|
|
|
|
|
cv_set_call_checker(GvCV(_gv), pdfmake_const_call_checker, _ck); \ |
|
674
|
|
|
|
|
|
|
} \ |
|
675
|
|
|
|
|
|
|
} while(0) |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
/*============================================================================ |
|
678
|
|
|
|
|
|
|
* XOP registration helper — call once per pp function in BOOT |
|
679
|
|
|
|
|
|
|
*==========================================================================*/ |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
#define PDFMAKE_REGISTER_XOP(xop_var, pp_func, op_name, op_desc) \ |
|
682
|
|
|
|
|
|
|
do { \ |
|
683
|
|
|
|
|
|
|
XopENTRY_set(&(xop_var), xop_name, op_name); \ |
|
684
|
|
|
|
|
|
|
XopENTRY_set(&(xop_var), xop_desc, op_desc); \ |
|
685
|
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ pp_func, &(xop_var)); \ |
|
686
|
|
|
|
|
|
|
} while(0) |
|
687
|
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
#endif /* PDFMAKE_CUSTOM_OPS_H */ |