• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
~Operation()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.
ExecutionContext(const jtl_foundation::Hasher * hasher,const std::vector<Operation * > & sentence,const DictionaryValue * input,DictionaryValue * working_memory)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   }
~ExecutionContext()48   ~ExecutionContext() {}
49 
50   // Returns true in case of success.
ContinueExecution()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 
GetHash(const std::string & input)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.
GetValueHash(const Value & value,std::string * hash)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 
current_node() const87   const Value* current_node() const { return stack_.back(); }
stack()88   std::vector<const Value*>* stack() { return &stack_; }
working_memory()89   DictionaryValue* working_memory() { return working_memory_; }
error() const90   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:
NavigateOperation(const std::string & hashed_key)112   explicit NavigateOperation(const std::string& hashed_key)
113       : hashed_key_(hashed_key) {}
~NavigateOperation()114   virtual ~NavigateOperation() {}
Execute(ExecutionContext * context)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:
NavigateAnyOperation()143   NavigateAnyOperation() {}
~NavigateAnyOperation()144   virtual ~NavigateAnyOperation() {}
Execute(ExecutionContext * context)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:
NavigateBackOperation()176   NavigateBackOperation() {}
~NavigateBackOperation()177   virtual ~NavigateBackOperation() {}
Execute(ExecutionContext * context)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:
StoreValue(const std::string & hashed_name,scoped_ptr<Value> value)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   }
~StoreValue()198   virtual ~StoreValue() {}
Execute(ExecutionContext * context)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:
CompareStoredValue(const std::string & hashed_name,scoped_ptr<Value> value,scoped_ptr<Value> default_value)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   }
~CompareStoredValue()222   virtual ~CompareStoredValue() {}
Execute(ExecutionContext * context)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:
StoreNodeValue(const std::string & hashed_name)242   explicit StoreNodeValue(const std::string& hashed_name)
243       : hashed_name_(hashed_name) {
244     DCHECK(IsStringUTF8(hashed_name));
245   }
~StoreNodeValue()246   virtual ~StoreNodeValue() {}
Execute(ExecutionContext * context)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:
StoreNodeEffectiveSLD(const std::string & hashed_name)272   explicit StoreNodeEffectiveSLD(const std::string& hashed_name)
273       : hashed_name_(hashed_name) {
274     DCHECK(IsStringUTF8(hashed_name));
275   }
~StoreNodeEffectiveSLD()276   virtual ~StoreNodeEffectiveSLD() {}
Execute(ExecutionContext * context)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.
GetEffectiveSLD(const std::string & possibly_invalid_url,std::string * effective_sld)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:
CompareNodeBool(bool value)323   explicit CompareNodeBool(bool value) : value_(value) {}
~CompareNodeBool()324   virtual ~CompareNodeBool() {}
Execute(ExecutionContext * context)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:
CompareNodeHash(const std::string & hashed_value)341   explicit CompareNodeHash(const std::string& hashed_value)
342       : hashed_value_(hashed_value) {}
~CompareNodeHash()343   virtual ~CompareNodeHash() {}
Execute(ExecutionContext * context)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:
CompareNodeHashNot(const std::string & hashed_value)359   explicit CompareNodeHashNot(const std::string& hashed_value)
360       : hashed_value_(hashed_value) {}
~CompareNodeHashNot()361   virtual ~CompareNodeHashNot() {}
Execute(ExecutionContext * context)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:
CompareNodeToStored(const std::string & hashed_name)378   explicit CompareNodeToStored(const std::string& hashed_name)
379       : hashed_name_(hashed_name) {}
~CompareNodeToStored()380   virtual ~CompareNodeToStored() {}
Execute(ExecutionContext * context)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:
CompareNodeSubstring(const std::string & hashed_pattern,size_t pattern_length,uint32 pattern_sum)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   }
~CompareNodeSubstring()415   virtual ~CompareNodeSubstring() {}
Execute(ExecutionContext * context)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:
StopExecutingSentenceOperation()447   StopExecutingSentenceOperation() {}
~StopExecutingSentenceOperation()448   virtual ~StopExecutingSentenceOperation() {}
Execute(ExecutionContext * context)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:
Parser(const std::string & program)459   explicit Parser(const std::string& program)
460       : program_(program),
461         next_instruction_index_(0u) {}
~Parser()462   ~Parser() {}
ParseNextSentence(ScopedVector<Operation> * output)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 
HasNextSentence() const623   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.
ReadUint8(uint8 * out)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.
ReadUint32(uint32 * out)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.
ReadOpCode(uint8 * out)653   bool ReadOpCode(uint8* out) { return ReadUint8(out); }
654 
ReadHash(std::string * out)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 
ReadBool(bool * out)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 
JtlInterpreter(const std::string & hasher_seed,const std::string & program,const DictionaryValue * input)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 
~JtlInterpreter()700 JtlInterpreter::~JtlInterpreter() {}
701 
Execute()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 
GetOutputBoolean(const std::string & unhashed_key,bool * output) const721 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 
GetOutputString(const std::string & unhashed_key,std::string * output) const728 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