liquid-c 4.0.0 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/liquid.yml +45 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +14 -0
- data/Gemfile +14 -6
- data/README.md +29 -5
- data/Rakefile +13 -62
- data/ext/liquid_c/block.c +488 -60
- data/ext/liquid_c/block.h +28 -2
- data/ext/liquid_c/c_buffer.c +42 -0
- data/ext/liquid_c/c_buffer.h +76 -0
- data/ext/liquid_c/context.c +233 -0
- data/ext/liquid_c/context.h +70 -0
- data/ext/liquid_c/document_body.c +89 -0
- data/ext/liquid_c/document_body.h +59 -0
- data/ext/liquid_c/expression.c +116 -0
- data/ext/liquid_c/expression.h +24 -0
- data/ext/liquid_c/extconf.rb +22 -6
- data/ext/liquid_c/intutil.h +22 -0
- data/ext/liquid_c/lexer.c +6 -2
- data/ext/liquid_c/lexer.h +18 -3
- data/ext/liquid_c/liquid.c +76 -6
- data/ext/liquid_c/liquid.h +24 -1
- data/ext/liquid_c/parse_context.c +76 -0
- data/ext/liquid_c/parse_context.h +13 -0
- data/ext/liquid_c/parser.c +141 -65
- data/ext/liquid_c/parser.h +4 -2
- data/ext/liquid_c/raw.c +110 -0
- data/ext/liquid_c/raw.h +6 -0
- data/ext/liquid_c/resource_limits.c +279 -0
- data/ext/liquid_c/resource_limits.h +23 -0
- data/ext/liquid_c/stringutil.h +44 -0
- data/ext/liquid_c/tokenizer.c +149 -35
- data/ext/liquid_c/tokenizer.h +20 -9
- data/ext/liquid_c/usage.c +18 -0
- data/ext/liquid_c/usage.h +9 -0
- data/ext/liquid_c/variable.c +196 -20
- data/ext/liquid_c/variable.h +18 -1
- data/ext/liquid_c/variable_lookup.c +44 -0
- data/ext/liquid_c/variable_lookup.h +8 -0
- data/ext/liquid_c/vm.c +588 -0
- data/ext/liquid_c/vm.h +25 -0
- data/ext/liquid_c/vm_assembler.c +491 -0
- data/ext/liquid_c/vm_assembler.h +240 -0
- data/ext/liquid_c/vm_assembler_pool.c +97 -0
- data/ext/liquid_c/vm_assembler_pool.h +27 -0
- data/lib/liquid/c/compile_ext.rb +44 -0
- data/lib/liquid/c/version.rb +3 -1
- data/lib/liquid/c.rb +225 -46
- data/liquid-c.gemspec +18 -10
- data/performance/c_profile.rb +23 -0
- data/performance.rb +6 -4
- data/rakelib/compile.rake +15 -0
- data/rakelib/integration_test.rake +43 -0
- data/rakelib/performance.rake +43 -0
- data/rakelib/rubocop.rake +6 -0
- data/rakelib/unit_test.rake +14 -0
- data/test/integration_test.rb +11 -0
- data/test/liquid_test_helper.rb +21 -0
- data/test/test_helper.rb +14 -2
- data/test/unit/block_test.rb +130 -0
- data/test/unit/context_test.rb +83 -0
- data/test/unit/expression_test.rb +186 -0
- data/test/unit/gc_stress_test.rb +28 -0
- data/test/unit/raw_test.rb +19 -0
- data/test/unit/resource_limits_test.rb +50 -0
- data/test/unit/tokenizer_test.rb +90 -20
- data/test/unit/variable_test.rb +212 -60
- metadata +64 -16
- data/.travis.yml +0 -11
- data/test/liquid_test.rb +0 -11
@@ -0,0 +1,240 @@
|
|
1
|
+
#ifndef VM_ASSEMBLER_H
|
2
|
+
#define VM_ASSEMBLER_H
|
3
|
+
|
4
|
+
#include <assert.h>
|
5
|
+
#include "liquid.h"
|
6
|
+
#include "c_buffer.h"
|
7
|
+
#include "intutil.h"
|
8
|
+
|
9
|
+
enum opcode {
|
10
|
+
OP_LEAVE = 0,
|
11
|
+
OP_WRITE_RAW_W = 1,
|
12
|
+
OP_WRITE_NODE = 2,
|
13
|
+
OP_POP_WRITE,
|
14
|
+
OP_WRITE_RAW_SKIP,
|
15
|
+
OP_PUSH_CONST,
|
16
|
+
OP_PUSH_NIL,
|
17
|
+
OP_PUSH_TRUE,
|
18
|
+
OP_PUSH_FALSE,
|
19
|
+
OP_PUSH_INT8,
|
20
|
+
OP_PUSH_INT16,
|
21
|
+
OP_FIND_STATIC_VAR,
|
22
|
+
OP_FIND_VAR,
|
23
|
+
OP_LOOKUP_CONST_KEY,
|
24
|
+
OP_LOOKUP_KEY,
|
25
|
+
OP_LOOKUP_COMMAND,
|
26
|
+
OP_NEW_INT_RANGE,
|
27
|
+
OP_HASH_NEW, // rb_hash_new & rb_hash_bulk_insert
|
28
|
+
OP_FILTER,
|
29
|
+
OP_BUILTIN_FILTER,
|
30
|
+
OP_RENDER_VARIABLE_RESCUE, // setup state to rescue variable rendering
|
31
|
+
OP_WRITE_RAW,
|
32
|
+
OP_JUMP_FWD_W,
|
33
|
+
OP_JUMP_FWD,
|
34
|
+
};
|
35
|
+
|
36
|
+
typedef struct {
|
37
|
+
const char *name;
|
38
|
+
VALUE sym;
|
39
|
+
} filter_desc_t;
|
40
|
+
|
41
|
+
extern filter_desc_t builtin_filters[];
|
42
|
+
|
43
|
+
typedef struct vm_assembler {
|
44
|
+
c_buffer_t instructions;
|
45
|
+
c_buffer_t constants;
|
46
|
+
st_table *constants_table;
|
47
|
+
size_t max_stack_size;
|
48
|
+
size_t stack_size;
|
49
|
+
size_t protected_stack_size;
|
50
|
+
bool parsing; // prevent executing when incomplete or extending when complete
|
51
|
+
} vm_assembler_t;
|
52
|
+
|
53
|
+
void liquid_define_vm_assembler(void);
|
54
|
+
void vm_assembler_init(vm_assembler_t *code);
|
55
|
+
void vm_assembler_reset(vm_assembler_t *code);
|
56
|
+
void vm_assembler_free(vm_assembler_t *code);
|
57
|
+
void vm_assembler_gc_mark(vm_assembler_t *code);
|
58
|
+
VALUE vm_assembler_disassemble(const uint8_t *start_ip, const uint8_t *end_ip, const VALUE *constants);
|
59
|
+
void vm_assembler_concat(vm_assembler_t *dest, vm_assembler_t *src);
|
60
|
+
void vm_assembler_require_stack_args(vm_assembler_t *code, unsigned int count);
|
61
|
+
|
62
|
+
void vm_assembler_add_write_raw(vm_assembler_t *code, const char *string, size_t size);
|
63
|
+
void vm_assembler_add_write_node(vm_assembler_t *code, VALUE node);
|
64
|
+
void vm_assembler_add_push_fixnum(vm_assembler_t *code, VALUE num);
|
65
|
+
void vm_assembler_add_push_literal(vm_assembler_t *code, VALUE literal);
|
66
|
+
void vm_assembler_add_filter(vm_assembler_t *code, VALUE filter_name, size_t arg_count);
|
67
|
+
|
68
|
+
void vm_assembler_add_evaluate_expression_from_ruby(vm_assembler_t *code, VALUE code_obj, VALUE expression);
|
69
|
+
void vm_assembler_add_find_variable_from_ruby(vm_assembler_t *code, VALUE code_obj, VALUE expression);
|
70
|
+
void vm_assembler_add_lookup_command_from_ruby(vm_assembler_t *code, VALUE command);
|
71
|
+
void vm_assembler_add_lookup_key_from_ruby(vm_assembler_t *code, VALUE code_obj, VALUE expression);
|
72
|
+
void vm_assembler_add_new_int_range_from_ruby(vm_assembler_t *code);
|
73
|
+
void vm_assembler_add_hash_new_from_ruby(vm_assembler_t *code, VALUE hash_size_obj);
|
74
|
+
void vm_assembler_add_filter_from_ruby(vm_assembler_t *code, VALUE filter_name, VALUE arg_count_obj);
|
75
|
+
|
76
|
+
bool vm_assembler_opcode_has_constant(uint8_t ip);
|
77
|
+
|
78
|
+
static inline size_t vm_assembler_alloc_memsize(const vm_assembler_t *code)
|
79
|
+
{
|
80
|
+
return c_buffer_capacity(&code->instructions) + c_buffer_capacity(&code->constants) + sizeof(st_table);
|
81
|
+
}
|
82
|
+
|
83
|
+
static inline void vm_assembler_write_opcode(vm_assembler_t *code, enum opcode op)
|
84
|
+
{
|
85
|
+
c_buffer_write_byte(&code->instructions, op);
|
86
|
+
}
|
87
|
+
|
88
|
+
static inline uint16_t vm_assembler_write_ruby_constant(vm_assembler_t *code, VALUE constant)
|
89
|
+
{
|
90
|
+
st_table *constants_table = code->constants_table;
|
91
|
+
st_data_t index_value;
|
92
|
+
|
93
|
+
if (st_lookup(constants_table, constant, &index_value)) {
|
94
|
+
return (uint16_t)index_value;
|
95
|
+
} else {
|
96
|
+
uint16_t index = c_buffer_size(&code->constants) / sizeof(VALUE);
|
97
|
+
st_insert(constants_table, constant, index);
|
98
|
+
c_buffer_write(&code->constants, &constant, sizeof(VALUE));
|
99
|
+
return index;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
static inline void vm_assembler_increment_stack_size(vm_assembler_t *code, size_t amount)
|
104
|
+
{
|
105
|
+
code->stack_size += amount;
|
106
|
+
if (code->stack_size > code->max_stack_size)
|
107
|
+
code->max_stack_size = code->stack_size;
|
108
|
+
}
|
109
|
+
|
110
|
+
static inline void vm_assembler_reserve_stack_size(vm_assembler_t *code, size_t amount)
|
111
|
+
{
|
112
|
+
vm_assembler_increment_stack_size(code, amount);
|
113
|
+
code->stack_size -= amount;
|
114
|
+
}
|
115
|
+
|
116
|
+
static inline void vm_assembler_add_op_with_constant(vm_assembler_t *code, VALUE constant, uint8_t opcode)
|
117
|
+
{
|
118
|
+
uint16_t index = vm_assembler_write_ruby_constant(code, constant);
|
119
|
+
uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 3);
|
120
|
+
instructions[0] = opcode;
|
121
|
+
instructions[1] = index >> 8;
|
122
|
+
instructions[2] = (uint8_t)index;
|
123
|
+
}
|
124
|
+
|
125
|
+
static inline void vm_assembler_add_leave(vm_assembler_t *code)
|
126
|
+
{
|
127
|
+
vm_assembler_write_opcode(code, OP_LEAVE);
|
128
|
+
code->parsing = false;
|
129
|
+
}
|
130
|
+
|
131
|
+
static inline void vm_assembler_remove_leave(vm_assembler_t *code)
|
132
|
+
{
|
133
|
+
code->parsing = true;
|
134
|
+
code->instructions.data_end--;
|
135
|
+
assert(*code->instructions.data_end == OP_LEAVE);
|
136
|
+
}
|
137
|
+
|
138
|
+
static inline void vm_assembler_add_pop_write(vm_assembler_t *code)
|
139
|
+
{
|
140
|
+
code->stack_size -= 1;
|
141
|
+
vm_assembler_write_opcode(code, OP_POP_WRITE);
|
142
|
+
}
|
143
|
+
|
144
|
+
static inline void vm_assembler_add_hash_new(vm_assembler_t *code, size_t hash_size)
|
145
|
+
{
|
146
|
+
if (hash_size > 255)
|
147
|
+
rb_enc_raise(utf8_encoding, cLiquidSyntaxError, "Hash literal has too many keys");
|
148
|
+
code->stack_size -= hash_size * 2;
|
149
|
+
code->stack_size++;
|
150
|
+
uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 2);
|
151
|
+
instructions[0] = OP_HASH_NEW;
|
152
|
+
instructions[1] = hash_size;
|
153
|
+
}
|
154
|
+
|
155
|
+
|
156
|
+
static inline void vm_assembler_add_push_nil(vm_assembler_t *code)
|
157
|
+
{
|
158
|
+
vm_assembler_increment_stack_size(code, 1);
|
159
|
+
vm_assembler_write_opcode(code, OP_PUSH_NIL);
|
160
|
+
}
|
161
|
+
|
162
|
+
static inline void vm_assembler_add_push_true(vm_assembler_t *code)
|
163
|
+
{
|
164
|
+
vm_assembler_increment_stack_size(code, 1);
|
165
|
+
vm_assembler_write_opcode(code, OP_PUSH_TRUE);
|
166
|
+
}
|
167
|
+
|
168
|
+
static inline void vm_assembler_add_push_false(vm_assembler_t *code)
|
169
|
+
{
|
170
|
+
vm_assembler_increment_stack_size(code, 1);
|
171
|
+
vm_assembler_write_opcode(code, OP_PUSH_FALSE);
|
172
|
+
}
|
173
|
+
|
174
|
+
static inline void vm_assembler_add_push_int8(vm_assembler_t *code, int8_t value)
|
175
|
+
{
|
176
|
+
vm_assembler_increment_stack_size(code, 1);
|
177
|
+
uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 2);
|
178
|
+
instructions[0] = OP_PUSH_INT8;
|
179
|
+
instructions[1] = value;
|
180
|
+
}
|
181
|
+
|
182
|
+
static inline void vm_assembler_add_push_int16(vm_assembler_t *code, int16_t value)
|
183
|
+
{
|
184
|
+
vm_assembler_increment_stack_size(code, 1);
|
185
|
+
uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 3);
|
186
|
+
instructions[0] = OP_PUSH_INT16;
|
187
|
+
instructions[1] = value >> 8;
|
188
|
+
instructions[2] = (uint8_t)value;
|
189
|
+
}
|
190
|
+
|
191
|
+
static inline void vm_assembler_add_push_const(vm_assembler_t *code, VALUE constant)
|
192
|
+
{
|
193
|
+
vm_assembler_increment_stack_size(code, 1);
|
194
|
+
vm_assembler_add_op_with_constant(code, constant, OP_PUSH_CONST);
|
195
|
+
}
|
196
|
+
|
197
|
+
static inline void vm_assembler_add_find_static_variable(vm_assembler_t *code, VALUE key)
|
198
|
+
{
|
199
|
+
vm_assembler_increment_stack_size(code, 1);
|
200
|
+
vm_assembler_add_op_with_constant(code, key, OP_FIND_STATIC_VAR);
|
201
|
+
}
|
202
|
+
|
203
|
+
static inline void vm_assembler_add_find_variable(vm_assembler_t *code)
|
204
|
+
{
|
205
|
+
// pop 1, push 1
|
206
|
+
vm_assembler_write_opcode(code, OP_FIND_VAR);
|
207
|
+
}
|
208
|
+
|
209
|
+
static inline void vm_assembler_add_lookup_const_key(vm_assembler_t *code, VALUE key)
|
210
|
+
{
|
211
|
+
vm_assembler_reserve_stack_size(code, 1); // push 1, pop 2, push 1
|
212
|
+
vm_assembler_add_op_with_constant(code, key, OP_LOOKUP_CONST_KEY);
|
213
|
+
}
|
214
|
+
|
215
|
+
static inline void vm_assembler_add_lookup_key(vm_assembler_t *code)
|
216
|
+
{
|
217
|
+
code->stack_size--; // pop 2, push 1
|
218
|
+
vm_assembler_write_opcode(code, OP_LOOKUP_KEY);
|
219
|
+
}
|
220
|
+
|
221
|
+
static inline void vm_assembler_add_lookup_command(vm_assembler_t *code, VALUE command)
|
222
|
+
{
|
223
|
+
vm_assembler_reserve_stack_size(code, 1); // push 1, pop 2, push 1
|
224
|
+
vm_assembler_add_op_with_constant(code, command, OP_LOOKUP_COMMAND);
|
225
|
+
}
|
226
|
+
|
227
|
+
static inline void vm_assembler_add_new_int_range(vm_assembler_t *code)
|
228
|
+
{
|
229
|
+
code->stack_size--; // pop 2, push 1
|
230
|
+
vm_assembler_write_opcode(code, OP_NEW_INT_RANGE);
|
231
|
+
}
|
232
|
+
|
233
|
+
static inline void vm_assembler_add_render_variable_rescue(vm_assembler_t *code, size_t node_line_number)
|
234
|
+
{
|
235
|
+
uint8_t *instructions = c_buffer_extend_for_write(&code->instructions, 4);
|
236
|
+
instructions[0] = OP_RENDER_VARIABLE_RESCUE;
|
237
|
+
uint24_to_bytes((unsigned int)node_line_number, &instructions[1]);
|
238
|
+
}
|
239
|
+
|
240
|
+
#endif
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#include "vm_assembler_pool.h"
|
2
|
+
#include "parse_context.h"
|
3
|
+
|
4
|
+
static VALUE cLiquidCVMAssemblerPool;
|
5
|
+
|
6
|
+
void vm_assembler_pool_gc_mark(vm_assembler_pool_t *pool)
|
7
|
+
{
|
8
|
+
rb_gc_mark(pool->self);
|
9
|
+
}
|
10
|
+
|
11
|
+
static void vm_assembler_pool_free(void *ptr)
|
12
|
+
{
|
13
|
+
vm_assembler_pool_t *pool = ptr;
|
14
|
+
|
15
|
+
vm_assembler_element_t *element = pool->freelist;
|
16
|
+
while (element) {
|
17
|
+
vm_assembler_free(&element->vm_assembler);
|
18
|
+
vm_assembler_element_t *next = element->next;
|
19
|
+
xfree(element);
|
20
|
+
element = next;
|
21
|
+
}
|
22
|
+
|
23
|
+
xfree(pool);
|
24
|
+
}
|
25
|
+
|
26
|
+
static size_t vm_assembler_pool_memsize(const void *ptr)
|
27
|
+
{
|
28
|
+
const vm_assembler_pool_t *pool = ptr;
|
29
|
+
|
30
|
+
size_t elements_size = 0;
|
31
|
+
vm_assembler_element_t *element = pool->freelist;
|
32
|
+
while (element) {
|
33
|
+
elements_size += sizeof(vm_assembler_element_t) + vm_assembler_alloc_memsize(&element->vm_assembler);
|
34
|
+
element = element->next;
|
35
|
+
}
|
36
|
+
|
37
|
+
return sizeof(vm_assembler_pool_t) + elements_size;
|
38
|
+
}
|
39
|
+
|
40
|
+
const rb_data_type_t vm_assembler_pool_data_type = {
|
41
|
+
"liquid_vm_assembler_pool",
|
42
|
+
{ NULL, vm_assembler_pool_free, vm_assembler_pool_memsize, },
|
43
|
+
NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY
|
44
|
+
};
|
45
|
+
|
46
|
+
VALUE vm_assembler_pool_new(void)
|
47
|
+
{
|
48
|
+
vm_assembler_pool_t *pool;
|
49
|
+
VALUE obj = TypedData_Make_Struct(cLiquidCVMAssemblerPool, vm_assembler_pool_t, &vm_assembler_pool_data_type, pool);
|
50
|
+
|
51
|
+
pool->self = obj;
|
52
|
+
pool->freelist = NULL;
|
53
|
+
|
54
|
+
return obj;
|
55
|
+
}
|
56
|
+
|
57
|
+
vm_assembler_t *vm_assembler_pool_alloc_assembler(vm_assembler_pool_t *pool)
|
58
|
+
{
|
59
|
+
vm_assembler_element_t *element;
|
60
|
+
if (!pool->freelist) {
|
61
|
+
element = xmalloc(sizeof(vm_assembler_element_t));
|
62
|
+
element->next = NULL;
|
63
|
+
vm_assembler_init(&element->vm_assembler);
|
64
|
+
} else {
|
65
|
+
element = pool->freelist;
|
66
|
+
pool->freelist = element->next;
|
67
|
+
}
|
68
|
+
|
69
|
+
return &element->vm_assembler;
|
70
|
+
}
|
71
|
+
|
72
|
+
static vm_assembler_element_t *get_element_from_assembler(vm_assembler_t *assembler)
|
73
|
+
{
|
74
|
+
return (vm_assembler_element_t *)((char *)assembler - offsetof(vm_assembler_element_t, vm_assembler));
|
75
|
+
}
|
76
|
+
|
77
|
+
void vm_assembler_pool_free_assembler(vm_assembler_t *assembler)
|
78
|
+
{
|
79
|
+
vm_assembler_element_t *element = get_element_from_assembler(assembler);
|
80
|
+
vm_assembler_free(&element->vm_assembler);
|
81
|
+
xfree(element);
|
82
|
+
}
|
83
|
+
|
84
|
+
void vm_assembler_pool_recycle_assembler(vm_assembler_pool_t *pool, vm_assembler_t *assembler)
|
85
|
+
{
|
86
|
+
vm_assembler_element_t *element = get_element_from_assembler(assembler);
|
87
|
+
vm_assembler_reset(&element->vm_assembler);
|
88
|
+
element->next = pool->freelist;
|
89
|
+
pool->freelist = element;
|
90
|
+
}
|
91
|
+
|
92
|
+
void liquid_define_vm_assembler_pool(void)
|
93
|
+
{
|
94
|
+
cLiquidCVMAssemblerPool = rb_define_class_under(mLiquidC, "VMAssemblerPool", rb_cObject);
|
95
|
+
rb_global_variable(&cLiquidCVMAssemblerPool);
|
96
|
+
rb_undef_alloc_func(cLiquidCVMAssemblerPool);
|
97
|
+
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#ifndef LIQUID_VM_ASSEMBLER_POOL_H
|
2
|
+
#define LIQUID_VM_ASSEMBLER_POOL_H
|
3
|
+
|
4
|
+
#include "liquid.h"
|
5
|
+
#include "vm_assembler.h"
|
6
|
+
|
7
|
+
typedef struct vm_assembler_element {
|
8
|
+
struct vm_assembler_element *next;
|
9
|
+
vm_assembler_t vm_assembler;
|
10
|
+
} vm_assembler_element_t;
|
11
|
+
|
12
|
+
typedef struct vm_assembler_pool {
|
13
|
+
VALUE self;
|
14
|
+
vm_assembler_element_t *freelist;
|
15
|
+
} vm_assembler_pool_t;
|
16
|
+
|
17
|
+
extern const rb_data_type_t vm_assembler_pool_data_type;
|
18
|
+
#define VMAssemblerPool_Get_Struct(obj, sval) TypedData_Get_Struct(obj, vm_assembler_pool_t, &vm_assembler_pool_data_type, sval)
|
19
|
+
|
20
|
+
void liquid_define_vm_assembler_pool(void);
|
21
|
+
void vm_assembler_pool_gc_mark(vm_assembler_pool_t *pool);
|
22
|
+
VALUE vm_assembler_pool_new(void);
|
23
|
+
vm_assembler_t *vm_assembler_pool_alloc_assembler(vm_assembler_pool_t *pool);
|
24
|
+
void vm_assembler_pool_free_assembler(vm_assembler_t *assembler);
|
25
|
+
void vm_assembler_pool_recycle_assembler(vm_assembler_pool_t *pool, vm_assembler_t *assembler);
|
26
|
+
|
27
|
+
#endif
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Liquid::Variable.class_eval do
|
4
|
+
def compile_evaluate(code)
|
5
|
+
code.add_evaluate_expression(@name)
|
6
|
+
filters.each do |filter_name, filter_args, keyword_args|
|
7
|
+
filter_args.each do |arg|
|
8
|
+
code.add_evaluate_expression(arg)
|
9
|
+
end
|
10
|
+
num_args = filter_args.size
|
11
|
+
if keyword_args
|
12
|
+
keyword_args.each do |key, value|
|
13
|
+
code.add_evaluate_expression(key)
|
14
|
+
code.add_evaluate_expression(value)
|
15
|
+
end
|
16
|
+
num_args += 1
|
17
|
+
code.add_hash_new(keyword_args.size)
|
18
|
+
end
|
19
|
+
code.add_filter(filter_name, num_args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Liquid::VariableLookup.class_eval do
|
25
|
+
def compile_evaluate(code)
|
26
|
+
code.add_find_variable(name)
|
27
|
+
lookups.each_with_index do |lookup, i|
|
28
|
+
is_command = @command_flags & (1 << i) != 0
|
29
|
+
if is_command
|
30
|
+
code.add_lookup_command(lookup)
|
31
|
+
else
|
32
|
+
code.add_lookup_key(lookup)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Liquid::RangeLookup.class_eval do
|
39
|
+
def compile_evaluate(code)
|
40
|
+
code.add_evaluate_expression(@start_obj)
|
41
|
+
code.add_evaluate_expression(@end_obj)
|
42
|
+
code.add_new_int_range
|
43
|
+
end
|
44
|
+
end
|