1/*
2 * This file is part of the KDE libraries
3 * Copyright (C) 2002 Harri Porten (porten@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
5 * Copyright (C) 2003 Apple Computer, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include <ctype.h>
25#include <stdio.h>
26#include <config-kjs.h>
27
28#include "nodes.h"
29#include "function.h"
30#include "scriptfunction.h"
31
32#include <typeinfo>
33
34#define NOINLINE
35#if COMPILER(CWP)
36#pragma auto_inline off
37#elif COMPILER(MSVC)
38#pragma auto_inline(off)
39#elif COMPILER(GCC)
40// #undef NOINLINE
41// #define NOINLINE __attribute__ (noinline)
42#endif
43
44// GCC cstring uses these automatically, but not all implementations do.
45using std::strlen;
46using std::strcpy;
47using std::strncpy;
48using std::memset;
49using std::memcpy;
50
51namespace KJS {
52 const bool kDontQuote = false, kQuote = true;
53
54 /**
55 * A simple text streaming class that helps with code indentation.
56 */
57 class SourceStream {
58 public:
59 enum eEndl { Endl };
60 enum eIndent { Indent };
61 enum eUnindent { Unindent };
62
63 static const int kBufSize = 2048;
64 typedef unsigned short UTF16;
65 SourceStream () : indent(0), bufUsed(0), reindentLine(0), reindenting(false) {}
66 const UString& toString() { flush(); return str; }
67 SourceStream& operator<<(const Identifier& s) NOINLINE;
68 SourceStream& operator<<(const UString& s) NOINLINE;
69 SourceStream& operator<<(const char* s) NOINLINE;
70 SourceStream& operator<<(char s) NOINLINE;
71 SourceStream& operator<<(eEndl) NOINLINE;
72 SourceStream& operator<<(const Node* n) NOINLINE;
73 SourceStream& operator<<(const StatementNode* n) NOINLINE;
74 SourceStream& operator<<(Operator op) NOINLINE;
75 inline SourceStream& operator<<(eIndent) { indent += 2; return *this; }
76 inline SourceStream& operator<<(eUnindent) { indent -= 2; return *this; }
77 SourceStream& append(const Node* expr1, const char* sep, const Node* expr2) NOINLINE;
78 SourceStream& append(const RefPtr<Node>& expr1, const char* sep, const RefPtr<Node>& expr2) NOINLINE;
79 SourceStream& append(const UTF16* src, int srcLen) NOINLINE;
80 SourceStream& append(const UString& inStr, bool quote) NOINLINE;
81 template <typename T>
82 inline SourceStream& operator<<(const RefPtr<T>& n) { return this->operator<<(n.get()); }
83
84 void setReindenting(int baseLine) { reindenting = true; reindentLine = baseLine; }
85 private:
86 UString str;
87 int indent;
88 int bufUsed;
89 UTF16 buffer[kBufSize];
90 void flush() NOINLINE;
91 void put(UTF16 ch) { buffer[bufUsed++] = ch; }
92 void put(char ch) { buffer[bufUsed++] = static_cast<unsigned char>(ch); }
93
94 int reindentLine;
95 bool reindenting;
96 };
97}
98
99using namespace KJS;
100
101SourceStream& SourceStream::operator<<(Operator op)
102{
103 assert(op == OpPlusPlus || op == OpMinusMinus);
104 return *this << ((op == OpPlusPlus) ? "++" : "--");
105}
106
107void SourceStream::flush()
108{
109 if (bufUsed)
110 {
111 str.append(UString(reinterpret_cast<const UChar*>(buffer), bufUsed));
112 bufUsed = 0;
113 }
114}
115
116SourceStream& SourceStream::operator<<(char c)
117{
118 if (bufUsed == kBufSize)
119 flush();
120 put(c);
121 return *this;
122}
123
124SourceStream& SourceStream::operator<<(const char *s)
125{
126 assert(strlen(s) < 100);
127 if (bufUsed > kBufSize - 100)
128 flush();
129
130 unsigned char ch;
131 int i = bufUsed;
132 --s;
133 for (UTF16* dst = &buffer[i] - 1; (ch = *++s) != 0; ++i)
134 *++dst = ch;
135 bufUsed = i;
136
137 return *this;
138}
139
140SourceStream& SourceStream::operator<<(const UString &s)
141{
142 return append(&s.data()->uc, s.size());
143}
144
145SourceStream& SourceStream::operator<<(const Identifier &s)
146{
147 return append(s.ustring(), kDontQuote);
148}
149
150SourceStream& SourceStream::operator<<(const StatementNode *n)
151{
152 if (n) {
153 // Update debug info with new line numbers if needed.
154 // Note that streamTo will output endLine first thing,
155 // so we want the next line in the debug info
156 int firstLine = reindentLine + 1;
157 n->streamTo(*this);
158 if (reindenting)
159 n->setLoc(firstLine, reindentLine - 1);
160 }
161 return *this;
162}
163
164
165SourceStream& SourceStream::operator<<(const Node *n)
166{
167 if (n)
168 n->streamTo(*this);
169 return *this;
170}
171
172SourceStream& SourceStream::operator<<(eEndl)
173{
174 if (bufUsed > kBufSize - 1 - indent)
175 flush();
176 put('\n');
177 ++reindentLine;
178
179 if (indent > 0)
180 {
181 UTF16* dst = &buffer[bufUsed];
182 for (int i = indent; i > 0; --i)
183 *dst++ = ' ';
184 bufUsed += indent;
185 }
186
187 return *this;
188}
189
190SourceStream& SourceStream::append(const Node* expr1, const char* sep, const Node* expr2)
191{
192 return *this << expr1 << sep << expr2;
193}
194
195SourceStream&
196SourceStream::append(const RefPtr<Node>& expr1, const char* sep, const RefPtr<Node>& expr2)
197{
198 return *this << expr1 << sep << expr2;
199}
200
201SourceStream& SourceStream::append(const UTF16* src, int srcLen)
202{
203 if (kBufSize - bufUsed < srcLen)
204 flush();
205 if (kBufSize - bufUsed < srcLen)
206 str.append(UString(reinterpret_cast<const UChar*>(src), srcLen));
207 else
208 {
209 UTF16* dst = &buffer[bufUsed];
210 bufUsed += srcLen;
211// while (--srcLen >= 0)
212 while (srcLen-- > 0)
213 *dst++ = *src++;
214 }
215
216 return *this;
217}
218
219// Append a quoted string
220SourceStream& SourceStream::append(const UString& inStr, bool quote)
221{
222 if (quote)
223 *this << '"';
224 const UTF16* src = &inStr.data()->uc;
225 const size_t size = inStr.size();
226 for (size_t i = 0; i < size; ++i) {
227 if (bufUsed >= kBufSize - 8)
228 flush();
229 UTF16 c = *src++, esc = '\\';
230 switch (c) {
231 case '\"': break;
232 case '\n': c = 'n'; break;
233 case '\r': c = 'r'; break;
234 case '\t': c = 't'; break;
235 case '\\': break;
236 default:
237 if (c >= 128 || !isprint(c)) { // ### FIXME: use Unicode tables
238 char hexValue[8];
239 int len = sprintf(hexValue, (c < 256) ? "\\x%02X" : "\\u%04X", c);
240 UTF16* dst = &buffer[bufUsed];
241 bufUsed += len;
242 for (int j = 0; j < len; ++j)
243 dst[j] = hexValue[j];
244 continue;
245 }
246 esc = 0; // don't escape
247 break;
248 }
249 if (esc) put(esc);
250 put(c);
251 }
252
253 if (quote)
254 *this << '"';
255 return *this;
256}
257
258UString FunctionImp::toSource() const
259{
260 SourceStream str;
261 str << "function ";
262 str.append(functionName().ustring(), kDontQuote) << '(';
263 const FunctionBodyNode* body = this->body.get();
264 const int numParams = body->numParams();
265 for (int i = 0; i < numParams; ++i) {
266 if (i > 0)
267 str << ", ";
268 str << body->paramName(i).ustring();
269 }
270 str << ") ";
271 body->streamTo(str);
272
273 return str.toString();
274}
275
276UString Node::toString() const
277{
278 SourceStream str;
279 streamTo(str);
280
281 return str.toString();
282}
283
284UString Node::reindent(int baseLine) const
285{
286 SourceStream str;
287 str.setReindenting(baseLine);
288
289 streamTo(str);
290
291 return str.toString();
292}
293
294void NullNode::streamTo(SourceStream &s) const { s << "null"; }
295
296void BooleanNode::streamTo(SourceStream &s) const
297{
298 s << (value() ? "true" : "false");
299}
300
301void NumberNode::streamTo(SourceStream &s) const
302{
303 s << UString::from(value());
304}
305
306void StringNode::streamTo(SourceStream &s) const
307{
308 s.append(value(), kQuote);
309}
310
311void RegExpNode::streamTo(SourceStream &s) const
312{
313 s << '/' << pattern << '/' << flags;
314}
315
316void ThisNode::streamTo(SourceStream &s) const { s << "this"; }
317
318void VarAccessNode::streamTo(SourceStream &s) const { s << ident; }
319
320void GroupNode::streamTo(SourceStream &s) const
321{
322 s << '(' << group << ')';
323}
324
325void ElementNode::streamTo(SourceStream &s) const
326{
327 for (const ElementNode *n = this; n; n = n->next.get()) {
328 for (int i = 0; i < n->elision; i++)
329 s << ',';
330 s << n->node;
331 if (n->next)
332 s << ',';
333 }
334}
335
336void ArrayNode::streamTo(SourceStream &s) const
337{
338 s << '[' << element;
339 for (int i = 0; i < elision; i++)
340 s << ',';
341 // Parser consumes one elision comma if there's array elements
342 // present in the expression.
343 if (opt && element)
344 s << ',';
345 s << ']';
346}
347
348void ObjectLiteralNode::streamTo(SourceStream &s) const
349{
350 if (list)
351 s << "{ " << list << " }";
352 else
353 s << "{ }";
354}
355
356void PropertyListNode::streamTo(SourceStream &s) const
357{
358 s << node;
359
360 for (const PropertyListNode *n = next.get(); n; n = n->next.get())
361 s << ", " << n->node;
362}
363
364void PropertyNode::streamTo(SourceStream &s) const
365{
366 switch (type) {
367 case Constant:
368 s << name << ": " << assign;
369 break;
370 case Getter:
371 case Setter: {
372 const FuncExprNode *func = static_cast<const FuncExprNode *>(assign.get());
373 if (type == Getter)
374 s << "get ";
375 else
376 s << "set ";
377
378 s << name << '(' << func->param << ')' << func->body;
379 break;
380 }
381 }
382}
383
384void PropertyNameNode::streamTo(SourceStream &s) const
385{
386 s.append(str.ustring(), kQuote);
387}
388
389void BracketAccessorNode::streamTo(SourceStream &s) const
390{
391 s.append(expr1, "[", expr2) << ']';
392}
393
394void DotAccessorNode::streamTo(SourceStream &s) const
395{
396 s << expr << '.' << ident;
397}
398
399void ArgumentListNode::streamTo(SourceStream &s) const
400{
401 s << expr;
402 for (ArgumentListNode *n = next.get(); n; n = n->next.get())
403 s << ", " << n->expr;
404}
405
406void ArgumentsNode::streamTo(SourceStream &s) const
407{
408 s << '(' << list << ')';
409}
410
411void NewExprNode::streamTo(SourceStream &s) const
412{
413 s << "new " << expr << args;
414}
415
416void FunctionCallValueNode::streamTo(SourceStream &s) const
417{
418 s << expr << args;
419}
420
421void FunctionCallReferenceNode::streamTo(SourceStream &s) const
422{
423 s << expr << args;
424}
425
426void PostfixNode::streamTo(SourceStream &s) const
427{
428 s << m_loc << m_oper;
429}
430
431void DeleteReferenceNode::streamTo(SourceStream &s) const
432{
433 s << "delete " << loc;
434}
435
436void DeleteValueNode::streamTo(SourceStream &s) const
437{
438 s << "delete " << m_expr;
439}
440
441void VoidNode::streamTo(SourceStream &s) const
442{
443 s << "void " << expr;
444}
445
446void TypeOfValueNode::streamTo(SourceStream &s) const
447{
448 s << "typeof " << m_expr;
449}
450
451void TypeOfVarNode::streamTo(SourceStream &s) const
452{
453 s << "typeof " << loc;
454}
455
456void PrefixNode::streamTo(SourceStream &s) const
457{
458 s << m_oper << m_loc;
459}
460
461void UnaryPlusNode::streamTo(SourceStream &s) const
462{
463 s << "+ " << expr;
464}
465
466void NegateNode::streamTo(SourceStream &s) const
467{
468 s << "- " << expr;
469}
470
471void BitwiseNotNode::streamTo(SourceStream &s) const
472{
473 s << '~' << expr;
474}
475
476void LogicalNotNode::streamTo(SourceStream &s) const
477{
478 s << '!' << expr;
479}
480
481void BinaryOperatorNode::streamTo(SourceStream& s) const
482{
483 const char* opStr;
484 switch (oper) {
485 case OpMult:
486 opStr = " * ";
487 break;
488 case OpDiv:
489 opStr = " / ";
490 break;
491 case OpMod:
492 opStr = " % ";
493 break;
494 case OpPlus:
495 opStr = " + ";
496 break;
497 case OpMinus:
498 opStr = " - ";
499 break;
500 case OpLShift:
501 opStr = " << ";
502 break;
503 case OpRShift:
504 opStr = " >> ";
505 break;
506 case OpURShift:
507 opStr = " >>> ";
508 break;
509 case OpLess:
510 opStr = " < ";
511 break;
512 case OpGreaterEq:
513 opStr = " >= ";
514 break;
515 case OpGreater:
516 opStr = " > ";
517 break;
518 case OpLessEq:
519 opStr = " <= ";
520 break;
521 case OpIn:
522 opStr = " in ";
523 break;
524 case OpInstanceOf:
525 opStr = " instanceof ";
526 break;
527 case OpEqEq:
528 opStr = " == ";
529 break;
530 case OpNotEq:
531 opStr = " != ";
532 break;
533 case OpStrEq:
534 opStr = " === ";
535 break;
536 case OpStrNEq:
537 opStr = " !== ";
538 break;
539 case OpBitAnd:
540 opStr = " & ";
541 break;
542 case OpBitXOr:
543 opStr = " ^ ";
544 break;
545 case OpBitOr:
546 opStr = " | ";
547 break;
548 default:
549 assert(!"Unhandled case in BinaryOperatorNode::streamTo()");
550 opStr = " ??? ";
551 break;
552 }
553 s.append(expr1, opStr, expr2);
554}
555
556void BinaryLogicalNode::streamTo(SourceStream &s) const
557{
558 s.append(expr1, (oper == OpAnd ? " && " : " || "), expr2);
559}
560
561void ConditionalNode::streamTo(SourceStream &s) const
562{
563 s << logical << " ? ";
564 s.append(expr1, " : ", expr2);
565}
566
567static void streamAssignmentOperatorTo(SourceStream &s, Operator oper)
568{
569 const char *opStr;
570 switch (oper) {
571 case OpEqual:
572 opStr = " = ";
573 break;
574 case OpMultEq:
575 opStr = " *= ";
576 break;
577 case OpDivEq:
578 opStr = " /= ";
579 break;
580 case OpPlusEq:
581 opStr = " += ";
582 break;
583 case OpMinusEq:
584 opStr = " -= ";
585 break;
586 case OpLShift:
587 opStr = " <<= ";
588 break;
589 case OpRShift:
590 opStr = " >>= ";
591 break;
592 case OpURShift:
593 opStr = " >>>= ";
594 break;
595 case OpAndEq:
596 opStr = " &= ";
597 break;
598 case OpXOrEq:
599 opStr = " ^= ";
600 break;
601 case OpOrEq:
602 opStr = " |= ";
603 break;
604 case OpModEq:
605 opStr = " %= ";
606 break;
607 default:
608 opStr = " ?= ";
609 }
610 s << opStr;
611}
612
613void AssignNode::streamTo(SourceStream &s) const
614{
615 s << m_loc;
616 streamAssignmentOperatorTo(s, m_oper);
617 s << m_right;
618// s.append(m_ident, opStr, m_right);
619}
620
621void CommaNode::streamTo(SourceStream &s) const
622{
623 s.append(expr1, ", ", expr2);
624}
625
626void AssignExprNode::streamTo(SourceStream &s) const
627{
628 s << " = " << expr;
629}
630
631void VarDeclNode::streamTo(SourceStream &s) const
632{
633 s << ident << init;
634}
635
636void VarDeclListNode::streamTo(SourceStream &s) const
637{
638 s << "var " << var;
639 for (VarDeclListNode *n = next.get(); n; n = n->next.get())
640 s << ", " << n->var;
641}
642
643void VarStatementNode::streamTo(SourceStream &s) const
644{
645 s << SourceStream::Endl << next << ';';
646}
647
648void BlockNode::streamTo(SourceStream &s) const
649{
650 s << SourceStream::Endl << '{' << SourceStream::Indent
651 << source << SourceStream::Unindent << SourceStream::Endl << '}';
652}
653
654void ProgramNode::streamTo(SourceStream &s) const
655{
656 // we don't want braces here, unlike in the above
657 s << source << SourceStream::Endl;
658}
659
660void EmptyStatementNode::streamTo(SourceStream &s) const
661{
662 s << SourceStream::Endl << ';';
663}
664
665void ExprStatementNode::streamTo(SourceStream &s) const
666{
667 s << SourceStream::Endl << expr << ';';
668}
669
670void IfNode::streamTo(SourceStream &s) const
671{
672 s << SourceStream::Endl << "if (" << expr << ')' << SourceStream::Indent
673 << statement1 << SourceStream::Unindent;
674 if (statement2)
675 s << SourceStream::Endl << "else" << SourceStream::Indent
676 << statement2 << SourceStream::Unindent;
677}
678
679void DoWhileNode::streamTo(SourceStream &s) const
680{
681 s << SourceStream::Endl << "do " << SourceStream::Indent
682 << statement << SourceStream::Unindent << SourceStream::Endl
683 << "while (" << expr << ");";
684}
685
686void WhileNode::streamTo(SourceStream &s) const
687{
688 s << SourceStream::Endl << "while (" << expr << ')' << SourceStream::Indent
689 << statement << SourceStream::Unindent;
690}
691
692void ForNode::streamTo(SourceStream &s) const
693{
694 s << SourceStream::Endl << "for ("
695 << expr1
696 << "; " << expr2
697 << "; " << expr3
698 << ')' << SourceStream::Indent << statement << SourceStream::Unindent;
699}
700
701void ForInNode::streamTo(SourceStream &s) const
702{
703 s << SourceStream::Endl << "for (";
704 if (varDecl)
705 s << "var " << varDecl;
706 else
707 s << lexpr;
708
709 s << " in " << expr << ')' << SourceStream::Indent
710 << statement << SourceStream::Unindent;
711}
712
713void ContinueNode::streamTo(SourceStream &s) const
714{
715 s << SourceStream::Endl << "continue";
716 if (!ident.isNull())
717 s << ' ' << ident;
718 s << ';';
719}
720
721void BreakNode::streamTo(SourceStream &s) const
722{
723 s << SourceStream::Endl << "break";
724 if (!ident.isNull())
725 s << ' ' << ident;
726 s << ';';
727}
728
729void ReturnNode::streamTo(SourceStream &s) const
730{
731 s << SourceStream::Endl << "return";
732 if (value)
733 s << ' ' << value;
734 s << ';';
735}
736
737void WithNode::streamTo(SourceStream &s) const
738{
739 s << SourceStream::Endl << "with (" << expr << ") "
740 << statement;
741}
742
743void CaseClauseNode::streamTo(SourceStream &s) const
744{
745 s << SourceStream::Endl;
746 if (expr)
747 s << "case " << expr;
748 else
749 s << "default";
750 s << ':' << SourceStream::Indent;
751 if (source)
752 s << source;
753 s << SourceStream::Unindent;
754}
755
756void ClauseListNode::streamTo(SourceStream &s) const
757{
758 for (const ClauseListNode *n = this; n; n = n->getNext())
759 s << n->getClause();
760}
761
762void CaseBlockNode::streamTo(SourceStream &s) const
763{
764 for (const ClauseListNode *n = list1.get(); n; n = n->getNext())
765 s << n->getClause();
766 if (def)
767 s << def;
768 for (const ClauseListNode *n = list2.get(); n; n = n->getNext())
769 s << n->getClause();
770}
771
772void SwitchNode::streamTo(SourceStream &s) const
773{
774 s << SourceStream::Endl << "switch (" << expr << ") {"
775 << SourceStream::Indent << block << SourceStream::Unindent
776 << SourceStream::Endl << '}';
777}
778
779void LabelNode::streamTo(SourceStream &s) const
780{
781 s << SourceStream::Endl << label << ':' << SourceStream::Indent
782 << statement << SourceStream::Unindent;
783}
784
785void ThrowNode::streamTo(SourceStream &s) const
786{
787 s << SourceStream::Endl << "throw " << expr << ';';
788}
789
790void TryNode::streamTo(SourceStream &s) const
791{
792 s << SourceStream::Endl << "try " << tryBlock;
793 if (catchBlock)
794 s << SourceStream::Endl << "catch (" << exceptionIdent << ')' << catchBlock;
795 if (finallyBlock)
796 s << SourceStream::Endl << "finally " << finallyBlock;
797}
798
799void ParameterNode::streamTo(SourceStream &s) const
800{
801 s << id;
802 for (ParameterNode *n = next.get(); n; n = n->next.get())
803 s << ", " << n->id;
804}
805
806void FuncDeclNode::streamTo(SourceStream &s) const
807{
808 s << SourceStream::Endl << "function " << ident << '(' << param << ')' << body;
809}
810
811void FuncExprNode::streamTo(SourceStream &s) const
812{
813 s << "function " << ident << '(' << param << ')' << body;
814}
815
816void SourceElementsNode::streamTo(SourceStream &s) const
817{
818 for (const SourceElementsNode *n = this; n; n = n->next.get())
819 s << n->node;
820}
821
822void PackageNameNode::streamTo(SourceStream &s) const
823{
824 if (names)
825 s << names << '.';
826 s << id;
827}
828
829void ImportStatement::streamTo(SourceStream &s) const
830{
831 s << SourceStream::Endl << "import ";
832 if (!al.isEmpty())
833 s << al << " = ";
834 s << name << (wld ? ".*;" : ";");
835}
836
837