blob: f98d3d5eb3b31dbdc9f8b988f39644f9776c92d7 [file] [log] [blame]
// Copyright 2013 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.
#include "chrome/tools/profile_reset/jtl_compiler.h"
#include <map>
#include "base/logging.h"
#include "chrome/browser/profile_resetter/jtl_foundation.h"
#include "chrome/tools/profile_reset/jtl_parser.h"
namespace jtl = jtl_foundation;
namespace {
// Serializes symbols into byte-code in a streaming manner.
class ByteCodeWriter {
public:
explicit ByteCodeWriter(std::string* output) : output_(output) {}
~ByteCodeWriter() {}
void WriteUint8(uint8 value) { output_->push_back(static_cast<char>(value)); }
void WriteOpCode(uint8 op_code) { WriteUint8(op_code); }
void WriteHash(const std::string& hash) {
CHECK(jtl::Hasher::IsHash(hash));
*output_ += hash;
}
void WriteBool(bool value) { WriteUint8(value ? 1u : 0u); }
private:
std::string* output_;
DISALLOW_COPY_AND_ASSIGN(ByteCodeWriter);
};
// Encapsulates meta-data about all instructions, and is capable of transcoding
// each instruction from a parsed text-based format to byte-code.
class InstructionSet {
public:
InstructionSet() {
// Define each instruction in this list.
// Note:
// - Instructions ending in "hash" will write their 'HashString' arguments
// directly into the byte-code.
// - Instructions ending in "hashed" will first hash their 'String'
// arguments, and will write this hash to the byte-code.
Add(Instruction("go", jtl::NAVIGATE, Arguments(String)));
Add(Instruction("any", jtl::NAVIGATE_ANY, Arguments()));
Add(Instruction("back", jtl::NAVIGATE_BACK, Arguments()));
Add(Instruction("store_bool", jtl::STORE_BOOL, Arguments(String, Bool)));
Add(Instruction("store_hash",
jtl::STORE_HASH, Arguments(String, HashString)));
Add(Instruction("store_hashed",
jtl::STORE_HASH, Arguments(String, String)));
Add(Instruction("store_node_bool",
jtl::STORE_NODE_BOOL, Arguments(String)));
Add(Instruction("store_node_hash",
jtl::STORE_NODE_HASH, Arguments(String)));
Add(Instruction("compare_bool", jtl::COMPARE_NODE_BOOL, Arguments(Bool)));
Add(Instruction("compare_hashed",
jtl::COMPARE_NODE_HASH, Arguments(String)));
Add(Instruction("compare_hashed_not",
jtl::COMPARE_NODE_HASH_NOT, Arguments(String)));
Add(Instruction("compare_stored_bool",
jtl::COMPARE_STORED_BOOL,
Arguments(String, Bool, Bool)));
Add(Instruction("compare_stored_hashed",
jtl::COMPARE_STORED_HASH,
Arguments(String, String, String)));
Add(Instruction("compare_to_stored_bool",
jtl::COMPARE_NODE_TO_STORED_BOOL,
Arguments(String)));
Add(Instruction("compare_to_stored_hash",
jtl::COMPARE_NODE_TO_STORED_HASH,
Arguments(String)));
Add(Instruction("break", jtl::STOP_EXECUTING_SENTENCE, Arguments()));
}
JtlCompiler::CompileError::ErrorCode TranscodeInstruction(
const std::string& name,
const ListValue& arguments,
bool ends_sentence,
const jtl::Hasher& hasher,
ByteCodeWriter* target) const {
if (instruction_map_.count(name) == 0)
return JtlCompiler::CompileError::INVALID_OPERATION_NAME;
const Instruction& instruction(instruction_map_.at(name));
if (instruction.argument_types.size() != arguments.GetSize())
return JtlCompiler::CompileError::INVALID_ARGUMENT_COUNT;
target->WriteOpCode(instruction.op_code);
for (size_t i = 0; i < arguments.GetSize(); ++i) {
switch (instruction.argument_types[i]) {
case Bool: {
bool value = false;
if (!arguments.GetBoolean(i, &value))
return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
target->WriteBool(value);
break;
}
case String: {
std::string value;
if (!arguments.GetString(i, &value))
return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
target->WriteHash(hasher.GetHash(value));
break;
}
case HashString: {
std::string hash_value;
if (!arguments.GetString(i, &hash_value) ||
!jtl::Hasher::IsHash(hash_value))
return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
target->WriteHash(hash_value);
break;
}
default:
NOTREACHED();
return JtlCompiler::CompileError::INVALID_ARGUMENT_TYPE;
}
}
if (ends_sentence)
target->WriteOpCode(jtl::END_OF_SENTENCE);
return JtlCompiler::CompileError::ERROR_NONE;
}
private:
// The possible types of an operation's argument.
enum ArgumentType {
None,
Bool,
String,
HashString
};
// Encapsulates meta-data about one instruction.
struct Instruction {
Instruction() : op_code(jtl::END_OF_SENTENCE) {}
Instruction(const char* name,
jtl_foundation::OpCodes op_code,
const std::vector<ArgumentType>& argument_types)
: name(name), op_code(op_code), argument_types(argument_types) {}
std::string name;
jtl::OpCodes op_code;
std::vector<ArgumentType> argument_types;
};
static std::vector<ArgumentType> Arguments(ArgumentType arg1_type = None,
ArgumentType arg2_type = None,
ArgumentType arg3_type = None) {
std::vector<ArgumentType> result;
if (arg1_type != None)
result.push_back(arg1_type);
if (arg2_type != None)
result.push_back(arg2_type);
if (arg3_type != None)
result.push_back(arg3_type);
return result;
}
void Add(const Instruction& instruction) {
instruction_map_[instruction.name] = instruction;
}
std::map<std::string, Instruction> instruction_map_;
DISALLOW_COPY_AND_ASSIGN(InstructionSet);
};
} // namespace
bool JtlCompiler::Compile(const std::string& source_code,
const std::string& hash_seed,
std::string* output_bytecode,
CompileError* error_details) {
DCHECK(output_bytecode);
InstructionSet instruction_set;
ByteCodeWriter bytecode_writer(output_bytecode);
jtl::Hasher hasher(hash_seed);
std::string compacted_source_code;
std::vector<size_t> newline_indices;
size_t mismatched_quotes_line;
if (!JtlParser::RemoveCommentsAndAllWhitespace(source_code,
&compacted_source_code,
&newline_indices,
&mismatched_quotes_line)) {
if (error_details) {
error_details->context = ""; // No meaningful intra-line context here.
error_details->line_number = mismatched_quotes_line;
error_details->error_code = CompileError::MISMATCHED_DOUBLE_QUOTES;
}
return false;
}
JtlParser parser(compacted_source_code, newline_indices);
while (!parser.HasFinished()) {
std::string operation_name;
ListValue arguments;
bool ends_sentence = false;
if (!parser.ParseNextOperation(
&operation_name, &arguments, &ends_sentence)) {
if (error_details) {
error_details->context = parser.GetLastContext();
error_details->line_number = parser.GetLastLineNumber();
error_details->error_code = CompileError::PARSING_ERROR;
}
return false;
}
CompileError::ErrorCode error_code = instruction_set.TranscodeInstruction(
operation_name, arguments, ends_sentence, hasher, &bytecode_writer);
if (error_code != CompileError::ERROR_NONE) {
if (error_details) {
error_details->context = parser.GetLastContext();
error_details->line_number = parser.GetLastLineNumber();
error_details->error_code = error_code;
}
return false;
}
}
return true;
}