]>
Commit | Line | Data |
---|---|---|
9dae56ea | 1 | /* |
93a37866 | 2 | * Copyright (C) 2008, 2009, 2012, 2013 Apple Inc. All rights reserved. |
9dae56ea | 3 | * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> |
93a37866 | 4 | * Copyright (C) 2012 Igalia, S.L. |
9dae56ea A |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions and the following disclaimer. | |
12 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
16 | * its contributors may be used to endorse or promote products derived | |
17 | * from this software without specific prior written permission. | |
18 | * | |
19 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
22 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 | */ | |
30 | ||
31 | #ifndef BytecodeGenerator_h | |
32 | #define BytecodeGenerator_h | |
33 | ||
34 | #include "CodeBlock.h" | |
6fe7ccc8 | 35 | #include <wtf/HashTraits.h> |
9dae56ea A |
36 | #include "Instruction.h" |
37 | #include "Label.h" | |
38 | #include "LabelScope.h" | |
39 | #include "Interpreter.h" | |
93a37866 | 40 | #include "ParserError.h" |
9dae56ea | 41 | #include "RegisterID.h" |
9dae56ea A |
42 | #include "SymbolTable.h" |
43 | #include "Debugger.h" | |
44 | #include "Nodes.h" | |
93a37866 A |
45 | #include "StaticPropertyAnalyzer.h" |
46 | #include "UnlinkedCodeBlock.h" | |
9dae56ea | 47 | #include <wtf/PassRefPtr.h> |
ba379fdc | 48 | #include <wtf/SegmentedVector.h> |
9dae56ea A |
49 | #include <wtf/Vector.h> |
50 | ||
51 | namespace JSC { | |
52 | ||
53 | class Identifier; | |
6fe7ccc8 | 54 | class Label; |
93a37866 A |
55 | class JSScope; |
56 | ||
57 | enum ExpectedFunction { | |
58 | NoExpectedFunction, | |
59 | ExpectObjectConstructor, | |
60 | ExpectArrayConstructor | |
61 | }; | |
14957cd0 A |
62 | |
63 | class CallArguments { | |
64 | public: | |
65 | CallArguments(BytecodeGenerator& generator, ArgumentsNode* argumentsNode); | |
66 | ||
67 | RegisterID* thisRegister() { return m_argv[0].get(); } | |
68 | RegisterID* argumentRegister(unsigned i) { return m_argv[i + 1].get(); } | |
6fe7ccc8 A |
69 | unsigned registerOffset() { return m_argv.last()->index() + CallFrame::offsetFor(argumentCountIncludingThis()); } |
70 | unsigned argumentCountIncludingThis() { return m_argv.size(); } | |
14957cd0 A |
71 | RegisterID* profileHookRegister() { return m_profileHookRegister.get(); } |
72 | ArgumentsNode* argumentsNode() { return m_argumentsNode; } | |
73 | ||
74 | private: | |
6fe7ccc8 A |
75 | void newArgument(BytecodeGenerator&); |
76 | ||
14957cd0 A |
77 | RefPtr<RegisterID> m_profileHookRegister; |
78 | ArgumentsNode* m_argumentsNode; | |
93a37866 | 79 | Vector<RefPtr<RegisterID>, 8, UnsafeVectorOverflow> m_argv; |
14957cd0 | 80 | }; |
9dae56ea A |
81 | |
82 | struct FinallyContext { | |
6fe7ccc8 A |
83 | StatementNode* finallyBlock; |
84 | unsigned scopeContextStackSize; | |
85 | unsigned switchContextStackSize; | |
86 | unsigned forInContextStackSize; | |
93a37866 | 87 | unsigned tryContextStackSize; |
6fe7ccc8 A |
88 | unsigned labelScopesSize; |
89 | int finallyDepth; | |
90 | int dynamicScopeDepth; | |
9dae56ea A |
91 | }; |
92 | ||
93 | struct ControlFlowContext { | |
94 | bool isFinallyBlock; | |
95 | FinallyContext finallyContext; | |
96 | }; | |
97 | ||
f9bf01c6 A |
98 | struct ForInContext { |
99 | RefPtr<RegisterID> expectedSubscriptRegister; | |
100 | RefPtr<RegisterID> iterRegister; | |
101 | RefPtr<RegisterID> indexRegister; | |
102 | RefPtr<RegisterID> propertyRegister; | |
103 | }; | |
104 | ||
93a37866 A |
105 | struct TryData { |
106 | RefPtr<Label> target; | |
107 | unsigned targetScopeDepth; | |
108 | }; | |
109 | ||
110 | struct TryContext { | |
111 | RefPtr<Label> start; | |
112 | TryData* tryData; | |
113 | }; | |
114 | ||
115 | struct TryRange { | |
116 | RefPtr<Label> start; | |
117 | RefPtr<Label> end; | |
118 | TryData* tryData; | |
119 | }; | |
120 | ||
121 | class ResolveResult { | |
122 | public: | |
123 | enum Flags { | |
124 | // The property is locally bound, in a register. | |
125 | RegisterFlag = 0x1, | |
126 | // We need to traverse the scope chain at runtime, checking for | |
127 | // non-strict eval and/or `with' nodes. | |
128 | DynamicFlag = 0x2, | |
129 | // The resolved binding is immutable. | |
130 | ReadOnlyFlag = 0x4, | |
131 | // The property has a static location | |
132 | StaticFlag = 0x8, | |
133 | // Entry at scope distance "m_depth" and located at "m_index" | |
134 | ScopedFlag = 0x10 | |
135 | }; | |
136 | ||
137 | enum Type { | |
138 | // The property is local, and stored in a register. | |
139 | Register = RegisterFlag | StaticFlag, | |
140 | // A read-only local, created by "const". | |
141 | ReadOnlyRegister = RegisterFlag | ReadOnlyFlag | StaticFlag, | |
142 | // Lexically fixed location in the scope chain | |
143 | Lexical = ScopedFlag | StaticFlag, | |
144 | // A read-only Lexical, created by "const". | |
145 | ReadOnlyLexical = ScopedFlag | ReadOnlyFlag | StaticFlag, | |
146 | // Any other form of lookup | |
147 | Dynamic = DynamicFlag, | |
148 | }; | |
149 | ||
150 | static ResolveResult registerResolve(RegisterID *local, unsigned flags) | |
151 | { | |
152 | return ResolveResult(Register | flags, local); | |
153 | } | |
154 | static ResolveResult dynamicResolve() | |
155 | { | |
156 | return ResolveResult(Dynamic, 0); | |
157 | } | |
158 | static ResolveResult lexicalResolve(int index, size_t depth, unsigned flags) | |
159 | { | |
160 | if (flags & DynamicFlag) | |
161 | return dynamicResolve(); | |
162 | return ResolveResult(Lexical | flags, index, depth); | |
163 | } | |
164 | unsigned type() const { return m_type; } | |
165 | ||
166 | // Returns the register corresponding to a local variable, or 0 if no | |
167 | // such register exists. Registers returned by ResolveResult::local() do | |
168 | // not require explicit reference counting. | |
169 | RegisterID* local() const { return m_local; } | |
170 | ||
171 | bool isRegister() const { return m_type & RegisterFlag; } | |
172 | bool isStatic() const { return (m_type & StaticFlag) && !isDynamic(); } | |
173 | bool isDynamic() const { return m_type & DynamicFlag; } | |
174 | bool isReadOnly() const { return (m_type & ReadOnlyFlag) && !isDynamic(); } | |
175 | ||
176 | unsigned depth() const { ASSERT(isStatic()); return m_depth; } | |
177 | int32_t index() const { ASSERT(isStatic()); return m_index; } | |
178 | ||
179 | private: | |
180 | ResolveResult(unsigned type, RegisterID* local) | |
181 | : m_type(type) | |
182 | , m_local(local) | |
183 | , m_index(0) | |
184 | , m_depth(0) | |
185 | { | |
186 | #ifndef NDEBUG | |
187 | checkValidity(); | |
188 | #endif | |
189 | } | |
190 | ||
191 | ResolveResult(unsigned type, int index, unsigned depth) | |
192 | : m_type(type) | |
193 | , m_local(0) | |
194 | , m_index(index) | |
195 | , m_depth(depth) | |
196 | { | |
197 | #ifndef NDEBUG | |
198 | checkValidity(); | |
199 | #endif | |
200 | } | |
201 | ||
202 | #ifndef NDEBUG | |
203 | void checkValidity(); | |
204 | #endif | |
205 | ||
206 | unsigned m_type; | |
207 | RegisterID* m_local; // Local register, if RegisterFlag is set | |
208 | int m_index; | |
209 | unsigned m_depth; | |
210 | }; | |
211 | ||
212 | struct NonlocalResolveInfo { | |
213 | friend class BytecodeGenerator; | |
214 | NonlocalResolveInfo() | |
215 | : m_state(Unused) | |
216 | { | |
217 | } | |
218 | ~NonlocalResolveInfo() | |
219 | { | |
220 | ASSERT(m_state == Put); | |
221 | } | |
222 | private: | |
223 | void resolved(uint32_t putToBaseIndex) | |
224 | { | |
225 | ASSERT(putToBaseIndex); | |
226 | ASSERT(m_state == Unused); | |
227 | m_state = Resolved; | |
228 | m_putToBaseIndex = putToBaseIndex; | |
229 | } | |
230 | uint32_t put() | |
231 | { | |
232 | ASSERT(m_state == Resolved); | |
233 | m_state = Put; | |
234 | return m_putToBaseIndex; | |
235 | } | |
236 | enum State { Unused, Resolved, Put }; | |
237 | State m_state; | |
238 | uint32_t m_putToBaseIndex; | |
239 | }; | |
240 | ||
14957cd0 A |
241 | class BytecodeGenerator { |
242 | WTF_MAKE_FAST_ALLOCATED; | |
9dae56ea A |
243 | public: |
244 | typedef DeclarationStacks::VarStack VarStack; | |
245 | typedef DeclarationStacks::FunctionStack FunctionStack; | |
246 | ||
93a37866 A |
247 | BytecodeGenerator(VM&, JSScope*, ProgramNode*, UnlinkedProgramCodeBlock*, DebuggerMode, ProfilerMode); |
248 | BytecodeGenerator(VM&, JSScope*, FunctionBodyNode*, UnlinkedFunctionCodeBlock*, DebuggerMode, ProfilerMode); | |
249 | BytecodeGenerator(VM&, JSScope*, EvalNode*, UnlinkedEvalCodeBlock*, DebuggerMode, ProfilerMode); | |
9dae56ea | 250 | |
6fe7ccc8 A |
251 | ~BytecodeGenerator(); |
252 | ||
93a37866 A |
253 | VM* vm() const { return m_vm; } |
254 | const CommonIdentifiers& propertyNames() const { return *m_vm->propertyNames; } | |
9dae56ea | 255 | |
93a37866 | 256 | bool isConstructor() { return m_codeBlock->isConstructor(); } |
14957cd0 | 257 | |
93a37866 | 258 | ParserError generate(); |
14957cd0 | 259 | |
6fe7ccc8 | 260 | bool isArgumentNumber(const Identifier&, int); |
14957cd0 A |
261 | |
262 | void setIsNumericCompareFunction(bool isNumericCompareFunction); | |
263 | ||
ba379fdc A |
264 | bool willResolveToArguments(const Identifier&); |
265 | RegisterID* uncheckedRegisterForArguments(); | |
9dae56ea | 266 | |
93a37866 A |
267 | // Resolve an identifier, given the current compile-time scope chain. |
268 | ResolveResult resolve(const Identifier&); | |
269 | // Behaves as resolve does, but ignores dynamic scope as | |
9dae56ea | 270 | // dynamic scope should not interfere with const initialisation |
93a37866 | 271 | ResolveResult resolveConstDecl(const Identifier&); |
9dae56ea A |
272 | |
273 | // Returns the register storing "this" | |
274 | RegisterID* thisRegister() { return &m_thisRegister; } | |
275 | ||
9dae56ea A |
276 | // Returns the next available temporary register. Registers returned by |
277 | // newTemporary require a modified form of reference counting: any | |
278 | // register with a refcount of 0 is considered "available", meaning that | |
279 | // the next instruction may overwrite it. | |
280 | RegisterID* newTemporary(); | |
281 | ||
9dae56ea A |
282 | // The same as newTemporary(), but this function returns "suggestion" if |
283 | // "suggestion" is a temporary. This function is helpful in situations | |
284 | // where you've put "suggestion" in a RefPtr, but you'd like to allow | |
285 | // the next instruction to overwrite it anyway. | |
286 | RegisterID* newTemporaryOr(RegisterID* suggestion) { return suggestion->isTemporary() ? suggestion : newTemporary(); } | |
287 | ||
288 | // Functions for handling of dst register | |
289 | ||
290 | RegisterID* ignoredResult() { return &m_ignoredResultRegister; } | |
291 | ||
292 | // Returns a place to write intermediate values of an operation | |
293 | // which reuses dst if it is safe to do so. | |
294 | RegisterID* tempDestination(RegisterID* dst) | |
295 | { | |
296 | return (dst && dst != ignoredResult() && dst->isTemporary()) ? dst : newTemporary(); | |
297 | } | |
298 | ||
299 | // Returns the place to write the final output of an operation. | |
300 | RegisterID* finalDestination(RegisterID* originalDst, RegisterID* tempDst = 0) | |
301 | { | |
302 | if (originalDst && originalDst != ignoredResult()) | |
303 | return originalDst; | |
304 | ASSERT(tempDst != ignoredResult()); | |
305 | if (tempDst && tempDst->isTemporary()) | |
306 | return tempDst; | |
307 | return newTemporary(); | |
308 | } | |
309 | ||
14957cd0 A |
310 | // Returns the place to write the final output of an operation. |
311 | RegisterID* finalDestinationOrIgnored(RegisterID* originalDst, RegisterID* tempDst = 0) | |
312 | { | |
313 | if (originalDst) | |
314 | return originalDst; | |
315 | ASSERT(tempDst != ignoredResult()); | |
316 | if (tempDst && tempDst->isTemporary()) | |
317 | return tempDst; | |
318 | return newTemporary(); | |
319 | } | |
320 | ||
9dae56ea A |
321 | RegisterID* destinationForAssignResult(RegisterID* dst) |
322 | { | |
323 | if (dst && dst != ignoredResult() && m_codeBlock->needsFullScopeChain()) | |
324 | return dst->isTemporary() ? dst : newTemporary(); | |
325 | return 0; | |
326 | } | |
327 | ||
328 | // Moves src to dst if dst is not null and is different from src, otherwise just returns src. | |
329 | RegisterID* moveToDestinationIfNeeded(RegisterID* dst, RegisterID* src) | |
330 | { | |
331 | return dst == ignoredResult() ? 0 : (dst && dst != src) ? emitMove(dst, src) : src; | |
332 | } | |
333 | ||
93a37866 | 334 | LabelScopePtr newLabelScope(LabelScope::Type, const Identifier* = 0); |
9dae56ea A |
335 | PassRefPtr<Label> newLabel(); |
336 | ||
93a37866 | 337 | void emitNode(RegisterID* dst, StatementNode* n) |
9dae56ea A |
338 | { |
339 | // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. | |
340 | ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); | |
93a37866 A |
341 | if (!m_stack.isSafeToRecurse()) { |
342 | emitThrowExpressionTooDeepException(); | |
343 | return; | |
344 | } | |
345 | n->emitBytecode(*this, dst); | |
9dae56ea A |
346 | } |
347 | ||
93a37866 A |
348 | void emitNode(StatementNode* n) |
349 | { | |
350 | emitNode(0, n); | |
351 | } | |
352 | ||
353 | RegisterID* emitNode(RegisterID* dst, ExpressionNode* n) | |
354 | { | |
355 | // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. | |
356 | ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); | |
357 | if (!m_stack.isSafeToRecurse()) | |
358 | return emitThrowExpressionTooDeepException(); | |
359 | return n->emitBytecode(*this, dst); | |
360 | } | |
361 | ||
362 | RegisterID* emitNode(ExpressionNode* n) | |
9dae56ea A |
363 | { |
364 | return emitNode(0, n); | |
365 | } | |
366 | ||
93a37866 | 367 | void emitNodeInConditionContext(ExpressionNode* n, Label* trueTarget, Label* falseTarget, FallThroughMode fallThroughMode) |
f9bf01c6 | 368 | { |
93a37866 | 369 | if (!m_stack.isSafeToRecurse()) { |
f9bf01c6 | 370 | emitThrowExpressionTooDeepException(); |
93a37866 A |
371 | return; |
372 | } | |
373 | ||
374 | n->emitBytecodeInConditionContext(*this, trueTarget, falseTarget, fallThroughMode); | |
f9bf01c6 A |
375 | } |
376 | ||
93a37866 | 377 | void emitExpressionInfo(int divot, int startOffset, int endOffset, unsigned line, int lineStart) |
14957cd0 | 378 | { |
93a37866 A |
379 | int sourceOffset = m_scopeNode->source().startOffset(); |
380 | unsigned firstLine = m_scopeNode->source().firstLine(); | |
14957cd0 | 381 | |
93a37866 A |
382 | ASSERT(divot >= lineStart); |
383 | ASSERT(divot >= sourceOffset); | |
384 | divot -= sourceOffset; | |
385 | ||
386 | if (lineStart > sourceOffset) | |
387 | lineStart -= sourceOffset; | |
388 | else | |
389 | lineStart = 0; | |
390 | ||
391 | ASSERT(line >= firstLine); | |
392 | line -= firstLine; | |
393 | ||
394 | unsigned instructionOffset = instructions().size(); | |
395 | ASSERT(divot >= lineStart); | |
396 | unsigned column = divot - lineStart; | |
397 | m_codeBlock->addExpressionInfo(instructionOffset, divot, startOffset, endOffset, line, column); | |
9dae56ea A |
398 | } |
399 | ||
9dae56ea A |
400 | ALWAYS_INLINE bool leftHandSideNeedsCopy(bool rightHasAssignments, bool rightIsPure) |
401 | { | |
402 | return (m_codeType != FunctionCode || m_codeBlock->needsFullScopeChain() || rightHasAssignments) && !rightIsPure; | |
403 | } | |
404 | ||
405 | ALWAYS_INLINE PassRefPtr<RegisterID> emitNodeForLeftHandSide(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure) | |
406 | { | |
407 | if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) { | |
408 | PassRefPtr<RegisterID> dst = newTemporary(); | |
409 | emitNode(dst.get(), n); | |
410 | return dst; | |
411 | } | |
412 | ||
6fe7ccc8 | 413 | return emitNode(n); |
9dae56ea A |
414 | } |
415 | ||
416 | RegisterID* emitLoad(RegisterID* dst, bool); | |
417 | RegisterID* emitLoad(RegisterID* dst, double); | |
418 | RegisterID* emitLoad(RegisterID* dst, const Identifier&); | |
ba379fdc | 419 | RegisterID* emitLoad(RegisterID* dst, JSValue); |
93a37866 | 420 | RegisterID* emitLoadGlobalObject(RegisterID* dst); |
9dae56ea A |
421 | |
422 | RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src); | |
423 | RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes); | |
424 | RegisterID* emitEqualityOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2); | |
425 | RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src); | |
426 | ||
93a37866 | 427 | RegisterID* emitCreateThis(RegisterID* dst); |
9dae56ea | 428 | RegisterID* emitNewObject(RegisterID* dst); |
14957cd0 | 429 | RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision |
9dae56ea | 430 | |
f9bf01c6 | 431 | RegisterID* emitNewFunction(RegisterID* dst, FunctionBodyNode* body); |
14957cd0 A |
432 | RegisterID* emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* body); |
433 | RegisterID* emitNewFunctionInternal(RegisterID* dst, unsigned index, bool shouldNullCheck); | |
9dae56ea | 434 | RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func); |
14957cd0 | 435 | RegisterID* emitNewRegExp(RegisterID* dst, RegExp*); |
9dae56ea A |
436 | |
437 | RegisterID* emitMove(RegisterID* dst, RegisterID* src); | |
438 | ||
93a37866 A |
439 | RegisterID* emitToNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_number, dst, src); } |
440 | RegisterID* emitInc(RegisterID* srcDst); | |
441 | RegisterID* emitDec(RegisterID* srcDst); | |
9dae56ea | 442 | |
93a37866 A |
443 | void emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target); |
444 | RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* basePrototype); | |
9dae56ea A |
445 | RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); } |
446 | RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); } | |
447 | ||
93a37866 A |
448 | RegisterID* emitGetStaticVar(RegisterID* dst, const ResolveResult&, const Identifier&); |
449 | RegisterID* emitPutStaticVar(const ResolveResult&, const Identifier&, RegisterID* value); | |
450 | RegisterID* emitInitGlobalConst(const Identifier&, RegisterID* value); | |
9dae56ea | 451 | |
93a37866 A |
452 | RegisterID* emitResolve(RegisterID* dst, const ResolveResult&, const Identifier& property); |
453 | RegisterID* emitResolveBase(RegisterID* dst, const ResolveResult&, const Identifier& property); | |
454 | RegisterID* emitResolveBaseForPut(RegisterID* dst, const ResolveResult&, const Identifier& property, NonlocalResolveInfo&); | |
455 | RegisterID* emitResolveWithBaseForPut(RegisterID* baseDst, RegisterID* propDst, const ResolveResult&, const Identifier& property, NonlocalResolveInfo&); | |
456 | RegisterID* emitResolveWithThis(RegisterID* baseDst, RegisterID* propDst, const ResolveResult&, const Identifier& property); | |
ba379fdc | 457 | |
93a37866 | 458 | RegisterID* emitPutToBase(RegisterID* base, const Identifier&, RegisterID* value, NonlocalResolveInfo&); |
9dae56ea A |
459 | |
460 | RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property); | |
14957cd0 | 461 | RegisterID* emitGetArgumentsLength(RegisterID* dst, RegisterID* base); |
9dae56ea | 462 | RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value); |
4e4e5a6f | 463 | RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value); |
9dae56ea A |
464 | RegisterID* emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier&); |
465 | RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property); | |
14957cd0 | 466 | RegisterID* emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property); |
9dae56ea A |
467 | RegisterID* emitPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); |
468 | RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property); | |
469 | RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value); | |
6fe7ccc8 | 470 | void emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter); |
93a37866 A |
471 | |
472 | ExpectedFunction expectedFunctionForIdentifier(const Identifier&); | |
473 | RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset, unsigned line, unsigned lineStart); | |
474 | RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset, unsigned line, unsigned lineStart); | |
475 | RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, RegisterID* profileHookRegister, unsigned divot, unsigned startOffset, unsigned endOffset, unsigned line, unsigned lineStart); | |
14957cd0 | 476 | RegisterID* emitLoadVarargs(RegisterID* argCountDst, RegisterID* thisRegister, RegisterID* args); |
9dae56ea A |
477 | |
478 | RegisterID* emitReturn(RegisterID* src); | |
479 | RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); } | |
480 | ||
93a37866 | 481 | RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset, unsigned line, unsigned lineStart); |
ba379fdc A |
482 | RegisterID* emitStrcat(RegisterID* dst, RegisterID* src, int count); |
483 | void emitToPrimitive(RegisterID* dst, RegisterID* src); | |
9dae56ea A |
484 | |
485 | PassRefPtr<Label> emitLabel(Label*); | |
6fe7ccc8 | 486 | void emitLoopHint(); |
9dae56ea A |
487 | PassRefPtr<Label> emitJump(Label* target); |
488 | PassRefPtr<Label> emitJumpIfTrue(RegisterID* cond, Label* target); | |
489 | PassRefPtr<Label> emitJumpIfFalse(RegisterID* cond, Label* target); | |
ba379fdc A |
490 | PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target); |
491 | PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target); | |
93a37866 | 492 | void emitPopScopes(int targetScopeDepth); |
9dae56ea | 493 | |
f9bf01c6 A |
494 | RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget); |
495 | RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target); | |
9dae56ea | 496 | |
93a37866 A |
497 | void emitReadOnlyExceptionIfNeeded(); |
498 | ||
499 | // Start a try block. 'start' must have been emitted. | |
500 | TryData* pushTry(Label* start); | |
501 | // End a try block. 'end' must have been emitted. | |
502 | RegisterID* popTryAndEmitCatch(TryData*, RegisterID* targetRegister, Label* end); | |
503 | ||
14957cd0 A |
504 | void emitThrow(RegisterID* exc) |
505 | { | |
506 | m_usesExceptions = true; | |
507 | emitUnaryNoDstOp(op_throw, exc); | |
508 | } | |
509 | ||
93a37866 | 510 | void emitThrowReferenceError(const String& message); |
14957cd0 | 511 | |
93a37866 | 512 | void emitPushNameScope(const Identifier& property, RegisterID* value, unsigned attributes); |
9dae56ea | 513 | |
93a37866 | 514 | RegisterID* emitPushWithScope(RegisterID* scope); |
9dae56ea A |
515 | void emitPopScope(); |
516 | ||
93a37866 | 517 | void emitDebugHook(DebugHookID, unsigned firstLine, unsigned lastLine, unsigned charOffset, unsigned lineStart); |
9dae56ea A |
518 | |
519 | int scopeDepth() { return m_dynamicScopeDepth + m_finallyDepth; } | |
520 | bool hasFinaliser() { return m_finallyDepth != 0; } | |
521 | ||
6fe7ccc8 | 522 | void pushFinallyContext(StatementNode* finallyBlock); |
9dae56ea A |
523 | void popFinallyContext(); |
524 | ||
f9bf01c6 A |
525 | void pushOptimisedForIn(RegisterID* expectedBase, RegisterID* iter, RegisterID* index, RegisterID* propertyRegister) |
526 | { | |
527 | ForInContext context = { expectedBase, iter, index, propertyRegister }; | |
528 | m_forInContextStack.append(context); | |
529 | } | |
530 | ||
531 | void popOptimisedForIn() | |
532 | { | |
533 | m_forInContextStack.removeLast(); | |
534 | } | |
535 | ||
9dae56ea A |
536 | LabelScope* breakTarget(const Identifier&); |
537 | LabelScope* continueTarget(const Identifier&); | |
538 | ||
539 | void beginSwitch(RegisterID*, SwitchInfo::SwitchType); | |
540 | void endSwitch(uint32_t clauseCount, RefPtr<Label>*, ExpressionNode**, Label* defaultLabel, int32_t min, int32_t range); | |
541 | ||
542 | CodeType codeType() const { return m_codeType; } | |
543 | ||
14957cd0 | 544 | bool shouldEmitProfileHooks() { return m_shouldEmitProfileHooks; } |
93a37866 | 545 | bool shouldEmitDebugHooks() { return m_shouldEmitDebugHooks; } |
14957cd0 A |
546 | |
547 | bool isStrictMode() const { return m_codeBlock->isStrictMode(); } | |
9dae56ea A |
548 | |
549 | private: | |
6fe7ccc8 A |
550 | friend class Label; |
551 | ||
9dae56ea | 552 | void emitOpcode(OpcodeID); |
93a37866 A |
553 | UnlinkedArrayAllocationProfile newArrayAllocationProfile(); |
554 | UnlinkedObjectAllocationProfile newObjectAllocationProfile(); | |
555 | UnlinkedArrayProfile newArrayProfile(); | |
556 | UnlinkedValueProfile emitProfiledOpcode(OpcodeID); | |
557 | int kill(RegisterID* dst) | |
558 | { | |
559 | int index = dst->index(); | |
560 | m_staticPropertyAnalyzer.kill(index); | |
561 | return index; | |
562 | } | |
563 | ||
9dae56ea A |
564 | void retrieveLastBinaryOp(int& dstIndex, int& src1Index, int& src2Index); |
565 | void retrieveLastUnaryOp(int& dstIndex, int& srcIndex); | |
4e4e5a6f A |
566 | ALWAYS_INLINE void rewindBinaryOp(); |
567 | ALWAYS_INLINE void rewindUnaryOp(); | |
9dae56ea | 568 | |
93a37866 | 569 | void emitComplexPopScopes(ControlFlowContext* topScope, ControlFlowContext* bottomScope); |
9dae56ea | 570 | |
ba379fdc | 571 | typedef HashMap<double, JSValue> NumberMap; |
14957cd0 | 572 | typedef HashMap<StringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap; |
93a37866 A |
573 | typedef struct { |
574 | int resolveOperations; | |
575 | int putOperations; | |
576 | } ResolveCacheEntry; | |
577 | typedef HashMap<StringImpl*, ResolveCacheEntry, IdentifierRepHash> IdentifierResolvePutMap; | |
578 | typedef HashMap<StringImpl*, uint32_t, IdentifierRepHash> IdentifierResolveMap; | |
579 | ||
580 | // Helper for emitCall() and emitConstruct(). This works because the set of | |
581 | // expected functions have identical behavior for both call and construct | |
582 | // (i.e. "Object()" is identical to "new Object()"). | |
583 | ExpectedFunction emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, Label* done); | |
9dae56ea | 584 | |
93a37866 | 585 | RegisterID* emitCall(OpcodeID, RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset, unsigned line, unsigned lineStart); |
14957cd0 | 586 | |
9dae56ea A |
587 | RegisterID* newRegister(); |
588 | ||
14957cd0 | 589 | // Adds a var slot and maps it to the name ident in symbolTable(). |
9dae56ea A |
590 | RegisterID* addVar(const Identifier& ident, bool isConstant) |
591 | { | |
592 | RegisterID* local; | |
593 | addVar(ident, isConstant, local); | |
594 | return local; | |
595 | } | |
14957cd0 A |
596 | |
597 | // Ditto. Returns true if a new RegisterID was added, false if a pre-existing RegisterID was re-used. | |
9dae56ea | 598 | bool addVar(const Identifier&, bool isConstant, RegisterID*&); |
14957cd0 A |
599 | |
600 | // Adds an anonymous var slot. To give this slot a name, add it to symbolTable(). | |
601 | RegisterID* addVar() | |
602 | { | |
603 | ++m_codeBlock->m_numVars; | |
604 | return newRegister(); | |
605 | } | |
9dae56ea | 606 | |
6fe7ccc8 | 607 | // Returns the index of the added var. |
14957cd0 | 608 | void addParameter(const Identifier&, int parameterIndex); |
93a37866 A |
609 | RegisterID* resolveCallee(FunctionBodyNode*); |
610 | void addCallee(FunctionBodyNode*, RegisterID*); | |
611 | ||
ba379fdc | 612 | void preserveLastVar(); |
14957cd0 | 613 | bool shouldAvoidResolveGlobal(); |
9dae56ea A |
614 | |
615 | RegisterID& registerFor(int index) | |
616 | { | |
617 | if (index >= 0) | |
618 | return m_calleeRegisters[index]; | |
619 | ||
93a37866 A |
620 | if (index == JSStack::Callee) |
621 | return m_calleeRegister; | |
622 | ||
6fe7ccc8 | 623 | ASSERT(m_parameters.size()); |
93a37866 | 624 | return m_parameters[index + m_parameters.size() + JSStack::CallFrameHeaderSize]; |
9dae56ea A |
625 | } |
626 | ||
9dae56ea | 627 | unsigned addConstant(const Identifier&); |
ba379fdc | 628 | RegisterID* addConstantValue(JSValue); |
93a37866 | 629 | RegisterID* addConstantEmptyValue(); |
9dae56ea A |
630 | unsigned addRegExp(RegExp*); |
631 | ||
14957cd0 A |
632 | unsigned addConstantBuffer(unsigned length); |
633 | ||
93a37866 | 634 | UnlinkedFunctionExecutable* makeFunction(FunctionBodyNode* body) |
f9bf01c6 | 635 | { |
93a37866 | 636 | return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), body); |
f9bf01c6 A |
637 | } |
638 | ||
93a37866 | 639 | RegisterID* emitInitLazyRegister(RegisterID*); |
f9bf01c6 | 640 | |
93a37866 | 641 | public: |
14957cd0 A |
642 | JSString* addStringConstant(const Identifier&); |
643 | ||
93a37866 | 644 | Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; } |
14957cd0 | 645 | |
93a37866 | 646 | SharedSymbolTable& symbolTable() { return *m_symbolTable; } |
9dae56ea | 647 | |
6fe7ccc8 A |
648 | bool shouldOptimizeLocals() |
649 | { | |
650 | if (m_dynamicScopeDepth) | |
651 | return false; | |
652 | ||
653 | if (m_codeType != FunctionCode) | |
654 | return false; | |
655 | ||
656 | return true; | |
657 | } | |
658 | ||
659 | bool canOptimizeNonLocals() | |
660 | { | |
661 | if (m_dynamicScopeDepth) | |
662 | return false; | |
663 | ||
664 | if (m_codeType == EvalCode) | |
665 | return false; | |
666 | ||
667 | if (m_codeType == FunctionCode && m_codeBlock->usesEval()) | |
668 | return false; | |
669 | ||
670 | return true; | |
671 | } | |
9dae56ea A |
672 | |
673 | RegisterID* emitThrowExpressionTooDeepException(); | |
674 | ||
ba379fdc | 675 | void createArgumentsIfNecessary(); |
14957cd0 A |
676 | void createActivationIfNecessary(); |
677 | RegisterID* createLazyRegisterIfNecessary(RegisterID*); | |
6fe7ccc8 | 678 | |
93a37866 | 679 | Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow> m_instructions; |
ba379fdc | 680 | |
9dae56ea A |
681 | bool m_shouldEmitDebugHooks; |
682 | bool m_shouldEmitProfileHooks; | |
683 | ||
93a37866 | 684 | SharedSymbolTable* m_symbolTable; |
9dae56ea A |
685 | |
686 | ScopeNode* m_scopeNode; | |
93a37866 A |
687 | Strong<JSScope> m_scope; |
688 | Strong<UnlinkedCodeBlock> m_codeBlock; | |
9dae56ea | 689 | |
ba379fdc A |
690 | // Some of these objects keep pointers to one another. They are arranged |
691 | // to ensure a sane destruction order that avoids references to freed memory. | |
14957cd0 | 692 | HashSet<RefPtr<StringImpl>, IdentifierRepHash> m_functions; |
9dae56ea A |
693 | RegisterID m_ignoredResultRegister; |
694 | RegisterID m_thisRegister; | |
93a37866 | 695 | RegisterID m_calleeRegister; |
14957cd0 | 696 | RegisterID* m_activationRegister; |
93a37866 A |
697 | RegisterID* m_emptyValueRegister; |
698 | RegisterID* m_globalObjectRegister; | |
f9bf01c6 A |
699 | SegmentedVector<RegisterID, 32> m_constantPoolRegisters; |
700 | SegmentedVector<RegisterID, 32> m_calleeRegisters; | |
701 | SegmentedVector<RegisterID, 32> m_parameters; | |
f9bf01c6 | 702 | SegmentedVector<Label, 32> m_labels; |
93a37866 | 703 | LabelScopeStore m_labelScopes; |
ba379fdc | 704 | RefPtr<RegisterID> m_lastVar; |
9dae56ea A |
705 | int m_finallyDepth; |
706 | int m_dynamicScopeDepth; | |
9dae56ea A |
707 | CodeType m_codeType; |
708 | ||
93a37866 | 709 | Vector<ControlFlowContext, 0, UnsafeVectorOverflow> m_scopeContextStack; |
9dae56ea | 710 | Vector<SwitchInfo> m_switchContextStack; |
f9bf01c6 | 711 | Vector<ForInContext> m_forInContextStack; |
93a37866 A |
712 | Vector<TryContext> m_tryContextStack; |
713 | ||
714 | Vector<TryRange> m_tryRanges; | |
715 | SegmentedVector<TryData, 8> m_tryData; | |
9dae56ea | 716 | |
ba379fdc A |
717 | int m_firstConstantIndex; |
718 | int m_nextConstantOffset; | |
719 | unsigned m_globalConstantIndex; | |
9dae56ea A |
720 | |
721 | int m_globalVarStorageOffset; | |
722 | ||
14957cd0 A |
723 | bool m_hasCreatedActivation; |
724 | int m_firstLazyFunction; | |
725 | int m_lastLazyFunction; | |
726 | HashMap<unsigned int, FunctionBodyNode*, WTF::IntHash<unsigned int>, WTF::UnsignedWithZeroKeyHashTraits<unsigned int> > m_lazyFunctions; | |
727 | typedef HashMap<FunctionBodyNode*, unsigned> FunctionOffsetMap; | |
728 | FunctionOffsetMap m_functionOffsets; | |
729 | ||
9dae56ea A |
730 | // Constant pool |
731 | IdentifierMap m_identifierMap; | |
732 | JSValueMap m_jsValueMap; | |
733 | NumberMap m_numberMap; | |
734 | IdentifierStringMap m_stringMap; | |
735 | ||
93a37866 A |
736 | uint32_t getResolveOperations(const Identifier& property) |
737 | { | |
738 | if (m_dynamicScopeDepth) | |
739 | return m_codeBlock->addResolve(); | |
740 | IdentifierResolveMap::AddResult result = m_resolveCacheMap.add(property.impl(), 0); | |
741 | if (result.isNewEntry) | |
742 | result.iterator->value = m_codeBlock->addResolve(); | |
743 | return result.iterator->value; | |
744 | } | |
745 | ||
746 | uint32_t getResolveWithThisOperations(const Identifier& property) | |
747 | { | |
748 | if (m_dynamicScopeDepth) | |
749 | return m_codeBlock->addResolve(); | |
750 | IdentifierResolveMap::AddResult result = m_resolveWithThisCacheMap.add(property.impl(), 0); | |
751 | if (result.isNewEntry) | |
752 | result.iterator->value = m_codeBlock->addResolve(); | |
753 | return result.iterator->value; | |
754 | } | |
755 | ||
756 | uint32_t getResolveBaseOperations(IdentifierResolvePutMap& map, const Identifier& property, uint32_t& putToBaseOperation) | |
757 | { | |
758 | if (m_dynamicScopeDepth) { | |
759 | putToBaseOperation = m_codeBlock->addPutToBase(); | |
760 | return m_codeBlock->addResolve(); | |
761 | } | |
762 | ResolveCacheEntry entry = {-1, -1}; | |
763 | IdentifierResolvePutMap::AddResult result = map.add(property.impl(), entry); | |
764 | if (result.isNewEntry) | |
765 | result.iterator->value.resolveOperations = m_codeBlock->addResolve(); | |
766 | if (result.iterator->value.putOperations == -1) | |
767 | result.iterator->value.putOperations = getPutToBaseOperation(property); | |
768 | putToBaseOperation = result.iterator->value.putOperations; | |
769 | return result.iterator->value.resolveOperations; | |
770 | } | |
771 | ||
772 | uint32_t getResolveBaseOperations(const Identifier& property) | |
773 | { | |
774 | uint32_t scratch; | |
775 | return getResolveBaseOperations(m_resolveBaseMap, property, scratch); | |
776 | } | |
777 | ||
778 | uint32_t getResolveBaseForPutOperations(const Identifier& property, uint32_t& putToBaseOperation) | |
779 | { | |
780 | return getResolveBaseOperations(m_resolveBaseForPutMap, property, putToBaseOperation); | |
781 | } | |
782 | ||
783 | uint32_t getResolveWithBaseForPutOperations(const Identifier& property, uint32_t& putToBaseOperation) | |
784 | { | |
785 | return getResolveBaseOperations(m_resolveWithBaseForPutMap, property, putToBaseOperation); | |
786 | } | |
787 | ||
788 | uint32_t getPutToBaseOperation(const Identifier& property) | |
789 | { | |
790 | if (m_dynamicScopeDepth) | |
791 | return m_codeBlock->addPutToBase(); | |
792 | IdentifierResolveMap::AddResult result = m_putToBaseMap.add(property.impl(), 0); | |
793 | if (result.isNewEntry) | |
794 | result.iterator->value = m_codeBlock->addPutToBase(); | |
795 | return result.iterator->value; | |
796 | } | |
797 | ||
798 | IdentifierResolveMap m_putToBaseMap; | |
799 | IdentifierResolveMap m_resolveCacheMap; | |
800 | IdentifierResolveMap m_resolveWithThisCacheMap; | |
801 | IdentifierResolvePutMap m_resolveBaseMap; | |
802 | IdentifierResolvePutMap m_resolveBaseForPutMap; | |
803 | IdentifierResolvePutMap m_resolveWithBaseForPutMap; | |
804 | ||
805 | StaticPropertyAnalyzer m_staticPropertyAnalyzer; | |
806 | ||
807 | VM* m_vm; | |
9dae56ea A |
808 | |
809 | OpcodeID m_lastOpcodeID; | |
14957cd0 A |
810 | #ifndef NDEBUG |
811 | size_t m_lastOpcodePosition; | |
812 | #endif | |
9dae56ea | 813 | |
14957cd0 | 814 | StackBounds m_stack; |
9dae56ea | 815 | |
14957cd0 A |
816 | bool m_usesExceptions; |
817 | bool m_expressionTooDeep; | |
b80e6193 | 818 | }; |
93a37866 | 819 | |
9dae56ea A |
820 | } |
821 | ||
822 | #endif // BytecodeGenerator_h |