1 /*******************************************************************************
2 *
3 * MODULE: type.c
4 *
5 ********************************************************************************
6 *
7 * DESCRIPTION: C::B::C type names
8 *
9 ********************************************************************************
10 *
11 * Copyright (c) 2002-2020 Marcus Holland-Moritz. All rights reserved.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the same terms as Perl itself.
14 *
15 *******************************************************************************/
16 
17 /*===== GLOBAL INCLUDES ======================================================*/
18 
19 #define PERL_NO_GET_CONTEXT
20 #include <EXTERN.h>
21 #include <perl.h>
22 #include <XSUB.h>
23 
24 #include "ppport.h"
25 
26 
27 /*===== LOCAL INCLUDES =======================================================*/
28 
29 #include "cbc/basic.h"
30 #include "cbc/cbc.h"
31 #include "cbc/type.h"
32 #include "cbc/util.h"
33 
34 
35 /*===== DEFINES ==============================================================*/
36 
37 /*===== TYPEDEFS =============================================================*/
38 
39 /*===== STATIC FUNCTION PROTOTYPES ===========================================*/
40 
41 static void *get_type_pointer(CBC *THIS, const char *name, const char **pEOS);
42 
43 
44 /*===== EXTERNAL VARIABLES ===================================================*/
45 
46 /*===== GLOBAL VARIABLES =====================================================*/
47 
48 /*===== STATIC VARIABLES =====================================================*/
49 
50 /*===== STATIC FUNCTIONS =====================================================*/
51 
52 /*******************************************************************************
53 *
54 *   ROUTINE: get_type_pointer
55 *
56 *   WRITTEN BY: Marcus Holland-Moritz             ON: Jan 2002
57 *   CHANGED BY:                                   ON:
58 *
59 ********************************************************************************
60 *
61 * DESCRIPTION:
62 *
63 *   ARGUMENTS:
64 *
65 *     RETURNS:
66 *
67 *******************************************************************************/
68 
get_type_pointer(CBC * THIS,const char * name,const char ** pEOS)69 static void *get_type_pointer(CBC *THIS, const char *name, const char **pEOS)
70 {
71   const char *c   = name;
72   void       *ptr = NULL;
73   int         len = 0;
74   enum { S_UNKNOWN, S_STRUCT, S_UNION, S_ENUM } type = S_UNKNOWN;
75 
76   if (!THIS->cpi.available)
77     return NULL;
78 
79   while (isSPACE(*c))
80     c++;
81 
82   if (*c == '\0')
83     return NULL;
84 
85   switch (c[0])
86   {
87     case 's':
88       if (c[1] == 't' &&
89           c[2] == 'r' &&
90           c[3] == 'u' &&
91           c[4] == 'c' &&
92           c[5] == 't' &&
93           isSPACE(c[6]))
94       {
95         type = S_STRUCT;
96         c += 6;
97       }
98       break;
99 
100     case 'u':
101       if (c[1] == 'n' &&
102           c[2] == 'i' &&
103           c[3] == 'o' &&
104           c[4] == 'n' &&
105           isSPACE(c[5]))
106       {
107         type = S_UNION;
108         c += 5;
109       }
110       break;
111 
112     case 'e':
113       if (c[1] == 'n' &&
114           c[2] == 'u' &&
115           c[3] == 'm' &&
116           isSPACE(c[4]))
117       {
118         type = S_ENUM;
119         c += 4;
120       }
121       break;
122 
123     default:
124       break;
125   }
126 
127   while (isSPACE(*c))
128     c++;
129 
130   while (c[len] == '_' || isALNUM(c[len]))
131     len++;
132 
133   if (len == 0)
134     return NULL;
135 
136   switch (type)
137   {
138     case S_STRUCT:
139     case S_UNION:
140       {
141         Struct *pStruct = HT_get(THIS->cpi.htStructs, c, len, 0);
142         ptr = (void *) (pStruct && (pStruct->tflags & (type == S_STRUCT
143                         ? T_STRUCT : T_UNION)) ? pStruct : NULL);
144       }
145       break;
146 
147     case S_ENUM:
148       ptr = HT_get(THIS->cpi.htEnums, c, len, 0);
149       break;
150 
151     default:
152       if ((ptr = HT_get(THIS->cpi.htTypedefs, c, len, 0)) == NULL)
153         if ((ptr = HT_get(THIS->cpi.htStructs, c, len, 0)) == NULL)
154           ptr = HT_get(THIS->cpi.htEnums, c, len, 0);
155       break;
156   }
157 
158   if (pEOS)
159   {
160     c += len;
161 
162     while (isSPACE(*c))
163       c++;
164 
165     *pEOS = c;
166   }
167 
168   return ptr;
169 }
170 
171 
172 /*===== FUNCTIONS ============================================================*/
173 
174 /*******************************************************************************
175 *
176 *   ROUTINE: get_member_info
177 *
178 *   WRITTEN BY: Marcus Holland-Moritz             ON: Oct 2002
179 *   CHANGED BY:                                   ON:
180 *
181 ********************************************************************************
182 *
183 * DESCRIPTION:
184 *
185 *   ARGUMENTS:
186 *
187 *     RETURNS:
188 *
189 *******************************************************************************/
190 
get_member_info(pTHX_ CBC * THIS,const char * name,MemberInfo * pMI,unsigned gmi_flags)191 int get_member_info(pTHX_ CBC *THIS, const char *name, MemberInfo *pMI, unsigned gmi_flags)
192 {
193   const int do_calc = (gmi_flags & CBC_GMI_NO_CALC) == 0;
194   const char *member;
195   MemberInfo mi;
196 
197   if (get_type_spec(THIS, name, &member, &mi.type) == 0)
198     return 0;
199 
200   if (pMI)
201   {
202     pMI->flags  = 0;
203     pMI->parent = NULL;
204 
205     if (member && *member)
206     {
207       mi.pDecl = NULL;
208       mi.level = 0;
209       (void) get_member(aTHX_ &mi, member, pMI, do_calc ? 0 : CBC_GM_NO_OFFSET_SIZE_CALC);
210     }
211     else if (mi.type.ptr == NULL)
212     {
213       Declarator *pDecl = basic_types_get_declarator(THIS->basic, mi.type.tflags);
214 
215       if (pDecl == NULL)
216       {
217         SV *str = NULL;
218         get_basic_type_spec_string(aTHX_ &str, mi.type.tflags);
219         sv_2mortal(str);
220         Perl_croak(aTHX_ "Unsupported basic type '%s'", SvPV_nolen(str));
221       }
222 
223       if (do_calc && pDecl->size < 0)
224         (void) THIS->cfg.get_type_info(&THIS->cfg.layout, &mi.type, NULL,
225                                        "si", &pDecl->size, &pDecl->item_size);
226 
227       pMI->pDecl  = pDecl;
228       pMI->type   = mi.type;
229       pMI->flags  = 0;
230       pMI->level  = 0;
231       pMI->offset = 0;
232       pMI->size   = do_calc ? pDecl->size : 0;
233     }
234     else
235     {
236       void *ptr = mi.type.ptr;  /* TODO: improve this... */
237 
238       switch (GET_CTYPE(ptr))
239       {
240         case TYP_TYPEDEF:
241           {
242             /* TODO: get rid of get_type_info, add flags to size */
243             ErrorGTI err;
244             err = THIS->cfg.get_type_info(&THIS->cfg.layout, ((Typedef *) ptr)->pType,
245                                           ((Typedef *) ptr)->pDecl,
246                                           "sf", &pMI->size, &pMI->flags);
247 
248             if (err != GTI_NO_ERROR)
249               croak_gti(aTHX_ err, name, 0);
250           }
251           break;
252 
253         case TYP_STRUCT:
254           if (((Struct *) ptr)->declarations == NULL)
255             CROAK_UNDEF_STRUCT((Struct *) ptr);
256 
257           pMI->size  = ((Struct *) ptr)->size;
258           pMI->flags = ((Struct *) ptr)->tflags & (T_HASBITFIELD | T_UNSAFE_VAL);
259           break;
260 
261         case TYP_ENUM:
262           pMI->size = GET_ENUM_SIZE(&THIS->cfg, (EnumSpecifier *) ptr);
263           break;
264 
265         default:
266           fatal("get_type_spec returned an invalid type (%d) in "
267                 "get_member_info( '%s' )", GET_CTYPE(ptr), name);
268           break;
269       }
270 
271       if (!do_calc)
272       {
273         pMI->size = 0;
274       }
275 
276       pMI->type   = mi.type;
277       pMI->pDecl  = NULL;
278       pMI->level  = 0;
279       pMI->offset = 0;
280     }
281   }
282 
283   return 1;
284 }
285 
286 /*******************************************************************************
287 *
288 *   ROUTINE: get_type_spec
289 *
290 *   WRITTEN BY: Marcus Holland-Moritz             ON: Jan 2002
291 *   CHANGED BY:                                   ON:
292 *
293 ********************************************************************************
294 *
295 * DESCRIPTION:
296 *
297 *   ARGUMENTS:
298 *
299 *     RETURNS:
300 *
301 *******************************************************************************/
302 
get_type_spec(CBC * THIS,const char * name,const char ** pEOS,TypeSpec * pTS)303 int get_type_spec(CBC *THIS, const char *name, const char **pEOS, TypeSpec *pTS)
304 {
305   void *ptr = get_type_pointer(THIS, name, pEOS);
306 
307   if (ptr == NULL)
308   {
309     if (pEOS)
310       *pEOS = NULL;
311 
312     return get_basic_type_spec(name, pTS);
313   }
314 
315   switch (GET_CTYPE(ptr))
316   {
317     case TYP_TYPEDEF:
318       pTS->tflags = T_TYPE;
319       break;
320 
321     case TYP_STRUCT:
322       pTS->tflags = ((Struct *) ptr)->tflags;
323       break;
324 
325     case TYP_ENUM:
326       pTS->tflags = T_ENUM;
327       break;
328 
329     default:
330       fatal("Invalid type (%d) in get_type_spec( '%s' )", GET_CTYPE(ptr), name);
331       break;
332   }
333 
334   pTS->ptr = ptr;
335 
336   return 1;
337 }
338 
339 /*******************************************************************************
340 *
341 *   ROUTINE: get_type_name_string
342 *
343 *   WRITTEN BY: Marcus Holland-Moritz             ON: Mar 2003
344 *   CHANGED BY:                                   ON:
345 *
346 ********************************************************************************
347 *
348 * DESCRIPTION:
349 *
350 *   ARGUMENTS:
351 *
352 *     RETURNS:
353 *
354 *******************************************************************************/
355 
get_type_name_string(pTHX_ const MemberInfo * pMI)356 SV *get_type_name_string(pTHX_ const MemberInfo *pMI)
357 {
358   SV *sv;
359 
360   if (pMI == NULL)
361     fatal("get_type_name_string() called with NULL pointer");
362 
363   if (pMI->type.ptr == NULL)
364   {
365     sv = NULL;
366     get_basic_type_spec_string(aTHX_ &sv, pMI->type.tflags);
367   }
368   else
369   {
370     switch (GET_CTYPE(pMI->type.ptr))
371     {
372       case TYP_TYPEDEF:
373         sv = newSVpv(((Typedef *) pMI->type.ptr)->pDecl->identifier, 0);
374         break;
375 
376       case TYP_STRUCT:
377         {
378           Struct *pS = (Struct *) pMI->type.ptr;
379           sv = pS->identifier[0] == '\0'
380              ? newSVpv(pS->tflags & T_STRUCT ? "struct" : "union", 0)
381              : newSVpvf("%s %s", pS->tflags & T_STRUCT
382                                  ? "struct" : "union", pS->identifier);
383         }
384         break;
385 
386       case TYP_ENUM:
387         {
388           EnumSpecifier *pE = (EnumSpecifier *) pMI->type.ptr;
389           sv = pE->identifier[0] == '\0'
390              ? newSVpvn("enum", 4)
391              : newSVpvf("enum %s", pE->identifier);
392         }
393         break;
394 
395       default:
396         fatal("GET_CTYPE() returned an invalid type (%d) "
397               "in get_type_name_string()", GET_CTYPE(pMI->type.ptr));
398         break;
399     }
400   }
401 
402   if (pMI->pDecl != NULL)
403   {
404     if (pMI->pDecl->bitfield_flag)
405       sv_catpvf(sv, " :%d", pMI->pDecl->ext.bitfield.bits);
406     else
407     {
408       if (pMI->pDecl->pointer_flag)
409         sv_catpv(sv, " *");
410 
411       if (pMI->pDecl->array_flag)
412       {
413         int level = pMI->level;
414         int count = LL_count(pMI->pDecl->ext.array);
415 
416         if (level < count)
417         {
418           sv_catpv(sv, " ");
419           while (level < count)
420           {
421             Value *pValue = LL_get(pMI->pDecl->ext.array, level);
422 
423             if (pValue->flags & V_IS_UNDEF)
424               sv_catpvn(sv, "[]", 2);
425             else
426               sv_catpvf(sv, "[%ld]", pValue->iv);
427 
428             level++;
429           }
430         }
431       }
432     }
433   }
434 
435   return sv;
436 }
437 
438 /*******************************************************************************
439 *
440 *   ROUTINE: is_typedef_defined
441 *
442 *   WRITTEN BY: Marcus Holland-Moritz             ON: Mar 2002
443 *   CHANGED BY:                                   ON:
444 *
445 ********************************************************************************
446 *
447 * DESCRIPTION:
448 *
449 *   ARGUMENTS:
450 *
451 *     RETURNS:
452 *
453 *******************************************************************************/
454 
is_typedef_defined(Typedef * pTypedef)455 int is_typedef_defined(Typedef *pTypedef)
456 {
457   if (pTypedef->pDecl->pointer_flag)
458     return 1;
459 
460   while (pTypedef->pType->tflags & T_TYPE)
461   {
462     pTypedef = (Typedef *) pTypedef->pType->ptr;
463 
464     if (pTypedef->pDecl->pointer_flag)
465       return 1;
466   }
467 
468   if (pTypedef->pType->tflags & T_COMPOUND)
469     return ((Struct*) pTypedef->pType->ptr)->declarations != NULL;
470 
471   if (pTypedef->pType->tflags & T_ENUM)
472     return ((EnumSpecifier*) pTypedef->pType->ptr)->enumerators != NULL;
473 
474   return 1;
475 }
476 
477 /*******************************************************************************
478 *
479 *   ROUTINE: check_allowed_types_string
480 *
481 *   WRITTEN BY: Marcus Holland-Moritz             ON: Mar 2006
482 *   CHANGED BY:                                   ON:
483 *
484 ********************************************************************************
485 *
486 * DESCRIPTION:
487 *
488 *   ARGUMENTS:
489 *
490 *     RETURNS:
491 *
492 *******************************************************************************/
493 
494 #define CHECK_ALLOWED(flag, string)                                            \
495         STMT_START {                                                           \
496           if ((allowed_types & ALLOW_ ## flag) == 0)                           \
497             return string;                                                     \
498           return NULL;                                                         \
499         } STMT_END
500 
check_allowed_types_string(const MemberInfo * pMI,U32 allowed_types)501 const char *check_allowed_types_string(const MemberInfo *pMI, U32 allowed_types)
502 {
503   const Declarator *pDecl = pMI->pDecl;
504   const TypeSpec   *pType = &pMI->type;
505   int               level = 0;
506 
507   if (pType->tflags & T_TYPE &&
508       (pDecl == NULL || (!pDecl->pointer_flag && !pDecl->array_flag)))
509   {
510     do
511     {
512       const Typedef *pTypedef = (Typedef *) pType->ptr;
513       pDecl = pTypedef->pDecl;
514       pType = pTypedef->pType;
515     }
516     while (!pDecl->pointer_flag &&
517            !pDecl->array_flag &&
518            pType->tflags & T_TYPE);
519   }
520   else
521     level = pMI->level;
522 
523   if (pDecl != NULL)
524   {
525     if (pDecl->array_flag && level < LL_count(pDecl->ext.array))
526       CHECK_ALLOWED(ARRAYS, "an array type");
527 
528     if (pDecl->pointer_flag)
529       CHECK_ALLOWED(POINTERS, "a pointer type");
530   }
531 
532   if (pType->ptr == NULL)
533     CHECK_ALLOWED(BASIC_TYPES, "a basic type");
534 
535   if (pType->tflags & T_UNION)
536     CHECK_ALLOWED(UNIONS, "a union");
537 
538   if (pType->tflags & T_STRUCT)
539     CHECK_ALLOWED(STRUCTS, "a struct");
540 
541   if (pType->tflags & T_ENUM)
542     CHECK_ALLOWED(ENUMS, "an enum");
543 
544   return NULL;
545 }
546 
547 /*******************************************************************************
548 *
549 *   ROUTINE: check_allowed_types
550 *
551 *   WRITTEN BY: Marcus Holland-Moritz             ON: Apr 2003
552 *   CHANGED BY:                                   ON:
553 *
554 ********************************************************************************
555 *
556 * DESCRIPTION:
557 *
558 *   ARGUMENTS:
559 *
560 *     RETURNS:
561 *
562 *******************************************************************************/
563 
check_allowed_types(pTHX_ const MemberInfo * pMI,const char * method,U32 allowed_types)564 void check_allowed_types(pTHX_ const MemberInfo *pMI, const char *method, U32 allowed_types)
565 {
566   const char *failed_type = check_allowed_types_string(pMI, allowed_types);
567 
568   if (failed_type)
569     Perl_croak(aTHX_ "Cannot use %s on %s", method, failed_type);
570 }
571 
572