Skip to content

Commit 809932a

Browse files
committed
Implement deserialize
1 parent 986bff5 commit 809932a

17 files changed

+514
-81
lines changed

ext/liquid_c/block.c

Lines changed: 147 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "variable.h"
88
#include "context.h"
99
#include "parse_context.h"
10+
#include "serialize_parse_context.h"
1011
#include "vm_assembler.h"
1112
#include "tag_markup.h"
1213
#include <stdio.h>
@@ -44,6 +45,9 @@ static void block_body_mark(void *ptr)
4445
if (body->compiled) {
4546
document_body_entry_mark(&body->as.compiled.document_body_entry);
4647
rb_gc_mark(body->as.compiled.nodelist);
48+
} else if (body->from_serialize) {
49+
document_body_entry_mark(&body->as.serialize.document_body_entry);
50+
rb_gc_mark(body->as.serialize.parse_context);
4751
} else {
4852
rb_gc_mark(body->as.intermediate.parse_context);
4953
if (body->as.intermediate.vm_assembler_pool)
@@ -56,7 +60,7 @@ static void block_body_mark(void *ptr)
5660
static void block_body_free(void *ptr)
5761
{
5862
block_body_t *body = ptr;
59-
if (!body->compiled) {
63+
if (!body->compiled && !body->from_serialize) {
6064
// Free the assembler instead of recycling it because the vm_assembler_pool may have been GC'd
6165
vm_assembler_pool_free_assembler(body->as.intermediate.code);
6266
}
@@ -67,7 +71,7 @@ static size_t block_body_memsize(const void *ptr)
6771
{
6872
const block_body_t *body = ptr;
6973
if (!ptr) return 0;
70-
if (body->compiled) {
74+
if (body->compiled || body->from_serialize) {
7175
return sizeof(block_body_t);
7276
} else {
7377
return sizeof(block_body_t) + vm_assembler_alloc_memsize(body->as.intermediate.code);
@@ -88,6 +92,7 @@ static VALUE block_body_allocate(VALUE klass)
8892
VALUE obj = TypedData_Make_Struct(klass, block_body_t, &block_body_data_type, body);
8993

9094
body->compiled = false;
95+
body->from_serialize = false;
9196
body->obj = obj;
9297
body->tags = c_buffer_init();
9398
body->as.intermediate.blank = true;
@@ -103,18 +108,24 @@ static VALUE block_body_initialize(VALUE self, VALUE parse_context)
103108
block_body_t *body;
104109
BlockBody_Get_Struct(self, body);
105110

106-
body->as.intermediate.parse_context = parse_context;
107-
108-
if (parse_context_document_body_initialized_p(parse_context)) {
109-
body->as.intermediate.vm_assembler_pool = parse_context_get_vm_assembler_pool(parse_context);
111+
if (is_serialize_parse_context_p(parse_context)) {
112+
body->from_serialize = true;
113+
body->as.serialize.document_body_entry = document_body_entry_init();
114+
body->as.serialize.parse_context = parse_context;
110115
} else {
111-
parse_context_init_document_body(parse_context);
112-
body->as.intermediate.root = true;
113-
body->as.intermediate.vm_assembler_pool = parse_context_init_vm_assembler_pool(parse_context);
114-
}
116+
body->as.intermediate.parse_context = parse_context;
117+
118+
if (parse_context_document_body_initialized_p(parse_context)) {
119+
body->as.intermediate.vm_assembler_pool = parse_context_get_vm_assembler_pool(parse_context);
120+
} else {
121+
parse_context_init_document_body(parse_context);
122+
body->as.intermediate.root = true;
123+
body->as.intermediate.vm_assembler_pool = parse_context_init_vm_assembler_pool(parse_context);
124+
}
115125

116-
body->as.intermediate.code = vm_assembler_pool_alloc_assembler(body->as.intermediate.vm_assembler_pool);
117-
vm_assembler_add_leave(body->as.intermediate.code);
126+
body->as.intermediate.code = vm_assembler_pool_alloc_assembler(body->as.intermediate.vm_assembler_pool);
127+
vm_assembler_add_leave(body->as.intermediate.code);
128+
}
118129

119130
return Qnil;
120131
}
@@ -138,6 +149,22 @@ static void block_body_push_tag_markup(block_body_t *body, VALUE parse_context,
138149
parse_context_set_parent_tag(parse_context, tag_markup);
139150
}
140151

152+
static void ensure_intermediate(block_body_t *body)
153+
{
154+
if (body->compiled) {
155+
rb_raise(rb_eRuntimeError, "Liquid::C::BlockBody is already compiled");
156+
}
157+
}
158+
159+
static void ensure_intermediate_not_parsing(block_body_t *body)
160+
{
161+
ensure_intermediate(body);
162+
163+
if (body->as.intermediate.code->parsing) {
164+
rb_raise(rb_eRuntimeError, "Liquid::C::BlockBody is in a incompletely parsed state");
165+
}
166+
}
167+
141168
static VALUE internal_block_body_parse(block_body_t *body, parse_context_t *parse_context)
142169
{
143170
tokenizer_t *tokenizer = parse_context->tokenizer;
@@ -257,7 +284,7 @@ static VALUE internal_block_body_parse(block_body_t *body, parse_context_t *pars
257284
}
258285

259286
VALUE tag_markup = tag_markup_new(tag_name, markup, false);
260-
block_body_push_tag_markup(body, parse_context->ruby_obj, tag_markup);
287+
parse_context_set_parent_tag(parse_context->ruby_obj, tag_markup);
261288

262289
VALUE new_tag = rb_funcall(tag_class, intern_parse, 4,
263290
tag_name, markup, parse_context->tokenizer_obj, parse_context->ruby_obj);
@@ -270,11 +297,12 @@ static VALUE internal_block_body_parse(block_body_t *body, parse_context_t *pars
270297
if (tokenizer->raw_tag_body) {
271298
if (tokenizer->raw_tag_body_len) {
272299
vm_assembler_add_write_raw(body->as.intermediate.code, tokenizer->raw_tag_body,
273-
tokenizer->raw_tag_body_len);
300+
tokenizer->raw_tag_body_len);
274301
}
275302
tokenizer->raw_tag_body = NULL;
276303
tokenizer->raw_tag_body_len = 0;
277304
} else {
305+
vm_assembler_write_tag(body->as.intermediate.code, tag_markup);
278306
block_body_add_node(body, new_tag);
279307
}
280308

@@ -290,32 +318,87 @@ static VALUE internal_block_body_parse(block_body_t *body, parse_context_t *pars
290318
return unknown_tag;
291319
}
292320

293-
static void ensure_intermediate(block_body_t *body)
321+
typedef struct block_body_yield_tag_args {
322+
block_body_t *body;
323+
serialize_parse_context_t *serialize_context;
324+
tag_markup_header_t *current_tag;
325+
} block_body_yield_tag_args_t;
326+
327+
static VALUE block_body_try_yield_tag(VALUE uncast_args)
294328
{
295-
if (body->compiled) {
296-
rb_raise(rb_eRuntimeError, "Liquid::C::BlockBody is already compiled");
297-
}
329+
block_body_yield_tag_args_t *args = (block_body_yield_tag_args_t *)uncast_args;
330+
tag_markup_header_t *current_tag = args->current_tag;
331+
332+
serialize_parse_context_enter_tag(args->serialize_context, current_tag);
333+
VALUE tag_name = rb_utf8_str_new(tag_markup_header_name(current_tag), current_tag->tag_name_len);
334+
VALUE markup = rb_utf8_str_new(tag_markup_header_markup(current_tag), current_tag->markup_len);
335+
return rb_yield_values(2, tag_name, markup);
298336
}
299337

300-
static void ensure_intermediate_not_parsing(block_body_t *body)
338+
static VALUE block_body_rescue_yield_tag(VALUE uncast_args, VALUE exception)
301339
{
340+
block_body_yield_tag_args_t *args = (block_body_yield_tag_args_t *)uncast_args;
341+
342+
serialize_parse_context_exit_tag(args->serialize_context, &args->body->as.serialize.document_body_entry,
343+
args->current_tag);
344+
rb_exc_raise(exception);
345+
}
346+
347+
static VALUE block_body_parse_from_serialize(block_body_t *body, VALUE tokenizer_obj, VALUE parse_context_obj)
348+
{
349+
assert(body->from_serialize);
350+
assert(is_serialize_parse_context_p(parse_context_obj));
351+
302352
ensure_intermediate(body);
353+
if (body->as.serialize.parse_context != parse_context_obj) {
354+
rb_raise(rb_eArgError, "Liquid::C::BlockBody#parse called with different parse context");
355+
}
303356

304-
if (body->as.intermediate.code->parsing) {
305-
rb_raise(rb_eRuntimeError, "Liquid::C::BlockBody is in a incompletely parsed state");
357+
serialize_parse_context_t *serialize_context;
358+
SerializeParseContext_Get_Struct(parse_context_obj, serialize_context);
359+
360+
body->as.serialize.document_body_entry = serialize_context->current_entry;
361+
362+
tag_markup_header_t *current_tag = serialize_context->current_tag;
363+
while (current_tag) {
364+
bool tag_unknown = TAG_UNKNOWN_P(current_tag);
365+
366+
if (tag_unknown) {
367+
block_body_yield_tag_args_t yield_args = {
368+
.body = body,
369+
.serialize_context = serialize_context,
370+
.current_tag = current_tag
371+
};
372+
return rb_rescue(block_body_try_yield_tag, (VALUE)&yield_args, block_body_rescue_yield_tag, (VALUE)&yield_args);
373+
} else {
374+
VALUE tag_name = rb_utf8_str_new(tag_markup_header_name(current_tag), current_tag->tag_name_len);
375+
VALUE markup = rb_utf8_str_new(tag_markup_header_markup(current_tag), current_tag->markup_len);
376+
377+
VALUE tag_class = rb_funcall(tag_registry, intern_square_brackets, 1, tag_name);
378+
assert(RTEST(tag_class));
379+
380+
serialize_parse_context_enter_tag(serialize_context, current_tag);
381+
VALUE new_tag = rb_funcall(tag_class, intern_parse, 4,
382+
tag_name, markup, tokenizer_obj, parse_context_obj);
383+
serialize_parse_context_exit_tag(serialize_context, &body->as.serialize.document_body_entry, current_tag);
384+
385+
c_buffer_write_ruby_value(&body->tags, new_tag);
386+
}
387+
388+
current_tag = tag_markup_get_next_tag(&body->as.serialize.document_body_entry, current_tag);
306389
}
390+
391+
return rb_yield_values(2, Qnil, Qnil);
307392
}
308393

309-
static VALUE block_body_parse(VALUE self, VALUE tokenizer_obj, VALUE parse_context_obj)
394+
static VALUE block_body_parse_from_source(VALUE self, block_body_t *body, VALUE tokenizer_obj, VALUE parse_context_obj)
310395
{
311396
parse_context_t parse_context = {
312397
.parent_tag = parse_context_get_parent_tag(parse_context_obj),
313398
.tokenizer_obj = tokenizer_obj,
314399
.ruby_obj = parse_context_obj,
315400
};
316401
Tokenizer_Get_Struct(tokenizer_obj, parse_context.tokenizer);
317-
block_body_t *body;
318-
BlockBody_Get_Struct(self, body);
319402

320403
ensure_intermediate_not_parsing(body);
321404
if (body->as.intermediate.parse_context != parse_context_obj) {
@@ -332,17 +415,29 @@ static VALUE block_body_parse(VALUE self, VALUE tokenizer_obj, VALUE parse_conte
332415
tag_name = tag_markup_get_tag_name(unknown_tag);
333416
markup = tag_markup_get_markup(unknown_tag);
334417
block_body_push_tag_markup(body, parse_context_obj, unknown_tag);
418+
419+
if (RTEST(parse_context.parent_tag)) {
420+
tag_markup_set_block_body(parse_context.parent_tag, self, body);
421+
}
335422
}
336423

337424
VALUE block_ret = rb_yield_values(2, tag_name, markup);
338425

339-
if (RTEST(parse_context.parent_tag)) {
340-
tag_markup_set_block_body(parse_context.parent_tag, self, body);
341-
}
342-
343426
return block_ret;
344427
}
345428

429+
static VALUE block_body_parse(VALUE self, VALUE tokenizer_obj, VALUE parse_context_obj)
430+
{
431+
block_body_t *body;
432+
BlockBody_Get_Struct(self, body);
433+
434+
if (body->from_serialize) {
435+
return block_body_parse_from_serialize(body, tokenizer_obj, parse_context_obj);
436+
} else {
437+
return block_body_parse_from_source(self, body, tokenizer_obj, parse_context_obj);
438+
}
439+
}
440+
346441

347442
static VALUE block_body_freeze(VALUE self)
348443
{
@@ -351,26 +446,32 @@ static VALUE block_body_freeze(VALUE self)
351446

352447
if (body->compiled) return Qnil;
353448

354-
VALUE parse_context = body->as.intermediate.parse_context;
355-
VALUE document_body = parse_context_get_document_body(parse_context);
356-
357-
bool root = body->as.intermediate.root;
358-
359-
vm_assembler_pool_t *assembler_pool = body->as.intermediate.vm_assembler_pool;
360-
vm_assembler_t *assembler = body->as.intermediate.code;
361-
bool blank = body->as.intermediate.blank;
362-
uint32_t render_score = body->as.intermediate.render_score;
363-
vm_assembler_t *code = body->as.intermediate.code;
364449
body->compiled = true;
365-
body->as.compiled.nodelist = Qundef;
366-
document_body_write_block_body(document_body, blank, render_score, code, &body->as.compiled.document_body_entry);
367-
vm_assembler_pool_recycle_assembler(assembler_pool, assembler);
368450

369-
if (root) {
370-
parse_context_remove_document_body(parse_context);
371-
parse_context_remove_vm_assembler_pool(parse_context);
451+
if (body->from_serialize) {
452+
body->as.compiled.nodelist = Qundef;
453+
} else {
454+
VALUE parse_context = body->as.intermediate.parse_context;
455+
VALUE document_body = parse_context_get_document_body(parse_context);
456+
457+
bool root = body->as.intermediate.root;
458+
459+
vm_assembler_pool_t *assembler_pool = body->as.intermediate.vm_assembler_pool;
460+
vm_assembler_t *assembler = body->as.intermediate.code;
461+
bool blank = body->as.intermediate.blank;
462+
uint32_t render_score = body->as.intermediate.render_score;
463+
vm_assembler_t *code = body->as.intermediate.code;
464+
body->as.compiled.nodelist = Qundef;
465+
document_body_write_block_body(document_body, blank, render_score, code, &body->as.compiled.document_body_entry);
466+
vm_assembler_pool_recycle_assembler(assembler_pool, assembler);
467+
468+
if (root) {
469+
parse_context_remove_document_body(parse_context);
470+
parse_context_remove_vm_assembler_pool(parse_context);
471+
}
372472
}
373473

474+
374475
rb_call_super(0, NULL);
375476

376477
return Qnil;
@@ -408,6 +509,8 @@ static VALUE block_body_remove_blank_strings(VALUE self)
408509
block_body_t *body;
409510
BlockBody_Get_Struct(self, body);
410511

512+
if (body->from_serialize) return Qnil;
513+
411514
ensure_intermediate_not_parsing(body);
412515

413516
if (!body->as.intermediate.blank) {

ext/liquid_c/block.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
typedef struct block_body {
88
bool compiled;
9+
bool from_serialize;
910
VALUE obj;
1011
c_buffer_t tags;
1112

@@ -14,6 +15,10 @@ typedef struct block_body {
1415
document_body_entry_t document_body_entry;
1516
VALUE nodelist;
1617
} compiled;
18+
struct {
19+
document_body_entry_t document_body_entry;
20+
VALUE parse_context;
21+
} serialize;
1722
struct {
1823
VALUE parse_context;
1924
vm_assembler_pool_t *vm_assembler_pool;

ext/liquid_c/document.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <ruby.h>
2+
#include "liquid.h"
3+
#include "document.h"
4+
#include "parse_context.h"
5+
#include "document_body.h"
6+
7+
static ID id_parse;
8+
static VALUE cLiquidDocument;
9+
10+
VALUE document_parse(VALUE tokenizer, VALUE parse_context)
11+
{
12+
return rb_funcall(cLiquidDocument, id_parse, 2, tokenizer, parse_context);
13+
}
14+
15+
void liquid_define_document()
16+
{
17+
id_parse = rb_intern("parse");
18+
19+
cLiquidDocument = rb_const_get(mLiquid, rb_intern("Document"));
20+
rb_global_variable(&cLiquidDocument);
21+
}

ext/liquid_c/document.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef LIQUID_DOCUMENT_H
2+
#define LIQUID_DOCUMENT_H
3+
4+
void liquid_define_document();
5+
VALUE document_parse(VALUE tokenizer, VALUE parse_context);
6+
7+
#endif

0 commit comments

Comments
 (0)