• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_IMPLEMENTATION_VISITOR_H_
6 #define V8_TORQUE_IMPLEMENTATION_VISITOR_H_
7 
8 #include <memory>
9 #include <string>
10 
11 #include "src/base/macros.h"
12 #include "src/torque/ast.h"
13 #include "src/torque/cfg.h"
14 #include "src/torque/cpp-builder.h"
15 #include "src/torque/declarations.h"
16 #include "src/torque/global-context.h"
17 #include "src/torque/type-oracle.h"
18 #include "src/torque/types.h"
19 #include "src/torque/utils.h"
20 
21 namespace v8 {
22 namespace internal {
23 namespace torque {
24 
25 template <typename T>
26 class Binding;
27 class LocalValue;
28 class ImplementationVisitor;
29 
30 // LocationReference is the representation of an l-value, so a value that might
31 // allow for assignment. For uniformity, this class can also represent
32 // unassignable temporaries. Assignable values fall in two categories:
33 //   - stack ranges that represent mutable variables, including structs.
34 //   - field or element access expressions that generate operator calls.
35 class LocationReference {
36  public:
37   // An assignable stack range.
38   static LocationReference VariableAccess(
39       VisitResult variable,
40       base::Optional<Binding<LocalValue>*> binding = base::nullopt) {
41     DCHECK(variable.IsOnStack());
42     LocationReference result;
43     result.variable_ = std::move(variable);
44     result.binding_ = binding;
45     return result;
46   }
47   // An unassignable value. {description} is only used for error messages.
Temporary(VisitResult temporary,std::string description)48   static LocationReference Temporary(VisitResult temporary,
49                                      std::string description) {
50     LocationReference result;
51     result.temporary_ = std::move(temporary);
52     result.temporary_description_ = std::move(description);
53     return result;
54   }
55   // A heap reference, that is, a tagged value and an offset to encode an inner
56   // pointer.
HeapReference(VisitResult heap_reference)57   static LocationReference HeapReference(VisitResult heap_reference) {
58     LocationReference result;
59     DCHECK(TypeOracle::MatchReferenceGeneric(heap_reference.type()));
60     result.heap_reference_ = std::move(heap_reference);
61     return result;
62   }
63   // A reference to an array on the heap. That is, a tagged value, an offset to
64   // encode an inner pointer, and the number of elements.
HeapSlice(VisitResult heap_slice)65   static LocationReference HeapSlice(VisitResult heap_slice) {
66     LocationReference result;
67     DCHECK(Type::MatchUnaryGeneric(heap_slice.type(),
68                                    TypeOracle::GetConstSliceGeneric()) ||
69            Type::MatchUnaryGeneric(heap_slice.type(),
70                                    TypeOracle::GetMutableSliceGeneric()));
71     result.heap_slice_ = std::move(heap_slice);
72     return result;
73   }
ArrayAccess(VisitResult base,VisitResult offset)74   static LocationReference ArrayAccess(VisitResult base, VisitResult offset) {
75     LocationReference result;
76     result.eval_function_ = std::string{"[]"};
77     result.assign_function_ = std::string{"[]="};
78     result.call_arguments_ = {base, offset};
79     return result;
80   }
FieldAccess(VisitResult object,std::string fieldname)81   static LocationReference FieldAccess(VisitResult object,
82                                        std::string fieldname) {
83     LocationReference result;
84     result.eval_function_ = "." + fieldname;
85     result.assign_function_ = "." + fieldname + "=";
86     result.call_arguments_ = {object};
87     return result;
88   }
BitFieldAccess(const LocationReference & object,BitField field)89   static LocationReference BitFieldAccess(const LocationReference& object,
90                                           BitField field) {
91     LocationReference result;
92     result.bit_field_struct_ = std::make_shared<LocationReference>(object);
93     result.bit_field_ = std::move(field);
94     return result;
95   }
96 
IsConst()97   bool IsConst() const {
98     if (IsHeapReference()) {
99       bool is_const;
100       bool success =
101           TypeOracle::MatchReferenceGeneric(heap_reference().type(), &is_const)
102               .has_value();
103       CHECK(success);
104       return is_const;
105     }
106     return IsTemporary();
107   }
108 
IsVariableAccess()109   bool IsVariableAccess() const { return variable_.has_value(); }
variable()110   const VisitResult& variable() const {
111     DCHECK(IsVariableAccess());
112     return *variable_;
113   }
IsTemporary()114   bool IsTemporary() const { return temporary_.has_value(); }
temporary()115   const VisitResult& temporary() const {
116     DCHECK(IsTemporary());
117     return *temporary_;
118   }
IsHeapReference()119   bool IsHeapReference() const { return heap_reference_.has_value(); }
heap_reference()120   const VisitResult& heap_reference() const {
121     DCHECK(IsHeapReference());
122     return *heap_reference_;
123   }
IsHeapSlice()124   bool IsHeapSlice() const { return heap_slice_.has_value(); }
heap_slice()125   const VisitResult& heap_slice() const {
126     DCHECK(IsHeapSlice());
127     return *heap_slice_;
128   }
IsBitFieldAccess()129   bool IsBitFieldAccess() const {
130     bool is_bitfield_access = bit_field_struct_ != nullptr;
131     DCHECK_EQ(is_bitfield_access, bit_field_.has_value());
132     return is_bitfield_access;
133   }
bit_field_struct_location()134   const LocationReference& bit_field_struct_location() const {
135     DCHECK(IsBitFieldAccess());
136     return *bit_field_struct_;
137   }
bit_field()138   const BitField& bit_field() const {
139     DCHECK(IsBitFieldAccess());
140     return *bit_field_;
141   }
142 
ReferencedType()143   base::Optional<const Type*> ReferencedType() const {
144     if (IsHeapReference()) {
145       return *TypeOracle::MatchReferenceGeneric(heap_reference().type());
146     }
147     if (IsHeapSlice()) {
148       if (auto type = Type::MatchUnaryGeneric(
149               heap_slice().type(), TypeOracle::GetMutableSliceGeneric())) {
150         return *type;
151       }
152       return Type::MatchUnaryGeneric(heap_slice().type(),
153                                      TypeOracle::GetConstSliceGeneric());
154     }
155     if (IsBitFieldAccess()) {
156       return bit_field_->name_and_type.type;
157     }
158     if (IsVariableAccess() || IsHeapSlice() || IsTemporary()) {
159       return GetVisitResult().type();
160     }
161     return base::nullopt;
162   }
163 
GetVisitResult()164   const VisitResult& GetVisitResult() const {
165     if (IsVariableAccess()) return variable();
166     if (IsHeapSlice()) return heap_slice();
167     DCHECK(IsTemporary());
168     return temporary();
169   }
170 
171   // For error reporting.
temporary_description()172   const std::string& temporary_description() const {
173     DCHECK(IsTemporary());
174     return *temporary_description_;
175   }
176 
IsCallAccess()177   bool IsCallAccess() const {
178     bool is_call_access = eval_function_.has_value();
179     DCHECK_EQ(is_call_access, assign_function_.has_value());
180     return is_call_access;
181   }
call_arguments()182   const VisitResultVector& call_arguments() const {
183     DCHECK(IsCallAccess());
184     return call_arguments_;
185   }
eval_function()186   const std::string& eval_function() const {
187     DCHECK(IsCallAccess());
188     return *eval_function_;
189   }
assign_function()190   const std::string& assign_function() const {
191     DCHECK(IsCallAccess());
192     return *assign_function_;
193   }
binding()194   base::Optional<Binding<LocalValue>*> binding() const {
195     DCHECK(IsVariableAccess());
196     return binding_;
197   }
198 
199  private:
200   base::Optional<VisitResult> variable_;
201   base::Optional<VisitResult> temporary_;
202   base::Optional<std::string> temporary_description_;
203   base::Optional<VisitResult> heap_reference_;
204   base::Optional<VisitResult> heap_slice_;
205   base::Optional<std::string> eval_function_;
206   base::Optional<std::string> assign_function_;
207   VisitResultVector call_arguments_;
208   base::Optional<Binding<LocalValue>*> binding_;
209 
210   // The location of the bitfield struct that contains this bitfield, if this
211   // reference is a bitfield access. Uses a shared_ptr so that LocationReference
212   // is copyable, allowing us to set this field equal to a copy of a
213   // stack-allocated LocationReference.
214   std::shared_ptr<const LocationReference> bit_field_struct_;
215   base::Optional<BitField> bit_field_;
216 
217   LocationReference() = default;
218 };
219 
220 struct InitializerResults {
221   std::vector<Identifier*> names;
222   std::map<std::string, VisitResult> field_value_map;
223 };
224 
225 struct LayoutForInitialization {
226   std::map<std::string, VisitResult> array_lengths;
227   std::map<std::string, VisitResult> offsets;
228   VisitResult size;
229 };
230 
231 extern uint64_t next_unique_binding_index;
232 
233 template <class T>
234 class Binding;
235 
236 template <class T>
237 class BindingsManager {
238  public:
TryLookup(const std::string & name)239   base::Optional<Binding<T>*> TryLookup(const std::string& name) {
240     if (StartsWithSingleUnderscore(name)) {
241       Error("Trying to reference '", name, "' which is marked as unused.")
242           .Throw();
243     }
244     auto binding = current_bindings_[name];
245     if (binding) {
246       (*binding)->SetUsed();
247     }
248     return binding;
249   }
250 
251  private:
252   friend class Binding<T>;
253   std::unordered_map<std::string, base::Optional<Binding<T>*>>
254       current_bindings_;
255 };
256 
257 template <class T>
258 class Binding : public T {
259  public:
260   template <class... Args>
Binding(BindingsManager<T> * manager,const std::string & name,Args &&...args)261   Binding(BindingsManager<T>* manager, const std::string& name, Args&&... args)
262       : T(std::forward<Args>(args)...),
263         manager_(manager),
264         name_(name),
265         previous_binding_(this),
266         used_(false),
267         written_(false),
268         unique_index_(next_unique_binding_index++) {
269     std::swap(previous_binding_, manager_->current_bindings_[name]);
270   }
271   template <class... Args>
Binding(BindingsManager<T> * manager,const Identifier * name,Args &&...args)272   Binding(BindingsManager<T>* manager, const Identifier* name, Args&&... args)
273       : Binding(manager, name->value, std::forward<Args>(args)...) {
274     declaration_position_ = name->pos;
275   }
~Binding()276   ~Binding() {
277     if (!used_ && !SkipLintCheck()) {
278       Lint(BindingTypeString(), "'", name_,
279            "' is never used. Prefix with '_' if this is intentional.")
280           .Position(declaration_position_);
281     }
282 
283     if (CheckWritten() && !written_ && !SkipLintCheck()) {
284       Lint(BindingTypeString(), "'", name_,
285            "' is never assigned to. Use 'const' instead of 'let'.")
286           .Position(declaration_position_);
287     }
288 
289     manager_->current_bindings_[name_] = previous_binding_;
290   }
291   Binding(const Binding&) = delete;
292   Binding& operator=(const Binding&) = delete;
293 
294   std::string BindingTypeString() const;
295   bool CheckWritten() const;
296 
name()297   const std::string& name() const { return name_; }
declaration_position()298   SourcePosition declaration_position() const { return declaration_position_; }
299 
Used()300   bool Used() const { return used_; }
SetUsed()301   void SetUsed() { used_ = true; }
302 
Written()303   bool Written() const { return written_; }
SetWritten()304   void SetWritten() { written_ = true; }
305 
unique_index()306   uint64_t unique_index() const { return unique_index_; }
307 
308  private:
SkipLintCheck()309   bool SkipLintCheck() const { return name_.length() > 0 && name_[0] == '_'; }
310 
311   BindingsManager<T>* manager_;
312   const std::string name_;
313   base::Optional<Binding*> previous_binding_;
314   SourcePosition declaration_position_ = CurrentSourcePosition::Get();
315   bool used_;
316   bool written_;
317   uint64_t unique_index_;
318 };
319 
320 template <class T>
321 class BlockBindings {
322  public:
BlockBindings(BindingsManager<T> * manager)323   explicit BlockBindings(BindingsManager<T>* manager) : manager_(manager) {}
324   Binding<T>* Add(std::string name, T value, bool mark_as_used = false) {
325     ReportErrorIfAlreadyBound(name);
326     auto binding =
327         std::make_unique<Binding<T>>(manager_, name, std::move(value));
328     Binding<T>* result = binding.get();
329     if (mark_as_used) binding->SetUsed();
330     bindings_.push_back(std::move(binding));
331     return result;
332   }
333 
334   Binding<T>* Add(const Identifier* name, T value, bool mark_as_used = false) {
335     ReportErrorIfAlreadyBound(name->value);
336     auto binding =
337         std::make_unique<Binding<T>>(manager_, name, std::move(value));
338     Binding<T>* result = binding.get();
339     if (mark_as_used) binding->SetUsed();
340     bindings_.push_back(std::move(binding));
341     return result;
342   }
343 
bindings()344   std::vector<Binding<T>*> bindings() const {
345     std::vector<Binding<T>*> result;
346     result.reserve(bindings_.size());
347     for (auto& b : bindings_) {
348       result.push_back(b.get());
349     }
350     return result;
351   }
352 
353  private:
ReportErrorIfAlreadyBound(const std::string & name)354   void ReportErrorIfAlreadyBound(const std::string& name) {
355     for (const auto& binding : bindings_) {
356       if (binding->name() == name) {
357         ReportError(
358             "redeclaration of name \"", name,
359             "\" in the same block is illegal, previous declaration at: ",
360             binding->declaration_position());
361       }
362     }
363   }
364 
365   BindingsManager<T>* manager_;
366   std::vector<std::unique_ptr<Binding<T>>> bindings_;
367 };
368 
369 class LocalValue {
370  public:
LocalValue(LocationReference reference)371   explicit LocalValue(LocationReference reference)
372       : value(std::move(reference)) {}
LocalValue(std::string inaccessible_explanation)373   explicit LocalValue(std::string inaccessible_explanation)
374       : inaccessible_explanation(std::move(inaccessible_explanation)) {}
LocalValue(std::function<LocationReference ()> lazy)375   explicit LocalValue(std::function<LocationReference()> lazy)
376       : lazy(std::move(lazy)) {}
377 
GetLocationReference(Binding<LocalValue> * binding)378   LocationReference GetLocationReference(Binding<LocalValue>* binding) {
379     if (value) {
380       const LocationReference& ref = *value;
381       if (ref.IsVariableAccess()) {
382         // Attach the binding to enable the never-assigned-to lint check.
383         return LocationReference::VariableAccess(ref.GetVisitResult(), binding);
384       }
385       return ref;
386     } else if (lazy) {
387       return (*lazy)();
388     } else {
389       Error("Cannot access ", binding->name(), ": ", inaccessible_explanation)
390           .Throw();
391     }
392   }
393 
IsAccessibleNonLazy()394   bool IsAccessibleNonLazy() const { return value.has_value(); }
395 
396  private:
397   base::Optional<LocationReference> value;
398   base::Optional<std::function<LocationReference()>> lazy;
399   std::string inaccessible_explanation;
400 };
401 
402 struct LocalLabel {
403   Block* block;
404   std::vector<const Type*> parameter_types;
405 
406   explicit LocalLabel(Block* block,
407                       std::vector<const Type*> parameter_types = {})
blockLocalLabel408       : block(block), parameter_types(std::move(parameter_types)) {}
409 };
410 
411 template <>
BindingTypeString()412 inline std::string Binding<LocalValue>::BindingTypeString() const {
413   return "Variable ";
414 }
415 template <>
CheckWritten()416 inline bool Binding<LocalValue>::CheckWritten() const {
417   // Do the check only for non-const variables and non struct types.
418   auto binding = *manager_->current_bindings_[name_];
419   if (!binding->IsAccessibleNonLazy()) return false;
420   const LocationReference& ref = binding->GetLocationReference(binding);
421   if (!ref.IsVariableAccess()) return false;
422   return !ref.GetVisitResult().type()->StructSupertype();
423 }
424 template <>
BindingTypeString()425 inline std::string Binding<LocalLabel>::BindingTypeString() const {
426   return "Label ";
427 }
428 template <>
CheckWritten()429 inline bool Binding<LocalLabel>::CheckWritten() const {
430   return false;
431 }
432 
433 struct Arguments {
434   VisitResultVector parameters;
435   std::vector<Binding<LocalLabel>*> labels;
436 };
437 
438 // Determine if a callable should be considered as an overload.
439 bool IsCompatibleSignature(const Signature& sig, const TypeVector& types,
440                            size_t label_count);
441 
442 class ImplementationVisitor {
443  public:
444   void GenerateBuiltinDefinitionsAndInterfaceDescriptors(
445       const std::string& output_directory);
446   void GenerateVisitorLists(const std::string& output_directory);
447   void GenerateBitFields(const std::string& output_directory);
448   void GeneratePrintDefinitions(const std::string& output_directory);
449   void GenerateClassDefinitions(const std::string& output_directory);
450   void GenerateBodyDescriptors(const std::string& output_directory);
451   void GenerateInstanceTypes(const std::string& output_directory);
452   void GenerateClassVerifiers(const std::string& output_directory);
453   void GenerateEnumVerifiers(const std::string& output_directory);
454   void GenerateClassDebugReaders(const std::string& output_directory);
455   void GenerateExportedMacrosAssembler(const std::string& output_directory);
456   void GenerateCSATypes(const std::string& output_directory);
457 
458   VisitResult Visit(Expression* expr);
459   const Type* Visit(Statement* stmt);
460 
461   template <typename T>
462   void CheckInitializersWellformed(
463       const std::string& aggregate_name, const std::vector<T>& aggregate_fields,
464       const std::vector<NameAndExpression>& initializers,
465       bool ignore_first_field = false) {
466     size_t fields_offset = ignore_first_field ? 1 : 0;
467     size_t fields_size = aggregate_fields.size() - fields_offset;
468     for (size_t i = 0; i < std::min(fields_size, initializers.size()); i++) {
469       const std::string& field_name =
470           aggregate_fields[i + fields_offset].name_and_type.name;
471       Identifier* found_name = initializers[i].name;
472       if (field_name != found_name->value) {
473         Error("Expected field name \"", field_name, "\" instead of \"",
474               found_name->value, "\"")
475             .Position(found_name->pos)
476             .Throw();
477       }
478     }
479     if (fields_size != initializers.size()) {
480       ReportError("expected ", fields_size, " initializers for ",
481                   aggregate_name, " found ", initializers.size());
482     }
483   }
484 
485   InitializerResults VisitInitializerResults(
486       const ClassType* class_type,
487       const std::vector<NameAndExpression>& expressions);
488   LocationReference GenerateFieldReference(
489       VisitResult object, const Field& field, const ClassType* class_type,
490       bool treat_optional_as_indexed = false);
491   LocationReference GenerateFieldReferenceForInit(
492       VisitResult object, const Field& field,
493       const LayoutForInitialization& layout);
494   VisitResult GenerateArrayLength(
495       Expression* array_length, Namespace* nspace,
496       const std::map<std::string, LocalValue>& bindings);
497   VisitResult GenerateArrayLength(VisitResult object, const Field& field);
498   VisitResult GenerateArrayLength(const ClassType* class_type,
499                                   const InitializerResults& initializer_results,
500                                   const Field& field);
501   LayoutForInitialization GenerateLayoutForInitialization(
502       const ClassType* class_type,
503       const InitializerResults& initializer_results);
504 
505   void InitializeClass(const ClassType* class_type, VisitResult allocate_result,
506                        const InitializerResults& initializer_results,
507                        const LayoutForInitialization& layout);
508 
509   VisitResult Visit(StructExpression* decl);
510 
511   LocationReference GetLocationReference(Expression* location);
512   LocationReference LookupLocalValue(const std::string& name);
513   LocationReference GetLocationReference(IdentifierExpression* expr);
514   LocationReference GetLocationReference(DereferenceExpression* expr);
515   LocationReference GetLocationReference(FieldAccessExpression* expr);
516   LocationReference GenerateFieldAccess(
517       LocationReference reference, const std::string& fieldname,
518       bool ignore_stuct_field_constness = false,
519       base::Optional<SourcePosition> pos = {});
520   LocationReference GetLocationReference(ElementAccessExpression* expr);
521   LocationReference GenerateReferenceToItemInHeapSlice(LocationReference slice,
522                                                        VisitResult index);
523 
524   VisitResult GenerateFetchFromLocation(const LocationReference& reference);
525 
526   VisitResult GetBuiltinCode(Builtin* builtin);
527 
528   VisitResult Visit(LocationExpression* expr);
529   VisitResult Visit(FieldAccessExpression* expr);
530 
531   void VisitAllDeclarables();
532   void Visit(Declarable* delarable, base::Optional<SourceId> file = {});
533   void Visit(TypeAlias* decl);
534   VisitResult InlineMacro(Macro* macro,
535                           base::Optional<LocationReference> this_reference,
536                           const std::vector<VisitResult>& arguments,
537                           const std::vector<Block*> label_blocks);
538   void VisitMacroCommon(Macro* macro);
Visit(ExternMacro * macro)539   void Visit(ExternMacro* macro) {}
540   void Visit(TorqueMacro* macro);
541   void Visit(Method* macro);
542   void Visit(Builtin* builtin);
543   void Visit(NamespaceConstant* decl);
544 
545   VisitResult Visit(CallExpression* expr, bool is_tail = false);
546   VisitResult Visit(CallMethodExpression* expr);
547   VisitResult Visit(IntrinsicCallExpression* intrinsic);
548   const Type* Visit(TailCallStatement* stmt);
549 
550   VisitResult Visit(ConditionalExpression* expr);
551 
552   VisitResult Visit(LogicalOrExpression* expr);
553   VisitResult Visit(LogicalAndExpression* expr);
554 
555   VisitResult Visit(IncrementDecrementExpression* expr);
556   VisitResult Visit(AssignmentExpression* expr);
557   VisitResult Visit(StringLiteralExpression* expr);
558   VisitResult Visit(FloatingPointLiteralExpression* expr);
559   VisitResult Visit(IntegerLiteralExpression* expr);
560   VisitResult Visit(AssumeTypeImpossibleExpression* expr);
561   VisitResult Visit(TryLabelExpression* expr);
562   VisitResult Visit(StatementExpression* expr);
563   VisitResult Visit(NewExpression* expr);
564   VisitResult Visit(SpreadExpression* expr);
565 
566   const Type* Visit(ReturnStatement* stmt);
567   const Type* Visit(GotoStatement* stmt);
568   const Type* Visit(IfStatement* stmt);
569   const Type* Visit(WhileStatement* stmt);
570   const Type* Visit(BreakStatement* stmt);
571   const Type* Visit(ContinueStatement* stmt);
572   const Type* Visit(ForLoopStatement* stmt);
573   const Type* Visit(VarDeclarationStatement* stmt);
574   const Type* Visit(VarDeclarationStatement* stmt,
575                     BlockBindings<LocalValue>* block_bindings);
576   const Type* Visit(BlockStatement* block);
577   const Type* Visit(ExpressionStatement* stmt);
578   const Type* Visit(DebugStatement* stmt);
579   const Type* Visit(AssertStatement* stmt);
580 
581   void BeginGeneratedFiles();
582   void EndGeneratedFiles();
583   void BeginDebugMacrosFile();
584   void EndDebugMacrosFile();
585 
586   void GenerateImplementation(const std::string& dir);
587 
588   DECLARE_CONTEXTUAL_VARIABLE(ValueBindingsManager,
589                               BindingsManager<LocalValue>);
590   DECLARE_CONTEXTUAL_VARIABLE(LabelBindingsManager,
591                               BindingsManager<LocalLabel>);
592   DECLARE_CONTEXTUAL_VARIABLE(CurrentCallable, Callable*);
593   DECLARE_CONTEXTUAL_VARIABLE(CurrentFileStreams,
594                               GlobalContext::PerFileStreams*);
595   DECLARE_CONTEXTUAL_VARIABLE(CurrentReturnValue, base::Optional<VisitResult>);
596 
597   // A BindingsManagersScope has to be active for local bindings to be created.
598   // Shadowing an existing BindingsManagersScope by creating a new one hides all
599   // existing bindings while the additional BindingsManagersScope is active.
600   struct BindingsManagersScope {
601     ValueBindingsManager::Scope value_bindings_manager;
602     LabelBindingsManager::Scope label_bindings_manager;
603   };
604 
SetDryRun(bool is_dry_run)605   void SetDryRun(bool is_dry_run) { is_dry_run_ = is_dry_run; }
606 
607  private:
608   base::Optional<Block*> GetCatchBlock();
609   void GenerateCatchBlock(base::Optional<Block*> catch_block);
610 
611   // {StackScope} records the stack height at creation time and reconstructs it
612   // when being destructed by emitting a {DeleteRangeInstruction}, except for
613   // the slots protected by {StackScope::Yield}. Calling {Yield(v)} deletes all
614   // slots above the initial stack height except for the slots of {v}, which are
615   // moved to form the only slots above the initial height and marks them to
616   // survive destruction of the {StackScope}. A typical pattern is the
617   // following:
618   //
619   // VisitResult result;
620   // {
621   //   StackScope stack_scope(this);
622   //   // ... create temporary slots ...
623   //   result = stack_scope.Yield(surviving_slots);
624   // }
625   class V8_NODISCARD StackScope {
626    public:
StackScope(ImplementationVisitor * visitor)627     explicit StackScope(ImplementationVisitor* visitor) : visitor_(visitor) {
628       base_ = visitor_->assembler().CurrentStack().AboveTop();
629     }
Yield(VisitResult result)630     VisitResult Yield(VisitResult result) {
631       DCHECK(!closed_);
632       closed_ = true;
633       if (!result.IsOnStack()) {
634         if (!visitor_->assembler().CurrentBlockIsComplete()) {
635           visitor_->assembler().DropTo(base_);
636         }
637         return result;
638       }
639       DCHECK_LE(base_, result.stack_range().begin());
640       DCHECK_LE(result.stack_range().end(),
641                 visitor_->assembler().CurrentStack().AboveTop());
642       visitor_->assembler().DropTo(result.stack_range().end());
643       visitor_->assembler().DeleteRange(
644           StackRange{base_, result.stack_range().begin()});
645       base_ = visitor_->assembler().CurrentStack().AboveTop();
646       return VisitResult(result.type(), visitor_->assembler().TopRange(
647                                             result.stack_range().Size()));
648     }
649 
Close()650     void Close() {
651       DCHECK(!closed_);
652       closed_ = true;
653       if (!visitor_->assembler().CurrentBlockIsComplete()) {
654         visitor_->assembler().DropTo(base_);
655       }
656     }
657 
~StackScope()658     ~StackScope() {
659       if (closed_) {
660         DCHECK_IMPLIES(
661             !visitor_->assembler().CurrentBlockIsComplete(),
662             base_ == visitor_->assembler().CurrentStack().AboveTop());
663       } else {
664         Close();
665       }
666     }
667 
668    private:
669     ImplementationVisitor* visitor_;
670     BottomOffset base_;
671     bool closed_ = false;
672   };
673 
674   class BreakContinueActivator {
675    public:
BreakContinueActivator(Block * break_block,Block * continue_block)676     BreakContinueActivator(Block* break_block, Block* continue_block)
677         : break_binding_{&LabelBindingsManager::Get(), kBreakLabelName,
678                          LocalLabel{break_block}},
Get()679           continue_binding_{&LabelBindingsManager::Get(), kContinueLabelName,
680                             LocalLabel{continue_block}} {}
681 
682    private:
683     Binding<LocalLabel> break_binding_;
684     Binding<LocalLabel> continue_binding_;
685   };
686 
687   base::Optional<Binding<LocalValue>*> TryLookupLocalValue(
688       const std::string& name);
689   base::Optional<Binding<LocalLabel>*> TryLookupLabel(const std::string& name);
690   Binding<LocalLabel>* LookupLabel(const std::string& name);
691   Block* LookupSimpleLabel(const std::string& name);
692   template <class Container>
693   Callable* LookupCallable(const QualifiedName& name,
694                            const Container& declaration_container,
695                            const TypeVector& types,
696                            const std::vector<Binding<LocalLabel>*>& labels,
697                            const TypeVector& specialization_types,
698                            bool silence_errors = false);
699   bool TestLookupCallable(const QualifiedName& name,
700                           const TypeVector& parameter_types);
701 
702   template <class Container>
703   Callable* LookupCallable(const QualifiedName& name,
704                            const Container& declaration_container,
705                            const Arguments& arguments,
706                            const TypeVector& specialization_types);
707 
708   Method* LookupMethod(const std::string& name,
709                        const AggregateType* receiver_type,
710                        const Arguments& arguments,
711                        const TypeVector& specialization_types);
712 
713   TypeArgumentInference InferSpecializationTypes(
714       GenericCallable* generic, const TypeVector& explicit_specialization_types,
715       const TypeVector& explicit_arguments);
716 
717   const Type* GetCommonType(const Type* left, const Type* right);
718 
719   VisitResult GenerateCopy(const VisitResult& to_copy);
720 
721   void GenerateAssignToLocation(const LocationReference& reference,
722                                 const VisitResult& assignment_value);
723 
724   void AddCallParameter(Callable* callable, VisitResult parameter,
725                         const Type* parameter_type,
726                         std::vector<VisitResult>* converted_arguments,
727                         StackRange* argument_range,
728                         std::vector<std::string>* constexpr_arguments,
729                         bool inline_macro);
730 
731   VisitResult GenerateCall(Callable* callable,
732                            base::Optional<LocationReference> this_parameter,
733                            Arguments parameters,
734                            const TypeVector& specialization_types = {},
735                            bool tail_call = false);
736   VisitResult GenerateCall(const QualifiedName& callable_name,
737                            Arguments parameters,
738                            const TypeVector& specialization_types = {},
739                            bool tail_call = false);
740   VisitResult GenerateCall(std::string callable_name, Arguments parameters,
741                            const TypeVector& specialization_types = {},
742                            bool tail_call = false) {
743     return GenerateCall(QualifiedName(std::move(callable_name)),
744                         std::move(parameters), specialization_types, tail_call);
745   }
746   VisitResult GeneratePointerCall(Expression* callee,
747                                   const Arguments& parameters, bool tail_call);
748 
749   void GenerateBranch(const VisitResult& condition, Block* true_block,
750                       Block* false_block);
751 
752   VisitResult GenerateBoolConstant(bool constant);
753 
754   void GenerateExpressionBranch(Expression* expression, Block* true_block,
755                                 Block* false_block);
756 
757   cpp::Function GenerateMacroFunctionDeclaration(Macro* macro);
758 
759   cpp::Function GenerateFunction(
760       cpp::Class* owner, const std::string& name, const Signature& signature,
761       const NameVector& parameter_names, bool pass_code_assembler_state = true,
762       std::vector<std::string>* generated_parameter_names = nullptr);
763 
764   VisitResult GenerateImplicitConvert(const Type* destination_type,
765                                       VisitResult source);
766 
767   StackRange GenerateLabelGoto(LocalLabel* label,
768                                base::Optional<StackRange> arguments = {});
769 
770   VisitResult GenerateSetBitField(const Type* bitfield_struct_type,
771                                   const BitField& bitfield,
772                                   VisitResult bitfield_struct,
773                                   VisitResult value,
774                                   bool starts_as_zero = false);
775 
776   std::vector<Binding<LocalLabel>*> LabelsFromIdentifiers(
777       const std::vector<Identifier*>& names);
778 
779   StackRange LowerParameter(const Type* type, const std::string& parameter_name,
780                             Stack<std::string>* lowered_parameters);
781 
782   void LowerLabelParameter(const Type* type, const std::string& parameter_name,
783                            std::vector<std::string>* lowered_parameters);
784 
785   std::string ExternalLabelName(const std::string& label_name);
786   std::string ExternalLabelParameterName(const std::string& label_name,
787                                          size_t i);
788   std::string ExternalParameterName(const std::string& name);
789 
csa_ccfile()790   std::ostream& csa_ccfile() {
791     if (auto* streams = CurrentFileStreams::Get()) {
792       switch (output_type_) {
793         case OutputType::kCSA:
794           return streams->csa_ccfile;
795         case OutputType::kCC:
796           return streams->class_definition_inline_headerfile_macro_definitions;
797         case OutputType::kCCDebug:
798           return debug_macros_cc_;
799         default:
800           UNREACHABLE();
801       }
802     }
803     return null_stream_;
804   }
csa_headerfile()805   std::ostream& csa_headerfile() {
806     if (auto* streams = CurrentFileStreams::Get()) {
807       switch (output_type_) {
808         case OutputType::kCSA:
809           return streams->csa_headerfile;
810         case OutputType::kCC:
811           return streams->class_definition_inline_headerfile_macro_declarations;
812         case OutputType::kCCDebug:
813           return debug_macros_h_;
814         default:
815           UNREACHABLE();
816       }
817     }
818     return null_stream_;
819   }
820 
assembler()821   CfgAssembler& assembler() { return *assembler_; }
822 
SetReturnValue(VisitResult return_value)823   void SetReturnValue(VisitResult return_value) {
824     base::Optional<VisitResult>& current_return_value =
825         CurrentReturnValue::Get();
826     DCHECK_IMPLIES(current_return_value, *current_return_value == return_value);
827     current_return_value = std::move(return_value);
828   }
829 
GetAndClearReturnValue()830   VisitResult GetAndClearReturnValue() {
831     VisitResult return_value = *CurrentReturnValue::Get();
832     CurrentReturnValue::Get() = base::nullopt;
833     return return_value;
834   }
835 
WriteFile(const std::string & file,const std::string & content)836   void WriteFile(const std::string& file, const std::string& content) {
837     if (is_dry_run_) return;
838     ReplaceFileContentsIfDifferent(file, content);
839   }
840 
TryGetSourceForBitfieldExpression(const Expression * expr)841   const Identifier* TryGetSourceForBitfieldExpression(
842       const Expression* expr) const {
843     auto it = bitfield_expressions_.find(expr);
844     if (it == bitfield_expressions_.end()) return nullptr;
845     return it->second;
846   }
847 
PropagateBitfieldMark(const Expression * original,const Expression * derived)848   void PropagateBitfieldMark(const Expression* original,
849                              const Expression* derived) {
850     if (const Identifier* source =
851             TryGetSourceForBitfieldExpression(original)) {
852       bitfield_expressions_[derived] = source;
853     }
854   }
855 
856   class MacroInliningScope;
857 
858   base::Optional<CfgAssembler> assembler_;
859   NullOStream null_stream_;
860   bool is_dry_run_;
861 
862   // Just for allowing us to emit warnings. After visiting an Expression, if
863   // that Expression is a bitfield load, plus an optional inversion or an
864   // equality check with a constant, then that Expression will be present in
865   // this map. The Identifier associated is the bitfield struct that contains
866   // the value to load.
867   std::unordered_map<const Expression*, const Identifier*>
868       bitfield_expressions_;
869 
870   // For emitting warnings. Contains the current set of macros being inlined in
871   // calls to InlineMacro.
872   std::unordered_set<const Macro*> inlining_macros_;
873 
874   // The contents of the debug macros output files. These contain all Torque
875   // macros that have been generated using the C++ backend with debug purpose.
876   std::stringstream debug_macros_cc_;
877   std::stringstream debug_macros_h_;
878 
879   OutputType output_type_ = OutputType::kCSA;
880 };
881 
882 void ReportAllUnusedMacros();
883 
884 }  // namespace torque
885 }  // namespace internal
886 }  // namespace v8
887 
888 #endif  // V8_TORQUE_IMPLEMENTATION_VISITOR_H_
889