Merge "edify: Some clean-ups to libedify."
diff --git a/edify/README b/edify/README.md
similarity index 99%
rename from edify/README
rename to edify/README.md
index 810455c..b3330e2 100644
--- a/edify/README
+++ b/edify/README.md
@@ -1,3 +1,6 @@
+edify
+=====
+
 Update scripts (from donut onwards) are written in a new little
 scripting language ("edify") that is superficially somewhat similar to
 the old one ("amend").  This is a brief overview of the new language.
diff --git a/edify/edify_parser.cpp b/edify/edify_parser.cpp
index 15342d3..1b89cf2 100644
--- a/edify/edify_parser.cpp
+++ b/edify/edify_parser.cpp
@@ -45,7 +45,6 @@
 
 int main(int argc, char** argv) {
     RegisterBuiltins();
-    FinishRegistration();
 
     if (argc != 2) {
         printf("Usage: %s <edify script>\n", argv[0]);
diff --git a/edify/expr.cpp b/edify/expr.cpp
index aa3a55a..4000bc4 100644
--- a/edify/expr.cpp
+++ b/edify/expr.cpp
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-#include <string.h>
-#include <stdbool.h>
+#include "expr.h"
+
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
+#include <string.h>
 #include <unistd.h>
 
 #include <string>
+#include <unordered_map>
 
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include "expr.h"
-
 // Functions should:
 //
 //    - return a malloc()'d string
@@ -319,61 +319,22 @@
     return StringValue(strdup(name));
 }
 
-Expr* Build(Function fn, YYLTYPE loc, int count, ...) {
-    va_list v;
-    va_start(v, count);
-    Expr* e = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
-    e->fn = fn;
-    e->name = "(operator)";
-    e->argc = count;
-    e->argv = reinterpret_cast<Expr**>(malloc(count * sizeof(Expr*)));
-    int i;
-    for (i = 0; i < count; ++i) {
-        e->argv[i] = va_arg(v, Expr*);
-    }
-    va_end(v);
-    e->start = loc.start;
-    e->end = loc.end;
-    return e;
-}
-
 // -----------------------------------------------------------------
 //   the function table
 // -----------------------------------------------------------------
 
-static int fn_entries = 0;
-static int fn_size = 0;
-NamedFunction* fn_table = NULL;
+static std::unordered_map<std::string, Function> fn_table;
 
-void RegisterFunction(const char* name, Function fn) {
-    if (fn_entries >= fn_size) {
-        fn_size = fn_size*2 + 1;
-        fn_table = reinterpret_cast<NamedFunction*>(realloc(fn_table, fn_size * sizeof(NamedFunction)));
+void RegisterFunction(const std::string& name, Function fn) {
+    fn_table[name] = fn;
+}
+
+Function FindFunction(const std::string& name) {
+    if (fn_table.find(name) == fn_table.end()) {
+        return nullptr;
+    } else {
+        return fn_table[name];
     }
-    fn_table[fn_entries].name = name;
-    fn_table[fn_entries].fn = fn;
-    ++fn_entries;
-}
-
-static int fn_entry_compare(const void* a, const void* b) {
-    const char* na = ((const NamedFunction*)a)->name;
-    const char* nb = ((const NamedFunction*)b)->name;
-    return strcmp(na, nb);
-}
-
-void FinishRegistration() {
-    qsort(fn_table, fn_entries, sizeof(NamedFunction), fn_entry_compare);
-}
-
-Function FindFunction(const char* name) {
-    NamedFunction key;
-    key.name = name;
-    NamedFunction* nf = reinterpret_cast<NamedFunction*>(bsearch(&key, fn_table, fn_entries,
-            sizeof(NamedFunction), fn_entry_compare));
-    if (nf == NULL) {
-        return NULL;
-    }
-    return nf->fn;
 }
 
 void RegisterBuiltins() {
diff --git a/edify/expr.h b/edify/expr.h
index f045d93..cd6139a 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -21,11 +21,6 @@
 #include <string>
 
 #include "error_code.h"
-#include "yydefs.h"
-
-#define MAX_STRING_LEN 1024
-
-typedef struct Expr Expr;
 
 struct State {
     State(const std::string& script, void* cookie);
@@ -56,14 +51,15 @@
 #define VAL_STRING  1  // data will be NULL-terminated; size doesn't count null
 #define VAL_BLOB    2
 
-typedef struct {
+struct Value {
     int type;
     ssize_t size;
     char* data;
-} Value;
+};
 
-typedef Value* (*Function)(const char* name, State* state,
-                           int argc, Expr* argv[]);
+struct Expr;
+
+using Function = Value* (*)(const char* name, State* state, int argc, Expr* argv[]);
 
 struct Expr {
     Function fn;
@@ -100,43 +96,21 @@
 Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
 Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]);
 
-// Convenience function for building expressions with a fixed number
-// of arguments.
-Expr* Build(Function fn, YYLTYPE loc, int count, ...);
-
 // Global builtins, registered by RegisterBuiltins().
 Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
 Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]);
 Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]);
 
-
-// For setting and getting the global error string (when returning
-// NULL from a function).
-void SetError(const char* message);  // makes a copy
-const char* GetError();              // retains ownership
-void ClearError();
-
-
-typedef struct {
-  const char* name;
-  Function fn;
-} NamedFunction;
-
 // Register a new function.  The same Function may be registered under
 // multiple names, but a given name should only be used once.
