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