KJS

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

KDE's Doxygen guidelines are available online.