|  | #!/usr/bin/env python | 
|  |  | 
|  | # Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  |  | 
|  | """ | 
|  | Script for generating .proto and a conversion .cc file for a templated library | 
|  | based JavaScript parser fuzzer. | 
|  | """ | 
|  |  | 
|  | import sys | 
|  |  | 
|  | def ParseWord(word_string): | 
|  | # Every part of the word is either a string surrounded by "" or a placeholder | 
|  | # $<int>. | 
|  |  | 
|  | word_string = word_string.lstrip().rstrip() | 
|  |  | 
|  | parts = [] | 
|  | while len(word_string) > 0: | 
|  | if word_string[0] == '"': | 
|  | end_ix = 1 + word_string[1:].index('"') | 
|  | parts.append(word_string[1:end_ix]) | 
|  | word_string = word_string[(end_ix + 1):] | 
|  | elif word_string[0] == '$': | 
|  | if ' ' in word_string: | 
|  | end_ix = word_string.index(' ') | 
|  | else: | 
|  | end_ix = len(word_string) | 
|  | parts.append(int(word_string[1:end_ix])) | 
|  | word_string = word_string[end_ix:] | 
|  | else: | 
|  | assert(False) | 
|  | word_string = word_string.lstrip() | 
|  | return parts | 
|  |  | 
|  | def GenerateProtoContents(words): | 
|  | contents = '' | 
|  | for ix in range(len(words)): | 
|  | contents += '    token_value_' + str(ix) + ' = ' + str(ix) + ';\n' | 
|  | return contents | 
|  |  | 
|  | def GenerateConversionContents(words): | 
|  | contents = '' | 
|  | ix = 0 | 
|  | for word in words: | 
|  | contents += '    case ' + str(ix) + ':\n' | 
|  | max_part = -1 | 
|  | first = True | 
|  | building_string = '' | 
|  | for part in word: | 
|  | if not first: | 
|  | building_string += ' + std::string(" ") + ' | 
|  | if isinstance(part, str): | 
|  | building_string += 'std::string("' + part + '")' | 
|  | else: | 
|  | if (part > max_part): | 
|  | max_part = part | 
|  | building_string += ('token_to_string(token.inner_tokens(' + str(part) + | 
|  | '), depth)') | 
|  | first = False | 
|  | if max_part >= 0: | 
|  | contents += ('      if (token.inner_tokens().size() < ' + | 
|  | str(max_part + 1) + ') return std::string("");\n') | 
|  | contents += '      return ' + building_string + ';\n' | 
|  | ix += 1 | 
|  | return contents | 
|  |  | 
|  | def ReadDictionary(filename): | 
|  | with open(filename) as input_file: | 
|  | lines = input_file.readlines() | 
|  | words = [] | 
|  | for line in lines: | 
|  | if not line.startswith('#'): | 
|  | word = ParseWord(line) | 
|  | if len(word) > 0: | 
|  | words.append(word) | 
|  | return words | 
|  |  | 
|  | def main(argv): | 
|  | output_proto_file = argv[1] | 
|  | output_cc_file = argv[2] | 
|  | input_dict_file = argv[3] | 
|  |  | 
|  | words = ReadDictionary(input_dict_file) | 
|  |  | 
|  | proto_header = ('// Generated by generate_javascript_parser_proto.py.\n' | 
|  | '\n' | 
|  | 'syntax = "proto2";\n' | 
|  | 'package javascript_parser_proto_fuzzer;\n' | 
|  | '\n' | 
|  | 'message Token {\n' | 
|  | '  enum Value {\n') | 
|  |  | 
|  |  | 
|  | proto_footer = ('  }\n' | 
|  | '  required Value value = 1;\n' | 
|  | '  repeated Token inner_tokens = 2;\n' | 
|  | '}\n' | 
|  | '\n' | 
|  | 'message Source {\n' | 
|  | '  required bool is_module = 1;\n' | 
|  | '  repeated Token tokens = 2;\n' | 
|  | '}\n') | 
|  |  | 
|  | proto_contents = proto_header + GenerateProtoContents(words) + proto_footer | 
|  |  | 
|  | with open(output_proto_file, 'w') as f: | 
|  | f.write(proto_contents) | 
|  |  | 
|  | conversion_header = ( | 
|  | '// Generated by generate_javascript_parser_proto.py.\n' | 
|  | '\n' | 
|  | '#include "testing/libfuzzer/fuzzers/' | 
|  | 'javascript_parser_proto_to_string.h"\n' | 
|  | '\n' | 
|  | '// Bound calls to token_to_string to prevent memory usage from growing\n' | 
|  | '// too much.\n' | 
|  | 'const int kMaxRecursiveDepth = 10;\n' | 
|  | '\n' | 
|  | 'std::string token_to_string(\n' | 
|  | '    const javascript_parser_proto_fuzzer::Token& token, int depth)' | 
|  | ' {\n' | 
|  | '  if (++depth == kMaxRecursiveDepth) return std::string("");\n' | 
|  | '  switch(token.value()) {\n') | 
|  |  | 
|  | conversion_footer = ('    default: break;\n' | 
|  | '  }\n' | 
|  | '  return std::string("");\n' | 
|  | '}\n') | 
|  |  | 
|  | conversion_contents = (conversion_header + GenerateConversionContents(words) | 
|  | + conversion_footer) | 
|  |  | 
|  | with open(output_cc_file, 'w') as f: | 
|  | f.write(conversion_contents) | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main(sys.argv) |