1/*
2 * Copyright (C) 2009-2018 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#pragma once
27
28#include "ExecutableToCodeBlockEdge.h"
29#include "ScriptExecutable.h"
30#include "SourceCode.h"
31#include <wtf/Box.h>
32#include <wtf/Markable.h>
33
34namespace JSC {
35
36struct FunctionOverrideInfo;
37
38class FunctionExecutable final : public ScriptExecutable {
39 friend class JIT;
40 friend class LLIntOffsetsExtractor;
41public:
42 typedef ScriptExecutable Base;
43 static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
44
45 template<typename CellType, SubspaceAccess>
46 static IsoSubspace* subspaceFor(VM& vm)
47 {
48 return &vm.functionExecutableSpace.space;
49 }
50
51 static FunctionExecutable* create(VM& vm, ScriptExecutable* topLevelExecutable, const SourceCode& source, UnlinkedFunctionExecutable* unlinkedExecutable, Intrinsic intrinsic)
52 {
53 FunctionExecutable* executable = new (NotNull, allocateCell<FunctionExecutable>(vm.heap)) FunctionExecutable(vm, source, unlinkedExecutable, intrinsic);
54 executable->finishCreation(vm, topLevelExecutable);
55 return executable;
56 }
57 static FunctionExecutable* fromGlobalCode(
58 const Identifier& name, ExecState&, const SourceCode&,
59 JSObject*& exception, int overrideLineNumber, Optional<int> functionConstructorParametersEndPosition);
60
61 static void destroy(JSCell*);
62
63 UnlinkedFunctionExecutable* unlinkedExecutable() const
64 {
65 return m_unlinkedExecutable.get();
66 }
67
68 // Returns either call or construct bytecode. This can be appropriate
69 // for answering questions that that don't vary between call and construct --
70 // for example, argumentsRegister().
71 FunctionCodeBlock* eitherCodeBlock()
72 {
73 ExecutableToCodeBlockEdge* edge;
74 if (m_codeBlockForCall)
75 edge = m_codeBlockForCall.get();
76 else
77 edge = m_codeBlockForConstruct.get();
78 return bitwise_cast<FunctionCodeBlock*>(ExecutableToCodeBlockEdge::unwrap(edge));
79 }
80
81 bool isGeneratedForCall() const
82 {
83 return !!m_codeBlockForCall;
84 }
85
86 FunctionCodeBlock* codeBlockForCall()
87 {
88 return bitwise_cast<FunctionCodeBlock*>(ExecutableToCodeBlockEdge::unwrap(m_codeBlockForCall.get()));
89 }
90
91 bool isGeneratedForConstruct() const
92 {
93 return !!m_codeBlockForConstruct;
94 }
95
96 FunctionCodeBlock* codeBlockForConstruct()
97 {
98 return bitwise_cast<FunctionCodeBlock*>(ExecutableToCodeBlockEdge::unwrap(m_codeBlockForConstruct.get()));
99 }
100
101 bool isGeneratedFor(CodeSpecializationKind kind)
102 {
103 if (kind == CodeForCall)
104 return isGeneratedForCall();
105 ASSERT(kind == CodeForConstruct);
106 return isGeneratedForConstruct();
107 }
108
109 FunctionCodeBlock* codeBlockFor(CodeSpecializationKind kind)
110 {
111 if (kind == CodeForCall)
112 return codeBlockForCall();
113 ASSERT(kind == CodeForConstruct);
114 return codeBlockForConstruct();
115 }
116
117 FunctionCodeBlock* baselineCodeBlockFor(CodeSpecializationKind);
118
119 FunctionCodeBlock* profiledCodeBlockFor(CodeSpecializationKind kind)
120 {
121 return baselineCodeBlockFor(kind);
122 }
123
124 RefPtr<TypeSet> returnStatementTypeSet()
125 {
126 RareData& rareData = ensureRareData();
127 if (!rareData.m_returnStatementTypeSet)
128 rareData.m_returnStatementTypeSet = TypeSet::create();
129 return rareData.m_returnStatementTypeSet;
130 }
131
132 FunctionMode functionMode() { return m_unlinkedExecutable->functionMode(); }
133 bool isBuiltinFunction() const { return m_unlinkedExecutable->isBuiltinFunction(); }
134 ConstructAbility constructAbility() const { return m_unlinkedExecutable->constructAbility(); }
135 bool isClass() const { return m_unlinkedExecutable->isClass(); }
136 bool isArrowFunction() const { return parseMode() == SourceParseMode::ArrowFunctionMode; }
137 bool isGetter() const { return parseMode() == SourceParseMode::GetterMode; }
138 bool isSetter() const { return parseMode() == SourceParseMode::SetterMode; }
139 bool isGenerator() const { return isGeneratorParseMode(parseMode()); }
140 bool isAsyncGenerator() const { return isAsyncGeneratorParseMode(parseMode()); }
141 bool isMethod() const { return parseMode() == SourceParseMode::MethodMode; }
142 bool hasCallerAndArgumentsProperties() const
143 {
144 // Per https://tc39.github.io/ecma262/#sec-forbidden-extensions, only sloppy-mode non-builtin functions in old-style (pre-ES6) syntactic forms can contain
145 // "caller" and "arguments".
146 return !isStrictMode() && parseMode() == SourceParseMode::NormalFunctionMode && !isClassConstructorFunction();
147 }
148 bool hasPrototypeProperty() const
149 {
150 return SourceParseModeSet(
151 SourceParseMode::NormalFunctionMode,
152 SourceParseMode::GeneratorBodyMode,
153 SourceParseMode::GeneratorWrapperFunctionMode,
154 SourceParseMode::GeneratorWrapperMethodMode,
155 SourceParseMode::AsyncGeneratorWrapperFunctionMode,
156 SourceParseMode::AsyncGeneratorWrapperMethodMode,
157 SourceParseMode::AsyncGeneratorBodyMode
158 ).contains(parseMode()) || isClass();
159 }
160 DerivedContextType derivedContextType() const { return m_unlinkedExecutable->derivedContextType(); }
161 bool isClassConstructorFunction() const { return m_unlinkedExecutable->isClassConstructorFunction(); }
162 const Identifier& name() { return m_unlinkedExecutable->name(); }
163 const Identifier& ecmaName() { return m_unlinkedExecutable->ecmaName(); }
164 unsigned parameterCount() const { return m_unlinkedExecutable->parameterCount(); } // Excluding 'this'!
165 SourceParseMode parseMode() const { return m_unlinkedExecutable->parseMode(); }
166 JSParserScriptMode scriptMode() const { return m_unlinkedExecutable->scriptMode(); }
167 SourceCode classSource() const { return m_unlinkedExecutable->classSource(); }
168
169 static void visitChildren(JSCell*, SlotVisitor&);
170 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
171 {
172 return Structure::create(vm, globalObject, proto, TypeInfo(FunctionExecutableType, StructureFlags), info());
173 }
174
175 void setOverrideLineNumber(int overrideLineNumber)
176 {
177 if (overrideLineNumber == -1) {
178 if (UNLIKELY(m_rareData))
179 m_rareData->m_overrideLineNumber = WTF::nullopt;
180 return;
181 }
182 ensureRareData().m_overrideLineNumber = overrideLineNumber;
183 }
184
185 Optional<int> overrideLineNumber() const
186 {
187 if (UNLIKELY(m_rareData))
188 return m_rareData->m_overrideLineNumber;
189 return WTF::nullopt;
190 }
191
192 int lineCount() const
193 {
194 if (UNLIKELY(m_rareData))
195 return m_rareData->m_lineCount;
196 return m_unlinkedExecutable->lineCount();
197 }
198
199 int endColumn() const
200 {
201 if (UNLIKELY(m_rareData))
202 return m_rareData->m_endColumn;
203 return m_unlinkedExecutable->linkedEndColumn(m_source.startColumn().oneBasedInt());
204 }
205
206 int firstLine() const
207 {
208 return source().firstLine().oneBasedInt();
209 }
210
211 int lastLine() const
212 {
213 return firstLine() + lineCount();
214 }
215
216 unsigned typeProfilingStartOffset(VM&) const
217 {
218 return typeProfilingStartOffset();
219 }
220
221 unsigned typeProfilingStartOffset() const
222 {
223 if (UNLIKELY(m_rareData))
224 return m_rareData->m_typeProfilingStartOffset;
225 return m_unlinkedExecutable->typeProfilingStartOffset();
226 }
227
228 unsigned typeProfilingEndOffset(VM&) const
229 {
230 return typeProfilingEndOffset();
231 }
232
233 unsigned typeProfilingEndOffset() const
234 {
235 if (UNLIKELY(m_rareData))
236 return m_rareData->m_typeProfilingEndOffset;
237 return m_unlinkedExecutable->typeProfilingEndOffset();
238 }
239
240 unsigned parametersStartOffset() const
241 {
242 if (UNLIKELY(m_rareData))
243 return m_rareData->m_parametersStartOffset;
244 return m_unlinkedExecutable->parametersStartOffset();
245 }
246
247 void overrideInfo(const FunctionOverrideInfo&);
248
249 DECLARE_INFO;
250
251 InferredValue<JSFunction>& singleton()
252 {
253 return m_singleton;
254 }
255
256 void notifyCreation(VM& vm, JSFunction* function, const char* reason)
257 {
258 m_singleton.notifyWrite(vm, this, function, reason);
259 }
260
261 // Cached poly proto structure for the result of constructing this executable.
262 Structure* cachedPolyProtoStructure()
263 {
264 if (UNLIKELY(m_rareData))
265 return m_rareData->m_cachedPolyProtoStructure.get();
266 return nullptr;
267 }
268 void setCachedPolyProtoStructure(VM& vm, Structure* structure)
269 {
270 ensureRareData().m_cachedPolyProtoStructure.set(vm, this, structure);
271 }
272
273 InlineWatchpointSet& ensurePolyProtoWatchpoint()
274 {
275 if (!m_polyProtoWatchpoint)
276 m_polyProtoWatchpoint = Box<InlineWatchpointSet>::create(IsWatched);
277 return *m_polyProtoWatchpoint;
278 }
279
280 Box<InlineWatchpointSet> sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
281
282 ScriptExecutable* topLevelExecutable() const { return m_topLevelExecutable.get(); }
283
284 TemplateObjectMap& ensureTemplateObjectMap(VM&);
285
286 void finalizeUnconditionally(VM&);
287
288private:
289 friend class ExecutableBase;
290 FunctionExecutable(VM&, const SourceCode&, UnlinkedFunctionExecutable*, Intrinsic);
291
292 void finishCreation(VM&, ScriptExecutable* topLevelExecutable);
293
294 friend class ScriptExecutable;
295
296 struct RareData {
297 WTF_MAKE_STRUCT_FAST_ALLOCATED;
298 RefPtr<TypeSet> m_returnStatementTypeSet;
299 unsigned m_lineCount;
300 unsigned m_endColumn;
301 Markable<int, IntegralMarkableTraits<int, -1>> m_overrideLineNumber;
302 unsigned m_parametersStartOffset { 0 };
303 unsigned m_typeProfilingStartOffset { UINT_MAX };
304 unsigned m_typeProfilingEndOffset { UINT_MAX };
305 std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
306 WriteBarrier<Structure> m_cachedPolyProtoStructure;
307 };
308
309 RareData& ensureRareData()
310 {
311 if (LIKELY(m_rareData))
312 return *m_rareData;
313 return ensureRareDataSlow();
314 }
315 RareData& ensureRareDataSlow();
316
317 // FIXME: We can merge rareData pointer and top-level executable pointer. First time, setting parent.
318 // If RareData is required, materialize RareData, swap it, and store top-level executable pointer inside RareData.
319 // https://bugs.webkit.org/show_bug.cgi?id=197625
320 std::unique_ptr<RareData> m_rareData;
321 WriteBarrier<ScriptExecutable> m_topLevelExecutable;
322 WriteBarrier<UnlinkedFunctionExecutable> m_unlinkedExecutable;
323 WriteBarrier<ExecutableToCodeBlockEdge> m_codeBlockForCall;
324 WriteBarrier<ExecutableToCodeBlockEdge> m_codeBlockForConstruct;
325 InferredValue<JSFunction> m_singleton;
326 Box<InlineWatchpointSet> m_polyProtoWatchpoint;
327};
328
329} // namespace JSC
330