Commit f2dbf318 authored by Nico Hartmann's avatar Nico Hartmann Committed by V8 LUCI CQ

[Torque] Introduce a builder pattern to generate C++ code

Generating C++ source files is very cumbersome using raw ostreams. This
CL introduces a few classes und the torque::cpp namespace that build an
abstraction of printing/formatting C++ source code (mostly functions for
now).

This is an initial implementation with a limited set of features and
uses. Landing features incrementally shall avoid huge CLs (+reviews) and
complex rebasing.

Bug: v8:7793
Change-Id: I3ae869755156175c7b37ea9f649e2f9c431ce3a3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2784688Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarSeth Brenith <seth.brenith@microsoft.com>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74705}
parent 78a06b56
......@@ -4454,6 +4454,8 @@ v8_source_set("torque_base") {
"src/torque/class-debug-reader-generator.cc",
"src/torque/constants.h",
"src/torque/contextual.h",
"src/torque/cpp-builder.cc",
"src/torque/cpp-builder.h",
"src/torque/csa-generator.cc",
"src/torque/csa-generator.h",
"src/torque/declarable.cc",
......
......@@ -81,7 +81,7 @@ class Flags final {
constexpr operator mask_type() const { return mask_; }
constexpr bool operator!() const { return !mask_; }
Flags without(flag_type flag) { return *this & (~Flags(flag)); }
Flags without(flag_type flag) const { return *this & (~Flags(flag)); }
friend size_t hash_value(const Flags& flags) { return flags.mask_; }
......
// Copyright 2021 the V8 project 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 "src/torque/cpp-builder.h"
namespace v8 {
namespace internal {
namespace torque {
namespace cpp {
void Function::PrintDeclarationHeader(std::ostream& stream,
int indentation) const {
if (!description_.empty()) {
stream << std::string(indentation, ' ') << "// " << description_ << "\n";
}
stream << std::string(indentation, ' ');
if (IsExport()) stream << "V8_EXPORT_PRIVATE ";
if (IsV8Inline())
stream << "V8_INLINE ";
else if (IsInline())
stream << "inline ";
if (IsStatic()) stream << "static ";
if (IsConstexpr()) stream << "constexpr ";
stream << return_type_ << " " << name_ << "(";
bool first = true;
for (const auto& p : parameters_) {
if (!first) stream << ", ";
stream << p.type;
if (!p.name.empty()) stream << " " << p.name;
if (!p.default_value.empty()) stream << " = " << p.default_value;
first = false;
}
stream << ")";
if (IsConst()) stream << " const";
}
void Function::PrintDeclaration(std::ostream& stream, int indentation) const {
PrintDeclarationHeader(stream, indentation);
stream << ";\n";
}
void Function::PrintDefinition(
std::ostream& stream, const std::function<void(std::ostream&)>& builder,
int indentation) const {
PrintBeginDefinition(stream, indentation);
if (builder) {
builder(stream);
}
PrintEndDefinition(stream, indentation);
}
void Function::PrintInlineDefinition(
std::ostream& stream, const std::function<void(std::ostream&)>& builder,
int indentation) const {
PrintDeclarationHeader(stream, indentation);
stream << " {\n";
if (builder) {
builder(stream);
}
PrintEndDefinition(stream, indentation);
}
void Function::PrintBeginDefinition(std::ostream& stream,
int indentation) const {
std::string scope;
if (owning_class_) {
scope = owning_class_->GetName();
const auto class_template_parameters =
owning_class_->GetTemplateParameters();
if (!class_template_parameters.empty()) {
stream << std::string(indentation, ' ');
stream << "template<";
scope += "<";
bool first = true;
for (const auto& p : class_template_parameters) {
if (!first) {
stream << ", ";
scope += ", ";
}
if (p.type.empty()) {
stream << "class " << p.name;
} else {
stream << p.type << " " << p.name;
}
scope += p.name;
first = false;
}
stream << ">\n";
scope += ">";
}
scope += "::";
}
stream << std::string(indentation, ' ') << return_type_ << " " << scope
<< name_ << "(";
bool first = true;
for (const auto& p : parameters_) {
if (!first) stream << ", ";
stream << p.type;
if (!p.name.empty()) stream << " " << p.name;
first = false;
}
stream << ")";
if (IsConst()) {
stream << " const";
}
stream << " {\n";
}
void Function::PrintEndDefinition(std::ostream& stream, int indentation) const {
stream << std::string(indentation, ' ');
stream << "}\n\n";
}
void File::BeginIncludeGuard(const std::string& name) {
s() << "#ifndef " << name
<< "\n"
"#define "
<< name << "\n";
}
void File::EndIncludeGuard(const std::string& name) {
s() << "#endif // " << name << "\n";
}
void File::BeginNamespace(std::string name) {
DCHECK(!name.empty());
DCHECK_EQ(name.find(':'), std::string::npos);
s() << "namespace " << name << " {\n";
namespaces_.push(std::move(name));
}
void File::BeginNamespace(std::string name0, std::string name1) {
BeginNamespace(name0);
BeginNamespace(name1);
}
void File::EndNamespace(const std::string& name) {
DCHECK(!namespaces_.empty());
DCHECK_EQ(namespaces_.top(), name);
s() << "} // namespace " << namespaces_.top() << "\n";
namespaces_.pop();
}
void File::EndNamespace(const std::string& name0, const std::string& name1) {
EndNamespace(name1);
EndNamespace(name0);
}
} // namespace cpp
} // namespace torque
} // namespace internal
} // namespace v8
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TORQUE_CPP_BUILDER_H_
#define V8_TORQUE_CPP_BUILDER_H_
#include <stack>
#include "src/torque/ast.h"
#include "src/torque/types.h"
namespace v8 {
namespace internal {
namespace torque {
namespace cpp {
struct TemplateParameter {
explicit TemplateParameter(std::string name) : name(std::move(name)) {}
TemplateParameter(std::string type, std::string name)
: name(std::move(name)), type(std::move(type)) {}
std::string name;
std::string type;
};
class Class {
public:
explicit Class(std::string name) : name_(std::move(name)) {}
Class(std::vector<TemplateParameter> template_parameters, std::string name)
: template_parameters_(std::move(template_parameters)),
name_(std::move(name)) {}
std::string GetName() const { return name_; }
std::vector<TemplateParameter> GetTemplateParameters() const {
return template_parameters_;
}
private:
std::vector<TemplateParameter> template_parameters_;
std::string name_;
};
#define FUNCTION_FLAG_LIST(V) \
V(Inline, 0x01) \
V(V8Inline, 0x03) \
V(Const, 0x04) \
V(Constexpr, 0x08) \
V(Export, 0x10) \
V(Static, 0x20) \
V(Override, 0x40)
class Function {
public:
enum FunctionFlag {
#define ENTRY(name, value) k##name = value,
FUNCTION_FLAG_LIST(ENTRY)
#undef ENTRY
};
struct Parameter {
std::string type;
std::string name;
std::string default_value;
Parameter(std::string type, std::string name,
std::string default_value = {})
: type(std::move(type)),
name(std::move(name)),
default_value(std::move(default_value)) {}
};
explicit Function(std::string name)
: owning_class_(nullptr), name_(std::move(name)) {}
Function(Class* owning_class, std::string name)
: owning_class_(owning_class), name_(std::move(name)) {}
~Function() = default;
static Function DefaultGetter(std::string return_type, Class* owner,
std::string name) {
Function getter(owner, std::move(name));
getter.SetReturnType(std::move(return_type));
getter.SetInline();
getter.SetConst();
return getter;
}
static Function DefaultSetter(Class* owner, std::string name,
std::string parameter_type,
std::string parameter_name) {
Function setter(owner, std::move(name));
setter.SetReturnType("void");
setter.AddParameter(std::move(parameter_type), std::move(parameter_name));
setter.SetInline();
return setter;
}
void SetFlag(FunctionFlag flag, bool value = true) {
if (value) {
flags_ = flags_ | flag;
} else {
flags_ = flags_.without(flag);
}
}
void SetFlags(base::Flags<FunctionFlag> flags, bool value = true) {
if (value) {
flags_ |= flags;
} else {
flags_ &= ~flags;
}
}
bool HasFlag(FunctionFlag flag) const { return (flags_ & flag) == flag; }
#define ACCESSOR(name, value) \
void Set##name(bool v = true) { SetFlag(k##name, v); } \
bool Is##name() const { return HasFlag(k##name); }
FUNCTION_FLAG_LIST(ACCESSOR)
#undef ACCESSOR
void SetDescription(std::string description) {
description_ = std::move(description);
}
void SetName(std::string name) { name_ = std::move(name); }
void SetReturnType(std::string return_type) {
return_type_ = std::move(return_type);
}
void AddParameter(std::string type, std::string name = {},
std::string default_value = {}) {
parameters_.emplace_back(std::move(type), std::move(name),
std::move(default_value));
}
void InsertParameter(int index, std::string type, std::string name = {},
std::string default_value = {}) {
DCHECK_GE(index, 0);
DCHECK_LE(index, parameters_.size());
parameters_.insert(
parameters_.begin() + index,
Parameter(std::move(type), std::move(name), std::move(default_value)));
}
std::vector<Parameter> GetParameters() const { return parameters_; }
std::vector<std::string> GetParameterNames() const {
std::vector<std::string> names;
std::transform(parameters_.begin(), parameters_.end(),
std::back_inserter(names),
[](const Parameter& p) { return p.name; });
return names;
}
void PrintDeclaration(std::ostream& stream, int indentation = 0) const;
void PrintDefinition(std::ostream& stream,
const std::function<void(std::ostream&)>& builder,
int indentation = 0) const;
void PrintInlineDefinition(std::ostream& stream,
const std::function<void(std::ostream&)>& builder,
int indentation = 0) const;
void PrintBeginDefinition(std::ostream& stream, int indentation = 0) const;
void PrintEndDefinition(std::ostream& stream, int indentation = 0) const;
protected:
void PrintDeclarationHeader(std::ostream& stream, int indentation) const;
private:
Class* owning_class_;
std::string description_;
std::string name_;
std::string return_type_;
std::vector<Parameter> parameters_;
base::Flags<FunctionFlag> flags_;
};
DEFINE_OPERATORS_FOR_FLAGS(base::Flags<Function::FunctionFlag>)
#undef FUNCTION_FLAG_LIST
class File {
public:
explicit File(std::ostream& stream) : stream_(&stream) {}
void BeginIncludeGuard(const std::string& name);
void EndIncludeGuard(const std::string& name);
void BeginNamespace(std::string name);
void BeginNamespace(std::string name0, std::string name1);
void EndNamespace(const std::string& name);
void EndNamespace(const std::string& name0, const std::string& name1);
void AddInclude(std::string include) { includes_.insert(std::move(include)); }
template <typename T>
File& operator<<(const T& value) {
s() << value;
return *this;
}
protected:
std::ostream& s() { return *stream_; }
private:
std::ostream* stream_;
std::set<std::string> includes_;
std::stack<std::string> namespaces_;
};
class IncludeGuardScope {
public:
explicit IncludeGuardScope(File* file, std::string name)
: file_(file), name_(std::move(name)) {
file_->BeginIncludeGuard(name_);
}
IncludeGuardScope(const IncludeGuardScope&) = delete;
IncludeGuardScope(IncludeGuardScope&& other) V8_NOEXCEPT : file_(nullptr),
name_() {
std::swap(file_, other.file_);
std::swap(name_, other.name_);
}
~IncludeGuardScope() {
if (file_) {
file_->EndIncludeGuard(name_);
}
}
IncludeGuardScope& operator=(const IncludeGuardScope&) = delete;
IncludeGuardScope& operator=(IncludeGuardScope&& other) V8_NOEXCEPT {
if (this != &other) {
DCHECK_NULL(file_);
DCHECK(name_.empty());
std::swap(file_, other.file_);
std::swap(name_, other.name_);
}
return *this;
}
private:
File* file_;
std::string name_;
};
} // namespace cpp
} // namespace torque
} // namespace internal
} // namespace v8
#endif // V8_TORQUE_CPP_BUILDER_H_
......@@ -11,6 +11,7 @@
#include "src/common/globals.h"
#include "src/torque/ast.h"
#include "src/torque/contextual.h"
#include "src/torque/cpp-builder.h"
#include "src/torque/declarable.h"
namespace v8 {
......@@ -62,10 +63,17 @@ class GlobalContext : public ContextualClass<GlobalContext> {
}
struct PerFileStreams {
PerFileStreams() : file(SourceId::Invalid()) {}
PerFileStreams()
: file(SourceId::Invalid()),
csa_header(csa_headerfile),
csa_cc(csa_ccfile),
class_definition_cc(class_definition_ccfile) {}
SourceId file;
std::stringstream csa_headerfile;
cpp::File csa_header;
std::stringstream csa_ccfile;
cpp::File csa_cc;
std::stringstream class_definition_headerfile;
// The beginning of the generated -inl.inc file, which includes declarations
......@@ -79,6 +87,7 @@ class GlobalContext : public ContextualClass<GlobalContext> {
std::stringstream class_definition_inline_headerfile;
std::stringstream class_definition_ccfile;
cpp::File class_definition_cc;
std::set<SourceId> required_builtin_includes;
};
......
This diff is collapsed.
......@@ -11,6 +11,7 @@
#include "src/base/macros.h"
#include "src/torque/ast.h"
#include "src/torque/cfg.h"
#include "src/torque/cpp-builder.h"
#include "src/torque/declarations.h"
#include "src/torque/global-context.h"
#include "src/torque/type-oracle.h"
......@@ -742,12 +743,12 @@ class ImplementationVisitor {
void GenerateExpressionBranch(Expression* expression, Block* true_block,
Block* false_block);
void GenerateMacroFunctionDeclaration(std::ostream& o,
Macro* macro);
std::vector<std::string> GenerateFunctionDeclaration(
std::ostream& o, const std::string& macro_prefix, const std::string& name,
const Signature& signature, const NameVector& parameter_names,
bool pass_code_assembler_state = true);
cpp::Function GenerateMacroFunctionDeclaration(Macro* macro);
cpp::Function GenerateFunction(
cpp::Class* owner, const std::string& name, const Signature& signature,
const NameVector& parameter_names, bool pass_code_assembler_state = true,
std::vector<std::string>* generated_parameter_names = nullptr);
VisitResult GenerateImplicitConvert(const Type* destination_type,
VisitResult source);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment