1 /******************************************************************************
   2  *
   3  * Module Name: prmacros - Preprocessor #define macro support
   4  *
   5  *****************************************************************************/
   6 
   7 /*
   8  * Copyright (C) 2000 - 2014, 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 "aslcompiler.h"
  45 #include "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             return;
 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] = (Token - Gbl_MainTokenBuffer) - MacroBodyOffset;
 391 
 392                 DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
 393                     "Macro Arg #%u: %s UseCount %u Offset %u \n",
 394                     Gbl_CurrentLineNumber, i, Token,
 395                     UseCount+1, Args[i].Offset[UseCount]);
 396 
 397                 Args[i].UseCount++;
 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                     return;
 404                 }
 405                 break;
 406             }
 407         }
 408 
 409         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
 410     }
 411 
 412     BodyInSource = &Gbl_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             Gbl_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         return;
 436     }
 437 
 438     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
 439         "Macro body: %s \n",
 440         Gbl_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 
 456 
 457 /*******************************************************************************
 458  *
 459  * FUNCTION:    PrDoMacroInvocation
 460  *
 461  * PARAMETERS:  TokenBuffer         - Current line buffer
 462  *              MacroStart          - Start of the macro invocation within
 463  *                                    the token buffer
 464  *              DefineInfo          - Info for this macro
 465  *              Next                - "Next" buffer from GetNextToken
 466  *
 467  * RETURN:      None
 468  *
 469  * DESCRIPTION: Expand a macro invocation
 470  *
 471  ******************************************************************************/
 472 
 473 void
 474 PrDoMacroInvocation (
 475     char                    *TokenBuffer,
 476     char                    *MacroStart,
 477     PR_DEFINE_INFO          *DefineInfo,
 478     char                    **Next)
 479 {
 480     PR_MACRO_ARG            *Args;
 481     char                    *Token = NULL;
 482     UINT32                  TokenOffset;
 483     UINT32                  Length;
 484     UINT32                  i;
 485 
 486 
 487     /* Take a copy of the macro body for expansion */
 488 
 489     strcpy (Gbl_MacroTokenBuffer, DefineInfo->Body);
 490 
 491     /* Replace each argument within the prototype body */
 492 
 493     Args = DefineInfo->Args;
 494     if (!Args->Name)
 495     {
 496         /* This macro has no arguments */
 497 
 498         Token = PrGetNextToken (NULL, PR_MACRO_ARGUMENTS, Next);
 499         if (!Token)
 500         {
 501             goto BadInvocation;
 502         }
 503 
 504         TokenOffset = (MacroStart - TokenBuffer);
 505         Length = Token - MacroStart + strlen (Token) + 1;
 506 
 507         PrReplaceData (
 508             &Gbl_CurrentLineBuffer[TokenOffset], Length,
 509             Gbl_MacroTokenBuffer, strlen (Gbl_MacroTokenBuffer));
 510         return;
 511     }
 512 
 513     while (Args->Name)
 514     {
 515         /* Get the next argument from macro invocation */
 516 
 517         Token = PrGetNextToken (NULL, PR_MACRO_SEPARATORS, Next);
 518         if (!Token)
 519         {
 520             goto BadInvocation;
 521         }
 522 
 523         /* Replace all instances of this argument */
 524 
 525         for (i = 0; i < Args->UseCount; i++)
 526         {
 527             /* Offset zero indicates "arg not used" */
 528             /* TBD: Not really needed now, with UseCount available */
 529 
 530             if (Args->Offset[i] == 0)
 531             {
 532                 break;
 533             }
 534 
 535             PrReplaceData (
 536                 &Gbl_MacroTokenBuffer[Args->Offset[i]], strlen (Args->Name),
 537                 Token, strlen (Token));
 538 
 539             DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
 540                 "ExpandArg: %s \n",
 541                 Gbl_CurrentLineNumber, Gbl_MacroTokenBuffer);
 542         }
 543 
 544         Args++;
 545     }
 546 
 547     /* TBD: need to make sure macro was not invoked with too many arguments */
 548 
 549     if (!Token)
 550     {
 551         return;
 552     }
 553 
 554     /* Replace the entire macro invocation with the expanded macro */
 555 
 556     TokenOffset = (MacroStart - TokenBuffer);
 557     Length = Token - MacroStart + strlen (Token) + 1;
 558 
 559     PrReplaceData (
 560         &Gbl_CurrentLineBuffer[TokenOffset], Length,
 561         Gbl_MacroTokenBuffer, strlen (Gbl_MacroTokenBuffer));
 562 
 563     return;
 564 
 565 
 566 BadInvocation:
 567     PrError (ASL_ERROR, ASL_MSG_INVALID_INVOCATION,
 568         THIS_TOKEN_OFFSET (MacroStart));
 569 
 570     DbgPrint (ASL_DEBUG_OUTPUT, PR_PREFIX_ID
 571         "Bad macro invocation: %s \n",
 572         Gbl_CurrentLineNumber, Gbl_MacroTokenBuffer);
 573     return;
 574 }