1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef frontend_Stencil_h
8 #define frontend_Stencil_h
9 
10 #include "mozilla/Assertions.h"  // MOZ_ASSERT, MOZ_RELEASE_ASSERT
11 #include "mozilla/CheckedInt.h"  // CheckedUint32
12 #include "mozilla/Maybe.h"       // mozilla::{Maybe, Nothing}
13 #include "mozilla/Range.h"       // mozilla::Range
14 #include "mozilla/Span.h"        // mozilla::Span
15 
16 #include <stdint.h>  // char16_t, uint8_t, uint32_t
17 #include <stdlib.h>  // size_t
18 
19 #include "frontend/AbstractScopePtr.h"    // AbstractScopePtr, ScopeIndex
20 #include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
21 #include "frontend/NameAnalysisTypes.h"   // AtomVector
22 #include "frontend/ObjLiteral.h"          // ObjLiteralCreationData
23 #include "frontend/TypedIndex.h"          // TypedIndex
24 #include "gc/Barrier.h"                   // HeapPtr, GCPtrAtom
25 #include "gc/Rooting.h"  // HandleAtom, HandleModuleObject, HandleScriptSourceObject, MutableHandleScope
26 #include "js/GCVariant.h"              // GC Support for mozilla::Variant
27 #include "js/RegExpFlags.h"            // JS::RegExpFlags
28 #include "js/RootingAPI.h"             // Handle
29 #include "js/TypeDecls.h"              // JSContext,JSAtom,JSFunction
30 #include "js/UniquePtr.h"              // js::UniquePtr
31 #include "js/Utility.h"                // JS::FreePolicy, UniqueTwoByteChars
32 #include "js/Vector.h"                 // js::Vector
33 #include "util/Text.h"                 // DuplicateString
34 #include "vm/BigIntType.h"             // ParseBigIntLiteral
35 #include "vm/FunctionFlags.h"          // FunctionFlags
36 #include "vm/GeneratorAndAsyncKind.h"  // GeneratorKind, FunctionAsyncKind
37 #include "vm/JSFunction.h"             // FunctionFlags
38 #include "vm/JSScript.h"  // GeneratorKind, FunctionAsyncKind, FieldInitializers
39 #include "vm/Runtime.h"   // ReportOutOfMemory
40 #include "vm/Scope.h"  // BaseScopeData, FunctionScope, LexicalScope, VarScope, GlobalScope, EvalScope, ModuleScope
41 #include "vm/ScopeKind.h"      // ScopeKind
42 #include "vm/SharedStencil.h"  // ImmutableScriptFlags
43 
44 class JS_PUBLIC_API JSTracer;
45 
46 namespace js::frontend {
47 
48 struct CompilationInfo;
49 class FunctionBox;
50 
51 // [SMDOC] Script Stencil (Frontend Representation)
52 //
53 // Stencils are GC object free representations of artifacts created during
54 // parsing and bytecode emission that are being used as part of Project
55 // Stencil (https://bugzilla.mozilla.org/show_bug.cgi?id=stencil) to revamp
56 // the frontend.
57 //
58 // Renaming to use the term stencil more broadly is still in progress.
59 
60 // Arbitrary typename to disambiguate TypedIndexes;
61 class FunctionIndexType;
62 
63 // We need to be able to forward declare this type, so make a subclass
64 // rather than just using.
65 class FunctionIndex : public TypedIndex<FunctionIndexType> {
66   // Delegate constructors;
67   using Base = TypedIndex<FunctionIndexType>;
68   using Base::Base;
69 };
70 
71 FunctionFlags InitialFunctionFlags(FunctionSyntaxKind kind,
72                                    GeneratorKind generatorKind,
73                                    FunctionAsyncKind asyncKind,
74                                    bool isSelfHosting = false,
75                                    bool hasUnclonedName = false);
76 
77 // This owns a set of characters, previously syntax checked as a RegExp. Used
78 // to avoid allocating the RegExp on the GC heap during parsing.
79 class RegExpCreationData {
80   UniquePtr<char16_t[], JS::FreePolicy> buf_;
81   size_t length_ = 0;
82   JS::RegExpFlags flags_;
83 
84  public:
85   RegExpCreationData() = default;
86 
init(JSContext * cx,mozilla::Range<const char16_t> range,JS::RegExpFlags flags)87   MOZ_MUST_USE bool init(JSContext* cx, mozilla::Range<const char16_t> range,
88                          JS::RegExpFlags flags) {
89     length_ = range.length();
90     buf_ = js::DuplicateString(cx, range.begin().get(), range.length());
91     if (!buf_) {
92       return false;
93     }
94     flags_ = flags;
95     return true;
96   }
97 
98   MOZ_MUST_USE bool init(JSContext* cx, JSAtom* pattern, JS::RegExpFlags flags);
99 
100   RegExpObject* createRegExp(JSContext* cx) const;
101 };
102 
103 using RegExpIndex = TypedIndex<RegExpCreationData>;
104 
105 // This owns a set of characters guaranteed to parse into a BigInt via
106 // ParseBigIntLiteral. Used to avoid allocating the BigInt on the
107 // GC heap during parsing.
108 class BigIntCreationData {
109   UniqueTwoByteChars buf_;
110   size_t length_ = 0;
111 
112  public:
113   BigIntCreationData() = default;
114 
init(JSContext * cx,const Vector<char16_t,32> & buf)115   MOZ_MUST_USE bool init(JSContext* cx, const Vector<char16_t, 32>& buf) {
116 #ifdef DEBUG
117     // Assert we have no separators; if we have a separator then the algorithm
118     // used in BigInt::literalIsZero will be incorrect.
119     for (char16_t c : buf) {
120       MOZ_ASSERT(c != '_');
121     }
122 #endif
123     length_ = buf.length();
124     buf_ = js::DuplicateString(cx, buf.begin(), buf.length());
125     return buf_ != nullptr;
126   }
127 
createBigInt(JSContext * cx)128   BigInt* createBigInt(JSContext* cx) {
129     mozilla::Range<const char16_t> source(buf_.get(), length_);
130 
131     return js::ParseBigIntLiteral(cx, source);
132   }
133 
isZero()134   bool isZero() {
135     mozilla::Range<const char16_t> source(buf_.get(), length_);
136     return js::BigIntLiteralIsZero(source);
137   }
138 };
139 
140 using BigIntIndex = TypedIndex<BigIntCreationData>;
141 
142 class EnvironmentShapeCreationData {
143   // Data used to call CreateEnvShapeData
144   struct CreateEnvShapeData {
145     BindingIter freshBi;
146     const JSClass* cls;
147     uint32_t nextEnvironmentSlot;
148     uint32_t baseShapeFlags;
149 
traceCreateEnvShapeData150     void trace(JSTracer* trc) { freshBi.trace(trc); }
151   };
152 
153   // Data used to call EmptyEnvironmentShape
154   struct EmptyEnvShapeData {
155     const JSClass* cls;
156     uint32_t baseShapeFlags;
traceEmptyEnvShapeData157     void trace(JSTracer* trc){
158         // Rather than having to expose this type as public to provide
159         // an IgnoreGCPolicy, just have an empty trace.
160     };
161   };
162 
163   // Three different paths to creating an environment shape: Either we produce
164   // nullptr directly (represented by storing Nothing in the variant), or we
165   // call CreateEnvironmentShape, or we call EmptyEnvironmentShape.
166   mozilla::Variant<mozilla::Nothing, CreateEnvShapeData, EmptyEnvShapeData>
167       data_ = mozilla::AsVariant(mozilla::Nothing());
168 
169  public:
170   explicit operator bool() const { return !data_.is<mozilla::Nothing>(); }
171 
172   // Setup for calling CreateEnvironmentShape
set(const BindingIter & freshBi,const JSClass * cls,uint32_t nextEnvironmentSlot,uint32_t baseShapeFlags)173   void set(const BindingIter& freshBi, const JSClass* cls,
174            uint32_t nextEnvironmentSlot, uint32_t baseShapeFlags) {
175     data_ = mozilla::AsVariant(
176         CreateEnvShapeData{freshBi, cls, nextEnvironmentSlot, baseShapeFlags});
177   }
178 
179   // Setup for calling EmptyEnviornmentShape
set(const JSClass * cls,uint32_t shapeFlags)180   void set(const JSClass* cls, uint32_t shapeFlags) {
181     data_ = mozilla::AsVariant(EmptyEnvShapeData{cls, shapeFlags});
182   }
183 
184   // Reifiy this into an actual shape.
185   MOZ_MUST_USE bool createShape(JSContext* cx, MutableHandleShape shape);
186 
trace(JSTracer * trc)187   void trace(JSTracer* trc) {
188     using DataGCPolicy = JS::GCPolicy<decltype(data_)>;
189     DataGCPolicy::trace(trc, &data_, "data_");
190   }
191 };
192 
193 class ScopeCreationData {
194   friend class js::AbstractScopePtr;
195   friend class js::GCMarker;
196 
197   // The enclosing scope if it exists
198   AbstractScopePtr enclosing_;
199 
200   // The kind determines data_.
201   ScopeKind kind_;
202 
203   // Data to reify an environment shape at creation time.
204   EnvironmentShapeCreationData environmentShape_;
205 
206   // Once we've produced a scope from a scope creation data, there may still be
207   // AbstractScopePtrs refering to this ScopeCreationData, and if reification is
208   // requested multiple times, we should return the same scope rather than
209   // creating multiple sopes.
210   //
211   // As well, any queries that require data() to answer must be redirected to
212   // the scope once the scope has been reified, as the ScopeCreationData loses
213   // ownership of the data on reification.
214   HeapPtr<Scope*> scope_ = {};
215 
216   // For FunctionScopes we need the funbox; nullptr otherwise.
217   frontend::FunctionBox* funbox_ = nullptr;
218 
219   UniquePtr<BaseScopeData> data_;
220 
221  public:
222   ScopeCreationData(
223       JSContext* cx, ScopeKind kind, Handle<AbstractScopePtr> enclosing,
224       Handle<frontend::EnvironmentShapeCreationData> environmentShape,
225       UniquePtr<BaseScopeData> data = {},
226       frontend::FunctionBox* funbox = nullptr)
enclosing_(enclosing)227       : enclosing_(enclosing),
228         kind_(kind),
229         environmentShape_(environmentShape),  // Copied
230         funbox_(funbox),
231         data_(std::move(data)) {}
232 
kind()233   ScopeKind kind() const { return kind_; }
enclosing()234   AbstractScopePtr enclosing() { return enclosing_; }
getOrCreateEnclosingScope(JSContext * cx,MutableHandleScope scope)235   bool getOrCreateEnclosingScope(JSContext* cx, MutableHandleScope scope) {
236     return enclosing_.getOrCreateScope(cx, scope);
237   }
238 
239   // FunctionScope
240   static bool create(JSContext* cx, frontend::CompilationInfo& compilationInfo,
241                      Handle<FunctionScope::Data*> dataArg,
242                      bool hasParameterExprs, bool needsEnvironment,
243                      frontend::FunctionBox* funbox,
244                      Handle<AbstractScopePtr> enclosing, ScopeIndex* index);
245 
246   // LexicalScope
247   static bool create(JSContext* cx, frontend::CompilationInfo& compilationInfo,
248                      ScopeKind kind, Handle<LexicalScope::Data*> dataArg,
249                      uint32_t firstFrameSlot,
250                      Handle<AbstractScopePtr> enclosing, ScopeIndex* index);
251   // VarScope
252   static bool create(JSContext* cx, frontend::CompilationInfo& compilationInfo,
253                      ScopeKind kind, Handle<VarScope::Data*> dataArg,
254                      uint32_t firstFrameSlot, bool needsEnvironment,
255                      Handle<AbstractScopePtr> enclosing, ScopeIndex* index);
256 
257   // GlobalScope
258   static bool create(JSContext* cx, frontend::CompilationInfo& compilationInfo,
259                      ScopeKind kind, Handle<GlobalScope::Data*> dataArg,
260                      ScopeIndex* index);
261 
262   // EvalScope
263   static bool create(JSContext* cx, frontend::CompilationInfo& compilationInfo,
264                      ScopeKind kind, Handle<EvalScope::Data*> dataArg,
265                      Handle<AbstractScopePtr> enclosing, ScopeIndex* index);
266 
267   // ModuleScope
268   static bool create(JSContext* cx, frontend::CompilationInfo& compilationInfo,
269                      Handle<ModuleScope::Data*> dataArg,
270                      HandleModuleObject module,
271                      Handle<AbstractScopePtr> enclosing, ScopeIndex* index);
272 
273   // WithScope
274   static bool create(JSContext* cx, frontend::CompilationInfo& compilationInfo,
275                      Handle<AbstractScopePtr> enclosing, ScopeIndex* index);
276 
hasEnvironment()277   bool hasEnvironment() const {
278     // Check if scope kind alone means we have an env shape, and
279     // otherwise check if we have one created.
280     return Scope::hasEnvironment(kind(), !!environmentShape_);
281   }
282 
283   // Valid for functions;
284   bool isArrow() const;
285 
hasScope()286   bool hasScope() const { return scope_ != nullptr; }
287 
getScope()288   Scope* getScope() const {
289     MOZ_ASSERT(hasScope());
290     return scope_;
291   }
292 
293   Scope* createScope(JSContext* cx);
294 
295   void trace(JSTracer* trc);
296 
297   uint32_t nextFrameSlot() const;
298 
299  private:
300   // Non owning reference to data
301   template <typename SpecificScopeType>
data()302   typename SpecificScopeType::Data& data() const {
303     MOZ_ASSERT(data_.get());
304     return *static_cast<typename SpecificScopeType::Data*>(data_.get());
305   }
306 
307   // Transfer ownership into a new UniquePtr.
308   template <typename SpecificScopeType>
309   UniquePtr<typename SpecificScopeType::Data> releaseData();
310 
311   template <typename SpecificScopeType>
312   Scope* createSpecificScope(JSContext* cx);
313 
314   template <typename SpecificScopeType>
nextFrameSlot()315   uint32_t nextFrameSlot() const {
316     // If a scope has been allocated for the ScopeCreationData we no longer own
317     // data, so defer to scope
318     if (hasScope()) {
319       return getScope()->template as<SpecificScopeType>().nextFrameSlot();
320     }
321     return data<SpecificScopeType>().nextFrameSlot;
322   }
323 };
324 
325 class EmptyGlobalScopeType {};
326 
327 // The lazy closed-over-binding info is represented by these types that will
328 // convert to a GCCellPtr(nullptr), GCCellPtr(JSAtom*).
329 class NullScriptThing {};
330 using ScriptAtom = JSAtom*;
331 
332 // These types all end up being baked into GC things as part of stencil
333 // instantiation.
334 using ScriptThingVariant =
335     mozilla::Variant<ScriptAtom, NullScriptThing, BigIntIndex,
336                      ObjLiteralCreationData, RegExpIndex, ScopeIndex,
337                      FunctionIndex, EmptyGlobalScopeType>;
338 
339 // A vector of things destined to be converted to GC things.
340 using ScriptThingsVector = Vector<ScriptThingVariant>;
341 
342 // Data generated by frontend that will be used to create a js::BaseScript.
343 class ScriptStencil {
344  public:
345   // See `BaseScript::functionOrGlobal_`.
346   mozilla::Maybe<FunctionIndex> functionIndex;
347 
348   // See `BaseScript::immutableFlags_`.
349   ImmutableScriptFlags immutableFlags;
350 
351   // See `BaseScript::data_`.
352   mozilla::Maybe<FieldInitializers> fieldInitializers;
353   ScriptThingsVector gcThings;
354 
355   // See `BaseScript::sharedData_`.
356   js::UniquePtr<js::ImmutableScriptData> immutableScriptData = nullptr;
357 
358   // End of fields.
359 
ScriptStencil(JSContext * cx)360   explicit ScriptStencil(JSContext* cx) : gcThings(cx) {}
361 
362   // This traces any JSAtoms in the gcThings array. This will be removed once
363   // atoms are deferred from parsing.
364   void trace(JSTracer* trc);
365 };
366 
367 } /* namespace js::frontend */
368 
369 namespace JS {
370 template <>
371 struct GCPolicy<js::frontend::ScopeCreationData*> {
372   static void trace(JSTracer* trc, js::frontend::ScopeCreationData** data,
373                     const char* name) {
374     (*data)->trace(trc);
375   }
376 };
377 }  // namespace JS
378 #endif /* frontend_Stencil_h */
379