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