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 #include "frontend/BytecodeSection.h"
8 
9 #include "mozilla/Assertions.h"       // MOZ_ASSERT
10 #include "mozilla/ReverseIterator.h"  // mozilla::Reversed
11 
12 #include "frontend/CompilationInfo.h"
13 #include "frontend/SharedContext.h"  // FunctionBox
14 #include "frontend/Stencil.h"        // ScopeCreationData
15 #include "vm/BytecodeUtil.h"         // INDEX_LIMIT, StackUses, StackDefs
16 #include "vm/GlobalObject.h"
17 #include "vm/JSContext.h"     // JSContext
18 #include "vm/RegExpObject.h"  // RegexpObject
19 
20 using namespace js;
21 using namespace js::frontend;
22 
append(FunctionBox * funbox,uint32_t * index)23 bool GCThingList::append(FunctionBox* funbox, uint32_t* index) {
24   // Append the function to the vector and return the index in *index.
25   *index = vector.length();
26 
27   // To avoid circular include issues, funbox can't return a FunctionIndex, so
28   // instead it returns a size_t, which we wrap in FunctionIndex here to
29   // disambiguate the variant.
30   return vector.append(mozilla::AsVariant(FunctionIndex(funbox->index())));
31 }
32 
getScope(size_t index) const33 AbstractScopePtr GCThingList::getScope(size_t index) const {
34   const ScriptThingVariant& elem = vector[index];
35   if (elem.is<EmptyGlobalScopeType>()) {
36     return AbstractScopePtr(&compilationInfo.cx->global()->emptyGlobalScope());
37   }
38   return AbstractScopePtr(compilationInfo, elem.as<ScopeIndex>());
39 }
40 
EmitScriptThingsVector(JSContext * cx,CompilationInfo & compilationInfo,const ScriptThingsVector & objects,mozilla::Span<JS::GCCellPtr> output)41 bool js::frontend::EmitScriptThingsVector(JSContext* cx,
42                                           CompilationInfo& compilationInfo,
43                                           const ScriptThingsVector& objects,
44                                           mozilla::Span<JS::GCCellPtr> output) {
45   MOZ_ASSERT(objects.length() <= INDEX_LIMIT);
46   MOZ_ASSERT(objects.length() == output.size());
47 
48   struct Matcher {
49     JSContext* cx;
50     CompilationInfo& compilationInfo;
51     uint32_t i;
52     mozilla::Span<JS::GCCellPtr>& output;
53 
54     bool operator()(const ScriptAtom& data) {
55       JSAtom* atom = data;
56       output[i] = JS::GCCellPtr(atom);
57       return true;
58     }
59 
60     bool operator()(const NullScriptThing& data) {
61       output[i] = JS::GCCellPtr(nullptr);
62       return true;
63     }
64 
65     bool operator()(const BigIntIndex& index) {
66       BigIntCreationData& data = compilationInfo.bigIntData[index];
67       BigInt* bi = data.createBigInt(cx);
68       if (!bi) {
69         return false;
70       }
71       output[i] = JS::GCCellPtr(bi);
72       return true;
73     }
74 
75     bool operator()(const RegExpIndex& rindex) {
76       RegExpCreationData& data = compilationInfo.regExpData[rindex];
77       RegExpObject* regexp = data.createRegExp(cx);
78       if (!regexp) {
79         return false;
80       }
81       output[i] = JS::GCCellPtr(regexp);
82       return true;
83     }
84 
85     bool operator()(const ObjLiteralCreationData& data) {
86       JSObject* obj = data.create(cx);
87       if (!obj) {
88         return false;
89       }
90       output[i] = JS::GCCellPtr(obj);
91       return true;
92     }
93 
94     bool operator()(const ScopeIndex& index) {
95       MutableHandle<ScopeCreationData> data =
96           compilationInfo.scopeCreationData[index];
97       Scope* scope = data.get().createScope(cx);
98       if (!scope) {
99         return false;
100       }
101 
102       output[i] = JS::GCCellPtr(scope);
103       return true;
104     }
105 
106     bool operator()(const FunctionIndex& index) {
107       // We should have already converted this data to a JSFunction as part
108       // of publishDeferredFunctions, which currently happens before BCE begins.
109       // Once we can do LazyScriptCreationData::create without referencing the
110       // functionbox, then we should be able to do JSFunction allocation here.
111       output[i] = JS::GCCellPtr(compilationInfo.functions[index]);
112       return true;
113     }
114 
115     bool operator()(const EmptyGlobalScopeType& emptyGlobalScope) {
116       Scope* scope = &cx->global()->emptyGlobalScope();
117       output[i] = JS::GCCellPtr(scope);
118       return true;
119     }
120   };
121 
122   for (uint32_t i = 0; i < objects.length(); i++) {
123     Matcher m{cx, compilationInfo, i, output};
124     if (!objects[i].match(m)) {
125       return false;
126     }
127   }
128   return true;
129 }
130 
append(TryNoteKind kind,uint32_t stackDepth,BytecodeOffset start,BytecodeOffset end)131 bool CGTryNoteList::append(TryNoteKind kind, uint32_t stackDepth,
132                            BytecodeOffset start, BytecodeOffset end) {
133   MOZ_ASSERT(start <= end);
134 
135   // Offsets are given relative to sections, but we only expect main-section
136   // to have TryNotes. In finish() we will fixup base offset.
137 
138   TryNote note(uint32_t(kind), stackDepth, start.toUint32(),
139                (end - start).toUint32());
140 
141   return list.append(note);
142 }
143 
append(uint32_t scopeIndex,BytecodeOffset offset,uint32_t parent)144 bool CGScopeNoteList::append(uint32_t scopeIndex, BytecodeOffset offset,
145                              uint32_t parent) {
146   ScopeNote note;
147   note.index = scopeIndex;
148   note.start = offset.toUint32();
149   note.length = 0;
150   note.parent = parent;
151 
152   return list.append(note);
153 }
154 
recordEnd(uint32_t index,BytecodeOffset offset)155 void CGScopeNoteList::recordEnd(uint32_t index, BytecodeOffset offset) {
156   recordEndImpl(index, offset.toUint32());
157 }
158 
recordEndFunctionBodyVar(uint32_t index)159 void CGScopeNoteList::recordEndFunctionBodyVar(uint32_t index) {
160   recordEndImpl(index, UINT32_MAX);
161 }
162 
recordEndImpl(uint32_t index,uint32_t offset)163 void CGScopeNoteList::recordEndImpl(uint32_t index, uint32_t offset) {
164   MOZ_ASSERT(index < length());
165   MOZ_ASSERT(list[index].length == 0);
166   MOZ_ASSERT(offset >= list[index].start);
167   list[index].length = offset - list[index].start;
168 }
169 
create(JSContext * cx) const170 JSObject* ObjLiteralCreationData::create(JSContext* cx) const {
171   return InterpretObjLiteral(cx, atoms_, writer_);
172 }
173 
BytecodeSection(JSContext * cx,uint32_t lineNum)174 BytecodeSection::BytecodeSection(JSContext* cx, uint32_t lineNum)
175     : code_(cx),
176       notes_(cx),
177       lastNoteOffset_(0),
178       tryNoteList_(cx),
179       scopeNoteList_(cx),
180       resumeOffsetList_(cx),
181       currentLine_(lineNum) {}
182 
updateDepth(BytecodeOffset target)183 void BytecodeSection::updateDepth(BytecodeOffset target) {
184   jsbytecode* pc = code(target);
185 
186   int nuses = StackUses(pc);
187   int ndefs = StackDefs(pc);
188 
189   stackDepth_ -= nuses;
190   MOZ_ASSERT(stackDepth_ >= 0);
191   stackDepth_ += ndefs;
192 
193   if (uint32_t(stackDepth_) > maxStackDepth_) {
194     maxStackDepth_ = stackDepth_;
195   }
196 }
197 
PerScriptData(JSContext * cx,frontend::CompilationInfo & compilationInfo)198 PerScriptData::PerScriptData(JSContext* cx,
199                              frontend::CompilationInfo& compilationInfo)
200     : gcThingList_(cx, compilationInfo),
201       atomIndices_(cx->frontendCollectionPool()) {}
202 
init(JSContext * cx)203 bool PerScriptData::init(JSContext* cx) { return atomIndices_.acquire(cx); }
204