]> git.proxmox.com Git - rustc.git/blob - src/binaryen/src/emscripten-optimizer/simple_ast.h
New upstream version 1.23.0+dfsg1
[rustc.git] / src / binaryen / src / emscripten-optimizer / simple_ast.h
1 /*
2 * Copyright 2015 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef wasm_simple_ast_h
18 #define wasm_simple_ast_h
19
20 #include <algorithm>
21 #include <cassert>
22 #include <cmath>
23 #include <cstdio>
24 #include <cstdlib>
25 #include <cstring>
26 #include <functional>
27 #include <iomanip>
28 #include <iostream>
29 #include <limits>
30 #include <ostream>
31 #include <set>
32 #include <unordered_map>
33 #include <unordered_set>
34 #include <vector>
35
36 #include "parser.h"
37 #include "snprintf.h"
38 #include "support/safe_integer.h"
39 #include "mixed_arena.h"
40
41 #define err(str) fprintf(stderr, str "\n");
42 #define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__);
43 #define printErr err
44
45 namespace cashew {
46
47 struct Value;
48 struct Ref;
49
50 void dump(const char *str, Ref node, bool pretty=false);
51
52 // Reference to a value, plus some operators for convenience
53 struct Ref {
54 Value* inst;
55
56 Ref(Value *v=nullptr) : inst(v) {}
57
58 Value* get() { return inst; }
59
60 Value& operator*() { return *inst; }
61 Value* operator->() { return inst; }
62 Ref& operator[](unsigned x);
63 Ref& operator[](IString x);
64
65 // special conveniences
66 bool operator==(const char *str); // comparison to string, which is by value
67 bool operator!=(const char *str);
68 bool operator==(const IString &str);
69 bool operator!=(const IString &str);
70 bool operator==(double d) { abort(); return false; } // prevent Ref == number, which is potentially ambiguous; use ->getNumber() == number
71 bool operator==(Ref other);
72 bool operator!(); // check if null, in effect
73 };
74
75 // Arena allocation, free it all on process exit
76
77 // A mixed arena for global allocation only, so members do not
78 // receive an allocator, they all use the global one anyhow
79 class GlobalMixedArena : public MixedArena {
80 public:
81 template<class T>
82 T* alloc() {
83 auto* ret = static_cast<T*>(allocSpace(sizeof(T)));
84 new (ret) T();
85 return ret;
86 }
87 };
88
89 extern GlobalMixedArena arena;
90
91 class ArrayStorage : public ArenaVectorBase<ArrayStorage, Ref> {
92 public:
93 void allocate(size_t size) {
94 allocatedElements = size;
95 data = static_cast<Ref*>(arena.allocSpace(sizeof(Ref) * allocatedElements));
96 }
97 };
98
99 struct Assign;
100 struct AssignName;
101
102 // Main value type
103 struct Value {
104 enum Type {
105 String = 0,
106 Number = 1,
107 Array = 2,
108 Null = 3,
109 Bool = 4,
110 Object = 5,
111 Assign_ = 6, // ref = target
112 AssignName_ = 7
113 };
114
115 Type type;
116
117 typedef std::unordered_map<IString, Ref> ObjectStorage;
118
119 #ifdef _MSC_VER // MSVC does not allow unrestricted unions: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
120 IString str;
121 #endif
122 union { // TODO: optimize
123 #ifndef _MSC_VER
124 IString str;
125 #endif
126 double num;
127 ArrayStorage *arr;
128 bool boo;
129 ObjectStorage *obj;
130 Ref ref;
131 };
132
133 // constructors all copy their input
134 Value() : type(Null), num(0) {}
135 explicit Value(const char *s) : type(Null) {
136 setString(s);
137 }
138 explicit Value(double n) : type(Null) {
139 setNumber(n);
140 }
141 explicit Value(ArrayStorage &a) : type(Null) {
142 setArray();
143 *arr = a;
144 }
145 // no bool constructor - would endanger the double one (int might convert the wrong way)
146
147 ~Value() {
148 free();
149 }
150
151 void free() {
152 if (type == Array) { arr->clear(); }
153 else if (type == Object) delete obj;
154 type = Null;
155 num = 0;
156 }
157
158 Value& setString(const char *s) {
159 free();
160 type = String;
161 str.set(s);
162 return *this;
163 }
164 Value& setString(const IString &s) {
165 free();
166 type = String;
167 str.set(s);
168 return *this;
169 }
170 Value& setNumber(double n) {
171 free();
172 type = Number;
173 num = n;
174 return *this;
175 }
176 Value& setArray(ArrayStorage &a) {
177 free();
178 type = Array;
179 arr = arena.alloc<ArrayStorage>();
180 *arr = a;
181 return *this;
182 }
183 Value& setArray(size_t size_hint=0) {
184 free();
185 type = Array;
186 arr = arena.alloc<ArrayStorage>();
187 arr->reserve(size_hint);
188 return *this;
189 }
190 Value& setNull() {
191 free();
192 type = Null;
193 return *this;
194 }
195 Value& setBool(bool b) { // Bool in the name, as otherwise might overload over int
196 free();
197 type = Bool;
198 boo = b;
199 return *this;
200 }
201 Value& setObject() {
202 free();
203 type = Object;
204 obj = new ObjectStorage();
205 return *this;
206 }
207 Value& setAssign(Ref target, Ref value);
208 Value& setAssignName(IString target, Ref value);
209
210 bool isString() { return type == String; }
211 bool isNumber() { return type == Number; }
212 bool isArray() { return type == Array; }
213 bool isNull() { return type == Null; }
214 bool isBool() { return type == Bool; }
215 bool isObject() { return type == Object; }
216 bool isAssign() { return type == Assign_; }
217 bool isAssignName() { return type == AssignName_; }
218
219 bool isBool(bool b) { return type == Bool && b == boo; } // avoid overloading == as it might overload over int
220
221 // convenience function to check if something is an array and
222 // also has a certain string as the first element. This is a
223 // very common operation as the first element defines the node
224 // type for most ast nodes
225 bool isArray(IString name) {
226 return isArray() && (*this)[0] == name;
227 }
228
229 const char* getCString() {
230 assert(isString());
231 return str.str;
232 }
233 IString& getIString() {
234 assert(isString());
235 return str;
236 }
237 double& getNumber() {
238 assert(isNumber());
239 return num;
240 }
241 ArrayStorage& getArray() {
242 assert(isArray());
243 return *arr;
244 }
245 bool& getBool() {
246 assert(isBool());
247 return boo;
248 }
249
250 Assign* asAssign();
251 AssignName* asAssignName();
252
253 int32_t getInteger() { // convenience function to get a known integer
254 assert(fmod(getNumber(), 1) == 0);
255 int32_t ret = getNumber();
256 assert(double(ret) == getNumber()); // no loss in conversion
257 return ret;
258 }
259
260 Value& operator=(const Value& other) {
261 free();
262 switch (other.type) {
263 case String:
264 setString(other.str);
265 break;
266 case Number:
267 setNumber(other.num);
268 break;
269 case Array:
270 setArray(*other.arr);
271 break;
272 case Null:
273 setNull();
274 break;
275 case Bool:
276 setBool(other.boo);
277 break;
278 default:
279 abort(); // TODO
280 }
281 return *this;
282 }
283
284 bool operator==(const Value& other) {
285 if (type != other.type) return false;
286 switch (other.type) {
287 case String:
288 return str == other.str;
289 case Number:
290 return num == other.num;
291 case Array:
292 return this == &other; // if you want a deep compare, use deepCompare
293 case Null:
294 break;
295 case Bool:
296 return boo == other.boo;
297 case Object:
298 return this == &other; // if you want a deep compare, use deepCompare
299 default:
300 abort();
301 }
302 return true;
303 }
304
305 char* parse(char* curr) {
306 #define is_json_space(x) (x == 32 || x == 9 || x == 10 || x == 13) /* space, tab, linefeed/newline, or return */
307 #define skip() { while (*curr && is_json_space(*curr)) curr++; }
308 skip();
309 if (*curr == '"') {
310 // String
311 curr++;
312 char *close = strchr(curr, '"');
313 assert(close);
314 *close = 0; // end this string, and reuse it straight from the input
315 setString(curr);
316 curr = close+1;
317 } else if (*curr == '[') {
318 // Array
319 curr++;
320 skip();
321 setArray();
322 while (*curr != ']') {
323 Ref temp = arena.alloc<Value>();
324 arr->push_back(temp);
325 curr = temp->parse(curr);
326 skip();
327 if (*curr == ']') break;
328 assert(*curr == ',');
329 curr++;
330 skip();
331 }
332 curr++;
333 } else if (*curr == 'n') {
334 // Null
335 assert(strncmp(curr, "null", 4) == 0);
336 setNull();
337 curr += 4;
338 } else if (*curr == 't') {
339 // Bool true
340 assert(strncmp(curr, "true", 4) == 0);
341 setBool(true);
342 curr += 4;
343 } else if (*curr == 'f') {
344 // Bool false
345 assert(strncmp(curr, "false", 5) == 0);
346 setBool(false);
347 curr += 5;
348 } else if (*curr == '{') {
349 // Object
350 curr++;
351 skip();
352 setObject();
353 while (*curr != '}') {
354 assert(*curr == '"');
355 curr++;
356 char *close = strchr(curr, '"');
357 assert(close);
358 *close = 0; // end this string, and reuse it straight from the input
359 IString key(curr);
360 curr = close+1;
361 skip();
362 assert(*curr == ':');
363 curr++;
364 skip();
365 Ref value = arena.alloc<Value>();
366 curr = value->parse(curr);
367 (*obj)[key] = value;
368 skip();
369 if (*curr == '}') break;
370 assert(*curr == ',');
371 curr++;
372 skip();
373 }
374 curr++;
375 } else {
376 // Number
377 char *after;
378 setNumber(strtod(curr, &after));
379 curr = after;
380 }
381 return curr;
382 }
383
384 void stringify(std::ostream &os, bool pretty=false);
385
386 // String operations
387
388 // Number operations
389
390 // Array operations
391
392 size_t size() {
393 assert(isArray());
394 return arr->size();
395 }
396
397 void setSize(size_t size) {
398 assert(isArray());
399 auto old = arr->size();
400 if (old != size) arr->resize(size);
401 if (old < size) {
402 for (auto i = old; i < size; i++) {
403 (*arr)[i] = arena.alloc<Value>();
404 }
405 }
406 }
407
408 Ref& operator[](unsigned x) {
409 assert(isArray());
410 return (*arr)[x];
411 }
412
413 Value& push_back(Ref r) {
414 assert(isArray());
415 arr->push_back(r);
416 return *this;
417 }
418 Ref pop_back() {
419 assert(isArray());
420 Ref ret = arr->back();
421 arr->pop_back();
422 return ret;
423 }
424
425 Ref back() {
426 assert(isArray());
427 if (arr->size() == 0) return nullptr;
428 return arr->back();
429 }
430
431 void splice(int x, int num) {
432 assert(isArray());
433 arr->erase(arr->begin() + x, arr->begin() + x + num);
434 }
435
436 int indexOf(Ref other) {
437 assert(isArray());
438 for (size_t i = 0; i < arr->size(); i++) {
439 if (other == (*arr)[i]) return i;
440 }
441 return -1;
442 }
443
444 Ref map(std::function<Ref (Ref node)> func) {
445 assert(isArray());
446 Ref ret = arena.alloc<Value>();
447 ret->setArray();
448 for (size_t i = 0; i < arr->size(); i++) {
449 ret->push_back(func((*arr)[i]));
450 }
451 return ret;
452 }
453
454 Ref filter(std::function<bool (Ref node)> func) {
455 assert(isArray());
456 Ref ret = arena.alloc<Value>();
457 ret->setArray();
458 for (size_t i = 0; i < arr->size(); i++) {
459 Ref curr = (*arr)[i];
460 if (func(curr)) ret->push_back(curr);
461 }
462 return ret;
463 }
464
465 /*
466 void forEach(std::function<void (Ref)> func) {
467 for (size_t i = 0; i < arr->size(); i++) {
468 func((*arr)[i]);
469 }
470 }
471 */
472
473 // Null operations
474
475 // Bool operations
476
477 // Object operations
478
479 Ref& operator[](IString x) {
480 assert(isObject());
481 return (*obj)[x];
482 }
483
484 bool has(IString x) {
485 assert(isObject());
486 return obj->count(x) > 0;
487 }
488 };
489
490 struct Assign : public Value {
491 Ref value_;
492
493 Assign(Ref targetInit, Ref valueInit) {
494 type = Assign_;
495 target() = targetInit;
496 value() = valueInit;
497 }
498
499 Assign() : Assign(nullptr, nullptr) {}
500
501 Ref& target() {
502 return ref;
503 }
504 Ref& value() {
505 return value_;
506 }
507 };
508
509 struct AssignName : public Value {
510 IString target_;
511
512 AssignName(IString targetInit, Ref valueInit) {
513 type = AssignName_;
514 target() = targetInit;
515 value() = valueInit;
516 }
517
518 AssignName() : AssignName(IString(), nullptr) {}
519
520 IString& target() {
521 return target_;
522 }
523 Ref& value() {
524 return ref;
525 }
526 };
527
528 // AST traversals
529
530 // Traverse, calling visit before the children
531 void traversePre(Ref node, std::function<void (Ref)> visit);
532
533 // Traverse, calling visitPre before the children and visitPost after
534 void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost);
535
536 // Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children
537 void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, std::function<void (Ref)> visitPost);
538
539 // Traverses all the top-level functions in the document
540 void traverseFunctions(Ref ast, std::function<void (Ref)> visit);
541
542 // JS printing support
543
544 struct JSPrinter {
545 bool pretty, finalize;
546
547 char *buffer;
548 size_t size, used;
549
550 int indent;
551 bool possibleSpace; // add a space to separate identifiers
552
553 Ref ast;
554
555 JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), buffer(0), size(0), used(0), indent(0), possibleSpace(false), ast(ast_) {}
556
557 ~JSPrinter() {
558 free(buffer);
559 }
560
561 void printAst() {
562 print(ast);
563 buffer[used] = 0;
564 }
565
566 // Utils
567
568 void ensure(int safety=100) {
569 if (size >= used + safety) {
570 return;
571 }
572 size = std::max((size_t)1024, size * 2) + safety;
573 if (!buffer) {
574 buffer = (char*)malloc(size);
575 if (!buffer) {
576 errv("Out of memory allocating %zd bytes for output buffer!", size);
577 abort();
578 }
579 } else {
580 char *buf = (char*)realloc(buffer, size);
581 if (!buf) {
582 free(buffer);
583 errv("Out of memory allocating %zd bytes for output buffer!", size);
584 abort();
585 }
586 buffer = buf;
587 }
588 }
589
590 void emit(char c) {
591 maybeSpace(c);
592 if (!pretty && c == '}' && buffer[used-1] == ';') used--; // optimize ;} into }, the ; is not separating anything
593 ensure(1);
594 buffer[used++] = c;
595 }
596
597 void emit(const char *s) {
598 maybeSpace(*s);
599 int len = strlen(s);
600 ensure(len+1);
601 strncpy(buffer + used, s, len+1);
602 used += len;
603 }
604
605 void newline() {
606 if (!pretty) return;
607 emit('\n');
608 for (int i = 0; i < indent; i++) emit(' ');
609 }
610
611 void space() {
612 if (pretty) emit(' ');
613 }
614
615 void safeSpace() {
616 if (pretty) emit(' ');
617 else possibleSpace = true;
618 }
619
620 void maybeSpace(char s) {
621 if (possibleSpace) {
622 possibleSpace = false;
623 if (isIdentPart(s)) emit(' ');
624 }
625 }
626
627 bool isNothing(Ref node) {
628 return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0;
629 }
630
631 bool isDefun(Ref node) {
632 return node->isArray() && node[0] == DEFUN;
633 }
634
635 bool isBlock(Ref node) {
636 return node->isArray() && node[0] == BLOCK;
637 }
638
639 bool isIf(Ref node) {
640 return node->isArray() && node[0] == IF;
641 }
642
643 void print(Ref node) {
644 ensure();
645 if (node->isString()) {
646 printName(node);
647 return;
648 }
649 if (node->isNumber()) {
650 printNum(node);
651 return;
652 }
653 if (node->isAssignName()) {
654 printAssignName(node);
655 return;
656 }
657 if (node->isAssign()) {
658 printAssign(node);
659 return;
660 }
661 IString type = node[0]->getIString();
662 switch (type.str[0]) {
663 case 'a': {
664 if (type == ARRAY) printArray(node);
665 else abort();
666 break;
667 }
668 case 'b': {
669 if (type == BINARY) printBinary(node);
670 else if (type == BLOCK) printBlock(node);
671 else if (type == BREAK) printBreak(node);
672 else abort();
673 break;
674 }
675 case 'c': {
676 if (type == CALL) printCall(node);
677 else if (type == CONDITIONAL) printConditional(node);
678 else if (type == CONTINUE) printContinue(node);
679 else abort();
680 break;
681 }
682 case 'd': {
683 if (type == DEFUN) printDefun(node);
684 else if (type == DO) printDo(node);
685 else if (type == DOT) printDot(node);
686 else abort();
687 break;
688 }
689 case 'i': {
690 if (type == IF) printIf(node);
691 else abort();
692 break;
693 }
694 case 'l': {
695 if (type == LABEL) printLabel(node);
696 else abort();
697 break;
698 }
699 case 'n': {
700 if (type == NEW) printNew(node);
701 else abort();
702 break;
703 }
704 case 'o': {
705 if (type == OBJECT) printObject(node);
706 break;
707 }
708 case 'r': {
709 if (type == RETURN) printReturn(node);
710 else abort();
711 break;
712 }
713 case 's': {
714 if (type == SUB) printSub(node);
715 else if (type == SEQ) printSeq(node);
716 else if (type == SWITCH) printSwitch(node);
717 else if (type == STRING) printString(node);
718 else abort();
719 break;
720 }
721 case 't': {
722 if (type == TOPLEVEL) printToplevel(node);
723 else if (type == TRY) printTry(node);
724 else abort();
725 break;
726 }
727 case 'u': {
728 if (type == UNARY_PREFIX) printUnaryPrefix(node);
729 else abort();
730 break;
731 }
732 case 'v': {
733 if (type == VAR) printVar(node);
734 else abort();
735 break;
736 }
737 case 'w': {
738 if (type == WHILE) printWhile(node);
739 else abort();
740 break;
741 }
742 default: {
743 errv("cannot yet print %s\n", type.str);
744 abort();
745 }
746 }
747 }
748
749 // print a node, and if nothing is emitted, emit something instead
750 void print(Ref node, const char *otherwise) {
751 auto last = used;
752 print(node);
753 if (used == last) emit(otherwise);
754 }
755
756 void printStats(Ref stats) {
757 bool first = true;
758 for (size_t i = 0; i < stats->size(); i++) {
759 Ref curr = stats[i];
760 if (!isNothing(curr)) {
761 if (first) first = false;
762 else newline();
763 print(curr);
764 if (!isDefun(curr) && !isBlock(curr) && !isIf(curr)) {
765 emit(';');
766 }
767 }
768 }
769 }
770
771 void printToplevel(Ref node) {
772 if (node[1]->size() > 0) {
773 printStats(node[1]);
774 }
775 }
776
777 void printBlock(Ref node) {
778 if (node->size() == 1 || node[1]->size() == 0) {
779 emit("{}");
780 return;
781 }
782 emit('{');
783 indent++;
784 newline();
785 printStats(node[1]);
786 indent--;
787 newline();
788 emit('}');
789 }
790
791 void printDefun(Ref node) {
792 emit("function ");
793 emit(node[1]->getCString());
794 emit('(');
795 Ref args = node[2];
796 for (size_t i = 0; i < args->size(); i++) {
797 if (i > 0) (pretty ? emit(", ") : emit(','));
798 emit(args[i]->getCString());
799 }
800 emit(')');
801 space();
802 if (node->size() == 3 || node[3]->size() == 0) {
803 emit("{}");
804 return;
805 }
806 emit('{');
807 indent++;
808 newline();
809 printStats(node[3]);
810 indent--;
811 newline();
812 emit('}');
813 newline();
814 }
815
816 void printAssign(Ref node) {
817 auto* assign = node->asAssign();
818 printChild(assign->target(), node, -1);
819 space();
820 emit('=');
821 space();
822 printChild(assign->value(), node, 1);
823 }
824
825 void printAssignName(Ref node) {
826 auto *assign = node->asAssignName();
827 emit(assign->target().c_str());
828 space();
829 emit('=');
830 space();
831 printChild(assign->value(), node, 1);
832 }
833
834 void printName(Ref node) {
835 emit(node->getCString());
836 }
837
838 static char* numToString(double d, bool finalize=true) {
839 bool neg = d < 0;
840 if (neg) d = -d;
841 // try to emit the fewest necessary characters
842 bool integer = fmod(d, 1) == 0;
843 #define BUFFERSIZE 1000
844 static char full_storage_f[BUFFERSIZE], full_storage_e[BUFFERSIZE]; // f is normal, e is scientific for float, x for integer
845 static char *storage_f = full_storage_f + 1, *storage_e = full_storage_e + 1; // full has one more char, for a possible '-'
846 auto err_f = std::numeric_limits<double>::quiet_NaN();
847 auto err_e = std::numeric_limits<double>::quiet_NaN();
848 for (int e = 0; e <= 1; e++) {
849 char *buffer = e ? storage_e : storage_f;
850 double temp;
851 if (!integer) {
852 static char format[6];
853 for (int i = 0; i <= 18; i++) {
854 format[0] = '%';
855 format[1] = '.';
856 if (i < 10) {
857 format[2] = '0' + i;
858 format[3] = e ? 'e' : 'f';
859 format[4] = 0;
860 } else {
861 format[2] = '1';
862 format[3] = '0' + (i - 10);
863 format[4] = e ? 'e' : 'f';
864 format[5] = 0;
865 }
866 snprintf(buffer, BUFFERSIZE-1, format, d);
867 sscanf(buffer, "%lf", &temp);
868 //errv("%.18f, %.18e => %s => %.18f, %.18e (%d), ", d, d, buffer, temp, temp, temp == d);
869 if (temp == d) break;
870 }
871 } else {
872 // integer
873 assert(d >= 0);
874 if (wasm::isUInteger64(d)) {
875 unsigned long long uu = wasm::toUInteger64(d);
876 bool asHex = e && !finalize;
877 snprintf(buffer, BUFFERSIZE-1, asHex ? "0x%llx" : "%llu", uu);
878 if (asHex) {
879 unsigned long long tempULL;
880 sscanf(buffer, "%llx", &tempULL);
881 temp = (double)tempULL;
882 } else {
883 sscanf(buffer, "%lf", &temp);
884 }
885 } else {
886 // too large for a machine integer, just use floats
887 snprintf(buffer, BUFFERSIZE-1, e ? "%e" : "%.0f", d); // even on integers, e with a dot is useful, e.g. 1.2e+200
888 sscanf(buffer, "%lf", &temp);
889 }
890 //errv("%.18f, %.18e => %s => %.18f, %.18e, %llu (%d)\n", d, d, buffer, temp, temp, uu, temp == d);
891 }
892 (e ? err_e : err_f) = fabs(temp - d);
893 //errv("current attempt: %.18f => %s", d, buffer);
894 //assert(temp == d);
895 char *dot = strchr(buffer, '.');
896 if (dot) {
897 // remove trailing zeros
898 char *end = dot+1;
899 while (*end >= '0' && *end <= '9') end++;
900 end--;
901 while (*end == '0') {
902 char *copy = end;
903 do {
904 copy[0] = copy[1];
905 } while (*copy++ != 0);
906 end--;
907 }
908 //errv("%.18f => %s", d, buffer);
909 // remove preceding zeros
910 while (*buffer == '0') {
911 char *copy = buffer;
912 do {
913 copy[0] = copy[1];
914 } while (*copy++ != 0);
915 }
916 //errv("%.18f ===> %s", d, buffer);
917 } else if (!integer || !e) {
918 // no dot. try to change 12345000 => 12345e3
919 char *end = strchr(buffer, 0);
920 end--;
921 char *test = end;
922 // remove zeros, and also doubles can use at most 24 digits, we can truncate any extras even if not zero
923 while ((*test == '0' || test - buffer > 24) && test > buffer) test--;
924 int num = end - test;
925 if (num >= 3) {
926 test++;
927 test[0] = 'e';
928 if (num < 10) {
929 test[1] = '0' + num;
930 test[2] = 0;
931 } else if (num < 100) {
932 test[1] = '0' + (num / 10);
933 test[2] = '0' + (num % 10);
934 test[3] = 0;
935 } else {
936 assert(num < 1000);
937 test[1] = '0' + (num / 100);
938 test[2] = '0' + (num % 100) / 10;
939 test[3] = '0' + (num % 10);
940 test[4] = 0;
941 }
942 }
943 }
944 //errv("..current attempt: %.18f => %s", d, buffer);
945 }
946 //fprintf(stderr, "options:\n%s\n%s\n (first? %d)\n", storage_e, storage_f, strlen(storage_e) < strlen(storage_f));
947 char *ret;
948 if (err_e == err_f) {
949 ret = strlen(storage_e) < strlen(storage_f) ? storage_e : storage_f;
950 } else {
951 ret = err_e < err_f ? storage_e : storage_f;
952 }
953 if (neg) {
954 ret--; // safe to go back one, there is one more char in full_*
955 *ret = '-';
956 }
957 return ret;
958 }
959
960 void printNum(Ref node) {
961 emit(numToString(node->getNumber(), finalize));
962 }
963
964 void printString(Ref node) {
965 emit('"');
966 emit(node[1]->getCString());
967 emit('"');
968 }
969
970 // Parens optimizing
971
972 bool capturesOperators(Ref node) {
973 Ref type = node[0];
974 return type == CALL || type == ARRAY || type == OBJECT || type == SEQ;
975 }
976
977 int getPrecedence(Ref node, bool parent) {
978 if (node->isAssign() || node->isAssignName()) {
979 return OperatorClass::getPrecedence(OperatorClass::Binary, SET);
980 }
981 if (!node->isArray()) {
982 // node is a value
983 return -1;
984 }
985 Ref type = node[0];
986 if (type == BINARY || type == UNARY_PREFIX) {
987 return OperatorClass::getPrecedence(type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, node[1]->getIString());
988 } else if (type == SEQ) {
989 return OperatorClass::getPrecedence(OperatorClass::Binary, COMMA);
990 } else if (type == CALL) {
991 return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) : -1; // call arguments are split by commas, but call itself is safe
992 } else if (type == CONDITIONAL) {
993 return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION);
994 }
995 // otherwise, this is something that fixes precedence explicitly, and we can ignore
996 return -1; // XXX
997 }
998
999 // check whether we need parens for the child, when rendered in the parent
1000 // @param childPosition -1 means it is printed to the left of parent, 0 means "anywhere", 1 means right
1001 bool needParens(Ref parent, Ref child, int childPosition) {
1002 int parentPrecedence = getPrecedence(parent, true);
1003 int childPrecedence = getPrecedence(child, false);
1004
1005 if (childPrecedence > parentPrecedence) return true; // child is definitely a danger
1006 if (childPrecedence < parentPrecedence) return false; // definitely cool
1007 // equal precedence, so associativity (rtl/ltr) is what matters
1008 // (except for some exceptions, where multiple operators can combine into confusion)
1009 if (parent->isArray() && parent[0] == UNARY_PREFIX) {
1010 assert(child[0] == UNARY_PREFIX);
1011 if ((parent[1] == PLUS || parent[1] == MINUS) && child[1] == parent[1]) {
1012 // cannot emit ++x when we mean +(+x)
1013 return true;
1014 }
1015 }
1016 if (childPosition == 0) return true; // child could be anywhere, so always paren
1017 if (childPrecedence < 0) return false; // both precedences are safe
1018 // check if child is on the dangerous side
1019 if (OperatorClass::getRtl(parentPrecedence)) return childPosition < 0;
1020 else return childPosition > 0;
1021 }
1022
1023 void printChild(Ref child, Ref parent, int childPosition=0) {
1024 bool parens = needParens(parent, child, childPosition);
1025 if (parens) emit('(');
1026 print(child);
1027 if (parens) emit(')');
1028 }
1029
1030 void printBinary(Ref node) {
1031 printChild(node[2], node, -1);
1032 space();
1033 emit(node[1]->getCString());
1034 space();
1035 printChild(node[3], node, 1);
1036 }
1037
1038 void printUnaryPrefix(Ref node) {
1039 if (finalize && node[1] == PLUS &&
1040 (node[2]->isNumber() ||
1041 (node[2]->isArray() && node[2][0] == UNARY_PREFIX &&
1042 node[2][1] == MINUS && node[2][2]->isNumber()))) {
1043 // emit a finalized number
1044 int last = used;
1045 print(node[2]);
1046 ensure(1); // we temporarily append a 0
1047 char *curr = buffer + last; // ensure might invalidate
1048 buffer[used] = 0;
1049 if (strchr(curr, '.')) return; // already a decimal point, all good
1050 char *e = strchr(curr, 'e');
1051 if (!e) {
1052 emit(".0");
1053 return;
1054 }
1055 ensure(3);
1056 curr = buffer + last; // ensure might invalidate
1057 char *end = strchr(curr, 0);
1058 while (end >= e) {
1059 end[2] = end[0];
1060 end--;
1061 }
1062 e[0] = '.';
1063 e[1] = '0';
1064 used += 2;
1065 return;
1066 }
1067 if ((buffer[used-1] == '-' && node[1] == MINUS) ||
1068 (buffer[used-1] == '+' && node[1] == PLUS)) {
1069 emit(' '); // cannot join - and - to --, looks like the -- operator
1070 }
1071 emit(node[1]->getCString());
1072 printChild(node[2], node, 1);
1073 }
1074
1075 void printConditional(Ref node) {
1076 printChild(node[1], node, -1);
1077 space();
1078 emit('?');
1079 space();
1080 printChild(node[2], node, 0);
1081 space();
1082 emit(':');
1083 space();
1084 printChild(node[3], node, 1);
1085 }
1086
1087 void printCall(Ref node) {
1088 printChild(node[1], node, 0);
1089 emit('(');
1090 Ref args = node[2];
1091 for (size_t i = 0; i < args->size(); i++) {
1092 if (i > 0) (pretty ? emit(", ") : emit(','));
1093 printChild(args[i], node, 0);
1094 }
1095 emit(')');
1096 }
1097
1098 void printSeq(Ref node) {
1099 printChild(node[1], node, -1);
1100 emit(',');
1101 space();
1102 printChild(node[2], node, 1);
1103 }
1104
1105 void printDot(Ref node) {
1106 print(node[1]);
1107 emit('.');
1108 emit(node[2]->getCString());
1109 }
1110
1111 void printSwitch(Ref node) {
1112 emit("switch");
1113 space();
1114 emit('(');
1115 print(node[1]);
1116 emit(')');
1117 space();
1118 emit('{');
1119 newline();
1120 Ref cases = node[2];
1121 for (size_t i = 0; i < cases->size(); i++) {
1122 Ref c = cases[i];
1123 if (!c[0]) {
1124 emit("default:");
1125 } else {
1126 emit("case ");
1127 print(c[0]);
1128 emit(':');
1129 }
1130 if (c[1]->size() > 0) {
1131 indent++;
1132 newline();
1133 auto curr = used;
1134 printStats(c[1]);
1135 indent--;
1136 if (curr != used) newline();
1137 else used--; // avoid the extra indentation we added tentatively
1138 } else {
1139 newline();
1140 }
1141 }
1142 emit('}');
1143 }
1144
1145 void printTry(Ref node) {
1146 emit("try ");
1147 printBlock(node[1]);
1148 emit(" catch (");
1149 printName(node[2]);
1150 emit(") ");
1151 printBlock(node[3]);
1152 }
1153
1154 void printSub(Ref node) {
1155 printChild(node[1], node, -1);
1156 emit('[');
1157 print(node[2]);
1158 emit(']');
1159 }
1160
1161 void printVar(Ref node) {
1162 emit("var ");
1163 Ref args = node[1];
1164 for (size_t i = 0; i < args->size(); i++) {
1165 if (i > 0) (pretty ? emit(", ") : emit(','));
1166 emit(args[i][0]->getCString());
1167 if (args[i]->size() > 1) {
1168 space();
1169 emit('=');
1170 space();
1171 print(args[i][1]);
1172 }
1173 }
1174 }
1175
1176 static bool ifHasElse(Ref node) {
1177 assert(node->isArray() && node[0] == IF);
1178 return node->size() >= 4 && !!node[3];
1179 }
1180
1181 void printIf(Ref node) {
1182 emit("if");
1183 safeSpace();
1184 emit('(');
1185 print(node[1]);
1186 emit(')');
1187 space();
1188 // special case: we need braces to save us from ambiguity, if () { if () } else. otherwise else binds to inner if
1189 // also need to recurse for if () { if () { } else { if () } else
1190 // (note that this is only a problem if the if body has a single element in it, not a block or such, as then
1191 // the block would be braced)
1192 // this analysis is a little conservative - it assumes any child if could be confused with us, which implies
1193 // all other braces vanished (the worst case for us, we are not saved by other braces).
1194 bool needBraces = false;
1195 bool hasElse = ifHasElse(node);
1196 if (hasElse) {
1197 Ref child = node[2];
1198 while (child->isArray() && child[0] == IF) {
1199 if (!ifHasElse(child)) {
1200 needBraces = true;
1201 break;
1202 }
1203 child = child[3]; // continue into the else
1204 }
1205 }
1206 if (needBraces) {
1207 emit('{');
1208 indent++;
1209 newline();
1210 print(node[2]);
1211 indent--;
1212 newline();
1213 emit('}');
1214 } else {
1215 print(node[2], "{}");
1216 if (!isBlock(node[2])) emit(';');
1217 }
1218 if (hasElse) {
1219 space();
1220 emit("else");
1221 safeSpace();
1222 print(node[3], "{}");
1223 if (!isBlock(node[3])) emit(';');
1224 }
1225 }
1226
1227 void printDo(Ref node) {
1228 emit("do");
1229 safeSpace();
1230 print(node[2], "{}");
1231 space();
1232 emit("while");
1233 space();
1234 emit('(');
1235 print(node[1]);
1236 emit(')');
1237 }
1238
1239 void printWhile(Ref node) {
1240 emit("while");
1241 space();
1242 emit('(');
1243 print(node[1]);
1244 emit(')');
1245 space();
1246 print(node[2], "{}");
1247 }
1248
1249 void printLabel(Ref node) {
1250 emit(node[1]->getCString());
1251 space();
1252 emit(':');
1253 space();
1254 print(node[2]);
1255 }
1256
1257 void printReturn(Ref node) {
1258 emit("return");
1259 if (!!node[1]) {
1260 emit(' ');
1261 print(node[1]);
1262 }
1263 }
1264
1265 void printBreak(Ref node) {
1266 emit("break");
1267 if (!!node[1]) {
1268 emit(' ');
1269 emit(node[1]->getCString());
1270 }
1271 }
1272
1273 void printContinue(Ref node) {
1274 emit("continue");
1275 if (!!node[1]) {
1276 emit(' ');
1277 emit(node[1]->getCString());
1278 }
1279 }
1280
1281 void printNew(Ref node) {
1282 emit("new ");
1283 print(node[1]);
1284 }
1285
1286 void printArray(Ref node) {
1287 emit('[');
1288 Ref args = node[1];
1289 for (size_t i = 0; i < args->size(); i++) {
1290 if (i > 0) (pretty ? emit(", ") : emit(','));
1291 print(args[i]);
1292 }
1293 emit(']');
1294 }
1295
1296 void printObject(Ref node) {
1297 emit('{');
1298 indent++;
1299 newline();
1300 Ref args = node[1];
1301 for (size_t i = 0; i < args->size(); i++) {
1302 if (i > 0) {
1303 pretty ? emit(", ") : emit(',');
1304 newline();
1305 }
1306 const char *str = args[i][0]->getCString();
1307 const char *check = str;
1308 bool needQuote = false;
1309 while (*check) {
1310 if (!isalnum(*check) && *check != '_' && *check != '$') {
1311 needQuote = true;
1312 break;
1313 }
1314 check++;
1315 }
1316 if (needQuote) emit('"');
1317 emit(str);
1318 if (needQuote) emit('"');
1319 emit(":");
1320 space();
1321 print(args[i][1]);
1322 }
1323 indent--;
1324 newline();
1325 emit('}');
1326 }
1327 };
1328
1329
1330 // cashew builder
1331
1332 class ValueBuilder {
1333 static Ref makeRawString(const IString& s) {
1334 return &arena.alloc<Value>()->setString(s);
1335 }
1336
1337 static Ref makeNull() {
1338 return &arena.alloc<Value>()->setNull();
1339 }
1340
1341 public:
1342 static Ref makeRawArray(int size_hint=0) {
1343 return &arena.alloc<Value>()->setArray(size_hint);
1344 }
1345
1346 static Ref makeToplevel() {
1347 return &makeRawArray(2)->push_back(makeRawString(TOPLEVEL))
1348 .push_back(makeRawArray());
1349 }
1350
1351 static Ref makeString(IString str) {
1352 return &makeRawArray(2)->push_back(makeRawString(STRING))
1353 .push_back(makeRawString(str));
1354 }
1355
1356 static Ref makeBlock() {
1357 return &makeRawArray(2)->push_back(makeRawString(BLOCK))
1358 .push_back(makeRawArray());
1359 }
1360
1361 static Ref makeName(IString name) {
1362 return makeRawString(name);
1363 }
1364
1365 static void setBlockContent(Ref target, Ref block) {
1366 if (target[0] == TOPLEVEL) {
1367 target[1]->setArray(block[1]->getArray());
1368 } else if (target[0] == DEFUN) {
1369 target[3]->setArray(block[1]->getArray());
1370 } else abort();
1371 }
1372
1373 static void appendToBlock(Ref block, Ref element) {
1374 assert(block[0] == BLOCK);
1375 block[1]->push_back(element);
1376 }
1377
1378 static Ref makeCall(Ref target) {
1379 return &makeRawArray(3)->push_back(makeRawString(CALL))
1380 .push_back(target)
1381 .push_back(makeRawArray());
1382 }
1383 static Ref makeCall(Ref target, Ref arg) {
1384 Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL))
1385 .push_back(target)
1386 .push_back(makeRawArray());
1387 ret[2]->push_back(arg);
1388 return ret;
1389 }
1390 static Ref makeCall(IString target) {
1391 Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL))
1392 .push_back(makeName(target))
1393 .push_back(makeRawArray());
1394 return ret;
1395 }
1396
1397 template<typename ...Ts>
1398 static Ref makeCall(IString target, Ts... args) {
1399 size_t nArgs = sizeof...(Ts);
1400 Ref callArgs = makeRawArray(nArgs);
1401 Ref argArray[] = {args...};
1402 for (size_t i = 0; i < nArgs; ++i) {
1403 callArgs->push_back(argArray[i]);
1404 }
1405 return &makeRawArray(3)->push_back(makeRawString(CALL))
1406 .push_back(makeName(target))
1407 .push_back(callArgs);
1408 }
1409
1410 static void appendToCall(Ref call, Ref element) {
1411 assert(call[0] == CALL);
1412 call[2]->push_back(element);
1413 }
1414
1415 static Ref makeStatement(Ref contents) {
1416 return contents;
1417 }
1418
1419 static Ref makeDouble(double num) {
1420 return &arena.alloc<Value>()->setNumber(num);
1421 }
1422 static Ref makeInt(uint32_t num) {
1423 return makeDouble(double(num));
1424 }
1425 static Ref makeNum(double num) {
1426 return makeDouble(num);
1427 }
1428
1429 static Ref makeUnary(IString op, Ref value) {
1430 return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX))
1431 .push_back(makeRawString(op))
1432 .push_back(value);
1433 }
1434
1435 static Ref makeBinary(Ref left, IString op, Ref right) {
1436 if (op == SET) {
1437 if (left->isString()) {
1438 return &arena.alloc<AssignName>()->setAssignName(left->getIString(), right);
1439 } else {
1440 return &arena.alloc<Assign>()->setAssign(left, right);
1441 }
1442 } else if (op == COMMA) {
1443 return &makeRawArray(3)->push_back(makeRawString(SEQ))
1444 .push_back(left)
1445 .push_back(right);
1446 } else {
1447 return &makeRawArray(4)->push_back(makeRawString(BINARY))
1448 .push_back(makeRawString(op))
1449 .push_back(left)
1450 .push_back(right);
1451 }
1452 }
1453
1454 static Ref makePrefix(IString op, Ref right) {
1455 return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX))
1456 .push_back(makeRawString(op))
1457 .push_back(right);
1458 }
1459
1460 static Ref makeFunction(IString name) {
1461 return &makeRawArray(4)->push_back(makeRawString(DEFUN))
1462 .push_back(makeRawString(name))
1463 .push_back(makeRawArray())
1464 .push_back(makeRawArray());
1465 }
1466
1467 static void appendArgumentToFunction(Ref func, IString arg) {
1468 assert(func[0] == DEFUN);
1469 func[2]->push_back(makeRawString(arg));
1470 }
1471
1472 static Ref makeVar(bool is_const=false) {
1473 return &makeRawArray(2)->push_back(makeRawString(VAR))
1474 .push_back(makeRawArray());
1475 }
1476
1477 static void appendToVar(Ref var, IString name, Ref value) {
1478 assert(var[0] == VAR);
1479 Ref array = &makeRawArray(1)->push_back(makeRawString(name));
1480 if (!!value) array->push_back(value);
1481 var[1]->push_back(array);
1482 }
1483
1484 static Ref makeReturn(Ref value) {
1485 return &makeRawArray(2)->push_back(makeRawString(RETURN))
1486 .push_back(!!value ? value : makeNull());
1487 }
1488
1489 static Ref makeIndexing(Ref target, Ref index) {
1490 return &makeRawArray(3)->push_back(makeRawString(SUB))
1491 .push_back(target)
1492 .push_back(index);
1493 }
1494
1495 static Ref makeIf(Ref condition, Ref ifTrue, Ref ifFalse) {
1496 return &makeRawArray(4)->push_back(makeRawString(IF))
1497 .push_back(condition)
1498 .push_back(ifTrue)
1499 .push_back(!!ifFalse ? ifFalse : makeNull());
1500 }
1501
1502 static Ref makeConditional(Ref condition, Ref ifTrue, Ref ifFalse) {
1503 return &makeRawArray(4)->push_back(makeRawString(CONDITIONAL))
1504 .push_back(condition)
1505 .push_back(ifTrue)
1506 .push_back(ifFalse);
1507 }
1508
1509 static Ref makeSeq(Ref left, Ref right) {
1510 return &makeRawArray(3)->push_back(makeRawString(SEQ))
1511 .push_back(left)
1512 .push_back(right);
1513 }
1514
1515 static Ref makeDo(Ref body, Ref condition) {
1516 return &makeRawArray(3)->push_back(makeRawString(DO))
1517 .push_back(condition)
1518 .push_back(body);
1519 }
1520
1521 static Ref makeWhile(Ref condition, Ref body) {
1522 return &makeRawArray(3)->push_back(makeRawString(WHILE))
1523 .push_back(condition)
1524 .push_back(body);
1525 }
1526
1527 static Ref makeFor(Ref init, Ref condition, Ref inc, Ref body) {
1528 return &makeRawArray(5)->push_back(makeRawString(FOR))
1529 .push_back(init)
1530 .push_back(condition)
1531 .push_back(inc)
1532 .push_back(body);
1533 }
1534
1535 static Ref makeBreak(IString label) {
1536 return &makeRawArray(2)->push_back(makeRawString(BREAK))
1537 .push_back(!!label ? makeRawString(label) : makeNull());
1538 }
1539
1540 static Ref makeContinue(IString label) {
1541 return &makeRawArray(2)->push_back(makeRawString(CONTINUE))
1542 .push_back(!!label ? makeRawString(label) : makeNull());
1543 }
1544
1545 static Ref makeLabel(IString name, Ref body) {
1546 return &makeRawArray(3)->push_back(makeRawString(LABEL))
1547 .push_back(makeRawString(name))
1548 .push_back(body);
1549 }
1550
1551 static Ref makeSwitch(Ref input) {
1552 return &makeRawArray(3)->push_back(makeRawString(SWITCH))
1553 .push_back(input)
1554 .push_back(makeRawArray());
1555 }
1556
1557 static void appendCaseToSwitch(Ref switch_, Ref arg) {
1558 assert(switch_[0] == SWITCH);
1559 switch_[2]->push_back(&makeRawArray(2)->push_back(arg)
1560 .push_back(makeRawArray()));
1561 }
1562
1563 static void appendDefaultToSwitch(Ref switch_) {
1564 assert(switch_[0] == SWITCH);
1565 switch_[2]->push_back(&makeRawArray(2)->push_back(makeNull())
1566 .push_back(makeRawArray()));
1567 }
1568
1569 static void appendCodeToSwitch(Ref switch_, Ref code, bool explicitBlock) {
1570 assert(switch_[0] == SWITCH);
1571 assert(code[0] == BLOCK);
1572 if (!explicitBlock) {
1573 for (size_t i = 0; i < code[1]->size(); i++) {
1574 switch_[2]->back()->back()->push_back(code[1][i]);
1575 }
1576 } else {
1577 switch_[2]->back()->back()->push_back(code);
1578 }
1579 }
1580
1581 static Ref makeTry(Ref try_, Ref arg, Ref catch_) {
1582 assert(try_[0] == BLOCK);
1583 assert(catch_[0] == BLOCK);
1584 return &makeRawArray(3)->push_back(makeRawString(TRY))
1585 .push_back(try_)
1586 .push_back(arg)
1587 .push_back(catch_);
1588 }
1589
1590 static Ref makeDot(Ref obj, IString key) {
1591 return &makeRawArray(3)->push_back(makeRawString(DOT))
1592 .push_back(obj)
1593 .push_back(makeRawString(key));
1594 }
1595
1596 template<typename ...Ts>
1597 static Ref makeDot(Ref obj, Ref key, Ts... args) {
1598 return makeDot(makeDot(obj, key), args...);
1599 }
1600
1601 static Ref makeDot(Ref obj, Ref key) {
1602 assert(key->isString());
1603 return makeDot(obj, key->getIString());
1604 }
1605
1606 static Ref makeNew(Ref call) {
1607 return &makeRawArray(2)->push_back(makeRawString(NEW))
1608 .push_back(call);
1609 }
1610
1611 static Ref makeArray() {
1612 return &makeRawArray(2)->push_back(makeRawString(ARRAY))
1613 .push_back(makeRawArray());
1614 }
1615
1616 static void appendToArray(Ref array, Ref element) {
1617 assert(array[0] == ARRAY);
1618 array[1]->push_back(element);
1619 }
1620
1621 static Ref makeObject() {
1622 return &makeRawArray(2)->push_back(makeRawString(OBJECT))
1623 .push_back(makeRawArray());
1624 }
1625
1626 static void appendToObject(Ref array, IString key, Ref value) {
1627 assert(array[0] == OBJECT);
1628 array[1]->push_back(&makeRawArray(2)->push_back(makeRawString(key))
1629 .push_back(value));
1630 }
1631
1632 static Ref makeSub(Ref obj, Ref index) {
1633 return &makeRawArray(2)->push_back(makeRawString(SUB))
1634 .push_back(obj)
1635 .push_back(index);
1636 }
1637
1638 static Ref makePtrShift(Ref ptr, int shifts) {
1639 return makeBinary(ptr, RSHIFT, makeInt(shifts));
1640 }
1641 };
1642
1643 // Tolerates 0.0 in the input; does not trust a +() to be there.
1644 class DotZeroValueBuilder : public ValueBuilder {
1645 public:
1646 static Ref makeDouble(double num) {
1647 return makePrefix(PLUS, ValueBuilder::makeDouble(num));
1648 }
1649 };
1650
1651 } // namespace cashew
1652
1653 #endif // wasm_simple_ast_h