diff --git a/Makefile b/Makefile index 2193c96..a117d71 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,9 @@ incdir ?= $(prefix)/include libdir ?= $(prefix)/lib ifeq "$(wildcard .git)" ".git" -VERSION := $(shell git describe --tags --always) + VERSION := $(shell git describe --tags --always) else -VERSION := $(shell basename $$PWD | sed -e s,^mujs-,,) + VERSION := $(shell basename $$PWD | sed -e s,^mujs-,,) endif # Compiler flags for various configurations: @@ -18,22 +18,22 @@ endif CFLAGS := -std=c99 -pedantic -Wall -Wextra -Wno-unused-parameter ifeq "$(CC)" "clang" -CFLAGS += -Wunreachable-code + CFLAGS += -Wunreachable-code endif ifeq "$(shell uname)" "Linux" -CFLAGS += -ffunction-sections -fdata-sections -LDFLAGS += -Wl,--gc-sections + CFLAGS += -ffunction-sections -fdata-sections + LDFLAGS += -Wl,--gc-sections endif ifeq "$(build)" "debug" -CFLAGS += -g + CFLAGS += -g else ifeq "$(build)" "sanitize" -CFLAGS += -pipe -g -fsanitize=address -fno-omit-frame-pointer -LDFLAGS += -fsanitize=address + CFLAGS += -pipe -g -fsanitize=address -fno-omit-frame-pointer + LDFLAGS += -fsanitize=address else -CFLAGS += -Os -LDFLAGS += -Wl,-s + CFLAGS += -Os + LDFLAGS += -Wl,-s endif CFLAGS += $(XCFLAGS) @@ -46,7 +46,7 @@ SRCS := $(wildcard js*.c utf*.c regexp.c) HDRS := $(wildcard js*.h mujs.h utf.h regexp.h) default: static -static: $(OUT) $(OUT)/mujs $(OUT)/libmujs.a $(OUT)/mujs.pc +static: $(OUT)/mujs-pp $(OUT)/mujs $(OUT)/libmujs.a $(OUT)/mujs.pc shared: static $(OUT)/libmujs.so astnames.h: jsparse.h @@ -60,22 +60,28 @@ one.c: $(SRCS) jsdump.c: astnames.h opnames.h -$(OUT): - mkdir -p $(OUT) - -$(OUT)/main.o: main.c $(HDRS) +$(OUT)/%.o: %.c $(HDRS) + @ mkdir -p $(dir $@) $(CC) $(CFLAGS) -o $@ -c $< $(OUT)/libmujs.o: one.c $(HDRS) + @ mkdir -p $(dir $@) $(CC) $(CFLAGS) -o $@ -c $< $(OUT)/libmujs.a: $(OUT)/libmujs.o + @ mkdir -p $(dir $@) $(AR) cru $@ $^ $(OUT)/libmujs.so: one.c $(HDRS) + @ mkdir -p $(dir $@) $(CC) $(CFLAGS) -fPIC -shared -o $@ $< -lm $(OUT)/mujs: $(OUT)/libmujs.o $(OUT)/main.o + @ mkdir -p $(dir $@) + $(CC) $(LDFLAGS) -o $@ $^ -lm + +$(OUT)/mujs-pp: $(OUT)/libmujs.o $(OUT)/pp.o + @ mkdir -p $(dir $@) $(CC) $(LDFLAGS) -o $@ $^ -lm $(OUT)/mujs.pc: diff --git a/jsdump.c b/jsdump.c index f3627d4..d3f089d 100644 --- a/jsdump.c +++ b/jsdump.c @@ -17,6 +17,8 @@ static const char *opname[] = { NULL }; +static int minify = 0; + const char *jsP_aststring(enum js_AstType type) { if (type < nelem(astname)-1) @@ -34,6 +36,7 @@ const char *jsC_opcodestring(enum js_OpCode opcode) static int prec(enum js_AstType type) { switch (type) { + case AST_IDENTIFIER: case EXP_IDENTIFIER: case EXP_NUMBER: case EXP_STRING: @@ -142,13 +145,27 @@ static void ps(const char *s) static void in(int d) { - while (d-- > 0) - putchar('\t'); + if (minify < 1) + while (d-- > 0) + putchar('\t'); } static void nl(void) { - putchar('\n'); + if (minify < 2) + putchar('\n'); +} + +static void sp(void) +{ + if (minify < 1) + putchar(' '); +} + +static void comma(void) +{ + putchar(','); + sp(); } /* Pretty-printed Javascript syntax */ @@ -166,68 +183,79 @@ static void pargs(int d, js_Ast *list) pexpi(d, COMMA, list->a); list = list->b; if (list) - ps(", "); + comma(); } } static void parray(int d, js_Ast *list) { - ps("["); + pc('['); while (list) { assert(list->type == AST_LIST); pexpi(d, COMMA, list->a); list = list->b; if (list) - ps(", "); + comma(); } - ps("]"); + pc(']'); } static void pobject(int d, js_Ast *list) { - ps("{"); + pc('{'); + if (list) { + nl(); + in(d+1); + } while (list) { js_Ast *kv = list->a; assert(list->type == AST_LIST); switch (kv->type) { default: break; case EXP_PROP_VAL: - pexpi(d, COMMA, kv->a); - ps(": "); - pexpi(d, COMMA, kv->b); + pexpi(d+1, COMMA, kv->a); + pc(':'); sp(); + pexpi(d+1, COMMA, kv->b); break; case EXP_PROP_GET: ps("get "); - pexpi(d, COMMA, kv->a); - ps("() {\n"); - pstmlist(d, kv->c); - in(d); ps("}"); + pexpi(d+1, COMMA, kv->a); + ps("()"); sp(); pc('{'); nl(); + pstmlist(d+1, kv->c); + in(d+1); pc('}'); break; case EXP_PROP_SET: ps("set "); - pexpi(d, COMMA, kv->a); - ps("("); - pargs(d, kv->b); - ps(") {\n"); - pstmlist(d, kv->c); - in(d); ps("}"); + pexpi(d+1, COMMA, kv->a); + pc('('); + pargs(d+1, kv->b); + pc(')'); sp(); pc('{'); nl(); + pstmlist(d+1, kv->c); + in(d+1); pc('}'); break; } list = list->b; - if (list) - ps(", "); + if (list) { + pc(','); + nl(); + in(d+1); + } else { + nl(); + in(d); + } } - ps("}"); + pc('}'); } static void pstr(const char *s) { static const char *HEX = "0123456789ABCDEF"; Rune c; - pc('"'); + pc(minify ? '\'' : '"'); while (*s) { s += chartorune(&c, s); switch (c) { + case '\'': ps("\\'"); break; case '"': ps("\\\""); break; case '\\': ps("\\\\"); break; case '\b': ps("\\b"); break; @@ -247,7 +275,7 @@ static void pstr(const char *s) } } } - pc('"'); + pc(minify ? '\'' : '"'); } static void pregexp(const char *prog, int flags) @@ -263,7 +291,9 @@ static void pregexp(const char *prog, int flags) static void pbin(int d, int p, js_Ast *exp, const char *op) { pexpi(d, p, exp->a); + sp(); ps(op); + sp(); pexpi(d, p, exp->b); } @@ -316,49 +346,58 @@ static void pexpi(int d, int p, js_Ast *exp) case EXP_BITNOT: puna(d, p, exp, "~", ""); break; case EXP_LOGNOT: puna(d, p, exp, "!", ""); break; - case EXP_LOGOR: pbin(d, p, exp, " || "); break; - case EXP_LOGAND: pbin(d, p, exp, " && "); break; - case EXP_BITOR: pbin(d, p, exp, " | "); break; - case EXP_BITXOR: pbin(d, p, exp, " ^ "); break; - case EXP_BITAND: pbin(d, p, exp, " & "); break; - case EXP_EQ: pbin(d, p, exp, " == "); break; - case EXP_NE: pbin(d, p, exp, " != "); break; - case EXP_STRICTEQ: pbin(d, p, exp, " === "); break; - case EXP_STRICTNE: pbin(d, p, exp, " !== "); break; - case EXP_LT: pbin(d, p, exp, " < "); break; - case EXP_GT: pbin(d, p, exp, " > "); break; - case EXP_LE: pbin(d, p, exp, " <= "); break; - case EXP_GE: pbin(d, p, exp, " >= "); break; - case EXP_INSTANCEOF: pbin(d, p, exp, " instanceof "); break; - case EXP_IN: pbin(d, p, exp, " in "); break; - case EXP_SHL: pbin(d, p, exp, " << "); break; - case EXP_SHR: pbin(d, p, exp, " >> "); break; - case EXP_USHR: pbin(d, p, exp, " >>> "); break; - case EXP_ADD: pbin(d, p, exp, " + "); break; - case EXP_SUB: pbin(d, p, exp, " - "); break; - case EXP_MUL: pbin(d, p, exp, " * "); break; - case EXP_DIV: pbin(d, p, exp, " / "); break; - case EXP_MOD: pbin(d, p, exp, " % "); break; - case EXP_ASS: pbin(d, p, exp, " = "); break; - case EXP_ASS_MUL: pbin(d, p, exp, " *= "); break; - case EXP_ASS_DIV: pbin(d, p, exp, " /= "); break; - case EXP_ASS_MOD: pbin(d, p, exp, " %= "); break; - case EXP_ASS_ADD: pbin(d, p, exp, " += "); break; - case EXP_ASS_SUB: pbin(d, p, exp, " -= "); break; - case EXP_ASS_SHL: pbin(d, p, exp, " <<= "); break; - case EXP_ASS_SHR: pbin(d, p, exp, " >>= "); break; - case EXP_ASS_USHR: pbin(d, p, exp, " >>>= "); break; - case EXP_ASS_BITAND: pbin(d, p, exp, " &= "); break; - case EXP_ASS_BITXOR: pbin(d, p, exp, " ^= "); break; - case EXP_ASS_BITOR: pbin(d, p, exp, " |= "); break; + case EXP_LOGOR: pbin(d, p, exp, "||"); break; + case EXP_LOGAND: pbin(d, p, exp, "&&"); break; + case EXP_BITOR: pbin(d, p, exp, "|"); break; + case EXP_BITXOR: pbin(d, p, exp, "^"); break; + case EXP_BITAND: pbin(d, p, exp, "&"); break; + case EXP_EQ: pbin(d, p, exp, "=="); break; + case EXP_NE: pbin(d, p, exp, "!="); break; + case EXP_STRICTEQ: pbin(d, p, exp, "==="); break; + case EXP_STRICTNE: pbin(d, p, exp, "!=="); break; + case EXP_LT: pbin(d, p, exp, "<"); break; + case EXP_GT: pbin(d, p, exp, ">"); break; + case EXP_LE: pbin(d, p, exp, "<="); break; + case EXP_GE: pbin(d, p, exp, ">="); break; + case EXP_IN: pbin(d, p, exp, "in"); break; + case EXP_SHL: pbin(d, p, exp, "<<"); break; + case EXP_SHR: pbin(d, p, exp, ">>"); break; + case EXP_USHR: pbin(d, p, exp, ">>>"); break; + case EXP_ADD: pbin(d, p, exp, "+"); break; + case EXP_SUB: pbin(d, p, exp, "-"); break; + case EXP_MUL: pbin(d, p, exp, "*"); break; + case EXP_DIV: pbin(d, p, exp, "/"); break; + case EXP_MOD: pbin(d, p, exp, "%"); break; + case EXP_ASS: pbin(d, p, exp, "="); break; + case EXP_ASS_MUL: pbin(d, p, exp, "*="); break; + case EXP_ASS_DIV: pbin(d, p, exp, "/="); break; + case EXP_ASS_MOD: pbin(d, p, exp, "%="); break; + case EXP_ASS_ADD: pbin(d, p, exp, "+="); break; + case EXP_ASS_SUB: pbin(d, p, exp, "-="); break; + case EXP_ASS_SHL: pbin(d, p, exp, "<<="); break; + case EXP_ASS_SHR: pbin(d, p, exp, ">>="); break; + case EXP_ASS_USHR: pbin(d, p, exp, ">>>="); break; + case EXP_ASS_BITAND: pbin(d, p, exp, "&="); break; + case EXP_ASS_BITXOR: pbin(d, p, exp, "^="); break; + case EXP_ASS_BITOR: pbin(d, p, exp, "|="); break; - case EXP_COMMA: pbin(d, p, exp, ", "); break; + case EXP_INSTANCEOF: + pexpi(d, p, exp->a); + ps(" instanceof "); + pexpi(d, p, exp->b); + break; + + case EXP_COMMA: + pexpi(d, p, exp->a); + pc(','); sp(); + pexpi(d, p, exp->b); + break; case EXP_COND: pexpi(d, p, exp->a); - ps(" ? "); + sp(); pc('?'); sp(); pexpi(d, p, exp->b); - ps(" : "); + sp(); pc(':'); sp(); pexpi(d, p, exp->c); break; @@ -396,7 +435,7 @@ static void pexpi(int d, int p, js_Ast *exp) pexpi(d, 0, exp->a); pc('('); pargs(d, exp->b); - ps(") {\n"); + pc(')'); sp(); pc('{'); nl(); pstmlist(d, exp->c); in(d); pc('}'); if (p == 0) pc(')'); @@ -420,7 +459,7 @@ static void pvar(int d, js_Ast *var) assert(var->type == EXP_VAR); pexp(d, var->a); if (var->b) { - ps(" = "); + sp(); pc('='); sp(); pexp(d, var->b); } } @@ -432,23 +471,24 @@ static void pvarlist(int d, js_Ast *list) pvar(d, list->a); list = list->b; if (list) - ps(", "); + comma(); } } static void pblock(int d, js_Ast *block) { assert(block->type == STM_BLOCK); - ps(" {\n"); + pc('{'); nl(); pstmlist(d, block->a); in(d); pc('}'); } static void pstmh(int d, js_Ast *stm) { - if (stm->type == STM_BLOCK) + if (stm->type == STM_BLOCK) { + sp(); pblock(d, stm); - else { + } else { nl(); pstm(d+1, stm); } @@ -459,11 +499,11 @@ static void pcaselist(int d, js_Ast *list) while (list) { js_Ast *stm = list->a; if (stm->type == STM_CASE) { - in(d); ps("case "); pexp(d, stm->a); ps(":\n"); + in(d); ps("case "); pexp(d, stm->a); pc(':'); nl(); pstmlist(d, stm->b); } if (stm->type == STM_DEFAULT) { - in(d); ps("default:\n"); + in(d); ps("default:"); nl(); pstmlist(d, stm->a); } list = list->b; @@ -485,9 +525,9 @@ static void pstm(int d, js_Ast *stm) pexp(d, stm->a); pc('('); pargs(d, stm->b); - ps(") {\n"); + pc(')'); sp(); pc('{'); nl(); pstmlist(d, stm->c); - in(d); ps("}"); + in(d); pc('}'); break; case STM_EMPTY: @@ -497,11 +537,11 @@ static void pstm(int d, js_Ast *stm) case STM_VAR: ps("var "); pvarlist(d, stm->a); - ps(";"); + pc(';'); break; case STM_IF: - ps("if ("); pexp(d, stm->a); ps(")"); + ps("if"); sp(); pc('('); pexp(d, stm->a); pc(')'); pstmh(d, stm->b); if (stm->c) { nl(); in(d); ps("else"); @@ -513,87 +553,89 @@ static void pstm(int d, js_Ast *stm) ps("do"); pstmh(d, stm->a); nl(); - in(d); ps("while ("); pexp(d, stm->b); ps(");"); + in(d); ps("while"); sp(); pc('('); pexp(d, stm->b); pc(')'); pc(';'); break; case STM_WHILE: - ps("while ("); pexp(d, stm->a); ps(")"); + ps("while"); sp(); pc('('); pexp(d, stm->a); pc(')'); pstmh(d, stm->b); break; case STM_FOR: - ps("for ("); - pexp(d, stm->a); ps("; "); - pexp(d, stm->b); ps("; "); - pexp(d, stm->c); ps(")"); + ps("for"); sp(); pc('('); + pexp(d, stm->a); pc(';'); sp(); + pexp(d, stm->b); pc(';'); sp(); + pexp(d, stm->c); pc(')'); pstmh(d, stm->d); break; case STM_FOR_VAR: - ps("for (var "); - pvarlist(d, stm->a); ps("; "); - pexp(d, stm->b); ps("; "); - pexp(d, stm->c); ps(")"); + ps("for"); sp(); ps("(var "); + pvarlist(d, stm->a); pc(';'); sp(); + pexp(d, stm->b); pc(';'); sp(); + pexp(d, stm->c); pc(')'); pstmh(d, stm->d); break; case STM_FOR_IN: - ps("for ("); + ps("for"); sp(); pc('('); pexp(d, stm->a); ps(" in "); - pexp(d, stm->b); ps(")"); + pexp(d, stm->b); pc(')'); pstmh(d, stm->c); break; case STM_FOR_IN_VAR: - ps("for (var "); + ps("for"); sp(); ps("(var "); pvarlist(d, stm->a); ps(" in "); - pexp(d, stm->b); ps(")"); + pexp(d, stm->b); pc(')'); pstmh(d, stm->c); break; case STM_CONTINUE: + ps("continue"); if (stm->a) { - ps("continue "); pexp(d, stm->a); ps(";"); - } else { - ps("continue;"); + pc(' '); pexp(d, stm->a); } + pc(';'); break; case STM_BREAK: + ps("break"); if (stm->a) { - ps("break "); pexp(d, stm->a); ps(";"); - } else { - ps("break;"); + pc(' '); pexp(d, stm->a); } + pc(';'); break; case STM_RETURN: + ps("return"); if (stm->a) { - ps("return "); pexp(d, stm->a); ps(";"); - } else { - ps("return;"); + pc(' '); pexp(d, stm->a); } + pc(';'); break; case STM_WITH: - ps("with ("); pexp(d, stm->a); ps(")"); + ps("with"); sp(); pc('('); pexp(d, stm->a); pc(')'); pstmh(d, stm->b); break; case STM_SWITCH: - ps("switch ("); + ps("switch"); sp(); pc('('); pexp(d, stm->a); - ps(") {\n"); + pc(')'); sp(); pc('{'); nl(); pcaselist(d, stm->b); - in(d); ps("}"); + in(d); pc('}'); break; case STM_THROW: - ps("throw "); pexp(d, stm->a); ps(";"); + ps("throw "); pexp(d, stm->a); pc(';'); break; case STM_TRY: ps("try"); + if (minify && stm->a->type != STM_BLOCK) + pc(' '); pstmh(d, stm->a); if (stm->b && stm->c) { - nl(); in(d); ps("catch ("); pexp(d, stm->b); ps(")"); + nl(); in(d); ps("catch"); sp(); pc('('); pexp(d, stm->b); pc(')'); pstmh(d, stm->c); } if (stm->d) { @@ -603,15 +645,17 @@ static void pstm(int d, js_Ast *stm) break; case STM_LABEL: - pexp(d, stm->a); ps(": "); pstm(d, stm->b); + pexp(d, stm->a); pc(':'); sp(); pstm(d, stm->b); break; case STM_DEBUGGER: - ps("debugger;"); + ps("debugger"); + pc(';'); break; default: - pexp(d, stm); pc(';'); + pexp(d, stm); + pc(';'); } } @@ -625,14 +669,17 @@ static void pstmlist(int d, js_Ast *list) } } -void jsP_dumpsyntax(js_State *J, js_Ast *prog) +void jsP_dumpsyntax(js_State *J, js_Ast *prog, int dominify) { + minify = dominify; if (prog->type == AST_LIST) pstmlist(-1, prog); else { pstm(0, prog); nl(); } + if (minify > 1) + putchar('\n'); } /* S-expression list representation */ @@ -708,6 +755,7 @@ static void sblock(int d, js_Ast *list) void jsP_dumplist(js_State *J, js_Ast *prog) { + minify = 0; if (prog->type == AST_LIST) sblock(0, prog); else @@ -723,6 +771,8 @@ void jsC_dumpfunction(js_State *J, js_Function *F) js_Instruction *end = F->code + F->codelen; int i; + minify = 0; + printf("%s(%d)\n", F->name, F->numparams); if (F->lightweight) printf("\tlightweight\n"); if (F->arguments) printf("\targuments\n"); @@ -802,6 +852,7 @@ void jsC_dumpfunction(js_State *J, js_Function *F) void js_dumpvalue(js_State *J, js_Value v) { + minify = 0; switch (v.type) { case JS_TUNDEFINED: printf("undefined"); break; case JS_TNULL: printf("null"); break; @@ -843,6 +894,7 @@ void js_dumpvalue(js_State *J, js_Value v) static void js_dumpproperty(js_State *J, js_Property *node) { + minify = 0; if (node->left->level) js_dumpproperty(J, node->left); printf("\t%s: ", node->name); @@ -854,6 +906,7 @@ static void js_dumpproperty(js_State *J, js_Property *node) void js_dumpobject(js_State *J, js_Object *obj) { + minify = 0; printf("{\n"); if (obj->properties->level) js_dumpproperty(J, obj->properties); diff --git a/jsparse.h b/jsparse.h index 4187c34..f5df25a 100644 --- a/jsparse.h +++ b/jsparse.h @@ -140,7 +140,7 @@ js_Ast *jsP_parse(js_State *J, const char *filename, const char *source); void jsP_freeparse(js_State *J); const char *jsP_aststring(enum js_AstType type); -void jsP_dumpsyntax(js_State *J, js_Ast *prog); +void jsP_dumpsyntax(js_State *J, js_Ast *prog, int minify); void jsP_dumplist(js_State *J, js_Ast *prog); #endif diff --git a/pp.c b/pp.c new file mode 100644 index 0000000..8d13afb --- /dev/null +++ b/pp.c @@ -0,0 +1,115 @@ +/* Pretty-print input source by emitting parse tree back as syntax. + * with no flags: pretty-printed source + * with -m: minified source with line breaks + * with -mm: minified source without line breaks + * with -s: s-expression syntax tree + */ + +#include + +#include "jsi.h" +#include "jsparse.h" + +static void js_ppstring(js_State *J, const char *filename, const char *source, int minify) +{ + js_Ast *P; + if (js_try(J)) { + jsP_freeparse(J); + js_throw(J); + } + P = jsP_parse(J, filename, source); + if (minify > 2) + jsP_dumplist(J, P); + else + jsP_dumpsyntax(J, P, minify); + jsP_freeparse(J); + js_endtry(J); +} + +void js_ppfile(js_State *J, const char *filename, int minify) +{ + FILE *f; + char *s; + int n, t; + + f = fopen(filename, "rb"); + if (!f) { + js_error(J, "cannot open file: '%s'", filename); + } + + if (fseek(f, 0, SEEK_END) < 0) { + fclose(f); + js_error(J, "cannot seek in file: '%s'", filename); + } + + n = ftell(f); + if (n < 0) { + fclose(f); + js_error(J, "cannot tell in file: '%s'", filename); + } + + if (fseek(f, 0, SEEK_SET) < 0) { + fclose(f); + js_error(J, "cannot seek in file: '%s'", filename); + } + + s = js_malloc(J, n + 1); /* add space for string terminator */ + if (!s) { + fclose(f); + js_error(J, "cannot allocate storage for file contents: '%s'", filename); + } + + t = fread(s, 1, (size_t)n, f); + if (t != n) { + js_free(J, s); + fclose(f); + js_error(J, "cannot read data from file: '%s'", filename); + } + + s[n] = 0; /* zero-terminate string containing file data */ + + if (js_try(J)) { + js_free(J, s); + fclose(f); + js_throw(J); + } + + js_ppstring(J, filename, s, minify); + + js_free(J, s); + fclose(f); + js_endtry(J); +} + +int +main(int argc, char **argv) +{ + js_State *J; + int minify = 0; + int i; + + J = js_newstate(NULL, NULL, 0); + + for (i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-m")) + minify = 1; + else if (!strcmp(argv[i], "-mm")) + minify = 2; + else if (!strcmp(argv[i], "-s")) + minify = 3; + else { + if (js_try(J)) { + js_report(J, js_trystring(J, -1, "Error")); + js_pop(J, 1); + continue; + } + js_ppfile(J, argv[i], minify); + js_endtry(J); + } + } + + js_gc(J, 0); + js_freestate(J); + + return 0; +}