Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile assign tag to VM code #139

Draft
wants to merge 1 commit into
base: vm-tag-compile
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ext/liquid_c/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,19 @@ VALUE context_filtering_p(VALUE self)
return liquid_vm_filtering(self) ? Qtrue : Qfalse;
}

void context_assign(context_t *context, VALUE name, VALUE value)
{
VALUE scopes = context->scopes;
long scopes_size = RARRAY_LEN(scopes);
if (RB_UNLIKELY(scopes_size == 0))
rb_raise(rb_eRuntimeError, "Liquid::Context#scopes is empty, missing the required outer scope");

VALUE last_scope = RARRAY_AREF(scopes, scopes_size - 1);
Check_Type(last_scope, T_HASH);

rb_hash_aset(last_scope, name, value);
}

void liquid_define_context()
{
id_has_key = rb_intern("key?");
Expand Down
1 change: 1 addition & 0 deletions ext/liquid_c/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void context_internal_init(VALUE context_obj, context_t *context);
void context_mark(context_t *context);
VALUE context_find_variable(context_t *context, VALUE key, VALUE raise_on_not_found);
void context_maybe_raise_undefined_variable(VALUE self, VALUE key);
void context_assign(context_t *context, VALUE name, VALUE value);

extern ID id_aset, id_set_context;

Expand Down
2 changes: 1 addition & 1 deletion ext/liquid_c/resource_limits.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ static VALUE resource_limits_increment_render_score_method(VALUE self, VALUE amo
return Qnil;
}

static void resource_limits_increment_assign_score(resource_limits_t *resource_limits, long amount)
void resource_limits_increment_assign_score(resource_limits_t *resource_limits, long amount)
{
resource_limits->assign_score = resource_limits->assign_score + amount;

Expand Down
1 change: 1 addition & 0 deletions ext/liquid_c/resource_limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extern const rb_data_type_t resource_limits_data_type;
void liquid_define_resource_limits();
void resource_limits_raise_limits_reached(resource_limits_t *resource_limit);
void resource_limits_increment_render_score(resource_limits_t *resource_limits, long amount);
void resource_limits_increment_assign_score(resource_limits_t *resource_limits, long amount);
void resource_limits_increment_write_score(resource_limits_t *resource_limits, VALUE output);

#endif
36 changes: 35 additions & 1 deletion ext/liquid_c/tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

static ID id_parse;

static VALUE cLiquidTag;
static VALUE cLiquidTag, cLiquidAssign;
static VALUE liquid_assign_syntax;

static VALUE tag_class_compile(VALUE klass, VALUE tag_name, VALUE markup,
VALUE tokenizer_obj, VALUE parse_context_obj, VALUE block_body_obj)
Expand Down Expand Up @@ -57,6 +58,31 @@ static VALUE echo_class_compile(VALUE klass, VALUE tag_name, VALUE markup,
return Qnil;
}

static VALUE assign_class_compile(VALUE klass, VALUE tag_name, VALUE markup,
VALUE tokenizer_obj, VALUE parse_context_obj, VALUE block_body_obj)
{
block_body_t *body;
BlockBody_Get_Struct(block_body_obj, body);
block_body_ensure_intermediate(body);

if (rb_reg_match(liquid_assign_syntax, markup) == Qnil)
rb_funcall(cLiquidAssign, rb_intern("raise_syntax_error"), 1, parse_context_obj);
VALUE last_match = rb_backref_get();
VALUE var_name = rb_reg_nth_match(1, last_match);
VALUE var_markup = rb_reg_nth_match(2, last_match);

variable_parse_args_t parse_args = {
.markup = RSTRING_PTR(var_markup),
.markup_end = RSTRING_END(var_markup),
.code = body->as.intermediate.code,
.code_obj = block_body_obj,
.parse_context = parse_context_obj,
};
internal_variable_compile_evaluate(&parse_args);
vm_assembler_add_assign(body->as.intermediate.code, var_name);
return Qnil;
}

void liquid_define_tag()
{
id_parse = rb_intern("parse");
Expand All @@ -69,4 +95,12 @@ void liquid_define_tag()

VALUE cLiquidEcho = rb_const_get(mLiquid, rb_intern("Echo"));
rb_define_singleton_method(cLiquidEcho, "compile", echo_class_compile, 5);

cLiquidAssign = rb_const_get(mLiquid, rb_intern("Assign"));
rb_global_variable(&cLiquidAssign);
rb_define_singleton_method(cLiquidAssign, "compile", assign_class_compile, 5);

liquid_assign_syntax = rb_const_get(cLiquidAssign, rb_intern("Syntax"));
rb_global_variable(&liquid_assign_syntax);
Check_Type(liquid_assign_syntax, T_REGEXP);
}
46 changes: 46 additions & 0 deletions ext/liquid_c/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,43 @@ static void hash_bulk_insert(long argc, const VALUE *argv, VALUE hash)
}
#endif

static long assign_score_of(VALUE value);

static int assign_score_of_each_hash_value(VALUE key, VALUE value, VALUE func_arg)
{
long *sum = (long *)func_arg;
*sum += assign_score_of(key);
*sum += assign_score_of(value);
return ST_CONTINUE;
}

static long assign_score_of(VALUE value)
{
if (RB_SPECIAL_CONST_P(value))
return 1;

switch (RB_BUILTIN_TYPE(value)) {
case T_STRING:
return RSTRING_LEN(value);
case T_HASH:
{
long sum = 1;
rb_hash_foreach(value, assign_score_of_each_hash_value, (VALUE)&sum);
return sum;
}
case T_ARRAY:
{
long sum = 1;
for (long i = 0; i < RARRAY_LEN(value); i++) {
sum += assign_score_of(RARRAY_AREF(value, i));
}
return sum;
}
default:
return 1;
}
}

// Actually returns a bool resume_rendering value
static VALUE vm_render_until_error(VALUE uncast_args)
{
Expand Down Expand Up @@ -386,6 +423,14 @@ static VALUE vm_render_until_error(VALUE uncast_args)
resource_limits_increment_write_score(vm->context.resource_limits, output);
break;
}
case OP_ASSIGN:
{
VALUE var_name = (VALUE)*const_ptr++;
VALUE value = vm_stack_pop(vm);
context_assign(&vm->context, var_name, value);
resource_limits_increment_assign_score(vm->context.resource_limits, assign_score_of(value));
break;
}

default:
rb_bug("invalid opcode: %u", ip[-1]);
Expand Down Expand Up @@ -444,6 +489,7 @@ void liquid_vm_next_instruction(const uint8_t **ip_ptr, const VALUE **const_ptr_
case OP_FIND_STATIC_VAR:
case OP_LOOKUP_CONST_KEY:
case OP_LOOKUP_COMMAND:
case OP_ASSIGN:
(*const_ptr_ptr)++;
break;

Expand Down
4 changes: 4 additions & 0 deletions ext/liquid_c/vm_assembler.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ VALUE vm_assembler_disassemble(const uint8_t *start_ip, const uint8_t *end_ip, c
rb_str_catf(output, "lookup_command(%+"PRIsVALUE")\n", const_ptr[0]);
break;

case OP_ASSIGN:
rb_str_catf(output, "assign(%+"PRIsVALUE")\n", const_ptr[0]);
break;

case OP_FILTER:
rb_str_catf(output, "filter(name: %+"PRIsVALUE", num_args: %u)\n", const_ptr[0], ip[1]);
break;
Expand Down
8 changes: 8 additions & 0 deletions ext/liquid_c/vm_assembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum opcode {
OP_WRITE_NODE = 2,
OP_POP_WRITE,
OP_WRITE_RAW_SKIP,
OP_ASSIGN,
OP_PUSH_CONST,
OP_PUSH_NIL,
OP_PUSH_TRUE,
Expand Down Expand Up @@ -222,4 +223,11 @@ static inline void vm_assembler_add_render_variable_rescue(vm_assembler_t *code,
uint24_to_bytes((unsigned int)node_line_number, &instructions[1]);
}

static inline void vm_assembler_add_assign(vm_assembler_t *code, VALUE variable_name)
{
code->stack_size--;
vm_assembler_write_ruby_constant(code, variable_name);
vm_assembler_write_opcode(code, OP_ASSIGN);
}

#endif