1536db887f
This class adds a wrapper to the runtime dependent functions. Therefore, the behavior of update on device stays the same, while simulators can have their own implementations. Also change the caller side of the registered updater functions to call these runtime wrappers. Bug: 131911365 Test: unit tests pass, sideload an update on cuttlefish Change-Id: Ib3ab67132991d67fc132f27120e4152439d16ac5
425 lines
12 KiB
C++
425 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2009 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "edify/expr.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
|
|
#include "otautil/error_code.h"
|
|
|
|
// Functions should:
|
|
//
|
|
// - return a malloc()'d string
|
|
// - if Evaluate() on any argument returns nullptr, return nullptr.
|
|
|
|
static bool BooleanString(const std::string& s) {
|
|
return !s.empty();
|
|
}
|
|
|
|
bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result) {
|
|
if (result == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
|
|
if (!v) {
|
|
return false;
|
|
}
|
|
if (v->type != Value::Type::STRING) {
|
|
ErrorAbort(state, kArgsParsingFailure, "expecting string, got value type %d", v->type);
|
|
return false;
|
|
}
|
|
|
|
*result = v->data;
|
|
return true;
|
|
}
|
|
|
|
Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr) {
|
|
return expr->fn(expr->name.c_str(), state, expr->argv);
|
|
}
|
|
|
|
Value* StringValue(const char* str) {
|
|
if (str == nullptr) {
|
|
return nullptr;
|
|
}
|
|
return new Value(Value::Type::STRING, str);
|
|
}
|
|
|
|
Value* StringValue(const std::string& str) {
|
|
return StringValue(str.c_str());
|
|
}
|
|
|
|
Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
if (argv.empty()) {
|
|
return StringValue("");
|
|
}
|
|
std::string result;
|
|
for (size_t i = 0; i < argv.size(); ++i) {
|
|
std::string str;
|
|
if (!Evaluate(state, argv[i], &str)) {
|
|
return nullptr;
|
|
}
|
|
result += str;
|
|
}
|
|
|
|
return StringValue(result);
|
|
}
|
|
|
|
Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
if (argv.size() != 2 && argv.size() != 3) {
|
|
state->errmsg = "ifelse expects 2 or 3 arguments";
|
|
return nullptr;
|
|
}
|
|
|
|
std::string cond;
|
|
if (!Evaluate(state, argv[0], &cond)) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!cond.empty()) {
|
|
return EvaluateValue(state, argv[1]);
|
|
} else if (argv.size() == 3) {
|
|
return EvaluateValue(state, argv[2]);
|
|
}
|
|
|
|
return StringValue("");
|
|
}
|
|
|
|
Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string msg;
|
|
if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
|
|
state->errmsg += msg;
|
|
} else {
|
|
state->errmsg += "called abort()";
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
for (size_t i = 0; i < argv.size(); ++i) {
|
|
std::string result;
|
|
if (!Evaluate(state, argv[i], &result)) {
|
|
return nullptr;
|
|
}
|
|
if (result.empty()) {
|
|
int len = argv[i]->end - argv[i]->start;
|
|
state->errmsg = "assert failed: " + state->script.substr(argv[i]->start, len);
|
|
return nullptr;
|
|
}
|
|
}
|
|
return StringValue("");
|
|
}
|
|
|
|
Value* SleepFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string val;
|
|
if (!Evaluate(state, argv[0], &val)) {
|
|
return nullptr;
|
|
}
|
|
|
|
int v;
|
|
if (!android::base::ParseInt(val.c_str(), &v, 0)) {
|
|
return nullptr;
|
|
}
|
|
sleep(v);
|
|
|
|
return StringValue(val);
|
|
}
|
|
|
|
Value* StdoutFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
for (size_t i = 0; i < argv.size(); ++i) {
|
|
std::string v;
|
|
if (!Evaluate(state, argv[i], &v)) {
|
|
return nullptr;
|
|
}
|
|
fputs(v.c_str(), stdout);
|
|
}
|
|
return StringValue("");
|
|
}
|
|
|
|
Value* LogicalAndFn(const char* name, State* state,
|
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string left;
|
|
if (!Evaluate(state, argv[0], &left)) {
|
|
return nullptr;
|
|
}
|
|
if (BooleanString(left)) {
|
|
return EvaluateValue(state, argv[1]);
|
|
} else {
|
|
return StringValue("");
|
|
}
|
|
}
|
|
|
|
Value* LogicalOrFn(const char* name, State* state,
|
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string left;
|
|
if (!Evaluate(state, argv[0], &left)) {
|
|
return nullptr;
|
|
}
|
|
if (!BooleanString(left)) {
|
|
return EvaluateValue(state, argv[1]);
|
|
} else {
|
|
return StringValue(left);
|
|
}
|
|
}
|
|
|
|
Value* LogicalNotFn(const char* name, State* state,
|
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string val;
|
|
if (!Evaluate(state, argv[0], &val)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return StringValue(BooleanString(val) ? "" : "t");
|
|
}
|
|
|
|
Value* SubstringFn(const char* name, State* state,
|
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string needle;
|
|
if (!Evaluate(state, argv[0], &needle)) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::string haystack;
|
|
if (!Evaluate(state, argv[1], &haystack)) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::string result = (haystack.find(needle) != std::string::npos) ? "t" : "";
|
|
return StringValue(result);
|
|
}
|
|
|
|
Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string left;
|
|
if (!Evaluate(state, argv[0], &left)) {
|
|
return nullptr;
|
|
}
|
|
std::string right;
|
|
if (!Evaluate(state, argv[1], &right)) {
|
|
return nullptr;
|
|
}
|
|
|
|
const char* result = (left == right) ? "t" : "";
|
|
return StringValue(result);
|
|
}
|
|
|
|
Value* InequalityFn(const char* name, State* state,
|
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::string left;
|
|
if (!Evaluate(state, argv[0], &left)) {
|
|
return nullptr;
|
|
}
|
|
std::string right;
|
|
if (!Evaluate(state, argv[1], &right)) {
|
|
return nullptr;
|
|
}
|
|
|
|
const char* result = (left != right) ? "t" : "";
|
|
return StringValue(result);
|
|
}
|
|
|
|
Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
std::unique_ptr<Value> left(EvaluateValue(state, argv[0]));
|
|
if (!left) {
|
|
return nullptr;
|
|
}
|
|
return EvaluateValue(state, argv[1]);
|
|
}
|
|
|
|
Value* LessThanIntFn(const char* name, State* state,
|
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
if (argv.size() != 2) {
|
|
state->errmsg = "less_than_int expects 2 arguments";
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::string> args;
|
|
if (!ReadArgs(state, argv, &args)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Parse up to at least long long or 64-bit integers.
|
|
int64_t l_int;
|
|
if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
|
|
state->errmsg = "failed to parse int in " + args[0];
|
|
return nullptr;
|
|
}
|
|
|
|
int64_t r_int;
|
|
if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
|
|
state->errmsg = "failed to parse int in " + args[1];
|
|
return nullptr;
|
|
}
|
|
|
|
return StringValue(l_int < r_int ? "t" : "");
|
|
}
|
|
|
|
Value* GreaterThanIntFn(const char* name, State* state,
|
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
if (argv.size() != 2) {
|
|
state->errmsg = "greater_than_int expects 2 arguments";
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::string> args;
|
|
if (!ReadArgs(state, argv, &args)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Parse up to at least long long or 64-bit integers.
|
|
int64_t l_int;
|
|
if (!android::base::ParseInt(args[0].c_str(), &l_int)) {
|
|
state->errmsg = "failed to parse int in " + args[0];
|
|
return nullptr;
|
|
}
|
|
|
|
int64_t r_int;
|
|
if (!android::base::ParseInt(args[1].c_str(), &r_int)) {
|
|
state->errmsg = "failed to parse int in " + args[1];
|
|
return nullptr;
|
|
}
|
|
|
|
return StringValue(l_int > r_int ? "t" : "");
|
|
}
|
|
|
|
Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
|
return StringValue(name);
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
// the function table
|
|
// -----------------------------------------------------------------
|
|
|
|
static std::unordered_map<std::string, Function> fn_table;
|
|
|
|
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];
|
|
}
|
|
}
|
|
|
|
void RegisterBuiltins() {
|
|
RegisterFunction("ifelse", IfElseFn);
|
|
RegisterFunction("abort", AbortFn);
|
|
RegisterFunction("assert", AssertFn);
|
|
RegisterFunction("concat", ConcatFn);
|
|
RegisterFunction("is_substring", SubstringFn);
|
|
RegisterFunction("stdout", StdoutFn);
|
|
RegisterFunction("sleep", SleepFn);
|
|
|
|
RegisterFunction("less_than_int", LessThanIntFn);
|
|
RegisterFunction("greater_than_int", GreaterThanIntFn);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
// convenience methods for functions
|
|
// -----------------------------------------------------------------
|
|
|
|
// Evaluate the expressions in argv, and put the results of strings in args. If any expression
|
|
// evaluates to nullptr, return false. Return true on success.
|
|
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
|
std::vector<std::string>* args) {
|
|
return ReadArgs(state, argv, args, 0, argv.size());
|
|
}
|
|
|
|
bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
|
std::vector<std::string>* args, size_t start, size_t len) {
|
|
if (args == nullptr) {
|
|
return false;
|
|
}
|
|
if (start + len > argv.size()) {
|
|
return false;
|
|
}
|
|
for (size_t i = start; i < start + len; ++i) {
|
|
std::string var;
|
|
if (!Evaluate(state, argv[i], &var)) {
|
|
args->clear();
|
|
return false;
|
|
}
|
|
args->push_back(var);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Evaluate the expressions in argv, and put the results of Value* in args. If any expression
|
|
// evaluate to nullptr, return false. Return true on success.
|
|
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
|
std::vector<std::unique_ptr<Value>>* args) {
|
|
return ReadValueArgs(state, argv, args, 0, argv.size());
|
|
}
|
|
|
|
bool ReadValueArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
|
|
std::vector<std::unique_ptr<Value>>* args, size_t start, size_t len) {
|
|
if (args == nullptr) {
|
|
return false;
|
|
}
|
|
if (len == 0 || start + len > argv.size()) {
|
|
return false;
|
|
}
|
|
for (size_t i = start; i < start + len; ++i) {
|
|
std::unique_ptr<Value> v(EvaluateValue(state, argv[i]));
|
|
if (!v) {
|
|
args->clear();
|
|
return false;
|
|
}
|
|
args->push_back(std::move(v));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Use printf-style arguments to compose an error message to put into
|
|
// *state. Returns nullptr.
|
|
Value* ErrorAbort(State* state, const char* format, ...) {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
android::base::StringAppendV(&state->errmsg, format, ap);
|
|
va_end(ap);
|
|
return nullptr;
|
|
}
|
|
|
|
Value* ErrorAbort(State* state, CauseCode cause_code, const char* format, ...) {
|
|
std::string err_message;
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
android::base::StringAppendV(&err_message, format, ap);
|
|
va_end(ap);
|
|
// Ensure that there's exactly one line break at the end of the error message.
|
|
state->errmsg = android::base::Trim(err_message) + "\n";
|
|
state->cause_code = cause_code;
|
|
return nullptr;
|
|
}
|
|
|
|
State::State(const std::string& script, UpdaterInterface* interface)
|
|
: script(script), updater(interface), error_code(kNoError), cause_code(kNoCause) {}
|