edify: Some clean-ups to libedify.

- Remove dead declarations in expr.h: SetError(), GetError(),
  ClearError().
- Remove the declaration of Build() out of expr.h.
- Use std::unordered_map to implement RegisterFunction() and
  FindFunction(); kill FinishRegistration().
- Add a testcase for calling unknown functions.

Test: mmma bootable/recovery; recovery_component_test passes.
Change-Id: I9af6825ae677f92b22d716a4a5682f58522af03b
This commit is contained in:
Tao Bao 2016-10-10 22:52:18 -07:00
parent 19bb05dfc7
commit 39119ad8ec
8 changed files with 72 additions and 100 deletions

View file

@ -1,3 +1,6 @@
edify
=====
Update scripts (from donut onwards) are written in a new little Update scripts (from donut onwards) are written in a new little
scripting language ("edify") that is superficially somewhat similar to scripting language ("edify") that is superficially somewhat similar to
the old one ("amend"). This is a brief overview of the new language. the old one ("amend"). This is a brief overview of the new language.

View file

@ -45,7 +45,6 @@ static void ExprDump(int depth, const Expr* n, const std::string& script) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
RegisterBuiltins(); RegisterBuiltins();
FinishRegistration();
if (argc != 2) { if (argc != 2) {
printf("Usage: %s <edify script>\n", argv[0]); printf("Usage: %s <edify script>\n", argv[0]);

View file

@ -14,20 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
#include <string.h> #include "expr.h"
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <string> #include <string>
#include <unordered_map>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include "expr.h"
// Functions should: // Functions should:
// //
// - return a malloc()'d string // - return a malloc()'d string
@ -319,61 +319,22 @@ Value* Literal(const char* name, State* state, int argc, Expr* argv[]) {
return StringValue(strdup(name)); 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 // the function table
// ----------------------------------------------------------------- // -----------------------------------------------------------------
static int fn_entries = 0; static std::unordered_map<std::string, Function> fn_table;
static int fn_size = 0;
NamedFunction* fn_table = NULL;
void RegisterFunction(const char* name, Function fn) { void RegisterFunction(const std::string& name, Function fn) {
if (fn_entries >= fn_size) { fn_table[name] = fn;
fn_size = fn_size*2 + 1; }
fn_table = reinterpret_cast<NamedFunction*>(realloc(fn_table, fn_size * sizeof(NamedFunction)));
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() { void RegisterBuiltins() {

View file

@ -21,11 +21,6 @@
#include <string> #include <string>
#include "error_code.h" #include "error_code.h"
#include "yydefs.h"
#define MAX_STRING_LEN 1024
typedef struct Expr Expr;
struct State { struct State {
State(const std::string& script, void* cookie); State(const std::string& script, void* cookie);
@ -56,14 +51,15 @@ struct State {
#define VAL_STRING 1 // data will be NULL-terminated; size doesn't count null #define VAL_STRING 1 // data will be NULL-terminated; size doesn't count null
#define VAL_BLOB 2 #define VAL_BLOB 2
typedef struct { struct Value {
int type; int type;
ssize_t size; ssize_t size;
char* data; char* data;
} Value; };
typedef Value* (*Function)(const char* name, State* state, struct Expr;
int argc, Expr* argv[]);
using Function = Value* (*)(const char* name, State* state, int argc, Expr* argv[]);
struct Expr { struct Expr {
Function fn; Function fn;
@ -100,43 +96,21 @@ Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]);
Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]); Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]);
Value* SequenceFn(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(). // Global builtins, registered by RegisterBuiltins().
Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]); Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]);
Value* AssertFn(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[]); 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 // Register a new function. The same Function may be registered under
// multiple names, but a given name should only be used once. // 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. // Register all the builtins.
void RegisterBuiltins(); 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 // Find the Function for a given name; return NULL if no such function
// exists. // exists.
Function FindFunction(const char* name); Function FindFunction(const std::string& name);
// --- convenience functions for use in functions --- // --- convenience functions for use in functions ---

View file

@ -33,6 +33,25 @@ struct yy_buffer_state;
void yy_switch_to_buffer(struct yy_buffer_state* new_buffer); void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
struct yy_buffer_state* yy_scan_string(const char* yystr); 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 %locations
@ -70,7 +89,7 @@ input: expr { *root = $1; }
; ;
expr: STRING { expr: STRING {
$$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr))); $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
$$->fn = Literal; $$->fn = Literal;
$$->name = $1; $$->name = $1;
$$->argc = 0; $$->argc = 0;
@ -91,9 +110,9 @@ expr: STRING {
| IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); } | IF expr THEN expr ENDIF { $$ = Build(IfElseFn, @$, 2, $2, $4); }
| IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); } | IF expr THEN expr ELSE expr ENDIF { $$ = Build(IfElseFn, @$, 3, $2, $4, $6); }
| STRING '(' arglist ')' { | STRING '(' arglist ')' {
$$ = reinterpret_cast<Expr*>(malloc(sizeof(Expr))); $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
$$->fn = FindFunction($1); $$->fn = FindFunction($1);
if ($$->fn == NULL) { if ($$->fn == nullptr) {
char buffer[256]; char buffer[256];
snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1); snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
yyerror(root, error_count, buffer); yyerror(root, error_count, buffer);
@ -113,12 +132,12 @@ arglist: /* empty */ {
} }
| expr { | expr {
$$.argc = 1; $$.argc = 1;
$$.argv = reinterpret_cast<Expr**>(malloc(sizeof(Expr*))); $$.argv = static_cast<Expr**>(malloc(sizeof(Expr*)));
$$.argv[0] = $1; $$.argv[0] = $1;
} }
| arglist ',' expr { | arglist ',' expr {
$$.argc = $1.argc + 1; $$.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; $$.argv[$$.argc-1] = $3;
} }
; ;

View file

@ -22,17 +22,18 @@
static void expect(const char* expr_str, const char* expected) { static void expect(const char* expr_str, const char* expected) {
Expr* e; Expr* e;
int error_count; int error_count = 0;
EXPECT_EQ(parse_string(expr_str, &e, &error_count), 0); EXPECT_EQ(0, parse_string(expr_str, &e, &error_count));
EXPECT_EQ(0, error_count);
State state(expr_str, nullptr); State state(expr_str, nullptr);
char* result = Evaluate(&state, e); char* result = Evaluate(&state, e);
if (expected == nullptr) { if (expected == nullptr) {
EXPECT_EQ(result, nullptr); EXPECT_EQ(nullptr, result);
} else { } else {
EXPECT_STREQ(result, expected); EXPECT_STREQ(expected, result);
} }
free(result); free(result);
@ -42,7 +43,6 @@ class EdifyTest : public ::testing::Test {
protected: protected:
virtual void SetUp() { virtual void SetUp() {
RegisterBuiltins(); RegisterBuiltins();
FinishRegistration();
} }
}; };
@ -149,3 +149,21 @@ TEST_F(EdifyTest, big_string) {
expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str()); 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);
}

View file

@ -54,7 +54,6 @@ class UpdaterTest : public ::testing::Test {
virtual void SetUp() { virtual void SetUp() {
RegisterBuiltins(); RegisterBuiltins();
RegisterInstallFunctions(); RegisterInstallFunctions();
FinishRegistration();
} }
}; };

View file

@ -111,7 +111,6 @@ int main(int argc, char** argv) {
RegisterInstallFunctions(); RegisterInstallFunctions();
RegisterBlockImageFunctions(); RegisterBlockImageFunctions();
RegisterDeviceExtensions(); RegisterDeviceExtensions();
FinishRegistration();
// Parse the script. // Parse the script.