xref: /netbsd-src/sys/external/bsd/acpica/dist/compiler/prmacros.c (revision c7960b37466ae0fd417c32e6acbb4b956ac7a121)
1 /******************************************************************************
2  *
3  * Module Name: prmacros - Preprocessor #define macro support
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2023, Intel Corp.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions, and the following disclaimer,
16  *    without modification.
17  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
18  *    substantially similar to the "NO WARRANTY" disclaimer below
19  *    ("Disclaimer") and any redistribution must be conditioned upon
20  *    including a substantially similar Disclaimer requirement for further
21  *    binary redistribution.
22  * 3. Neither the names of the above-listed copyright holders nor the names
23  *    of any contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * Alternatively, this software may be distributed under the terms of the
27  * GNU General Public License ("GPL") version 2 as published by the Free
28  * Software Foundation.
29  *
30  * NO WARRANTY
31  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGES.
42  */
43 
44 #include "aslcompiler.h"
45 
46 #define _COMPONENT          ASL_PREPROCESSOR
47         ACPI_MODULE_NAME    ("prmacros")
48 
49 
50 /*******************************************************************************
51  *
52  * FUNCTION:    PrDumpPredefinedNames
53  *
54  * PARAMETERS:  None
55  *
56  * RETURN:      None
57  *
58  * DESCRIPTION: Dump the list of #defines. Used as the preprocessor starts, to
59  *              display the names that were defined on the command line.
60  *              Debug information only.
61  *
62  ******************************************************************************/
63 
64 void
65 PrDumpPredefinedNames (
66     void)
67 {
68     PR_DEFINE_INFO          *DefineInfo;
69 
70 
71     DefineInfo = AslGbl_DefineList;
72     while (DefineInfo)
73     {
74         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
75             "Predefined #define: %s->%s\n",
76             0, DefineInfo->Identifier, DefineInfo->Replacement);
77 
78         DefineInfo = DefineInfo->Next;
79     }
80 }
81 
82 
83 /*******************************************************************************
84  *
85  * FUNCTION:    PrAddDefine
86  *
87  * PARAMETERS:  Identifier          - Name to be replaced
88  *              Replacement         - Replacement for Identifier
89  *              Persist             - Keep define across multiple compiles?
90  *
91  * RETURN:      A new define_info struct. NULL on error.
92  *
93  * DESCRIPTION: Add a new #define to the global list
94  *
95  ******************************************************************************/
96 
97 PR_DEFINE_INFO *
98 PrAddDefine (
99     char                    *Identifier,
100     char                    *Replacement,
101     BOOLEAN                 Persist)
102 {
103     char                    *IdentifierString;
104     char                    *ReplacementString;
105     PR_DEFINE_INFO          *DefineInfo;
106 
107 
108     if (!Replacement)
109     {
110         Replacement = "";
111     }
112 
113     /* Check for already-defined first */
114 
115     DefineInfo = PrMatchDefine (Identifier);
116     if (DefineInfo)
117     {
118         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
119             "#define: name already exists: %s\n",
120             AslGbl_CurrentLineNumber, Identifier);
121 
122         /*
123          * Name already exists. This is only an error if the target name
124          * is different.
125          */
126         if (strcmp (Replacement, DefineInfo->Replacement))
127         {
128             PrError (ASL_ERROR, ASL_MSG_EXISTING_NAME,
129                 THIS_TOKEN_OFFSET (Identifier));
130 
131             return (NULL);
132         }
133 
134         return (DefineInfo);
135     }
136 
137     /* Copy input strings */
138 
139     IdentifierString = UtLocalCalloc (strlen (Identifier) + 1);
140     strcpy (IdentifierString, Identifier);
141 
142     ReplacementString = UtLocalCalloc (strlen (Replacement) + 1);
143     strcpy (ReplacementString, Replacement);
144 
145     /* Init and link new define info struct */
146 
147     DefineInfo = UtLocalCalloc (sizeof (PR_DEFINE_INFO));
148     DefineInfo->Replacement = ReplacementString;
149     DefineInfo->Identifier = IdentifierString;
150     DefineInfo->Persist = Persist;
151 
152     if (AslGbl_DefineList)
153     {
154         AslGbl_DefineList->Previous = DefineInfo;
155     }
156 
157     DefineInfo->Next = AslGbl_DefineList;
158     AslGbl_DefineList = DefineInfo;
159     return (DefineInfo);
160 }
161 
162 
163 /*******************************************************************************
164  *
165  * FUNCTION:    PrRemoveDefine
166  *
167  * PARAMETERS:  DefineName          - Name of define to be removed
168  *
169  * RETURN:      None
170  *
171  * DESCRIPTION: Implements #undef. Remove a #define if found in the global
172  *              list. No error if the target of the #undef does not exist,
173  *              as per the C #undef definition.
174  *
175  ******************************************************************************/
176 
177 void
178 PrRemoveDefine (
179     char                    *DefineName)
180 {
181     PR_DEFINE_INFO          *DefineInfo;
182 
183 
184     /* Match name and delete the node */
185 
186     DefineInfo = AslGbl_DefineList;
187     while (DefineInfo)
188     {
189         if (!strcmp (DefineName, DefineInfo->Identifier))
190         {
191             /* Remove from linked list */
192 
193             if (DefineInfo->Previous)
194             {
195                 (DefineInfo->Previous)->Next = DefineInfo->Next;
196             }
197             else
198             {
199                 AslGbl_DefineList = DefineInfo->Next;
200             }
201 
202             if (DefineInfo->Next)
203             {
204                 (DefineInfo->Next)->Previous = DefineInfo->Previous;
205             }
206 
207             free (DefineInfo);
208             return;
209         }
210 
211         DefineInfo = DefineInfo->Next;
212     }
213 
214     /*
215      * Name was not found. By definition of #undef, this is not
216      * an error, however.
217      */
218     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
219         "#undef: could not find %s\n",
220         AslGbl_CurrentLineNumber, DefineName);
221 }
222 
223 
224 /*******************************************************************************
225  *
226  * FUNCTION:    PrMatchDefine
227  *
228  * PARAMETERS:  MatchString         - Name associated with the #define
229  *
230  * RETURN:      Matched string if found. NULL otherwise.
231  *
232  * DESCRIPTION: Find a name in global #define list
233  *
234  ******************************************************************************/
235 
236 PR_DEFINE_INFO *
237 PrMatchDefine (
238     char                    *MatchString)
239 {
240     PR_DEFINE_INFO          *DefineInfo;
241 
242 
243     DefineInfo = AslGbl_DefineList;
244     while (DefineInfo)
245     {
246         if (!strcmp (MatchString, DefineInfo->Identifier))
247         {
248             return (DefineInfo);
249         }
250 
251         DefineInfo = DefineInfo->Next;
252     }
253 
254     return (NULL);
255 }
256 
257 
258 /*******************************************************************************
259  *
260  * FUNCTION:    PrAddMacro
261  *
262  * PARAMETERS:  Name                - Start of the macro definition
263  *              Next                - "Next" buffer from GetNextToken
264  *
265  * RETURN:      None
266  *
267  * DESCRIPTION: Add a new macro to the list of #defines. Handles argument
268  *              processing.
269  *
270  ******************************************************************************/
271 
272 void
273 PrAddMacro (
274     char                    *Name,
275     char                    **Next)
276 {
277     char                    *Token = NULL;
278     ACPI_SIZE               TokenOffset;
279     ACPI_SIZE               MacroBodyOffset;
280     PR_DEFINE_INFO          *DefineInfo;
281     PR_MACRO_ARG            *Args;
282     char                    *Body;
283     char                    *BodyInSource;
284     UINT32                  i;
285     UINT16                  UseCount = 0;
286     UINT16                  ArgCount = 0;
287     UINT32                  Depth = 1;
288     /*UINT32                  Depth = 1;*/
289     UINT32                  EndOfArgList;
290     char                    BufferChar;
291 
292     /* Find the end of the arguments list */
293 
294     TokenOffset = Name - AslGbl_MainTokenBuffer + strlen (Name) + 1;
295     while (1)
296     {
297         BufferChar = AslGbl_CurrentLineBuffer[TokenOffset];
298         if (BufferChar == '(')
299         {
300             Depth++;
301         }
302         else if (BufferChar == ')')
303         {
304             Depth--;
305         }
306         else if (BufferChar == 0)
307         {
308             PrError (ASL_ERROR, ASL_MSG_MACRO_SYNTAX, TokenOffset);
309             return;
310         }
311 
312         if (Depth == 0)
313         {
314             /* Found arg list end */
315 
316             EndOfArgList = TokenOffset;
317             break;
318         }
319 
320         TokenOffset++;
321     }
322 
323     /* At this point, we know that we have a reasonable argument list */
324 
325     Args = UtLocalCalloc (sizeof (PR_MACRO_ARG) * PR_MAX_MACRO_ARGS);
326 
327     /* Get the macro argument names */
328 
329     for (i = 0; i < PR_MAX_MACRO_ARGS; i++)
330     {
331         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
332 
333         if (!Token)
334         {
335             /* This is the case for a NULL macro body */
336 
337             BodyInSource = "";
338             goto AddMacroToList;
339         }
340 
341         /* Don't go beyond the argument list */
342 
343         TokenOffset = Token - AslGbl_MainTokenBuffer + strlen (Token);
344         if (TokenOffset > EndOfArgList)
345         {
346             break;
347         }
348 
349         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
350             "Macro param: %s\n",
351             AslGbl_CurrentLineNumber, Token);
352 
353         Args[i].Name = UtLocalCalloc (strlen (Token) + 1);
354         strcpy (Args[i].Name, Token);
355 
356         Args[i].UseCount = 0;
357         ArgCount++;
358         if (ArgCount >= PR_MAX_MACRO_ARGS)
359         {
360             PrError (ASL_ERROR, ASL_MSG_TOO_MANY_ARGUMENTS, TokenOffset);
361             goto ErrorExit;
362         }
363     }
364 
365     /* Get the macro body. Token now points to start of body */
366 
367     MacroBodyOffset = Token - AslGbl_MainTokenBuffer;
368 
369     /* Match each method arg in the macro body for later use */
370 
371     while (Token)
372     {
373         /* Search the macro arg list for matching arg */
374 
375         for (i = 0; ((i < PR_MAX_MACRO_ARGS) && Args[i].Name); i++)
376         {
377             /*
378              * Save argument offset within macro body. This is the mechanism
379              * used to expand the macro upon invocation.
380              *
381              * Handles multiple instances of the same argument
382              */
383             if (!strcmp (Token, Args[i].Name))
384             {
385                 UseCount = Args[i].UseCount;
386 
387                 Args[i].Offset[UseCount] =
388                     (Token - AslGbl_MainTokenBuffer) - MacroBodyOffset;
389 
390 
391                 DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
392                     "Macro Arg #%u: %s UseCount %u Offset %u\n",
393                     AslGbl_CurrentLineNumber, i, Token,
394                     UseCount+1, Args[i].Offset[UseCount]);
395 
396                 Args[i].UseCount++;
397 
398                 if (Args[i].UseCount >= PR_MAX_ARG_INSTANCES)
399                 {
400                     PrError (ASL_ERROR, ASL_MSG_TOO_MANY_ARGUMENTS,
401                         THIS_TOKEN_OFFSET (Token));
402 
403                     goto ErrorExit;
404                 }
405                 break;
406             }
407         }
408 
409         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
410     }
411 
412     BodyInSource = &AslGbl_CurrentLineBuffer[MacroBodyOffset];
413 
414 
415 AddMacroToList:
416 
417     /* Check if name is already defined first */
418 
419     DefineInfo = PrMatchDefine (Name);
420     if (DefineInfo)
421     {
422         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
423             "#define: macro name already exists: %s\n",
424             AslGbl_CurrentLineNumber, Name);
425 
426         /* Error only if not exactly the same macro */
427 
428         if (strcmp (DefineInfo->Body, BodyInSource) ||
429             (DefineInfo->ArgCount != ArgCount))
430         {
431             PrError (ASL_ERROR, ASL_MSG_EXISTING_NAME,
432                 THIS_TOKEN_OFFSET (Name));
433         }
434 
435         goto ErrorExit;
436     }
437 
438     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
439         "Macro body: %s\n",
440         AslGbl_CurrentLineNumber, BodyInSource);
441 
442     /* Add macro to the #define list */
443 
444     DefineInfo = PrAddDefine (Name, BodyInSource, FALSE);
445     if (DefineInfo)
446     {
447         Body = UtLocalCalloc (strlen (BodyInSource) + 1);
448         strcpy (Body, BodyInSource);
449 
450         DefineInfo->Body = Body;
451         DefineInfo->Args = Args;
452         DefineInfo->ArgCount = ArgCount;
453     }
454 
455     return;
456 
457 
458 ErrorExit:
459     ACPI_FREE (Args);
460     return;
461 }
462 
463 
464 /*******************************************************************************
465  *
466  * FUNCTION:    PrDoMacroInvocation
467  *
468  * PARAMETERS:  TokenBuffer         - Current line buffer
469  *              MacroStart          - Start of the macro invocation within
470  *                                    the token buffer
471  *              DefineInfo          - Info for this macro
472  *              Next                - "Next" buffer from GetNextToken
473  *
474  * RETURN:      None
475  *
476  * DESCRIPTION: Expand a macro invocation
477  *
478  ******************************************************************************/
479 
480 void
481 PrDoMacroInvocation (
482     char                    *TokenBuffer,
483     char                    *MacroStart,
484     PR_DEFINE_INFO          *DefineInfo,
485     char                    **Next)
486 {
487     PR_MACRO_ARG            *Args;
488     char                    *Token = NULL;
489     UINT32                  TokenOffset;
490     UINT32                  Length;
491     UINT32                  i;
492     UINT32                  Diff1;
493     UINT32                  Diff2;
494 
495     /* Take a copy of the macro body for expansion */
496 
497     strcpy (AslGbl_MacroTokenBuffer, DefineInfo->Body);
498 
499     /* Replace each argument within the prototype body */
500 
501     Args = DefineInfo->Args;
502     if (!Args->Name)
503     {
504         /* This macro has no arguments */
505 
506         Token = PrGetNextToken (NULL, PR_MACRO_ARGUMENTS, Next);
507 
508         if (!Token)
509         {
510             goto BadInvocation;
511         }
512 
513         TokenOffset = (MacroStart - TokenBuffer);
514         Length = Token - MacroStart + strlen (Token) + 1;
515 
516         PrReplaceData (
517             &AslGbl_CurrentLineBuffer[TokenOffset], Length,
518             AslGbl_MacroTokenBuffer, strlen (AslGbl_MacroTokenBuffer));
519         return;
520     }
521 
522     while (Args->Name)
523     {
524         /* Get the next argument from macro invocation */
525 
526         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
527         if (!Token)
528         {
529             goto BadInvocation;
530         }
531 
532         /*
533          * Avoid optimizing using just 1 signed int due to specific
534          * non-portable implementations of signed ints
535          */
536         Diff1 = strlen (Args->Name) > strlen (Token) ? strlen (Args->Name) -
537             strlen (Token) : 0;
538 
539         Diff2 = strlen (Args->Name) < strlen (Token) ? strlen (Token) -
540             strlen (Args->Name) : 0;
541 
542         /* Replace all instances of this argument */
543 
544         for (i = 0; i < Args->UseCount; i++)
545         {
546             /*
547              * To test the output of the preprocessed macro function that
548              * is passed to the compiler
549              */
550 
551              /*
552               * fprintf (stderr, "Current token = %s \t Current arg_name = %s \
553               * \t strlen (Token) = %u \t strlen (Args->Name) = %u \t Offset = %u \
554               * \t UseCount = %u \t", Token, Args->Name, strlen (Token), \
555               *     strlen (Args->Name), Args->Offset[i], Args->UseCount);
556               */
557 
558             AslGbl_MacroTokenReplaceBuffer = (char *) calloc ((strlen (AslGbl_MacroTokenBuffer)), sizeof (char));
559 
560             PrReplaceResizeSubstring (Args, Diff1, Diff2, i, Token);
561 
562             DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
563                 "ExpandArg: %s\n",
564                 AslGbl_CurrentLineNumber, AslGbl_MacroTokenBuffer);
565         }
566 
567         Args++;
568     }
569 
570     if (!Token)
571     {
572         return;
573     }
574 
575     /* Replace the entire macro invocation with the expanded macro */
576 
577     TokenOffset = (MacroStart - TokenBuffer);
578     Length = Token - MacroStart + strlen (Token) + 1;
579 
580     PrReplaceData (
581         &AslGbl_CurrentLineBuffer[TokenOffset], Length,
582         AslGbl_MacroTokenBuffer, strlen (AslGbl_MacroTokenBuffer));
583 
584     return;
585 
586 BadInvocation:
587     PrError (ASL_ERROR, ASL_MSG_INVALID_INVOCATION,
588         THIS_TOKEN_OFFSET (MacroStart));
589 
590     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
591         "Bad macro invocation: %s\n",
592         AslGbl_CurrentLineNumber, AslGbl_MacroTokenBuffer);
593     return;
594 }
595