-void RegisterFunction(const char* name, Function fn);
+void RegisterFunction(const std::string& name, Function fn);
 
 // Register all the builtins.
 void RegisterBuiltins();
 
-// Call this after all calls to RegisterFunction() but before parsing
-// any scripts to finish building the function table.
-void FinishRegistration();
-
 // Find the Function for a given name; return NULL if no such function
 // exists.
-Function FindFunction(const char* name);
-
+Function FindFunction(const std::string& name);
 
 // --- convenience functions for use in functions ---
 
diff --git a/edify/parser.yy b/edify/parser.yy
index 098a637..58a8dec 100644
--- a/edify/parser.yy
+++ b/edify/parser.yy
@@ -33,6 +33,25 @@
 void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
 struct yy_buffer_state* yy_scan_string(const char* yystr);
 
+// Convenience function for building expressions with a fixed number
+// of arguments.
+static Expr* Build(Function fn, YYLTYPE loc, size_t count, ...) {
+    va_list v;
+    va_start(v, count);
+    Expr* e = static_cast<Expr*>(malloc(sizeof(Expr)));
+    e->fn = fn;
+    e->name = "(operator)";
+    e->argc = count;
+    e->argv = static_cast<Expr**>(malloc(count * sizeof(Expr*)));
+    for (size_t i = 0; i < count; ++i) {
+        e->argv[i] = va_arg(v, Expr*);
+    }
+    va_end(v);
+    e->start = loc.start;
+    e->end = loc.end;
+    return e;
+}
+
 %}
 
 %locations
@@ -70,7 +89,7 @@
 ;
 
 expr:  STRING {
-    $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
+    $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
     $$->fn = Literal;
     $$->name = $1;
     $$->argc = 0;
@@ -91,9 +110,9 @@
 |  IF expr THEN expr ENDIF           { $$ = Build(IfElseFn, @$, 2, $2, $4); }
 |  IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
 | STRING '(' arglist ')' {
-    $$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr)));
+    $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
     $$->fn = FindFunction($1);
-    if ($$->fn == NULL) {
+    if ($$->fn == nullptr) {
         char buffer[256];
         snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
         yyerror(root, error_count, buffer);
@@ -113,12 +132,12 @@
 }
 | expr {
     $$.argc = 1;
-    $$.argv = reinterpret_cast<Expr**>(malloc(sizeof(Expr*)));
+    $$.argv = static_cast<Expr**>(malloc(sizeof(Expr*)));
     $$.argv[0] = $1;
 }
 | arglist ',' expr {
     $$.argc = $1.argc + 1;
-    $$.argv = reinterpret_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
+    $$.argv = static_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
     $$.argv[$$.argc-1] = $3;
 }
 ;
diff --git a/tests/component/edify_test.cpp b/tests/component/edify_test.cpp
index ede2ecb..a4dbb9f 100644
--- a/tests/component/edify_test.cpp
+++ b/tests/component/edify_test.cpp
@@ -22,17 +22,18 @@
 
 static void expect(const char* expr_str, const char* expected) {
     Expr* e;
-    int error_count;
-    EXPECT_EQ(parse_string(expr_str, &e, &error_count), 0);
+    int error_count = 0;
+    EXPECT_EQ(0, parse_string(expr_str, &e, &error_count));
+    EXPECT_EQ(0, error_count);
 
     State state(expr_str, nullptr);
 
     char* result = Evaluate(&state, e);
 
     if (expected == nullptr) {
-        EXPECT_EQ(result, nullptr);
+        EXPECT_EQ(nullptr, result);
     } else {
-        EXPECT_STREQ(result, expected);
+        EXPECT_STREQ(expected, result);
     }
 
     free(result);
@@ -42,7 +43,6 @@
   protected:
     virtual void SetUp() {
         RegisterBuiltins();
-        FinishRegistration();
     }
 };
 
@@ -149,3 +149,21 @@
     expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str());
 }
 
+TEST_F(EdifyTest, unknown_function) {
+    // unknown function
+    const char* script1 = "unknown_function()";
+    Expr* expr;
+    int error_count = 0;
+    EXPECT_EQ(1, parse_string(script1, &expr, &error_count));
+    EXPECT_EQ(1, error_count);
+
+    const char* script2 = "abc; unknown_function()";
+    error_count = 0;
+    EXPECT_EQ(1, parse_string(script2, &expr, &error_count));
+    EXPECT_EQ(1, error_count);
+
+    const char* script3 = "unknown_function1() || yes";
+    error_count = 0;
+    EXPECT_EQ(1, parse_string(script3, &expr, &error_count));
+    EXPECT_EQ(1, error_count);
+}
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index bd6534b..64a6b37 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -54,7 +54,6 @@
     virtual void SetUp() {
         RegisterBuiltins();
         RegisterInstallFunctions();
-        FinishRegistration();
     }
 };
 
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 45e31e0..c752ebb 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -111,7 +111,6 @@
     RegisterInstallFunctions();
     RegisterBlockImageFunctions();
     RegisterDeviceExtensions();
-    FinishRegistration();
 
     // Parse the script.