merge in oc-release history after reset to master
diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp
index ca4ee52..a9dfccc 100644
--- a/asn1_decoder.cpp
+++ b/asn1_decoder.cpp
@@ -14,178 +14,145 @@
  * limitations under the License.
  */
 
-#include <malloc.h>
-#include <stdint.h>
-#include <string.h>
-
 #include "asn1_decoder.h"
 
+#include <stdint.h>
 
-typedef struct asn1_context {
-  size_t length;
-  const uint8_t* p;
-  int app_type;
-} asn1_context_t;
-
-
-static const int kMaskConstructed = 0xE0;
-static const int kMaskTag = 0x7F;
-static const int kMaskAppType = 0x1F;
-
-static const int kTagOctetString = 0x04;
-static const int kTagOid = 0x06;
-static const int kTagSequence = 0x30;
-static const int kTagSet = 0x31;
-static const int kTagConstructed = 0xA0;
-
-asn1_context_t* asn1_context_new(const uint8_t* buffer, size_t length) {
-    asn1_context_t* ctx = (asn1_context_t*) calloc(1, sizeof(asn1_context_t));
-    if (ctx == NULL) {
-        return NULL;
-    }
-    ctx->p = buffer;
-    ctx->length = length;
-    return ctx;
+int asn1_context::peek_byte() const {
+  if (length_ <= 0) {
+    return -1;
+  }
+  return *p_;
 }
 
-void asn1_context_free(asn1_context_t* ctx) {
-    free(ctx);
+int asn1_context::get_byte() {
+  if (length_ <= 0) {
+    return -1;
+  }
+
+  int byte = *p_;
+  p_++;
+  length_--;
+  return byte;
 }
 
-static inline int peek_byte(asn1_context_t* ctx) {
-    if (ctx->length <= 0) {
-        return -1;
-    }
-    return *ctx->p;
+bool asn1_context::skip_bytes(size_t num_skip) {
+  if (length_ < num_skip) {
+    return false;
+  }
+  p_ += num_skip;
+  length_ -= num_skip;
+  return true;
 }
 
-static inline int get_byte(asn1_context_t* ctx) {
-    if (ctx->length <= 0) {
-        return -1;
-    }
-    int byte = *ctx->p;
-    ctx->p++;
-    ctx->length--;
-    return byte;
-}
-
-static inline bool skip_bytes(asn1_context_t* ctx, size_t num_skip) {
-    if (ctx->length < num_skip) {
-        return false;
-    }
-    ctx->p += num_skip;
-    ctx->length -= num_skip;
+bool asn1_context::decode_length(size_t* out_len) {
+  int num_octets = get_byte();
+  if (num_octets == -1) {
+    return false;
+  }
+  if ((num_octets & 0x80) == 0x00) {
+    *out_len = num_octets;
     return true;
-}
-
-static bool decode_length(asn1_context_t* ctx, size_t* out_len) {
-    int num_octets = get_byte(ctx);
-    if (num_octets == -1) {
-        return false;
+  }
+  num_octets &= kMaskTag;
+  if (static_cast<size_t>(num_octets) >= sizeof(size_t)) {
+    return false;
+  }
+  size_t length = 0;
+  for (int i = 0; i < num_octets; ++i) {
+    int byte = get_byte();
+    if (byte == -1) {
+      return false;
     }
-    if ((num_octets & 0x80) == 0x00) {
-        *out_len = num_octets;
-        return 1;
-    }
-    num_octets &= kMaskTag;
-    if ((size_t)num_octets >= sizeof(size_t)) {
-        return false;
-    }
-    size_t length = 0;
-    for (int i = 0; i < num_octets; ++i) {
-        int byte = get_byte(ctx);
-        if (byte == -1) {
-            return false;
-        }
-        length <<= 8;
-        length += byte;
-    }
-    *out_len = length;
-    return true;
+    length <<= 8;
+    length += byte;
+  }
+  *out_len = length;
+  return true;
 }
 
 /**
  * Returns the constructed type and advances the pointer. E.g. A0 -> 0
  */
-asn1_context_t* asn1_constructed_get(asn1_context_t* ctx) {
-    int type = get_byte(ctx);
-    if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
-        return NULL;
-    }
+asn1_context* asn1_context::asn1_constructed_get() {
+  int type = get_byte();
+  if (type == -1 || (type & kMaskConstructed) != kTagConstructed) {
+    return nullptr;
+  }
+  size_t length;
+  if (!decode_length(&length) || length > length_) {
+    return nullptr;
+  }
+  asn1_context* app_ctx = new asn1_context(p_, length);
+  app_ctx->app_type_ = type & kMaskAppType;
+  return app_ctx;
+}
+
+bool asn1_context::asn1_constructed_skip_all() {
+  int byte = peek_byte();
+  while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
+    skip_bytes(1);
     size_t length;
-    if (!decode_length(ctx, &length) || length > ctx->length) {
-        return NULL;
+    if (!decode_length(&length) || !skip_bytes(length)) {
+      return false;
     }
-    asn1_context_t* app_ctx = asn1_context_new(ctx->p, length);
-    app_ctx->app_type = type & kMaskAppType;
-    return app_ctx;
+    byte = peek_byte();
+  }
+  return byte != -1;
 }
 
-bool asn1_constructed_skip_all(asn1_context_t* ctx) {
-    int byte = peek_byte(ctx);
-    while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) {
-        skip_bytes(ctx, 1);
-        size_t length;
-        if (!decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
-            return false;
-        }
-        byte = peek_byte(ctx);
-    }
-    return byte != -1;
+int asn1_context::asn1_constructed_type() const {
+  return app_type_;
 }
 
-int asn1_constructed_type(asn1_context_t* ctx) {
-    return ctx->app_type;
+asn1_context* asn1_context::asn1_sequence_get() {
+  if ((get_byte() & kMaskTag) != kTagSequence) {
+    return nullptr;
+  }
+  size_t length;
+  if (!decode_length(&length) || length > length_) {
+    return nullptr;
+  }
+  return new asn1_context(p_, length);
 }
 
-asn1_context_t* asn1_sequence_get(asn1_context_t* ctx) {
-    if ((get_byte(ctx) & kMaskTag) != kTagSequence) {
-        return NULL;
-    }
-    size_t length;
-    if (!decode_length(ctx, &length) || length > ctx->length) {
-        return NULL;
-    }
-    return asn1_context_new(ctx->p, length);
+asn1_context* asn1_context::asn1_set_get() {
+  if ((get_byte() & kMaskTag) != kTagSet) {
+    return nullptr;
+  }
+  size_t length;
+  if (!decode_length(&length) || length > length_) {
+    return nullptr;
+  }
+  return new asn1_context(p_, length);
 }
 
-asn1_context_t* asn1_set_get(asn1_context_t* ctx) {
-    if ((get_byte(ctx) & kMaskTag) != kTagSet) {
-        return NULL;
-    }
-    size_t length;
-    if (!decode_length(ctx, &length) || length > ctx->length) {
-        return NULL;
-    }
-    return asn1_context_new(ctx->p, length);
+bool asn1_context::asn1_sequence_next() {
+  size_t length;
+  if (get_byte() == -1 || !decode_length(&length) || !skip_bytes(length)) {
+    return false;
+  }
+  return true;
 }
 
-bool asn1_sequence_next(asn1_context_t* ctx) {
-    size_t length;
-    if (get_byte(ctx) == -1 || !decode_length(ctx, &length) || !skip_bytes(ctx, length)) {
-        return false;
-    }
-    return true;
+bool asn1_context::asn1_oid_get(const uint8_t** oid, size_t* length) {
+  if (get_byte() != kTagOid) {
+    return false;
+  }
+  if (!decode_length(length) || *length == 0 || *length > length_) {
+    return false;
+  }
+  *oid = p_;
+  return true;
 }
 
-bool asn1_oid_get(asn1_context_t* ctx, const uint8_t** oid, size_t* length) {
-    if (get_byte(ctx) != kTagOid) {
-        return false;
-    }
-    if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
-        return false;
-    }
-    *oid = ctx->p;
-    return true;
-}
-
-bool asn1_octet_string_get(asn1_context_t* ctx, const uint8_t** octet_string, size_t* length) {
-    if (get_byte(ctx) != kTagOctetString) {
-        return false;
-    }
-    if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) {
-        return false;
-    }
-    *octet_string = ctx->p;
-    return true;
+bool asn1_context::asn1_octet_string_get(const uint8_t** octet_string, size_t* length) {
+  if (get_byte() != kTagOctetString) {
+    return false;
+  }
+  if (!decode_length(length) || *length == 0 || *length > length_) {
+    return false;
+  }
+  *octet_string = p_;
+  return true;
 }
diff --git a/asn1_decoder.h b/asn1_decoder.h
index fbd118f..3e99211 100644
--- a/asn1_decoder.h
+++ b/asn1_decoder.h
@@ -14,23 +14,42 @@
  * limitations under the License.
  */
 
-
 #ifndef ASN1_DECODER_H_
 #define ASN1_DECODER_H_
 
 #include <stdint.h>
 
-typedef struct asn1_context asn1_context_t;
+class asn1_context {
+ public:
+  asn1_context(const uint8_t* buffer, size_t length) : p_(buffer), length_(length), app_type_(0) {}
+  int asn1_constructed_type() const;
+  asn1_context* asn1_constructed_get();
+  bool asn1_constructed_skip_all();
+  asn1_context* asn1_sequence_get();
+  asn1_context* asn1_set_get();
+  bool asn1_sequence_next();
+  bool asn1_oid_get(const uint8_t** oid, size_t* length);
+  bool asn1_octet_string_get(const uint8_t** octet_string, size_t* length);
 
-asn1_context_t* asn1_context_new(const uint8_t* buffer, size_t length);
-void asn1_context_free(asn1_context_t* ctx);
-asn1_context_t* asn1_constructed_get(asn1_context_t* ctx);
-bool asn1_constructed_skip_all(asn1_context_t* ctx);
-int asn1_constructed_type(asn1_context_t* ctx);
-asn1_context_t* asn1_sequence_get(asn1_context_t* ctx);
-asn1_context_t* asn1_set_get(asn1_context_t* ctx);
-bool asn1_sequence_next(asn1_context_t* seq);
-bool asn1_oid_get(asn1_context_t* ctx, const uint8_t** oid, size_t* length);
-bool asn1_octet_string_get(asn1_context_t* ctx, const uint8_t** octet_string, size_t* length);
+ private:
+  static constexpr int kMaskConstructed = 0xE0;
+  static constexpr int kMaskTag = 0x7F;
+  static constexpr int kMaskAppType = 0x1F;
+
+  static constexpr int kTagOctetString = 0x04;
+  static constexpr int kTagOid = 0x06;
+  static constexpr int kTagSequence = 0x30;
+  static constexpr int kTagSet = 0x31;
+  static constexpr int kTagConstructed = 0xA0;
+
+  int peek_byte() const;
+  int get_byte();
+  bool skip_bytes(size_t num_skip);
+  bool decode_length(size_t* out_len);
+
+  const uint8_t* p_;
+  size_t length_;
+  int app_type_;
+};
 
 #endif /* ASN1_DECODER_H_ */
diff --git a/edify/edify_parser.cpp b/edify/edify_parser.cpp
index 908fcf1..f1b5628 100644
--- a/edify/edify_parser.cpp
+++ b/edify/edify_parser.cpp
@@ -27,18 +27,19 @@
 #include <errno.h>
 #include <stdio.h>
 
+#include <memory>
 #include <string>
 
 #include <android-base/file.h>
 
 #include "expr.h"
 
-static void ExprDump(int depth, const Expr* n, const std::string& script) {
+static void ExprDump(int depth, const std::unique_ptr<Expr>& n, const std::string& script) {
     printf("%*s", depth*2, "");
     printf("%s %p (%d-%d) \"%s\"\n",
-           n->name == NULL ? "(NULL)" : n->name, n->fn, n->start, n->end,
+           n->name.c_str(), n->fn, n->start, n->end,
            script.substr(n->start, n->end - n->start).c_str());
-    for (int i = 0; i < n->argc; ++i) {
+    for (size_t i = 0; i < n->argv.size(); ++i) {
         ExprDump(depth+1, n->argv[i], script);
     }
 }
@@ -57,7 +58,7 @@
         return 1;
     }
 
-    Expr* root;
+    std::unique_ptr<Expr> root;
     int error_count = 0;
     int error = parse_string(buffer.data(), &root, &error_count);
     printf("parse returned %d; %d errors encountered\n", error, error_count);
diff --git a/edify/expr.cpp b/edify/expr.cpp
index 329cf3a..2b7fd7a 100644
--- a/edify/expr.cpp
+++ b/edify/expr.cpp
@@ -40,12 +40,12 @@
     return !s.empty();
 }
 
-bool Evaluate(State* state, Expr* expr, std::string* result) {
+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, state, expr->argc, expr->argv));
+    std::unique_ptr<Value> v(expr->fn(expr->name.c_str(), state, expr->argv));
     if (!v) {
         return false;
     }
@@ -58,8 +58,8 @@
     return true;
 }
 
-Value* EvaluateValue(State* state, Expr* expr) {
-    return expr->fn(expr->name, state, expr->argc, expr->argv);
+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) {
@@ -73,12 +73,12 @@
     return StringValue(str.c_str());
 }
 
-Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc == 0) {
+Value* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.empty()) {
         return StringValue("");
     }
     std::string result;
-    for (int i = 0; i < argc; ++i) {
+    for (size_t i = 0; i < argv.size(); ++i) {
         std::string str;
         if (!Evaluate(state, argv[i], &str)) {
             return nullptr;
@@ -89,8 +89,8 @@
     return StringValue(result);
 }
 
-Value* IfElseFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 2 && argc != 3) {
+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;
     }
@@ -102,16 +102,16 @@
 
     if (!cond.empty()) {
         return EvaluateValue(state, argv[1]);
-    } else if (argc == 3) {
+    } else if (argv.size() == 3) {
         return EvaluateValue(state, argv[2]);
     }
 
     return StringValue("");
 }
 
-Value* AbortFn(const char* name, State* state, int argc, Expr* argv[]) {
+Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
     std::string msg;
-    if (argc > 0 && Evaluate(state, argv[0], &msg)) {
+    if (!argv.empty() && Evaluate(state, argv[0], &msg)) {
         state->errmsg = msg;
     } else {
         state->errmsg = "called abort()";
@@ -119,8 +119,8 @@
     return nullptr;
 }
 
-Value* AssertFn(const char* name, State* state, int argc, Expr* argv[]) {
-    for (int i = 0; i < argc; ++i) {
+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;
@@ -134,7 +134,7 @@
     return StringValue("");
 }
 
-Value* SleepFn(const char* name, State* state, int argc, Expr* argv[]) {
+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;
@@ -149,8 +149,8 @@
     return StringValue(val);
 }
 
-Value* StdoutFn(const char* name, State* state, int argc, Expr* argv[]) {
-    for (int i = 0; i < argc; ++i) {
+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;
@@ -161,7 +161,7 @@
 }
 
 Value* LogicalAndFn(const char* name, State* state,
-                   int argc, Expr* argv[]) {
+                    const std::vector<std::unique_ptr<Expr>>& argv) {
     std::string left;
     if (!Evaluate(state, argv[0], &left)) {
         return nullptr;
@@ -174,7 +174,7 @@
 }
 
 Value* LogicalOrFn(const char* name, State* state,
-                   int argc, Expr* argv[]) {
+                   const std::vector<std::unique_ptr<Expr>>& argv) {
     std::string left;
     if (!Evaluate(state, argv[0], &left)) {
         return nullptr;
@@ -187,7 +187,7 @@
 }
 
 Value* LogicalNotFn(const char* name, State* state,
-                    int argc, Expr* argv[]) {
+                    const std::vector<std::unique_ptr<Expr>>& argv) {
     std::string val;
     if (!Evaluate(state, argv[0], &val)) {
         return nullptr;
@@ -197,7 +197,7 @@
 }
 
 Value* SubstringFn(const char* name, State* state,
-                   int argc, Expr* argv[]) {
+                   const std::vector<std::unique_ptr<Expr>>& argv) {
     std::string needle;
     if (!Evaluate(state, argv[0], &needle)) {
         return nullptr;
@@ -212,7 +212,7 @@
     return StringValue(result);
 }
 
-Value* EqualityFn(const char* name, State* state, int argc, Expr* argv[]) {
+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;
@@ -226,7 +226,8 @@
     return StringValue(result);
 }
 
-Value* InequalityFn(const char* name, State* state, int argc, Expr* argv[]) {
+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;
@@ -240,7 +241,7 @@
     return StringValue(result);
 }
 
-Value* SequenceFn(const char* name, State* state, int argc, Expr* argv[]) {
+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;
@@ -248,14 +249,15 @@
     return EvaluateValue(state, argv[1]);
 }
 
-Value* LessThanIntFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 2) {
+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, 2, argv, &args)) {
+    if (!ReadArgs(state, argv, &args)) {
         return nullptr;
     }
 
@@ -276,20 +278,34 @@
 }
 
 Value* GreaterThanIntFn(const char* name, State* state,
-                        int argc, Expr* argv[]) {
-    if (argc != 2) {
+                        const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.size() != 2) {
         state->errmsg = "greater_than_int expects 2 arguments";
         return nullptr;
     }
 
-    Expr* temp[2];
-    temp[0] = argv[1];
-    temp[1] = argv[0];
+    std::vector<std::string> args;
+    if (!ReadArgs(state, argv, &args)) {
+        return nullptr;
+    }
 
-    return LessThanIntFn(name, state, 2, temp);
+    // 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, int argc, Expr* argv[]) {
+Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
     return StringValue(name);
 }
 
@@ -329,14 +345,22 @@
 //   convenience methods for functions
 // -----------------------------------------------------------------
 
-// Evaluate the expressions in argv, and put the results of strings in
-// args. If any expression evaluates to nullptr, free the rest and return
-// false. Return true on success.
-bool ReadArgs(State* state, int argc, Expr* argv[], std::vector<std::string>* args) {
+// 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;
     }
-    for (int i = 0; i < argc; ++i) {
+    if (len == 0 || 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();
@@ -347,15 +371,22 @@
     return true;
 }
 
-// Evaluate the expressions in argv, and put the results of Value* in
-// args. If any expression evaluate to nullptr, free the rest and return
-// false. Return true on success.
-bool ReadValueArgs(State* state, int argc, Expr* argv[],
+// 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;
     }
-    for (int i = 0; i < argc; ++i) {
+    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();
diff --git a/edify/expr.h b/edify/expr.h
index 911adbc..4838d20 100644
--- a/edify/expr.h
+++ b/edify/expr.h
@@ -18,7 +18,10 @@
 #define _EXPRESSION_H
 
 #include <unistd.h>
+
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "error_code.h"
 
@@ -65,47 +68,49 @@
 
 struct Expr;
 
-using Function = Value* (*)(const char* name, State* state, int argc, Expr* argv[]);
+using Function = Value* (*)(const char* name, State* state,
+                            const std::vector<std::unique_ptr<Expr>>& argv);
 
 struct Expr {
-    Function fn;
-    const char* name;
-    int argc;
-    Expr** argv;
-    int start, end;
+  Function fn;
+  std::string name;
+  std::vector<std::unique_ptr<Expr>> argv;
+  int start, end;
+
+  Expr(Function fn, const std::string& name, int start, int end) :
+    fn(fn),
+    name(name),
+    start(start),
+    end(end) {}
 };
 
-// Take one of the Expr*s passed to the function as an argument,
-// evaluate it, return the resulting Value.  The caller takes
-// ownership of the returned Value.
-Value* EvaluateValue(State* state, Expr* expr);
+// Evaluate the input expr, return the resulting Value.
+Value* EvaluateValue(State* state, const std::unique_ptr<Expr>& expr);
 
-// Take one of the Expr*s passed to the function as an argument,
-// evaluate it, assert that it is a string, and update the result
-// parameter. This function returns true if the evaluation succeeds.
-// This is a convenience function for older functions that want to
-// deal only with strings.
-bool Evaluate(State* state, Expr* expr, std::string* result);
+// Evaluate the input expr, assert that it is a string, and update the result parameter. This
+// function returns true if the evaluation succeeds. This is a convenience function for older
+// functions that want to deal only with strings.
+bool Evaluate(State* state, const std::unique_ptr<Expr>& expr, std::string* result);
 
 // Glue to make an Expr out of a literal.
-Value* Literal(const char* name, State* state, int argc, Expr* argv[]);
+Value* Literal(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
 
 // Functions corresponding to various syntactic sugar operators.
 // ("concat" is also available as a builtin function, to concatenate
 // more than two strings.)
-Value* ConcatFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalAndFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalOrFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* LogicalNotFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* SubstringFn(const char* name, State* state, int argc, Expr* argv[]);
-Value* EqualityFn(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* ConcatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalAndFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalOrFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* LogicalNotFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* SubstringFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* EqualityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* InequalityFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* SequenceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
 
 // 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[]);
+Value* IfElseFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* AssertFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
+Value* AbortFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv);
 
 // Register a new function.  The same Function may be registered under
 // multiple names, but a given name should only be used once.
@@ -120,15 +125,19 @@
 
 // --- convenience functions for use in functions ---
 
-// Evaluate the expressions in argv, and put the results of strings in
-// args. If any expression evaluates to nullptr, free the rest and return
-// false. Return true on success.
-bool ReadArgs(State* state, int argc, Expr* argv[], std::vector<std::string>* args);
+// 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);
+bool ReadArgs(State* state, const std::vector<std::unique_ptr<Expr>>& argv,
+              std::vector<std::string>* args, size_t start, size_t len);
 
-// Evaluate the expressions in argv, and put the results of Value* in
-// args. If any expression evaluate to nullptr, free the rest and return
-// false. Return true on success.
-bool ReadValueArgs(State* state, int argc, Expr* argv[], std::vector<std::unique_ptr<Value>>* args);
+// 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);
+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);
 
 // Use printf-style arguments to compose an error message to put into
 // *state.  Returns NULL.
@@ -145,6 +154,6 @@
 
 Value* StringValue(const std::string& str);
 
-int parse_string(const char* str, Expr** root, int* error_count);
+int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count);
 
 #endif  // _EXPRESSION_H
diff --git a/edify/parser.yy b/edify/parser.yy
index 58a8dec..97205fe 100644
--- a/edify/parser.yy
+++ b/edify/parser.yy
@@ -19,6 +19,10 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "expr.h"
 #include "yydefs.h"
 #include "parser.h"
@@ -26,8 +30,8 @@
 extern int gLine;
 extern int gColumn;
 
-void yyerror(Expr** root, int* error_count, const char* s);
-int yyparse(Expr** root, int* error_count);
+void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s);
+int yyparse(std::unique_ptr<Expr>* root, int* error_count);
 
 struct yy_buffer_state;
 void yy_switch_to_buffer(struct yy_buffer_state* new_buffer);
@@ -38,17 +42,11 @@
 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*)));
+    Expr* e = new Expr(fn, "(operator)", loc.start, loc.end);
     for (size_t i = 0; i < count; ++i) {
-        e->argv[i] = va_arg(v, Expr*);
+        e->argv.emplace_back(va_arg(v, Expr*));
     }
     va_end(v);
-    e->start = loc.start;
-    e->end = loc.end;
     return e;
 }
 
@@ -59,10 +57,7 @@
 %union {
     char* str;
     Expr* expr;
-    struct {
-        int argc;
-        Expr** argv;
-    } args;
+    std::vector<std::unique_ptr<Expr>>* args;
 }
 
 %token AND OR SUBSTR SUPERSTR EQ NE IF THEN ELSE ENDIF
@@ -70,7 +65,10 @@
 %type <expr> expr
 %type <args> arglist
 
-%parse-param {Expr** root}
+%destructor { delete $$; } expr
+%destructor { delete $$; } arglist
+
+%parse-param {std::unique_ptr<Expr>* root}
 %parse-param {int* error_count}
 %error-verbose
 
@@ -85,17 +83,11 @@
 
 %%
 
-input:  expr           { *root = $1; }
+input:  expr           { root->reset($1); }
 ;
 
 expr:  STRING {
-    $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
-    $$->fn = Literal;
-    $$->name = $1;
-    $$->argc = 0;
-    $$->argv = NULL;
-    $$->start = @$.start;
-    $$->end = @$.end;
+    $$ = new Expr(Literal, $1, @$.start, @$.end);
 }
 |  '(' expr ')'                      { $$ = $2; $$->start=@$.start; $$->end=@$.end; }
 |  expr ';'                          { $$ = $1; $$->start=@1.start; $$->end=@1.end; }
@@ -110,41 +102,32 @@
 |  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 ')' {
-    $$ = static_cast<Expr*>(malloc(sizeof(Expr)));
-    $$->fn = FindFunction($1);
-    if ($$->fn == nullptr) {
-        char buffer[256];
-        snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1);
-        yyerror(root, error_count, buffer);
+    Function fn = FindFunction($1);
+    if (fn == nullptr) {
+        std::string msg = "unknown function \"" + std::string($1) + "\"";
+        yyerror(root, error_count, msg.c_str());
         YYERROR;
     }
-    $$->name = $1;
-    $$->argc = $3.argc;
-    $$->argv = $3.argv;
-    $$->start = @$.start;
-    $$->end = @$.end;
+    $$ = new Expr(fn, $1, @$.start, @$.end);
+    $$->argv = std::move(*$3);
 }
 ;
 
 arglist:    /* empty */ {
-    $$.argc = 0;
-    $$.argv = NULL;
+    $$ = new std::vector<std::unique_ptr<Expr>>;
 }
 | expr {
-    $$.argc = 1;
-    $$.argv = static_cast<Expr**>(malloc(sizeof(Expr*)));
-    $$.argv[0] = $1;
+    $$ = new std::vector<std::unique_ptr<Expr>>;
+    $$->emplace_back($1);
 }
 | arglist ',' expr {
-    $$.argc = $1.argc + 1;
-    $$.argv = static_cast<Expr**>(realloc($$.argv, $$.argc * sizeof(Expr*)));
-    $$.argv[$$.argc-1] = $3;
+    $$->push_back(std::unique_ptr<Expr>($3));
 }
 ;
 
 %%
 
-void yyerror(Expr** root, int* error_count, const char* s) {
+void yyerror(std::unique_ptr<Expr>* root, int* error_count, const char* s) {
   if (strlen(s) == 0) {
     s = "syntax error";
   }
@@ -152,7 +135,7 @@
   ++*error_count;
 }
 
-int parse_string(const char* str, Expr** root, int* error_count) {
+int parse_string(const char* str, std::unique_ptr<Expr>* root, int* error_count) {
     yy_switch_to_buffer(yy_scan_string(str));
     return yyparse(root, error_count);
 }
diff --git a/tests/component/edify_test.cpp b/tests/component/edify_test.cpp
index 287e40c..61a1e6b 100644
--- a/tests/component/edify_test.cpp
+++ b/tests/component/edify_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <memory>
 #include <string>
 
 #include <gtest/gtest.h>
@@ -21,7 +22,7 @@
 #include "edify/expr.h"
 
 static void expect(const char* expr_str, const char* expected) {
-    Expr* e;
+    std::unique_ptr<Expr> e;
     int error_count = 0;
     EXPECT_EQ(0, parse_string(expr_str, &e, &error_count));
     EXPECT_EQ(0, error_count);
@@ -152,7 +153,7 @@
 TEST_F(EdifyTest, unknown_function) {
     // unknown function
     const char* script1 = "unknown_function()";
-    Expr* expr;
+    std::unique_ptr<Expr> expr;
     int error_count = 0;
     EXPECT_EQ(1, parse_string(script1, &expr, &error_count));
     EXPECT_EQ(1, error_count);
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index 4f8349e..ef121a9 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -46,7 +47,7 @@
 
 static void expect(const char* expected, const char* expr_str, CauseCode cause_code,
                    UpdaterInfo* info = nullptr) {
-  Expr* e;
+  std::unique_ptr<Expr> e;
   int error_count = 0;
   ASSERT_EQ(0, parse_string(expr_str, &e, &error_count));
   ASSERT_EQ(0, error_count);
diff --git a/tests/unit/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp
index 997639d..b334a65 100644
--- a/tests/unit/asn1_decoder_test.cpp
+++ b/tests/unit/asn1_decoder_test.cpp
@@ -14,225 +14,188 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "asn1_decoder_test"
-
-#include <cutils/log.h>
-#include <gtest/gtest.h>
 #include <stdint.h>
-#include <unistd.h>
+
+#include <memory>
+
+#include <gtest/gtest.h>
 
 #include "asn1_decoder.h"
 
-namespace android {
+TEST(Asn1DecoderTest, Empty_Failure) {
+  uint8_t empty[] = {};
+  asn1_context ctx(empty, sizeof(empty));
 
-class Asn1DecoderTest : public testing::Test {
-};
+  ASSERT_EQ(nullptr, ctx.asn1_constructed_get());
+  ASSERT_FALSE(ctx.asn1_constructed_skip_all());
+  ASSERT_EQ(0, ctx.asn1_constructed_type());
+  ASSERT_EQ(nullptr, ctx.asn1_sequence_get());
+  ASSERT_EQ(nullptr, ctx.asn1_set_get());
+  ASSERT_FALSE(ctx.asn1_sequence_next());
 
-TEST_F(Asn1DecoderTest, Empty_Failure) {
-    uint8_t empty[] = { };
-    asn1_context_t* ctx = asn1_context_new(empty, sizeof(empty));
-
-    EXPECT_EQ(NULL, asn1_constructed_get(ctx));
-    EXPECT_FALSE(asn1_constructed_skip_all(ctx));
-    EXPECT_EQ(0, asn1_constructed_type(ctx));
-    EXPECT_EQ(NULL, asn1_sequence_get(ctx));
-    EXPECT_EQ(NULL, asn1_set_get(ctx));
-    EXPECT_FALSE(asn1_sequence_next(ctx));
-
-    const uint8_t* junk;
-    size_t length;
-    EXPECT_FALSE(asn1_oid_get(ctx, &junk, &length));
-    EXPECT_FALSE(asn1_octet_string_get(ctx, &junk, &length));
-
-    asn1_context_free(ctx);
+  const uint8_t* junk;
+  size_t length;
+  ASSERT_FALSE(ctx.asn1_oid_get(&junk, &length));
+  ASSERT_FALSE(ctx.asn1_octet_string_get(&junk, &length));
 }
 
-TEST_F(Asn1DecoderTest, ConstructedGet_TruncatedLength_Failure) {
-    uint8_t truncated[] = { 0xA0, 0x82, };
-    asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
-    EXPECT_EQ(NULL, asn1_constructed_get(ctx));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, ConstructedGet_TruncatedLength_Failure) {
+  uint8_t truncated[] = { 0xA0, 0x82 };
+  asn1_context ctx(truncated, sizeof(truncated));
+  ASSERT_EQ(nullptr, ctx.asn1_constructed_get());
 }
 
-TEST_F(Asn1DecoderTest, ConstructedGet_LengthTooBig_Failure) {
-    uint8_t truncated[] = { 0xA0, 0x8a, 0xA5, 0x5A, 0xA5, 0x5A,
-                            0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, };
-    asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
-    EXPECT_EQ(NULL, asn1_constructed_get(ctx));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, ConstructedGet_LengthTooBig_Failure) {
+  uint8_t truncated[] = { 0xA0, 0x8a, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A };
+  asn1_context ctx(truncated, sizeof(truncated));
+  ASSERT_EQ(nullptr, ctx.asn1_constructed_get());
 }
 
-TEST_F(Asn1DecoderTest, ConstructedGet_TooSmallForChild_Failure) {
-    uint8_t data[] = { 0xA5, 0x02, 0x06, 0x01, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    asn1_context_t* ptr = asn1_constructed_get(ctx);
-    ASSERT_NE((asn1_context_t*)NULL, ptr);
-    EXPECT_EQ(5, asn1_constructed_type(ptr));
-    const uint8_t* oid;
-    size_t length;
-    EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length));
-    asn1_context_free(ptr);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, ConstructedGet_TooSmallForChild_Failure) {
+  uint8_t data[] = { 0xA5, 0x02, 0x06, 0x01, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  std::unique_ptr<asn1_context> ptr(ctx.asn1_constructed_get());
+  ASSERT_NE(nullptr, ptr);
+  ASSERT_EQ(5, ptr->asn1_constructed_type());
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_FALSE(ptr->asn1_oid_get(&oid, &length));
 }
 
-TEST_F(Asn1DecoderTest, ConstructedGet_Success) {
-    uint8_t data[] = { 0xA5, 0x03, 0x06, 0x01, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    asn1_context_t* ptr = asn1_constructed_get(ctx);
-    ASSERT_NE((asn1_context_t*)NULL, ptr);
-    EXPECT_EQ(5, asn1_constructed_type(ptr));
-    const uint8_t* oid;
-    size_t length;
-    ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length));
-    EXPECT_EQ(1U, length);
-    EXPECT_EQ(0x01U, *oid);
-    asn1_context_free(ptr);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, ConstructedGet_Success) {
+  uint8_t data[] = { 0xA5, 0x03, 0x06, 0x01, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  std::unique_ptr<asn1_context> ptr(ctx.asn1_constructed_get());
+  ASSERT_NE(nullptr, ptr);
+  ASSERT_EQ(5, ptr->asn1_constructed_type());
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_TRUE(ptr->asn1_oid_get(&oid, &length));
+  ASSERT_EQ(1U, length);
+  ASSERT_EQ(0x01U, *oid);
 }
 
-TEST_F(Asn1DecoderTest, ConstructedSkipAll_TruncatedLength_Failure) {
-    uint8_t truncated[] = { 0xA2, 0x82, };
-    asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
-    EXPECT_FALSE(asn1_constructed_skip_all(ctx));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, ConstructedSkipAll_TruncatedLength_Failure) {
+  uint8_t truncated[] = { 0xA2, 0x82 };
+  asn1_context ctx(truncated, sizeof(truncated));
+  ASSERT_FALSE(ctx.asn1_constructed_skip_all());
 }
 
-TEST_F(Asn1DecoderTest, ConstructedSkipAll_Success) {
-    uint8_t data[] = { 0xA0, 0x03, 0x02, 0x01, 0x01,
-                            0xA1, 0x03, 0x02, 0x01, 0x01,
-                            0x06, 0x01, 0xA5, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    ASSERT_TRUE(asn1_constructed_skip_all(ctx));
-    const uint8_t* oid;
-    size_t length;
-    ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length));
-    EXPECT_EQ(1U, length);
-    EXPECT_EQ(0xA5U, *oid);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, ConstructedSkipAll_Success) {
+  uint8_t data[] = { 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA1, 0x03, 0x02, 0x01, 0x01, 0x06, 0x01, 0xA5 };
+  asn1_context ctx(data, sizeof(data));
+  ASSERT_TRUE(ctx.asn1_constructed_skip_all());
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_TRUE(ctx.asn1_oid_get(&oid, &length));
+  ASSERT_EQ(1U, length);
+  ASSERT_EQ(0xA5U, *oid);
 }
 
-TEST_F(Asn1DecoderTest, SequenceGet_TruncatedLength_Failure) {
-    uint8_t truncated[] = { 0x30, 0x82, };
-    asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
-    EXPECT_EQ(NULL, asn1_sequence_get(ctx));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, SequenceGet_TruncatedLength_Failure) {
+  uint8_t truncated[] = { 0x30, 0x82 };
+  asn1_context ctx(truncated, sizeof(truncated));
+  ASSERT_EQ(nullptr, ctx.asn1_sequence_get());
 }
 
-TEST_F(Asn1DecoderTest, SequenceGet_TooSmallForChild_Failure) {
-    uint8_t data[] = { 0x30, 0x02, 0x06, 0x01, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    asn1_context_t* ptr = asn1_sequence_get(ctx);
-    ASSERT_NE((asn1_context_t*)NULL, ptr);
-    const uint8_t* oid;
-    size_t length;
-    EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length));
-    asn1_context_free(ptr);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, SequenceGet_TooSmallForChild_Failure) {
+  uint8_t data[] = { 0x30, 0x02, 0x06, 0x01, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  std::unique_ptr<asn1_context> ptr(ctx.asn1_sequence_get());
+  ASSERT_NE(nullptr, ptr);
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_FALSE(ptr->asn1_oid_get(&oid, &length));
 }
 
-TEST_F(Asn1DecoderTest, SequenceGet_Success) {
-    uint8_t data[] = { 0x30, 0x03, 0x06, 0x01, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    asn1_context_t* ptr = asn1_sequence_get(ctx);
-    ASSERT_NE((asn1_context_t*)NULL, ptr);
-    const uint8_t* oid;
-    size_t length;
-    ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length));
-    EXPECT_EQ(1U, length);
-    EXPECT_EQ(0x01U, *oid);
-    asn1_context_free(ptr);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, SequenceGet_Success) {
+  uint8_t data[] = { 0x30, 0x03, 0x06, 0x01, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  std::unique_ptr<asn1_context> ptr(ctx.asn1_sequence_get());
+  ASSERT_NE(nullptr, ptr);
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_TRUE(ptr->asn1_oid_get(&oid, &length));
+  ASSERT_EQ(1U, length);
+  ASSERT_EQ(0x01U, *oid);
 }
 
-TEST_F(Asn1DecoderTest, SetGet_TruncatedLength_Failure) {
-    uint8_t truncated[] = { 0x31, 0x82, };
-    asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated));
-    EXPECT_EQ(NULL, asn1_set_get(ctx));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, SetGet_TruncatedLength_Failure) {
+  uint8_t truncated[] = { 0x31, 0x82 };
+  asn1_context ctx(truncated, sizeof(truncated));
+  ASSERT_EQ(nullptr, ctx.asn1_set_get());
 }
 
-TEST_F(Asn1DecoderTest, SetGet_TooSmallForChild_Failure) {
-    uint8_t data[] = { 0x31, 0x02, 0x06, 0x01, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    asn1_context_t* ptr = asn1_set_get(ctx);
-    ASSERT_NE((asn1_context_t*)NULL, ptr);
-    const uint8_t* oid;
-    size_t length;
-    EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length));
-    asn1_context_free(ptr);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, SetGet_TooSmallForChild_Failure) {
+  uint8_t data[] = { 0x31, 0x02, 0x06, 0x01, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  std::unique_ptr<asn1_context> ptr(ctx.asn1_set_get());
+  ASSERT_NE(nullptr, ptr);
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_FALSE(ptr->asn1_oid_get(&oid, &length));
 }
 
-TEST_F(Asn1DecoderTest, SetGet_Success) {
-    uint8_t data[] = { 0x31, 0x03, 0x06, 0x01, 0xBA, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    asn1_context_t* ptr = asn1_set_get(ctx);
-    ASSERT_NE((asn1_context_t*)NULL, ptr);
-    const uint8_t* oid;
-    size_t length;
-    ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length));
-    EXPECT_EQ(1U, length);
-    EXPECT_EQ(0xBAU, *oid);
-    asn1_context_free(ptr);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, SetGet_Success) {
+  uint8_t data[] = { 0x31, 0x03, 0x06, 0x01, 0xBA };
+  asn1_context ctx(data, sizeof(data));
+  std::unique_ptr<asn1_context> ptr(ctx.asn1_set_get());
+  ASSERT_NE(nullptr, ptr);
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_TRUE(ptr->asn1_oid_get(&oid, &length));
+  ASSERT_EQ(1U, length);
+  ASSERT_EQ(0xBAU, *oid);
 }
 
-TEST_F(Asn1DecoderTest, OidGet_LengthZero_Failure) {
-    uint8_t data[] = { 0x06, 0x00, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    const uint8_t* oid;
-    size_t length;
-    EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, OidGet_LengthZero_Failure) {
+  uint8_t data[] = { 0x06, 0x00, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_FALSE(ctx.asn1_oid_get(&oid, &length));
 }
 
-TEST_F(Asn1DecoderTest, OidGet_TooSmall_Failure) {
-    uint8_t data[] = { 0x06, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    const uint8_t* oid;
-    size_t length;
-    EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, OidGet_TooSmall_Failure) {
+  uint8_t data[] = { 0x06, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_FALSE(ctx.asn1_oid_get(&oid, &length));
 }
 
-TEST_F(Asn1DecoderTest, OidGet_Success) {
-    uint8_t data[] = { 0x06, 0x01, 0x99, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    const uint8_t* oid;
-    size_t length;
-    ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length));
-    EXPECT_EQ(1U, length);
-    EXPECT_EQ(0x99U, *oid);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, OidGet_Success) {
+  uint8_t data[] = { 0x06, 0x01, 0x99 };
+  asn1_context ctx(data, sizeof(data));
+  const uint8_t* oid;
+  size_t length;
+  ASSERT_TRUE(ctx.asn1_oid_get(&oid, &length));
+  ASSERT_EQ(1U, length);
+  ASSERT_EQ(0x99U, *oid);
 }
 
-TEST_F(Asn1DecoderTest, OctetStringGet_LengthZero_Failure) {
-    uint8_t data[] = { 0x04, 0x00, 0x55, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    const uint8_t* string;
-    size_t length;
-    ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, OctetStringGet_LengthZero_Failure) {
+  uint8_t data[] = { 0x04, 0x00, 0x55 };
+  asn1_context ctx(data, sizeof(data));
+  const uint8_t* string;
+  size_t length;
+  ASSERT_FALSE(ctx.asn1_octet_string_get(&string, &length));
 }
 
-TEST_F(Asn1DecoderTest, OctetStringGet_TooSmall_Failure) {
-    uint8_t data[] = { 0x04, 0x01, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    const uint8_t* string;
-    size_t length;
-    ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length));
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, OctetStringGet_TooSmall_Failure) {
+  uint8_t data[] = { 0x04, 0x01 };
+  asn1_context ctx(data, sizeof(data));
+  const uint8_t* string;
+  size_t length;
+  ASSERT_FALSE(ctx.asn1_octet_string_get(&string, &length));
 }
 
-TEST_F(Asn1DecoderTest, OctetStringGet_Success) {
-    uint8_t data[] = { 0x04, 0x01, 0xAA, };
-    asn1_context_t* ctx = asn1_context_new(data, sizeof(data));
-    const uint8_t* string;
-    size_t length;
-    ASSERT_TRUE(asn1_octet_string_get(ctx, &string, &length));
-    EXPECT_EQ(1U, length);
-    EXPECT_EQ(0xAAU, *string);
-    asn1_context_free(ctx);
+TEST(Asn1DecoderTest, OctetStringGet_Success) {
+  uint8_t data[] = { 0x04, 0x01, 0xAA };
+  asn1_context ctx(data, sizeof(data));
+  const uint8_t* string;
+  size_t length;
+  ASSERT_TRUE(ctx.asn1_octet_string_get(&string, &length));
+  ASSERT_EQ(1U, length);
+  ASSERT_EQ(0xAAU, *string);
 }
-
-} // namespace android
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 0fa83d9..efdfec1 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -44,6 +44,7 @@
 #include <android-base/unique_fd.h>
 #include <applypatch/applypatch.h>
 #include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
 #include <ziparchive/zip_archive.h>
 
 #include "edify/expr.h"
@@ -772,6 +773,11 @@
         return -1;
     }
 
+    if (fchown(fd, AID_SYSTEM, AID_SYSTEM) != 0) {  // system user
+        PLOG(ERROR) << "failed to chown \"" << fn << "\"";
+        return -1;
+    }
+
     if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
         return -1;
     }
@@ -841,6 +847,12 @@
       return -1;
     }
 
+    if (chown(dirname.c_str(), AID_SYSTEM, AID_SYSTEM) != 0) {  // system user
+      ErrorAbort(state, kStashCreationFailure, "chown \"%s\" failed: %s\n", dirname.c_str(),
+                 strerror(errno));
+      return -1;
+    }
+
     if (CacheSizeCheck(max_stash_size) != 0) {
       ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)\n",
                  max_stash_size);
@@ -1458,8 +1470,9 @@
 //    - new data stream (filename within package.zip)
 //    - patch stream (filename within package.zip, must be uncompressed)
 
-static Value* PerformBlockImageUpdate(const char* name, State* state, int /* argc */, Expr* argv[],
-        const Command* commands, size_t cmdcount, bool dryrun) {
+static Value* PerformBlockImageUpdate(const char* name, State* state,
+                                      const std::vector<std::unique_ptr<Expr>>& argv,
+                                      const Command* commands, size_t cmdcount, bool dryrun) {
     CommandParameters params = {};
     params.canwrite = !dryrun;
 
@@ -1468,9 +1481,14 @@
         is_retry = true;
         LOG(INFO) << "This update is a retry.";
     }
+    if (argv.size() != 4) {
+        ErrorAbort(state, kArgsParsingFailure, "block_image_update expects 4 arguments, got %zu",
+                   argv.size());
+        return StringValue("");
+    }
 
     std::vector<std::unique_ptr<Value>> args;
-    if (!ReadValueArgs(state, 4, argv, &args)) {
+    if (!ReadValueArgs(state, argv, &args)) {
         return nullptr;
     }
 
@@ -1750,7 +1768,8 @@
 // command has already been completed and verify the integrity of
 // the source data.
 
-Value* BlockImageVerifyFn(const char* name, State* state, int argc, Expr* argv[]) {
+Value* BlockImageVerifyFn(const char* name, State* state,
+                          const std::vector<std::unique_ptr<Expr>>& argv) {
     // Commands which are not tested are set to nullptr to skip them completely
     const Command commands[] = {
         { "bsdiff",     PerformCommandDiff  },
@@ -1764,11 +1783,12 @@
     };
 
     // Perform a dry run without writing to test if an update can proceed
-    return PerformBlockImageUpdate(name, state, argc, argv, commands,
+    return PerformBlockImageUpdate(name, state, argv, commands,
                 sizeof(commands) / sizeof(commands[0]), true);
 }
 
-Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
+Value* BlockImageUpdateFn(const char* name, State* state,
+                          const std::vector<std::unique_ptr<Expr>>& argv) {
     const Command commands[] = {
         { "bsdiff",     PerformCommandDiff  },
         { "erase",      PerformCommandErase },
@@ -1780,13 +1800,19 @@
         { "zero",       PerformCommandZero  }
     };
 
-    return PerformBlockImageUpdate(name, state, argc, argv, commands,
+    return PerformBlockImageUpdate(name, state, argv, commands,
                 sizeof(commands) / sizeof(commands[0]), false);
 }
 
-Value* RangeSha1Fn(const char* name, State* state, int /* argc */, Expr* argv[]) {
+Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.size() != 2) {
+        ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu",
+                   argv.size());
+        return StringValue("");
+    }
+
     std::vector<std::unique_ptr<Value>> args;
-    if (!ReadValueArgs(state, 2, argv, &args)) {
+    if (!ReadValueArgs(state, argv, &args)) {
         return nullptr;
     }
 
@@ -1844,9 +1870,16 @@
 // 1st block of each partition and check for mounting time/count. It return string "t"
 // if executes successfully and an empty string otherwise.
 
-Value* CheckFirstBlockFn(const char* name, State* state, int argc, Expr* argv[]) {
+Value* CheckFirstBlockFn(const char* name, State* state,
+                         const std::vector<std::unique_ptr<Expr>>& argv) {
+     if (argv.size() != 1) {
+        ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu",
+                   argv.size());
+        return StringValue("");
+    }
+
     std::vector<std::unique_ptr<Value>> args;
-    if (!ReadValueArgs(state, 1, argv, &args)) {
+    if (!ReadValueArgs(state, argv, &args)) {
         return nullptr;
     }
 
@@ -1892,9 +1925,16 @@
 }
 
 
-Value* BlockImageRecoverFn(const char* name, State* state, int argc, Expr* argv[]) {
+Value* BlockImageRecoverFn(const char* name, State* state,
+                           const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.size() != 2) {
+        ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu",
+                   argv.size());
+        return StringValue("");
+    }
+
     std::vector<std::unique_ptr<Value>> args;
-    if (!ReadValueArgs(state, 2, argv, &args)) {
+    if (!ReadValueArgs(state, argv, &args)) {
         return nullptr;
     }
 
diff --git a/updater/install.cpp b/updater/install.cpp
index 0963333..c9a0270 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -126,15 +126,16 @@
 
 // mount(fs_type, partition_type, location, mount_point)
 // mount(fs_type, partition_type, location, mount_point, mount_options)
-//
+
 //    fs_type="ext4"   partition_type="EMMC"    location=device
-Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 4 && argc != 5) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %d", name, argc);
+Value* MountFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 4 && argv.size() != 5) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 4-5 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& fs_type = args[0];
@@ -143,7 +144,7 @@
   const std::string& mount_point = args[3];
   std::string mount_options;
 
-  if (argc == 5) {
+  if (argv.size() == 5) {
     mount_options = args[4];
   }
 
@@ -188,15 +189,14 @@
   return StringValue(mount_point);
 }
 
-
 // is_mounted(mount_point)
-Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 1) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
+Value* IsMountedFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& mount_point = args[0];
@@ -214,12 +214,12 @@
   return StringValue(mount_point);
 }
 
-Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 1) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
+Value* UnmountFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
   }
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& mount_point = args[0];
@@ -265,13 +265,14 @@
 //    if fs_size == 0, then make fs uses the entire partition.
 //    if fs_size > 0, that is the size to use
 //    if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs")
-Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 5) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %d", name, argc);
+Value* FormatFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 5) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 5 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& fs_type = args[0];
@@ -332,13 +333,15 @@
   return nullptr;
 }
 
-Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+Value* ShowProgressFn(const char* name, State* state,
+                      const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& frac_str = args[0];
@@ -361,13 +364,13 @@
   return StringValue(frac_str);
 }
 
-Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 1) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
+Value* SetProgressFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 1, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& frac_str = args[0];
@@ -390,13 +393,15 @@
 //   Example: package_extract_dir("system", "/system")
 //
 //   Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path.
-Value* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+Value* PackageExtractDirFn(const char* name, State* state,
+                           const std::vector<std::unique_ptr<Expr>>&argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 2, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& zip_path = args[0];
@@ -416,17 +421,20 @@
 //   Extracts a single package_file from the update package and writes it to dest_file,
 //   overwriting existing files if necessary. Without the dest_file argument, returns the
 //   contents of the package file as a binary blob.
-Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc < 1 || argc > 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %d", name, argc);
+Value* PackageExtractFileFn(const char* name, State* state,
+                            const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() < 1 || argv.size() > 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 or 2 args, got %zu", name,
+                      argv.size());
   }
 
-  if (argc == 2) {
+  if (argv.size() == 2) {
     // The two-argument version extracts to a file.
 
     std::vector<std::string> args;
-    if (!ReadArgs(state, 2, argv, &args)) {
-      return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, argc);
+    if (!ReadArgs(state, argv, &args)) {
+      return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name,
+                        argv.size());
     }
     const std::string& zip_path = args[0];
     const std::string& dest_path = args[1];
@@ -468,8 +476,9 @@
     // The one-argument version returns the contents of the file as the result.
 
     std::vector<std::string> args;
-    if (!ReadArgs(state, 1, argv, &args)) {
-      return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %d args", name, argc);
+    if (!ReadArgs(state, argv, &args)) {
+      return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse %zu args", name,
+                        argv.size());
     }
     const std::string& zip_path = args[0];
 
@@ -495,9 +504,9 @@
   }
 }
 
-Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 1) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
+Value* GetPropFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
   }
   std::string key;
   if (!Evaluate(state, argv[0], &key)) {
@@ -513,13 +522,14 @@
 //   interprets 'file' as a getprop-style file (key=value pairs, one
 //   per line. # comment lines, blank lines, lines without '=' ignored),
 //   and returns the value for 'key' (or "" if it isn't defined).
-Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+Value* FileGetPropFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 2, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& filename = args[0];
@@ -578,9 +588,13 @@
 }
 
 // apply_patch_space(bytes)
-Value* ApplyPatchSpaceFn(const char* name, State* state, int argc, Expr* argv[]) {
+Value* ApplyPatchSpaceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 args, got %zu", name,
+                      argv.size());
+  }
   std::vector<std::string> args;
-  if (!ReadArgs(state, 1, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& bytes_str = args[0];
@@ -606,14 +620,14 @@
 //   state. If the process is interrupted during patching, the target file may be in an intermediate
 //   state; a copy exists in the cache partition so restarting the update can successfully update
 //   the file.
-Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc < 6 || (argc % 2) == 1) {
+Value* ApplyPatchFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+    if (argv.size() < 6 || (argv.size() % 2) == 1) {
         return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 6 args and an "
-                          "even number, got %d", name, argc);
+                          "even number, got %zu", name, argv.size());
     }
 
     std::vector<std::string> args;
-    if (!ReadArgs(state, 4, argv, &args)) {
+    if (!ReadArgs(state, argv, &args, 0, 4)) {
         return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
     }
     const std::string& source_filename = args[0];
@@ -627,9 +641,9 @@
                           name, target_size_str.c_str());
     }
 
-    int patchcount = (argc-4) / 2;
+    int patchcount = (argv.size()-4) / 2;
     std::vector<std::unique_ptr<Value>> arg_values;
-    if (!ReadValueArgs(state, argc-4, argv+4, &arg_values)) {
+    if (!ReadValueArgs(state, argv, &arg_values, 4, argv.size() - 4)) {
         return nullptr;
     }
 
@@ -664,20 +678,20 @@
 //   specified as 40 hex digits. This function differs from sha1_check(read_file(filename),
 //   sha1 [, ...]) in that it knows to check the cache partition copy, so apply_patch_check() will
 //   succeed even if the file was corrupted by an interrupted apply_patch() update.
-Value* ApplyPatchCheckFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc < 1) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %d", name,
-                      argc);
+Value* ApplyPatchCheckFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() < 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s(): expected at least 1 arg, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 1, argv, &args)) {
+  if (!ReadArgs(state, argv, &args, 0, 1)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& filename = args[0];
 
   std::vector<std::string> sha1s;
-  if (!ReadArgs(state, argc - 1, argv + 1, &sha1s)) {
+  if (!ReadArgs(state, argv, &sha1s, 1, argv.size() - 1)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   int result = applypatch_check(filename.c_str(), sha1s);
@@ -687,9 +701,9 @@
 
 // This is the updater side handler for ui_print() in edify script. Contents
 // will be sent over to the recovery side for on-screen display.
-Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
+Value* UIPrintFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
 
@@ -698,31 +712,32 @@
   return StringValue(buffer);
 }
 
-Value* WipeCacheFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 0) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc);
+Value* WipeCacheFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (!argv.empty()) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
+                      argv.size());
   }
   fprintf(static_cast<UpdaterInfo*>(state->cookie)->cmd_pipe, "wipe_cache\n");
   return StringValue("t");
 }
 
-Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc < 1) {
+Value* RunProgramFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() < 1) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name);
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
 
-  char* args2[argc + 1];
-  for (int i = 0; i < argc; i++) {
+  char* args2[argv.size() + 1];
+  for (size_t i = 0; i < argv.size(); i++) {
     args2[i] = &args[i][0];
   }
-  args2[argc] = nullptr;
+  args2[argv.size()] = nullptr;
 
-  LOG(INFO) << "about to run program [" << args2[0] << "] with " << argc << " args";
+  LOG(INFO) << "about to run program [" << args2[0] << "] with " << argv.size() << " args";
 
   pid_t child = fork();
   if (child == 0) {
@@ -752,13 +767,13 @@
 //    returns the sha1 of the file if it matches any of the hex
 //    strings passed, or "" if it does not equal any of them.
 //
-Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc < 1) {
+Value* Sha1CheckFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() < 1) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() expects at least 1 arg", name);
   }
 
   std::vector<std::unique_ptr<Value>> args;
-  if (!ReadValueArgs(state, argc, argv, &args)) {
+  if (!ReadValueArgs(state, argv, &args)) {
     return nullptr;
   }
 
@@ -768,11 +783,11 @@
   uint8_t digest[SHA_DIGEST_LENGTH];
   SHA1(reinterpret_cast<const uint8_t*>(args[0]->data.c_str()), args[0]->data.size(), digest);
 
-  if (argc == 1) {
+  if (argv.size() == 1) {
     return StringValue(print_sha1(digest));
   }
 
-  for (int i = 1; i < argc; ++i) {
+  for (size_t i = 1; i < argv.size(); ++i) {
     uint8_t arg_digest[SHA_DIGEST_LENGTH];
     if (args[i]->type != VAL_STRING) {
       LOG(ERROR) << name << "(): arg " << i << " is not a string; skipping";
@@ -791,13 +806,13 @@
 
 // Read a local file and return its contents (the Value* returned
 // is actually a FileContents*).
-Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 1) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
+Value* ReadFileFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 1, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& filename = args[0];
@@ -815,13 +830,14 @@
 // write_value(value, filename)
 //   Writes 'value' to 'filename'.
 //   Example: write_value("960000", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
-Value* WriteValueFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+Value* WriteValueFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 2, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
   }
 
@@ -848,13 +864,14 @@
 // property.  It can be "recovery" to boot from the recovery
 // partition, or "" (empty string) to boot from the regular boot
 // partition.
-Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+Value* RebootNowFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 2, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
   }
   const std::string& filename = args[0];
@@ -890,13 +907,14 @@
 // ("/misc" in the fstab), which is where this value is stored.  The
 // second argument is the string to store; it should not exceed 31
 // bytes.
-Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+Value* SetStageFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 2, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& filename = args[0];
@@ -923,13 +941,13 @@
 
 // Return the value most recently saved with SetStageFn.  The argument
 // is the block device for the misc partition.
-Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 1) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
+Value* GetStageFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 1, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& filename = args[0];
@@ -944,13 +962,14 @@
   return StringValue(boot.stage);
 }
 
-Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 2) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+Value* WipeBlockDeviceFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.size() != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name,
+                      argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, 2, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
   const std::string& filename = args[0];
@@ -967,38 +986,39 @@
   return StringValue((status == 0) ? "t" : "");
 }
 
-Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc != 0) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %d", name, argc);
+Value* EnableRebootFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (!argv.empty()) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
+                      argv.size());
   }
   UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
   fprintf(ui->cmd_pipe, "enable_reboot\n");
   return StringValue("t");
 }
 
-Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) {
-  if (argc == 0) {
-    return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %d", name, argc);
+Value* Tune2FsFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
+  if (argv.empty()) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects args, got %zu", name, argv.size());
   }
 
   std::vector<std::string> args;
-  if (!ReadArgs(state, argc, argv, &args)) {
+  if (!ReadArgs(state, argv, &args)) {
     return ErrorAbort(state, kArgsParsingFailure, "%s() could not read args", name);
   }
 
-  char* args2[argc + 1];
+  char* args2[argv.size() + 1];
   // Tune2fs expects the program name as its args[0]
   args2[0] = const_cast<char*>(name);
   if (args2[0] == nullptr) {
     return nullptr;
   }
-  for (int i = 0; i < argc; ++i) {
+  for (size_t i = 0; i < argv.size(); ++i) {
     args2[i + 1] = &args[i][0];
   }
 
   // tune2fs changes the file system parameters on an ext2 file system; it
   // returns 0 on success.
-  int result = tune2fs_main(argc + 1, args2);
+  int result = tune2fs_main(argv.size() + 1, args2);
   if (result != 0) {
     return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result);
   }
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 22c060f..0693cbd 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -130,7 +130,7 @@
 
   // Parse the script.
 
-  Expr* root;
+  std::unique_ptr<Expr> root;
   int error_count = 0;
   int error = parse_string(script.c_str(), &root, &error_count);
   if (error != 0 || error_count > 0) {
diff --git a/verifier.cpp b/verifier.cpp
index 7f16586..8be6da0 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -66,48 +66,50 @@
   CHECK(sig_der != nullptr);
   sig_der->clear();
 
-  asn1_context_t* ctx = asn1_context_new(pkcs7_der, pkcs7_der_len);
-  if (ctx == NULL) {
+  asn1_context ctx(pkcs7_der, pkcs7_der_len);
+
+  std::unique_ptr<asn1_context> pkcs7_seq(ctx.asn1_sequence_get());
+  if (pkcs7_seq == nullptr || !pkcs7_seq->asn1_sequence_next()) {
     return false;
   }
 
-  asn1_context_t* pkcs7_seq = asn1_sequence_get(ctx);
-  if (pkcs7_seq != NULL && asn1_sequence_next(pkcs7_seq)) {
-    asn1_context_t *signed_data_app = asn1_constructed_get(pkcs7_seq);
-    if (signed_data_app != NULL) {
-      asn1_context_t* signed_data_seq = asn1_sequence_get(signed_data_app);
-      if (signed_data_seq != NULL
-          && asn1_sequence_next(signed_data_seq)
-          && asn1_sequence_next(signed_data_seq)
-          && asn1_sequence_next(signed_data_seq)
-          && asn1_constructed_skip_all(signed_data_seq)) {
-        asn1_context_t *sig_set = asn1_set_get(signed_data_seq);
-        if (sig_set != NULL) {
-          asn1_context_t* sig_seq = asn1_sequence_get(sig_set);
-          if (sig_seq != NULL
-              && asn1_sequence_next(sig_seq)
-              && asn1_sequence_next(sig_seq)
-              && asn1_sequence_next(sig_seq)
-              && asn1_sequence_next(sig_seq)) {
-            const uint8_t* sig_der_ptr;
-            size_t sig_der_length;
-            if (asn1_octet_string_get(sig_seq, &sig_der_ptr, &sig_der_length)) {
-              sig_der->resize(sig_der_length);
-              std::copy(sig_der_ptr, sig_der_ptr + sig_der_length, sig_der->begin());
-            }
-            asn1_context_free(sig_seq);
-          }
-          asn1_context_free(sig_set);
-        }
-        asn1_context_free(signed_data_seq);
-      }
-      asn1_context_free(signed_data_app);
-    }
-    asn1_context_free(pkcs7_seq);
+  std::unique_ptr<asn1_context> signed_data_app(pkcs7_seq->asn1_constructed_get());
+  if (signed_data_app == nullptr) {
+    return false;
   }
-  asn1_context_free(ctx);
 
-  return !sig_der->empty();
+  std::unique_ptr<asn1_context> signed_data_seq(signed_data_app->asn1_sequence_get());
+  if (signed_data_seq == nullptr ||
+      !signed_data_seq->asn1_sequence_next() ||
+      !signed_data_seq->asn1_sequence_next() ||
+      !signed_data_seq->asn1_sequence_next() ||
+      !signed_data_seq->asn1_constructed_skip_all()) {
+    return false;
+  }
+
+  std::unique_ptr<asn1_context> sig_set(signed_data_seq->asn1_set_get());
+  if (sig_set == nullptr) {
+    return false;
+  }
+
+  std::unique_ptr<asn1_context> sig_seq(sig_set->asn1_sequence_get());
+  if (sig_seq == nullptr ||
+      !sig_seq->asn1_sequence_next() ||
+      !sig_seq->asn1_sequence_next() ||
+      !sig_seq->asn1_sequence_next() ||
+      !sig_seq->asn1_sequence_next()) {
+    return false;
+  }
+
+  const uint8_t* sig_der_ptr;
+  size_t sig_der_length;
+  if (!sig_seq->asn1_octet_string_get(&sig_der_ptr, &sig_der_length)) {
+    return false;
+  }
+
+  sig_der->resize(sig_der_length);
+  std::copy(sig_der_ptr, sig_der_ptr + sig_der_length, sig_der->begin());
+  return true;
 }
 
 /*