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.
5 #include "chrome/tools/profile_reset/jtl_compiler.h"
11 #include "base/logging.h"
12 #include "chrome/browser/profile_resetter/jtl_foundation.h"
13 #include "chrome/tools/profile_reset/jtl_parser.h"
15 namespace jtl
= jtl_foundation
;
19 // Serializes symbols into byte-code in a streaming manner.
20 class ByteCodeWriter
{
22 explicit ByteCodeWriter(std::string
* output
) : output_(output
) {}
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
));
32 void WriteOpCode(uint8 op_code
) { WriteUint8(op_code
); }
33 void WriteHash(const std::string
& hash
) {
34 CHECK(jtl::Hasher::IsHash(hash
));
37 void WriteBool(bool value
) { WriteUint8(value
? 1u : 0u); }
42 DISALLOW_COPY_AND_ASSIGN(ByteCodeWriter
);
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
{
50 // Define each instruction in this list.
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
,
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
,
85 Add(Instruction("compare_to_stored_hash",
86 jtl::COMPARE_NODE_TO_STORED_HASH
,
88 Add(Instruction("compare_substring_hashed",
89 jtl::COMPARE_NODE_SUBSTRING
,
90 Arguments(StringPattern
)));
91 Add(Instruction("break", jtl::STOP_EXECUTING_SENTENCE
, Arguments()));
94 JtlCompiler::CompileError::ErrorCode
TranscodeInstruction(
95 const std::string
& name
,
96 const base::ListValue
& arguments
,
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
]) {
110 if (!arguments
.GetBoolean(i
, &value
))
111 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
112 target
->WriteBool(value
);
117 if (!arguments
.GetString(i
, &value
))
118 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
119 target
->WriteHash(hasher
.GetHash(value
));
122 case StringPattern
: {
124 if (!arguments
.GetString(i
, &value
))
125 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
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
);
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
);
146 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
150 target
->WriteOpCode(jtl::END_OF_SENTENCE
);
151 return JtlCompiler::CompileError::ERROR_NONE
;
155 // The possible types of an operation's argument.
164 // Encapsulates meta-data about one 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
) {}
173 jtl::OpCodes op_code
;
174 std::vector
<ArgumentType
> argument_types
;
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
);
190 void Add(const Instruction
& instruction
) {
191 instruction_map_
[instruction
.name
] = instruction
;
194 std::map
<std::string
, Instruction
> instruction_map_
;
196 DISALLOW_COPY_AND_ASSIGN(InstructionSet
);
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
);
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
,
216 &mismatched_quotes_line
)) {
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
;
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
)) {
233 error_details
->context
= parser
.GetLastContext();
234 error_details
->line_number
= parser
.GetLastLineNumber();
235 error_details
->error_code
= CompileError::PARSING_ERROR
;
239 CompileError::ErrorCode error_code
= instruction_set
.TranscodeInstruction(
240 operation_name
, arguments
, ends_sentence
, hasher
, &bytecode_writer
);
241 if (error_code
!= CompileError::ERROR_NONE
) {
243 error_details
->context
= parser
.GetLastContext();
244 error_details
->line_number
= parser
.GetLastLineNumber();
245 error_details
->error_code
= error_code
;