From d8fcda549d98ad6d215080111fd8bb2490402098 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Wed, 14 Oct 2020 10:07:43 -0400 Subject: [PATCH 1/8] Implement immediate raw instruction --- ext/liquid_c/block.c | 14 +++++++------- ext/liquid_c/intutil.h | 22 ++++++++++++++++++++++ ext/liquid_c/vm.c | 21 +++++++++++++++++---- ext/liquid_c/vm_assembler.c | 15 ++++++++++----- ext/liquid_c/vm_assembler.h | 6 +++--- 5 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 ext/liquid_c/intutil.h diff --git a/ext/liquid_c/block.c b/ext/liquid_c/block.c index 29f8968a..575e0f72 100644 --- a/ext/liquid_c/block.c +++ b/ext/liquid_c/block.c @@ -1,5 +1,6 @@ #include "liquid.h" #include "block.h" +#include "intutil.h" #include "tokenizer.h" #include "stringutil.h" #include "vm.h" @@ -272,17 +273,16 @@ static VALUE block_body_remove_blank_strings(VALUE self) ensure_not_parsing(body); size_t *const_ptr = (size_t *)body->code.constants.data; - const uint8_t *ip = body->code.instructions.data; + uint8_t *ip = (uint8_t *)body->code.instructions.data; while (*ip != OP_LEAVE) { if (*ip == OP_WRITE_RAW) { - size_t *size_ptr = &const_ptr[1]; - if (*size_ptr) { - *size_ptr = 0; // effectively a no-op + if (ip[1] || ip[2] || ip[3]) { // if (size != 0) + ip[0] = OP_WRITE_RAW_SKIP; // effectively a no-op body->render_score--; } } - liquid_vm_next_instruction(&ip, (const size_t **)&const_ptr); + liquid_vm_next_instruction((const uint8_t **)&ip, (const size_t **)&const_ptr); } return Qnil; @@ -318,8 +318,8 @@ static VALUE block_body_nodelist(VALUE self) goto loop_break; case OP_WRITE_RAW: { - const char *text = (const char *)const_ptr[0]; - size_t size = const_ptr[1]; + const char *text = (const char *)&ip[4]; + size_t size = bytes_to_uint24(&ip[1]); VALUE string = rb_enc_str_new(text, size, utf8_encoding); rb_ary_push(nodelist, string); break; diff --git a/ext/liquid_c/intutil.h b/ext/liquid_c/intutil.h new file mode 100644 index 00000000..9ed7074f --- /dev/null +++ b/ext/liquid_c/intutil.h @@ -0,0 +1,22 @@ +#ifndef LIQUID_INTUTIL_H +#define LIQUID_INTUTIL_H + +#include + +static inline unsigned int bytes_to_uint24(const uint8_t *bytes) +{ + return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; +} + +static inline void uint24_to_bytes(unsigned int num, uint8_t *bytes) +{ + assert(num < (1 << 24)); + + bytes[0] = num >> 16; + bytes[1] = num >> 8; + bytes[2] = num; + + assert(bytes_to_uint24(bytes) == num); +} + +#endif diff --git a/ext/liquid_c/vm.c b/ext/liquid_c/vm.c index 6aa20214..fca1804b 100644 --- a/ext/liquid_c/vm.c +++ b/ext/liquid_c/vm.c @@ -6,6 +6,7 @@ #include "resource_limits.h" #include "context.h" #include "variable_lookup.h" +#include "intutil.h" ID id_render_node; ID id_ivar_interrupts; @@ -357,12 +358,20 @@ static VALUE vm_render_until_error(VALUE uncast_args) case OP_WRITE_RAW: { - const char *text = (const char *)*const_ptr++; - size_t size = *const_ptr++; + const char *text = (const char *)&ip[3]; + size_t size = bytes_to_uint24(ip); rb_str_cat(output, text, size); resource_limits_increment_write_score(vm->resource_limits, output); + ip += 3 + size; break; } + case OP_WRITE_RAW_SKIP: + { + size_t size = bytes_to_uint24(ip); + ip += 3 + size; + break; + } + case OP_WRITE_NODE: rb_funcall(cLiquidBlockBody, id_render_node, 3, args->context, output, (VALUE)*const_ptr++); if (RARRAY_LEN(vm->interrupts)) { @@ -460,8 +469,12 @@ void liquid_vm_next_instruction(const uint8_t **ip_ptr, const size_t **const_ptr break; case OP_WRITE_RAW: - (*const_ptr_ptr) += 2; + case OP_WRITE_RAW_SKIP: + { + size_t size = bytes_to_uint24(ip); + ip += 3 + size; break; + } default: rb_bug("invalid opcode: %u", ip[-1]); @@ -506,7 +519,7 @@ static VALUE vm_render_rescue(VALUE uncast_args, VALUE exception) VALUE line_number = Qnil; if (render_args->node_line_number) { - unsigned int node_line_number = decode_node_line_number(render_args->node_line_number); + unsigned int node_line_number = bytes_to_uint24(render_args->node_line_number); if (node_line_number != 0) { line_number = UINT2NUM(node_line_number); } diff --git a/ext/liquid_c/vm_assembler.c b/ext/liquid_c/vm_assembler.c index 73efcdf0..b6308593 100644 --- a/ext/liquid_c/vm_assembler.c +++ b/ext/liquid_c/vm_assembler.c @@ -52,8 +52,12 @@ void vm_assembler_gc_mark(vm_assembler_t *code) break; case OP_WRITE_RAW: - const_ptr += 2; + case OP_WRITE_RAW_SKIP: + { + size_t size = bytes_to_uint24(ip); + ip += 3 + size; break; + } case OP_WRITE_NODE: case OP_PUSH_CONST: @@ -202,10 +206,11 @@ void vm_assembler_require_stack_args(vm_assembler_t *code, unsigned int count) void vm_assembler_add_write_raw(vm_assembler_t *code, const char *string, size_t size) { - vm_assembler_write_opcode(code, OP_WRITE_RAW); - VALUE *constants = c_buffer_extend_for_write(&code->constants, 2 * sizeof(VALUE)); - constants[0] = (size_t)string; - constants[1] = size; + uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 4); + instructions[0] = OP_WRITE_RAW; + uint24_to_bytes((unsigned int)size, &instructions[1]); + + c_buffer_write(&code->instructions, (char *)string, size); } void vm_assembler_add_write_node(vm_assembler_t *code, VALUE node) diff --git a/ext/liquid_c/vm_assembler.h b/ext/liquid_c/vm_assembler.h index 3bbfc087..11d1328d 100644 --- a/ext/liquid_c/vm_assembler.h +++ b/ext/liquid_c/vm_assembler.h @@ -4,12 +4,14 @@ #include #include "liquid.h" #include "c_buffer.h" +#include "intutil.h" enum opcode { OP_LEAVE = 0, OP_WRITE_RAW = 1, OP_WRITE_NODE = 2, OP_POP_WRITE, + OP_WRITE_RAW_SKIP, OP_PUSH_CONST, OP_PUSH_NIL, OP_PUSH_TRUE, @@ -214,9 +216,7 @@ static inline void vm_assembler_add_render_variable_rescue(vm_assembler_t *code, { uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 4); instructions[0] = OP_RENDER_VARIABLE_RESCUE; - instructions[1] = node_line_number >> 16; - instructions[2] = node_line_number >> 8; - instructions[3] = node_line_number; + uint24_to_bytes((unsigned int)node_line_number, &instructions[1]); } #endif From 0461aa853e1ee441f1144e819f878226ebee861e Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 15 Oct 2020 11:42:56 -0400 Subject: [PATCH 2/8] Mark all constants directly --- ext/liquid_c/vm_assembler.c | 56 +------------------------------------ 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/ext/liquid_c/vm_assembler.c b/ext/liquid_c/vm_assembler.c index b6308593..374b1747 100644 --- a/ext/liquid_c/vm_assembler.c +++ b/ext/liquid_c/vm_assembler.c @@ -21,61 +21,7 @@ void vm_assembler_free(vm_assembler_t *code) void vm_assembler_gc_mark(vm_assembler_t *code) { - size_t *const_ptr = (size_t *)code->constants.data; - const uint8_t *ip = code->instructions.data; - // Don't rely on a terminating OP_LEAVE instruction - // since this could be called in the middle of parsing - const uint8_t *end_ip = code->instructions.data_end; - while (ip < end_ip) { - switch (*ip++) { - case OP_LEAVE: - case OP_POP_WRITE: - case OP_PUSH_NIL: - case OP_PUSH_TRUE: - case OP_PUSH_FALSE: - case OP_FIND_VAR: - case OP_LOOKUP_KEY: - case OP_NEW_INT_RANGE: - break; - - case OP_HASH_NEW: - case OP_PUSH_INT8: - ip++; - break; - - case OP_PUSH_INT16: - ip += 2; - break; - - case OP_RENDER_VARIABLE_RESCUE: - ip += 3; - break; - - case OP_WRITE_RAW: - case OP_WRITE_RAW_SKIP: - { - size_t size = bytes_to_uint24(ip); - ip += 3 + size; - break; - } - - case OP_WRITE_NODE: - case OP_PUSH_CONST: - case OP_FIND_STATIC_VAR: - case OP_LOOKUP_CONST_KEY: - case OP_LOOKUP_COMMAND: - rb_gc_mark(*const_ptr++); - break; - - case OP_FILTER: - ip++; - rb_gc_mark(*const_ptr++); - break; - - default: - rb_bug("invalid opcode: %u", ip[-1]); - } - } + c_buffer_rb_gc_mark(&code->constants); } VALUE vm_assembler_disassemble(vm_assembler_t *code) From 08840d75e93884d0da55e19434c654df3981b5a3 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 15 Oct 2020 11:56:28 -0400 Subject: [PATCH 3/8] Implement OP_JUMP_FWD to jump forward --- ext/liquid_c/block.c | 2 +- ext/liquid_c/vm.c | 4 ++-- ext/liquid_c/vm_assembler.h | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/liquid_c/block.c b/ext/liquid_c/block.c index 575e0f72..68ec9818 100644 --- a/ext/liquid_c/block.c +++ b/ext/liquid_c/block.c @@ -278,7 +278,7 @@ static VALUE block_body_remove_blank_strings(VALUE self) while (*ip != OP_LEAVE) { if (*ip == OP_WRITE_RAW) { if (ip[1] || ip[2] || ip[3]) { // if (size != 0) - ip[0] = OP_WRITE_RAW_SKIP; // effectively a no-op + ip[0] = OP_JUMP_FWD; // effectively a no-op body->render_score--; } } diff --git a/ext/liquid_c/vm.c b/ext/liquid_c/vm.c index fca1804b..391c5bd0 100644 --- a/ext/liquid_c/vm.c +++ b/ext/liquid_c/vm.c @@ -365,7 +365,7 @@ static VALUE vm_render_until_error(VALUE uncast_args) ip += 3 + size; break; } - case OP_WRITE_RAW_SKIP: + case OP_JUMP_FWD: { size_t size = bytes_to_uint24(ip); ip += 3 + size; @@ -469,7 +469,7 @@ void liquid_vm_next_instruction(const uint8_t **ip_ptr, const size_t **const_ptr break; case OP_WRITE_RAW: - case OP_WRITE_RAW_SKIP: + case OP_JUMP_FWD: { size_t size = bytes_to_uint24(ip); ip += 3 + size; diff --git a/ext/liquid_c/vm_assembler.h b/ext/liquid_c/vm_assembler.h index 11d1328d..644d131a 100644 --- a/ext/liquid_c/vm_assembler.h +++ b/ext/liquid_c/vm_assembler.h @@ -27,6 +27,7 @@ enum opcode { OP_HASH_NEW, // rb_hash_new & rb_hash_bulk_insert OP_FILTER, OP_RENDER_VARIABLE_RESCUE, // setup state to rescue variable rendering + OP_JUMP_FWD, }; typedef struct vm_assembler { From b70e49d9b6da140ae2abd18ca7d37022eed80ff2 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 16 Oct 2020 14:38:33 -0400 Subject: [PATCH 4/8] Implement single byte OP_WRITE_RAW instruction --- ext/liquid_c/block.c | 19 ++++++++++++++++--- ext/liquid_c/vm.c | 36 ++++++++++++++++++++++++++++++------ ext/liquid_c/vm_assembler.c | 12 +++++++++--- ext/liquid_c/vm_assembler.h | 4 +++- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/ext/liquid_c/block.c b/ext/liquid_c/block.c index 68ec9818..e5308686 100644 --- a/ext/liquid_c/block.c +++ b/ext/liquid_c/block.c @@ -277,10 +277,15 @@ static VALUE block_body_remove_blank_strings(VALUE self) while (*ip != OP_LEAVE) { if (*ip == OP_WRITE_RAW) { - if (ip[1] || ip[2] || ip[3]) { // if (size != 0) + if (ip[1]) { // if (size != 0) ip[0] = OP_JUMP_FWD; // effectively a no-op body->render_score--; } + } else if (*ip == OP_WRITE_RAW_W) { + if (ip[1] || ip[2] || ip[3]) { // if (size != 0) + ip[0] = OP_JUMP_FWD_W; // effectively a no-op + body->render_score--; + } } liquid_vm_next_instruction((const uint8_t **)&ip, (const size_t **)&const_ptr); } @@ -316,10 +321,18 @@ static VALUE block_body_nodelist(VALUE self) switch (*ip) { case OP_LEAVE: goto loop_break; + case OP_WRITE_RAW_W: case OP_WRITE_RAW: { - const char *text = (const char *)&ip[4]; - size_t size = bytes_to_uint24(&ip[1]); + const char *text; + size_t size; + if (*ip == OP_WRITE_RAW_W) { + size = bytes_to_uint24(&ip[1]); + text = (const char *)&ip[4]; + } else { + size = ip[1]; + text = (const char *)&ip[2]; + } VALUE string = rb_enc_str_new(text, size, utf8_encoding); rb_ary_push(nodelist, string); break; diff --git a/ext/liquid_c/vm.c b/ext/liquid_c/vm.c index 391c5bd0..207b453c 100644 --- a/ext/liquid_c/vm.c +++ b/ext/liquid_c/vm.c @@ -356,22 +356,38 @@ static VALUE vm_render_until_error(VALUE uncast_args) // Rendering instructions + case OP_WRITE_RAW_W: case OP_WRITE_RAW: { - const char *text = (const char *)&ip[3]; - size_t size = bytes_to_uint24(ip); + const char *text; + size_t size; + if (ip[-1] == OP_WRITE_RAW_W) { + size = bytes_to_uint24(ip); + text = (const char *)&ip[3]; + ip += 3 + size; + } else { + size = *ip; + text = (const char *)&ip[1]; + ip += 1 + size; + } rb_str_cat(output, text, size); resource_limits_increment_write_score(vm->resource_limits, output); - ip += 3 + size; break; } - case OP_JUMP_FWD: + case OP_JUMP_FWD_W: { size_t size = bytes_to_uint24(ip); ip += 3 + size; break; } + case OP_JUMP_FWD: + { + uint8_t size = *ip; + ip += 1 + size; + break; + } + case OP_WRITE_NODE: rb_funcall(cLiquidBlockBody, id_render_node, 3, args->context, output, (VALUE)*const_ptr++); if (RARRAY_LEN(vm->interrupts)) { @@ -468,14 +484,22 @@ void liquid_vm_next_instruction(const uint8_t **ip_ptr, const size_t **const_ptr (*const_ptr_ptr)++; break; - case OP_WRITE_RAW: - case OP_JUMP_FWD: + case OP_WRITE_RAW_W: + case OP_JUMP_FWD_W: { size_t size = bytes_to_uint24(ip); ip += 3 + size; break; } + case OP_WRITE_RAW: + case OP_JUMP_FWD: + { + uint8_t size = *ip; + ip += 1 + size; + break; + } + default: rb_bug("invalid opcode: %u", ip[-1]); } diff --git a/ext/liquid_c/vm_assembler.c b/ext/liquid_c/vm_assembler.c index 374b1747..1836e620 100644 --- a/ext/liquid_c/vm_assembler.c +++ b/ext/liquid_c/vm_assembler.c @@ -152,9 +152,15 @@ void vm_assembler_require_stack_args(vm_assembler_t *code, unsigned int count) void vm_assembler_add_write_raw(vm_assembler_t *code, const char *string, size_t size) { - uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 4); - instructions[0] = OP_WRITE_RAW; - uint24_to_bytes((unsigned int)size, &instructions[1]); + if (size > UINT8_MAX) { + uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 4); + instructions[0] = OP_WRITE_RAW_W; + uint24_to_bytes((unsigned int)size, &instructions[1]); + } else { + uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 2); + instructions[0] = OP_WRITE_RAW; + instructions[1] = size; + } c_buffer_write(&code->instructions, (char *)string, size); } diff --git a/ext/liquid_c/vm_assembler.h b/ext/liquid_c/vm_assembler.h index 644d131a..936141ad 100644 --- a/ext/liquid_c/vm_assembler.h +++ b/ext/liquid_c/vm_assembler.h @@ -8,7 +8,7 @@ enum opcode { OP_LEAVE = 0, - OP_WRITE_RAW = 1, + OP_WRITE_RAW_W = 1, OP_WRITE_NODE = 2, OP_POP_WRITE, OP_WRITE_RAW_SKIP, @@ -27,6 +27,8 @@ enum opcode { OP_HASH_NEW, // rb_hash_new & rb_hash_bulk_insert OP_FILTER, OP_RENDER_VARIABLE_RESCUE, // setup state to rescue variable rendering + OP_WRITE_RAW, + OP_JUMP_FWD_W, OP_JUMP_FWD, }; From 1aafc9ed19552f19222db1c97f70759da99cede0 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Tue, 27 Oct 2020 16:13:44 -0400 Subject: [PATCH 5/8] Remove source from block_body_t --- ext/liquid_c/block.c | 7 ------- ext/liquid_c/block.h | 1 - 2 files changed, 8 deletions(-) diff --git a/ext/liquid_c/block.c b/ext/liquid_c/block.c index e5308686..597aa4da 100644 --- a/ext/liquid_c/block.c +++ b/ext/liquid_c/block.c @@ -33,7 +33,6 @@ typedef struct parse_context { static void block_body_mark(void *ptr) { block_body_t *body = ptr; - rb_gc_mark(body->source); vm_assembler_gc_mark(&body->code); } @@ -67,7 +66,6 @@ static VALUE block_body_allocate(VALUE klass) vm_assembler_init(&body->code); vm_assembler_add_leave(&body->code); body->obj = obj; - body->source = Qnil; body->render_score = 0; body->blank = true; body->nodelist = Qundef; @@ -231,11 +229,6 @@ static VALUE block_body_parse(VALUE self, VALUE tokenizer_obj, VALUE parse_conte BlockBody_Get_Struct(self, body); ensure_not_parsing(body); - if (body->source == Qnil) { - body->source = parse_context.tokenizer->source; - } else if (body->source != parse_context.tokenizer->source) { - rb_raise(rb_eArgError, "Liquid::C::BlockBody#parse must be passed the same tokenizer when called multiple times"); - } vm_assembler_remove_leave(&body->code); // to extend block tag_markup_t unknown_tag = internal_block_body_parse(body, &parse_context); diff --git a/ext/liquid_c/block.h b/ext/liquid_c/block.h index 76117dc6..9aadd9da 100644 --- a/ext/liquid_c/block.h +++ b/ext/liquid_c/block.h @@ -6,7 +6,6 @@ typedef struct block_body { VALUE obj; vm_assembler_t code; - VALUE source; // hold a reference to the ruby object that OP_WRITE_RAW points to bool blank; int render_score; VALUE nodelist; From d80c8407aca90e5b0560221c7be62fd61fcee7e6 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 29 Oct 2020 10:44:32 -0400 Subject: [PATCH 6/8] Add test for OP_WRITE_RAW_W --- test/unit/block_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/unit/block_test.rb b/test/unit/block_test.rb index 7a40e8bb..d8e82eaa 100644 --- a/test/unit/block_test.rb +++ b/test/unit/block_test.rb @@ -25,6 +25,12 @@ def test_write_unicode_characters assert_equal("üñ", template.render!({ 'unicode_char' => 'ñ' }, output: output)) end + def test_op_write_raw_w + output = "a" * 2**8 + template = Liquid::Template.parse(output) + assert_equal(output, template.render!) + end + def test_disassemble source = <<~LIQUID raw From 95553b17df703305dad0878ff92a2358a605737b Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 29 Oct 2020 14:03:03 -0400 Subject: [PATCH 7/8] Add support for disassemble --- ext/liquid_c/vm_assembler.c | 12 ++++++++++-- test/unit/block_test.rb | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/ext/liquid_c/vm_assembler.c b/ext/liquid_c/vm_assembler.c index 1836e620..9db37d48 100644 --- a/ext/liquid_c/vm_assembler.c +++ b/ext/liquid_c/vm_assembler.c @@ -88,10 +88,18 @@ VALUE vm_assembler_disassemble(vm_assembler_t *code) break; } + case OP_WRITE_RAW_W: case OP_WRITE_RAW: { - const char *text = (const char *)const_ptr[0]; - size_t size = const_ptr[1]; + const char *text; + size_t size; + if (*ip == OP_WRITE_RAW_W) { + size = bytes_to_uint24(&ip[1]); + text = (const char *)&ip[4]; + } else { + size = ip[1]; + text = (const char *)&ip[2]; + } VALUE string = rb_enc_str_new(text, size, utf8_encoding); rb_str_catf(output, "write_raw(%+"PRIsVALUE")\n", string); break; diff --git a/test/unit/block_test.rb b/test/unit/block_test.rb index d8e82eaa..f09d3428 100644 --- a/test/unit/block_test.rb +++ b/test/unit/block_test.rb @@ -43,16 +43,16 @@ def test_disassemble assert_instance_of(Liquid::Increment, increment_node) assert_equal(<<~ASM, block_body.disassemble) 0x0000: write_raw("raw") - 0x0001: render_variable_rescue(line_number: 2) - 0x0005: find_static_var("var") - 0x0006: push_const("none") - 0x0007: push_const("allow_false") - 0x0008: push_true - 0x0009: hash_new(1) - 0x000b: filter(name: :default, num_args: 3) - 0x000d: pop_write - 0x000e: write_node(#{increment_node.inspect}) - 0x000f: leave + 0x0005: render_variable_rescue(line_number: 2) + 0x0009: find_static_var("var") + 0x000a: push_const("none") + 0x000b: push_const("allow_false") + 0x000c: push_true + 0x000d: hash_new(1) + 0x000f: filter(name: :default, num_args: 3) + 0x0011: pop_write + 0x0012: write_node(#{increment_node.inspect}) + 0x0013: leave ASM end end From 971611f12290dc65eb5743eee02e12cabcae5a9f Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Fri, 30 Oct 2020 10:00:55 -0400 Subject: [PATCH 8/8] Add test for disassembly for OP_WRITE_RAW_W --- ext/liquid_c/vm_assembler.c | 5 ++++- test/unit/block_test.rb | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ext/liquid_c/vm_assembler.c b/ext/liquid_c/vm_assembler.c index 9db37d48..c71b40cd 100644 --- a/ext/liquid_c/vm_assembler.c +++ b/ext/liquid_c/vm_assembler.c @@ -93,15 +93,18 @@ VALUE vm_assembler_disassemble(vm_assembler_t *code) { const char *text; size_t size; + const char *name; if (*ip == OP_WRITE_RAW_W) { + name = "write_raw_w"; size = bytes_to_uint24(&ip[1]); text = (const char *)&ip[4]; } else { + name = "write_raw"; size = ip[1]; text = (const char *)&ip[2]; } VALUE string = rb_enc_str_new(text, size, utf8_encoding); - rb_str_catf(output, "write_raw(%+"PRIsVALUE")\n", string); + rb_str_catf(output, "%s(%+"PRIsVALUE")\n", name, string); break; } diff --git a/test/unit/block_test.rb b/test/unit/block_test.rb index f09d3428..c114a020 100644 --- a/test/unit/block_test.rb +++ b/test/unit/block_test.rb @@ -26,9 +26,19 @@ def test_write_unicode_characters end def test_op_write_raw_w - output = "a" * 2**8 - template = Liquid::Template.parse(output) - assert_equal(output, template.render!) + source = "a" * 2**8 + template = Liquid::Template.parse(source) + assert_equal(source, template.render!) + end + + def test_disassemble_raw_w + source = "a" * 2**8 + template = Liquid::Template.parse(source) + block_body = template.root.body + assert_equal(<<~ASM, block_body.disassemble) + 0x0000: write_raw_w("#{source}") + 0x0104: leave + ASM end def test_disassemble