Home | History | Annotate | Download | only in profile_resetter
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/profile_resetter/jtl_interpreter.h"
      6 
      7 #include <numeric>
      8 
      9 #include "base/memory/scoped_vector.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/string_util.h"
     12 #include "chrome/browser/profile_resetter/jtl_foundation.h"
     13 #include "crypto/hmac.h"
     14 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
     15 #include "url/gurl.h"
     16 
     17 namespace {
     18 
     19 class ExecutionContext;
     20 
     21 // An operation in an interpreted program.
     22 class Operation {
     23  public:
     24   virtual ~Operation() {}
     25   // Executes the operation on the specified context and instructs the context
     26   // to continue execution with the next instruction if appropriate.
     27   // Returns true if we should continue with any potential backtracking that
     28   // needs to be done.
     29   virtual bool Execute(ExecutionContext* context) = 0;
     30 };
     31 
     32 // An execution context of operations.
     33 class ExecutionContext {
     34  public:
     35   // |input| is the root of a dictionary that stores the information the
     36   // sentence is evaluated on.
     37   ExecutionContext(const jtl_foundation::Hasher* hasher,
     38                    const std::vector<Operation*>& sentence,
     39                    const DictionaryValue* input,
     40                    DictionaryValue* working_memory)
     41       : hasher_(hasher),
     42         sentence_(sentence),
     43         next_instruction_index_(0u),
     44         working_memory_(working_memory),
     45         error_(false) {
     46     stack_.push_back(input);
     47   }
     48   ~ExecutionContext() {}
     49 
     50   // Returns true in case of success.
     51   bool ContinueExecution() {
     52     if (error_ || stack_.empty()) {
     53       error_ = true;
     54       return false;
     55     }
     56     if (next_instruction_index_ >= sentence_.size())
     57       return true;
     58 
     59     Operation* op = sentence_[next_instruction_index_];
     60     next_instruction_index_++;
     61     bool continue_traversal = op->Execute(this);
     62     next_instruction_index_--;
     63     return continue_traversal;
     64   }
     65 
     66   std::string GetHash(const std::string& input) {
     67     return hasher_->GetHash(input);
     68   }
     69 
     70   // Calculates the |hash| of a string, integer or double |value|, and returns
     71   // true. Returns false otherwise.
     72   bool GetValueHash(const Value& value, std::string* hash) {
     73     DCHECK(hash);
     74     std::string value_as_string;
     75     int tmp_int = 0;
     76     double tmp_double = 0.0;
     77     if (value.GetAsInteger(&tmp_int))
     78       value_as_string = base::IntToString(tmp_int);
     79     else if (value.GetAsDouble(&tmp_double))
     80       value_as_string = base::DoubleToString(tmp_double);
     81     else if (!value.GetAsString(&value_as_string))
     82       return false;
     83     *hash = GetHash(value_as_string);
     84     return true;
     85   }
     86 
     87   const Value* current_node() const { return stack_.back(); }
     88   std::vector<const Value*>* stack() { return &stack_; }
     89   DictionaryValue* working_memory() { return working_memory_; }
     90   bool error() const { return error_; }
     91 
     92  private:
     93   // A hasher used to hash node names in a dictionary.
     94   const jtl_foundation::Hasher* hasher_;
     95   // The sentence to be executed.
     96   const std::vector<Operation*> sentence_;
     97   // Position in |sentence_|.
     98   size_t next_instruction_index_;
     99   // A stack of Values, indicating a navigation path from the root node of
    100   // |input| (see constructor) to the current node on which the
    101   // sentence_[next_instruction_index_] is evaluated.
    102   std::vector<const Value*> stack_;
    103   // Memory into which values can be stored by the program.
    104   DictionaryValue* working_memory_;
    105   // Whether a runtime error occurred.
    106   bool error_;
    107   DISALLOW_COPY_AND_ASSIGN(ExecutionContext);
    108 };
    109 
    110 class NavigateOperation : public Operation {
    111  public:
    112   explicit NavigateOperation(const std::string& hashed_key)
    113       : hashed_key_(hashed_key) {}
    114   virtual ~NavigateOperation() {}
    115   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    116     const DictionaryValue* dict = NULL;
    117     if (!context->current_node()->GetAsDictionary(&dict)) {
    118       // Just ignore this node gracefully as this navigation is a dead end.
    119       // If this NavigateOperation occurred after a NavigateAny operation, those
    120       // may still be fulfillable, so we allow continuing the execution of the
    121       // sentence on other nodes.
    122       return true;
    123     }
    124     for (DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) {
    125       if (context->GetHash(i.key()) != hashed_key_)
    126         continue;
    127       context->stack()->push_back(&i.value());
    128       bool continue_traversal = context->ContinueExecution();
    129       context->stack()->pop_back();
    130       if (!continue_traversal)
    131         return false;
    132     }
    133     return true;
    134   }
    135 
    136  private:
    137   std::string hashed_key_;
    138   DISALLOW_COPY_AND_ASSIGN(NavigateOperation);
    139 };
    140 
    141 class NavigateAnyOperation : public Operation {
    142  public:
    143   NavigateAnyOperation() {}
    144   virtual ~NavigateAnyOperation() {}
    145   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    146     const DictionaryValue* dict = NULL;
    147     const ListValue* list = NULL;
    148     if (context->current_node()->GetAsDictionary(&dict)) {
    149       for (DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) {
    150         context->stack()->push_back(&i.value());
    151         bool continue_traversal = context->ContinueExecution();
    152         context->stack()->pop_back();
    153         if (!continue_traversal)
    154           return false;
    155       }
    156     } else if (context->current_node()->GetAsList(&list)) {
    157       for (ListValue::const_iterator i = list->begin(); i != list->end(); ++i) {
    158         context->stack()->push_back(*i);
    159         bool continue_traversal = context->ContinueExecution();
    160         context->stack()->pop_back();
    161         if (!continue_traversal)
    162           return false;
    163       }
    164     } else {
    165       // Do nothing, just ignore this node.
    166     }
    167     return true;
    168   }
    169 
    170  private:
    171   DISALLOW_COPY_AND_ASSIGN(NavigateAnyOperation);
    172 };
    173 
    174 class NavigateBackOperation : public Operation {
    175  public:
    176   NavigateBackOperation() {}
    177   virtual ~NavigateBackOperation() {}
    178   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    179     const Value* current_node = context->current_node();
    180     context->stack()->pop_back();
    181     bool continue_traversal = context->ContinueExecution();
    182     context->stack()->push_back(current_node);
    183     return continue_traversal;
    184   }
    185 
    186  private:
    187   DISALLOW_COPY_AND_ASSIGN(NavigateBackOperation);
    188 };
    189 
    190 class StoreValue : public Operation {
    191  public:
    192   StoreValue(const std::string& hashed_name, scoped_ptr<Value> value)
    193       : hashed_name_(hashed_name),
    194         value_(value.Pass()) {
    195     DCHECK(IsStringUTF8(hashed_name));
    196     DCHECK(value_);
    197   }
    198   virtual ~StoreValue() {}
    199   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    200     context->working_memory()->Set(hashed_name_, value_->DeepCopy());
    201     return context->ContinueExecution();
    202   }
    203 
    204  private:
    205   std::string hashed_name_;
    206   scoped_ptr<Value> value_;
    207   DISALLOW_COPY_AND_ASSIGN(StoreValue);
    208 };
    209 
    210 class CompareStoredValue : public Operation {
    211  public:
    212   CompareStoredValue(const std::string& hashed_name,
    213                      scoped_ptr<Value> value,
    214                      scoped_ptr<Value> default_value)
    215       : hashed_name_(hashed_name),
    216         value_(value.Pass()),
    217         default_value_(default_value.Pass()) {
    218     DCHECK(IsStringUTF8(hashed_name));
    219     DCHECK(value_);
    220     DCHECK(default_value_);
    221   }
    222   virtual ~CompareStoredValue() {}
    223   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    224     const Value* actual_value = NULL;
    225     if (!context->working_memory()->Get(hashed_name_, &actual_value))
    226       actual_value = default_value_.get();
    227     if (!value_->Equals(actual_value))
    228       return true;
    229     return context->ContinueExecution();
    230   }
    231 
    232  private:
    233   std::string hashed_name_;
    234   scoped_ptr<Value> value_;
    235   scoped_ptr<Value> default_value_;
    236   DISALLOW_COPY_AND_ASSIGN(CompareStoredValue);
    237 };
    238 
    239 template<bool ExpectedTypeIsBooleanNotHashable>
    240 class StoreNodeValue : public Operation {
    241  public:
    242   explicit StoreNodeValue(const std::string& hashed_name)
    243       : hashed_name_(hashed_name) {
    244     DCHECK(IsStringUTF8(hashed_name));
    245   }
    246   virtual ~StoreNodeValue() {}
    247   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    248     scoped_ptr<base::Value> value;
    249     if (ExpectedTypeIsBooleanNotHashable) {
    250       if (!context->current_node()->IsType(base::Value::TYPE_BOOLEAN))
    251         return true;
    252       value.reset(context->current_node()->DeepCopy());
    253     } else {
    254       std::string hash;
    255       if (!context->GetValueHash(*context->current_node(), &hash))
    256         return true;
    257       value.reset(new base::StringValue(hash));
    258     }
    259     context->working_memory()->Set(hashed_name_, value.release());
    260     return context->ContinueExecution();
    261   }
    262 
    263  private:
    264   std::string hashed_name_;
    265   DISALLOW_COPY_AND_ASSIGN(StoreNodeValue);
    266 };
    267 
    268 // Stores the effective SLD (second-level domain) of the URL represented by the
    269 // current node into working memory.
    270 class StoreNodeEffectiveSLD : public Operation {
    271  public:
    272   explicit StoreNodeEffectiveSLD(const std::string& hashed_name)
    273       : hashed_name_(hashed_name) {
    274     DCHECK(IsStringUTF8(hashed_name));
    275   }
    276   virtual ~StoreNodeEffectiveSLD() {}
    277   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    278     std::string possibly_invalid_url;
    279     std::string effective_sld;
    280     if (!context->current_node()->GetAsString(&possibly_invalid_url) ||
    281         !GetEffectiveSLD(possibly_invalid_url, &effective_sld))
    282       return true;
    283     context->working_memory()->Set(
    284         hashed_name_, new StringValue(context->GetHash(effective_sld)));
    285     return context->ContinueExecution();
    286   }
    287 
    288  private:
    289   // If |possibly_invalid_url| is a valid URL that has an effective second-level
    290   // domain part, outputs that in |effective_sld| and returns true.
    291   // Returns false otherwise.
    292   static bool GetEffectiveSLD(const std::string& possibly_invalid_url,
    293                               std::string* effective_sld) {
    294     namespace domains = net::registry_controlled_domains;
    295     DCHECK(effective_sld);
    296     GURL url(possibly_invalid_url);
    297     if (!url.is_valid())
    298       return false;
    299     std::string sld_and_registry = domains::GetDomainAndRegistry(
    300         url.host(), domains::EXCLUDE_PRIVATE_REGISTRIES);
    301     size_t registry_length = domains::GetRegistryLength(
    302         url.host(),
    303         domains::EXCLUDE_UNKNOWN_REGISTRIES,
    304         domains::EXCLUDE_PRIVATE_REGISTRIES);
    305     // Fail unless (1.) the URL has a host part; and (2.) that host part is a
    306     // well-formed domain name that ends in, but is not in itself, as a whole,
    307     // a recognized registry identifier that is acknowledged by ICANN.
    308     if (registry_length == std::string::npos || registry_length == 0)
    309       return false;
    310     DCHECK_LT(registry_length, sld_and_registry.size());
    311     // Subtract one to cut off the dot separating the SLD and the registry.
    312     effective_sld->assign(
    313         sld_and_registry, 0, sld_and_registry.size() - registry_length - 1);
    314     return true;
    315   }
    316 
    317   std::string hashed_name_;
    318   DISALLOW_COPY_AND_ASSIGN(StoreNodeEffectiveSLD);
    319 };
    320 
    321 class CompareNodeBool : public Operation {
    322  public:
    323   explicit CompareNodeBool(bool value) : value_(value) {}
    324   virtual ~CompareNodeBool() {}
    325   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    326     bool actual_value = false;
    327     if (!context->current_node()->GetAsBoolean(&actual_value))
    328       return true;
    329     if (actual_value != value_)
    330       return true;
    331     return context->ContinueExecution();
    332   }
    333 
    334  private:
    335   bool value_;
    336   DISALLOW_COPY_AND_ASSIGN(CompareNodeBool);
    337 };
    338 
    339 class CompareNodeHash : public Operation {
    340  public:
    341   explicit CompareNodeHash(const std::string& hashed_value)
    342       : hashed_value_(hashed_value) {}
    343   virtual ~CompareNodeHash() {}
    344   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    345     std::string actual_hash;
    346     if (!context->GetValueHash(*context->current_node(), &actual_hash) ||
    347         actual_hash != hashed_value_)
    348       return true;
    349     return context->ContinueExecution();
    350   }
    351 
    352  private:
    353   std::string hashed_value_;
    354   DISALLOW_COPY_AND_ASSIGN(CompareNodeHash);
    355 };
    356 
    357 class CompareNodeHashNot : public Operation {
    358  public:
    359   explicit CompareNodeHashNot(const std::string& hashed_value)
    360       : hashed_value_(hashed_value) {}
    361   virtual ~CompareNodeHashNot() {}
    362   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    363     std::string actual_hash;
    364     if (context->GetValueHash(*context->current_node(), &actual_hash) &&
    365         actual_hash == hashed_value_)
    366       return true;
    367     return context->ContinueExecution();
    368   }
    369 
    370  private:
    371   std::string hashed_value_;
    372   DISALLOW_COPY_AND_ASSIGN(CompareNodeHashNot);
    373 };
    374 
    375 template<bool ExpectedTypeIsBooleanNotHashable>
    376 class CompareNodeToStored : public Operation {
    377  public:
    378   explicit CompareNodeToStored(const std::string& hashed_name)
    379       : hashed_name_(hashed_name) {}
    380   virtual ~CompareNodeToStored() {}
    381   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    382     const Value* stored_value = NULL;
    383     if (!context->working_memory()->Get(hashed_name_, &stored_value))
    384       return true;
    385     if (ExpectedTypeIsBooleanNotHashable) {
    386       if (!context->current_node()->IsType(base::Value::TYPE_BOOLEAN) ||
    387           !context->current_node()->Equals(stored_value))
    388         return true;
    389     } else {
    390       std::string actual_hash;
    391       std::string stored_hash;
    392       if (!context->GetValueHash(*context->current_node(), &actual_hash) ||
    393           !stored_value->GetAsString(&stored_hash) ||
    394           actual_hash != stored_hash)
    395         return true;
    396     }
    397     return context->ContinueExecution();
    398   }
    399 
    400  private:
    401   std::string hashed_name_;
    402   DISALLOW_COPY_AND_ASSIGN(CompareNodeToStored);
    403 };
    404 
    405 class CompareNodeSubstring : public Operation {
    406  public:
    407   explicit CompareNodeSubstring(const std::string& hashed_pattern,
    408                                 size_t pattern_length,
    409                                 uint32 pattern_sum)
    410       : hashed_pattern_(hashed_pattern),
    411         pattern_length_(pattern_length),
    412         pattern_sum_(pattern_sum) {
    413     DCHECK(pattern_length_);
    414   }
    415   virtual ~CompareNodeSubstring() {}
    416   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    417     std::string value_as_string;
    418     if (!context->current_node()->GetAsString(&value_as_string) ||
    419         !pattern_length_ || value_as_string.size() < pattern_length_)
    420       return true;
    421     // Go over the string with a sliding window. Meanwhile, maintain the sum in
    422     // an incremental fashion, and only calculate the SHA-256 hash when the sum
    423     // checks out so as to improve performance.
    424     std::string::const_iterator window_begin = value_as_string.begin();
    425     std::string::const_iterator window_end = window_begin + pattern_length_ - 1;
    426     uint32 window_sum =
    427         std::accumulate(window_begin, window_end, static_cast<uint32>(0u));
    428     while (window_end != value_as_string.end()) {
    429       window_sum += *window_end++;
    430       if (window_sum == pattern_sum_ && context->GetHash(std::string(
    431           window_begin, window_end)) == hashed_pattern_)
    432         return context->ContinueExecution();
    433       window_sum -= *window_begin++;
    434     }
    435     return true;
    436   }
    437 
    438  private:
    439   std::string hashed_pattern_;
    440   size_t pattern_length_;
    441   uint32 pattern_sum_;
    442   DISALLOW_COPY_AND_ASSIGN(CompareNodeSubstring);
    443 };
    444 
    445 class StopExecutingSentenceOperation : public Operation {
    446  public:
    447   StopExecutingSentenceOperation() {}
    448   virtual ~StopExecutingSentenceOperation() {}
    449   virtual bool Execute(ExecutionContext* context) OVERRIDE {
    450     return false;
    451   }
    452 
    453  private:
    454   DISALLOW_COPY_AND_ASSIGN(StopExecutingSentenceOperation);
    455 };
    456 
    457 class Parser {
    458  public:
    459   explicit Parser(const std::string& program)
    460       : program_(program),
    461         next_instruction_index_(0u) {}
    462   ~Parser() {}
    463   bool ParseNextSentence(ScopedVector<Operation>* output) {
    464     ScopedVector<Operation> operators;
    465     bool sentence_ended = false;
    466     while (next_instruction_index_ < program_.size() && !sentence_ended) {
    467       uint8 op_code = 0;
    468       if (!ReadOpCode(&op_code))
    469         return false;
    470       switch (static_cast<jtl_foundation::OpCodes>(op_code)) {
    471         case jtl_foundation::NAVIGATE: {
    472           std::string hashed_key;
    473           if (!ReadHash(&hashed_key))
    474             return false;
    475           operators.push_back(new NavigateOperation(hashed_key));
    476           break;
    477         }
    478         case jtl_foundation::NAVIGATE_ANY:
    479           operators.push_back(new NavigateAnyOperation);
    480           break;
    481         case jtl_foundation::NAVIGATE_BACK:
    482           operators.push_back(new NavigateBackOperation);
    483           break;
    484         case jtl_foundation::STORE_BOOL: {
    485           std::string hashed_name;
    486           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    487             return false;
    488           bool value = false;
    489           if (!ReadBool(&value))
    490             return false;
    491           operators.push_back(new StoreValue(
    492               hashed_name,
    493               scoped_ptr<Value>(new base::FundamentalValue(value))));
    494           break;
    495         }
    496         case jtl_foundation::COMPARE_STORED_BOOL: {
    497           std::string hashed_name;
    498           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    499             return false;
    500           bool value = false;
    501           if (!ReadBool(&value))
    502             return false;
    503           bool default_value = false;
    504           if (!ReadBool(&default_value))
    505             return false;
    506           operators.push_back(new CompareStoredValue(
    507               hashed_name,
    508               scoped_ptr<Value>(new base::FundamentalValue(value)),
    509               scoped_ptr<Value>(new base::FundamentalValue(default_value))));
    510           break;
    511         }
    512         case jtl_foundation::STORE_HASH: {
    513           std::string hashed_name;
    514           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    515             return false;
    516           std::string hashed_value;
    517           if (!ReadHash(&hashed_value))
    518             return false;
    519           operators.push_back(new StoreValue(
    520               hashed_name,
    521               scoped_ptr<Value>(new base::StringValue(hashed_value))));
    522           break;
    523         }
    524         case jtl_foundation::COMPARE_STORED_HASH: {
    525           std::string hashed_name;
    526           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    527             return false;
    528           std::string hashed_value;
    529           if (!ReadHash(&hashed_value))
    530             return false;
    531           std::string hashed_default_value;
    532           if (!ReadHash(&hashed_default_value))
    533             return false;
    534           operators.push_back(new CompareStoredValue(
    535               hashed_name,
    536               scoped_ptr<Value>(new base::StringValue(hashed_value)),
    537               scoped_ptr<Value>(new base::StringValue(hashed_default_value))));
    538           break;
    539         }
    540         case jtl_foundation::STORE_NODE_BOOL: {
    541           std::string hashed_name;
    542           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    543             return false;
    544           operators.push_back(new StoreNodeValue<true>(hashed_name));
    545           break;
    546         }
    547         case jtl_foundation::STORE_NODE_HASH: {
    548           std::string hashed_name;
    549           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    550             return false;
    551           operators.push_back(new StoreNodeValue<false>(hashed_name));
    552           break;
    553         }
    554         case jtl_foundation::STORE_NODE_EFFECTIVE_SLD_HASH: {
    555           std::string hashed_name;
    556           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    557             return false;
    558           operators.push_back(new StoreNodeEffectiveSLD(hashed_name));
    559           break;
    560         }
    561         case jtl_foundation::COMPARE_NODE_BOOL: {
    562           bool value = false;
    563           if (!ReadBool(&value))
    564             return false;
    565           operators.push_back(new CompareNodeBool(value));
    566           break;
    567         }
    568         case jtl_foundation::COMPARE_NODE_HASH: {
    569           std::string hashed_value;
    570           if (!ReadHash(&hashed_value))
    571             return false;
    572           operators.push_back(new CompareNodeHash(hashed_value));
    573           break;
    574         }
    575         case jtl_foundation::COMPARE_NODE_HASH_NOT: {
    576           std::string hashed_value;
    577           if (!ReadHash(&hashed_value))
    578             return false;
    579           operators.push_back(new CompareNodeHashNot(hashed_value));
    580           break;
    581         }
    582         case jtl_foundation::COMPARE_NODE_TO_STORED_BOOL: {
    583           std::string hashed_name;
    584           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    585             return false;
    586           operators.push_back(new CompareNodeToStored<true>(hashed_name));
    587           break;
    588         }
    589         case jtl_foundation::COMPARE_NODE_TO_STORED_HASH: {
    590           std::string hashed_name;
    591           if (!ReadHash(&hashed_name) || !IsStringUTF8(hashed_name))
    592             return false;
    593           operators.push_back(new CompareNodeToStored<false>(hashed_name));
    594           break;
    595         }
    596         case jtl_foundation::COMPARE_NODE_SUBSTRING: {
    597           std::string hashed_pattern;
    598           uint32 pattern_length = 0, pattern_sum = 0;
    599           if (!ReadHash(&hashed_pattern))
    600             return false;
    601           if (!ReadUint32(&pattern_length) || pattern_length == 0)
    602             return false;
    603           if (!ReadUint32(&pattern_sum))
    604             return false;
    605           operators.push_back(new CompareNodeSubstring(
    606               hashed_pattern, pattern_length, pattern_sum));
    607           break;
    608         }
    609         case jtl_foundation::STOP_EXECUTING_SENTENCE:
    610           operators.push_back(new StopExecutingSentenceOperation);
    611           break;
    612         case jtl_foundation::END_OF_SENTENCE:
    613           sentence_ended = true;
    614           break;
    615         default:
    616           return false;
    617       }
    618     }
    619     output->swap(operators);
    620     return true;
    621   }
    622 
    623   bool HasNextSentence() const {
    624     return next_instruction_index_ < program_.size();
    625   }
    626 
    627  private:
    628   // Reads an uint8 and returns whether this operation was successful.
    629   bool ReadUint8(uint8* out) {
    630     DCHECK(out);
    631     if (next_instruction_index_ + 1u > program_.size())
    632       return false;
    633     *out = static_cast<uint8>(program_[next_instruction_index_]);
    634     ++next_instruction_index_;
    635     return true;
    636   }
    637 
    638   // Reads an uint32 and returns whether this operation was successful.
    639   bool ReadUint32(uint32* out) {
    640     DCHECK(out);
    641     if (next_instruction_index_ + 4u > program_.size())
    642       return false;
    643     *out = 0u;
    644     for (int i = 0; i < 4; ++i) {
    645       *out >>= 8;
    646       *out |= static_cast<uint8>(program_[next_instruction_index_]) << 24;
    647       ++next_instruction_index_;
    648     }
    649     return true;
    650   }
    651 
    652   // Reads an operator code and returns whether this operation was successful.
    653   bool ReadOpCode(uint8* out) { return ReadUint8(out); }
    654 
    655   bool ReadHash(std::string* out) {
    656     DCHECK(out);
    657     if (next_instruction_index_ + jtl_foundation::kHashSizeInBytes >
    658         program_.size())
    659       return false;
    660     *out = program_.substr(next_instruction_index_,
    661                            jtl_foundation::kHashSizeInBytes);
    662     next_instruction_index_ += jtl_foundation::kHashSizeInBytes;
    663     DCHECK(jtl_foundation::Hasher::IsHash(*out));
    664     return true;
    665   }
    666 
    667   bool ReadBool(bool* out) {
    668     DCHECK(out);
    669     uint8 value = 0;
    670     if (!ReadUint8(&value))
    671       return false;
    672     if (value == 0)
    673       *out = false;
    674     else if (value == 1)
    675       *out = true;
    676     else
    677       return false;
    678     return true;
    679   }
    680 
    681   std::string program_;
    682   size_t next_instruction_index_;
    683   DISALLOW_COPY_AND_ASSIGN(Parser);
    684 };
    685 
    686 }  // namespace
    687 
    688 JtlInterpreter::JtlInterpreter(
    689     const std::string& hasher_seed,
    690     const std::string& program,
    691     const DictionaryValue* input)
    692     : hasher_seed_(hasher_seed),
    693       program_(program),
    694       input_(input),
    695       working_memory_(new DictionaryValue),
    696       result_(OK) {
    697   DCHECK(input->IsType(Value::TYPE_DICTIONARY));
    698 }
    699 
    700 JtlInterpreter::~JtlInterpreter() {}
    701 
    702 void JtlInterpreter::Execute() {
    703   jtl_foundation::Hasher hasher(hasher_seed_);
    704   Parser parser(program_);
    705   while (parser.HasNextSentence()) {
    706     ScopedVector<Operation> sentence;
    707     if (!parser.ParseNextSentence(&sentence)) {
    708       result_ = PARSE_ERROR;
    709       return;
    710     }
    711     ExecutionContext context(
    712         &hasher, sentence.get(), input_, working_memory_.get());
    713     context.ContinueExecution();
    714     if (context.error()) {
    715       result_ = RUNTIME_ERROR;
    716       return;
    717     }
    718   }
    719 }
    720 
    721 bool JtlInterpreter::GetOutputBoolean(const std::string& unhashed_key,
    722                                       bool* output) const {
    723   std::string hashed_key =
    724       jtl_foundation::Hasher(hasher_seed_).GetHash(unhashed_key);
    725   return working_memory_->GetBoolean(hashed_key, output);
    726 }
    727 
    728 bool JtlInterpreter::GetOutputString(const std::string& unhashed_key,
    729                                      std::string* output) const {
    730   std::string hashed_key =
    731       jtl_foundation::Hasher(hasher_seed_).GetHash(unhashed_key);
    732   return working_memory_->GetString(hashed_key, output);
    733 }
    734