Home | History | Annotate | Download | only in profile_reset
      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:
     22   explicit ByteCodeWriter(std::string* output) : output_(output) {}
     23   ~ByteCodeWriter() {}
     24 
     25   void WriteUint8(uint8 value) { output_->push_back(static_cast<char>(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   }
     32   void WriteOpCode(uint8 op_code) { WriteUint8(op_code); }
     33   void WriteHash(const std::string& hash) {
     34     CHECK(jtl::Hasher::IsHash(hash));
     35     *output_ += hash;
     36   }
     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:
     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_effective_sld_hash",
     69                     jtl::STORE_NODE_EFFECTIVE_SLD_HASH, Arguments(String)));
     70     Add(Instruction("compare_bool", jtl::COMPARE_NODE_BOOL, Arguments(Bool)));
     71     Add(Instruction("compare_hashed",
     72                     jtl::COMPARE_NODE_HASH, Arguments(String)));
     73     Add(Instruction("compare_hashed_not",
     74                     jtl::COMPARE_NODE_HASH_NOT, Arguments(String)));
     75     Add(Instruction("compare_stored_bool",
     76                     jtl::COMPARE_STORED_BOOL,
     77                     Arguments(String, Bool, Bool)));
     78     Add(Instruction("compare_stored_hashed",
     79                     jtl::COMPARE_STORED_HASH,
     80                     Arguments(String, String, String)));
     81     Add(Instruction("compare_to_stored_bool",
     82                     jtl::COMPARE_NODE_TO_STORED_BOOL,
     83                     Arguments(String)));
     84     Add(Instruction("compare_to_stored_hash",
     85                     jtl::COMPARE_NODE_TO_STORED_HASH,
     86                     Arguments(String)));
     87     Add(Instruction("compare_substring_hashed",
     88                     jtl::COMPARE_NODE_SUBSTRING,
     89                     Arguments(StringPattern)));
     90     Add(Instruction("break", jtl::STOP_EXECUTING_SENTENCE, Arguments()));
     91   }
     92 
     93   JtlCompiler::CompileError::ErrorCode TranscodeInstruction(
     94       const std::string& name,
     95       const ListValue& arguments,
     96       bool ends_sentence,
     97       const jtl::Hasher& hasher,
     98       ByteCodeWriter* target) const {
     99     if (instruction_map_.count(name) == 0)
    100       return JtlCompiler::CompileError::INVALID_OPERATION_NAME;
    101     const Instruction& instruction(instruction_map_.at(name));
    102     if (instruction.argument_types.size() != arguments.GetSize())
    103       return JtlCompiler::CompileError::INVALID_ARGUMENT_COUNT;
    104     target->WriteOpCode(instruction.op_code);
    105     for (size_t i = 0; i < arguments.GetSize(); ++i) {
    106       switch (instruction.argument_types[i]) {
    107         case Bool: {
    108           bool value = false;
    109           if (!arguments.GetBoolean(i, &value))
    110             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
    111           target->WriteBool(value);
    112           break;
    113         }
    114         case String: {
    115           std::string value;
    116           if (!arguments.GetString(i, &value))
    117             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
    118           target->WriteHash(hasher.GetHash(value));
    119           break;
    120         }
    121         case StringPattern: {
    122           std::string value;
    123           if (!arguments.GetString(i, &value))
    124             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
    125           if (value.empty() ||
    126               value.size() > std::numeric_limits<uint32>::max())
    127             return JtlCompiler::CompileError::INVALID_ARGUMENT_VALUE;
    128           target->WriteHash(hasher.GetHash(value));
    129           target->WriteUint32(static_cast<uint32>(value.size()));
    130           uint32 pattern_sum = std::accumulate(
    131               value.begin(), value.end(), static_cast<uint32>(0u));
    132           target->WriteUint32(pattern_sum);
    133           break;
    134         }
    135         case HashString: {
    136           std::string hash_value;
    137           if (!arguments.GetString(i, &hash_value) ||
    138               !jtl::Hasher::IsHash(hash_value))
    139             return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
    140           target->WriteHash(hash_value);
    141           break;
    142         }
    143         default:
    144           NOTREACHED();
    145           return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
    146       }
    147     }
    148     if (ends_sentence)
    149       target->WriteOpCode(jtl::END_OF_SENTENCE);
    150     return JtlCompiler::CompileError::ERROR_NONE;
    151   }
    152 
    153  private:
    154   // The possible types of an operation's argument.
    155   enum ArgumentType {
    156     None,
    157     Bool,
    158     String,
    159     StringPattern,
    160     HashString
    161   };
    162 
    163   // Encapsulates meta-data about one instruction.
    164   struct Instruction {
    165     Instruction() : op_code(jtl::END_OF_SENTENCE) {}
    166     Instruction(const char* name,
    167                 jtl_foundation::OpCodes op_code,
    168                 const std::vector<ArgumentType>& argument_types)
    169         : name(name), op_code(op_code), argument_types(argument_types) {}
    170 
    171     std::string name;
    172     jtl::OpCodes op_code;
    173     std::vector<ArgumentType> argument_types;
    174   };
    175 
    176   static std::vector<ArgumentType> Arguments(ArgumentType arg1_type = None,
    177                                              ArgumentType arg2_type = None,
    178                                              ArgumentType arg3_type = None) {
    179     std::vector<ArgumentType> result;
    180     if (arg1_type != None)
    181       result.push_back(arg1_type);
    182     if (arg2_type != None)
    183       result.push_back(arg2_type);
    184     if (arg3_type != None)
    185       result.push_back(arg3_type);
    186     return result;
    187   }
    188 
    189   void Add(const Instruction& instruction) {
    190     instruction_map_[instruction.name] = instruction;
    191   }
    192 
    193   std::map<std::string, Instruction> instruction_map_;
    194 
    195   DISALLOW_COPY_AND_ASSIGN(InstructionSet);
    196 };
    197 
    198 }  // namespace
    199 
    200 bool JtlCompiler::Compile(const std::string& source_code,
    201                           const std::string& hash_seed,
    202                           std::string* output_bytecode,
    203                           CompileError* error_details) {
    204   DCHECK(output_bytecode);
    205   InstructionSet instruction_set;
    206   ByteCodeWriter bytecode_writer(output_bytecode);
    207   jtl::Hasher hasher(hash_seed);
    208 
    209   std::string compacted_source_code;
    210   std::vector<size_t> newline_indices;
    211   size_t mismatched_quotes_line;
    212   if (!JtlParser::RemoveCommentsAndAllWhitespace(source_code,
    213                                                  &compacted_source_code,
    214                                                  &newline_indices,
    215                                                  &mismatched_quotes_line)) {
    216     if (error_details) {
    217       error_details->context = "";  // No meaningful intra-line context here.
    218       error_details->line_number = mismatched_quotes_line;
    219       error_details->error_code = CompileError::MISMATCHED_DOUBLE_QUOTES;
    220     }
    221     return false;
    222   }
    223 
    224   JtlParser parser(compacted_source_code, newline_indices);
    225   while (!parser.HasFinished()) {
    226     std::string operation_name;
    227     ListValue arguments;
    228     bool ends_sentence = false;
    229     if (!parser.ParseNextOperation(
    230              &operation_name, &arguments, &ends_sentence)) {
    231       if (error_details) {
    232         error_details->context = parser.GetLastContext();
    233         error_details->line_number = parser.GetLastLineNumber();
    234         error_details->error_code = CompileError::PARSING_ERROR;
    235       }
    236       return false;
    237     }
    238     CompileError::ErrorCode error_code = instruction_set.TranscodeInstruction(
    239         operation_name, arguments, ends_sentence, hasher, &bytecode_writer);
    240     if (error_code != CompileError::ERROR_NONE) {
    241       if (error_details) {
    242         error_details->context = parser.GetLastContext();
    243         error_details->line_number = parser.GetLastLineNumber();
    244         error_details->error_code = error_code;
    245       }
    246       return false;
    247     }
    248   }
    249 
    250   return true;
    251 }
    252