herb 0.9.1-arm-linux-gnu → 0.9.3-arm-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/config.yml +125 -0
  4. data/ext/herb/error_helpers.c +172 -2
  5. data/ext/herb/extconf.rb +6 -0
  6. data/ext/herb/extension.c +16 -2
  7. data/ext/herb/extension_helpers.c +6 -5
  8. data/ext/herb/extension_helpers.h +4 -4
  9. data/ext/herb/nodes.c +89 -3
  10. data/lib/herb/3.0/herb.so +0 -0
  11. data/lib/herb/3.1/herb.so +0 -0
  12. data/lib/herb/3.2/herb.so +0 -0
  13. data/lib/herb/3.3/herb.so +0 -0
  14. data/lib/herb/3.4/herb.so +0 -0
  15. data/lib/herb/4.0/herb.so +0 -0
  16. data/lib/herb/ast/erb_content_node.rb +32 -0
  17. data/lib/herb/ast/nodes.rb +244 -3
  18. data/lib/herb/cli.rb +12 -2
  19. data/lib/herb/engine/compiler.rb +136 -97
  20. data/lib/herb/engine/validators/security_validator.rb +40 -0
  21. data/lib/herb/engine.rb +21 -0
  22. data/lib/herb/errors.rb +268 -0
  23. data/lib/herb/parser_options.rb +7 -2
  24. data/lib/herb/version.rb +1 -1
  25. data/lib/herb/visitor.rb +82 -0
  26. data/lib/herb.rb +1 -0
  27. data/sig/herb/ast/erb_content_node.rbs +13 -0
  28. data/sig/herb/ast/nodes.rbs +98 -2
  29. data/sig/herb/engine/compiler.rbs +18 -3
  30. data/sig/herb/engine/validators/security_validator.rbs +4 -0
  31. data/sig/herb/engine.rbs +4 -0
  32. data/sig/herb/errors.rbs +122 -0
  33. data/sig/herb/parser_options.rbs +6 -2
  34. data/sig/herb/visitor.rbs +12 -0
  35. data/sig/serialized_ast_errors.rbs +29 -0
  36. data/sig/serialized_ast_nodes.rbs +19 -0
  37. data/src/analyze/action_view/attribute_extraction_helpers.c +425 -87
  38. data/src/analyze/action_view/image_tag.c +87 -0
  39. data/src/analyze/action_view/javascript_include_tag.c +102 -0
  40. data/src/analyze/action_view/javascript_tag.c +55 -0
  41. data/src/analyze/action_view/registry.c +10 -3
  42. data/src/analyze/action_view/tag.c +19 -2
  43. data/src/analyze/action_view/tag_helper_node_builders.c +119 -37
  44. data/src/analyze/action_view/tag_helpers.c +1033 -32
  45. data/src/analyze/analyze.c +165 -10
  46. data/src/analyze/{helpers.c → analyze_helpers.c} +1 -1
  47. data/src/analyze/analyzed_ruby.c +1 -1
  48. data/src/analyze/builders.c +11 -8
  49. data/src/analyze/conditional_elements.c +6 -7
  50. data/src/analyze/conditional_open_tags.c +6 -7
  51. data/src/analyze/control_type.c +4 -2
  52. data/src/analyze/invalid_structures.c +5 -5
  53. data/src/analyze/missing_end.c +2 -2
  54. data/src/analyze/parse_errors.c +5 -5
  55. data/src/analyze/prism_annotate.c +7 -7
  56. data/src/analyze/render_nodes.c +6 -26
  57. data/src/analyze/strict_locals.c +637 -0
  58. data/src/analyze/transform.c +7 -0
  59. data/src/{ast_node.c → ast/ast_node.c} +8 -8
  60. data/src/{ast_nodes.c → ast/ast_nodes.c} +82 -11
  61. data/src/{ast_pretty_print.c → ast/ast_pretty_print.c} +113 -9
  62. data/src/{pretty_print.c → ast/pretty_print.c} +9 -9
  63. data/src/errors.c +398 -8
  64. data/src/extract.c +5 -5
  65. data/src/herb.c +15 -5
  66. data/src/include/analyze/action_view/attribute_extraction_helpers.h +3 -1
  67. data/src/include/analyze/action_view/tag_helper_handler.h +3 -3
  68. data/src/include/analyze/action_view/tag_helper_node_builders.h +34 -5
  69. data/src/include/analyze/action_view/tag_helpers.h +4 -3
  70. data/src/include/analyze/analyze.h +6 -4
  71. data/src/include/analyze/analyzed_ruby.h +2 -2
  72. data/src/include/analyze/builders.h +4 -4
  73. data/src/include/analyze/conditional_elements.h +2 -2
  74. data/src/include/analyze/conditional_open_tags.h +2 -2
  75. data/src/include/analyze/control_type.h +1 -1
  76. data/src/include/analyze/helpers.h +2 -2
  77. data/src/include/analyze/invalid_structures.h +1 -1
  78. data/src/include/analyze/prism_annotate.h +2 -2
  79. data/src/include/analyze/render_nodes.h +1 -1
  80. data/src/include/analyze/strict_locals.h +11 -0
  81. data/src/include/{ast_node.h → ast/ast_node.h} +4 -4
  82. data/src/include/{ast_nodes.h → ast/ast_nodes.h} +38 -14
  83. data/src/include/{ast_pretty_print.h → ast/ast_pretty_print.h} +3 -3
  84. data/src/include/{pretty_print.h → ast/pretty_print.h} +4 -4
  85. data/src/include/errors.h +65 -7
  86. data/src/include/extract.h +2 -2
  87. data/src/include/herb.h +5 -5
  88. data/src/include/{lex_helpers.h → lexer/lex_helpers.h} +5 -5
  89. data/src/include/{lexer.h → lexer/lexer.h} +1 -1
  90. data/src/include/{lexer_peek_helpers.h → lexer/lexer_peek_helpers.h} +2 -2
  91. data/src/include/{lexer_struct.h → lexer/lexer_struct.h} +2 -2
  92. data/src/include/{token.h → lexer/token.h} +3 -3
  93. data/src/include/{token_matchers.h → lexer/token_matchers.h} +1 -1
  94. data/src/include/{token_struct.h → lexer/token_struct.h} +3 -3
  95. data/src/include/{util → lib}/hb_foreach.h +1 -1
  96. data/src/include/{util → lib}/hb_string.h +5 -1
  97. data/src/include/{location.h → location/location.h} +1 -1
  98. data/src/include/parser/dot_notation.h +12 -0
  99. data/src/include/{parser.h → parser/parser.h} +11 -4
  100. data/src/include/{parser_helpers.h → parser/parser_helpers.h} +6 -6
  101. data/src/include/{prism_context.h → prism/prism_context.h} +2 -2
  102. data/src/include/{prism_helpers.h → prism/prism_helpers.h} +6 -6
  103. data/src/include/{html_util.h → util/html_util.h} +2 -1
  104. data/src/include/util/ruby_util.h +9 -0
  105. data/src/include/{utf8.h → util/utf8.h} +1 -1
  106. data/src/include/{util.h → util/util.h} +1 -1
  107. data/src/include/version.h +1 -1
  108. data/src/include/visitor.h +3 -3
  109. data/src/{lexer_peek_helpers.c → lexer/lexer_peek_helpers.c} +3 -3
  110. data/src/{token.c → lexer/token.c} +8 -8
  111. data/src/{token_matchers.c → lexer/token_matchers.c} +2 -2
  112. data/src/lexer.c +6 -6
  113. data/src/{util → lib}/hb_allocator.c +2 -2
  114. data/src/{util → lib}/hb_arena.c +4 -8
  115. data/src/{util → lib}/hb_arena_debug.c +2 -2
  116. data/src/{util → lib}/hb_array.c +2 -2
  117. data/src/{util → lib}/hb_buffer.c +2 -2
  118. data/src/{util → lib}/hb_narray.c +1 -1
  119. data/src/{util → lib}/hb_string.c +2 -2
  120. data/src/{location.c → location/location.c} +2 -2
  121. data/src/{position.c → location/position.c} +2 -2
  122. data/src/{range.c → location/range.c} +1 -1
  123. data/src/main.c +11 -11
  124. data/src/parser/dot_notation.c +100 -0
  125. data/src/{parser_match_tags.c → parser/match_tags.c} +34 -5
  126. data/src/{parser_helpers.c → parser/parser_helpers.c} +10 -10
  127. data/src/parser.c +68 -32
  128. data/src/{prism_helpers.c → prism/prism_helpers.c} +7 -7
  129. data/src/{ruby_parser.c → prism/ruby_parser.c} +1 -1
  130. data/src/{html_util.c → util/html_util.c} +54 -4
  131. data/src/{io.c → util/io.c} +3 -3
  132. data/src/util/ruby_util.c +42 -0
  133. data/src/{utf8.c → util/utf8.c} +2 -2
  134. data/src/{util.c → util/util.c} +4 -4
  135. data/src/visitor.c +35 -3
  136. data/templates/ext/herb/error_helpers.c.erb +2 -2
  137. data/templates/ext/herb/nodes.c.erb +1 -1
  138. data/templates/java/error_helpers.c.erb +1 -1
  139. data/templates/java/error_helpers.h.erb +2 -2
  140. data/templates/java/nodes.c.erb +4 -4
  141. data/templates/java/nodes.h.erb +1 -1
  142. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +4 -4
  143. data/templates/javascript/packages/node/extension/nodes.cpp.erb +4 -4
  144. data/templates/lib/herb/visitor.rb.erb +14 -0
  145. data/templates/src/analyze/missing_end.c.erb +2 -2
  146. data/templates/src/{ast_nodes.c.erb → ast/ast_nodes.c.erb} +9 -9
  147. data/templates/src/{ast_pretty_print.c.erb → ast/ast_pretty_print.c.erb} +8 -8
  148. data/templates/src/errors.c.erb +8 -8
  149. data/templates/src/include/{ast_nodes.h.erb → ast/ast_nodes.h.erb} +11 -12
  150. data/templates/src/include/{ast_pretty_print.h.erb → ast/ast_pretty_print.h.erb} +2 -2
  151. data/templates/src/include/errors.h.erb +7 -7
  152. data/templates/src/{parser_match_tags.c.erb → parser/match_tags.c.erb} +4 -4
  153. data/templates/src/visitor.c.erb +3 -3
  154. data/templates/wasm/error_helpers.cpp.erb +4 -4
  155. data/templates/wasm/nodes.cpp.erb +5 -5
  156. metadata +78 -68
  157. data/src/include/element_source.h +0 -10
  158. /data/src/include/{util → lib}/hb_allocator.h +0 -0
  159. /data/src/include/{util → lib}/hb_arena.h +0 -0
  160. /data/src/include/{util → lib}/hb_arena_debug.h +0 -0
  161. /data/src/include/{util → lib}/hb_array.h +0 -0
  162. /data/src/include/{util → lib}/hb_buffer.h +0 -0
  163. /data/src/include/{util → lib}/hb_narray.h +0 -0
  164. /data/src/include/{util → lib}/string.h +0 -0
  165. /data/src/include/{position.h → location/position.h} +0 -0
  166. /data/src/include/{range.h → location/range.h} +0 -0
  167. /data/src/include/{herb_prism_node.h → prism/herb_prism_node.h} +0 -0
  168. /data/src/include/{prism_serialized.h → prism/prism_serialized.h} +0 -0
  169. /data/src/include/{ruby_parser.h → prism/ruby_parser.h} +0 -0
  170. /data/src/include/{io.h → util/io.h} +0 -0
  171. /data/templates/src/include/{util → lib}/hb_foreach.h.erb +0 -0
