diff --git a/3p/WL.c b/3p/WL.c index 83dc75b..54dda36 100644 --- a/3p/WL.c +++ b/3p/WL.c @@ -34,6 +34,7 @@ typedef struct { #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #define SIZEOF(X) (int) sizeof(X) #define ALIGNOF(X) (int) _Alignof(X) +#define COUNT(X) (int) (sizeof(X)/sizeof((X)[0])) #ifndef NDEBUG #define UNREACHABLE __builtin_trap() @@ -68,14 +69,12 @@ static bool is_hex_digit(char c) return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); } -#if 0 static char to_lower(char c) { if (c >= 'A' && c <= 'Z') return c - 'A' + 'a'; return c; } -#endif static int hex_digit_to_int(char c) { @@ -98,7 +97,6 @@ static bool streq(String a, String b) return true; } -#if 0 static bool streqcase(String a, String b) { if (a.len != b.len) @@ -108,7 +106,7 @@ static bool streqcase(String a, String b) return false; return true; } -#endif + #define REPORT(err, fmt, ...) report((err), __FILE__, __LINE__, fmt, ## __VA_ARGS__) static void report(Error *err, char *file, int line, char *fmt, ...) @@ -161,7 +159,6 @@ static bool grow_alloc(WL_Arena *a, char *p, int new_len) return true; } -#if 0 static String copystr(String s, WL_Arena *a) { char *p = alloc(a, s.len, 1); @@ -170,7 +167,6 @@ static String copystr(String s, WL_Arena *a) memcpy(p, s.ptr, s.len); return (String) { p, s.len }; } -#endif ///////////////////////////////////////////////////////////////////////// // WRITER @@ -193,14 +189,14 @@ static void write_raw_mem(Writer *w, void *ptr, int len) } static void write_raw_u8 (Writer *w, uint8_t x) { write_raw_mem(w, &x, SIZEOF(x)); } -//static void write_raw_u16(Writer *w, uint16_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_u16(Writer *w, uint16_t x) { write_raw_mem(w, &x, SIZEOF(x)); } static void write_raw_u32(Writer *w, uint32_t x) { write_raw_mem(w, &x, SIZEOF(x)); } -//static void write_raw_u64(Writer *w, uint64_t x) { write_raw_mem(w, &x, SIZEOF(x)); } -//static void write_raw_s8 (Writer *w, int8_t x) { write_raw_mem(w, &x, SIZEOF(x)); } -//static void write_raw_s16(Writer *w, int16_t x) { write_raw_mem(w, &x, SIZEOF(x)); } -//static void write_raw_s32(Writer *w, int32_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_u64(Writer *w, uint64_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_s8 (Writer *w, int8_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_s16(Writer *w, int16_t x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_s32(Writer *w, int32_t x) { write_raw_mem(w, &x, SIZEOF(x)); } static void write_raw_s64(Writer *w, int64_t x) { write_raw_mem(w, &x, SIZEOF(x)); } -//static void write_raw_f32(Writer *w, float x) { write_raw_mem(w, &x, SIZEOF(x)); } +static void write_raw_f32(Writer *w, float x) { write_raw_mem(w, &x, SIZEOF(x)); } static void write_raw_f64(Writer *w, double x) { write_raw_mem(w, &x, SIZEOF(x)); } static void write_text(Writer *w, String str) @@ -265,6 +261,7 @@ typedef enum { TOKEN_KWORD_FALSE, TOKEN_KWORD_INCLUDE, TOKEN_KWORD_LEN, + TOKEN_KWORD_ESCAPE, TOKEN_VALUE_FLOAT, TOKEN_VALUE_INT, TOKEN_VALUE_STR, @@ -312,6 +309,7 @@ typedef enum { NODE_INCLUDE, NODE_SELECT, NODE_NESTED, + NODE_OPER_ESCAPE, NODE_OPER_LEN, NODE_OPER_POS, NODE_OPER_NEG, @@ -415,7 +413,6 @@ static bool consume_str(Scanner *s, String x) return true; } -#if 0 static void write_token(Writer *w, Token token) { switch (token.type) { @@ -436,6 +433,7 @@ static void write_token(Writer *w, Token token) case TOKEN_KWORD_FALSE : write_text(w, S("false")); break; case TOKEN_KWORD_INCLUDE: write_text(w, S("include")); break; case TOKEN_KWORD_LEN : write_text(w, S("len")); break; + case TOKEN_KWORD_ESCAPE: write_text(w, S("escape")); break; case TOKEN_VALUE_FLOAT : write_text_f64(w, token.fval); break; case TOKEN_VALUE_INT : write_text_s64(w, token.ival); break; case TOKEN_OPER_ASS : write_text(w, S("=")); break; @@ -469,7 +467,6 @@ static void write_token(Writer *w, Token token) } } -#endif static void parser_report(Parser *p, char *fmt, ...) { @@ -552,6 +549,7 @@ static Token next_token(Parser *p) if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE }; if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE }; if (streq(kword, S("len"))) return (Token) { .type=TOKEN_KWORD_LEN }; + if (streq(kword, S("escape"))) return (Token) { .type=TOKEN_KWORD_ESCAPE }; return (Token) { .type=TOKEN_IDENT, .sval=kword }; } @@ -778,8 +776,7 @@ static Node *parse_html(Parser *p) } if (s->cur == s->len) { - parser_report(p, "Invaid end of source inside HTML tag"); - return NULL; + ASSERT(0); // TODO } s->cur++; @@ -790,8 +787,7 @@ static Node *parse_html(Parser *p) while (s->cur < s->len && is_space(s->src[s->cur])) s->cur++; if (s->cur == s->len || s->src[s->cur] != '>') { - parser_report(p, "Invalid character inside HTML closing tag"); - return NULL; + ASSERT(0); // TODO } s->cur++; no_body = true; @@ -835,8 +831,7 @@ static Node *parse_html(Parser *p) } if (s->cur == s->len) { - parser_report(p, "Missing closing HTML tag ", tagname.len, tagname.ptr); - return NULL; + ASSERT(0); // TODO } s->cur++; @@ -848,20 +843,17 @@ static Node *parse_html(Parser *p) t = next_token(p); if (t.type != TOKEN_IDENT) { - parser_report(p, "Missing tag name after '' in closing HTML tag"); - return NULL; + ASSERT(0); } break; @@ -1134,6 +1126,23 @@ static Node *parse_atom(Parser *p) } break; + case TOKEN_KWORD_ESCAPE: + { + Node *child = parse_atom(p); + if (child == NULL) + return NULL; + + Node *parent = alloc_node(p); + if (parent == NULL) + return NULL; + + parent->type = NODE_OPER_ESCAPE; + parent->left = child; + + ret = parent; + } + break; + case TOKEN_IDENT: { Node *node = alloc_node(p); @@ -1910,6 +1919,12 @@ static void write_node(Writer *w, Node *node) write_text(w, S(")")); break; + case NODE_OPER_ESCAPE: + write_text(w, S("(escape ")); + write_node(w, node->left); + write_text(w, S(")")); + break; + case NODE_OPER_POS: write_text(w, S("(+")); write_node(w, node->left); @@ -2003,14 +2018,6 @@ static void write_node(Writer *w, Node *node) write_text(w, S(")")); break; - case NODE_OPER_SHOVEL: - write_text(w, S("(")); - write_node(w, node->left); - write_text(w, S("<<")); - write_node(w, node->right); - write_text(w, S(")")); - break; - case NODE_VALUE_INT: write_text_s64(w, node->ival); break; @@ -2206,6 +2213,7 @@ enum { OPCODE_CALL, OPCODE_RET, OPCODE_GROUP, + OPCODE_ESCAPE, OPCODE_PACK, OPCODE_GPOP, OPCODE_FOR, @@ -2516,8 +2524,6 @@ static void cg_pop_scope(Codegen *cg) ASSERT(cg->num_scopes > 0); Scope *scope = &cg->scopes[cg->num_scopes-1]; - ASSERT(scope->type == SCOPE_PROC || scope->type == SCOPE_GLOBAL || scope->max_vars == 0); - Scope *parent_scope = NULL; if (cg->num_scopes > 1) parent_scope = &cg->scopes[cg->num_scopes-2]; @@ -2552,6 +2558,9 @@ static void cg_pop_scope(Codegen *cg) call->next = cg->free_list_calls; cg->free_list_calls = call; + + // TODO: remove + ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS)); } cg->num_syms = scope->idx_syms; @@ -2620,10 +2629,13 @@ static void cg_write_pushs(Codegen *cg, String str, bool dont_group) } } -static void walk_node(Codegen *cg, Node *node); +static void walk_node(Codegen *cg, Node *node, bool inside_html); static void walk_expr_node(Codegen *cg, Node *node, bool one) { + // TODO: remove + ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS)); + switch (node->type) { case NODE_NESTED: @@ -2635,6 +2647,12 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one) cg_write_opcode(cg, OPCODE_LEN); break; + case NODE_OPER_ESCAPE: + cg_write_opcode(cg, OPCODE_GROUP); + walk_expr_node(cg, node->left, false); + cg_write_opcode(cg, OPCODE_ESCAPE); + break; + case NODE_OPER_POS: walk_expr_node(cg, node->left, one); break; @@ -2699,6 +2717,9 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one) case NODE_OPER_SHOVEL: { + Node *dst = node->left; + Node *src = node->right; + walk_expr_node(cg, node->left, true); cg_push_scope(cg, SCOPE_ASSIGNMENT); @@ -2824,7 +2845,7 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one) Node *child = node->html_attr; while (child) { - walk_node(cg, child); + walk_node(cg, child, true); child = child->next; } @@ -2834,7 +2855,7 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one) cg_write_pushs(cg, S(">"), false); Node *child = node->html_child; while (child) { - walk_node(cg, child); + walk_node(cg, child, true); child = child->next; } cg_write_pushs(cg, S("scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS)); + switch (node->type) { case NODE_GLOBAL: for (Node *child = node->left; child; child = child->next) { - walk_node(cg, child); + walk_node(cg, child, false); } break; @@ -2940,7 +2964,7 @@ static void walk_node(Codegen *cg, Node *node) cg_push_scope(cg, SCOPE_COMPOUND); for (Node *child = node->left; child; child = child->next) - walk_node(cg, child); + walk_node(cg, child, inside_html); cg_pop_scope(cg); break; @@ -2972,10 +2996,10 @@ static void walk_node(Codegen *cg, Node *node) int off1 = cg_write_opcode(cg, OPCODE_VARS); int off2 = cg_write_u8(cg, 0); - walk_node(cg, node->proc_body); + walk_node(cg, node->proc_body, false); cg_write_opcode(cg, OPCODE_RET); - cg_patch_u8 (cg, off2, cg->scopes[cg->num_scopes-1].max_vars); + cg_patch_u8 (cg, off2, count_function_vars(cg)); cg_patch_u32(cg, off0, cg_current_offset(cg)); cg_pop_scope(cg); @@ -3028,7 +3052,7 @@ static void walk_node(Codegen *cg, Node *node) int p1 = cg_write_u32(cg, 0); cg_push_scope(cg, SCOPE_IF); - walk_node(cg, node->if_branch1); + walk_node(cg, node->if_branch1, inside_html); cg_pop_scope(cg); cg_write_opcode(cg, OPCODE_JUMP); @@ -3038,7 +3062,7 @@ static void walk_node(Codegen *cg, Node *node) cg_patch_u32(cg, p1, cg_current_offset(cg)); cg_push_scope(cg, SCOPE_ELSE); - walk_node(cg, node->if_branch2); + walk_node(cg, node->if_branch2, inside_html); cg_pop_scope(cg); cg_flush_pushs(cg); @@ -3052,7 +3076,7 @@ static void walk_node(Codegen *cg, Node *node) int p1 = cg_write_u32(cg, 0); cg_push_scope(cg, SCOPE_IF); - walk_node(cg, node->if_branch1); + walk_node(cg, node->if_branch1, inside_html); cg_pop_scope(cg); cg_flush_pushs(cg); @@ -3086,7 +3110,7 @@ static void walk_node(Codegen *cg, Node *node) cg_write_u8(cg, var_2); int p = cg_write_u32(cg, 0); - walk_node(cg, node->left); + walk_node(cg, node->left, inside_html); cg_write_opcode(cg, OPCODE_JUMP); cg_write_u32(cg, start); @@ -3115,7 +3139,7 @@ static void walk_node(Codegen *cg, Node *node) int p = cg_write_u32(cg, 0); cg_push_scope(cg, SCOPE_WHILE); - walk_node(cg, node->left); + walk_node(cg, node->left, inside_html); cg_pop_scope(cg); cg_write_opcode(cg, OPCODE_JUMP); @@ -3126,12 +3150,12 @@ static void walk_node(Codegen *cg, Node *node) break; case NODE_INCLUDE: - walk_node(cg, node->include_root); + walk_node(cg, node->include_root, false); break; default: walk_expr_node(cg, node, false); - if (cg_global_scope(cg) && !inside_assignment(cg)) + if (cg_global_scope(cg) && !inside_assignment(cg) && !inside_html) cg_write_opcode(cg, OPCODE_OUTPUT); break; } @@ -3168,7 +3192,7 @@ static int codegen(Node *node, char *dst, int cap, char *errmsg, int errcap) cg_push_scope(&cg, SCOPE_GLOBAL); cg_write_opcode(&cg, OPCODE_VARS); int off = cg_write_u8(&cg, 0); - walk_node(&cg, node); + walk_node(&cg, node, false); cg_write_opcode(&cg, OPCODE_EXIT); cg_patch_u8(&cg, off, cg.scopes[0].max_vars); cg_pop_scope(&cg); @@ -3271,6 +3295,10 @@ static int write_instr(Writer *w, char *src, int len, String data) write_text(w, S("GROUP\n")); return 1; + case OPCODE_ESCAPE: + write_text(w, S("ESCAPE\n")); + return 1; + case OPCODE_PACK: write_text(w, S("PACK\n")); return 1; @@ -3452,7 +3480,7 @@ static int write_instr(Writer *w, char *src, int len, String data) static int write_program(WL_Program program, char *dst, int cap) { - if ((uint32_t) program.len < 3 * sizeof(uint32_t)) + if (program.len < 3 * sizeof(uint32_t)) return -1; uint32_t magic; @@ -3466,7 +3494,7 @@ static int write_program(WL_Program program, char *dst, int cap) if (magic != WL_MAGIC) return -1; - if (code_len + data_len + 3 * sizeof(uint32_t) != (uint32_t) program.len) + if (code_len + data_len + 3 * sizeof(uint32_t) != program.len) return -1; String code = { program.ptr + 3 * sizeof(uint32_t) , code_len }; @@ -3583,9 +3611,7 @@ WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String content) if (include->include_root == NULL) { if (compiler->num_files == FILE_LIMIT) { - snprintf(compiler->err, sizeof(compiler->err), "File limit reached"); - compiler->err = true; - return (WL_AddResult) { .type=WL_ADD_ERROR }; + ASSERT(0); // TODO } // TODO: Make the path relative to the compiled file @@ -4449,29 +4475,7 @@ static void value_convert_to_str_inner(Writer *w, Value v) break; case TYPE_MAP: - { - write_text(w, S("{")); - AggregateValue *agg = (void*) (v & ~(Value) 7); - for (int i = 0; i < agg->count; i += 2) { - value_convert_to_str_inner(w, agg->vals[i+0]); - write_text(w, S(": ")); - value_convert_to_str_inner(w, agg->vals[i+1]); - if (i+2 < agg->count || agg->ext) - write_text(w, S(", ")); - } - Extension *ext = agg->ext; - while (ext) { - for (int i = 0; i < ext->count; i += 2) { - value_convert_to_str_inner(w, ext->vals[i+0]); - write_text(w, S(": ")); - value_convert_to_str_inner(w, ext->vals[i+1]); - if (i+2 < ext->count || ext->next) - write_text(w, S(", ")); - } - ext = ext->next; - } - write_text(w, S("}")); - } + write_text(w, S("")); break; case TYPE_ERROR: @@ -4486,6 +4490,140 @@ static int value_convert_to_str(Value v, char *dst, int cap) return w.len; } +static Value value_escape_packed(Value v, WL_Arena *arena, Error *err); + +static int array_escape(Value v, Value *out, int max, WL_Arena *arena, Error *err) +{ + Value v2 = value_empty_array(value_length(v), arena, err); + if (v2 == VALUE_ERROR) return -1; + + AggregateValue *src = (void*) (v & ~(Value) 7); + + for (int i = 0; i < src->count; i++) { + + Value child = src->vals[i]; + + Value escaped_child = value_escape_packed(child, arena, err); + if (escaped_child == VALUE_ERROR) + return -1; + + if (!value_append(v2, escaped_child, arena, err)) + return -1; + } + Extension *ext = src->ext; + while (ext) { + for (int i = 0; i < ext->count; i++) { + + Value child = src->vals[i]; + + Value escaped_child = value_escape_packed(child, arena, err); + if (escaped_child == VALUE_ERROR) + return -1; + + if (!value_append(v2, escaped_child, arena, err)) + return -1; + } + ext = ext->next; + } + + if (max == 0) + return -1; + out[0] = v2; + return 1; +} + +static int string_escape(Value v, Value *out, int max, WL_Arena *arena, Error *err) +{ + String s = value_to_str(v); + + int i = 0; + int num = 0; + for (;;) { + + int off = i; + while (i < s.len + && s.ptr[i] != '<' + && s.ptr[i] != '>' + && s.ptr[i] != '&' + && s.ptr[i] != '"' + && s.ptr[i] != '\'') + i++; + String substr = { s.ptr + off, i - off }; + + Value escaped_v = value_from_str(substr, arena, err); // TODO: don't copy the string + if (escaped_v == VALUE_ERROR) return -1; + + if (num == max) { + REPORT(err, "Escape buffer limit reached"); + return -1; + } + out[num++] = escaped_v; + + if (i == s.len) break; + + switch (s.ptr[i++]) { + case '<' : escaped_v = value_from_str(S("<"), arena, err); break; // TODO: don't come these strings + case '>' : escaped_v = value_from_str(S(">"), arena, err); break; + case '&' : escaped_v = value_from_str(S("&"), arena, err); break; + case '"' : escaped_v = value_from_str(S("""), arena, err); break; + case '\'': escaped_v = value_from_str(S("'"), arena, err); break; + } + if (escaped_v == VALUE_ERROR) return -1; + + if (num == max) { + REPORT(err, "Escape buffer limit reached"); + return -1; + } + out[num++] = escaped_v; + } + + return num; +} + +static int value_escape(Value v, Value *out, int max, WL_Arena *arena, Error *err) +{ + Type t = value_type(v); + + if (t == TYPE_ARRAY) + return array_escape(v, out, max, arena, err); + + if (t == TYPE_STRING) + return string_escape(v, out, max, arena, err); + + if (max < 1) + return -1; + out[0] = v; + return 1; +} + +static Value value_escape_packed(Value v, WL_Arena *arena, Error *err) +{ + Value tmp[32]; + int num = value_escape(v, tmp, COUNT(tmp), arena, err); + if (num < 0) return VALUE_ERROR; + + Value escaped_v; + + if (num > 1) { + + Value packed = value_empty_array(num, arena, err); + if (packed == VALUE_ERROR) + return VALUE_ERROR; + + for (int j = 0; j < num; j++) + if (!value_append(packed, tmp[j], arena, err)) + return VALUE_ERROR; + escaped_v = packed; + + } else { + + ASSERT(num == 1); + escaped_v = tmp[0]; + } + + return escaped_v; +} + #undef TYPE_PAIR ///////////////////////////////////////////////////////////////////////// @@ -4543,7 +4681,7 @@ struct WL_Runtime { WL_Runtime *wl_runtime_init(WL_Arena *arena, WL_Program program) { - if ((uint32_t) program.len < 3 * sizeof(uint32_t)) + if (program.len < 3 * sizeof(uint32_t)) return NULL; uint32_t magic; @@ -4645,7 +4783,7 @@ static String rt_read_str(WL_Runtime *rt) ASSERT(rt->state == RUNTIME_LOOP); uint32_t off = rt_read_u32(rt); uint32_t len = rt_read_u32(rt); - ASSERT(off + len <= (uint32_t) rt->data.len); + ASSERT(off + len <= rt->data.len); return (String) { rt->data.ptr + off, len }; } @@ -4857,6 +4995,37 @@ static void step(WL_Runtime *rt) rt_push_group(rt); break; + case OPCODE_ESCAPE: + { + ASSERT(rt->num_groups > 0); + int start = rt->groups[--rt->num_groups]; + int end = rt->stack; + + Value escaped[256]; + int num_escaped = 0; + + for (int i = start; i < end; i++) { + Value v = rt->values[i]; + int num = value_escape(v, escaped + num_escaped, COUNT(escaped) - num_escaped, rt->arena, &rt->err); + if (num < 0) break; + num_escaped += num; + } + + if (num_escaped > COUNT(escaped)) { + REPORT(&rt->err, "Escape buffer limit reached"); + rt->state = RUNTIME_ERROR; + break; + } + + rt->stack = start; + if (!rt_check_stack(rt, num_escaped)) break; + + for (int i = 0; i < num_escaped; i++) + rt->values[rt->stack + i] = escaped[i]; + rt->stack += num_escaped; + } + break; + case OPCODE_PACK: rt_pack_group(rt); break; @@ -5156,7 +5325,6 @@ WL_EvalResult wl_runtime_eval(WL_Runtime *rt) switch (rt->state) { - case RUNTIME_BEGIN: case RUNTIME_LOOP: UNREACHABLE; diff --git a/pages/post.wl b/pages/post.wl index c55fcdc..367f333 100644 --- a/pages/post.wl +++ b/pages/post.wl @@ -179,13 +179,13 @@ let main =
- \post.title + \escape(post.title)
- submitted 3 hours ago by \post.username | \len comments + submitted 3 hours ago by \escape(post.username) | \len(comments)
-
\post.content
+
\escape(post.content)
@@ -209,10 +209,10 @@ let main =
- \comment.username 2 hours ago + \escape(comment.username) 2 hours ago
-
\comment.content
+
\escape(comment.content)
\if $login_user_id != none:
@@ -235,7 +235,7 @@ let main =
- \if len root_comments == 0: + \if len(root_comments) == 0:
No comments