]> git.saurik.com Git - cycript.git/blob - Output.cpp
552768b38a838b20a3d3563859fe5b28c3699467
[cycript.git] / Output.cpp
1 /* Cycript - Inlining/Optimizing JavaScript Compiler
2 * Copyright (C) 2009 Jay Freeman (saurik)
3 */
4
5 /* Modified BSD License {{{ */
6 /*
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the
12 * above copyright notice, this list of conditions
13 * and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the
15 * above copyright notice, this list of conditions
16 * and the following disclaimer in the documentation
17 * and/or other materials provided with the
18 * distribution.
19 * 3. The name of the author may not be used to endorse
20 * or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
25 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /* }}} */
39
40 #include "cycript.hpp"
41 #include "Parser.hpp"
42
43 #include <sstream>
44
45 _finline CYFlags operator ~(CYFlags rhs) {
46 return static_cast<CYFlags>(~static_cast<unsigned>(rhs));
47 }
48
49 _finline CYFlags operator &(CYFlags lhs, CYFlags rhs) {
50 return static_cast<CYFlags>(static_cast<unsigned>(lhs) & static_cast<unsigned>(rhs));
51 }
52
53 _finline CYFlags operator |(CYFlags lhs, CYFlags rhs) {
54 return static_cast<CYFlags>(static_cast<unsigned>(lhs) | static_cast<unsigned>(rhs));
55 }
56
57 _finline CYFlags &operator |=(CYFlags &lhs, CYFlags rhs) {
58 return lhs = lhs | rhs;
59 }
60
61 _finline CYFlags CYLeft(CYFlags flags) {
62 return flags & ~(CYNoDangle | CYNoInteger);
63 }
64
65 _finline CYFlags CYRight(CYFlags flags) {
66 return flags & ~CYNoBF;
67 }
68
69 _finline CYFlags CYCenter(CYFlags flags) {
70 return CYLeft(CYRight(flags));
71 }
72
73 void CYOutput::Terminate() {
74 out_ << ';';
75 mode_ = NoMode;
76 }
77
78 CYOutput &CYOutput::operator <<(char rhs) {
79 if (rhs == ' ' || rhs == '\n')
80 if (pretty_)
81 out_ << rhs;
82 else goto done;
83 else if (rhs == '\t')
84 if (pretty_)
85 for (unsigned i(0); i != indent_; ++i)
86 out_ << " ";
87 else goto done;
88 else if (rhs == '\r') {
89 if (right_) {
90 out_ << '\n';
91 right_ = false;
92 } goto done;
93 } else goto work;
94
95 right_ = true;
96 mode_ = NoMode;
97 goto done;
98
99 work:
100 if (mode_ == Terminated && rhs != '}') {
101 right_ = true;
102 out_ << ';';
103 }
104
105 if (rhs == ';') {
106 if (pretty_)
107 goto none;
108 else {
109 mode_ = Terminated;
110 goto done;
111 }
112 } else if (rhs == '+') {
113 if (mode_ == NoPlus)
114 out_ << ' ';
115 mode_ = NoPlus;
116 } else if (rhs == '-') {
117 if (mode_ == NoHyphen)
118 out_ << ' ';
119 mode_ = NoHyphen;
120 } else if (WordEndRange_[rhs]) {
121 if (mode_ == NoLetter)
122 out_ << ' ';
123 mode_ = NoLetter;
124 } else none:
125 mode_ = NoMode;
126
127 right_ = true;
128 out_ << rhs;
129 done:
130 return *this;
131 }
132
133 CYOutput &CYOutput::operator <<(const char *rhs) {
134 size_t size(strlen(rhs));
135
136 if (size == 1)
137 return *this << *rhs;
138
139 if (mode_ == Terminated)
140 out_ << ';';
141 else if (
142 mode_ == NoPlus && *rhs == '+' ||
143 mode_ == NoHyphen && *rhs == '-' ||
144 mode_ == NoLetter && WordEndRange_[*rhs]
145 )
146 out_ << ' ';
147
148 if (WordEndRange_[rhs[size - 1]])
149 mode_ = NoLetter;
150 else
151 mode_ = NoMode;
152
153 right_ = true;
154 out_ << rhs;
155 return *this;
156 }
157
158 void CYArgument::Output(CYOutput &out) const {
159 if (name_ != NULL) {
160 out << *name_;
161 if (value_ != NULL)
162 out << ':' << ' ';
163 }
164 if (value_ != NULL)
165 value_->Output(out, CYPA, CYNoFlags);
166 if (next_ != NULL) {
167 if (next_->name_ == NULL)
168 out << ',';
169 out << ' ' << *next_;
170 }
171 }
172
173 void CYArray::Output(CYOutput &out, CYFlags flags) const {
174 out << '[' << elements_ << ']';
175 }
176
177 void CYArrayComprehension::Output(CYOutput &out, CYFlags flags) const {
178 out << '[' << *expression_ << ' ' << *comprehensions_ << ']';
179 }
180
181 void CYAssignment::Output(CYOutput &out, CYFlags flags) const {
182 lhs_->Output(out, Precedence() - 1, CYLeft(flags) | CYNoRightHand);
183 out << ' ' << Operator() << ' ';
184 rhs_->Output(out, Precedence(), CYRight(flags));
185 }
186
187 void CYBlock::Output(CYOutput &out) const {
188 out << '{' << '\n';
189 ++out.indent_;
190 if (statements_ != NULL)
191 statements_->Multiple(out);
192 --out.indent_;
193 out << '\t' << '}';
194 }
195
196 void CYBlock::Output(CYOutput &out, CYFlags flags) const {
197 if (statements_ == NULL)
198 out.Terminate();
199 else if (statements_->next_ == NULL)
200 statements_->Single(out, flags);
201 else
202 Output(out);
203 }
204
205 void CYBoolean::Output(CYOutput &out, CYFlags flags) const {
206 out << (Value() ? "true" : "false");
207 }
208
209 void CYBreak::Output(CYOutput &out, CYFlags flags) const {
210 out << "break";
211 if (label_ != NULL)
212 out << ' ' << *label_;
213 out << ';';
214 }
215
216 void CYCall::Output(CYOutput &out, CYFlags flags) const {
217 bool protect((flags & CYNoCall) != 0);
218 if (protect)
219 out << '(';
220 function_->Output(out, Precedence(), protect ? CYNoFlags : flags);
221 out << '(' << arguments_ << ')';
222 if (protect)
223 out << ')';
224 }
225
226 namespace cy {
227 namespace Syntax {
228
229 void Catch::Output(CYOutput &out) const {
230 out << ' ' << "catch" << ' ' << '(' << *name_ << ')' << ' ' << code_;
231 }
232
233 } }
234
235 void CYComment::Output(CYOutput &out, CYFlags flags) const {
236 out << '\r';
237 out.out_ << value_;
238 out.right_ = true;
239 out << '\r';
240 }
241
242 void CYCompound::Output(CYOutput &out, CYFlags flags) const {
243 if (CYExpression *expression = expressions_)
244 if (CYExpression *next = expression->next_) {
245 expression->Output(out, CYLeft(flags));
246 CYFlags center(CYCenter(flags));
247 while (next != NULL) {
248 expression = next;
249 out << ',' << ' ';
250 next = expression->next_;
251 CYFlags right(next != NULL ? center : CYRight(flags));
252 expression->Output(out, right);
253 }
254 } else
255 expression->Output(out, flags);
256 }
257
258 void CYCondition::Output(CYOutput &out, CYFlags flags) const {
259 test_->Output(out, Precedence() - 1, CYLeft(flags));
260 out << ' ' << '?' << ' ';
261 if (true_ != NULL)
262 true_->Output(out, CYPA, CYNoFlags);
263 out << ' ' << ':' << ' ';
264 false_->Output(out, CYPA, CYRight(flags));
265 }
266
267 void CYContinue::Output(CYOutput &out, CYFlags flags) const {
268 out << "continue";
269 if (label_ != NULL)
270 out << ' ' << *label_;
271 out << ';';
272 }
273
274 void CYClause::Output(CYOutput &out) const {
275 if (case_ != NULL)
276 out << "case" << ' ' << *case_;
277 else
278 out << "default";
279 out << ':' << '\n';
280 if (statements_ != NULL)
281 statements_->Multiple(out);
282 out << next_;
283 }
284
285 const char *CYDeclaration::ForEachIn() const {
286 return identifier_->Word();
287 }
288
289 void CYDeclaration::ForIn(CYOutput &out, CYFlags flags) const {
290 out << "var";
291 Output(out, CYRight(flags));
292 }
293
294 void CYDeclaration::Output(CYOutput &out, CYFlags flags) const {
295 out << *identifier_;
296 //out.out_ << ':' << identifier_->usage_ << '#' << identifier_->offset_;
297 if (initialiser_ != NULL) {
298 out << ' ' << '=' << ' ';
299 initialiser_->Output(out, CYPA, CYRight(flags));
300 }
301 }
302
303 void CYDeclarations::For(CYOutput &out) const {
304 out << "var";
305 Output(out, CYNoIn);
306 }
307
308 void CYDeclarations::Output(CYOutput &out) const {
309 Output(out, CYNoFlags);
310 }
311
312 void CYDeclarations::Output(CYOutput &out, CYFlags flags) const {
313 const CYDeclarations *declaration(this);
314 bool first(true);
315 output:
316 CYDeclarations *next(declaration->next_);
317 CYFlags jacks(first ? CYLeft(flags) : next == NULL ? CYRight(flags) : CYCenter(flags));
318 first = false;
319 declaration->declaration_->Output(out, jacks);
320
321 if (next != NULL) {
322 out << ',' << ' ';
323 declaration = next;
324 goto output;
325 }
326 }
327
328 void CYDirectMember::Output(CYOutput &out, CYFlags flags) const {
329 object_->Output(out, Precedence(), CYLeft(flags));
330 if (const char *word = property_->Word())
331 out << '.' << word;
332 else
333 out << '[' << *property_ << ']';
334 }
335
336 void CYDoWhile::Output(CYOutput &out, CYFlags flags) const {
337 out << "do";
338 code_->Single(out, CYCenter(flags));
339 out << "while" << ' ' << '(' << *test_ << ')';
340 }
341
342 void CYElement::Output(CYOutput &out) const {
343 if (value_ != NULL)
344 value_->Output(out, CYPA, CYNoFlags);
345 if (next_ != NULL || value_ == NULL) {
346 out << ',';
347 if (next_ != NULL && next_->value_ != NULL)
348 out << ' ';
349 }
350 if (next_ != NULL)
351 next_->Output(out);
352 }
353
354 void CYEmpty::Output(CYOutput &out, CYFlags flags) const {
355 out.Terminate();
356 }
357
358 void CYExpress::Output(CYOutput &out, CYFlags flags) const {
359 expression_->Output(out, flags | CYNoBF);
360 out << ';';
361 }
362
363 void CYExpression::ClassName(CYOutput &out, bool object) const {
364 Output(out, CYPA, CYNoFlags);
365 }
366
367 const char *CYExpression::ForEachIn() const {
368 return NULL;
369 }
370
371 void CYExpression::For(CYOutput &out) const {
372 Output(out, CYNoIn);
373 }
374
375 void CYExpression::ForIn(CYOutput &out, CYFlags flags) const {
376 Output(out, flags | CYNoRightHand);
377 }
378
379 void CYExpression::Output(CYOutput &out) const {
380 Output(out, CYNoFlags);
381 }
382
383 void CYExpression::Output(CYOutput &out, unsigned precedence, CYFlags flags) const {
384 if (precedence < Precedence() || (flags & CYNoRightHand) != 0 && RightHand())
385 out << '(' << *this << ')';
386 else
387 Output(out, flags);
388 }
389
390 void CYFinally::Output(CYOutput &out) const {
391 out << ' ' << "finally" << ' ' << code_;
392 }
393
394 void CYFor::Output(CYOutput &out, CYFlags flags) const {
395 out << "for" << ' ' << '(';
396 if (initialiser_ != NULL)
397 initialiser_->For(out);
398 out.Terminate();
399 out << test_;
400 out.Terminate();
401 out << increment_;
402 out << ')';
403 code_->Single(out, CYRight(flags));
404 }
405
406 void CYForEachIn::Output(CYOutput &out, CYFlags flags) const {
407 out << "for" << ' ' << "each" << ' ' << '(';
408 initialiser_->ForIn(out, CYNoIn);
409 out << "in" << *set_ << ')';
410 code_->Single(out, CYRight(flags));
411 }
412
413 void CYForEachInComprehension::Output(CYOutput &out) const {
414 out << "for" << ' ' << "each" << ' ' << '(' << *name_ << ' ' << "in" << ' ' << *set_ << ')' << next_;
415 }
416
417 void CYForIn::Output(CYOutput &out, CYFlags flags) const {
418 out << "for" << ' ' << '(';
419 if (initialiser_ != NULL)
420 initialiser_->ForIn(out, CYNoIn);
421 out << "in" << *set_ << ')';
422 code_->Single(out, CYRight(flags));
423 }
424
425 void CYForInComprehension::Output(CYOutput &out) const {
426 out << "for" << ' ' << '(' << *name_ << ' ' << "in" << ' ' << *set_ << ')';
427 }
428
429 void CYFunction::Output(CYOutput &out, CYFlags flags) const {
430 // XXX: one could imagine using + here to save a byte
431 bool protect((flags & CYNoFunction) != 0);
432 if (protect)
433 out << '(';
434 out << "function";
435 if (out.options_.verbose_)
436 out.out_ << ':' << static_cast<const CYScope *>(this);
437 if (name_ != NULL)
438 out << ' ' << *name_;
439 out << '(' << parameters_ << ')';
440 out << ' ' << code_;
441 if (protect)
442 out << ')';
443 }
444
445 void CYFunctionExpression::Output(CYOutput &out, CYFlags flags) const {
446 CYFunction::Output(out, flags);
447 }
448
449 void CYFunctionStatement::Output(CYOutput &out, CYFlags flags) const {
450 CYFunction::Output(out, flags);
451 }
452
453 void CYFunctionParameter::Output(CYOutput &out) const {
454 out << *name_;
455 if (next_ != NULL)
456 out << ',' << ' ' << *next_;
457 }
458
459 const char *CYIdentifier::Word() const {
460 return replace_ == NULL || replace_ == this ? CYWord::Word() : replace_->Word();
461 }
462
463 void CYIf::Output(CYOutput &out, CYFlags flags) const {
464 bool protect(false);
465 if (false_ == NULL && (flags & CYNoDangle) != 0) {
466 protect = true;
467 out << '{';
468 }
469
470 out << "if" << ' ' << '(' << *test_ << ')';
471
472 CYFlags right(protect ? CYNoFlags : CYRight(flags));
473
474 CYFlags jacks(CYNoDangle);
475 if (false_ == NULL)
476 jacks |= right;
477 else
478 jacks |= protect ? CYNoFlags : CYCenter(flags);
479
480 true_->Single(out, jacks);
481
482 if (false_ != NULL) {
483 out << "else";
484 false_->Single(out, right);
485 }
486
487 if (protect)
488 out << '}';
489 }
490
491 void CYIfComprehension::Output(CYOutput &out) const {
492 out << "if" << ' ' << '(' << *test_ << ')' << next_;
493 }
494
495 void CYIndirectMember::Output(CYOutput &out, CYFlags flags) const {
496 object_->Output(out, Precedence(), CYLeft(flags));
497 if (const char *word = property_->Word())
498 out << "->" << word;
499 else
500 out << "->" << '[' << *property_ << ']';
501 }
502
503 void CYInfix::Output(CYOutput &out, CYFlags flags) const {
504 const char *name(Operator());
505 bool protect((flags & CYNoIn) != 0 && strcmp(name, "in") == 0);
506 if (protect)
507 out << '(';
508 CYFlags left(protect ? CYNoFlags : CYLeft(flags));
509 lhs_->Output(out, Precedence(), left);
510 out << ' ' << name << ' ';
511 CYFlags right(protect ? CYNoFlags : CYRight(flags));
512 rhs_->Output(out, Precedence() - 1, right);
513 if (protect)
514 out << ')';
515 }
516
517 void CYLabel::Output(CYOutput &out, CYFlags flags) const {
518 out << *name_ << ':' << ' ';
519 statement_->Single(out, CYRight(flags));
520 }
521
522 void CYLet::Output(CYOutput &out, CYFlags flags) const {
523 out << "let" << ' ' << '(' << *declarations_ << ')' << ' ' << code_;
524 }
525
526 void CYNew::Output(CYOutput &out, CYFlags flags) const {
527 out << "new" << ' ';
528 CYFlags jacks(CYNoCall | CYCenter(flags));
529 constructor_->Output(out, Precedence(), jacks);
530 if (arguments_ != NULL)
531 out << '(' << *arguments_ << ')';
532 }
533
534 void CYNull::Output(CYOutput &out, CYFlags flags) const {
535 CYWord::Output(out);
536 }
537
538 void CYNumber::Output(CYOutput &out, CYFlags flags) const {
539 std::ostringstream str;
540 CYNumerify(str, Value());
541 std::string value(str.str());
542 out << value.c_str();
543 // XXX: this should probably also handle hex conversions and exponents
544 if ((flags & CYNoInteger) != 0 && value.find('.') == std::string::npos)
545 out << '.';
546 }
547
548 void CYNumber::PropertyName(CYOutput &out) const {
549 Output(out, CYNoFlags);
550 }
551
552 void CYObject::Output(CYOutput &out, CYFlags flags) const {
553 bool protect((flags & CYNoBrace) != 0);
554 if (protect)
555 out << '(';
556 out << '{' << '\n';
557 ++out.indent_;
558 out << properties_;
559 --out.indent_;
560 out << '\t' << '}';
561 if (protect)
562 out << ')';
563 }
564
565 void CYPostfix::Output(CYOutput &out, CYFlags flags) const {
566 lhs_->Output(out, Precedence(), CYLeft(flags));
567 out << Operator();
568 }
569
570 void CYPrefix::Output(CYOutput &out, CYFlags flags) const {
571 const char *name(Operator());
572 out << name;
573 if (Alphabetic())
574 out << ' ';
575 rhs_->Output(out, Precedence(), CYRight(flags));
576 }
577
578 void CYProgram::Output(CYOutput &out) const {
579 if (statements_ != NULL)
580 statements_->Multiple(out);
581 }
582
583 void CYProperty::Output(CYOutput &out) const {
584 out << '\t';
585 name_->PropertyName(out);
586 out << ':' << ' ';
587 value_->Output(out, CYPA, CYNoFlags);
588 if (next_ != NULL)
589 out << ',' << '\n' << *next_;
590 else
591 out << '\n';
592 }
593
594 void CYRegEx::Output(CYOutput &out, CYFlags flags) const {
595 out << Value();
596 }
597
598 void CYReturn::Output(CYOutput &out, CYFlags flags) const {
599 out << "return";
600 if (value_ != NULL)
601 out << ' ' << *value_;
602 out << ';';
603 }
604
605 void CYStatement::Multiple(CYOutput &out, CYFlags flags) const {
606 bool first(true);
607 for (const CYStatement *next(this); next != NULL; next = next->next_) {
608 bool last(next->next_ == NULL);
609 CYFlags jacks(first ? last ? flags : CYLeft(flags) : last ? CYCenter(flags) : CYRight(flags));
610 first = false;
611 out << '\t';
612 next->Output(out, jacks);
613 out << '\n';
614 }
615 }
616
617 void CYStatement::Single(CYOutput &out, CYFlags flags) const {
618 _assert(next_ == NULL);
619 out << '\n';
620 ++out.indent_;
621 out << '\t';
622 Output(out, flags);
623 out << '\n';
624 --out.indent_;
625 }
626
627 void CYString::Output(CYOutput &out, CYFlags flags) const {
628 std::ostringstream str;
629 CYStringify(str, value_, size_);
630 out << str.str().c_str();
631 }
632
633 void CYString::PropertyName(CYOutput &out) const {
634 if (const char *word = Word())
635 out << word;
636 else
637 out << *this;
638 }
639
640 static const char *Reserved_[] = {
641 "false", "null", "true",
642
643 "break", "case", "catch", "continue", "default",
644 "delete", "do", "else", "finally", "for", "function",
645 "if", "in", "instanceof", "new", "return", "switch",
646 "this", "throw", "try", "typeof", "var", "void",
647 "while", "with",
648
649 "debugger", "const",
650
651 "class", "enum", "export", "extends", "import", "super",
652
653 "abstract", "boolean", "byte", "char", "double", "final",
654 "float", "goto", "int", "long", "native", "short",
655 "synchronized", "throws", "transient", "volatile",
656
657 "let", "yield",
658
659 NULL
660 };
661
662 const char *CYString::Word() const {
663 if (size_ == 0 || !WordStartRange_[value_[0]])
664 return NULL;
665 for (size_t i(1); i != size_; ++i)
666 if (!WordEndRange_[value_[i]])
667 return NULL;
668 const char *value(Value());
669 for (const char **reserved(Reserved_); *reserved != NULL; ++reserved)
670 if (strcmp(*reserved, value) == 0)
671 return NULL;
672 return value;
673 }
674
675 void CYSwitch::Output(CYOutput &out, CYFlags flags) const {
676 out << "switch" << ' ' << '(' << *value_ << ')' << ' ' << '{';
677 out << clauses_;
678 out << '}';
679 }
680
681 void CYThis::Output(CYOutput &out, CYFlags flags) const {
682 CYWord::Output(out);
683 }
684
685 namespace cy {
686 namespace Syntax {
687
688 void Throw::Output(CYOutput &out, CYFlags flags) const {
689 out << "throw";
690 if (value_ != NULL)
691 out << ' ' << *value_;
692 out << ';';
693 }
694
695 void Try::Output(CYOutput &out, CYFlags flags) const {
696 out << "try" << ' ' << code_ << catch_ << finally_;
697 }
698
699 } }
700
701 void CYVar::Output(CYOutput &out, CYFlags flags) const {
702 out << "var";
703 declarations_->Output(out, flags);
704 out << ';';
705 }
706
707 void CYVariable::Output(CYOutput &out, CYFlags flags) const {
708 out << *name_;
709 }
710
711 void CYWhile::Output(CYOutput &out, CYFlags flags) const {
712 out << "while" << '(' << *test_ << ')';
713 code_->Single(out, CYRight(flags));
714 }
715
716 void CYWith::Output(CYOutput &out, CYFlags flags) const {
717 out << "with" << '(' << *scope_ << ')';
718 code_->Single(out, CYRight(flags));
719 }
720
721 void CYWord::ClassName(CYOutput &out, bool object) const {
722 if (object)
723 out << "objc_getClass(";
724 out << '"' << Word() << '"';
725 if (object)
726 out << ')';
727 }
728
729 void CYWord::Output(CYOutput &out) const {
730 out << Word();
731 if (out.options_.verbose_)
732 out.out_ << '@' << this;
733 }
734
735 void CYWord::PropertyName(CYOutput &out) const {
736 Output(out);
737 }
738
739 const char *CYWord::Word() const {
740 return word_;
741 }