• 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/tools/profile_reset/jtl_compiler.h"
6 
7 #include <limits>
8 #include <map>
9 #include <numeric>
10 
11 #include "base/logging.h"
12 #include "chrome/browser/profile_resetter/jtl_foundation.h"
13 #include "chrome/tools/profile_reset/jtl_parser.h"
14 
15 namespace jtl = jtl_foundation;
16 
17 namespace {
18 
19 // Serializes symbols into byte-code in a streaming manner.
20 class ByteCodeWriter {
21  public:
ByteCodeWriter(std::string * output)22   explicit ByteCodeWriter(std::string* output) : output_(output) {}
~ByteCodeWriter()23   ~ByteCodeWriter() {}
24 
WriteUint8(uint8 value)25   void WriteUint8(uint8 value) { output_->push_back(static_cast<char>(value)); }
WriteUint32(uint32 value)26   void WriteUint32(uint32 value) {
27     for (int i = 0; i < 4; ++i) {
28       output_->push_back(static_cast<char>(value & 0xFFu));
29       value >>= 8;
30     }
31   }
WriteOpCode(uint8 op_code)32   void WriteOpCode(uint8 op_code) { WriteUint8(op_code); }
WriteHash(const std::string & hash)33   void WriteHash(const std::string& hash) {
34     CHECK(jtl::Hasher::IsHash(hash));
35     *output_ += hash;
36   }
WriteBool(bool value)37   void WriteBool(bool value) { WriteUint8(value ? 1u : 0u); }
38 
39  private:
40   std::string* output_;
41 
42   DISALLOW_COPY_AND_ASSIGN(ByteCodeWriter);
43 };
44 
45 // Encapsulates meta-data about all instructions, and is capable of transcoding
46 // each instruction from a parsed text-based format to byte-code.
47 class InstructionSet {
48  public:
InstructionSet()49   InstructionSet() {
50     // Define each instruction in this list.
51     // Note:
52     //  - Instructions ending in "hash" will write their 'HashString' arguments
53     //    directly into the byte-code.
54     //  - Instructions ending in "hashed" will first hash their 'String'
55     //    arguments, and will write this hash to the byte-code.
56     Add(Instruction("go", jtl::NAVIGATE, Arguments(String)));
57     Add(Instruction("any", jtl::NAVIGATE_ANY, Arguments()));
58     Add(Instruction("back", jtl::NAVIGATE_BACK, Arguments()));
59     Add(Instruction("store_bool", jtl::STORE_BOOL, Arguments(String, Bool)));
60     Add(Instruction("store_hash",
61                     jtl::STORE_HASH, Arguments(String, HashString)));
62     Add(Instruction("store_hashed",
63                     jtl::STORE_HASH, Arguments(String, String)));
64     Add(Instruction("store_node_bool",
65                     jtl::STORE_NODE_BOOL, Arguments(String)));
66     Add(Instruction("store_node_hash",
67                     jtl::STORE_NODE_HASH, Arguments(String)));
68     Add(Instruction("store_node_registerable_domain_hash",
69                     jtl::STORE_NODE_REGISTERABLE_DOMAIN_HASH,
70                     Arguments(String)));
71     Add(Instruction("compare_bool", jtl::COMPARE_NODE_BOOL, Arguments(Bool)));
72     Add(Instruction("compare_hashed",
73                     jtl::COMPARE_NODE_HASH, Arguments(String)));
74     Add(Instruction("compare_hashed_not",
75                     jtl::COMPARE_NODE_HASH_NOT, Arguments(String)));
76     Add(Instruction("compare_stored_bool",
77                     jtl::COMPARE_STORED_BOOL,
78                     Arguments(String, Bool, Bool)));
79     Add(Instruction("compare_stored_hashed",
80                     jtl::COMPARE_STORED_HASH,
81                     Arguments(String, String, String)));
82     Add(Instruction("compare_to_stored_bool",
83                     jtl::COMPARE_NODE_TO_STORED_BOOL,
84                     Arguments(String)));
85     Add(Instruction("compare_to_stored_hash",
86                     jtl::COMPARE_NODE_TO_STORED_HASH,
87                     Arguments(String)));
88     Add(Instruction("compare_substring_hashed",
89                     jtl::COMPARE_NODE_SUBSTRING,
90                     Arguments(StringPattern)));
91     Add(Instruction("break", jtl::STOP_EXECUTING_SENTENCE, Arguments()));
92   }
93 
TranscodeInstruction(const std::string & name,const base::ListValue & arguments,bool ends_sentence,const jtl::Hasher & hasher,ByteCodeWriter * target) const94   JtlCompiler::CompileError::ErrorCode TranscodeInstruction(
95       const std::string& name,
96       const base::ListValue& arguments,
97       bool ends_sentence,
98       const jtl::Hasher& hasher,
99       ByteCodeWriter* target) const {
100     if (instruction_map_.count(name) == 0)
101       return JtlCompiler::CompileError::INVALID_OPERATION_NAME;
102     const Instruction& instruction(instruction_map_.at(name));
103     if (instruction.argument_types.size() != arguments.GetSize())
104       return JtlCompiler::CompileError::INVALID_ARGUMENT_COUNT;
105     target->WriteOpCode(instruction.op_code);
106     for (size_t i = 0; i < arguments.GetSize(); ++i) {
107       switch (instruction.argument_types[i]) {
108         case Bool: {
109           bool value = false;
110           if (!arguments.GetBoolean(i, &value))
111             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
112           target->WriteBool(value);
113           break;
114         }
115         case String: {
116           std::string value;
117           if (!arguments.GetString(i, &value))
118             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
119           target->WriteHash(hasher.GetHash(value));
120           break;
121         }
122         case StringPattern: {
123           std::string value;
124           if (!arguments.GetString(i, &value))
125             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
126           if (value.empty() ||
127               value.size() > std::numeric_limits<uint32>::max())
128             return JtlCompiler::CompileError::INVALID_ARGUMENT_VALUE;
129           target->WriteHash(hasher.GetHash(value));
130           target->WriteUint32(static_cast<uint32>(value.size()));
131           uint32 pattern_sum = std::accumulate(
132               value.begin(), value.end(), static_cast<uint32>(0u));
133           target->WriteUint32(pattern_sum);
134           break;
135         }
136         case HashString: {
137           std::string hash_value;
138           if (!arguments.GetString(i, &hash_value) ||
139               !jtl::Hasher::IsHash(hash_value))
140             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
141           target->WriteHash(hash_value);
142           break;
143         }
144         default:
145           NOTREACHED();
146           return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
147       }
148     }
149     if (ends_sentence)
150       target->WriteOpCode(jtl::END_OF_SENTENCE);
151     return JtlCompiler::CompileError::ERROR_NONE;
152   }
153 
154  private:
155   // The possible types of an operation's argument.
156   enum ArgumentType {
157     None,
158     Bool,
159     String,
160     StringPattern,
161     HashString
162   };
163 
164   // Encapsulates meta-data about one instruction.
165   struct Instruction {
Instruction__anon5b7428890111::InstructionSet::Instruction166     Instruction() : op_code(jtl::END_OF_SENTENCE) {}
Instruction__anon5b7428890111::InstructionSet::Instruction167     Instruction(const char* name,
168                 jtl_foundation::OpCodes op_code,
169                 const std::vector<ArgumentType>& argument_types)
170         : name(name), op_code(op_code), argument_types(argument_types) {}
171 
172     std::string name;
173     jtl::OpCodes op_code;
174     std::vector<ArgumentType> argument_types;
175   };
176 
Arguments(ArgumentType arg1_type=None,ArgumentType arg2_type=None,ArgumentType arg3_type=None)177   static std::vector<ArgumentType> Arguments(ArgumentType arg1_type = None,
178                                              ArgumentType arg2_type = None,
179                                              ArgumentType arg3_type = None) {
180     std::vector<ArgumentType> result;
181     if (arg1_type != None)
182       result.push_back(arg1_type);
183     if (arg2_type != None)
184       result.push_back(arg2_type);
185     if (arg3_type != None)
186       result.push_back(arg3_type);
187     return result;
188   }
189 
Add(const Instruction & instruction)190   void Add(const Instruction& instruction) {
191     instruction_map_[instruction.name] = instruction;
192   }
193 
194   std::map<std::string, Instruction> instruction_map_;
195 
196   DISALLOW_COPY_AND_ASSIGN(InstructionSet);
197 };
198 
199 }  // namespace
200 
Compile(const std::string & source_code,const std::string & hash_seed,std::string * output_bytecode,CompileError * error_details)201 bool JtlCompiler::Compile(const std::string& source_code,
202                           const std::string& hash_seed,
203                           std::string* output_bytecode,
204                           CompileError* error_details) {
205   DCHECK(output_bytecode);
206   InstructionSet instruction_set;
207   ByteCodeWriter bytecode_writer(output_bytecode);
208   jtl::Hasher hasher(hash_seed);
209 
210   std::string compacted_source_code;
211   std::vector<size_t> newline_indices;
212   size_t mismatched_quotes_line;
213   if (!JtlParser::RemoveCommentsAndAllWhitespace(source_code,
214                                                  &compacted_source_code,
215                                                  &newline_indices,
216                                                  &mismatched_quotes_line)) {
217     if (error_details) {
218       error_details->context = "";  // No meaningful intra-line context here.
219       error_details->line_number = mismatched_quotes_line;
220       error_details->error_code = CompileError::MISMATCHED_DOUBLE_QUOTES;
221     }
222     return false;
223   }
224 
225   JtlParser parser(compacted_source_code, newline_indices);
226   while (!parser.HasFinished()) {
227     std::string operation_name;
228     base::ListValue arguments;
229     bool ends_sentence = false;
230     if (!parser.ParseNextOperation(
231              &operation_name, &arguments, &ends_sentence)) {
232       if (error_details) {
233         error_details->context = parser.GetLastContext();
234         error_details->line_number = parser.GetLastLineNumber();
235         error_details->error_code = CompileError::PARSING_ERROR;
236       }
237       return false;
238     }
239     CompileError::ErrorCode error_code = instruction_set.TranscodeInstruction(
240         operation_name, arguments, ends_sentence, hasher, &bytecode_writer);
241     if (error_code != CompileError::ERROR_NONE) {
242       if (error_details) {
243         error_details->context = parser.GetLastContext();
244         error_details->line_number = parser.GetLastLineNumber();
245         error_details->error_code = error_code;
246       }
247       return false;
248     }
249   }
250 
251   return true;
252 }
253