1 | /* |
2 | * This file is part of the KDE libraries |
3 | * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) |
4 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) |
5 | * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
6 | * Copyright (C) 2007, 2008 Maksim Orlovich (maksim@kde.org) |
7 | * |
8 | * This library is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Library General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Library General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Library General Public License |
19 | * along with this library; see the file COPYING.LIB. If not, write to |
20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | * Boston, MA 02110-1301, USA. |
22 | * |
23 | */ |
24 | #include "nodes2bytecode.h" |
25 | #include "CompileState.h" |
26 | #include <wtf/Assertions.h> |
27 | |
28 | #include <typeinfo> |
29 | #include <iostream> |
30 | |
31 | namespace KJS { |
32 | |
33 | // A few helpers.. |
34 | static void emitError(CompileState* comp, Node* node, ErrorType type, const char* msgStr) |
35 | { |
36 | OpValue me = OpValue::immNode(node); |
37 | OpValue se = OpValue::immInt32(type); |
38 | OpValue msg = OpValue::immCStr(msgStr); |
39 | CodeGen::emitOp(comp, Op_RaiseError, 0, &me, &se, &msg); |
40 | } |
41 | |
42 | static void emitSyntaxError(CompileState* comp, Node* node, const char* msgStr) |
43 | { |
44 | emitError(comp, node, SyntaxError, msgStr); |
45 | } |
46 | |
47 | static void emitReferenceError(CompileState* comp, Node* node, const char* msgStr) |
48 | { |
49 | emitError(comp, node, ReferenceError, msgStr); |
50 | } |
51 | |
52 | |
53 | OpValue Node::generateEvalCode(CompileState*) |
54 | { |
55 | std::cerr << "WARNING: no generateEvalCode for:" << typeid(*this).name() << "\n" ; |
56 | ASSERT(0); |
57 | |
58 | return OpValue::immInt32(42); |
59 | } |
60 | |
61 | void StatementNode::generateExecCode(CompileState*) |
62 | { |
63 | std::cerr << "WARNING: no generateExecCode for:" << typeid(*this).name() << "\n" ; |
64 | ASSERT(0); |
65 | } |
66 | |
67 | void StatementNode::generateDebugInfo(CompileState* comp) |
68 | { |
69 | OpValue me = OpValue::immNode(this); |
70 | CodeGen::emitOp(comp, Op_AtStatement, 0, &me); |
71 | } |
72 | |
73 | static inline bool exitContextNeeded(CompileState* comp) { |
74 | return comp->compileType() == Debug && |
75 | comp->codeType() == FunctionCode; |
76 | } |
77 | |
78 | static void generateExitContextIfNeeded(CompileState* comp) { |
79 | if (exitContextNeeded(comp)) { |
80 | OpValue ourNode = OpValue::immNode(comp->functionBody()); |
81 | CodeGen::emitOp(comp, Op_ExitDebugContext, 0, &ourNode); |
82 | } |
83 | } |
84 | |
85 | // ------------------------------ Basic literals ----------------------------------------- |
86 | |
87 | OpValue NullNode::generateEvalCode(CompileState*) |
88 | { |
89 | return OpValue::immValue(jsNull()); |
90 | } |
91 | |
92 | OpValue BooleanNode::generateEvalCode(CompileState*) |
93 | { |
94 | return OpValue::immBool(value()); |
95 | } |
96 | |
97 | OpValue NumberNode::generateEvalCode(CompileState*) |
98 | { |
99 | #if 0 |
100 | if (typeHint == OpType_Value) { |
101 | // Try to fit into a JSValue if at all possible.. |
102 | JSValue* im = JSImmediate::from(value()); |
103 | if (im) { |
104 | OpValue res = mkImmediateVal(OpType_value); |
105 | return res; |
106 | } |
107 | } |
108 | #endif |
109 | |
110 | // Numeric-like.. |
111 | double d = value(); |
112 | int32_t i32 = JSValue::toInt32(d); |
113 | if (double(i32) == d && !(i32 == 0 && signbit(d))) // be careful with -0.0 here |
114 | return OpValue::immInt32(i32); |
115 | else |
116 | return OpValue::immNumber(d); |
117 | } |
118 | |
119 | |
120 | OpValue StringNode::generateEvalCode(CompileState* comp) |
121 | { |
122 | // For now, just generate a JSValue |
123 | // We may want to permit string pointers as well, to help overload resolution, |
124 | // but it's not clear whether that's useful, since we can't MM them. Perhaps |
125 | // a special StringInstance type may be of use eventually. |
126 | |
127 | if (interned) // we're re-compiling.. just reuse it |
128 | return OpValue::immValue(interned); |
129 | |
130 | // Intern shorter strings |
131 | if (val.size() < 16) { |
132 | interned = Interpreter::internString(val); |
133 | return OpValue::immValue(interned); |
134 | } else { |
135 | OpValue inStr = OpValue::immString(&val); |
136 | |
137 | OpValue out; |
138 | CodeGen::emitOp(comp, Op_OwnedString, &out, &inStr); |
139 | return out; |
140 | } |
141 | } |
142 | |
143 | StringNode::~StringNode() |
144 | { |
145 | if (interned) |
146 | Interpreter::releaseInternedString(val); |
147 | } |
148 | |
149 | OpValue RegExpNode::generateEvalCode(CompileState* comp) |
150 | { |
151 | // ### TODO: cache the engine object? |
152 | OpValue out; |
153 | OpValue patternV = OpValue::immString(&pattern); |
154 | OpValue flagsV = OpValue::immString(&flags); |
155 | CodeGen::emitOp(comp, Op_NewRegExp, &out, &patternV, &flagsV); |
156 | return out; |
157 | } |
158 | |
159 | OpValue ThisNode::generateEvalCode(CompileState* comp) |
160 | { |
161 | return *comp->thisValue(); |
162 | } |
163 | |
164 | // ------------------------------ VarAccessNode ---------------------------------------- |
165 | |
166 | size_t VarAccessNode::classifyVariable(CompileState* comp, Classification& classify) |
167 | { |
168 | // Are we inside a with or catch? In that case, it's all dynamic. Boo. |
169 | // Ditto for eval. |
170 | // ### actually that may be improvable if we can |
171 | // distinguish eval-from-global and eval-from-local, since |
172 | // we'd have an activation or global object available for access. |
173 | if (comp->inNestedScope() || comp->codeType() == EvalCode) { |
174 | classify = Dynamic; |
175 | return missingSymbolMarker(); |
176 | } |
177 | |
178 | // If we're inside global scope (and as per above, not inside any nested scope!) |
179 | // we can always used the global object |
180 | if (comp->codeType() == GlobalCode) { |
181 | classify = Global; |
182 | return missingSymbolMarker(); |
183 | } |
184 | |
185 | // We're inside a function... |
186 | if (ident == CommonIdentifiers::shared()->arguments) { |
187 | // arguments is too much of a pain to handle in general path.. |
188 | classify = Dynamic; |
189 | return missingSymbolMarker(); |
190 | } |
191 | |
192 | // Do we know this? |
193 | size_t index = comp->functionBody()->lookupSymbolID(ident); |
194 | if (index == missingSymbolMarker()) |
195 | classify = NonLocal; |
196 | else |
197 | classify = Local; |
198 | |
199 | return index; |
200 | } |
201 | |
202 | OpValue VarAccessNode::generateEvalCode(CompileState* comp) |
203 | { |
204 | Classification classify; |
205 | size_t index = classifyVariable(comp, classify); |
206 | |
207 | OpValue out; |
208 | OpValue varName = OpValue::immIdent(&ident); |
209 | switch (classify) { |
210 | case Local: { |
211 | // Register read. |
212 | out = comp->localReadVal(index); |
213 | break; |
214 | } |
215 | case NonLocal: |
216 | CodeGen::emitOp(comp, Op_NonLocalVarGet, &out, &varName); |
217 | break; |
218 | case Global: |
219 | CodeGen::emitOp(comp, Op_GlobalObjectGet, &out, &varName); |
220 | break; |
221 | case Dynamic: |
222 | CodeGen::emitOp(comp, Op_VarGet, &out, &varName); |
223 | break; |
224 | } |
225 | |
226 | return out; |
227 | } |
228 | |
229 | OpValue VarAccessNode::valueForTypeOf(CompileState* comp) |
230 | { |
231 | // ### some code dupe here. |
232 | Classification classify; |
233 | size_t index = classifyVariable(comp, classify); |
234 | |
235 | OpValue scopeTemp; |
236 | OpValue out, outReg; |
237 | OpValue varName = OpValue::immIdent(&ident); |
238 | switch (classify) { |
239 | case Local: |
240 | // Register read. Easy. |
241 | out = comp->localReadVal(index); |
242 | break; |
243 | case Global: |
244 | CodeGen::emitOp(comp, Op_SymGetKnownObject, &out, comp->globalScope(), &varName); |
245 | break; |
246 | case NonLocal: |
247 | comp->requestTemporary(OpType_value, &out, &outReg); |
248 | CodeGen::emitOp(comp, Op_NonLocalScopeLookupAndGet, &scopeTemp, &outReg, &varName); |
249 | break; |
250 | case Dynamic: |
251 | comp->requestTemporary(OpType_value, &out, &outReg); |
252 | CodeGen::emitOp(comp, Op_ScopeLookupAndGet, &scopeTemp, &outReg, &varName); |
253 | break; |
254 | } |
255 | |
256 | return out; |
257 | } |
258 | |
259 | CompileReference* VarAccessNode::generateRefBind(CompileState* comp) |
260 | { |
261 | Classification classify; |
262 | classifyVariable(comp, classify); |
263 | |
264 | if (classify == Local || classify == Global) |
265 | return 0; // nothing to do, we know where it is |
266 | |
267 | // Otherwise, we need to find the scope for writing |
268 | CompileReference* ref = new CompileReference; |
269 | |
270 | OpValue quiet = OpValue::immNode(0); |
271 | OpValue varName = OpValue::immIdent(&ident); |
272 | CodeGen::emitOp(comp, classify == Dynamic ? Op_ScopeLookup : Op_NonLocalScopeLookup, |
273 | &ref->baseObj, &varName, &quiet); |
274 | return ref; |
275 | } |
276 | |
277 | CompileReference* VarAccessNode::generateRefRead(CompileState* comp, OpValue* out) |
278 | { |
279 | Classification classify; |
280 | classifyVariable(comp, classify); |
281 | |
282 | // We want to bind and read, also issuing an error. |
283 | |
284 | // If we don't need any binding, just use normal read code.. |
285 | if (classify == Local || classify == Global) { |
286 | *out = generateEvalCode(comp); |
287 | return 0; |
288 | } |
289 | |
290 | // For others, use the lookup-and-fetch ops |
291 | CompileReference* ref = new CompileReference; |
292 | |
293 | OpValue readReg; |
294 | OpValue varName = OpValue::immIdent(&ident); |
295 | comp->requestTemporary(OpType_value, out, &readReg); |
296 | |
297 | OpName op; |
298 | if (classify == Dynamic) |
299 | op = Op_ScopeLookupAndGetChecked; |
300 | else |
301 | op = Op_NonLocalScopeLookupAndGetChecked; |
302 | CodeGen::emitOp(comp, op, &ref->baseObj, &readReg, &varName); |
303 | |
304 | return ref; |
305 | } |
306 | |
307 | void VarAccessNode::generateRefWrite(CompileState* comp, |
308 | CompileReference* ref, OpValue& valToStore) |
309 | { |
310 | Classification classify; |
311 | size_t index = classifyVariable(comp, classify); |
312 | |
313 | if (classify == Local) { |
314 | // Straight register put.. |
315 | OpValue destReg = comp->localWriteRef(comp->codeBlock(), index); |
316 | CodeGen::emitOp(comp, Op_RegPutValue, 0, &destReg, &valToStore); |
317 | } else { |
318 | // Symbolic write to the appropriate scope.. |
319 | OpValue varName = OpValue::immIdent(&ident); |
320 | CodeGen::emitOp(comp, Op_SymPutKnownObject, 0, |
321 | (classify == Global ? comp->globalScope() : &ref->baseObj), &varName, &valToStore); |
322 | } |
323 | } |
324 | |
325 | OpValue VarAccessNode::generateRefDelete(CompileState* comp) |
326 | { |
327 | Classification classify; |
328 | classifyVariable(comp, classify); |
329 | |
330 | if (classify == Local) { |
331 | // Normal locals are DontDelete, so this always fails. |
332 | return OpValue::immBool(false); |
333 | } |
334 | |
335 | // Otherwise, fetch the appropriate scope |
336 | OpValue base; |
337 | if (classify == Global) { |
338 | base = *comp->globalScope(); |
339 | } else { |
340 | OpValue varName = OpValue::immIdent(&ident); |
341 | OpValue silent = OpValue::immNode(0); |
342 | CodeGen::emitOp(comp, classify == Dynamic ? Op_ScopeLookup : Op_NonLocalScopeLookup, |
343 | &base, &varName, &silent); |
344 | } |
345 | |
346 | // Remove the property.. |
347 | OpValue out; |
348 | OpValue varName = OpValue::immIdent(&ident); |
349 | CodeGen::emitOp(comp, Op_SymDeleteKnownObject, &out, &base, &varName); |
350 | return out; |
351 | } |
352 | |
353 | void VarAccessNode::generateRefFunc(CompileState* comp, OpValue* funOut, OpValue* thisOut) |
354 | { |
355 | Classification classify; |
356 | classifyVariable(comp, classify); |
357 | |
358 | OpValue varName = OpValue::immIdent(&ident); |
359 | |
360 | OpValue thisReg; |
361 | switch (classify) { |
362 | case Local: |
363 | case Global: |
364 | // Both of these use global object for this, and use straightforward lookup for value |
365 | *funOut = generateEvalCode(comp); |
366 | *thisOut = *comp->globalScope(); |
367 | break; |
368 | case NonLocal: |
369 | comp->requestTemporary(OpType_value, thisOut, &thisReg); |
370 | CodeGen::emitOp(comp, Op_NonLocalFunctionLookupAndGet, funOut, &thisReg, &varName); |
371 | break; |
372 | case Dynamic: |
373 | comp->requestTemporary(OpType_value, thisOut, &thisReg); |
374 | CodeGen::emitOp(comp, Op_FunctionLookupAndGet, funOut, &thisReg, &varName); |
375 | break; |
376 | } |
377 | } |
378 | |
379 | // ------------------------------ GroupNode---------------------------------------- |
380 | |
381 | OpValue GroupNode::generateEvalCode(CompileState* comp) |
382 | { |
383 | return group->generateEvalCode(comp); |
384 | } |
385 | |
386 | // ------------------------------ Object + Array literals -------------------------- |
387 | |
388 | OpValue ArrayNode::generateEvalCode(CompileState* comp) |
389 | { |
390 | OpValue arr; |
391 | CodeGen::emitOp(comp, Op_NewEmptyArray, &arr); |
392 | |
393 | OpValue und = OpValue::immValue(jsUndefined()); |
394 | |
395 | int pos = 0; |
396 | for (ElementNode* el = element.get(); el; el = el->next.get()) { |
397 | if (!el->node) { |
398 | // Fill elision w/undefined, unless we can just skip over to a value |
399 | for (int i = 0; i < el->elision; i++) { |
400 | OpValue ind = OpValue::immInt32(pos); |
401 | CodeGen::emitOp(comp, Op_BracketPutKnownObject, 0, &arr, &ind, &und); |
402 | ++pos; |
403 | } |
404 | } else { |
405 | pos += el->elision; |
406 | } |
407 | |
408 | if (el->node) { |
409 | OpValue val = el->node->generateEvalCode(comp); |
410 | OpValue ind = OpValue::immInt32(pos); |
411 | CodeGen::emitOp(comp, Op_BracketPutKnownObject, 0, &arr, &ind, &val); |
412 | ++pos; |
413 | } |
414 | } |
415 | |
416 | for (int i = 0; i < elision; i++) { |
417 | OpValue ind = OpValue::immInt32(pos); |
418 | CodeGen::emitOp(comp, Op_BracketPutKnownObject, 0, &arr, &ind, &und); |
419 | ++pos; |
420 | } |
421 | |
422 | return arr; |
423 | } |
424 | |
425 | OpValue ObjectLiteralNode::generateEvalCode(CompileState* comp) |
426 | { |
427 | OpValue obj; |
428 | CodeGen::emitOp(comp, Op_NewObject, &obj); |
429 | |
430 | for (PropertyListNode* entry = list.get(); entry; entry = entry->next.get()) { |
431 | PropertyNode* prop = entry->node.get(); |
432 | OpValue name = OpValue::immIdent(&prop->name->str); |
433 | OpValue val = prop->assign->generateEvalCode(comp); |
434 | |
435 | switch (prop->type) { |
436 | case PropertyNode::Getter: |
437 | CodeGen::emitOp(comp, Op_DefineGetter, 0, &obj, &name, &val); |
438 | break; |
439 | case PropertyNode::Setter: |
440 | CodeGen::emitOp(comp, Op_DefineSetter, 0, &obj, &name, &val); |
441 | break; |
442 | case PropertyNode::Constant: |
443 | CodeGen::emitOp(comp, Op_SymPutKnownObject, 0, &obj, &name, &val); |
444 | break; |
445 | } |
446 | } |
447 | |
448 | return obj; |
449 | } |
450 | |
451 | // ------------------------------ BracketAccessorNode -------------------------------- |
452 | OpValue BracketAccessorNode::generateEvalCode(CompileState* comp) |
453 | { |
454 | OpValue ret; |
455 | OpValue base = expr1->generateEvalCode(comp); |
456 | OpValue index = expr2->generateEvalCode(comp); |
457 | |
458 | // ### optimize foo["bar"] ? |
459 | CodeGen::emitOp(comp, Op_BracketGet, &ret, &base, &index); |
460 | return ret; |
461 | } |
462 | |
463 | CompileReference* BracketAccessorNode::generateRefBind(CompileState* comp) |
464 | { |
465 | // Per 11.2.1, the following steps must happen when evaluating foo[bar] |
466 | // 1) eval foo |
467 | // 2) eval bar |
468 | // 3) call toObject on [[foo]] |
469 | // 4) call toString on [[bar]] |
470 | // ... all of which are part of reference evaluation. Fun. |
471 | // ### FIXME FIXME FIXME: we don't do step 4 in right spot yet! |
472 | CompileReference* ref = new CompileReference; |
473 | OpValue baseV = expr1->generateEvalCode(comp); |
474 | ref->indexVal = expr2->generateEvalCode(comp); |
475 | CodeGen::emitOp(comp, Op_ToObject, &ref->baseObj, &baseV); |
476 | return ref; |
477 | } |
478 | |
479 | CompileReference* BracketAccessorNode::generateRefRead(CompileState* comp, OpValue* out) |
480 | { |
481 | CompileReference* ref = new CompileReference; |
482 | |
483 | // ### As above, this sequence should store the toString on reference, if there will be a follow up |
484 | // write --- need a hint for that.. |
485 | OpValue baseV = expr1->generateEvalCode(comp); |
486 | ref->indexVal = expr2->generateEvalCode(comp); |
487 | |
488 | // Store the object for future use. |
489 | OpValue baseReg; |
490 | comp->requestTemporary(OpType_value, &ref->baseObj, &baseReg); |
491 | |
492 | CodeGen::emitOp(comp, Op_BracketGetAndBind, out, &baseReg, &baseV, &ref->indexVal); |
493 | return ref; |
494 | } |
495 | |
496 | void BracketAccessorNode::generateRefWrite(CompileState* comp, |
497 | CompileReference* ref, OpValue& valToStore) |
498 | { |
499 | CodeGen::emitOp(comp, Op_BracketPutKnownObject, 0, &ref->baseObj, &ref->indexVal, &valToStore); |
500 | } |
501 | |
502 | OpValue BracketAccessorNode::generateRefDelete(CompileState* comp) |
503 | { |
504 | OpValue base = expr1->generateEvalCode(comp); |
505 | OpValue index = expr2->generateEvalCode(comp); |
506 | |
507 | OpValue out; |
508 | CodeGen::emitOp(comp, Op_BracketDelete, &out, &base, &index); |
509 | return out; |
510 | } |
511 | |
512 | void BracketAccessorNode::generateRefFunc(CompileState* comp, OpValue* funOut, OpValue* thisOut) |
513 | { |
514 | OpValue baseV = expr1->generateEvalCode(comp); |
515 | OpValue indexV = expr2->generateEvalCode(comp); |
516 | |
517 | // We need to memorize the toObject for 'this' |
518 | OpValue baseReg; |
519 | comp->requestTemporary(OpType_value, thisOut, &baseReg); |
520 | |
521 | CodeGen::emitOp(comp, Op_BracketGetAndBind, funOut, &baseReg, &baseV, &indexV); |
522 | } |
523 | |
524 | // ------------------------------ DotAccessorNode -------------------------------- |
525 | |
526 | // ECMA 11.2.1b |
527 | OpValue DotAccessorNode::generateEvalCode(CompileState* comp) |
528 | { |
529 | OpValue ret; |
530 | OpValue base = expr->generateEvalCode(comp); |
531 | OpValue varName = OpValue::immIdent(&ident); |
532 | CodeGen::emitOp(comp, Op_SymGet, &ret, &base, &varName); |
533 | return ret; |
534 | } |
535 | |
536 | CompileReference* DotAccessorNode::generateRefBind(CompileState* comp) |
537 | { |
538 | CompileReference* ref = new CompileReference; |
539 | OpValue baseV = expr->generateEvalCode(comp); |
540 | CodeGen::emitOp(comp, Op_ToObject, &ref->baseObj, &baseV); |
541 | return ref; |
542 | } |
543 | |
544 | CompileReference* DotAccessorNode::generateRefRead(CompileState* comp, OpValue* out) |
545 | { |
546 | CompileReference* ref = new CompileReference; |
547 | OpValue baseV = expr->generateEvalCode(comp); |
548 | OpValue baseReg; |
549 | OpValue varName = OpValue::immIdent(&ident); |
550 | comp->requestTemporary(OpType_value, &ref->baseObj, &baseReg); |
551 | CodeGen::emitOp(comp, Op_SymGetAndBind, out, &baseReg, &baseV, &varName); |
552 | return ref; |
553 | } |
554 | |
555 | void DotAccessorNode::generateRefWrite(CompileState* comp, |
556 | CompileReference* ref, OpValue& valToStore) |
557 | { |
558 | OpValue varName = OpValue::immIdent(&ident); |
559 | CodeGen::emitOp(comp, Op_SymPutKnownObject, 0, &ref->baseObj, &varName, &valToStore); |
560 | } |
561 | |
562 | OpValue DotAccessorNode::generateRefDelete(CompileState* comp) |
563 | { |
564 | OpValue base = expr->generateEvalCode(comp); |
565 | OpValue varName = OpValue::immIdent(&ident); |
566 | OpValue out; |
567 | CodeGen::emitOp(comp, Op_SymDelete, &out, &base, &varName); |
568 | return out; |
569 | } |
570 | |
571 | void DotAccessorNode::generateRefFunc(CompileState* comp, OpValue* funOut, OpValue* thisOut) |
572 | { |
573 | OpValue baseV = expr->generateEvalCode(comp); |
574 | OpValue varName = OpValue::immIdent(&ident); |
575 | |
576 | OpValue baseReg; |
577 | comp->requestTemporary(OpType_value, thisOut, &baseReg); |
578 | CodeGen::emitOp(comp, Op_SymGetAndBind, funOut, &baseReg, &baseV, &varName); |
579 | } |
580 | |
581 | // ------------------ ........ |
582 | |
583 | void ArgumentsNode::generateEvalArguments(CompileState* comp) |
584 | { |
585 | WTF::Vector<OpValue> args; |
586 | |
587 | // We need evaluate arguments and push them in separate steps as there may be |
588 | // function/ctor calls inside. |
589 | for (ArgumentListNode* arg = list.get(); arg; arg = arg->next.get()) { |
590 | args.append(arg->expr->generateEvalCode(comp)); |
591 | } |
592 | |
593 | CodeGen::emitOp(comp, Op_ClearArgs, 0); |
594 | |
595 | size_t c = 0; |
596 | while (c < args.size()) { |
597 | if (c + 3 <= args.size()) { |
598 | CodeGen::emitOp(comp, Op_Add3Arg, 0, &args[c], &args[c+1], &args[c+2]); |
599 | c += 3; |
600 | } else if (c + 2 <= args.size()) { |
601 | CodeGen::emitOp(comp, Op_Add2Arg, 0, &args[c], &args[c+1]); |
602 | c += 2; |
603 | } else { |
604 | CodeGen::emitOp(comp, Op_AddArg, 0, &args[c]); |
605 | c += 1; |
606 | } |
607 | } |
608 | } |
609 | |
610 | OpValue NewExprNode::generateEvalCode(CompileState* comp) |
611 | { |
612 | OpValue v = expr->generateEvalCode(comp); |
613 | |
614 | if (args) |
615 | args->generateEvalArguments(comp); |
616 | else |
617 | CodeGen::emitOp(comp, Op_ClearArgs, 0); |
618 | |
619 | OpValue out; |
620 | CodeGen::emitOp(comp, Op_CtorCall, &out, &v); |
621 | return out; |
622 | } |
623 | |
624 | OpValue FunctionCallValueNode::generateEvalCode(CompileState* comp) |
625 | { |
626 | OpValue v = expr->generateEvalCode(comp); |
627 | args->generateEvalArguments(comp); |
628 | |
629 | OpValue out; |
630 | CodeGen::emitOp(comp, Op_FunctionCall, &out, &v, comp->globalScope()); |
631 | return out; |
632 | } |
633 | |
634 | OpValue FunctionCallReferenceNode::generateEvalCode(CompileState* comp) |
635 | { |
636 | Node* cand = expr->nodeInsideAllParens(); |
637 | ASSERT(cand->isLocation()); |
638 | LocationNode* loc = static_cast<LocationNode*>(cand); |
639 | |
640 | OpValue funVal, thisVal; |
641 | loc->generateRefFunc(comp, &funVal, &thisVal); |
642 | args->generateEvalArguments(comp); |
643 | |
644 | OpValue out; |
645 | CodeGen::emitOp(comp, Op_FunctionCall, &out, &funVal, &thisVal); |
646 | return out; |
647 | } |
648 | |
649 | OpValue PostfixNode::generateEvalCode(CompileState* comp) |
650 | { |
651 | Node* cand = m_loc->nodeInsideAllParens(); |
652 | if (!cand->isLocation()) { |
653 | emitReferenceError(comp, this, |
654 | m_oper == OpPlusPlus ? |
655 | "Postfix ++ operator applied to value that is not a reference." : |
656 | "Postfix -- operator applied to value that is not a reference." ); |
657 | return OpValue::immValue(jsUndefined()); |
658 | } |
659 | |
660 | LocationNode* loc = static_cast<LocationNode*>(cand); |
661 | |
662 | // ### we want to fold this in if the kid is a local -- any elegant way? |
663 | |
664 | //read current value |
665 | OpValue curV; |
666 | CompileReference* ref = loc->generateRefRead(comp, &curV); |
667 | |
668 | // We need it to be a number.. |
669 | if (curV.type != OpType_number) { |
670 | OpValue numVal; |
671 | CodeGen::emitConvertTo(comp, &curV, OpType_number, &numVal); |
672 | curV = numVal; |
673 | } |
674 | |
675 | // Compute new one |
676 | OpValue newV; |
677 | CodeGen::emitOp(comp, (m_oper == OpPlusPlus) ? Op_Add1 : Op_Sub1, |
678 | &newV, &curV); |
679 | |
680 | loc->generateRefWrite(comp, ref, newV); |
681 | delete ref; |
682 | return curV; |
683 | } |
684 | |
685 | OpValue DeleteReferenceNode::generateEvalCode(CompileState* comp) |
686 | { |
687 | return loc->generateRefDelete(comp); |
688 | } |
689 | |
690 | OpValue DeleteValueNode::generateEvalCode(CompileState*) |
691 | { |
692 | return OpValue::immBool(true); |
693 | } |
694 | |
695 | OpValue VoidNode::generateEvalCode(CompileState* comp) |
696 | { |
697 | (void)expr->generateEvalCode(comp); |
698 | return OpValue::immValue(jsUndefined()); |
699 | } |
700 | |
701 | OpValue TypeOfVarNode::generateEvalCode(CompileState* comp) |
702 | { |
703 | OpValue v = loc->valueForTypeOf(comp); |
704 | |
705 | OpValue out; |
706 | CodeGen::emitOp(comp, Op_TypeOf, &out, &v); |
707 | return out; |
708 | } |
709 | |
710 | OpValue TypeOfValueNode::generateEvalCode(CompileState* comp) |
711 | { |
712 | OpValue v = m_expr->generateEvalCode(comp); |
713 | OpValue typeOfV; |
714 | CodeGen::emitOp(comp, Op_TypeOf, &typeOfV, &v); |
715 | return typeOfV; |
716 | } |
717 | |
718 | OpValue PrefixNode::generateEvalCode(CompileState* comp) |
719 | { |
720 | Node* cand = m_loc->nodeInsideAllParens(); |
721 | if (!cand->isLocation()) { |
722 | emitReferenceError(comp, this, |
723 | m_oper == OpPlusPlus ? |
724 | "Prefix ++ operator applied to value that is not a reference." : |
725 | "Prefix -- operator applied to value that is not a reference." ); |
726 | return OpValue::immValue(jsUndefined()); |
727 | } |
728 | |
729 | LocationNode* loc = static_cast<LocationNode*>(cand); |
730 | |
731 | // ### we want to fold this in if the kid is a local -- any elegant way? |
732 | |
733 | //read current value |
734 | OpValue curV; |
735 | CompileReference* ref = loc->generateRefRead(comp, &curV); |
736 | |
737 | OpValue newV; |
738 | CodeGen::emitOp(comp, (m_oper == OpPlusPlus) ? Op_Add1 : Op_Sub1, |
739 | &newV, &curV); |
740 | |
741 | // Write out + return new value. |
742 | loc->generateRefWrite(comp, ref, newV); |
743 | delete ref; |
744 | return newV; |
745 | } |
746 | |
747 | OpValue UnaryPlusNode::generateEvalCode(CompileState* comp) |
748 | { |
749 | // This is basically just a number cast |
750 | OpValue curV = expr->generateEvalCode(comp); |
751 | |
752 | if (curV.type != OpType_number) { |
753 | OpValue numVal; |
754 | CodeGen::emitConvertTo(comp, &curV, OpType_number, &numVal); |
755 | curV = numVal; |
756 | } |
757 | |
758 | return curV; |
759 | } |
760 | |
761 | OpValue NegateNode::generateEvalCode(CompileState* comp) |
762 | { |
763 | OpValue v = expr->generateEvalCode(comp); |
764 | OpValue negV; |
765 | CodeGen::emitOp(comp, Op_Neg, &negV, &v); |
766 | return negV; |
767 | } |
768 | |
769 | OpValue BitwiseNotNode::generateEvalCode(CompileState* comp) |
770 | { |
771 | OpValue v = expr->generateEvalCode(comp); |
772 | OpValue out; |
773 | CodeGen::emitOp(comp, Op_BitNot, &out, &v); |
774 | return out; |
775 | } |
776 | |
777 | OpValue LogicalNotNode::generateEvalCode(CompileState* comp) |
778 | { |
779 | OpValue v = expr->generateEvalCode(comp); |
780 | OpValue out; |
781 | CodeGen::emitOp(comp, Op_LogicalNot, &out, &v); |
782 | return out; |
783 | } |
784 | |
785 | OpValue BinaryOperatorNode::generateEvalCode(CompileState* comp) |
786 | { |
787 | OpValue v1 = expr1->generateEvalCode(comp); |
788 | OpValue v2 = expr2->generateEvalCode(comp); |
789 | |
790 | OpName codeOp; // ### could perhaps skip conversion entirely, |
791 | // and set these in the parser? |
792 | switch (oper) { |
793 | case OpMult: |
794 | // operator * |
795 | codeOp = Op_Mult; |
796 | break; |
797 | case OpDiv: |
798 | // operator / |
799 | codeOp = Op_Div; |
800 | break; |
801 | case OpMod: |
802 | // operator % |
803 | codeOp = Op_Mod; |
804 | break; |
805 | case OpPlus: |
806 | // operator + |
807 | codeOp = Op_Add; |
808 | break; |
809 | case OpMinus: |
810 | // operator - |
811 | codeOp = Op_Sub; |
812 | break; |
813 | case OpLShift: |
814 | // operator << |
815 | codeOp = Op_LShift; |
816 | break; |
817 | case OpRShift: |
818 | // operator >> |
819 | codeOp = Op_RShift; |
820 | break; |
821 | case OpURShift: |
822 | // operator >>> |
823 | codeOp = Op_URShift; |
824 | break; |
825 | case OpLess: |
826 | // operator < |
827 | codeOp = Op_Less; |
828 | break; |
829 | case OpGreaterEq: |
830 | // operator >= |
831 | codeOp = Op_GreaterEq; |
832 | break; |
833 | case OpGreater: |
834 | // operator > |
835 | codeOp = Op_Greater; |
836 | break; |
837 | case OpLessEq: |
838 | // operator <= |
839 | codeOp = Op_LessEq; |
840 | break; |
841 | case OpEqEq: |
842 | // operator == |
843 | codeOp = Op_EqEq; |
844 | break; |
845 | case OpNotEq: |
846 | // operator != |
847 | codeOp = Op_NotEq; |
848 | break; |
849 | case OpStrEq: |
850 | // operator === |
851 | codeOp = Op_StrEq; |
852 | break; |
853 | case OpStrNEq: |
854 | // operator !== |
855 | codeOp = Op_StrNEq; |
856 | break; |
857 | case OpBitAnd: |
858 | // operator & |
859 | codeOp = Op_BitAnd; |
860 | break; |
861 | case OpBitXOr: |
862 | // operator ^ |
863 | codeOp = Op_BitXOr; |
864 | break; |
865 | case OpBitOr: |
866 | // operator | |
867 | codeOp = Op_BitOr; |
868 | break; |
869 | case OpIn: |
870 | codeOp = Op_In; |
871 | break; |
872 | case OpInstanceOf: |
873 | codeOp = Op_InstanceOf; |
874 | break; |
875 | |
876 | default: |
877 | assert(!"BinaryOperatorNode: unhandled switch case" ); |
878 | } |
879 | |
880 | OpValue out; |
881 | CodeGen::emitOp(comp, codeOp, &out, &v1, &v2); |
882 | return out; |
883 | } |
884 | |
885 | OpValue BinaryLogicalNode::generateEvalCode(CompileState* comp) |
886 | { |
887 | // This is somewhat ugly since we can't patchup labels in already generated |
888 | // code, and don't know the types in advance. It could also benefit from |
889 | // a type hint, since it's easier if we only want a bool, which is quite common |
890 | |
891 | OpValue a = expr1->generateEvalCode(comp); |
892 | |
893 | // Make a register for storing the result, and put 'a' there, as out first guess. |
894 | OpValue aVal, aReg; |
895 | comp->requestTemporary(a.type, &aVal, &aReg); |
896 | CodeGen::emitRegStore(comp, &aReg, &a); |
897 | |
898 | // Is this enough to shortcircuit? |
899 | // if op is && and a is false, we jump out, ditto |
900 | // for || and true. |
901 | Addr jumpToShortCircuit = CodeGen::emitOp(comp, oper == OpAnd ? Op_IfNotJump : Op_IfJump, |
902 | 0, &a, OpValue::dummyAddr()); |
903 | |
904 | // Now, generate the code for b... |
905 | OpValue b = expr2->generateEvalCode(comp); |
906 | |
907 | // Hopefully, either the types match, or the result slot is already a value, |
908 | // so we can just promote b (which will happen automatically to produce param for Op_RegPutVal) |
909 | if (a.type == b.type || a.type == OpType_value) { |
910 | if (a.type == OpType_value) |
911 | CodeGen::emitOp(comp, Op_RegPutValue, 0, &aReg, &b); |
912 | else |
913 | CodeGen::emitRegStore(comp, &aReg, &b); |
914 | CodeGen::patchJumpToNext(comp, jumpToShortCircuit, 1); |
915 | return aVal; |
916 | } else { |
917 | // We need to promote 'a' as well, which means we need to skip over the code jumpToShortCircuit |
918 | // went to after handling store of 'b'. |
919 | |
920 | // Get a new register for the result, put b there.. |
921 | OpValue resVal, resReg; |
922 | comp->requestTemporary(OpType_value, &resVal, &resReg); |
923 | CodeGen::emitOp(comp, Op_RegPutValue, 0, &resReg, &b); |
924 | |
925 | // skip to after a promotion.. |
926 | Addr jumpToAfter = CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
927 | |
928 | // a's promotion goes here.. |
929 | CodeGen::patchJumpToNext(comp, jumpToShortCircuit, 1); |
930 | CodeGen::emitOp(comp, Op_RegPutValue, 0, &resReg, &a); |
931 | |
932 | // now we're after it.. |
933 | CodeGen::patchJumpToNext(comp, jumpToAfter, 0); |
934 | |
935 | return resVal; |
936 | } |
937 | } |
938 | |
939 | OpValue ConditionalNode::generateEvalCode(CompileState* comp) |
940 | { |
941 | // As above, we have some difficulty here, since we do not have a way of knowing |
942 | // the types in advance, but since we can't reasonably speculate on them both being bool, |
943 | // we just always produce a value. |
944 | OpValue resVal, resReg; |
945 | |
946 | // Evaluate conditional, and jump.. |
947 | OpValue v = logical->generateEvalCode(comp); |
948 | Addr jumpToElse = CodeGen::emitOp(comp, Op_IfNotJump, 0, &v, OpValue::dummyAddr()); |
949 | |
950 | // True branch |
951 | OpValue v1out = expr1->generateEvalCode(comp); |
952 | |
953 | // Request a temporary for the result. (We can't reuse any, since it may be a variable!) |
954 | // ### perhaps do an isTemporary check here? |
955 | comp->requestTemporary(OpType_value, &resVal, &resReg); |
956 | CodeGen::emitOp(comp, Op_RegPutValue, 0, &resReg, &v1out); |
957 | |
958 | Addr jumpToAfter = CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
959 | |
960 | // Jump to else goes here. |
961 | CodeGen::patchJumpToNext(comp, jumpToElse, 1); |
962 | |
963 | // : part.. |
964 | OpValue v2out = expr2->generateEvalCode(comp); |
965 | CodeGen::emitOp(comp, Op_RegPutValue, 0, &resReg, &v2out); |
966 | |
967 | // After everything |
968 | CodeGen::patchJumpToNext(comp, jumpToAfter, 0); |
969 | |
970 | return resVal; |
971 | } |
972 | |
973 | OpValue FuncExprNode::generateEvalCode(CompileState* comp) |
974 | { |
975 | comp->setNeedsClosures(); |
976 | |
977 | OpValue out; |
978 | OpValue nameV = OpValue::immIdent(&ident); |
979 | OpValue bodyV = OpValue::immNode(body.get()); |
980 | CodeGen::emitOp(comp, Op_EvalFuncExpr, &out, &nameV, &bodyV); |
981 | return out; |
982 | } |
983 | |
984 | void FuncDeclNode::generateExecCode(CompileState* comp) |
985 | { |
986 | comp->setNeedsClosures(); |
987 | |
988 | // No executable content... |
989 | } |
990 | |
991 | void SourceElementsNode::generateExecCode(CompileState* comp) |
992 | { |
993 | node->generateExecCode(comp); |
994 | |
995 | // ### FIXME: how do we do proper completion? |
996 | for (SourceElementsNode *n = next.get(); n; n = n->next.get()) { |
997 | n->node->generateExecCode(comp); |
998 | } |
999 | } |
1000 | |
1001 | OpValue AssignNode::generateEvalCode(CompileState* comp) |
1002 | { |
1003 | Node* cand = m_loc->nodeInsideAllParens(); |
1004 | if (!cand->isLocation()) { |
1005 | emitReferenceError(comp, this, "Left side of assignment is not a reference." ); |
1006 | return OpValue::immValue(jsUndefined()); |
1007 | } |
1008 | |
1009 | LocationNode* loc = static_cast<LocationNode*>(cand); |
1010 | |
1011 | CompileReference* ref; |
1012 | |
1013 | OpValue v; |
1014 | if (m_oper == OpEqual) { |
1015 | ref = loc->generateRefBind(comp); |
1016 | v = m_right->generateEvalCode(comp); |
1017 | } else { |
1018 | OpValue v1; |
1019 | ref = loc->generateRefRead(comp, &v1); |
1020 | OpValue v2 = m_right->generateEvalCode(comp); |
1021 | |
1022 | OpName codeOp; |
1023 | switch (m_oper) { |
1024 | case OpMultEq: |
1025 | codeOp = Op_Mult; |
1026 | break; |
1027 | case OpDivEq: |
1028 | codeOp = Op_Div; |
1029 | break; |
1030 | case OpModEq: |
1031 | codeOp = Op_Mod; |
1032 | break; |
1033 | case OpPlusEq: |
1034 | codeOp = Op_Add; |
1035 | break; |
1036 | case OpMinusEq: |
1037 | codeOp = Op_Sub; |
1038 | break; |
1039 | case OpLShift: |
1040 | codeOp = Op_LShift; |
1041 | break; |
1042 | case OpRShift: |
1043 | codeOp = Op_RShift; |
1044 | break; |
1045 | case OpURShift: |
1046 | codeOp = Op_URShift; |
1047 | break; |
1048 | case OpAndEq: |
1049 | codeOp = Op_BitAnd; |
1050 | break; |
1051 | case OpXOrEq: |
1052 | codeOp = Op_BitXOr; |
1053 | break; |
1054 | case OpOrEq: |
1055 | codeOp = Op_BitOr; |
1056 | break; |
1057 | default: |
1058 | ASSERT(0); |
1059 | } |
1060 | |
1061 | CodeGen::emitOp(comp, codeOp, &v, &v1, &v2); |
1062 | } |
1063 | |
1064 | loc->generateRefWrite(comp, ref, v); |
1065 | |
1066 | delete ref; |
1067 | return v; |
1068 | } |
1069 | |
1070 | OpValue CommaNode::generateEvalCode(CompileState* comp) |
1071 | { |
1072 | expr1->generateEvalCode(comp); |
1073 | return expr2->generateEvalCode(comp); |
1074 | } |
1075 | |
1076 | OpValue AssignExprNode::generateEvalCode(CompileState* comp) |
1077 | { |
1078 | return expr->generateEvalCode(comp); |
1079 | } |
1080 | |
1081 | void VarDeclNode::generateCode(CompileState* comp) |
1082 | { |
1083 | // We only care about things which have an initializer --- |
1084 | // everything else is a no-op at execution time, |
1085 | // and only makes a difference at processVarDecl time |
1086 | if (init) { |
1087 | if (comp->inNestedScope()) { |
1088 | // We need to do the full lookup mess, which includes doing split binding and store |
1089 | OpValue quiet = OpValue::immNode(0); |
1090 | OpValue varName = OpValue::immIdent(&ident); |
1091 | OpValue base; |
1092 | CodeGen::emitOp(comp, Op_ScopeLookup, &base, &varName, &quiet); |
1093 | |
1094 | OpValue val = init->generateEvalCode(comp); |
1095 | CodeGen::emitOp(comp, Op_SymPutKnownObject, 0, &base, &varName, &val); |
1096 | return; |
1097 | } |
1098 | |
1099 | OpValue val = init->generateEvalCode(comp); |
1100 | size_t localID = comp->functionBody()->lookupSymbolID(ident); |
1101 | if (localID == missingSymbolMarker()) { |
1102 | // Generate a symbolic assignment, always to local scope |
1103 | OpValue identV = OpValue::immIdent(&ident); |
1104 | CodeGen::emitOp(comp, Op_SymPutKnownObject, 0, comp->localScope(), &identV, &val); |
1105 | } else { |
1106 | // Store to the local.. |
1107 | OpValue dest = comp->localWriteRef(comp->codeBlock(), localID); |
1108 | CodeGen::emitOp(comp, Op_RegPutValue, 0, &dest, &val); |
1109 | } |
1110 | } // if initializer.. |
1111 | } |
1112 | |
1113 | OpValue VarDeclListNode::generateEvalCode(CompileState* comp) |
1114 | { |
1115 | for (VarDeclListNode *n = this; n; n = n->next.get()) |
1116 | n->var->generateCode(comp); |
1117 | |
1118 | return OpValue::immInt32(0); // unused.. |
1119 | } |
1120 | |
1121 | void VarStatementNode::generateExecCode(CompileState* comp) |
1122 | { |
1123 | generateDebugInfoIfNeeded(comp); |
1124 | next->generateEvalCode(comp); |
1125 | } |
1126 | |
1127 | void BlockNode::generateExecCode(CompileState* comp) |
1128 | { |
1129 | if (source) { |
1130 | generateDebugInfoIfNeeded(comp); |
1131 | source->generateExecCode(comp); |
1132 | } |
1133 | } |
1134 | |
1135 | void EmptyStatementNode::generateExecCode(CompileState*) |
1136 | {} |
1137 | |
1138 | void ExprStatementNode::generateExecCode(CompileState* comp) |
1139 | { |
1140 | generateDebugInfoIfNeeded(comp); |
1141 | OpValue val = expr->generateEvalCode(comp); |
1142 | |
1143 | // Update the result for eval or global code |
1144 | if (comp->codeType() != FunctionCode) |
1145 | CodeGen::emitOp(comp, Op_RegPutValue, 0, comp->evalResultReg(), &val); |
1146 | } |
1147 | |
1148 | void IfNode::generateExecCode(CompileState* comp) |
1149 | { |
1150 | generateDebugInfoIfNeeded(comp); |
1151 | |
1152 | // eval the condition |
1153 | OpValue cond = expr->generateEvalCode(comp); |
1154 | |
1155 | // If condition is not true, jump to after or else.. |
1156 | Addr afterTrueJmp = CodeGen::emitOp(comp, Op_IfNotJump, 0, &cond, OpValue::dummyAddr()); |
1157 | |
1158 | // Emit the body of true... |
1159 | statement1->generateExecCode(comp); |
1160 | |
1161 | // If we have an else, add in a jump to skip over it. |
1162 | Addr afterAllJmp = 0; |
1163 | if (statement2) |
1164 | afterAllJmp = CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
1165 | |
1166 | // This is where we go if true fails --- else, or afterwards. |
1167 | CodeGen::patchJumpToNext(comp, afterTrueJmp, 1); |
1168 | |
1169 | if (statement2) { |
1170 | // Body of else |
1171 | statement2->generateExecCode(comp); |
1172 | |
1173 | // Fix up the jump-over code |
1174 | CodeGen::patchJumpToNext(comp, afterAllJmp, 0); |
1175 | } |
1176 | } |
1177 | |
1178 | void DoWhileNode::generateExecCode(CompileState* comp) |
1179 | { |
1180 | generateDebugInfoIfNeeded(comp); |
1181 | comp->enterLoop(this); |
1182 | |
1183 | // Body |
1184 | OpValue beforeBody = OpValue::immAddr(CodeGen::nextPC(comp)); |
1185 | statement->generateExecCode(comp); |
1186 | |
1187 | // continues go to just before the test.. |
1188 | comp->resolvePendingContinues(this, CodeGen::nextPC(comp)); |
1189 | |
1190 | // test |
1191 | OpValue cond = expr->generateEvalCode(comp); |
1192 | CodeGen::emitOp(comp, Op_IfJump, 0, &cond, &beforeBody); |
1193 | |
1194 | comp->exitLoop(this); |
1195 | } |
1196 | |
1197 | void WhileNode::generateExecCode(CompileState* comp) |
1198 | { |
1199 | generateDebugInfoIfNeeded(comp); |
1200 | comp->enterLoop(this); |
1201 | |
1202 | // Jump to test. |
1203 | Addr jumpToTest = CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
1204 | |
1205 | // Body |
1206 | OpValue beforeBody = OpValue::immAddr(CodeGen::nextPC(comp)); |
1207 | statement->generateExecCode(comp); |
1208 | |
1209 | // continues go to just before the test.. |
1210 | comp->resolvePendingContinues(this, CodeGen::nextPC(comp)); |
1211 | |
1212 | // patch up the destination of the initial jump to test |
1213 | CodeGen::patchJumpToNext(comp, jumpToTest, 0); |
1214 | |
1215 | // test |
1216 | OpValue cond = expr->generateEvalCode(comp); |
1217 | CodeGen::emitOp(comp, Op_IfJump, 0, &cond, &beforeBody); |
1218 | |
1219 | comp->exitLoop(this); |
1220 | } |
1221 | |
1222 | void ForNode::generateExecCode(CompileState* comp) |
1223 | { |
1224 | generateDebugInfoIfNeeded(comp); |
1225 | comp->enterLoop(this); |
1226 | |
1227 | // Initializer, if any.. |
1228 | if (expr1) |
1229 | expr1->generateEvalCode(comp); |
1230 | |
1231 | // Insert a jump to the loop test (address not yet known) |
1232 | Addr jumpToTest = CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
1233 | |
1234 | // Generate loop body.. |
1235 | OpValue bodyAddr = OpValue::immAddr(CodeGen::nextPC(comp)); |
1236 | statement->generateExecCode(comp); |
1237 | |
1238 | // We're about to generate the increment... The continues should go here.. |
1239 | comp->resolvePendingContinues(this, CodeGen::nextPC(comp)); |
1240 | |
1241 | // ### there is a CheckTimeout hook here in nodes.cpp... |
1242 | |
1243 | // Generate increment... |
1244 | if (expr3) |
1245 | expr3->generateEvalCode(comp); |
1246 | |
1247 | // The test goes here, so patch up the previous jump.. |
1248 | CodeGen::patchJumpToNext(comp, jumpToTest, 0); |
1249 | |
1250 | // Make the test itself --- if it exists.. |
1251 | if (expr2) { |
1252 | OpValue cond = expr2->generateEvalCode(comp); |
1253 | CodeGen::emitOp(comp, Op_IfJump, 0, &cond, &bodyAddr); |
1254 | } else { |
1255 | // Just jump back to the body. |
1256 | CodeGen::emitOp(comp, Op_Jump, 0, &bodyAddr); |
1257 | } |
1258 | |
1259 | comp->exitLoop(this); |
1260 | } |
1261 | |
1262 | void ForInNode::generateExecCode(CompileState* comp) |
1263 | { |
1264 | generateDebugInfoIfNeeded(comp); |
1265 | if (varDecl) |
1266 | varDecl->generateCode(comp); |
1267 | |
1268 | OpValue val = expr->generateEvalCode(comp); |
1269 | OpValue obj; // version of val after toObject, returned by BeginForIn. |
1270 | |
1271 | OpValue stateVal, stateReg; |
1272 | comp->requestTemporary(OpType_value, &stateVal, &stateReg); |
1273 | |
1274 | // Fetch the property name array.. |
1275 | CodeGen::emitOp(comp, Op_BeginForIn, &obj, &val, &stateReg); |
1276 | |
1277 | comp->enterLoop(this); |
1278 | |
1279 | // We put the test first here, since the test and the fetch are combined. |
1280 | OpValue sym; |
1281 | Addr fetchNext = CodeGen::emitOp(comp, Op_NextForInEntry, &sym, &obj, |
1282 | &stateVal, OpValue::dummyAddr()); |
1283 | |
1284 | // Write to the variable |
1285 | assert (lexpr->isLocation()); |
1286 | LocationNode* loc = static_cast<LocationNode*>(lexpr.get()); |
1287 | |
1288 | CompileReference* ref = loc->generateRefBind(comp); |
1289 | loc->generateRefWrite (comp, ref, sym); |
1290 | delete ref; |
1291 | |
1292 | // Run the body. |
1293 | statement->generateExecCode(comp); |
1294 | |
1295 | // Can fix the continues to go back to the test... |
1296 | comp->resolvePendingContinues(this, fetchNext); |
1297 | |
1298 | // Jump back.. |
1299 | OpValue backVal = OpValue::immAddr(fetchNext); |
1300 | CodeGen::emitOp(comp, Op_Jump, 0, &backVal); |
1301 | |
1302 | // The end address is here (3 argument + return val) |
1303 | CodeGen::patchJumpToNext(comp, fetchNext, 3); |
1304 | |
1305 | comp->exitLoop(this); |
1306 | } |
1307 | |
1308 | // Helper for continue/break -- emits stack cleanup call if needed, |
1309 | // and a jump either to the or an ??? exception. |
1310 | static void handleJumpOut(CompileState* comp, Node* dest, ComplType breakOrCont) |
1311 | { |
1312 | // We scan up the nest stack until we get to the target or |
1313 | // a try-finally. |
1314 | int toUnwind = 0; |
1315 | |
1316 | const WTF::Vector<CompileState::NestInfo>& nests = comp->nestStack(); |
1317 | |
1318 | for (int pos = nests.size() - 1; pos >= 0; --pos) { |
1319 | switch (nests[pos].type) { |
1320 | case CompileState::Scope: |
1321 | case CompileState::OtherCleanup: |
1322 | ++toUnwind; |
1323 | break; |
1324 | case CompileState::TryFinally: { |
1325 | // Uh-oh. We have to handle this via exception machinery, giving it the |
1326 | // original address |
1327 | Addr pc = CodeGen::nextPC(comp); |
1328 | CodeGen::emitOp(comp, Op_ContBreakInTryFinally, 0, OpValue::dummyAddr()); |
1329 | |
1330 | // Queue destination for resolution |
1331 | if (breakOrCont == Continue) |
1332 | comp->addPendingContinue(dest, pc); |
1333 | else |
1334 | comp->addPendingBreak(dest, pc); |
1335 | |
1336 | return; |
1337 | } |
1338 | |
1339 | case CompileState::ContBreakTarget: |
1340 | if (nests[pos].node == dest) { |
1341 | // Great. We found where we're going! Emit the unwind instr (if needed), |
1342 | // and the jump. |
1343 | if (toUnwind) { |
1344 | OpValue unwind = OpValue::immInt32(toUnwind); |
1345 | CodeGen::emitOp(comp, Op_UnwindStacks, 0, &unwind); |
1346 | } |
1347 | |
1348 | // Emit a jump... |
1349 | Addr pc = CodeGen::nextPC(comp); |
1350 | CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
1351 | |
1352 | // Queue destination for resolution |
1353 | if (breakOrCont == Continue) |
1354 | comp->addPendingContinue(dest, pc); |
1355 | else |
1356 | comp->addPendingBreak(dest, pc); |
1357 | |
1358 | return; |
1359 | } // if matching destination.. |
1360 | } |
1361 | } |
1362 | |
1363 | assert (!"Huh? Unable to find continue/break target in the nest stack" ); |
1364 | } |
1365 | |
1366 | void ContinueNode::generateExecCode(CompileState* comp) |
1367 | { |
1368 | generateDebugInfoIfNeeded(comp); |
1369 | Node* dest = comp->resolveContinueLabel(ident); |
1370 | if (!dest) { |
1371 | if (ident.isEmpty()) |
1372 | emitSyntaxError(comp, this, "Illegal continue without target outside a loop." ); |
1373 | else |
1374 | emitSyntaxError(comp, this, "Invalid label in continue." ); |
1375 | } else { |
1376 | // Continue can only be used for a loop |
1377 | if (dest->isIterationStatement()) { |
1378 | handleJumpOut(comp, dest, Continue); |
1379 | } else { |
1380 | emitSyntaxError(comp, this, "Invalid continue target; must be a loop." ); |
1381 | } |
1382 | } |
1383 | } |
1384 | |
1385 | void BreakNode::generateExecCode(CompileState* comp) |
1386 | { |
1387 | generateDebugInfoIfNeeded(comp); |
1388 | Node* dest = comp->resolveBreakLabel(ident); |
1389 | if (!dest) { |
1390 | if (ident.isEmpty()) |
1391 | emitSyntaxError(comp, this, "Illegal break without target outside a loop or switch." ); |
1392 | else |
1393 | emitSyntaxError(comp, this, "Invalid label in break." ); |
1394 | } else { |
1395 | handleJumpOut(comp, dest, Break); |
1396 | } |
1397 | } |
1398 | |
1399 | void ReturnNode::generateExecCode(CompileState* comp) |
1400 | { |
1401 | generateDebugInfoIfNeeded(comp); |
1402 | OpValue arg; |
1403 | |
1404 | // Return is invalid in non-function.. |
1405 | if (comp->codeType() != FunctionCode) { |
1406 | emitSyntaxError(comp, this, "Invalid return." ); |
1407 | return; |
1408 | } |
1409 | |
1410 | if (!value) |
1411 | arg = OpValue::immValue(jsUndefined()); |
1412 | else |
1413 | arg = value->generateEvalCode(comp); |
1414 | |
1415 | if (!comp->inTryFinally()) |
1416 | generateExitContextIfNeeded(comp); |
1417 | |
1418 | CodeGen::emitOp(comp, comp->inTryFinally() ? Op_ReturnInTryFinally : Op_Return, 0, &arg); |
1419 | } |
1420 | |
1421 | void WithNode::generateExecCode(CompileState* comp) |
1422 | { |
1423 | generateDebugInfoIfNeeded(comp); |
1424 | // ### this may be too conservative --- it only applies if there is |
1425 | // a function call within |
1426 | comp->setNeedsClosures(); |
1427 | |
1428 | OpValue scopeObj = expr->generateEvalCode(comp); |
1429 | |
1430 | comp->pushNest(CompileState::Scope, this); |
1431 | CodeGen::emitOp(comp, Op_PushScope, 0, &scopeObj); |
1432 | |
1433 | statement->generateExecCode(comp); |
1434 | |
1435 | CodeGen::emitOp(comp, Op_PopScope, 0); |
1436 | comp->popNest(); |
1437 | } |
1438 | |
1439 | void LabelNode::generateExecCode(CompileState* comp) |
1440 | { |
1441 | if (!comp->pushLabel(label)) { |
1442 | emitSyntaxError(comp, this, "Duplicated label found." ); |
1443 | return; |
1444 | } |
1445 | |
1446 | if (!statement->isLabelNode()) { // we're the last label.. |
1447 | comp->pushNest(CompileState::ContBreakTarget, statement.get()); |
1448 | comp->bindLabels(statement.get()); |
1449 | } |
1450 | |
1451 | // Generate code for stuff inside the label... |
1452 | statement->generateExecCode(comp); |
1453 | |
1454 | // Fix up any breaks.. |
1455 | if (!statement->isLabelNode()) { |
1456 | comp->popNest(); |
1457 | comp->resolvePendingBreaks(statement.get(), CodeGen::nextPC(comp)); |
1458 | } |
1459 | |
1460 | comp->popLabel(); |
1461 | } |
1462 | |
1463 | void ThrowNode::generateExecCode(CompileState* comp) |
1464 | { |
1465 | generateDebugInfoIfNeeded(comp); |
1466 | OpValue projectile = expr->generateEvalCode(comp); |
1467 | CodeGen::emitOp(comp, Op_Throw, 0, &projectile); |
1468 | } |
1469 | |
1470 | void TryNode::generateExecCode(CompileState* comp) |
1471 | { |
1472 | generateDebugInfoIfNeeded(comp); |
1473 | // ### this may be too conservative --- it only applies if there is |
1474 | // a function call within the catch |
1475 | comp->setNeedsClosures(); |
1476 | |
1477 | // Set the catch handler, run the try clause, pop the try handler.. |
1478 | Addr setCatchHandler = CodeGen::emitOp(comp, Op_PushExceptionHandler, 0, OpValue::dummyAddr()); |
1479 | comp->pushNest(finallyBlock ? CompileState::TryFinally : CompileState::OtherCleanup); |
1480 | |
1481 | tryBlock->generateExecCode(comp); |
1482 | |
1483 | CodeGen::emitOp(comp, Op_PopExceptionHandler); |
1484 | comp->popNest(); |
1485 | |
1486 | // Jump over the catch if try is OK |
1487 | Addr jumpOverCatch = 0; |
1488 | if (catchBlock) |
1489 | jumpOverCatch = CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
1490 | |
1491 | // Exceptions would go here --- either in a catch or a finally. |
1492 | CodeGen::patchJumpToNext(comp, setCatchHandler, 0); |
1493 | |
1494 | Addr catchToFinallyEH = 0; |
1495 | if (catchBlock) { |
1496 | // If there is a finally block, that acts as an exception handler for the catch; |
1497 | // we need to set it before entering the catch scope, so the cleanup entries for that |
1498 | // are on top. Also, that's needed because if the inside raised a non-exception |
1499 | // continuation, EnterCatch will re-raise it. |
1500 | if (finallyBlock) { |
1501 | catchToFinallyEH = CodeGen::emitOp(comp, Op_PushExceptionHandler, 0, OpValue::dummyAddr()); |
1502 | comp->pushNest(CompileState::TryFinally); |
1503 | } |
1504 | |
1505 | // Emit the catch.. Note: the unwinder has already popped the catch handler entry, |
1506 | // but the exception object is still set, since we need to make a scope for it. |
1507 | // EnterCatch would do that for us, given the name |
1508 | OpValue catchVar = OpValue::immIdent(&exceptionIdent); |
1509 | CodeGen::emitOp(comp, Op_EnterCatch, 0, &catchVar); |
1510 | comp->pushNest(CompileState::Scope); |
1511 | |
1512 | catchBlock->generateExecCode(comp); |
1513 | |
1514 | // If needed, cleanup the binding to finally, and always cleans the catch scope |
1515 | CodeGen::emitOp(comp, Op_ExitCatch); |
1516 | comp->popNest(); |
1517 | |
1518 | if (finallyBlock) { |
1519 | CodeGen::emitOp(comp, Op_PopExceptionHandler); |
1520 | comp->popNest(); |
1521 | } |
1522 | |
1523 | // after an OK 'try', we always go to finally, if any, which needs an op if there is a catch block |
1524 | CodeGen::patchJumpToNext(comp, jumpOverCatch, 0); |
1525 | } |
1526 | |
1527 | |
1528 | if (finallyBlock) { |
1529 | if (catchBlock) // if a catch was using us an EH, patch that instruction to here |
1530 | CodeGen::patchJumpToNext(comp, catchToFinallyEH, 0); |
1531 | |
1532 | CodeGen::emitOp(comp, Op_DeferCompletion); |
1533 | comp->pushNest(CompileState::OtherCleanup); |
1534 | |
1535 | finallyBlock->generateExecCode(comp); |
1536 | |
1537 | OpValue otherTryFinally = OpValue::immBool(comp->inTryFinally()); |
1538 | |
1539 | if (exitContextNeeded(comp)) { |
1540 | OpValue ourNode = OpValue::immNode(comp->functionBody()); |
1541 | CodeGen::emitOp(comp, Op_ReactivateCompletionDebug, 0, &otherTryFinally, &ourNode); |
1542 | } else { |
1543 | CodeGen::emitOp(comp, Op_ReactivateCompletion, 0, &otherTryFinally); |
1544 | } |
1545 | comp->popNest(); |
1546 | } |
1547 | } |
1548 | |
1549 | void FunctionBodyNode::generateExecCode(CompileState* comp) |
1550 | { |
1551 | // Load scope, global and 'this' pointers. |
1552 | OpValue scopeVal, scopeReg, |
1553 | globalVal, globalReg, |
1554 | thisVal, thisReg; |
1555 | |
1556 | comp->requestTemporary(OpType_value, &scopeVal, &scopeReg); |
1557 | comp->requestTemporary(OpType_value, &globalVal, &globalReg); |
1558 | comp->requestTemporary(OpType_value, &thisVal, &thisReg); |
1559 | |
1560 | CodeGen::emitOp(comp, Op_Preamble, 0, &scopeReg, &globalReg, &thisReg); |
1561 | |
1562 | comp->setPreloadRegs(&scopeVal, &globalVal, &thisVal); |
1563 | |
1564 | OpValue evalResReg, evalResVal; |
1565 | if (comp->codeType() != FunctionCode) { |
1566 | comp->requestTemporary(OpType_value, &evalResVal, &evalResReg); |
1567 | comp->setEvalResultRegister(&evalResReg); |
1568 | |
1569 | // There is no need to initialize this as everything will be set to undefined anyway |
1570 | } else { |
1571 | if (comp->compileType() == Debug) { |
1572 | OpValue ourNode = OpValue::immNode(this); |
1573 | CodeGen::emitOp(comp, Op_EnterDebugContext, 0, &ourNode); |
1574 | } |
1575 | } |
1576 | |
1577 | // Set unwind.. |
1578 | Addr unwind = CodeGen::emitOp(comp, Op_PushExceptionHandler, 0, OpValue::dummyAddr()); |
1579 | |
1580 | // Generate body... |
1581 | BlockNode::generateExecCode(comp); |
1582 | |
1583 | // Make sure we exit! |
1584 | if (comp->codeType() != FunctionCode) { |
1585 | CodeGen::emitOp(comp, Op_Return, 0, &evalResVal); |
1586 | } else { |
1587 | generateExitContextIfNeeded(comp); |
1588 | CodeGen::emitOp(comp, Op_Exit); |
1589 | } |
1590 | |
1591 | // Unwind stuff.. |
1592 | CodeGen::patchJumpToNext(comp, unwind, 0); |
1593 | generateExitContextIfNeeded(comp); |
1594 | CodeGen::emitOp(comp, Op_PropagateException); |
1595 | } |
1596 | |
1597 | void SwitchNode::generateExecCode(CompileState* comp) |
1598 | { |
1599 | generateDebugInfoIfNeeded(comp); |
1600 | CaseBlockNode* caseBlock = this->block.get(); |
1601 | |
1602 | // The code we produce has 2 stages: first, we emit all the conditionals, and pick |
1603 | // the label to jump to (with the jump to the default being last), |
1604 | // then we just emit all the clauses in the row. The breaks will be |
1605 | // resolved at the end --- for that, we bind ourselves for label'less break. |
1606 | comp->pushNest(CompileState::ContBreakTarget, this); |
1607 | comp->pushDefaultBreak(this); |
1608 | |
1609 | // What we compare with |
1610 | OpValue switchOn = expr->generateEvalCode(comp); |
1611 | |
1612 | WTF::Vector<Addr> list1jumps; |
1613 | WTF::Vector<Addr> list2jumps; |
1614 | Addr defJump; |
1615 | |
1616 | // Jumps for list 1.. |
1617 | for (ClauseListNode* iter = caseBlock->list1.get(); iter; iter = iter->next.get()) { |
1618 | OpValue ref = iter->clause->expr->generateEvalCode(comp); |
1619 | OpValue match; |
1620 | CodeGen::emitOp(comp, Op_StrEq, &match, &switchOn, &ref); |
1621 | |
1622 | Addr jumpToClause = CodeGen::emitOp(comp, Op_IfJump, 0, &match, OpValue::dummyAddr()); |
1623 | list1jumps.append(jumpToClause); |
1624 | } |
1625 | |
1626 | // Jumps for list 2.. |
1627 | for (ClauseListNode* iter = caseBlock->list2.get(); iter; iter = iter->next.get()) { |
1628 | OpValue ref = iter->clause->expr->generateEvalCode(comp); |
1629 | OpValue match; |
1630 | CodeGen::emitOp(comp, Op_StrEq, &match, &switchOn, &ref); |
1631 | |
1632 | Addr jumpToClause = CodeGen::emitOp(comp, Op_IfJump, 0, &match, OpValue::dummyAddr()); |
1633 | list2jumps.append(jumpToClause); |
1634 | } |
1635 | |
1636 | // Jump to default (or after, if there is no default) |
1637 | defJump = CodeGen::emitOp(comp, Op_Jump, 0, OpValue::dummyAddr()); |
1638 | |
1639 | // Now, we can actually emit the bodies, fixing the addresses as we go |
1640 | int p = 0; |
1641 | for (ClauseListNode* iter = caseBlock->list1.get(); iter; iter = iter->next.get()) { |
1642 | CodeGen::patchJumpToNext(comp, list1jumps[p], 1); |
1643 | if (iter->clause->source) |
1644 | iter->clause->source->generateExecCode(comp); |
1645 | ++p; |
1646 | } |
1647 | |
1648 | if (caseBlock->def) { |
1649 | CodeGen::patchJumpToNext(comp, defJump, 0); |
1650 | if (caseBlock->def->source) |
1651 | caseBlock->def->source->generateExecCode(comp); |
1652 | } |
1653 | |
1654 | p = 0; |
1655 | for (ClauseListNode* iter = caseBlock->list2.get(); iter; iter = iter->next.get()) { |
1656 | CodeGen::patchJumpToNext(comp, list2jumps[p], 1); |
1657 | if (iter->clause->source) |
1658 | iter->clause->source->generateExecCode(comp); |
1659 | ++p; |
1660 | } |
1661 | |
1662 | // If we didn't have a default, that jump is to here.. |
1663 | if (!caseBlock->def) |
1664 | CodeGen::patchJumpToNext(comp, defJump, 0); |
1665 | |
1666 | // Breaks should go after us.. |
1667 | comp->popDefaultBreak(); |
1668 | comp->popNest(); |
1669 | comp->resolvePendingBreaks(this, CodeGen::nextPC(comp)); |
1670 | } |
1671 | |
1672 | void ImportStatement::generateExecCode(CompileState*) |
1673 | {} // handled as a declaration.. |
1674 | |
1675 | |
1676 | } |
1677 | |
1678 | // kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on; hl c++; |
1679 | |