@@ -1,9 +1,11 @@
1
1
  #include "../../include/analyze/action_view/attribute_extraction_helpers.h"
2
2
  #include "../../include/analyze/action_view/tag_helper_node_builders.h"
3
- #include "../../include/util.h"
4
- #include "../../include/util/hb_allocator.h"
5
- #include "../../include/util/hb_array.h"
6
- #include "../../include/util/hb_string.h"
3
+ #include "../../include/lib/hb_allocator.h"
4
+ #include "../../include/lib/hb_array.h"
5
+ #include "../../include/lib/hb_buffer.h"
6
+ #include "../../include/lib/hb_string.h"
7
+ #include "../../include/util/html_util.h"
8
+ #include "../../include/util/util.h"
7
9
 
8
10
  #include <prism.h>
9
11
  #include <stdlib.h>
@@ -28,6 +30,171 @@ static char* extract_string_from_prism_node(pm_node_t* node, hb_allocator_T* all
28
30
  return hb_allocator_strndup(allocator, (const char*) source, length);
29
31
  }
30
32
 
33
+ static void compute_position_pair(
34
+ const pm_location_t* location,
35
+ const uint8_t* source,
36
+ const char* original_source,
37
+ size_t erb_content_offset,
38
+ position_T* out_start,
39
+ position_T* out_end
40
+ ) {
41
+ *out_start = prism_location_to_position_with_offset(location, original_source, erb_content_offset, source);
42
+ pm_location_t end_location = { .start = location->end, .end = location->end };
43
+ *out_end = prism_location_to_position_with_offset(&end_location, original_source, erb_content_offset, source);
44
+ }
45
+
46
+ static void extract_key_name_location(pm_node_t* key, pm_location_t* out_name_loc) {
47
+ if (key->type == PM_SYMBOL_NODE) {
48
+ pm_symbol_node_t* symbol = (pm_symbol_node_t*) key;
49
+ *out_name_loc = symbol->value_loc;
50
+ } else if (key->type == PM_STRING_NODE) {
51
+ pm_string_node_t* string_node = (pm_string_node_t*) key;
52
+ *out_name_loc = string_node->content_loc;
53
+ } else {
54
+ *out_name_loc = key->location;
55
+ }
56
+ }
57
+
58
+ static void compute_separator_info(
59
+ pm_assoc_node_t* assoc,
60
+ const uint8_t* source,
61
+ const char* original_source,
62
+ size_t erb_content_offset,
63
+ position_T key_end,
64
+ const char** out_separator_string,
65
+ token_type_T* out_separator_type,
66
+ position_T* out_separator_start,
67
+ position_T* out_separator_end
68
+ ) {
69
+ *out_separator_end =
70
+ prism_location_to_position_with_offset(&assoc->value->location, original_source, erb_content_offset, source);
71
+
72
+ if (assoc->operator_loc.start != NULL) {
73
+ *out_separator_string = " => ";
74
+ *out_separator_type = TOKEN_EQUALS;
75
+ *out_separator_start = key_end;
76
+ } else {
77
+ *out_separator_string = ": ";
78
+ *out_separator_type = TOKEN_COLON;
79
+
80
+ pm_location_t colon_loc = {
81
+ .start = assoc->key->location.end - 1,
82
+ .end = assoc->key->location.end - 1,
83
+ };
84
+
85
+ *out_separator_start =
86
+ prism_location_to_position_with_offset(&colon_loc, original_source, erb_content_offset, source);
87
+ }
88
+ }
89
+
90
+ static void extract_delimited_locations(
91
+ pm_node_t* node,
92
+ const pm_location_t** out_opening,
93
+ const pm_location_t** out_closing,
94
+ const pm_location_t** out_content
95
+ ) {
96
+ if (node->type == PM_STRING_NODE) {
97
+ pm_string_node_t* string_node = (pm_string_node_t*) node;
98
+ *out_opening = &string_node->opening_loc;
99
+ *out_closing = &string_node->closing_loc;
100
+ *out_content = &string_node->content_loc;
101
+ } else if (node->type == PM_SYMBOL_NODE) {
102
+ pm_symbol_node_t* symbol_node = (pm_symbol_node_t*) node;
103
+ *out_opening = &symbol_node->opening_loc;
104
+ *out_closing = &symbol_node->closing_loc;
105
+ *out_content = &symbol_node->value_loc;
106
+ } else {
107
+ *out_opening = NULL;
108
+ *out_closing = NULL;
109
+ *out_content = NULL;
110
+ }
111
+ }
112
+
113
+ static void compute_value_positions(
114
+ pm_node_t* value_node,
115
+ const uint8_t* source,
116
+ const char* original_source,
117
+ size_t erb_content_offset,
118
+ position_T* out_value_start,
119
+ position_T* out_value_end,
120
+ position_T* out_content_start,
121
+ position_T* out_content_end,
122
+ bool* out_quoted
123
+ ) {
124
+ const pm_location_t* opening_loc;
125
+ const pm_location_t* closing_loc;
126
+ const pm_location_t* content_loc;
127
+
128
+ extract_delimited_locations(value_node, &opening_loc, &closing_loc, &content_loc);
129
+
130
+ if (opening_loc && opening_loc->start != NULL && closing_loc && closing_loc->start != NULL) {
131
+ compute_position_pair(
132
+ &*opening_loc,
133
+ source,
134
+ original_source,
135
+ erb_content_offset,
136
+ out_value_start,
137
+ out_content_start
138
+ );
139
+
140
+ compute_position_pair(&*closing_loc, source, original_source, erb_content_offset, out_content_end, out_value_end);
141
+ *out_quoted = true;
142
+ } else {
143
+ const pm_location_t* fallback_loc = content_loc ? content_loc : &value_node->location;
144
+ compute_position_pair(fallback_loc, source, original_source, erb_content_offset, out_value_start, out_value_end);
145
+ *out_content_start = *out_value_start;
146
+ *out_content_end = *out_value_end;
147
+ *out_quoted = false;
148
+ }
149
+ }
150
+
151
+ static void fill_attribute_positions(
152
+ pm_assoc_node_t* assoc,
153
+ const uint8_t* source,
154
+ const char* original_source,
155
+ size_t erb_content_offset,
156
+ attribute_positions_T* positions
157
+ ) {
158
+ pm_location_t name_loc;
159
+ extract_key_name_location(assoc->key, &name_loc);
160
+ compute_position_pair(
161
+ &name_loc,
162
+ source,
163
+ original_source,
164
+ erb_content_offset,
165
+ &positions->name_start,
166
+ &positions->name_end
167
+ );
168
+
169
+ position_T key_end;
170
+ pm_location_t key_end_loc = { .start = assoc->key->location.end, .end = assoc->key->location.end };
171
+ key_end = prism_location_to_position_with_offset(&key_end_loc, original_source, erb_content_offset, source);
172
+
173
+ compute_separator_info(
174
+ assoc,
175
+ source,
176
+ original_source,
177
+ erb_content_offset,
178
+ key_end,
179
+ &positions->separator_string,
180
+ &positions->separator_type,
181
+ &positions->separator_start,
182
+ &positions->separator_end
183
+ );
184
+
185
+ compute_value_positions(
186
+ assoc->value,
187
+ source,
188
+ original_source,
189
+ erb_content_offset,
190
+ &positions->value_start,
191
+ &positions->value_end,
192
+ &positions->content_start,
193
+ &positions->content_end,
194
+ &positions->quoted
195
+ );
196
+ }
197
+
31
198
  static char* build_prefixed_key(const char* prefix, const char* raw_key, hb_allocator_T* allocator) {
32
199
  char* dashed_key = convert_underscores_to_dashes(raw_key);
33
200
  const char* key = dashed_key ? dashed_key : raw_key;
@@ -42,19 +209,82 @@ static char* build_prefixed_key(const char* prefix, const char* raw_key, hb_allo
42
209
  return result;
43
210
  }
44
211
 
212
+ static bool is_static_string_array(pm_array_node_t* array) {
213
+ if (!array || array->elements.size == 0) { return false; }
214
+
215
+ for (size_t i = 0; i < array->elements.size; i++) {
216
+ pm_node_t* element = array->elements.nodes[i];
217
+
218
+ if (element->type != PM_STRING_NODE && element->type != PM_SYMBOL_NODE) { return false; }
219
+ }
220
+
221
+ return true;
222
+ }
223
+
224
+ static char* join_static_string_array(pm_array_node_t* array, hb_allocator_T* allocator) {
225
+ hb_buffer_T buffer;
226
+ hb_buffer_init(&buffer, 64, allocator);
227
+
228
+ for (size_t i = 0; i < array->elements.size; i++) {
229
+ if (i > 0) { hb_buffer_append(&buffer, " "); }
230
+
231
+ char* value = extract_string_from_prism_node(array->elements.nodes[i], allocator);
232
+
233
+ if (value) {
234
+ hb_buffer_append(&buffer, value);
235
+ hb_allocator_dealloc(allocator, value);
236
+ }
237
+ }
238
+
239
+ char* result = hb_allocator_strdup(allocator, hb_buffer_value(&buffer));
240
+
241
+ return result;
242
+ }
243
+
45
244
  static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
46
245
  const char* name_string,
47
246
  pm_node_t* value_node,
48
- position_T start_position,
49
- position_T end_position,
50
- hb_allocator_T* allocator
247
+ attribute_positions_T* positions,
248
+ hb_allocator_T* allocator,
249
+ bool is_nested
51
250
  ) {
251
+ if (value_node->type == PM_ARRAY_NODE && !is_nested && is_static_string_array((pm_array_node_t*) value_node)) {
252
+ char* joined = join_static_string_array((pm_array_node_t*) value_node, allocator);
253
+ if (!joined) { return NULL; }
254
+
255
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
256
+ create_html_attribute_node_precise(name_string, joined, positions, allocator);
257
+ hb_allocator_dealloc(allocator, joined);
258
+
259
+ return attribute;
260
+ }
261
+
52
262
  if (value_node->type == PM_SYMBOL_NODE || value_node->type == PM_STRING_NODE) {
53
263
  char* value_string = extract_string_from_prism_node(value_node, allocator);
54
264
  if (!value_string) { return NULL; }
55
265
 
56
266
  AST_HTML_ATTRIBUTE_NODE_T* attribute =
57
- create_html_attribute_node(name_string, value_string, start_position, end_position, allocator);
267
+ create_html_attribute_node_precise(name_string, value_string, positions, allocator);
268
+ hb_allocator_dealloc(allocator, value_string);
269
+
270
+ return attribute;
271
+ } else if (value_node->type == PM_TRUE_NODE) {
272
+ if (is_boolean_attribute(hb_string((char*) name_string))) {
273
+ return create_html_attribute_node_precise(name_string, NULL, positions, allocator);
274
+ }
275
+
276
+ return create_html_attribute_node_precise(name_string, "true", positions, allocator);
277
+ } else if (value_node->type == PM_FALSE_NODE) {
278
+ if (is_boolean_attribute(hb_string((char*) name_string))) { return NULL; }
279
+
280
+ return create_html_attribute_node_precise(name_string, "false", positions, allocator);
281
+ } else if (value_node->type == PM_INTEGER_NODE) {
282
+ size_t value_length = value_node->location.end - value_node->location.start;
283
+ char* value_string = hb_allocator_strndup(allocator, (const char*) value_node->location.start, value_length);
284
+ if (!value_string) { return NULL; }
285
+
286
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
287
+ create_html_attribute_node_precise(name_string, value_string, positions, allocator);
58
288
  hb_allocator_dealloc(allocator, value_string);
59
289
 
60
290
  return attribute;
@@ -62,18 +292,42 @@ static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
62
292
  return create_html_attribute_with_interpolated_value(
63
293
  name_string,
64
294
  (pm_interpolated_string_node_t*) value_node,
65
- start_position,
66
- end_position,
295
+ positions->name_start,
296
+ positions->value_end,
67
297
  allocator
68
298
  );
69
299
  } else {
70
300
  size_t value_length = value_node->location.end - value_node->location.start;
71
- char* ruby_content = hb_allocator_strndup(allocator, (const char*) value_node->location.start, value_length);
301
+ char* raw_content = hb_allocator_strndup(allocator, (const char*) value_node->location.start, value_length);
302
+
303
+ if (raw_content && value_node->location.start) {
304
+ char* ruby_content = raw_content;
305
+
306
+ if (!is_nested && strcmp(name_string, "class") == 0
307
+ && (value_node->type == PM_HASH_NODE || value_node->type == PM_ARRAY_NODE)) {
308
+ hb_buffer_T class_buffer;
309
+ hb_buffer_init(&class_buffer, value_length + 16, allocator);
310
+ hb_buffer_append(&class_buffer, "token_list(");
311
+ hb_buffer_append(&class_buffer, raw_content);
312
+ hb_buffer_append(&class_buffer, ")");
313
+
314
+ ruby_content = hb_buffer_value(&class_buffer);
315
+ }
316
+
317
+ // Rails calls .to_json on non-string/symbol values inside data:/aria: hashes
318
+ if (is_nested) {
319
+ hb_buffer_T json_buffer;
320
+ hb_buffer_init(&json_buffer, value_length + 16, allocator);
321
+ hb_buffer_append(&json_buffer, raw_content);
322
+ hb_buffer_append(&json_buffer, ".to_json");
323
+
324
+ ruby_content = hb_buffer_value(&json_buffer);
325
+ }
72
326
 
73
- if (ruby_content && value_node->location.start) {
74
327
  AST_HTML_ATTRIBUTE_NODE_T* attribute =
75
- create_html_attribute_with_ruby_literal(name_string, ruby_content, start_position, end_position, allocator);
76
- hb_allocator_dealloc(allocator, ruby_content);
328
+ create_html_attribute_with_ruby_literal_precise(name_string, ruby_content, positions, allocator);
329
+ hb_allocator_dealloc(allocator, raw_content);
330
+
77
331
  return attribute;
78
332
  }
79
333
 
@@ -81,6 +335,58 @@ static AST_HTML_ATTRIBUTE_NODE_T* create_attribute_from_value(
81
335
  }
82
336
  }
83
337
 
338
+ static const char* get_attribute_name_string(AST_HTML_ATTRIBUTE_NODE_T* attribute) {
339
+ if (!attribute || !attribute->name || !attribute->name->children) { return NULL; }
340
+ if (hb_array_size(attribute->name->children) == 0) { return NULL; }
341
+
342
+ AST_NODE_T* first_child = (AST_NODE_T*) hb_array_get(attribute->name->children, 0);
343
+ if (!first_child || first_child->type != AST_LITERAL_NODE) { return NULL; }
344
+
345
+ AST_LITERAL_NODE_T* literal = (AST_LITERAL_NODE_T*) first_child;
346
+ return (const char*) literal->content.data;
347
+ }
348
+
349
+ void resolve_nonce_attribute(hb_array_T* attributes, hb_allocator_T* allocator) {
350
+ if (!attributes) { return; }
351
+
352
+ for (size_t index = 0; index < hb_array_size(attributes); index++) {
353
+ AST_NODE_T* node = hb_array_get(attributes, index);
354
+ if (!node || node->type != AST_HTML_ATTRIBUTE_NODE) { continue; }
355
+
356
+ AST_HTML_ATTRIBUTE_NODE_T* attribute = (AST_HTML_ATTRIBUTE_NODE_T*) node;
357
+ const char* name = get_attribute_name_string(attribute);
358
+ if (!name || strcmp(name, "nonce") != 0) { continue; }
359
+
360
+ if (!attribute->value || !attribute->value->children) { continue; }
361
+ if (hb_array_size(attribute->value->children) == 0) { continue; }
362
+
363
+ AST_NODE_T* value_child = (AST_NODE_T*) hb_array_get(attribute->value->children, 0);
364
+ if (!value_child || value_child->type != AST_LITERAL_NODE) { continue; }
365
+
366
+ AST_LITERAL_NODE_T* literal = (AST_LITERAL_NODE_T*) value_child;
367
+
368
+ if (hb_string_equals(literal->content, hb_string("true"))) {
369
+ AST_RUBY_LITERAL_NODE_T* ruby_node = ast_ruby_literal_node_init(
370
+ hb_string_from_c_string("content_security_policy_nonce"),
371
+ attribute->value->base.location.start,
372
+ attribute->value->base.location.end,
373
+ hb_array_init(0, allocator),
374
+ allocator
375
+ );
376
+
377
+ hb_array_T* new_children = hb_array_init(1, allocator);
378
+ hb_array_append(new_children, (AST_NODE_T*) ruby_node);
379
+ attribute->value->children = new_children;
380
+ return;
381
+ }
382
+
383
+ if (hb_string_equals(literal->content, hb_string("false"))) {
384
+ hb_array_remove(attributes, index);
385
+ return;
386
+ }
387
+ }
388
+ }
389
+
84
390
  AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
85
391
  pm_assoc_node_t* assoc,
86
392
  const uint8_t* source,
@@ -93,10 +399,37 @@ AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
93
399
  char* name_string = extract_string_from_prism_node(assoc->key, allocator);
94
400
  if (!name_string) { return NULL; }
95
401
 
96
- position_T start_position =
97
- prism_location_to_position_with_offset(&assoc->key->location, original_source, erb_content_offset, source);
98
- position_T end_position =
99
- prism_location_to_position_with_offset(&assoc->value->location, original_source, erb_content_offset, source);
402
+ if (strcmp(name_string, "escape") == 0) {
403
+ hb_allocator_dealloc(allocator, name_string);
404
+ return NULL;
405
+ }
406
+
407
+ if (!assoc->value) {
408
+ hb_allocator_dealloc(allocator, name_string);
409
+ return NULL;
410
+ }
411
+
412
+ if (assoc->value->type == PM_IMPLICIT_NODE) {
413
+ pm_location_t name_loc;
414
+ extract_key_name_location(assoc->key, &name_loc);
415
+ position_T name_start, name_end;
416
+ compute_position_pair(&name_loc, source, original_source, erb_content_offset, &name_start, &name_end);
417
+
418
+ char* dashed_name = convert_underscores_to_dashes(name_string);
419
+
420
+ AST_HTML_ATTRIBUTE_NODE_T* attribute = create_html_attribute_with_ruby_literal(
421
+ dashed_name ? dashed_name : name_string,
422
+ name_string,
423
+ name_start,
424
+ name_start,
425
+ allocator
426
+ );
427
+
428
+ if (dashed_name) { free(dashed_name); }
429
+ hb_allocator_dealloc(allocator, name_string);
430
+
431
+ return attribute;
432
+ }
100
433
 
101
434
  // Rails converts `method:` and `remote:` to `data-*` attributes
102
435
  if (strcmp(name_string, "method") == 0 || strcmp(name_string, "remote") == 0) {
@@ -113,14 +446,12 @@ AST_HTML_ATTRIBUTE_NODE_T* extract_html_attribute_from_assoc(
113
446
  return NULL;
114
447
  }
115
448
 
449
+ attribute_positions_T positions;
450
+ fill_attribute_positions(assoc, source, original_source, erb_content_offset, &positions);
451
+
116
452
  char* dashed_name = convert_underscores_to_dashes(name_string);
117
- AST_HTML_ATTRIBUTE_NODE_T* attribute_node = create_attribute_from_value(
118
- dashed_name ? dashed_name : name_string,
119
- assoc->value,
120
- start_position,
121
- end_position,
122
- allocator
123
- );
453
+ AST_HTML_ATTRIBUTE_NODE_T* attribute_node =
454
+ create_attribute_from_value(dashed_name ? dashed_name : name_string, assoc->value, &positions, allocator, false);
124
455
 
125
456
  if (dashed_name) { free(dashed_name); }
126
457
  hb_allocator_dealloc(allocator, name_string);
@@ -145,25 +476,35 @@ hb_array_T* extract_html_attributes_from_keyword_hash(
145
476
 
146
477
  if (element->type == PM_ASSOC_SPLAT_NODE) {
147
478
  pm_assoc_splat_node_t* splat = (pm_assoc_splat_node_t*) element;
148
- size_t splat_length = splat->base.location.end - splat->base.location.start;
149
- char* splat_content = hb_allocator_strndup(allocator, (const char*) splat->base.location.start, splat_length);
150
-
151
- if (splat_content) {
152
- position_T splat_start =
153
- prism_location_to_position_with_offset(&splat->base.location, original_source, erb_content_offset, source);
154
-
155
- AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
156
- hb_string_from_c_string(splat_content),
157
- HB_STRING_EMPTY,
158
- splat_start,
159
- splat_start,
160
- hb_array_init(0, allocator),
161
- allocator
162
- );
163
479
 
164
- if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
165
-
166
- hb_allocator_dealloc(allocator, splat_content);
480
+ if (splat->value) {
481
+ size_t value_length = splat->value->location.end - splat->value->location.start;
482
+ char* value_source = hb_allocator_strndup(allocator, (const char*) splat->value->location.start, value_length);
483
+
484
+ if (value_source) {
485
+ hb_buffer_T wrapped;
486
+ hb_buffer_init(&wrapped, value_length + 32, allocator);
487
+ hb_buffer_append(&wrapped, "tag.attributes(**");
488
+ hb_buffer_append(&wrapped, value_source);
489
+ hb_buffer_append(&wrapped, ")");
490
+
491
+ position_T splat_start =
492
+ prism_location_to_position_with_offset(&splat->base.location, original_source, erb_content_offset, source);
493
+
494
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
495
+ hb_string_from_c_string(hb_buffer_value(&wrapped)),
496
+ HB_STRING_EMPTY,
497
+ splat_start,
498
+ splat_start,
499
+ hb_array_init(0, allocator),
500
+ allocator
501
+ );
502
+
503
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
504
+
505
+ hb_buffer_free(&wrapped);
506
+ hb_allocator_dealloc(allocator, value_source);
507
+ }
167
508
  }
168
509
  } else if (element->type == PM_ASSOC_NODE) {
169
510
  pm_assoc_node_t* assoc = (pm_assoc_node_t*) element;
@@ -179,30 +520,42 @@ hb_array_T* extract_html_attributes_from_keyword_hash(
179
520
 
180
521
  if (hash_element->type == PM_ASSOC_SPLAT_NODE) {
181
522
  pm_assoc_splat_node_t* splat = (pm_assoc_splat_node_t*) hash_element;
182
- size_t splat_length = splat->base.location.end - splat->base.location.start;
183
- char* splat_content =
184
- hb_allocator_strndup(allocator, (const char*) splat->base.location.start, splat_length);
185
-
186
- if (splat_content) {
187
- position_T splat_start = prism_location_to_position_with_offset(
188
- &splat->base.location,
189
- original_source,
190
- erb_content_offset,
191
- source
192
- );
193
-
194
- AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
195
- hb_string_from_c_string(splat_content),
196
- hb_string_from_c_string(key_string),
197
- splat_start,
198
- splat_start,
199
- hb_array_init(0, allocator),
200
- allocator
201
- );
202
-
203
- if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
204
-
205
- hb_allocator_dealloc(allocator, splat_content);
523
+
524
+ if (splat->value) {
525
+ size_t value_length = splat->value->location.end - splat->value->location.start;
526
+ char* value_source =
527
+ hb_allocator_strndup(allocator, (const char*) splat->value->location.start, value_length);
528
+
529
+ if (value_source) {
530
+ hb_buffer_T wrapped;
531
+ hb_buffer_init(&wrapped, value_length + 48, allocator);
532
+ hb_buffer_append(&wrapped, "tag.attributes(");
533
+ hb_buffer_append(&wrapped, key_string);
534
+ hb_buffer_append(&wrapped, ": ");
535
+ hb_buffer_append(&wrapped, value_source);
536
+ hb_buffer_append(&wrapped, ")");
537
+
538
+ position_T splat_start = prism_location_to_position_with_offset(
539
+ &splat->base.location,
540
+ original_source,
541
+ erb_content_offset,
542
+ source
543
+ );
544
+
545
+ AST_RUBY_HTML_ATTRIBUTES_SPLAT_NODE_T* splat_node = ast_ruby_html_attributes_splat_node_init(
546
+ hb_string_from_c_string(hb_buffer_value(&wrapped)),
547
+ hb_string_from_c_string(key_string),
548
+ splat_start,
549
+ splat_start,
550
+ hb_array_init(0, allocator),
551
+ allocator
552
+ );
553
+
554
+ if (splat_node) { hb_array_append(attributes, (AST_NODE_T*) splat_node); }
555
+
556
+ hb_buffer_free(&wrapped);
557
+ hb_allocator_dealloc(allocator, value_source);
558
+ }
206
559
  }
207
560
 
208
561
  continue;
@@ -218,26 +571,11 @@ hb_array_T* extract_html_attributes_from_keyword_hash(
218
571
  hb_allocator_dealloc(allocator, raw_key);
219
572
 
220
573
  if (attribute_key_string) {
221
- position_T attribute_start = prism_location_to_position_with_offset(
222
- &hash_assoc->key->location,
223
- original_source,
224
- erb_content_offset,
225
- source
226
- );
227
- position_T attribute_end = prism_location_to_position_with_offset(
228
- &hash_assoc->value->location,
229
- original_source,
230
- erb_content_offset,
231
- source
232
- );
233
-
234
- AST_HTML_ATTRIBUTE_NODE_T* attribute = create_attribute_from_value(
235
- attribute_key_string,
236
- hash_assoc->value,
237
- attribute_start,
238
- attribute_end,
239
- allocator
240
- );
574
+ attribute_positions_T hash_positions;
575
+ fill_attribute_positions(hash_assoc, source, original_source, erb_content_offset, &hash_positions);
576
+
577
+ AST_HTML_ATTRIBUTE_NODE_T* attribute =
578
+ create_attribute_from_value(attribute_key_string, hash_assoc->value, &hash_positions, allocator, true);
241
579
 
242
580
  if (attribute) { hb_array_append(attributes, attribute); }
243
581
  hb_allocator_dealloc(allocator, attribute_key_string);
@@ -0,0 +1,87 @@
1
+ #include <prism.h>
2
+ #include <stdbool.h>
3
+ #include <stdlib.h>
4
+ #include <string.h>
5
+
6
+ #include "../../include/analyze/action_view/tag_helper_handler.h"
7
+ #include "../../include/lib/hb_buffer.h"
8
+
9
+ bool detect_image_tag(pm_call_node_t* call_node, pm_parser_t* parser) {
10
+ if (!call_node || !call_node->name) { return false; }
11
+
12
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
13
+ return constant && constant->length == 9 && strncmp((const char*) constant->start, "image_tag", 9) == 0;
14
+ }
15
+
16
+ char* extract_image_tag_name(pm_call_node_t* _call_node, pm_parser_t* _parser, hb_allocator_T* allocator) {
17
+ return hb_allocator_strdup(allocator, "img");
18
+ }
19
+
20
+ char* extract_image_tag_content(pm_call_node_t* _call_node, pm_parser_t* _parser, hb_allocator_T* _allocator) {
21
+ return NULL;
22
+ }
23
+
24
+ char* extract_image_tag_src(pm_call_node_t* call_node, pm_parser_t* _parser, hb_allocator_T* allocator) {
25
+ if (!call_node || !call_node->arguments) { return NULL; }
26
+
27
+ pm_arguments_node_t* arguments = call_node->arguments;
28
+ if (!arguments->arguments.size) { return NULL; }
29
+
30
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
31
+
32
+ if (first_argument->type == PM_STRING_NODE) {
33
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
34
+ size_t length = pm_string_length(&string_node->unescaped);
35
+
36
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
37
+ }
38
+
39
+ size_t source_length = first_argument->location.end - first_argument->location.start;
40
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
41
+ }
42
+
43
+ bool image_tag_source_is_url(const char* source, size_t length) {
44
+ if (!source || length == 0) { return false; }
45
+
46
+ if (length >= 2 && source[0] == '/' && source[1] == '/') { return true; }
47
+ if (strstr(source, "://") != NULL) { return true; }
48
+
49
+ return false;
50
+ }
51
+
52
+ char* wrap_in_image_path(
53
+ const char* source,
54
+ size_t source_length,
55
+ const char* path_options,
56
+ hb_allocator_T* allocator
57
+ ) {
58
+ hb_buffer_T buffer;
59
+ hb_buffer_init(&buffer, source_length + 32, allocator);
60
+
61
+ hb_buffer_append(&buffer, "image_path(");
62
+ hb_buffer_append_with_length(&buffer, source, source_length);
63
+
64
+ if (path_options && strlen(path_options) > 0) {
65
+ hb_buffer_append(&buffer, ", ");
66
+ hb_buffer_append(&buffer, path_options);
67
+ }
68
+
69
+ hb_buffer_append(&buffer, ")");
70
+
71
+ char* result = hb_allocator_strdup(allocator, hb_buffer_value(&buffer));
72
+ hb_buffer_free(&buffer);
73
+
74
+ return result;
75
+ }
76
+
77
+ bool image_tag_supports_block(void) {
78
+ return false;
79
+ }
80
+
81
+ const tag_helper_handler_T image_tag_handler = { .name = "image_tag",
82
+ .source =
83
+ HB_STRING_LITERAL("ActionView::Helpers::AssetTagHelper#image_tag"),
84
+ .detect = detect_image_tag,
85
+ .extract_tag_name = extract_image_tag_name,
86
+ .extract_content = extract_image_tag_content,
87
+ .supports_block = image_tag_supports_block };