ext/liquid_c/vm.c
static VALUE vm_render_until_error(VALUE uncast_args)
{
    vm_render_until_error_args_t *args = (void *)uncast_args;
    const VALUE *const_ptr = args->const_ptr;
    const uint8_t *ip = args->ip;
    vm_t *vm = args->vm;
    VALUE output = args->output;
    args->ip = NULL; // used by vm_render_rescue, NULL to indicate that it isn't in a rescue block

    while (true) {
        switch (*ip++) {
            case OP_LEAVE:
                return false;

            case OP_PUSH_CONST:
                vm_stack_push(vm, *const_ptr++);
                break;
            case OP_PUSH_NIL:
                vm_stack_push(vm, Qnil);
                break;
            case OP_PUSH_TRUE:
                vm_stack_push(vm, Qtrue);
                break;
            case OP_PUSH_FALSE:
                vm_stack_push(vm, Qfalse);
                break;
            case OP_PUSH_INT8:
            {
                int num = *(int8_t *)ip++; // signed
                vm_stack_push(vm, RB_INT2FIX(num));
                break;
            }
            case OP_PUSH_INT16:
            {
                int num = *(int8_t *)ip++; // big endian encoding, so first byte has sign
                num = (num << 8) | *ip++;
                vm_stack_push(vm, RB_INT2FIX(num));
                break;
            }
            case OP_FIND_STATIC_VAR:
                vm_stack_push(vm, *const_ptr++);
                /* fallthrough */
            case OP_FIND_VAR:
            {
                VALUE key = vm_stack_pop(vm);
                VALUE value = context_find_variable(&vm->context, key, Qtrue);
                vm_stack_push(vm, value);
                break;
            }
            case OP_LOOKUP_CONST_KEY:
            case OP_LOOKUP_COMMAND:
                vm_stack_push(vm, *const_ptr++);
                /* fallthrough */
            case OP_LOOKUP_KEY:
            {
                bool is_command = ip[-1] == OP_LOOKUP_COMMAND;
                VALUE key = vm_stack_pop(vm);
                VALUE object = vm_stack_pop(vm);
                VALUE result = variable_lookup_key(vm->context.self, object, key, is_command);
                vm_stack_push(vm, result);
                break;
            }

            case OP_NEW_INT_RANGE:
            {
                VALUE end = range_value_to_integer(vm_stack_pop(vm));
                VALUE begin = range_value_to_integer(vm_stack_pop(vm));
                bool exclude_end = false;
                vm_stack_push(vm, rb_range_new(begin, end, exclude_end));
                break;
            }
            case OP_HASH_NEW:
            {
                size_t hash_size = *ip++;
                size_t num_keys_and_values = hash_size * 2;
                VALUE hash = rb_hash_new();
                VALUE *args_ptr = vm_stack_pop_n_use_in_place(vm, num_keys_and_values);
                hash_bulk_insert(num_keys_and_values, args_ptr, hash);
                vm_stack_push(vm, hash);
                break;
            }
            case OP_FILTER:
            case OP_BUILTIN_FILTER:
            {
                VALUE filter_name;
                if (ip[-1] == OP_FILTER) {
                    filter_name = *const_ptr++;
                } else {
                    assert(ip[-1] == OP_BUILTIN_FILTER);
                    filter_name = builtin_filters[*ip++].sym;
                }
                uint8_t num_args = *ip++; // includes input argument
                VALUE *args_ptr = vm_stack_pop_n_use_in_place(vm, num_args);
                VALUE result = vm_invoke_filter(vm, filter_name, num_args, args_ptr);
                vm_stack_push(vm, result);
                break;
            }

            // Rendering instructions

            case OP_WRITE_RAW_W:
            case OP_WRITE_RAW:
            {
                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->context.resource_limits, output);
                break;
            }
            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, vm->context.self, output, (VALUE)*const_ptr++);
                if (RARRAY_LEN(vm->context.interrupts)) {
                    return false;
                }
                resource_limits_increment_write_score(vm->context.resource_limits, output);
                break;
            case OP_RENDER_VARIABLE_RESCUE:
                // Save state used by vm_render_rescue to rescue from a variable rendering exception
                args->node_line_number = ip;
                // vm_render_rescue will iterate from this instruction to the instruction
                // following OP_POP_WRITE_VARIABLE to resume rendering from
                ip += 3;
                args->ip = ip;
                args->const_ptr = const_ptr;
                break;
            case OP_POP_WRITE:
            {
                VALUE var_result = vm_stack_pop(vm);
                if (vm->context.global_filter != Qnil)
                    var_result = rb_funcall(vm->context.global_filter, id_call, 1, var_result);
                write_obj(output, var_result);
                args->ip = NULL; // mark the end of a rescue block, used by vm_render_rescue
                resource_limits_increment_write_score(vm->context.resource_limits, output);
                break;
            }

            default:
                rb_bug("invalid opcode: %u", ip[-1]);
        }
    }
}
// Actually returns a bool resume_rendering value
Actually returns a bool resume_rendering value
Actually returns a bool resume_rendering value
static VALUE vm_render_until_error(VALUE uncast_args)
{
    vm_render_until_error_args_t *args = (void *)uncast_args;
    const VALUE *const_ptr = args->const_ptr;
    const uint8_t *ip = args->ip;
    vm_t *vm = args->vm;
    VALUE output = args->output;
    args->ip = NULL; 
    while (true) {
        switch (*ip++) {
            case OP_LEAVE:
                return false;
            case OP_PUSH_CONST:
                vm_stack_push(vm, *const_ptr++);
                break;
            case OP_PUSH_NIL:
                vm_stack_push(vm, Qnil);
                break;
            case OP_PUSH_TRUE:
                vm_stack_push(vm, Qtrue);
                break;
            case OP_PUSH_FALSE:
                vm_stack_push(vm, Qfalse);
                break;
            case OP_PUSH_INT8:
            {
                int num = *(int8_t *)ip++; 
                vm_stack_push(vm, RB_INT2FIX(num));
                break;
            }
            case OP_PUSH_INT16:
            {
                int num = *(int8_t *)ip++; 
                num = (num << 8) | *ip++;
                vm_stack_push(vm, RB_INT2FIX(num));
                break;
            }
            case OP_FIND_STATIC_VAR:
                vm_stack_push(vm, *const_ptr++);
            case OP_FIND_VAR:
            {
                VALUE key = vm_stack_pop(vm);
                VALUE value = context_find_variable(&vm->context, key, Qtrue);
                vm_stack_push(vm, value);
                break;
            }
            case OP_LOOKUP_CONST_KEY:
            case OP_LOOKUP_COMMAND:
                vm_stack_push(vm, *const_ptr++);
            case OP_LOOKUP_KEY:
            {
                bool is_command = ip[-1] == OP_LOOKUP_COMMAND;
                VALUE key = vm_stack_pop(vm);
                VALUE object = vm_stack_pop(vm);
                VALUE result = variable_lookup_key(vm->context.self, object, key, is_command);
                vm_stack_push(vm, result);
                break;
            }
            case OP_NEW_INT_RANGE:
            {
                VALUE end = range_value_to_integer(vm_stack_pop(vm));
                VALUE begin = range_value_to_integer(vm_stack_pop(vm));
                bool exclude_end = false;
                vm_stack_push(vm, rb_range_new(begin, end, exclude_end));
                break;
            }
            case OP_HASH_NEW:
            {
                size_t hash_size = *ip++;
                size_t num_keys_and_values = hash_size * 2;
                VALUE hash = rb_hash_new();
                VALUE *args_ptr = vm_stack_pop_n_use_in_place(vm, num_keys_and_values);
                hash_bulk_insert(num_keys_and_values, args_ptr, hash);
                vm_stack_push(vm, hash);
                break;
            }
            case OP_FILTER:
            case OP_BUILTIN_FILTER:
            {
                VALUE filter_name;
                if (ip[-1] == OP_FILTER) {
                    filter_name = *const_ptr++;
                } else {
                    assert(ip[-1] == OP_BUILTIN_FILTER);
                    filter_name = builtin_filters[*ip++].sym;
                }
                uint8_t num_args = *ip++; 
                VALUE *args_ptr = vm_stack_pop_n_use_in_place(vm, num_args);
                VALUE result = vm_invoke_filter(vm, filter_name, num_args, args_ptr);
                vm_stack_push(vm, result);
                break;
            }
            case OP_WRITE_RAW_W:
            case OP_WRITE_RAW:
            {
                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->context.resource_limits, output);
                break;
            }
            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, vm->context.self, output, (VALUE)*const_ptr++);
                if (RARRAY_LEN(vm->context.interrupts)) {
                    return false;
                }
                resource_limits_increment_write_score(vm->context.resource_limits, output);
                break;
            case OP_RENDER_VARIABLE_RESCUE:
                args->node_line_number = ip;
                ip += 3;
                args->ip = ip;
                args->const_ptr = const_ptr;
                break;
            case OP_POP_WRITE:
            {
                VALUE var_result = vm_stack_pop(vm);
                if (vm->context.global_filter != Qnil)
                    var_result = rb_funcall(vm->context.global_filter, id_call, 1, var_result);
                write_obj(output, var_result);
                args->ip = NULL; 
                resource_limits_increment_write_score(vm->context.resource_limits, output);
                break;
            }
            default:
                rb_bug("invalid opcode: %u", ip[-1]);
        }
    }
}
static VALUE vm_render_until_error ( VALUE uncast_args ) { vm_render_until_error_args_t * args = ( void * ) uncast_args ; const VALUE * const_ptr = args -> const_ptr ; const uint8_t * ip = args -> ip ; vm_t * vm = args -> vm ; VALUE output = args -> output ; args -> ip = NULL ; while ( true ) { switch ( * ip ++ ) { case OP_LEAVE : return false ; case OP_PUSH_CONST : vm_stack_push ( vm , * const_ptr ++ ) ; break ; case OP_PUSH_NIL : vm_stack_push ( vm , Qnil ) ; break ; case OP_PUSH_TRUE : vm_stack_push ( vm , Qtrue ) ; break ; case OP_PUSH_FALSE : vm_stack_push ( vm , Qfalse ) ; break ; case OP_PUSH_INT8 : { int num = * ( int8_t * ) ip ++ ; vm_stack_push ( vm , RB_INT2FIX ( num ) ) ; break ; } case OP_PUSH_INT16 : { int num = * ( int8_t * ) ip ++ ; num = ( num << 8 ) | * ip ++ ; vm_stack_push ( vm , RB_INT2FIX ( num ) ) ; break ; } case OP_FIND_STATIC_VAR : vm_stack_push ( vm , * const_ptr ++ ) ; case OP_FIND_VAR : { VALUE key = vm_stack_pop ( vm ) ; VALUE value = context_find_variable ( & vm -> context , key , Qtrue ) ; vm_stack_push ( vm , value ) ; break ; } case OP_LOOKUP_CONST_KEY : case OP_LOOKUP_COMMAND : vm_stack_push ( vm , * const_ptr ++ ) ; case OP_LOOKUP_KEY : { bool is_command = ip [ -1 ] == OP_LOOKUP_COMMAND ; VALUE key = vm_stack_pop ( vm ) ; VALUE object = vm_stack_pop ( vm ) ; VALUE result = variable_lookup_key ( vm -> context . self , object , key , is_command ) ; vm_stack_push ( vm , result ) ; break ; } case OP_NEW_INT_RANGE : { VALUE end = range_value_to_integer ( vm_stack_pop ( vm ) ) ; VALUE begin = range_value_to_integer ( vm_stack_pop ( vm ) ) ; bool exclude_end = false ; vm_stack_push ( vm , rb_range_new ( begin , end , exclude_end ) ) ; break ; } case OP_HASH_NEW : { size_t hash_size = * ip ++ ; size_t num_keys_and_values = hash_size * 2 ; VALUE hash = rb_hash_new ( ) ; VALUE * args_ptr = vm_stack_pop_n_use_in_place ( vm , num_keys_and_values ) ; hash_bulk_insert ( num_keys_and_values , args_ptr , hash ) ; vm_stack_push ( vm , hash ) ; break ; } case OP_FILTER : case OP_BUILTIN_FILTER : { VALUE filter_name ; if ( ip [ -1 ] == OP_FILTER ) { filter_name = * const_ptr ++ ; } else { assert ( ip [ -1 ] == OP_BUILTIN_FILTER ) ; filter_name = builtin_filters [ * ip ++ ] . sym ; } uint8_t num_args = * ip ++ ; VALUE * args_ptr = vm_stack_pop_n_use_in_place ( vm , num_args ) ; VALUE result = vm_invoke_filter ( vm , filter_name , num_args , args_ptr ) ; vm_stack_push ( vm , result ) ; break ; } case OP_WRITE_RAW_W : case OP_WRITE_RAW : { 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 -> context . resource_limits , output ) ; break ; } 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 , vm -> context . self , output , ( VALUE ) * const_ptr ++ ) ; if ( RARRAY_LEN ( vm -> context . interrupts ) ) { return false ; } resource_limits_increment_write_score ( vm -> context . resource_limits , output ) ; break ; case OP_RENDER_VARIABLE_RESCUE : args -> node_line_number = ip ; ip += 3 ; args -> ip = ip ; args -> const_ptr = const_ptr ; break ; case OP_POP_WRITE : { VALUE var_result = vm_stack_pop ( vm ) ; if ( vm -> context . global_filter != Qnil ) var_result = rb_funcall ( vm -> context . global_filter , id_call , 1 , var_result ) ; write_obj ( output , var_result ) ; args -> ip = NULL ; resource_limits_increment_write_score ( vm -> context . resource_limits , output ) ; break ; } default : rb_bug ( " " , ip [ -1 ] ) ; } } }
Actually returns a bool resume_rendering value
Actually returns a bool resume_rendering value
// used by vm_render_rescue, NULL to indicate that it isn't in a rescue block // signed // big endian encoding, so first byte has sign /* fallthrough */ /* fallthrough */ // includes input argument // Rendering instructions // Save state used by vm_render_rescue to rescue from a variable rendering exception // vm_render_rescue will iterate from this instruction to the instruction // following OP_POP_WRITE_VARIABLE to resume rendering from // mark the end of a rescue block, used by vm_render_rescue