| // Copyright 2017 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_DECLARABLE_H_ |
| #define V8_TORQUE_DECLARABLE_H_ |
| |
| #include <cassert> |
| #include <string> |
| #include <unordered_map> |
| |
| #include "src/base/functional.h" |
| #include "src/base/logging.h" |
| #include "src/torque/ast.h" |
| #include "src/torque/types.h" |
| #include "src/torque/utils.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace torque { |
| |
| class Scope; |
| class Namespace; |
| class TypeArgumentInference; |
| |
| DECLARE_CONTEXTUAL_VARIABLE(CurrentScope, Scope*); |
| |
| struct QualifiedName { |
| std::vector<std::string> namespace_qualification; |
| std::string name; |
| |
| QualifiedName(std::vector<std::string> namespace_qualification, |
| std::string name) |
| : namespace_qualification(std::move(namespace_qualification)), |
| name(std::move(name)) {} |
| explicit QualifiedName(std::string name) |
| : QualifiedName({}, std::move(name)) {} |
| |
| bool HasNamespaceQualification() const { |
| return !namespace_qualification.empty(); |
| } |
| |
| QualifiedName DropFirstNamespaceQualification() const { |
| return QualifiedName{ |
| std::vector<std::string>(namespace_qualification.begin() + 1, |
| namespace_qualification.end()), |
| name}; |
| } |
| |
| friend std::ostream& operator<<(std::ostream& os, const QualifiedName& name); |
| }; |
| |
| class Declarable { |
| public: |
| virtual ~Declarable() = default; |
| enum Kind { |
| kNamespace, |
| kTorqueMacro, |
| kExternMacro, |
| kMethod, |
| kBuiltin, |
| kRuntimeFunction, |
| kIntrinsic, |
| kGenericCallable, |
| kGenericType, |
| kTypeAlias, |
| kExternConstant, |
| kNamespaceConstant |
| }; |
| Kind kind() const { return kind_; } |
| bool IsNamespace() const { return kind() == kNamespace; } |
| bool IsMacro() const { return IsTorqueMacro() || IsExternMacro(); } |
| bool IsTorqueMacro() const { return kind() == kTorqueMacro || IsMethod(); } |
| bool IsMethod() const { return kind() == kMethod; } |
| bool IsExternMacro() const { return kind() == kExternMacro; } |
| bool IsIntrinsic() const { return kind() == kIntrinsic; } |
| bool IsBuiltin() const { return kind() == kBuiltin; } |
| bool IsRuntimeFunction() const { return kind() == kRuntimeFunction; } |
| bool IsGenericCallable() const { return kind() == kGenericCallable; } |
| bool IsGenericType() const { return kind() == kGenericType; } |
| bool IsTypeAlias() const { return kind() == kTypeAlias; } |
| bool IsExternConstant() const { return kind() == kExternConstant; } |
| bool IsNamespaceConstant() const { return kind() == kNamespaceConstant; } |
| bool IsValue() const { return IsExternConstant() || IsNamespaceConstant(); } |
| bool IsScope() const { return IsNamespace() || IsCallable(); } |
| bool IsCallable() const { |
| return IsMacro() || IsBuiltin() || IsRuntimeFunction() || IsIntrinsic() || |
| IsMethod(); |
| } |
| virtual const char* type_name() const { return "<<unknown>>"; } |
| Scope* ParentScope() const { return parent_scope_; } |
| |
| // The SourcePosition of the whole declarable. For example, for a macro |
| // this will encompass not only the signature, but also the body. |
| SourcePosition Position() const { return position_; } |
| void SetPosition(const SourcePosition& position) { position_ = position; } |
| |
| // The SourcePosition of the identifying name of the declarable. For example, |
| // for a macro this will be the SourcePosition of the name. |
| // Note that this SourcePosition might not make sense for all kinds of |
| // declarables, in that case, the default SourcePosition is returned. |
| SourcePosition IdentifierPosition() const { |
| return identifier_position_.source.IsValid() ? identifier_position_ |
| : position_; |
| } |
| void SetIdentifierPosition(const SourcePosition& position) { |
| identifier_position_ = position; |
| } |
| |
| bool IsUserDefined() const { return is_user_defined_; } |
| void SetIsUserDefined(bool is_user_defined) { |
| is_user_defined_ = is_user_defined; |
| } |
| |
| protected: |
| explicit Declarable(Kind kind) : kind_(kind) {} |
| |
| private: |
| const Kind kind_; |
| Scope* const parent_scope_ = CurrentScope::Get(); |
| SourcePosition position_ = CurrentSourcePosition::Get(); |
| SourcePosition identifier_position_ = SourcePosition::Invalid(); |
| bool is_user_defined_ = true; |
| }; |
| |
| #define DECLARE_DECLARABLE_BOILERPLATE(x, y) \ |
| static x* cast(Declarable* declarable) { \ |
| DCHECK(declarable->Is##x()); \ |
| return static_cast<x*>(declarable); \ |
| } \ |
| static const x* cast(const Declarable* declarable) { \ |
| DCHECK(declarable->Is##x()); \ |
| return static_cast<const x*>(declarable); \ |
| } \ |
| const char* type_name() const override { return #y; } \ |
| static x* DynamicCast(Declarable* declarable) { \ |
| if (!declarable) return nullptr; \ |
| if (!declarable->Is##x()) return nullptr; \ |
| return static_cast<x*>(declarable); \ |
| } \ |
| static const x* DynamicCast(const Declarable* declarable) { \ |
| if (!declarable) return nullptr; \ |
| if (!declarable->Is##x()) return nullptr; \ |
| return static_cast<const x*>(declarable); \ |
| } |
| |
| // Information about what code caused a specialization to exist. This is used |
| // for error reporting. |
| struct SpecializationRequester { |
| // The position of the expression that caused this specialization. |
| SourcePosition position; |
| // The Scope which contains the expression that caused this specialization. |
| // It may in turn also be within a specialization, which allows us to print |
| // the stack of requesters when an error occurs. |
| Scope* scope; |
| // The name of the specialization. |
| std::string name; |
| |
| static SpecializationRequester None() { |
| return {SourcePosition::Invalid(), nullptr, ""}; |
| } |
| |
| bool IsNone() const { |
| return position == SourcePosition::Invalid() && scope == nullptr && |
| name == ""; |
| } |
| SpecializationRequester(SourcePosition position, Scope* scope, |
| std::string name); |
| }; |
| |
| class Scope : public Declarable { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(Scope, scope) |
| explicit Scope(Declarable::Kind kind) : Declarable(kind) {} |
| |
| std::vector<Declarable*> LookupShallow(const QualifiedName& name) { |
| if (!name.HasNamespaceQualification()) return declarations_[name.name]; |
| Scope* child = nullptr; |
| for (Declarable* declarable : |
| declarations_[name.namespace_qualification.front()]) { |
| if (Scope* scope = Scope::DynamicCast(declarable)) { |
| if (child != nullptr) { |
| ReportError("ambiguous reference to scope ", |
| name.namespace_qualification.front()); |
| } |
| child = scope; |
| } |
| } |
| if (child == nullptr) return {}; |
| return child->LookupShallow(name.DropFirstNamespaceQualification()); |
| } |
| |
| std::vector<Declarable*> Lookup(const QualifiedName& name); |
| template <class T> |
| T* AddDeclarable(const std::string& name, T* declarable) { |
| declarations_[name].push_back(declarable); |
| return declarable; |
| } |
| |
| const SpecializationRequester& GetSpecializationRequester() const { |
| return requester_; |
| } |
| void SetSpecializationRequester(const SpecializationRequester& requester) { |
| requester_ = requester; |
| } |
| |
| private: |
| std::unordered_map<std::string, std::vector<Declarable*>> declarations_; |
| |
| // If this Scope was created for specializing a generic type or callable, |
| // then {requester_} refers to the place that caused the specialization so we |
| // can construct useful error messages. |
| SpecializationRequester requester_ = SpecializationRequester::None(); |
| }; |
| |
| class Namespace : public Scope { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(Namespace, namespace) |
| explicit Namespace(const std::string& name) |
| : Scope(Declarable::kNamespace), name_(name) {} |
| const std::string& name() const { return name_; } |
| bool IsDefaultNamespace() const; |
| bool IsTestNamespace() const; |
| |
| private: |
| std::string name_; |
| }; |
| |
| inline Namespace* CurrentNamespace() { |
| Scope* scope = CurrentScope::Get(); |
| while (true) { |
| if (Namespace* n = Namespace::DynamicCast(scope)) { |
| return n; |
| } |
| scope = scope->ParentScope(); |
| } |
| } |
| |
| class Value : public Declarable { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(Value, value) |
| const Identifier* name() const { return name_; } |
| virtual bool IsConst() const { return true; } |
| VisitResult value() const { return *value_; } |
| const Type* type() const { return type_; } |
| |
| void set_value(VisitResult value) { |
| DCHECK(!value_); |
| value_ = value; |
| } |
| |
| protected: |
| Value(Kind kind, const Type* type, Identifier* name) |
| : Declarable(kind), type_(type), name_(name) {} |
| |
| private: |
| const Type* type_; |
| Identifier* name_; |
| base::Optional<VisitResult> value_; |
| }; |
| |
| class NamespaceConstant : public Value { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(NamespaceConstant, constant) |
| |
| const std::string& external_name() const { return external_name_; } |
| Expression* body() const { return body_; } |
| |
| private: |
| friend class Declarations; |
| explicit NamespaceConstant(Identifier* constant_name, |
| std::string external_name, const Type* type, |
| Expression* body) |
| : Value(Declarable::kNamespaceConstant, type, constant_name), |
| external_name_(std::move(external_name)), |
| body_(body) {} |
| |
| std::string external_name_; |
| Expression* body_; |
| }; |
| |
| class ExternConstant : public Value { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(ExternConstant, constant) |
| |
| private: |
| friend class Declarations; |
| explicit ExternConstant(Identifier* name, const Type* type, std::string value) |
| : Value(Declarable::kExternConstant, type, name) { |
| set_value(VisitResult(type, std::move(value))); |
| } |
| }; |
| |
| enum class OutputType { |
| kCSA, |
| kCC, |
| }; |
| |
| class Callable : public Scope { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(Callable, callable) |
| const std::string& ExternalName() const { return external_name_; } |
| const std::string& ReadableName() const { return readable_name_; } |
| const Signature& signature() const { return signature_; } |
| bool IsTransitioning() const { return signature().transitioning; } |
| const NameVector& parameter_names() const { |
| return signature_.parameter_names; |
| } |
| bool HasReturnValue() const { |
| return !signature_.return_type->IsVoidOrNever(); |
| } |
| void IncrementReturns() { ++returns_; } |
| bool HasReturns() const { return returns_; } |
| base::Optional<Statement*> body() const { return body_; } |
| bool IsExternal() const { return !body_.has_value(); } |
| virtual bool ShouldBeInlined(OutputType output_type) const { |
| // C++ output doesn't support exiting to labels, so functions with labels in |
| // the signature must be inlined. |
| return output_type == OutputType::kCC && !signature().labels.empty(); |
| } |
| bool ShouldGenerateExternalCode(OutputType output_type) const { |
| return !ShouldBeInlined(output_type); |
| } |
| |
| static std::string PrefixNameForCCOutput(const std::string& name) { |
| // If a Torque macro requires a C++ runtime function to be generated, then |
| // the generated function begins with this prefix to avoid any naming |
| // collisions with the generated CSA function for the same macro. |
| return "TqRuntime" + name; |
| } |
| |
| // Name to use in runtime C++ code. |
| virtual std::string CCName() const { |
| return PrefixNameForCCOutput(ExternalName()); |
| } |
| |
| protected: |
| Callable(Declarable::Kind kind, std::string external_name, |
| std::string readable_name, Signature signature, |
| base::Optional<Statement*> body) |
| : Scope(kind), |
| external_name_(std::move(external_name)), |
| |
| readable_name_(std::move(readable_name)), |
| signature_(std::move(signature)), |
| returns_(0), |
| body_(body) { |
| DCHECK(!body || *body); |
| } |
| |
| private: |
| std::string external_name_; |
| std::string readable_name_; |
| Signature signature_; |
| size_t returns_; |
| base::Optional<Statement*> body_; |
| }; |
| |
| class Macro : public Callable { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(Macro, macro) |
| bool ShouldBeInlined(OutputType output_type) const override { |
| for (const LabelDeclaration& label : signature().labels) { |
| for (const Type* type : label.types) { |
| if (type->StructSupertype()) return true; |
| } |
| } |
| // Intrinsics that are used internally in Torque and implemented as torque |
| // code should be inlined and not generate C++ definitions. |
| if (ReadableName()[0] == '%') return true; |
| return Callable::ShouldBeInlined(output_type); |
| } |
| |
| void SetUsed() { used_ = true; } |
| bool IsUsed() const { return used_; } |
| |
| protected: |
| Macro(Declarable::Kind kind, std::string external_name, |
| std::string readable_name, const Signature& signature, |
| base::Optional<Statement*> body) |
| : Callable(kind, std::move(external_name), std::move(readable_name), |
| signature, body), |
| used_(false) { |
| if (signature.parameter_types.var_args) { |
| ReportError("Varargs are not supported for macros."); |
| } |
| } |
| |
| private: |
| bool used_; |
| }; |
| |
| class ExternMacro : public Macro { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(ExternMacro, ExternMacro) |
| |
| const std::string& external_assembler_name() const { |
| return external_assembler_name_; |
| } |
| |
| std::string CCName() const override { |
| return "TorqueRuntimeMacroShims::" + external_assembler_name() + |
| "::" + ExternalName(); |
| } |
| |
| private: |
| friend class Declarations; |
| ExternMacro(const std::string& name, std::string external_assembler_name, |
| Signature signature) |
| : Macro(Declarable::kExternMacro, name, name, std::move(signature), |
| base::nullopt), |
| external_assembler_name_(std::move(external_assembler_name)) {} |
| |
| std::string external_assembler_name_; |
| }; |
| |
| class TorqueMacro : public Macro { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(TorqueMacro, TorqueMacro) |
| bool IsExportedToCSA() const { return exported_to_csa_; } |
| std::string CCName() const override { |
| // Exported functions must have unique and C++-friendly readable names, so |
| // prefer those wherever possible. |
| return PrefixNameForCCOutput(IsExportedToCSA() ? ReadableName() |
| : ExternalName()); |
| } |
| |
| protected: |
| TorqueMacro(Declarable::Kind kind, std::string external_name, |
| std::string readable_name, const Signature& signature, |
| base::Optional<Statement*> body, bool is_user_defined, |
| bool exported_to_csa) |
| : Macro(kind, std::move(external_name), std::move(readable_name), |
| signature, body), |
| exported_to_csa_(exported_to_csa) { |
| SetIsUserDefined(is_user_defined); |
| } |
| |
| private: |
| friend class Declarations; |
| TorqueMacro(std::string external_name, std::string readable_name, |
| const Signature& signature, base::Optional<Statement*> body, |
| bool is_user_defined, bool exported_to_csa) |
| : TorqueMacro(Declarable::kTorqueMacro, std::move(external_name), |
| std::move(readable_name), signature, body, is_user_defined, |
| exported_to_csa) {} |
| |
| bool exported_to_csa_ = false; |
| }; |
| |
| class Method : public TorqueMacro { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(Method, Method) |
| bool ShouldBeInlined(OutputType output_type) const override { |
| return Macro::ShouldBeInlined(output_type) || |
| signature() |
| .parameter_types.types[signature().implicit_count] |
| ->IsStructType(); |
| } |
| AggregateType* aggregate_type() const { return aggregate_type_; } |
| |
| private: |
| friend class Declarations; |
| Method(AggregateType* aggregate_type, std::string external_name, |
| std::string readable_name, const Signature& signature, Statement* body) |
| : TorqueMacro(Declarable::kMethod, std::move(external_name), |
| std::move(readable_name), signature, body, true, false), |
| aggregate_type_(aggregate_type) {} |
| AggregateType* aggregate_type_; |
| }; |
| |
| class Builtin : public Callable { |
| public: |
| enum Kind { kStub, kFixedArgsJavaScript, kVarArgsJavaScript }; |
| DECLARE_DECLARABLE_BOILERPLATE(Builtin, builtin) |
| Kind kind() const { return kind_; } |
| bool IsStub() const { return kind_ == kStub; } |
| bool IsVarArgsJavaScript() const { return kind_ == kVarArgsJavaScript; } |
| bool IsFixedArgsJavaScript() const { return kind_ == kFixedArgsJavaScript; } |
| |
| private: |
| friend class Declarations; |
| Builtin(std::string external_name, std::string readable_name, |
| Builtin::Kind kind, const Signature& signature, |
| base::Optional<Statement*> body) |
| : Callable(Declarable::kBuiltin, std::move(external_name), |
| std::move(readable_name), signature, body), |
| kind_(kind) {} |
| |
| Kind kind_; |
| }; |
| |
| class RuntimeFunction : public Callable { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(RuntimeFunction, runtime) |
| |
| private: |
| friend class Declarations; |
| RuntimeFunction(const std::string& name, const Signature& signature) |
| : Callable(Declarable::kRuntimeFunction, name, name, signature, |
| base::nullopt) {} |
| }; |
| |
| class Intrinsic : public Callable { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(Intrinsic, intrinsic) |
| |
| private: |
| friend class Declarations; |
| Intrinsic(std::string name, const Signature& signature) |
| : Callable(Declarable::kIntrinsic, name, name, signature, base::nullopt) { |
| if (signature.parameter_types.var_args) { |
| ReportError("Varargs are not supported for intrinsics."); |
| } |
| } |
| }; |
| |
| class TypeConstraint { |
| public: |
| base::Optional<std::string> IsViolated(const Type*) const; |
| |
| static TypeConstraint Unconstrained() { return {}; } |
| static TypeConstraint SubtypeConstraint(const Type* upper_bound) { |
| TypeConstraint result; |
| result.upper_bound = {upper_bound}; |
| return result; |
| } |
| |
| private: |
| base::Optional<const Type*> upper_bound; |
| }; |
| |
| base::Optional<std::string> FindConstraintViolation( |
| const std::vector<const Type*>& types, |
| const std::vector<TypeConstraint>& constraints); |
| |
| std::vector<TypeConstraint> ComputeConstraints( |
| Scope* scope, const GenericParameters& parameters); |
| |
| template <class SpecializationType, class DeclarationType> |
| class GenericDeclarable : public Declarable { |
| private: |
| using Map = std::unordered_map<TypeVector, SpecializationType, |
| base::hash<TypeVector>>; |
| |
| public: |
| void AddSpecialization(const TypeVector& type_arguments, |
| SpecializationType specialization) { |
| DCHECK_EQ(0, specializations_.count(type_arguments)); |
| if (auto violation = |
| FindConstraintViolation(type_arguments, Constraints())) { |
| Error(*violation).Throw(); |
| } |
| specializations_[type_arguments] = specialization; |
| } |
| base::Optional<SpecializationType> GetSpecialization( |
| const TypeVector& type_arguments) const { |
| auto it = specializations_.find(type_arguments); |
| if (it != specializations_.end()) return it->second; |
| return base::nullopt; |
| } |
| |
| using iterator = typename Map::const_iterator; |
| iterator begin() const { return specializations_.begin(); } |
| iterator end() const { return specializations_.end(); } |
| |
| const std::string& name() const { return name_; } |
| auto declaration() const { return generic_declaration_->declaration; } |
| const GenericParameters& generic_parameters() const { |
| return generic_declaration_->generic_parameters; |
| } |
| |
| const std::vector<TypeConstraint>& Constraints() { |
| if (!constraints_) |
| constraints_ = {ComputeConstraints(ParentScope(), generic_parameters())}; |
| return *constraints_; |
| } |
| |
| protected: |
| GenericDeclarable(Declarable::Kind kind, const std::string& name, |
| DeclarationType generic_declaration) |
| : Declarable(kind), |
| name_(name), |
| generic_declaration_(generic_declaration) { |
| DCHECK(!generic_declaration->generic_parameters.empty()); |
| } |
| |
| private: |
| std::string name_; |
| DeclarationType generic_declaration_; |
| Map specializations_; |
| base::Optional<std::vector<TypeConstraint>> constraints_; |
| }; |
| |
| class GenericCallable |
| : public GenericDeclarable<Callable*, GenericCallableDeclaration*> { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(GenericCallable, generic_callable) |
| |
| base::Optional<Statement*> CallableBody(); |
| |
| TypeArgumentInference InferSpecializationTypes( |
| const TypeVector& explicit_specialization_types, |
| const std::vector<base::Optional<const Type*>>& arguments); |
| |
| private: |
| friend class Declarations; |
| GenericCallable(const std::string& name, |
| GenericCallableDeclaration* generic_declaration) |
| : GenericDeclarable<Callable*, GenericCallableDeclaration*>( |
| Declarable::kGenericCallable, name, generic_declaration) {} |
| }; |
| |
| class GenericType |
| : public GenericDeclarable<const Type*, GenericTypeDeclaration*> { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(GenericType, generic_type) |
| |
| private: |
| friend class Declarations; |
| GenericType(const std::string& name, |
| GenericTypeDeclaration* generic_declaration) |
| : GenericDeclarable<const Type*, GenericTypeDeclaration*>( |
| Declarable::kGenericType, name, generic_declaration) {} |
| }; |
| |
| class TypeAlias : public Declarable { |
| public: |
| DECLARE_DECLARABLE_BOILERPLATE(TypeAlias, type_alias) |
| |
| const Type* type() const { |
| if (type_) return *type_; |
| return Resolve(); |
| } |
| const Type* Resolve() const; |
| bool IsRedeclaration() const { return redeclaration_; } |
| SourcePosition GetDeclarationPosition() const { |
| return declaration_position_; |
| } |
| |
| private: |
| friend class Declarations; |
| friend class TypeVisitor; |
| |
| explicit TypeAlias( |
| const Type* type, bool redeclaration, |
| SourcePosition declaration_position = SourcePosition::Invalid()) |
| : Declarable(Declarable::kTypeAlias), |
| type_(type), |
| redeclaration_(redeclaration), |
| declaration_position_(declaration_position) {} |
| explicit TypeAlias( |
| TypeDeclaration* type, bool redeclaration, |
| SourcePosition declaration_position = SourcePosition::Invalid()) |
| : Declarable(Declarable::kTypeAlias), |
| delayed_(type), |
| redeclaration_(redeclaration), |
| declaration_position_(declaration_position) {} |
| |
| mutable bool being_resolved_ = false; |
| mutable base::Optional<TypeDeclaration*> delayed_; |
| mutable base::Optional<const Type*> type_; |
| bool redeclaration_; |
| const SourcePosition declaration_position_; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const Callable& m); |
| std::ostream& operator<<(std::ostream& os, const Builtin& b); |
| std::ostream& operator<<(std::ostream& os, const RuntimeFunction& b); |
| std::ostream& operator<<(std::ostream& os, const GenericCallable& g); |
| |
| #undef DECLARE_DECLARABLE_BOILERPLATE |
| |
| } // namespace torque |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_TORQUE_DECLARABLE_H_ |