]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
memory management for block parameters
authorBVK Chaitanya <bvk.groups@gmail.com>
Wed, 21 Jul 2010 23:19:05 +0000 (04:49 +0530)
committerBVK Chaitanya <bvk.groups@gmail.com>
Wed, 21 Jul 2010 23:19:05 +0000 (04:49 +0530)
include/grub/script_sh.h
script/argv.c
script/execute.c
script/main.c
script/parser.y
script/script.c

index 43791d0406e5a8d8de2be902d963d345f495fede..7618633ad2ea319cd91bc2b3215d195a6992e8ca 100644 (file)
@@ -42,6 +42,10 @@ struct grub_script
   unsigned refcnt;
   struct grub_script_mem *mem;
   struct grub_script_cmd *cmd;
+
+  /* Other grub_script's from block arguments.  */
+  struct grub_script *siblings;
+  struct grub_script *children;
 };
 \f
 typedef enum
@@ -222,6 +226,9 @@ struct grub_parser_param
   /* The memory that was used while parsing and scanning.  */
   struct grub_script_mem *memused;
 
+  /* The block argument scripts.  */
+  struct grub_script *scripts;
+
   /* The result of the parser.  */
   struct grub_script_cmd *parsed;
 
@@ -365,13 +372,17 @@ grub_normal_parse_line (char *line, grub_reader_getline_t getline);
 static inline struct grub_script *
 grub_script_get (struct grub_script *script)
 {
-  script->refcnt++;
+  if (script)
+    script->refcnt++;
   return script;
 }
 
 static inline void
 grub_script_put (struct grub_script *script)
 {
+  if (! script)
+    return;
+
   if (script->refcnt == 0)
     grub_script_free (script);
   else
index 42d573a0a047cfc99f587e19572c1f0bc08a0d4a..a7acbc23e95531ba42ee22f022473d596ee8dd9a 100644 (file)
@@ -52,9 +52,12 @@ grub_script_argv_free (struct grub_script_argv *argv)
 
       grub_free (argv->args);
     }
+  if (argv->script)
+    grub_script_put (argv->script);
 
   argv->argc = 0;
   argv->args = 0;
+  argv->script = 0;
 }
 
 /* Prepare for next argc.  */
index 932be6635a2e352db9a014c7bafe19ae2ca7fe29..b9538c29ba47242ce8727a5cd983ac7c5f3e00a1 100644 (file)
@@ -201,7 +201,7 @@ grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
                  grub_script_argv_append (&result, arg->str) ||
                  grub_script_argv_append (&result, "}"))
                goto fail;
-             result.script = arg->script;
+             result.script = grub_script_get (arg->script);
              break;
 
            case GRUB_SCRIPT_ARG_TYPE_TEXT:
index 752a8cd8a0867b36725ebd0a8f95d2658b7a4bd6..19ce88c432143bffd3dbd9dccb4d3bf8c4816ba3 100644 (file)
@@ -34,7 +34,7 @@ grub_normal_parse_line (char *line, grub_reader_getline_t getline)
       grub_script_execute (parsed_script);
 
       /* The parsed script was executed, throw it away.  */
-      grub_script_free (parsed_script);
+      grub_script_put (parsed_script);
     }
 
   return grub_errno;
index 76dc3fbca7d8967647c9d1c5bdaacdebbfefe85a..4f57ea4ffc9472526cf6aeef481ee4b92d39759f 100644 (file)
@@ -38,6 +38,7 @@
   struct {
     unsigned offset;
     struct grub_script_mem *memory;
+    struct grub_script *scripts;
   };
 }
 
@@ -152,16 +153,45 @@ argument : "case"      { $$ = grub_script_add_arglist (state, 0, $1); }
          | word { $$ = $1; }
 ;
 
+/*
+  Block parameter is passed to commands in two forms: as unparsed
+  string and as pre-parsed grub_script object.  Passing as grub_script
+  object makes memory management difficult, because:
+
+  (1) Command may want to keep a reference to grub_script objects for
+      later use, so script framework may not free the grub_script
+      object after command completes.
+
+  (2) Command may get called multiple times with same grub_script
+      object under loops, so we should not let command implementation
+      to free the grub_script object.
+
+  To solve above problems, we rely on reference counting for
+  grub_script objects.  Commands that want to keep the grub_script
+  object must take a reference to it.
+
+  Other complexity comes with arbitrary nesting of grub_script
+  objects: a grub_script object may have commands with several block
+  parameters, and each block parameter may further contain multiple
+  block parameters nested.  We use temporary variable, state->scripts
+  to collect nested child scripts (that are linked by siblings and
+  children members), and will build grub_scripts tree from bottom.
+ */
 block: "{"
        {
          grub_script_lexer_ref (state->lexerstate);
          $<offset>$ = grub_script_lexer_record_start (state);
         $<memory>$ = grub_script_mem_record (state);
+
+        /* save currently known scripts.  */
+        $<scripts>$ = state->scripts;
+        state->scripts = 0;
        }
        commands1 delimiters0 "}"
        {
          char *p;
         struct grub_script_mem *memory;
+        struct grub_script *s = $<scripts>2;
 
         memory = grub_script_mem_record_stop (state, $<memory>2);
          if ((p = grub_script_lexer_record_stop (state, $<offset>2)))
@@ -171,6 +201,19 @@ block: "{"
         if (! $$ || ! ($$->script = grub_script_create ($3, memory)))
           grub_script_mem_free (memory);
 
+        else {
+          /* attach nested scripts to $$->script as children */
+          $$->script->children = state->scripts;
+
+          /* restore old scripts; append $$->script to siblings. */
+          state->scripts = $<scripts>2 ?: $$->script;
+          if (s) {
+            while (s->siblings)
+              s = s->siblings;
+            s->siblings = $$->script;
+          }
+        }
+
          grub_script_lexer_deref (state->lexerstate);
        }
 ;
@@ -243,10 +286,13 @@ commands1: newlines0 command
            }
 ;
 
-function: "function" "name" 
+function: "function" "name"
           {
             grub_script_lexer_ref (state->lexerstate);
             state->func_mem = grub_script_mem_record (state);
+
+           $<scripts>$ = state->scripts;
+           state->scripts = 0;
           }
           delimiters0 "{" commands1 delimiters1 "}"
           {
@@ -256,9 +302,12 @@ function: "function" "name"
             script = grub_script_create ($6, state->func_mem);
             if (! script)
              grub_script_mem_free (state->func_mem);
-           else
+           else {
+             script->children = state->scripts;
              grub_script_function_create ($2, script);
+           }
 
+           state->scripts = $<scripts>3;
             grub_script_lexer_deref (state->lexerstate);
           }
 ;
index 7cf2f6baec106528d8ce02c0467d2092b58eccf4..6509b5f5d80d1d7f373b6db9b98dabebfce52d5d 100644 (file)
@@ -94,12 +94,19 @@ grub_script_mem_record_stop (struct grub_parser_param *state,
 void
 grub_script_free (struct grub_script *script)
 {
+  struct grub_script *s;
+
   if (!script)
     return;
 
   if (script->mem)
     grub_script_mem_free (script->mem);
 
+  s = script->children;
+  while (s) {
+    grub_script_put (s);
+    s = s->siblings;
+  }
   grub_free (script);
 }
 \f
@@ -346,6 +353,8 @@ grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem)
   parsed->mem = mem;
   parsed->cmd = cmd;
   parsed->refcnt = 0;
+  parsed->siblings = 0;
+  parsed->children = 0;
 
   return parsed;
 }
@@ -394,6 +403,7 @@ grub_script_parse (char *script, grub_reader_getline_t getline)
 
   parsed->mem = grub_script_mem_record_stop (parsestate, membackup);
   parsed->cmd = parsestate->parsed;
+  parsed->children = parsestate->scripts;
 
   grub_script_lexer_fini (lexstate);
   grub_free (parsestate);