xref: /netbsd-src/sys/external/bsd/acpica/dist/compiler/prmacros.c (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
1 /******************************************************************************
2  *
3  * Module Name: prmacros - Preprocessor #define macro support
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2021, 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                  EndOfArgList;
289     char                    BufferChar;
290 
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         if (!Token)
333         {
334             /* This is the case for a NULL macro body */
335 
336             BodyInSource = "";
337             goto AddMacroToList;
338         }
339 
340         /* Don't go beyond the argument list */
341 
342         TokenOffset = Token - AslGbl_MainTokenBuffer + strlen (Token);
343         if (TokenOffset > EndOfArgList)
344         {
345             break;
346         }
347 
348         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
349             "Macro arg: %s \n",
350             AslGbl_CurrentLineNumber, Token);
351 
352         Args[i].Name = UtLocalCalloc (strlen (Token) + 1);
353         strcpy (Args[i].Name, Token);
354 
355         Args[i].UseCount = 0;
356 
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     Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
372     while (Token)
373     {
374         /* Search the macro arg list for matching arg */
375 
376         for (i = 0; ((i < PR_MAX_MACRO_ARGS) && Args[i].Name); i++)
377         {
378             /*
379              * Save argument offset within macro body. This is the mechanism
380              * used to expand the macro upon invocation.
381              *
382              * Handles multiple instances of the same argument
383              */
384             if (!strcmp (Token, Args[i].Name))
385             {
386                 UseCount = Args[i].UseCount;
387 
388                 Args[i].Offset[UseCount] =
389                     (Token - AslGbl_MainTokenBuffer) - MacroBodyOffset;
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                 if (Args[i].UseCount >= PR_MAX_ARG_INSTANCES)
398                 {
399                     PrError (ASL_ERROR, ASL_MSG_TOO_MANY_ARGUMENTS,
400                         THIS_TOKEN_OFFSET (Token));
401 
402                     goto ErrorExit;
403                 }
404                 break;
405             }
406         }
407 
408         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
409     }
410 
411     BodyInSource = &AslGbl_CurrentLineBuffer[MacroBodyOffset];
412 
413 
414 AddMacroToList:
415 
416     /* Check if name is already defined first */
417 
418     DefineInfo = PrMatchDefine (Name);
419     if (DefineInfo)
420     {
421         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
422             "#define: macro name already exists: %s\n",
423             AslGbl_CurrentLineNumber, Name);
424 
425         /* Error only if not exactly the same macro */
426 
427         if (strcmp (DefineInfo->Body, BodyInSource) ||
428             (DefineInfo->ArgCount != ArgCount))
429         {
430             PrError (ASL_ERROR, ASL_MSG_EXISTING_NAME,
431                 THIS_TOKEN_OFFSET (Name));
432         }
433 
434         goto ErrorExit;
435     }
436 
437     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
438         "Macro body: %s \n",
439         AslGbl_CurrentLineNumber, BodyInSource);
440 
441     /* Add macro to the #define list */
442 
443     DefineInfo = PrAddDefine (Name, BodyInSource, FALSE);
444     if (DefineInfo)
445     {
446         Body = UtLocalCalloc (strlen (BodyInSource) + 1);
447         strcpy (Body, BodyInSource);
448 
449         DefineInfo->Body = Body;
450         DefineInfo->Args = Args;
451         DefineInfo->ArgCount = ArgCount;
452     }
453 
454     return;
455 
456 
457 ErrorExit:
458     ACPI_FREE (Args);
459     return;
460 }
461 
462 
463 /*******************************************************************************
464  *
465  * FUNCTION:    PrDoMacroInvocation
466  *
467  * PARAMETERS:  TokenBuffer         - Current line buffer
468  *              MacroStart          - Start of the macro invocation within
469  *                                    the token buffer
470  *              DefineInfo          - Info for this macro
471  *              Next                - "Next" buffer from GetNextToken
472  *
473  * RETURN:      None
474  *
475  * DESCRIPTION: Expand a macro invocation
476  *
477  ******************************************************************************/
478 
479 void
480 PrDoMacroInvocation (
481     char                    *TokenBuffer,
482     char                    *MacroStart,
483     PR_DEFINE_INFO          *DefineInfo,
484     char                    **Next)
485 {
486     PR_MACRO_ARG            *Args;
487     char                    *Token = NULL;
488     UINT32                  TokenOffset;
489     UINT32                  Length;
490     UINT32                  i;
491 
492 
493     /* Take a copy of the macro body for expansion */
494 
495     strcpy (AslGbl_MacroTokenBuffer, DefineInfo->Body);
496 
497     /* Replace each argument within the prototype body */
498 
499     Args = DefineInfo->Args;
500     if (!Args->Name)
501     {
502         /* This macro has no arguments */
503 
504         Token = PrGetNextToken (NULL, PR_MACRO_ARGUMENTS, Next);
505         if (!Token)
506         {
507             goto BadInvocation;
508         }
509 
510         TokenOffset = (MacroStart - TokenBuffer);
511         Length = Token - MacroStart + strlen (Token) + 1;
512 
513         PrReplaceData (
514             &AslGbl_CurrentLineBuffer[TokenOffset], Length,
515             AslGbl_MacroTokenBuffer, strlen (AslGbl_MacroTokenBuffer));
516         return;
517     }
518 
519     while (Args->Name)
520     {
521         /* Get the next argument from macro invocation */
522 
523         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
524         if (!Token)
525         {
526             goto BadInvocation;
527         }
528 
529         /* Replace all instances of this argument */
530 
531         for (i = 0; i < Args->UseCount; i++)
532         {
533             /* Offset zero indicates "arg not used" */
534             /* TBD: Not really needed now, with UseCount available */
535 
536             if (Args->Offset[i] == 0)
537             {
538                 break;
539             }
540 
541             PrReplaceData (
542                 &AslGbl_MacroTokenBuffer[Args->Offset[i]], strlen (Args->Name),
543                 Token, strlen (Token));
544 
545             DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
546                 "ExpandArg: %s \n",
547                 AslGbl_CurrentLineNumber, AslGbl_MacroTokenBuffer);
548         }
549 
550         Args++;
551     }
552 
553     /* TBD: need to make sure macro was not invoked with too many arguments */
554 
555     if (!Token)
556     {
557         return;
558     }
559 
560     /* Replace the entire macro invocation with the expanded macro */
561 
562     TokenOffset = (MacroStart - TokenBuffer);
563     Length = Token - MacroStart + strlen (Token) + 1;
564 
565     PrReplaceData (
566         &AslGbl_CurrentLineBuffer[TokenOffset], Length,
567         AslGbl_MacroTokenBuffer, strlen (AslGbl_MacroTokenBuffer));
568 
569     return;
570 
571 
572 BadInvocation:
573     PrError (ASL_ERROR, ASL_MSG_INVALID_INVOCATION,
574         THIS_TOKEN_OFFSET (MacroStart));
575 
576     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
577         "Bad macro invocation: %s \n",
578         AslGbl_CurrentLineNumber, AslGbl_MacroTokenBuffer);
579     return;
580 }
581