xref: /third_party/node/deps/v8/src/torque/declarable.h (revision 1cb0ef41)
1// Copyright 2017 the V8 project 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.
4
5#ifndef V8_TORQUE_DECLARABLE_H_
6#define V8_TORQUE_DECLARABLE_H_
7
8#include <cassert>
9#include <string>
10#include <unordered_map>
11
12#include "src/base/functional.h"
13#include "src/base/logging.h"
14#include "src/torque/ast.h"
15#include "src/torque/types.h"
16#include "src/torque/utils.h"
17
18namespace v8 {
19namespace internal {
20namespace torque {
21
22class Scope;
23class Namespace;
24class TypeArgumentInference;
25
26DECLARE_CONTEXTUAL_VARIABLE(CurrentScope, Scope*);
27
28struct QualifiedName {
29  std::vector<std::string> namespace_qualification;
30  std::string name;
31
32  QualifiedName(std::vector<std::string> namespace_qualification,
33                std::string name)
34      : namespace_qualification(std::move(namespace_qualification)),
35        name(std::move(name)) {}
36  explicit QualifiedName(std::string name)
37      : QualifiedName({}, std::move(name)) {}
38
39  static QualifiedName Parse(std::string qualified_name);
40
41  bool HasNamespaceQualification() const {
42    return !namespace_qualification.empty();
43  }
44
45  QualifiedName DropFirstNamespaceQualification() const {
46    return QualifiedName{
47        std::vector<std::string>(namespace_qualification.begin() + 1,
48                                 namespace_qualification.end()),
49        name};
50  }
51
52  friend std::ostream& operator<<(std::ostream& os, const QualifiedName& name);
53};
54
55class Declarable {
56 public:
57  virtual ~Declarable() = default;
58  enum Kind {
59    kNamespace,
60    kTorqueMacro,
61    kExternMacro,
62    kMethod,
63    kBuiltin,
64    kRuntimeFunction,
65    kIntrinsic,
66    kGenericCallable,
67    kGenericType,
68    kTypeAlias,
69    kExternConstant,
70    kNamespaceConstant
71  };
72  Kind kind() const { return kind_; }
73  bool IsNamespace() const { return kind() == kNamespace; }
74  bool IsMacro() const { return IsTorqueMacro() || IsExternMacro(); }
75  bool IsTorqueMacro() const { return kind() == kTorqueMacro || IsMethod(); }
76  bool IsMethod() const { return kind() == kMethod; }
77  bool IsExternMacro() const { return kind() == kExternMacro; }
78  bool IsIntrinsic() const { return kind() == kIntrinsic; }
79  bool IsBuiltin() const { return kind() == kBuiltin; }
80  bool IsRuntimeFunction() const { return kind() == kRuntimeFunction; }
81  bool IsGenericCallable() const { return kind() == kGenericCallable; }
82  bool IsGenericType() const { return kind() == kGenericType; }
83  bool IsTypeAlias() const { return kind() == kTypeAlias; }
84  bool IsExternConstant() const { return kind() == kExternConstant; }
85  bool IsNamespaceConstant() const { return kind() == kNamespaceConstant; }
86  bool IsValue() const { return IsExternConstant() || IsNamespaceConstant(); }
87  bool IsScope() const { return IsNamespace() || IsCallable(); }
88  bool IsCallable() const {
89    return IsMacro() || IsBuiltin() || IsRuntimeFunction() || IsIntrinsic() ||
90           IsMethod();
91  }
92  virtual const char* type_name() const { return "<<unknown>>"; }
93  Scope* ParentScope() const { return parent_scope_; }
94
95  // The SourcePosition of the whole declarable. For example, for a macro
96  // this will encompass not only the signature, but also the body.
97  SourcePosition Position() const { return position_; }
98  void SetPosition(const SourcePosition& position) { position_ = position; }
99
100  // The SourcePosition of the identifying name of the declarable. For example,
101  // for a macro this will be the SourcePosition of the name.
102  // Note that this SourcePosition might not make sense for all kinds of
103  // declarables, in that case, the default SourcePosition is returned.
104  SourcePosition IdentifierPosition() const {
105    return identifier_position_.source.IsValid() ? identifier_position_
106                                                 : position_;
107  }
108  void SetIdentifierPosition(const SourcePosition& position) {
109    identifier_position_ = position;
110  }
111
112  bool IsUserDefined() const { return is_user_defined_; }
113  void SetIsUserDefined(bool is_user_defined) {
114    is_user_defined_ = is_user_defined;
115  }
116
117 protected:
118  explicit Declarable(Kind kind) : kind_(kind) {}
119
120 private:
121  const Kind kind_;
122  Scope* const parent_scope_ = CurrentScope::Get();
123  SourcePosition position_ = CurrentSourcePosition::Get();
124  SourcePosition identifier_position_ = SourcePosition::Invalid();
125  bool is_user_defined_ = true;
126};
127
128#define DECLARE_DECLARABLE_BOILERPLATE(x, y)                  \
129  static x* cast(Declarable* declarable) {                    \
130    DCHECK(declarable->Is##x());                              \
131    return static_cast<x*>(declarable);                       \
132  }                                                           \
133  static const x* cast(const Declarable* declarable) {        \
134    DCHECK(declarable->Is##x());                              \
135    return static_cast<const x*>(declarable);                 \
136  }                                                           \
137  const char* type_name() const override { return #y; }       \
138  static x* DynamicCast(Declarable* declarable) {             \
139    if (!declarable) return nullptr;                          \
140    if (!declarable->Is##x()) return nullptr;                 \
141    return static_cast<x*>(declarable);                       \
142  }                                                           \
143  static const x* DynamicCast(const Declarable* declarable) { \
144    if (!declarable) return nullptr;                          \
145    if (!declarable->Is##x()) return nullptr;                 \
146    return static_cast<const x*>(declarable);                 \
147  }
148
149// Information about what code caused a specialization to exist. This is used
150// for error reporting.
151struct SpecializationRequester {
152  // The position of the expression that caused this specialization.
153  SourcePosition position;
154  // The Scope which contains the expression that caused this specialization.
155  // It may in turn also be within a specialization, which allows us to print
156  // the stack of requesters when an error occurs.
157  Scope* scope;
158  // The name of the specialization.
159  std::string name;
160
161  static SpecializationRequester None() {
162    return {SourcePosition::Invalid(), nullptr, ""};
163  }
164
165  bool IsNone() const {
166    return position == SourcePosition::Invalid() && scope == nullptr &&
167           name == "";
168  }
169  SpecializationRequester(SourcePosition position, Scope* scope,
170                          std::string name);
171};
172
173class Scope : public Declarable {
174 public:
175  DECLARE_DECLARABLE_BOILERPLATE(Scope, scope)
176  explicit Scope(Declarable::Kind kind) : Declarable(kind) {}
177
178  std::vector<Declarable*> LookupShallow(const QualifiedName& name) {
179    if (!name.HasNamespaceQualification()) return declarations_[name.name];
180    Scope* child = nullptr;
181    for (Declarable* declarable :
182         declarations_[name.namespace_qualification.front()]) {
183      if (Scope* scope = Scope::DynamicCast(declarable)) {
184        if (child != nullptr) {
185          ReportError("ambiguous reference to scope ",
186                      name.namespace_qualification.front());
187        }
188        child = scope;
189      }
190    }
191    if (child == nullptr) return {};
192    return child->LookupShallow(name.DropFirstNamespaceQualification());
193  }
194
195  std::vector<Declarable*> Lookup(const QualifiedName& name);
196  template <class T>
197  T* AddDeclarable(const std::string& name, T* declarable) {
198    declarations_[name].push_back(declarable);
199    return declarable;
200  }
201
202  const SpecializationRequester& GetSpecializationRequester() const {
203    return requester_;
204  }
205  void SetSpecializationRequester(const SpecializationRequester& requester) {
206    requester_ = requester;
207  }
208
209 private:
210  std::unordered_map<std::string, std::vector<Declarable*>> declarations_;
211
212  // If this Scope was created for specializing a generic type or callable,
213  // then {requester_} refers to the place that caused the specialization so we
214  // can construct useful error messages.
215  SpecializationRequester requester_ = SpecializationRequester::None();
216};
217
218class Namespace : public Scope {
219 public:
220  DECLARE_DECLARABLE_BOILERPLATE(Namespace, namespace)
221  explicit Namespace(const std::string& name)
222      : Scope(Declarable::kNamespace), name_(name) {}
223  const std::string& name() const { return name_; }
224  bool IsDefaultNamespace() const;
225  bool IsTestNamespace() const;
226
227 private:
228  std::string name_;
229};
230
231inline Namespace* CurrentNamespace() {
232  Scope* scope = CurrentScope::Get();
233  while (true) {
234    if (Namespace* n = Namespace::DynamicCast(scope)) {
235      return n;
236    }
237    scope = scope->ParentScope();
238  }
239}
240
241class Value : public Declarable {
242 public:
243  DECLARE_DECLARABLE_BOILERPLATE(Value, value)
244  const Identifier* name() const { return name_; }
245  virtual bool IsConst() const { return true; }
246  VisitResult value() const { return *value_; }
247  const Type* type() const { return type_; }
248
249  void set_value(VisitResult value) {
250    DCHECK(!value_);
251    value_ = value;
252  }
253
254 protected:
255  Value(Kind kind, const Type* type, Identifier* name)
256      : Declarable(kind), type_(type), name_(name) {}
257
258 private:
259  const Type* type_;
260  Identifier* name_;
261  base::Optional<VisitResult> value_;
262};
263
264class NamespaceConstant : public Value {
265 public:
266  DECLARE_DECLARABLE_BOILERPLATE(NamespaceConstant, constant)
267
268  const std::string& external_name() const { return external_name_; }
269  Expression* body() const { return body_; }
270
271 private:
272  friend class Declarations;
273  explicit NamespaceConstant(Identifier* constant_name,
274                             std::string external_name, const Type* type,
275                             Expression* body)
276      : Value(Declarable::kNamespaceConstant, type, constant_name),
277        external_name_(std::move(external_name)),
278        body_(body) {}
279
280  std::string external_name_;
281  Expression* body_;
282};
283
284class ExternConstant : public Value {
285 public:
286  DECLARE_DECLARABLE_BOILERPLATE(ExternConstant, constant)
287
288 private:
289  friend class Declarations;
290  explicit ExternConstant(Identifier* name, const Type* type, std::string value)
291      : Value(Declarable::kExternConstant, type, name) {
292    set_value(VisitResult(type, std::move(value)));
293  }
294};
295
296enum class OutputType {
297  kCSA,
298  kCC,
299  kCCDebug,
300};
301
302class Callable : public Scope {
303 public:
304  DECLARE_DECLARABLE_BOILERPLATE(Callable, callable)
305  const std::string& ExternalName() const { return external_name_; }
306  const std::string& ReadableName() const { return readable_name_; }
307  const Signature& signature() const { return signature_; }
308  bool IsTransitioning() const { return signature().transitioning; }
309  const NameVector& parameter_names() const {
310    return signature_.parameter_names;
311  }
312  bool HasReturnValue() const {
313    return !signature_.return_type->IsVoidOrNever();
314  }
315  void IncrementReturns() { ++returns_; }
316  bool HasReturns() const { return returns_; }
317  base::Optional<Statement*> body() const { return body_; }
318  bool IsExternal() const { return !body_.has_value(); }
319  virtual bool ShouldBeInlined(OutputType output_type) const {
320    // C++ output doesn't support exiting to labels, so functions with labels in
321    // the signature must be inlined.
322    return output_type == OutputType::kCC && !signature().labels.empty();
323  }
324  bool ShouldGenerateExternalCode(OutputType output_type) const {
325    return !ShouldBeInlined(output_type);
326  }
327
328  static std::string PrefixNameForCCOutput(const std::string& name) {
329    // If a Torque macro requires a C++ runtime function to be generated, then
330    // the generated function begins with this prefix to avoid any naming
331    // collisions with the generated CSA function for the same macro.
332    return "TqRuntime" + name;
333  }
334
335  static std::string PrefixNameForCCDebugOutput(const std::string& name) {
336    // If a Torque macro requires a C++ runtime function to be generated, then
337    // the generated function begins with this prefix to avoid any naming
338    // collisions with the generated CSA function for the same macro.
339    return "TqDebug" + name;
340  }
341
342  // Name to use in runtime C++ code.
343  virtual std::string CCName() const {
344    return PrefixNameForCCOutput(ExternalName());
345  }
346
347  // Name to use in debug C++ code.
348  virtual std::string CCDebugName() const {
349    return PrefixNameForCCDebugOutput(ExternalName());
350  }
351
352 protected:
353  Callable(Declarable::Kind kind, std::string external_name,
354           std::string readable_name, Signature signature,
355           base::Optional<Statement*> body)
356      : Scope(kind),
357        external_name_(std::move(external_name)),
358
359        readable_name_(std::move(readable_name)),
360        signature_(std::move(signature)),
361        returns_(0),
362        body_(body) {
363    DCHECK(!body || *body);
364  }
365
366 private:
367  std::string external_name_;
368  std::string readable_name_;
369  Signature signature_;
370  size_t returns_;
371  base::Optional<Statement*> body_;
372};
373
374class Macro : public Callable {
375 public:
376  DECLARE_DECLARABLE_BOILERPLATE(Macro, macro)
377  bool ShouldBeInlined(OutputType output_type) const override {
378    for (const LabelDeclaration& label : signature().labels) {
379      for (const Type* type : label.types) {
380        if (type->StructSupertype()) return true;
381      }
382    }
383    // Intrinsics that are used internally in Torque and implemented as torque
384    // code should be inlined and not generate C++ definitions.
385    if (ReadableName()[0] == '%') return true;
386    return Callable::ShouldBeInlined(output_type);
387  }
388
389  void SetUsed() { used_ = true; }
390  bool IsUsed() const { return used_; }
391
392 protected:
393  Macro(Declarable::Kind kind, std::string external_name,
394        std::string readable_name, const Signature& signature,
395        base::Optional<Statement*> body)
396      : Callable(kind, std::move(external_name), std::move(readable_name),
397                 signature, body),
398        used_(false) {
399    if (signature.parameter_types.var_args) {
400      ReportError("Varargs are not supported for macros.");
401    }
402  }
403
404 private:
405  bool used_;
406};
407
408class ExternMacro : public Macro {
409 public:
410  DECLARE_DECLARABLE_BOILERPLATE(ExternMacro, ExternMacro)
411
412  const std::string& external_assembler_name() const {
413    return external_assembler_name_;
414  }
415
416  std::string CCName() const override {
417    return "TorqueRuntimeMacroShims::" + external_assembler_name() +
418           "::" + ExternalName();
419  }
420
421  std::string CCDebugName() const override {
422    return "TorqueDebugMacroShims::" + external_assembler_name() +
423           "::" + ExternalName();
424  }
425
426 private:
427  friend class Declarations;
428  ExternMacro(const std::string& name, std::string external_assembler_name,
429              Signature signature)
430      : Macro(Declarable::kExternMacro, name, name, std::move(signature),
431              base::nullopt),
432        external_assembler_name_(std::move(external_assembler_name)) {}
433
434  std::string external_assembler_name_;
435};
436
437class TorqueMacro : public Macro {
438 public:
439  DECLARE_DECLARABLE_BOILERPLATE(TorqueMacro, TorqueMacro)
440  bool IsExportedToCSA() const { return exported_to_csa_; }
441  std::string CCName() const override {
442    // Exported functions must have unique and C++-friendly readable names, so
443    // prefer those wherever possible.
444    return PrefixNameForCCOutput(IsExportedToCSA() ? ReadableName()
445                                                   : ExternalName());
446  }
447  std::string CCDebugName() const override {
448    // Exported functions must have unique and C++-friendly readable names, so
449    // prefer those wherever possible.
450    return PrefixNameForCCDebugOutput(IsExportedToCSA() ? ReadableName()
451                                                        : ExternalName());
452  }
453
454 protected:
455  TorqueMacro(Declarable::Kind kind, std::string external_name,
456              std::string readable_name, const Signature& signature,
457              base::Optional<Statement*> body, bool is_user_defined,
458              bool exported_to_csa)
459      : Macro(kind, std::move(external_name), std::move(readable_name),
460              signature, body),
461        exported_to_csa_(exported_to_csa) {
462    SetIsUserDefined(is_user_defined);
463  }
464
465 private:
466  friend class Declarations;
467  TorqueMacro(std::string external_name, std::string readable_name,
468              const Signature& signature, base::Optional<Statement*> body,
469              bool is_user_defined, bool exported_to_csa)
470      : TorqueMacro(Declarable::kTorqueMacro, std::move(external_name),
471                    std::move(readable_name), signature, body, is_user_defined,
472                    exported_to_csa) {}
473
474  bool exported_to_csa_ = false;
475};
476
477class Method : public TorqueMacro {
478 public:
479  DECLARE_DECLARABLE_BOILERPLATE(Method, Method)
480  bool ShouldBeInlined(OutputType output_type) const override {
481    return Macro::ShouldBeInlined(output_type) ||
482           signature()
483               .parameter_types.types[signature().implicit_count]
484               ->IsStructType();
485  }
486  AggregateType* aggregate_type() const { return aggregate_type_; }
487
488 private:
489  friend class Declarations;
490  Method(AggregateType* aggregate_type, std::string external_name,
491         std::string readable_name, const Signature& signature, Statement* body)
492      : TorqueMacro(Declarable::kMethod, std::move(external_name),
493                    std::move(readable_name), signature, body, true, false),
494        aggregate_type_(aggregate_type) {}
495  AggregateType* aggregate_type_;
496};
497
498class Builtin : public Callable {
499 public:
500  enum Kind { kStub, kFixedArgsJavaScript, kVarArgsJavaScript };
501  DECLARE_DECLARABLE_BOILERPLATE(Builtin, builtin)
502  Kind kind() const { return kind_; }
503  bool IsStub() const { return kind_ == kStub; }
504  bool IsVarArgsJavaScript() const { return kind_ == kVarArgsJavaScript; }
505  bool IsFixedArgsJavaScript() const { return kind_ == kFixedArgsJavaScript; }
506
507 private:
508  friend class Declarations;
509  Builtin(std::string external_name, std::string readable_name,
510          Builtin::Kind kind, const Signature& signature,
511          base::Optional<Statement*> body)
512      : Callable(Declarable::kBuiltin, std::move(external_name),
513                 std::move(readable_name), signature, body),
514        kind_(kind) {}
515
516  Kind kind_;
517};
518
519class RuntimeFunction : public Callable {
520 public:
521  DECLARE_DECLARABLE_BOILERPLATE(RuntimeFunction, runtime)
522
523 private:
524  friend class Declarations;
525  RuntimeFunction(const std::string& name, const Signature& signature)
526      : Callable(Declarable::kRuntimeFunction, name, name, signature,
527                 base::nullopt) {}
528};
529
530class Intrinsic : public Callable {
531 public:
532  DECLARE_DECLARABLE_BOILERPLATE(Intrinsic, intrinsic)
533
534 private:
535  friend class Declarations;
536  Intrinsic(std::string name, const Signature& signature)
537      : Callable(Declarable::kIntrinsic, name, name, signature, base::nullopt) {
538    if (signature.parameter_types.var_args) {
539      ReportError("Varargs are not supported for intrinsics.");
540    }
541  }
542};
543
544class TypeConstraint {
545 public:
546  base::Optional<std::string> IsViolated(const Type*) const;
547
548  static TypeConstraint Unconstrained() { return {}; }
549  static TypeConstraint SubtypeConstraint(const Type* upper_bound) {
550    TypeConstraint result;
551    result.upper_bound = {upper_bound};
552    return result;
553  }
554
555 private:
556  base::Optional<const Type*> upper_bound;
557};
558
559base::Optional<std::string> FindConstraintViolation(
560    const std::vector<const Type*>& types,
561    const std::vector<TypeConstraint>& constraints);
562
563std::vector<TypeConstraint> ComputeConstraints(
564    Scope* scope, const GenericParameters& parameters);
565
566template <class SpecializationType, class DeclarationType>
567class GenericDeclarable : public Declarable {
568 private:
569  using Map = std::unordered_map<TypeVector, SpecializationType,
570                                 base::hash<TypeVector>>;
571
572 public:
573  void AddSpecialization(const TypeVector& type_arguments,
574                         SpecializationType specialization) {
575    DCHECK_EQ(0, specializations_.count(type_arguments));
576    if (auto violation =
577            FindConstraintViolation(type_arguments, Constraints())) {
578      Error(*violation).Throw();
579    }
580    specializations_[type_arguments] = specialization;
581  }
582  base::Optional<SpecializationType> GetSpecialization(
583      const TypeVector& type_arguments) const {
584    auto it = specializations_.find(type_arguments);
585    if (it != specializations_.end()) return it->second;
586    return base::nullopt;
587  }
588
589  using iterator = typename Map::const_iterator;
590  iterator begin() const { return specializations_.begin(); }
591  iterator end() const { return specializations_.end(); }
592
593  const std::string& name() const { return name_; }
594  auto declaration() const { return generic_declaration_->declaration; }
595  const GenericParameters& generic_parameters() const {
596    return generic_declaration_->generic_parameters;
597  }
598
599  const std::vector<TypeConstraint>& Constraints() {
600    if (!constraints_)
601      constraints_ = {ComputeConstraints(ParentScope(), generic_parameters())};
602    return *constraints_;
603  }
604
605 protected:
606  GenericDeclarable(Declarable::Kind kind, const std::string& name,
607                    DeclarationType generic_declaration)
608      : Declarable(kind),
609        name_(name),
610        generic_declaration_(generic_declaration) {
611    DCHECK(!generic_declaration->generic_parameters.empty());
612  }
613
614 private:
615  std::string name_;
616  DeclarationType generic_declaration_;
617  Map specializations_;
618  base::Optional<std::vector<TypeConstraint>> constraints_;
619};
620
621class GenericCallable
622    : public GenericDeclarable<Callable*, GenericCallableDeclaration*> {
623 public:
624  DECLARE_DECLARABLE_BOILERPLATE(GenericCallable, generic_callable)
625
626  base::Optional<Statement*> CallableBody();
627
628  TypeArgumentInference InferSpecializationTypes(
629      const TypeVector& explicit_specialization_types,
630      const std::vector<base::Optional<const Type*>>& arguments);
631
632 private:
633  friend class Declarations;
634  GenericCallable(const std::string& name,
635                  GenericCallableDeclaration* generic_declaration)
636      : GenericDeclarable<Callable*, GenericCallableDeclaration*>(
637            Declarable::kGenericCallable, name, generic_declaration) {}
638};
639
640class GenericType
641    : public GenericDeclarable<const Type*, GenericTypeDeclaration*> {
642 public:
643  DECLARE_DECLARABLE_BOILERPLATE(GenericType, generic_type)
644
645 private:
646  friend class Declarations;
647  GenericType(const std::string& name,
648              GenericTypeDeclaration* generic_declaration)
649      : GenericDeclarable<const Type*, GenericTypeDeclaration*>(
650            Declarable::kGenericType, name, generic_declaration) {}
651};
652
653class TypeAlias : public Declarable {
654 public:
655  DECLARE_DECLARABLE_BOILERPLATE(TypeAlias, type_alias)
656
657  const Type* type() const {
658    if (type_) return *type_;
659    return Resolve();
660  }
661  const Type* Resolve() const;
662  bool IsRedeclaration() const { return redeclaration_; }
663  SourcePosition GetDeclarationPosition() const {
664    return declaration_position_;
665  }
666
667 private:
668  friend class Declarations;
669  friend class TypeVisitor;
670
671  explicit TypeAlias(
672      const Type* type, bool redeclaration,
673      SourcePosition declaration_position = SourcePosition::Invalid())
674      : Declarable(Declarable::kTypeAlias),
675        type_(type),
676        redeclaration_(redeclaration),
677        declaration_position_(declaration_position) {}
678  explicit TypeAlias(
679      TypeDeclaration* type, bool redeclaration,
680      SourcePosition declaration_position = SourcePosition::Invalid())
681      : Declarable(Declarable::kTypeAlias),
682        delayed_(type),
683        redeclaration_(redeclaration),
684        declaration_position_(declaration_position) {}
685
686  mutable bool being_resolved_ = false;
687  mutable base::Optional<TypeDeclaration*> delayed_;
688  mutable base::Optional<const Type*> type_;
689  bool redeclaration_;
690  const SourcePosition declaration_position_;
691};
692
693std::ostream& operator<<(std::ostream& os, const Callable& m);
694std::ostream& operator<<(std::ostream& os, const Builtin& b);
695std::ostream& operator<<(std::ostream& os, const RuntimeFunction& b);
696std::ostream& operator<<(std::ostream& os, const GenericCallable& g);
697
698#undef DECLARE_DECLARABLE_BOILERPLATE
699
700}  // namespace torque
701}  // namespace internal
702}  // namespace v8
703
704#endif  // V8_TORQUE_DECLARABLE_H_
705