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)
5660static 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+
141168static 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
347442static 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 ) {
0 commit comments