1/*
2 * Copyright (C) 2012-2019 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#include "UnlinkedCodeBlock.h"
29
30#include "BytecodeGenerator.h"
31#include "BytecodeLivenessAnalysis.h"
32#include "BytecodeRewriter.h"
33#include "ClassInfo.h"
34#include "CodeCache.h"
35#include "ExecutableInfo.h"
36#include "FunctionOverrides.h"
37#include "InstructionStream.h"
38#include "JSCInlines.h"
39#include "JSString.h"
40#include "Opcode.h"
41#include "Parser.h"
42#include "PreciseJumpTargetsInlines.h"
43#include "SourceProvider.h"
44#include "Structure.h"
45#include "SymbolTable.h"
46#include "UnlinkedEvalCodeBlock.h"
47#include "UnlinkedFunctionCodeBlock.h"
48#include "UnlinkedMetadataTableInlines.h"
49#include "UnlinkedModuleProgramCodeBlock.h"
50#include "UnlinkedProgramCodeBlock.h"
51#include <wtf/DataLog.h>
52
53namespace JSC {
54
55const ClassInfo UnlinkedCodeBlock::s_info = { "UnlinkedCodeBlock", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(UnlinkedCodeBlock) };
56
57UnlinkedCodeBlock::UnlinkedCodeBlock(VM& vm, Structure* structure, CodeType codeType, const ExecutableInfo& info, OptionSet<CodeGenerationMode> codeGenerationMode)
58 : Base(vm, structure)
59 , m_usesEval(info.usesEval())
60 , m_isStrictMode(info.isStrictMode())
61 , m_isConstructor(info.isConstructor())
62 , m_hasCapturedVariables(false)
63 , m_isBuiltinFunction(info.isBuiltinFunction())
64 , m_superBinding(static_cast<unsigned>(info.superBinding()))
65 , m_scriptMode(static_cast<unsigned>(info.scriptMode()))
66 , m_isArrowFunctionContext(info.isArrowFunctionContext())
67 , m_isClassContext(info.isClassContext())
68 , m_hasTailCalls(false)
69 , m_constructorKind(static_cast<unsigned>(info.constructorKind()))
70 , m_derivedContextType(static_cast<unsigned>(info.derivedContextType()))
71 , m_evalContextType(static_cast<unsigned>(info.evalContextType()))
72 , m_codeType(static_cast<unsigned>(codeType))
73 , m_didOptimize(static_cast<unsigned>(MixedTriState))
74 , m_age(0)
75 , m_parseMode(info.parseMode())
76 , m_codeGenerationMode(codeGenerationMode)
77 , m_metadata(UnlinkedMetadataTable::create())
78{
79 ASSERT(m_constructorKind == static_cast<unsigned>(info.constructorKind()));
80 ASSERT(m_codeType == static_cast<unsigned>(codeType));
81 ASSERT(m_didOptimize == static_cast<unsigned>(MixedTriState));
82}
83
84void UnlinkedCodeBlock::visitChildren(JSCell* cell, SlotVisitor& visitor)
85{
86 UnlinkedCodeBlock* thisObject = jsCast<UnlinkedCodeBlock*>(cell);
87 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
88 Base::visitChildren(thisObject, visitor);
89 auto locker = holdLock(thisObject->cellLock());
90 if (visitor.isFirstVisit())
91 thisObject->m_age = std::min<unsigned>(static_cast<unsigned>(thisObject->m_age) + 1, maxAge);
92 for (FunctionExpressionVector::iterator ptr = thisObject->m_functionDecls.begin(), end = thisObject->m_functionDecls.end(); ptr != end; ++ptr)
93 visitor.append(*ptr);
94 for (FunctionExpressionVector::iterator ptr = thisObject->m_functionExprs.begin(), end = thisObject->m_functionExprs.end(); ptr != end; ++ptr)
95 visitor.append(*ptr);
96 visitor.appendValues(thisObject->m_constantRegisters.data(), thisObject->m_constantRegisters.size());
97 size_t extraMemory = thisObject->m_metadata->sizeInBytes();
98 if (thisObject->m_instructions)
99 extraMemory += thisObject->m_instructions->sizeInBytes();
100 visitor.reportExtraMemoryVisited(extraMemory);
101}
102
103size_t UnlinkedCodeBlock::estimatedSize(JSCell* cell, VM& vm)
104{
105 UnlinkedCodeBlock* thisObject = jsCast<UnlinkedCodeBlock*>(cell);
106 size_t extraSize = thisObject->m_metadata->sizeInBytes();
107 if (thisObject->m_instructions)
108 extraSize += thisObject->m_instructions->sizeInBytes();
109 return Base::estimatedSize(cell, vm) + extraSize;
110}
111
112int UnlinkedCodeBlock::lineNumberForBytecodeIndex(BytecodeIndex bytecodeIndex)
113{
114 ASSERT(bytecodeIndex.offset() < instructions().size());
115 int divot { 0 };
116 int startOffset { 0 };
117 int endOffset { 0 };
118 unsigned line { 0 };
119 unsigned column { 0 };
120 expressionRangeForBytecodeIndex(bytecodeIndex, divot, startOffset, endOffset, line, column);
121 return line;
122}
123
124inline void UnlinkedCodeBlock::getLineAndColumn(const ExpressionRangeInfo& info,
125 unsigned& line, unsigned& column) const
126{
127 switch (info.mode) {
128 case ExpressionRangeInfo::FatLineMode:
129 info.decodeFatLineMode(line, column);
130 break;
131 case ExpressionRangeInfo::FatColumnMode:
132 info.decodeFatColumnMode(line, column);
133 break;
134 case ExpressionRangeInfo::FatLineAndColumnMode: {
135 unsigned fatIndex = info.position;
136 ExpressionRangeInfo::FatPosition& fatPos = m_rareData->m_expressionInfoFatPositions[fatIndex];
137 line = fatPos.line;
138 column = fatPos.column;
139 break;
140 }
141 } // switch
142}
143
144#ifndef NDEBUG
145static void dumpLineColumnEntry(size_t index, const InstructionStream& instructionStream, unsigned instructionOffset, unsigned line, unsigned column)
146{
147 const auto instruction = instructionStream.at(instructionOffset);
148 const char* event = "";
149 if (instruction->is<OpDebug>()) {
150 switch (instruction->as<OpDebug>().m_debugHookType) {
151 case WillExecuteProgram: event = " WillExecuteProgram"; break;
152 case DidExecuteProgram: event = " DidExecuteProgram"; break;
153 case DidEnterCallFrame: event = " DidEnterCallFrame"; break;
154 case DidReachBreakpoint: event = " DidReachBreakpoint"; break;
155 case WillLeaveCallFrame: event = " WillLeaveCallFrame"; break;
156 case WillExecuteStatement: event = " WillExecuteStatement"; break;
157 case WillExecuteExpression: event = " WillExecuteExpression"; break;
158 }
159 }
160 dataLogF(" [%zu] pc %u @ line %u col %u : %s%s\n", index, instructionOffset, line, column, instruction->name(), event);
161}
162
163void UnlinkedCodeBlock::dumpExpressionRangeInfo()
164{
165 Vector<ExpressionRangeInfo>& expressionInfo = m_expressionInfo;
166
167 size_t size = m_expressionInfo.size();
168 dataLogF("UnlinkedCodeBlock %p expressionRangeInfo[%zu] {\n", this, size);
169 for (size_t i = 0; i < size; i++) {
170 ExpressionRangeInfo& info = expressionInfo[i];
171 unsigned line;
172 unsigned column;
173 getLineAndColumn(info, line, column);
174 dumpLineColumnEntry(i, instructions(), info.instructionOffset, line, column);
175 }
176 dataLog("}\n");
177}
178#endif
179
180void UnlinkedCodeBlock::expressionRangeForBytecodeIndex(BytecodeIndex bytecodeIndex,
181 int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) const
182{
183 ASSERT(bytecodeIndex.offset() < instructions().size());
184
185 if (!m_expressionInfo.size()) {
186 startOffset = 0;
187 endOffset = 0;
188 divot = 0;
189 line = 0;
190 column = 0;
191 return;
192 }
193
194 const Vector<ExpressionRangeInfo>& expressionInfo = m_expressionInfo;
195
196 int low = 0;
197 int high = expressionInfo.size();
198 while (low < high) {
199 int mid = low + (high - low) / 2;
200 if (expressionInfo[mid].instructionOffset <= bytecodeIndex.offset())
201 low = mid + 1;
202 else
203 high = mid;
204 }
205
206 if (!low)
207 low = 1;
208
209 const ExpressionRangeInfo& info = expressionInfo[low - 1];
210 startOffset = info.startOffset;
211 endOffset = info.endOffset;
212 divot = info.divotPoint;
213 getLineAndColumn(info, line, column);
214}
215
216void UnlinkedCodeBlock::addExpressionInfo(unsigned instructionOffset,
217 int divot, int startOffset, int endOffset, unsigned line, unsigned column)
218{
219 if (divot > ExpressionRangeInfo::MaxDivot) {
220 // Overflow has occurred, we can only give line number info for errors for this region
221 divot = 0;
222 startOffset = 0;
223 endOffset = 0;
224 } else if (startOffset > ExpressionRangeInfo::MaxOffset) {
225 // If the start offset is out of bounds we clear both offsets
226 // so we only get the divot marker. Error message will have to be reduced
227 // to line and charPosition number.
228 startOffset = 0;
229 endOffset = 0;
230 } else if (endOffset > ExpressionRangeInfo::MaxOffset) {
231 // The end offset is only used for additional context, and is much more likely
232 // to overflow (eg. function call arguments) so we are willing to drop it without
233 // dropping the rest of the range.
234 endOffset = 0;
235 }
236
237 unsigned positionMode =
238 (line <= ExpressionRangeInfo::MaxFatLineModeLine && column <= ExpressionRangeInfo::MaxFatLineModeColumn)
239 ? ExpressionRangeInfo::FatLineMode
240 : (line <= ExpressionRangeInfo::MaxFatColumnModeLine && column <= ExpressionRangeInfo::MaxFatColumnModeColumn)
241 ? ExpressionRangeInfo::FatColumnMode
242 : ExpressionRangeInfo::FatLineAndColumnMode;
243
244 ExpressionRangeInfo info;
245 info.instructionOffset = instructionOffset;
246 info.divotPoint = divot;
247 info.startOffset = startOffset;
248 info.endOffset = endOffset;
249
250 info.mode = positionMode;
251 switch (positionMode) {
252 case ExpressionRangeInfo::FatLineMode:
253 info.encodeFatLineMode(line, column);
254 break;
255 case ExpressionRangeInfo::FatColumnMode:
256 info.encodeFatColumnMode(line, column);
257 break;
258 case ExpressionRangeInfo::FatLineAndColumnMode: {
259 createRareDataIfNecessary();
260 unsigned fatIndex = m_rareData->m_expressionInfoFatPositions.size();
261 ExpressionRangeInfo::FatPosition fatPos = { line, column };
262 m_rareData->m_expressionInfoFatPositions.append(fatPos);
263 info.position = fatIndex;
264 }
265 } // switch
266
267 m_expressionInfo.append(info);
268}
269
270bool UnlinkedCodeBlock::typeProfilerExpressionInfoForBytecodeOffset(unsigned bytecodeOffset, unsigned& startDivot, unsigned& endDivot)
271{
272 static constexpr bool verbose = false;
273 if (!m_rareData) {
274 if (verbose)
275 dataLogF("Don't have assignment info for offset:%u\n", bytecodeOffset);
276 startDivot = UINT_MAX;
277 endDivot = UINT_MAX;
278 return false;
279 }
280
281 auto iter = m_rareData->m_typeProfilerInfoMap.find(bytecodeOffset);
282 if (iter == m_rareData->m_typeProfilerInfoMap.end()) {
283 if (verbose)
284 dataLogF("Don't have assignment info for offset:%u\n", bytecodeOffset);
285 startDivot = UINT_MAX;
286 endDivot = UINT_MAX;
287 return false;
288 }
289
290 RareData::TypeProfilerExpressionRange& range = iter->value;
291 startDivot = range.m_startDivot;
292 endDivot = range.m_endDivot;
293 return true;
294}
295
296void UnlinkedCodeBlock::addTypeProfilerExpressionInfo(unsigned instructionOffset, unsigned startDivot, unsigned endDivot)
297{
298 createRareDataIfNecessary();
299 RareData::TypeProfilerExpressionRange range;
300 range.m_startDivot = startDivot;
301 range.m_endDivot = endDivot;
302 m_rareData->m_typeProfilerInfoMap.set(instructionOffset, range);
303}
304
305UnlinkedCodeBlock::~UnlinkedCodeBlock()
306{
307}
308
309void UnlinkedCodeBlock::setInstructions(std::unique_ptr<InstructionStream> instructions)
310{
311 ASSERT(instructions);
312 {
313 auto locker = holdLock(cellLock());
314 m_instructions = WTFMove(instructions);
315 m_metadata->finalize();
316 }
317 Heap::heap(this)->reportExtraMemoryAllocated(m_instructions->sizeInBytes() + m_metadata->sizeInBytes());
318}
319
320const InstructionStream& UnlinkedCodeBlock::instructions() const
321{
322 ASSERT(m_instructions.get());
323 return *m_instructions;
324}
325
326UnlinkedHandlerInfo* UnlinkedCodeBlock::handlerForBytecodeIndex(BytecodeIndex bytecodeIndex, RequiredHandler requiredHandler)
327{
328 return handlerForIndex(bytecodeIndex.offset(), requiredHandler);
329}
330
331UnlinkedHandlerInfo* UnlinkedCodeBlock::handlerForIndex(unsigned index, RequiredHandler requiredHandler)
332{
333 if (!m_rareData)
334 return nullptr;
335 return UnlinkedHandlerInfo::handlerForIndex(m_rareData->m_exceptionHandlers, index, requiredHandler);
336}
337
338void UnlinkedCodeBlock::applyModification(BytecodeRewriter& rewriter, InstructionStreamWriter& instructions)
339{
340 // Before applying the changes, we adjust the jumps based on the original bytecode offset, the offset to the jump target, and
341 // the insertion information.
342
343 rewriter.adjustJumpTargets();
344
345 // Then, exception handlers should be adjusted.
346 if (m_rareData) {
347 for (UnlinkedHandlerInfo& handler : m_rareData->m_exceptionHandlers) {
348 handler.target = rewriter.adjustAbsoluteOffset(handler.target);
349 handler.start = rewriter.adjustAbsoluteOffset(handler.start);
350 handler.end = rewriter.adjustAbsoluteOffset(handler.end);
351 }
352
353 for (size_t i = 0; i < m_rareData->m_opProfileControlFlowBytecodeOffsets.size(); ++i)
354 m_rareData->m_opProfileControlFlowBytecodeOffsets[i] = rewriter.adjustAbsoluteOffset(m_rareData->m_opProfileControlFlowBytecodeOffsets[i]);
355
356 if (!m_rareData->m_typeProfilerInfoMap.isEmpty()) {
357 HashMap<unsigned, RareData::TypeProfilerExpressionRange> adjustedTypeProfilerInfoMap;
358 for (auto& entry : m_rareData->m_typeProfilerInfoMap)
359 adjustedTypeProfilerInfoMap.set(rewriter.adjustAbsoluteOffset(entry.key), entry.value);
360 m_rareData->m_typeProfilerInfoMap.swap(adjustedTypeProfilerInfoMap);
361 }
362 }
363
364 for (size_t i = 0; i < m_expressionInfo.size(); ++i)
365 m_expressionInfo[i].instructionOffset = rewriter.adjustAbsoluteOffset(m_expressionInfo[i].instructionOffset);
366
367 // Then, modify the unlinked instructions.
368 rewriter.applyModification();
369
370 // And recompute the jump target based on the modified unlinked instructions.
371 m_jumpTargets.clear();
372 recomputePreciseJumpTargets(this, instructions, m_jumpTargets);
373}
374
375void UnlinkedCodeBlock::shrinkToFit()
376{
377 auto locker = holdLock(cellLock());
378
379 m_jumpTargets.shrinkToFit();
380 m_identifiers.shrinkToFit();
381 m_constantRegisters.shrinkToFit();
382 m_constantsSourceCodeRepresentation.shrinkToFit();
383 m_functionDecls.shrinkToFit();
384 m_functionExprs.shrinkToFit();
385 m_expressionInfo.shrinkToFit();
386
387 if (m_rareData) {
388 m_rareData->m_exceptionHandlers.shrinkToFit();
389 m_rareData->m_switchJumpTables.shrinkToFit();
390 m_rareData->m_stringSwitchJumpTables.shrinkToFit();
391 m_rareData->m_expressionInfoFatPositions.shrinkToFit();
392 m_rareData->m_opProfileControlFlowBytecodeOffsets.shrinkToFit();
393 m_rareData->m_bitVectors.shrinkToFit();
394 m_rareData->m_constantIdentifierSets.shrinkToFit();
395 }
396}
397
398void UnlinkedCodeBlock::dump(PrintStream&) const
399{
400}
401
402BytecodeLivenessAnalysis& UnlinkedCodeBlock::livenessAnalysisSlow(CodeBlock* codeBlock)
403{
404 RELEASE_ASSERT(codeBlock->unlinkedCodeBlock() == this);
405
406 {
407 ConcurrentJSLocker locker(m_lock);
408 if (!m_liveness) {
409 // There is a chance two compiler threads raced to the slow path.
410 // Grabbing the lock above defends against computing liveness twice.
411 m_liveness = makeUnique<BytecodeLivenessAnalysis>(codeBlock);
412 }
413 }
414
415 return *m_liveness;
416}
417
418void UnlinkedCodeBlock::addOutOfLineJumpTarget(InstructionStream::Offset bytecodeOffset, int target)
419{
420 RELEASE_ASSERT(target);
421 m_outOfLineJumpTargets.set(bytecodeOffset, target);
422}
423
424int UnlinkedCodeBlock::outOfLineJumpOffset(InstructionStream::Offset bytecodeOffset)
425{
426 ASSERT(m_outOfLineJumpTargets.contains(bytecodeOffset));
427 return m_outOfLineJumpTargets.get(bytecodeOffset);
428}
429
430} // namespace JSC
431