xref: /freebsd/sys/contrib/dev/acpica/compiler/prmacros.c (revision 63d1fd5970ec814904aa0f4580b10a0d302d08b2)
1 /******************************************************************************
2  *
3  * Module Name: prmacros - Preprocessor #define macro support
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2016, 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 MERCHANTIBILITY 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 <contrib/dev/acpica/compiler/aslcompiler.h>
45 #include <contrib/dev/acpica/compiler/dtcompiler.h>
46 
47 
48 #define _COMPONENT          ASL_PREPROCESSOR
49         ACPI_MODULE_NAME    ("prmacros")
50 
51 
52 /*******************************************************************************
53  *
54  * FUNCTION:    PrDumpPredefinedNames
55  *
56  * PARAMETERS:  None
57  *
58  * RETURN:      None
59  *
60  * DESCRIPTION: Dump the list of #defines. Used as the preprocessor starts, to
61  *              display the names that were defined on the command line.
62  *              Debug information only.
63  *
64  ******************************************************************************/
65 
66 void
67 PrDumpPredefinedNames (
68     void)
69 {
70     PR_DEFINE_INFO          *DefineInfo;
71 
72 
73     DefineInfo = Gbl_DefineList;
74     while (DefineInfo)
75     {
76         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
77             "Predefined #define: %s->%s\n",
78             0, DefineInfo->Identifier, DefineInfo->Replacement);
79 
80         DefineInfo = DefineInfo->Next;
81     }
82 }
83 
84 
85 /*******************************************************************************
86  *
87  * FUNCTION:    PrAddDefine
88  *
89  * PARAMETERS:  Identifier          - Name to be replaced
90  *              Replacement         - Replacement for Identifier
91  *              Persist             - Keep define across multiple compiles?
92  *
93  * RETURN:      A new define_info struct. NULL on error.
94  *
95  * DESCRIPTION: Add a new #define to the global list
96  *
97  ******************************************************************************/
98 
99 PR_DEFINE_INFO *
100 PrAddDefine (
101     char                    *Identifier,
102     char                    *Replacement,
103     BOOLEAN                 Persist)
104 {
105     char                    *IdentifierString;
106     char                    *ReplacementString;
107     PR_DEFINE_INFO          *DefineInfo;
108 
109 
110     if (!Replacement)
111     {
112         Replacement = "";
113     }
114 
115     /* Check for already-defined first */
116 
117     DefineInfo = PrMatchDefine (Identifier);
118     if (DefineInfo)
119     {
120         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID,
121             "#define: name already exists: %s\n",
122             Gbl_CurrentLineNumber, Identifier);
123 
124         /*
125          * Name already exists. This is only an error if the target name
126          * is different.
127          */
128         if (strcmp (Replacement, DefineInfo->Replacement))
129         {
130             PrError (ASL_ERROR, ASL_MSG_EXISTING_NAME,
131                 THIS_TOKEN_OFFSET (Identifier));
132 
133             return (NULL);
134         }
135 
136         return (DefineInfo);
137     }
138 
139     /* Copy input strings */
140 
141     IdentifierString = UtLocalCalloc (strlen (Identifier) + 1);
142     strcpy (IdentifierString, Identifier);
143 
144     ReplacementString = UtLocalCalloc (strlen (Replacement) + 1);
145     strcpy (ReplacementString, Replacement);
146 
147     /* Init and link new define info struct */
148 
149     DefineInfo = UtLocalCalloc (sizeof (PR_DEFINE_INFO));
150     DefineInfo->Replacement = ReplacementString;
151     DefineInfo->Identifier = IdentifierString;
152     DefineInfo->Persist = Persist;
153 
154     if (Gbl_DefineList)
155     {
156         Gbl_DefineList->Previous = DefineInfo;
157     }
158 
159     DefineInfo->Next = Gbl_DefineList;
160     Gbl_DefineList = DefineInfo;
161     return (DefineInfo);
162 }
163 
164 
165 /*******************************************************************************
166  *
167  * FUNCTION:    PrRemoveDefine
168  *
169  * PARAMETERS:  DefineName          - Name of define to be removed
170  *
171  * RETURN:      None
172  *
173  * DESCRIPTION: Implements #undef. Remove a #define if found in the global
174  *              list. No error if the target of the #undef does not exist,
175  *              as per the C #undef definition.
176  *
177  ******************************************************************************/
178 
179 void
180 PrRemoveDefine (
181     char                    *DefineName)
182 {
183     PR_DEFINE_INFO          *DefineInfo;
184 
185 
186     /* Match name and delete the node */
187 
188     DefineInfo = Gbl_DefineList;
189     while (DefineInfo)
190     {
191         if (!strcmp (DefineName, DefineInfo->Identifier))
192         {
193             /* Remove from linked list */
194 
195             if (DefineInfo->Previous)
196             {
197                 (DefineInfo->Previous)->Next = DefineInfo->Next;
198             }
199             else
200             {
201                 Gbl_DefineList = DefineInfo->Next;
202             }
203 
204             if (DefineInfo->Next)
205             {
206                 (DefineInfo->Next)->Previous = DefineInfo->Previous;
207             }
208 
209             free (DefineInfo);
210             return;
211         }
212 
213         DefineInfo = DefineInfo->Next;
214     }
215 
216     /*
217      * Name was not found. By definition of #undef, this is not
218      * an error, however.
219      */
220     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
221         "#undef: could not find %s\n",
222         Gbl_CurrentLineNumber, DefineName);
223 }
224 
225 
226 /*******************************************************************************
227  *
228  * FUNCTION:    PrMatchDefine
229  *
230  * PARAMETERS:  MatchString         - Name associated with the #define
231  *
232  * RETURN:      Matched string if found. NULL otherwise.
233  *
234  * DESCRIPTION: Find a name in global #define list
235  *
236  ******************************************************************************/
237 
238 PR_DEFINE_INFO *
239 PrMatchDefine (
240     char                    *MatchString)
241 {
242     PR_DEFINE_INFO          *DefineInfo;
243 
244 
245     DefineInfo = Gbl_DefineList;
246     while (DefineInfo)
247     {
248         if (!strcmp (MatchString, DefineInfo->Identifier))
249         {
250             return (DefineInfo);
251         }
252 
253         DefineInfo = DefineInfo->Next;
254     }
255 
256     return (NULL);
257 }
258 
259 
260 /*******************************************************************************
261  *
262  * FUNCTION:    PrAddMacro
263  *
264  * PARAMETERS:  Name                - Start of the macro definition
265  *              Next                - "Next" buffer from GetNextToken
266  *
267  * RETURN:      None
268  *
269  * DESCRIPTION: Add a new macro to the list of #defines. Handles argument
270  *              processing.
271  *
272  ******************************************************************************/
273 
274 void
275 PrAddMacro (
276     char                    *Name,
277     char                    **Next)
278 {
279     char                    *Token = NULL;
280     ACPI_SIZE               TokenOffset;
281     ACPI_SIZE               MacroBodyOffset;
282     PR_DEFINE_INFO          *DefineInfo;
283     PR_MACRO_ARG            *Args;
284     char                    *Body;
285     char                    *BodyInSource;
286     UINT32                  i;
287     UINT16                  UseCount = 0;
288     UINT16                  ArgCount = 0;
289     UINT32                  Depth = 1;
290     UINT32                  EndOfArgList;
291     char                    BufferChar;
292 
293 
294     /* Find the end of the arguments list */
295 
296     TokenOffset = Name - Gbl_MainTokenBuffer + strlen (Name) + 1;
297     while (1)
298     {
299         BufferChar = Gbl_CurrentLineBuffer[TokenOffset];
300         if (BufferChar == '(')
301         {
302             Depth++;
303         }
304         else if (BufferChar == ')')
305         {
306             Depth--;
307         }
308         else if (BufferChar == 0)
309         {
310             PrError (ASL_ERROR, ASL_MSG_MACRO_SYNTAX, TokenOffset);
311             return;
312         }
313 
314         if (Depth == 0)
315         {
316             /* Found arg list end */
317 
318             EndOfArgList = TokenOffset;
319             break;
320         }
321 
322         TokenOffset++;
323     }
324 
325     /* At this point, we know that we have a reasonable argument list */
326 
327     Args = UtLocalCalloc (sizeof (PR_MACRO_ARG) * PR_MAX_MACRO_ARGS);
328 
329     /* Get the macro argument names */
330 
331     for (i = 0; i < PR_MAX_MACRO_ARGS; i++)
332     {
333         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
334         if (!Token)
335         {
336             /* This is the case for a NULL macro body */
337 
338             BodyInSource = "";
339             goto AddMacroToList;
340         }
341 
342         /* Don't go beyond the argument list */
343 
344         TokenOffset = Token - Gbl_MainTokenBuffer + strlen (Token);
345         if (TokenOffset > EndOfArgList)
346         {
347             break;
348         }
349 
350         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
351             "Macro arg: %s \n",
352             Gbl_CurrentLineNumber, Token);
353 
354         Args[i].Name = UtLocalCalloc (strlen (Token) + 1);
355         strcpy (Args[i].Name, Token);
356 
357         Args[i].UseCount = 0;
358 
359         ArgCount++;
360         if (ArgCount >= PR_MAX_MACRO_ARGS)
361         {
362             PrError (ASL_ERROR, ASL_MSG_TOO_MANY_ARGUMENTS, TokenOffset);
363             goto ErrorExit;
364         }
365     }
366 
367     /* Get the macro body. Token now points to start of body */
368 
369     MacroBodyOffset = Token - Gbl_MainTokenBuffer;
370 
371     /* Match each method arg in the macro body for later use */
372 
373     Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
374     while (Token)
375     {
376         /* Search the macro arg list for matching arg */
377 
378         for (i = 0; Args[i].Name && (i < PR_MAX_MACRO_ARGS); i++)
379         {
380             /*
381              * Save argument offset within macro body. This is the mechanism
382              * used to expand the macro upon invocation.
383              *
384              * Handles multiple instances of the same argument
385              */
386             if (!strcmp (Token, Args[i].Name))
387             {
388                 UseCount = Args[i].UseCount;
389 
390                 Args[i].Offset[UseCount] =
391                     (Token - Gbl_MainTokenBuffer) - MacroBodyOffset;
392 
393                 DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
394                     "Macro Arg #%u: %s UseCount %u Offset %u \n",
395                     Gbl_CurrentLineNumber, i, Token,
396                     UseCount+1, Args[i].Offset[UseCount]);
397 
398                 Args[i].UseCount++;
399                 if (Args[i].UseCount >= PR_MAX_ARG_INSTANCES)
400                 {
401                     PrError (ASL_ERROR, ASL_MSG_TOO_MANY_ARGUMENTS,
402                         THIS_TOKEN_OFFSET (Token));
403 
404                     goto ErrorExit;
405                 }
406                 break;
407             }
408         }
409 
410         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
411     }
412 
413     BodyInSource = &Gbl_CurrentLineBuffer[MacroBodyOffset];
414 
415 
416 AddMacroToList:
417 
418     /* Check if name is already defined first */
419 
420     DefineInfo = PrMatchDefine (Name);
421     if (DefineInfo)
422     {
423         DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
424             "#define: macro name already exists: %s\n",
425             Gbl_CurrentLineNumber, Name);
426 
427         /* Error only if not exactly the same macro */
428 
429         if (strcmp (DefineInfo->Body, BodyInSource) ||
430             (DefineInfo->ArgCount != ArgCount))
431         {
432             PrError (ASL_ERROR, ASL_MSG_EXISTING_NAME,
433                 THIS_TOKEN_OFFSET (Name));
434         }
435 
436         goto ErrorExit;
437     }
438 
439     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
440         "Macro body: %s \n",
441         Gbl_CurrentLineNumber, BodyInSource);
442 
443     /* Add macro to the #define list */
444 
445     DefineInfo = PrAddDefine (Name, BodyInSource, FALSE);
446     if (DefineInfo)
447     {
448         Body = UtLocalCalloc (strlen (BodyInSource) + 1);
449         strcpy (Body, BodyInSource);
450 
451         DefineInfo->Body = Body;
452         DefineInfo->Args = Args;
453         DefineInfo->ArgCount = ArgCount;
454     }
455 
456     return;
457 
458 
459 ErrorExit:
460     ACPI_FREE (Args);
461     return;
462 }
463 
464 
465 /*******************************************************************************
466  *
467  * FUNCTION:    PrDoMacroInvocation
468  *
469  * PARAMETERS:  TokenBuffer         - Current line buffer
470  *              MacroStart          - Start of the macro invocation within
471  *                                    the token buffer
472  *              DefineInfo          - Info for this macro
473  *              Next                - "Next" buffer from GetNextToken
474  *
475  * RETURN:      None
476  *
477  * DESCRIPTION: Expand a macro invocation
478  *
479  ******************************************************************************/
480 
481 void
482 PrDoMacroInvocation (
483     char                    *TokenBuffer,
484     char                    *MacroStart,
485     PR_DEFINE_INFO          *DefineInfo,
486     char                    **Next)
487 {
488     PR_MACRO_ARG            *Args;
489     char                    *Token = NULL;
490     UINT32                  TokenOffset;
491     UINT32                  Length;
492     UINT32                  i;
493 
494 
495     /* Take a copy of the macro body for expansion */
496 
497     strcpy (Gbl_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         if (!Token)
508         {
509             goto BadInvocation;
510         }
511 
512         TokenOffset = (MacroStart - TokenBuffer);
513         Length = Token - MacroStart + strlen (Token) + 1;
514 
515         PrReplaceData (
516             &Gbl_CurrentLineBuffer[TokenOffset], Length,
517             Gbl_MacroTokenBuffer, strlen (Gbl_MacroTokenBuffer));
518         return;
519     }
520 
521     while (Args->Name)
522     {
523         /* Get the next argument from macro invocation */
524 
525         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
526         if (!Token)
527         {
528             goto BadInvocation;
529         }
530 
531         /* Replace all instances of this argument */
532 
533         for (i = 0; i < Args->UseCount; i++)
534         {
535             /* Offset zero indicates "arg not used" */
536             /* TBD: Not really needed now, with UseCount available */
537 
538             if (Args->Offset[i] == 0)
539             {
540                 break;
541             }
542 
543             PrReplaceData (
544                 &Gbl_MacroTokenBuffer[Args->Offset[i]], strlen (Args->Name),
545                 Token, strlen (Token));
546 
547             DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
548                 "ExpandArg: %s \n",
549                 Gbl_CurrentLineNumber, Gbl_MacroTokenBuffer);
550         }
551 
552         Args++;
553     }
554 
555     /* TBD: need to make sure macro was not invoked with too many arguments */
556 
557     if (!Token)
558     {
559         return;
560     }
561 
562     /* Replace the entire macro invocation with the expanded macro */
563 
564     TokenOffset = (MacroStart - TokenBuffer);
565     Length = Token - MacroStart + strlen (Token) + 1;
566 
567     PrReplaceData (
568         &Gbl_CurrentLineBuffer[TokenOffset], Length,
569         Gbl_MacroTokenBuffer, strlen (Gbl_MacroTokenBuffer));
570 
571     return;
572 
573 
574 BadInvocation:
575     PrError (ASL_ERROR, ASL_MSG_INVALID_INVOCATION,
576         THIS_TOKEN_OFFSET (MacroStart));
577 
578     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
579         "Bad macro invocation: %s \n",
580         Gbl_CurrentLineNumber, Gbl_MacroTokenBuffer);
581     return;
582 }
583