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