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