1/*
2 * Copyright (C) 2009-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 "BatchedTransitionOptimizer.h"
29#include "CodeBlock.h"
30#include "Debugger.h"
31#include "EvalCodeBlock.h"
32#include "FunctionCodeBlock.h"
33#include "GlobalExecutable.h"
34#include "IsoCellSetInlines.h"
35#include "JIT.h"
36#include "JSCInlines.h"
37#include "LLIntEntrypoint.h"
38#include "ModuleProgramCodeBlock.h"
39#include "Parser.h"
40#include "ProgramCodeBlock.h"
41#include "TypeProfiler.h"
42#include "VMInlines.h"
43#include <wtf/CommaPrinter.h>
44
45namespace JSC {
46
47const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ScriptExecutable) };
48
49ScriptExecutable::ScriptExecutable(Structure* structure, VM& vm, const SourceCode& source, bool isInStrictContext, DerivedContextType derivedContextType, bool isInArrowFunctionContext, EvalContextType evalContextType, Intrinsic intrinsic)
50 : ExecutableBase(vm, structure)
51 , m_source(source)
52 , m_intrinsic(intrinsic)
53 , m_features(isInStrictContext ? StrictModeFeature : 0)
54 , m_hasCapturedVariables(false)
55 , m_neverInline(false)
56 , m_neverOptimize(false)
57 , m_neverFTLOptimize(false)
58 , m_isArrowFunctionContext(isInArrowFunctionContext)
59 , m_canUseOSRExitFuzzing(true)
60 , m_derivedContextType(static_cast<unsigned>(derivedContextType))
61 , m_evalContextType(static_cast<unsigned>(evalContextType))
62{
63}
64
65void ScriptExecutable::destroy(JSCell* cell)
66{
67 static_cast<ScriptExecutable*>(cell)->ScriptExecutable::~ScriptExecutable();
68}
69
70void ScriptExecutable::clearCode(IsoCellSet& clearableCodeSet)
71{
72 m_jitCodeForCall = nullptr;
73 m_jitCodeForConstruct = nullptr;
74 m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr<JSEntryPtrTag>();
75 m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr<JSEntryPtrTag>();
76
77 switch (type()) {
78 case FunctionExecutableType: {
79 FunctionExecutable* executable = static_cast<FunctionExecutable*>(this);
80 executable->m_codeBlockForCall.clear();
81 executable->m_codeBlockForConstruct.clear();
82 break;
83 }
84 case EvalExecutableType: {
85 EvalExecutable* executable = static_cast<EvalExecutable*>(this);
86 executable->m_evalCodeBlock.clear();
87 executable->m_unlinkedEvalCodeBlock.clear();
88 break;
89 }
90 case ProgramExecutableType: {
91 ProgramExecutable* executable = static_cast<ProgramExecutable*>(this);
92 executable->m_programCodeBlock.clear();
93 executable->m_unlinkedProgramCodeBlock.clear();
94 break;
95 }
96 case ModuleProgramExecutableType: {
97 ModuleProgramExecutable* executable = static_cast<ModuleProgramExecutable*>(this);
98 executable->m_moduleProgramCodeBlock.clear();
99 executable->m_unlinkedModuleProgramCodeBlock.clear();
100 executable->m_moduleEnvironmentSymbolTable.clear();
101 break;
102 }
103 default:
104 RELEASE_ASSERT_NOT_REACHED();
105 break;
106 }
107
108 ASSERT(&VM::SpaceAndSet::setFor(*subspace()) == &clearableCodeSet);
109 clearableCodeSet.remove(this);
110}
111
112void ScriptExecutable::installCode(CodeBlock* codeBlock)
113{
114 installCode(*codeBlock->vm(), codeBlock, codeBlock->codeType(), codeBlock->specializationKind());
115}
116
117void ScriptExecutable::installCode(VM& vm, CodeBlock* genericCodeBlock, CodeType codeType, CodeSpecializationKind kind)
118{
119 if (genericCodeBlock)
120 CODEBLOCK_LOG_EVENT(genericCodeBlock, "installCode", ());
121
122 CodeBlock* oldCodeBlock = nullptr;
123
124 switch (codeType) {
125 case GlobalCode: {
126 ProgramExecutable* executable = jsCast<ProgramExecutable*>(this);
127 ProgramCodeBlock* codeBlock = static_cast<ProgramCodeBlock*>(genericCodeBlock);
128
129 ASSERT(kind == CodeForCall);
130
131 oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_programCodeBlock.get());
132 executable->m_programCodeBlock.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
133 break;
134 }
135
136 case ModuleCode: {
137 ModuleProgramExecutable* executable = jsCast<ModuleProgramExecutable*>(this);
138 ModuleProgramCodeBlock* codeBlock = static_cast<ModuleProgramCodeBlock*>(genericCodeBlock);
139
140 ASSERT(kind == CodeForCall);
141
142 oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_moduleProgramCodeBlock.get());
143 executable->m_moduleProgramCodeBlock.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
144 break;
145 }
146
147 case EvalCode: {
148 EvalExecutable* executable = jsCast<EvalExecutable*>(this);
149 EvalCodeBlock* codeBlock = static_cast<EvalCodeBlock*>(genericCodeBlock);
150
151 ASSERT(kind == CodeForCall);
152
153 oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_evalCodeBlock.get());
154 executable->m_evalCodeBlock.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
155 break;
156 }
157
158 case FunctionCode: {
159 FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
160 FunctionCodeBlock* codeBlock = static_cast<FunctionCodeBlock*>(genericCodeBlock);
161
162 switch (kind) {
163 case CodeForCall:
164 oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_codeBlockForCall.get());
165 executable->m_codeBlockForCall.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
166 break;
167 case CodeForConstruct:
168 oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_codeBlockForConstruct.get());
169 executable->m_codeBlockForConstruct.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
170 break;
171 }
172 break;
173 }
174 }
175
176 switch (kind) {
177 case CodeForCall:
178 m_jitCodeForCall = genericCodeBlock ? genericCodeBlock->jitCode() : nullptr;
179 m_jitCodeForCallWithArityCheck = nullptr;
180 break;
181 case CodeForConstruct:
182 m_jitCodeForConstruct = genericCodeBlock ? genericCodeBlock->jitCode() : nullptr;
183 m_jitCodeForConstructWithArityCheck = nullptr;
184 break;
185 }
186
187 auto& clearableCodeSet = VM::SpaceAndSet::setFor(*subspace());
188 if (hasClearableCode(vm))
189 clearableCodeSet.add(this);
190 else
191 clearableCodeSet.remove(this);
192
193 if (genericCodeBlock) {
194 RELEASE_ASSERT(genericCodeBlock->ownerExecutable() == this);
195 RELEASE_ASSERT(JITCode::isExecutableScript(genericCodeBlock->jitType()));
196
197 if (UNLIKELY(Options::verboseOSR()))
198 dataLog("Installing ", *genericCodeBlock, "\n");
199
200 if (UNLIKELY(vm.m_perBytecodeProfiler))
201 vm.m_perBytecodeProfiler->ensureBytecodesFor(genericCodeBlock);
202
203 Debugger* debugger = genericCodeBlock->globalObject()->debugger();
204 if (UNLIKELY(debugger))
205 debugger->registerCodeBlock(genericCodeBlock);
206 }
207
208 if (oldCodeBlock)
209 oldCodeBlock->unlinkIncomingCalls();
210
211 vm.heap.writeBarrier(this);
212}
213
214bool ScriptExecutable::hasClearableCode(VM& vm) const
215{
216 if (m_jitCodeForCall
217 || m_jitCodeForConstruct
218 || m_jitCodeForCallWithArityCheck
219 || m_jitCodeForConstructWithArityCheck)
220 return true;
221
222 if (structure(vm)->classInfo() == FunctionExecutable::info()) {
223 auto* executable = static_cast<const FunctionExecutable*>(this);
224 if (executable->m_codeBlockForCall || executable->m_codeBlockForConstruct)
225 return true;
226
227 } else if (structure(vm)->classInfo() == EvalExecutable::info()) {
228 auto* executable = static_cast<const EvalExecutable*>(this);
229 if (executable->m_evalCodeBlock || executable->m_unlinkedEvalCodeBlock)
230 return true;
231
232 } else if (structure(vm)->classInfo() == ProgramExecutable::info()) {
233 auto* executable = static_cast<const ProgramExecutable*>(this);
234 if (executable->m_programCodeBlock || executable->m_unlinkedProgramCodeBlock)
235 return true;
236
237 } else if (structure(vm)->classInfo() == ModuleProgramExecutable::info()) {
238 auto* executable = static_cast<const ModuleProgramExecutable*>(this);
239 if (executable->m_moduleProgramCodeBlock
240 || executable->m_unlinkedModuleProgramCodeBlock
241 || executable->m_moduleEnvironmentSymbolTable)
242 return true;
243 }
244 return false;
245}
246
247CodeBlock* ScriptExecutable::newCodeBlockFor(
248 CodeSpecializationKind kind, JSFunction* function, JSScope* scope, Exception*& exception)
249{
250 VM* vm = scope->vm();
251 auto throwScope = DECLARE_THROW_SCOPE(*vm);
252
253 ASSERT(vm->heap.isDeferred());
254 ASSERT(endColumn() != UINT_MAX);
255
256 JSGlobalObject* globalObject = scope->globalObject(*vm);
257 ExecState* exec = globalObject->globalExec();
258
259 if (classInfo(*vm) == EvalExecutable::info()) {
260 EvalExecutable* executable = jsCast<EvalExecutable*>(this);
261 RELEASE_ASSERT(kind == CodeForCall);
262 RELEASE_ASSERT(!executable->m_evalCodeBlock);
263 RELEASE_ASSERT(!function);
264 auto codeBlock = EvalCodeBlock::create(vm,
265 executable, executable->m_unlinkedEvalCodeBlock.get(), scope);
266 EXCEPTION_ASSERT(throwScope.exception() || codeBlock);
267 if (!codeBlock) {
268 exception = throwException(
269 exec, throwScope,
270 createOutOfMemoryError(exec));
271 return nullptr;
272 }
273 return codeBlock;
274 }
275
276 if (classInfo(*vm) == ProgramExecutable::info()) {
277 ProgramExecutable* executable = jsCast<ProgramExecutable*>(this);
278 RELEASE_ASSERT(kind == CodeForCall);
279 RELEASE_ASSERT(!executable->m_programCodeBlock);
280 RELEASE_ASSERT(!function);
281 auto codeBlock = ProgramCodeBlock::create(vm,
282 executable, executable->m_unlinkedProgramCodeBlock.get(), scope);
283 EXCEPTION_ASSERT(throwScope.exception() || codeBlock);
284 if (!codeBlock) {
285 exception = throwException(
286 exec, throwScope,
287 createOutOfMemoryError(exec));
288 return nullptr;
289 }
290 return codeBlock;
291 }
292
293 if (classInfo(*vm) == ModuleProgramExecutable::info()) {
294 ModuleProgramExecutable* executable = jsCast<ModuleProgramExecutable*>(this);
295 RELEASE_ASSERT(kind == CodeForCall);
296 RELEASE_ASSERT(!executable->m_moduleProgramCodeBlock);
297 RELEASE_ASSERT(!function);
298 auto codeBlock = ModuleProgramCodeBlock::create(vm,
299 executable, executable->m_unlinkedModuleProgramCodeBlock.get(), scope);
300 EXCEPTION_ASSERT(throwScope.exception() || codeBlock);
301 if (!codeBlock) {
302 exception = throwException(
303 exec, throwScope,
304 createOutOfMemoryError(exec));
305 return nullptr;
306 }
307 return codeBlock;
308 }
309
310 RELEASE_ASSERT(classInfo(*vm) == FunctionExecutable::info());
311 RELEASE_ASSERT(function);
312 FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
313 RELEASE_ASSERT(!executable->codeBlockFor(kind));
314 ParserError error;
315 DebuggerMode debuggerMode = globalObject->hasInteractiveDebugger() ? DebuggerOn : DebuggerOff;
316 UnlinkedFunctionCodeBlock* unlinkedCodeBlock =
317 executable->m_unlinkedExecutable->unlinkedCodeBlockFor(
318 *vm, executable->source(), kind, debuggerMode, error,
319 executable->parseMode());
320 recordParse(
321 executable->m_unlinkedExecutable->features(),
322 executable->m_unlinkedExecutable->hasCapturedVariables(),
323 lastLine(), endColumn());
324 if (!unlinkedCodeBlock) {
325 exception = throwException(
326 globalObject->globalExec(), throwScope,
327 error.toErrorObject(globalObject, executable->source()));
328 return nullptr;
329 }
330
331 RELEASE_AND_RETURN(throwScope, FunctionCodeBlock::create(vm, executable, unlinkedCodeBlock, scope));
332}
333
334CodeBlock* ScriptExecutable::newReplacementCodeBlockFor(
335 CodeSpecializationKind kind)
336{
337 VM& vm = *this->vm();
338 if (classInfo(vm) == EvalExecutable::info()) {
339 RELEASE_ASSERT(kind == CodeForCall);
340 EvalExecutable* executable = jsCast<EvalExecutable*>(this);
341 EvalCodeBlock* baseline = static_cast<EvalCodeBlock*>(
342 executable->codeBlock()->baselineVersion());
343 EvalCodeBlock* result = EvalCodeBlock::create(&vm,
344 CodeBlock::CopyParsedBlock, *baseline);
345 result->setAlternative(vm, baseline);
346 return result;
347 }
348
349 if (classInfo(vm) == ProgramExecutable::info()) {
350 RELEASE_ASSERT(kind == CodeForCall);
351 ProgramExecutable* executable = jsCast<ProgramExecutable*>(this);
352 ProgramCodeBlock* baseline = static_cast<ProgramCodeBlock*>(
353 executable->codeBlock()->baselineVersion());
354 ProgramCodeBlock* result = ProgramCodeBlock::create(&vm,
355 CodeBlock::CopyParsedBlock, *baseline);
356 result->setAlternative(vm, baseline);
357 return result;
358 }
359
360 if (classInfo(vm) == ModuleProgramExecutable::info()) {
361 RELEASE_ASSERT(kind == CodeForCall);
362 ModuleProgramExecutable* executable = jsCast<ModuleProgramExecutable*>(this);
363 ModuleProgramCodeBlock* baseline = static_cast<ModuleProgramCodeBlock*>(
364 executable->codeBlock()->baselineVersion());
365 ModuleProgramCodeBlock* result = ModuleProgramCodeBlock::create(&vm,
366 CodeBlock::CopyParsedBlock, *baseline);
367 result->setAlternative(vm, baseline);
368 return result;
369 }
370
371 RELEASE_ASSERT(classInfo(vm) == FunctionExecutable::info());
372 FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
373 FunctionCodeBlock* baseline = static_cast<FunctionCodeBlock*>(
374 executable->codeBlockFor(kind)->baselineVersion());
375 FunctionCodeBlock* result = FunctionCodeBlock::create(&vm,
376 CodeBlock::CopyParsedBlock, *baseline);
377 result->setAlternative(vm, baseline);
378 return result;
379}
380
381static void setupLLInt(CodeBlock* codeBlock)
382{
383 LLInt::setEntrypoint(codeBlock);
384}
385
386static void setupJIT(VM& vm, CodeBlock* codeBlock)
387{
388#if ENABLE(JIT)
389 CompilationResult result = JIT::compile(&vm, codeBlock, JITCompilationMustSucceed);
390 RELEASE_ASSERT(result == CompilationSuccessful);
391#else
392 UNUSED_PARAM(vm);
393 UNUSED_PARAM(codeBlock);
394 UNREACHABLE_FOR_PLATFORM();
395#endif
396}
397
398Exception* ScriptExecutable::prepareForExecutionImpl(
399 VM& vm, JSFunction* function, JSScope* scope, CodeSpecializationKind kind, CodeBlock*& resultCodeBlock)
400{
401 auto throwScope = DECLARE_THROW_SCOPE(vm);
402 DeferGCForAWhile deferGC(vm.heap);
403
404 if (UNLIKELY(vm.getAndClearFailNextNewCodeBlock())) {
405 auto& state = *scope->globalObject(vm)->globalExec();
406 return throwException(&state, throwScope, createError(&state, "Forced Failure"_s));
407 }
408
409 Exception* exception = nullptr;
410 CodeBlock* codeBlock = newCodeBlockFor(kind, function, scope, exception);
411 resultCodeBlock = codeBlock;
412 EXCEPTION_ASSERT(!!throwScope.exception() == !codeBlock);
413 if (UNLIKELY(!codeBlock))
414 return exception;
415
416 if (Options::validateBytecode())
417 codeBlock->validate();
418
419 if (Options::useLLInt())
420 setupLLInt(codeBlock);
421 else
422 setupJIT(vm, codeBlock);
423
424 installCode(vm, codeBlock, codeBlock->codeType(), codeBlock->specializationKind());
425 return nullptr;
426}
427
428CodeBlockHash ScriptExecutable::hashFor(CodeSpecializationKind kind) const
429{
430 return CodeBlockHash(source(), kind);
431}
432
433Optional<int> ScriptExecutable::overrideLineNumber(VM& vm) const
434{
435 if (inherits<FunctionExecutable>(vm))
436 return jsCast<const FunctionExecutable*>(this)->overrideLineNumber();
437 return WTF::nullopt;
438}
439
440unsigned ScriptExecutable::typeProfilingStartOffset(VM& vm) const
441{
442 if (inherits<FunctionExecutable>(vm))
443 return jsCast<const FunctionExecutable*>(this)->typeProfilingStartOffset(vm);
444 if (inherits<EvalExecutable>(vm))
445 return UINT_MAX;
446 return 0;
447}
448
449unsigned ScriptExecutable::typeProfilingEndOffset(VM& vm) const
450{
451 if (inherits<FunctionExecutable>(vm))
452 return jsCast<const FunctionExecutable*>(this)->typeProfilingEndOffset(vm);
453 if (inherits<EvalExecutable>(vm))
454 return UINT_MAX;
455 return source().length() - 1;
456}
457
458void ScriptExecutable::recordParse(CodeFeatures features, bool hasCapturedVariables, int lastLine, unsigned endColumn)
459{
460 switch (type()) {
461 case FunctionExecutableType:
462 // Since UnlinkedFunctionExecutable holds the information to calculate lastLine and endColumn, we do not need to remember them in ScriptExecutable's fields.
463 jsCast<FunctionExecutable*>(this)->recordParse(features, hasCapturedVariables);
464 return;
465 default:
466 jsCast<GlobalExecutable*>(this)->recordParse(features, hasCapturedVariables, lastLine, endColumn);
467 return;
468 }
469}
470
471int ScriptExecutable::lastLine() const
472{
473 switch (type()) {
474 case FunctionExecutableType:
475 return jsCast<const FunctionExecutable*>(this)->lastLine();
476 default:
477 return jsCast<const GlobalExecutable*>(this)->lastLine();
478 }
479 return 0;
480}
481
482unsigned ScriptExecutable::endColumn() const
483{
484 switch (type()) {
485 case FunctionExecutableType:
486 return jsCast<const FunctionExecutable*>(this)->endColumn();
487 default:
488 return jsCast<const GlobalExecutable*>(this)->endColumn();
489 }
490 return 0;
491}
492
493} // namespace JSC
494