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_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 
     94   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 {
    166     Instruction() : op_code(jtl::END_OF_SENTENCE) {}
    167     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 
    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 
    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 
    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