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_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
,
84 Add(Instruction("compare_to_stored_hash",
85 jtl::COMPARE_NODE_TO_STORED_HASH
,
87 Add(Instruction("compare_substring_hashed",
88 jtl::COMPARE_NODE_SUBSTRING
,
89 Arguments(StringPattern
)));
90 Add(Instruction("break", jtl::STOP_EXECUTING_SENTENCE
, Arguments()));
93 JtlCompiler::CompileError::ErrorCode
TranscodeInstruction(
94 const std::string
& name
,
95 const base::ListValue
& arguments
,
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
]) {
109 if (!arguments
.GetBoolean(i
, &value
))
110 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
111 target
->WriteBool(value
);
116 if (!arguments
.GetString(i
, &value
))
117 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
118 target
->WriteHash(hasher
.GetHash(value
));
121 case StringPattern
: {
123 if (!arguments
.GetString(i
, &value
))
124 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
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
);
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
);
145 return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE
;
149 target
->WriteOpCode(jtl::END_OF_SENTENCE
);
150 return JtlCompiler::CompileError::ERROR_NONE
;
154 // The possible types of an operation's argument.
163 // Encapsulates meta-data about one 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
) {}
172 jtl::OpCodes op_code
;
173 std::vector
<ArgumentType
> argument_types
;
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
);
189 void Add(const Instruction
& instruction
) {
190 instruction_map_
[instruction
.name
] = instruction
;
193 std::map
<std::string
, Instruction
> instruction_map_
;
195 DISALLOW_COPY_AND_ASSIGN(InstructionSet
);
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
);
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
,
215 &mismatched_quotes_line
)) {
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
;
224 JtlParser
parser(compacted_source_code
, newline_indices
);
225 while (!parser
.HasFinished()) {
226 std::string operation_name
;
227 base::ListValue arguments
;
228 bool ends_sentence
= false;
229 if (!parser
.ParseNextOperation(
230 &operation_name
, &arguments
, &ends_sentence
)) {
232 error_details
->context
= parser
.GetLastContext();
233 error_details
->line_number
= parser
.GetLastLineNumber();
234 error_details
->error_code
= CompileError::PARSING_ERROR
;
238 CompileError::ErrorCode error_code
= instruction_set
.TranscodeInstruction(
239 operation_name
, arguments
, ends_sentence
, hasher
, &bytecode_writer
);
240 if (error_code
!= CompileError::ERROR_NONE
) {
242 error_details
->context
= parser
.GetLastContext();
243 error_details
->line_number
= parser
.GetLastLineNumber();
244 error_details
->error_code
= error_code
;