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 bool isAnonymousBuiltinFunction() const { return m_unlinkedExecutable->isAnonymousBuiltinFunction(); }
135 ConstructAbility constructAbility() const { return m_unlinkedExecutable->constructAbility(); }
136 bool isClass() const { return m_unlinkedExecutable->isClass(); }
137 bool isArrowFunction() const { return parseMode() == SourceParseMode::ArrowFunctionMode; }
138 bool isGetter() const { return parseMode() == SourceParseMode::GetterMode; }
139 bool isSetter() const { return parseMode() == SourceParseMode::SetterMode; }
140 bool isGenerator() const { return isGeneratorParseMode(parseMode()); }
141 bool isAsyncGenerator() const { return isAsyncGeneratorParseMode(parseMode()); }
142 bool isMethod() const { return parseMode() == SourceParseMode::MethodMode; }
143 bool hasCallerAndArgumentsProperties() const
144 {
145 // Per https://tc39.github.io/ecma262/#sec-forbidden-extensions, only sloppy-mode non-builtin functions in old-style (pre-ES6) syntactic forms can contain
146 // "caller" and "arguments".
147 return !isStrictMode() && parseMode() == SourceParseMode::NormalFunctionMode && !isClassConstructorFunction();
148 }
149 bool hasPrototypeProperty() const
150 {
151 return SourceParseModeSet(
152 SourceParseMode::NormalFunctionMode,
153 SourceParseMode::GeneratorBodyMode,
154 SourceParseMode::GeneratorWrapperFunctionMode,
155 SourceParseMode::GeneratorWrapperMethodMode,
156 SourceParseMode::AsyncGeneratorWrapperFunctionMode,
157 SourceParseMode::AsyncGeneratorWrapperMethodMode,
158 SourceParseMode::AsyncGeneratorBodyMode
159 ).contains(parseMode()) || isClass();
160 }
161 DerivedContextType derivedContextType() const { return m_unlinkedExecutable->derivedContextType(); }
162 bool isClassConstructorFunction() const { return m_unlinkedExecutable->isClassConstructorFunction(); }
163 const Identifier& name() { return m_unlinkedExecutable->name(); }
164 const Identifier& ecmaName() { return m_unlinkedExecutable->ecmaName(); }
165 unsigned parameterCount() const { return m_unlinkedExecutable->parameterCount(); } // Excluding 'this'!
166 SourceParseMode parseMode() const { return m_unlinkedExecutable->parseMode(); }
167 JSParserScriptMode scriptMode() const { return m_unlinkedExecutable->scriptMode(); }
168 SourceCode classSource() const { return m_unlinkedExecutable->classSource(); }
169
170 static void visitChildren(JSCell*, SlotVisitor&);
171 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
172 {
173 return Structure::create(vm, globalObject, proto, TypeInfo(FunctionExecutableType, StructureFlags), info());
174 }
175
176 void setOverrideLineNumber(int overrideLineNumber)
177 {
178 if (overrideLineNumber == -1) {
179 if (UNLIKELY(m_rareData))
180 m_rareData->m_overrideLineNumber = WTF::nullopt;
181 return;
182 }
183 ensureRareData().m_overrideLineNumber = overrideLineNumber;
184 }
185
186 Optional<int> overrideLineNumber() const
187 {
188 if (UNLIKELY(m_rareData))
189 return m_rareData->m_overrideLineNumber;
190 return WTF::nullopt;
191 }
192
193 int lineCount() const
194 {
195 if (UNLIKELY(m_rareData))
196 return m_rareData->m_lineCount;
197 return m_unlinkedExecutable->lineCount();
198 }
199
200 int endColumn() const
201 {
202 if (UNLIKELY(m_rareData))
203 return m_rareData->m_endColumn;
204 return m_unlinkedExecutable->linkedEndColumn(m_source.startColumn().oneBasedInt());
205 }
206
207 int firstLine() const
208 {
209 return source().firstLine().oneBasedInt();
210 }
211
212 int lastLine() const
213 {
214 return firstLine() + lineCount();
215 }
216
217 unsigned typeProfilingStartOffset(VM&) const
218 {
219 return typeProfilingStartOffset();
220 }
221
222 unsigned typeProfilingStartOffset() const
223 {
224 if (UNLIKELY(m_rareData))
225 return m_rareData->m_typeProfilingStartOffset;
226 return m_unlinkedExecutable->typeProfilingStartOffset();
227 }
228
229 unsigned typeProfilingEndOffset(VM&) const
230 {
231 return typeProfilingEndOffset();
232 }
233
234 unsigned typeProfilingEndOffset() const
235 {
236 if (UNLIKELY(m_rareData))
237 return m_rareData->m_typeProfilingEndOffset;
238 return m_unlinkedExecutable->typeProfilingEndOffset();
239 }
240
241 unsigned parametersStartOffset() const
242 {
243 if (UNLIKELY(m_rareData))
244 return m_rareData->m_parametersStartOffset;
245 return m_unlinkedExecutable->parametersStartOffset();
246 }
247
248 void overrideInfo(const FunctionOverrideInfo&);
249
250 DECLARE_INFO;
251
252 InferredValue<JSFunction>& singleton()
253 {
254 return m_singleton;
255 }
256
257 void notifyCreation(VM& vm, JSFunction* function, const char* reason)
258 {
259 m_singleton.notifyWrite(vm, this, function, reason);
260 }
261
262 // Cached poly proto structure for the result of constructing this executable.
263 Structure* cachedPolyProtoStructure()
264 {
265 if (UNLIKELY(m_rareData))
266 return m_rareData->m_cachedPolyProtoStructure.get();
267 return nullptr;
268 }
269 void setCachedPolyProtoStructure(VM& vm, Structure* structure)
270 {
271 ensureRareData().m_cachedPolyProtoStructure.set(vm, this, structure);
272 }
273
274 InlineWatchpointSet& ensurePolyProtoWatchpoint()
275 {
276 if (!m_polyProtoWatchpoint)
277 m_polyProtoWatchpoint = Box<InlineWatchpointSet>::create(IsWatched);
278 return *m_polyProtoWatchpoint;
279 }
280
281 Box<InlineWatchpointSet> sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
282
283 ScriptExecutable* topLevelExecutable() const { return m_topLevelExecutable.get(); }
284
285 TemplateObjectMap& ensureTemplateObjectMap(VM&);
286
287 void finalizeUnconditionally(VM&);
288
289private:
290 friend class ExecutableBase;
291 FunctionExecutable(VM&, const SourceCode&, UnlinkedFunctionExecutable*, Intrinsic);
292
293 void finishCreation(VM&, ScriptExecutable* topLevelExecutable);
294
295 friend class ScriptExecutable;
296
297 struct RareData {
298 WTF_MAKE_STRUCT_FAST_ALLOCATED;
299 RefPtr<TypeSet> m_returnStatementTypeSet;
300 unsigned m_lineCount;
301 unsigned m_endColumn;
302 Markable<int, IntegralMarkableTraits<int, -1>> m_overrideLineNumber;
303 unsigned m_parametersStartOffset { 0 };
304 unsigned m_typeProfilingStartOffset { UINT_MAX };
305 unsigned m_typeProfilingEndOffset { UINT_MAX };
306 std::unique_ptr<TemplateObjectMap> m_templateObjectMap;
307 WriteBarrier<Structure> m_cachedPolyProtoStructure;
308 };
309
310 RareData& ensureRareData()
311 {
312 if (LIKELY(m_rareData))
313 return *m_rareData;
314 return ensureRareDataSlow();
315 }
316 RareData& ensureRareDataSlow();
317
318 // FIXME: We can merge rareData pointer and top-level executable pointer. First time, setting parent.
319 // If RareData is required, materialize RareData, swap it, and store top-level executable pointer inside RareData.
320 // https://bugs.webkit.org/show_bug.cgi?id=197625
321 std::unique_ptr<RareData> m_rareData;
322 WriteBarrier<ScriptExecutable> m_topLevelExecutable;
323 WriteBarrier<UnlinkedFunctionExecutable> m_unlinkedExecutable;
324 WriteBarrier<ExecutableToCodeBlockEdge> m_codeBlockForCall;
325 WriteBarrier<ExecutableToCodeBlockEdge> m_codeBlockForConstruct;
326 InferredValue<JSFunction> m_singleton;
327 Box<InlineWatchpointSet> m_polyProtoWatchpoint;
328};
329
330} // namespace JSC
331